foreman_remote_execution 3.2.1 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9caedc0baf4a767d6b0149fd1b349b97081460193cdccad1cb9a3208d278b67f
4
- data.tar.gz: 709c83b4582ffd65cab576e84d1b502ce31a70c57f0ce9af76e3ffd9978de031
3
+ metadata.gz: d00f4722f74258ad947432c2d8104522d3fb53749f7bac4e7c52914862af3ecc
4
+ data.tar.gz: 187645d51578339523b94fb83fe781cba865477a616c2d8c0980255be1fd0377
5
5
  SHA512:
6
- metadata.gz: 9ca7d43729ca2e9b8d2012c8f1e9055832ba52a7c7abe8b14c37ad92b73af1a689dd92ec6415e2f4f1532b18a10310caee45c2370579e389914237acb2496cbb
7
- data.tar.gz: 687feaca288aec140529e59948290a4e99aae6261567953768340b0b36aa6a8920f9e2640fd0b7a8a02cb22f727b417302569fba4bfda736c0a2a0b6c04b41a1
6
+ metadata.gz: 04c6a44e96bb03d75310d7dd48acd249503f04778c62f520d1101bf4719d195c2cd4d11e9a6ae63c97e24d8cbd50884dd090ec56a54aff933ee19f3379bbeec1
7
+ data.tar.gz: 165ba49b54b18ed4128c7b72a53ef52fea60f6f9a619ee7bd0a22545910cf68997b763e27f3569892b20dd528e70236c44b241c1d1b6c5fcec15a5a21f76af7e
data/.eslintrc CHANGED
@@ -1,32 +1,7 @@
1
1
  {
2
- "root": true,
3
- "extends": ["airbnb-base", "./node_modules/@theforeman/vendor-dev/eslint.extends.js"],
4
- "plugins": [
5
- "react"
6
- ],
7
- "env": {
8
- "browser": true,
9
- "es6": true,
10
- "node": true,
11
- "jasmine": true,
12
- "jest": true
13
- },
14
- "parser": "babel-eslint",
15
- "rules": {
16
- "react/jsx-uses-vars": "error",
17
- "react/jsx-uses-react": "error",
18
- "no-unused-vars": [
19
- "error",
20
- {
21
- "vars": "all",
22
- "args": "none"
23
- }
24
- ],
25
- "no-underscore-dangle": "off",
26
- "no-use-before-define": "off",
27
- "import/prefer-default-export": "off",
28
- // Import rules off for now due to HoundCI issue
29
- "import/no-unresolved": "off",
30
- "import/extensions": "off"
31
- }
2
+ "plugins": ["@theforeman/foreman"],
3
+ "extends": [
4
+ "plugin:@theforeman/foreman/core",
5
+ "plugin:@theforeman/foreman/plugins"
6
+ ]
32
7
  }
data/.gitignore CHANGED
@@ -14,3 +14,4 @@ locale/*/*.po.time_stamp
14
14
  Gemfile.lock
15
15
  node_modules/
16
16
  package-lock.json
17
+ coverage/
data/.hound.yml CHANGED
@@ -11,9 +11,4 @@ rubocop:
11
11
  jshint:
12
12
  enabled: false
13
13
 
14
- eslint:
15
- enabled: true
16
- config_file: .eslintrc
17
- ignore_file: .eslintignore
18
-
19
14
  fail_on_violations: true
@@ -1,6 +1,5 @@
1
1
  language: node_js
2
2
  node_js:
3
- - '6.10' # current EPEL 7
4
- - '6' # previous LTS
5
- - '8' # current LTS
3
+ - '10'
4
+ - '12'
6
5
  script: ./scripts/travis_run_js_tests.sh
@@ -29,6 +29,9 @@ module Actions
29
29
 
30
30
  raise _('Could not use any template used in the job invocation') if template_invocation.blank?
31
31
 
32
+ provider = template_invocation.template.provider
33
+ proxy_selector = provider.required_proxy_selector_for(template_invocation.template) || proxy_selector
34
+
32
35
  provider_type = template_invocation.template.provider_type.to_s
33
36
  proxy = determine_proxy!(proxy_selector, provider_type, host)
34
37
 
@@ -36,8 +39,6 @@ module Actions
36
39
  script = renderer.render
37
40
  raise _('Failed rendering template: %s') % renderer.error_message unless script
38
41
 
39
- provider = template_invocation.template.provider
40
-
41
42
  additional_options = { :hostname => provider.find_ip_or_hostname(host),
42
43
  :script => script,
43
44
  :execution_timeout_interval => job_invocation.execution_timeout_interval,
@@ -13,6 +13,9 @@ module ForemanRemoteExecution
13
13
  proxy = ::SmartProxy.find(proxy_id)
14
14
  begin
15
15
  proxy.drop_host_from_known_hosts(target)
16
+ rescue RestClient::ResourceNotFound => e
17
+ # ignore 404 when known_hosts entry is missing or the module was not enabled
18
+ Foreman::Logging.exception "Proxy failed to delete SSH known_hosts for #{name}, #{ip}", e, :level => :error
16
19
  rescue => e
17
20
  Rails.logger.warn e.message
18
21
  return false
@@ -98,5 +98,10 @@ class RemoteExecutionProvider
98
98
  def proxy_action_class
99
99
  ForemanRemoteExecutionCore::Actions::RunScript
100
100
  end
101
+
102
+ # Return a specific proxy selector to use for running a given template
103
+ # Returns either nil to use the default selector or an instance of a (sub)class of ::ForemanTasks::ProxySelector
104
+ def required_proxy_selector_for(_template)
105
+ end
101
106
  end
102
107
  end
@@ -0,0 +1,18 @@
1
+ class DefaultProxyProxySelector < ::RemoteExecutionProxySelector
2
+ def initialize
3
+ # TODO: Remove this once we have a reliable way of determining the internal proxy without katello
4
+ # Tracked as https://projects.theforeman.org/issues/29840
5
+ raise _('Internal proxy selector can only be used if Katello is enabled') unless defined?(::Katello)
6
+
7
+ super
8
+ end
9
+
10
+ def available_proxies(host, provider)
11
+ # TODO: Once we have a internal proxy marker/feature on the proxy, we can
12
+ # swap the implementation
13
+ internal_proxy = ::Katello.default_capsule
14
+ super.reduce({}) do |acc, (key, proxies)|
15
+ acc.merge(key => proxies.select { |proxy| proxy == internal_proxy })
16
+ end
17
+ end
18
+ end
@@ -4,7 +4,7 @@ User.as_anonymous_admin do
4
4
  JobTemplate.without_auditing do
5
5
  Dir[File.join("#{ForemanRemoteExecution::Engine.root}/app/views/templates/**/*.erb")].each do |template|
6
6
  sync = !Rails.env.test? && Setting[:remote_execution_sync_templates]
7
- template = JobTemplate.import_raw!(File.read(template), :default => true, :locked => true, :update => sync)
7
+ template = JobTemplate.import_raw!(File.read(template), :default => true, :lock => true, :update => sync)
8
8
  template.organizations = organizations if template.present?
9
9
  template.locations = locations if template.present?
10
10
  end
@@ -1,3 +1,3 @@
1
1
  module ForemanRemoteExecution
2
- VERSION = '3.2.1'.freeze
2
+ VERSION = '3.3.0'.freeze
3
3
  end
@@ -3,25 +3,14 @@
3
3
  "version": "1.0.0",
4
4
  "license": "GPL-3.0",
5
5
  "scripts": {
6
- "lint": "./node_modules/.bin/eslint -c .eslintrc webpack/ script/",
7
- "test": "node node_modules/.bin/jest webpack",
8
- "test:watch": "node node_modules/.bin/jest webpack --watchAll",
9
- "test:current": "node node_modules/.bin/jest webpack --watch"
10
- },
11
- "jest": {
12
- "verbose": true,
13
- "moduleDirectories": [
14
- "node_modules",
15
- "webpack"
16
- ],
17
- "setupFiles": [
18
- "raf/polyfill",
19
- "./webpack/test_setup.js"
20
- ],
21
- "testPathIgnorePatterns": [
22
- "/node_modules/",
23
- "<rootDir>/foreman/"
24
- ]
6
+ "lint": "tfm-lint --plugin -d /webpack",
7
+ "test": "tfm-test --plugin",
8
+ "test:watch": "tfm-test --plugin --watchAll",
9
+ "test:current": "tfm-test --plugin --watch",
10
+ "publish-coverage": "tfm-publish-coverage",
11
+ "stories": "tfm-stories --plugin",
12
+ "stories:build": "tfm-build-stories --plugin",
13
+ "stories:deploy": "surge --project .storybook-dist"
25
14
  },
26
15
  "repository": {
27
16
  "type": "git",
@@ -32,22 +21,16 @@
32
21
  },
33
22
  "devDependencies": {
34
23
  "@babel/core": "^7.7.0",
35
- "@theforeman/builder": "^4.0.2",
36
- "@theforeman/vendor-dev": "^4.0.2",
24
+ "@theforeman/builder": "^4.2.1",
25
+ "@theforeman/eslint-plugin-foreman": "^4.2.1",
26
+ "@theforeman/stories": "^4.2.1",
27
+ "@theforeman/test": "^4.2.1",
28
+ "@theforeman/vendor-dev": "^4.2.1",
37
29
  "babel-eslint": "^10.0.0",
38
- "babel-jest": "^24.9.0",
39
- "enzyme": "^3.2.0",
40
- "enzyme-adapter-react-16": "^1.1.0",
41
- "enzyme-to-json": "^3.1.2",
42
- "eslint": "^4.10.0",
43
- "eslint-config-airbnb": "^16.0.0",
44
- "eslint-plugin-import": "^2.8.0",
45
- "eslint-plugin-jest": "^21.2.0",
46
- "eslint-plugin-jsx-a11y": "^6.0.2",
47
- "eslint-plugin-react": "^7.4.0",
48
- "jest": "^24.9.0"
30
+ "eslint": "^6.8.0",
31
+ "prettier": "^1.19.1"
49
32
  },
50
33
  "peerDependencies": {
51
- "@theforeman/vendor": ">= 4.0.2"
34
+ "@theforeman/vendor": ">= 4.2.1"
52
35
  }
53
36
  }
@@ -1,6 +1,5 @@
1
- import URI from 'urijs';
2
1
  // eslint-disable-next-line import/no-extraneous-dependencies
3
- import { mount, registerReducer } from 'foremanReact/common/MountingService';
2
+ import { registerReducer } from 'foremanReact/common/MountingService';
4
3
  // eslint-disable-next-line import/no-extraneous-dependencies
5
4
  import componentRegistry from 'foremanReact/components/componentRegistry';
6
5
  import JobInvocationContainer from './react_app/components/jobInvocations';
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import PropTypes from 'prop-types';
2
3
 
3
4
  const AggregateStatus = ({ statuses }) => (
4
5
  <div id="aggregate_statuses">
@@ -31,4 +32,13 @@ const AggregateStatus = ({ statuses }) => (
31
32
  </div>
32
33
  );
33
34
 
35
+ AggregateStatus.propTypes = {
36
+ statuses: PropTypes.shape({
37
+ cancelled: PropTypes.number,
38
+ failed: PropTypes.number,
39
+ pending: PropTypes.number,
40
+ success: PropTypes.number,
41
+ }).isRequired,
42
+ };
43
+
34
44
  export default AggregateStatus;
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import { shallow } from 'enzyme';
3
- import AggregateStatus from './index.js';
2
+ import { shallow } from '@theforeman/test';
3
+ import AggregateStatus from './index';
4
4
 
5
5
  jest.unmock('./index.js');
6
6
 
@@ -20,7 +20,10 @@ describe('AggregateStatus', () => {
20
20
 
21
21
  it('renders cards with props passed', () => {
22
22
  const statuses = {
23
- success: 19, failed: 20, cancelled: 31, pending: 3,
23
+ success: 19,
24
+ failed: 20,
25
+ cancelled: 31,
26
+ pending: 3,
24
27
  };
25
28
  const chartNumbers = shallow(<AggregateStatus statuses={statuses} />);
26
29
  const success = chartNumbers.find('#success_count').text();
@@ -4,14 +4,18 @@ import Immutable from 'seamless-immutable';
4
4
  import PropTypes from 'prop-types';
5
5
  // eslint-disable-next-line import/no-extraneous-dependencies
6
6
  import DonutChart from 'foremanReact/components/common/charts/DonutChart';
7
- import AggregateStatus from './AggregateStatus/index.js';
7
+ import AggregateStatus from './AggregateStatus';
8
8
  import * as JobInvocationActions from '../../redux/actions/jobInvocations';
9
9
 
10
- const colIndexOfMaxValue = columns => columns.reduce((iMax, x, i, arr) => (x[1] > arr[iMax][1] ? i : iMax), 0);
10
+ const colIndexOfMaxValue = columns =>
11
+ columns.reduce((iMax, x, i, arr) => (x[1] > arr[iMax][1] ? i : iMax), 0);
11
12
 
12
13
  class JobInvocationContainer extends React.Component {
13
14
  componentDidMount() {
14
- const { startJobInvocationsPolling, data: { url } } = this.props;
15
+ const {
16
+ startJobInvocationsPolling,
17
+ data: { url },
18
+ } = this.props;
15
19
 
16
20
  startJobInvocationsPolling(url);
17
21
  }
@@ -22,15 +26,20 @@ class JobInvocationContainer extends React.Component {
22
26
 
23
27
  return (
24
28
  <div id="job_invocations_chart_container">
25
- <DonutChart data={Immutable.asMutable(jobInvocations)}
26
- title={{type: 'percent', secondary: (jobInvocations[iMax] || [])[0]}}/>
29
+ <DonutChart
30
+ data={Immutable.asMutable(jobInvocations)}
31
+ title={{
32
+ type: 'percent',
33
+ secondary: (jobInvocations[iMax] || [])[0],
34
+ }}
35
+ />
27
36
  <AggregateStatus statuses={statuses} />
28
37
  </div>
29
38
  );
30
39
  }
31
40
  }
32
41
 
33
- const mapStateToProps = (state) => {
42
+ const mapStateToProps = state => {
34
43
  const {
35
44
  jobInvocations,
36
45
  statuses,
@@ -62,4 +71,7 @@ JobInvocationContainer.defaultProps = {
62
71
  jobInvocations: [['property', 3, 'color']],
63
72
  statuses: {},
64
73
  };
65
- export default connect(mapStateToProps, JobInvocationActions)(JobInvocationContainer);
74
+ export default connect(
75
+ mapStateToProps,
76
+ JobInvocationActions
77
+ )(JobInvocationContainer);
@@ -8,10 +8,10 @@ import {
8
8
  } from '../../consts';
9
9
 
10
10
  const defaultJobInvocationsPollingInterval = 1000;
11
- const jobInvocationsInterval = process.env.JOB_INVOCATIONS_POLLING ||
12
- defaultJobInvocationsPollingInterval;
11
+ const jobInvocationsInterval =
12
+ process.env.JOB_INVOCATIONS_POLLING || defaultJobInvocationsPollingInterval;
13
13
 
14
- const getJobInvocations = url => (dispatch, getState) => {
14
+ const getJobInvocations = url => async (dispatch, getState) => {
15
15
  function onGetJobInvocationsSuccess({ data }) {
16
16
  // If the job has finished, stop polling
17
17
  if (data.finished) {
@@ -41,7 +41,7 @@ const getJobInvocations = url => (dispatch, getState) => {
41
41
  if (jobInvocationsInterval) {
42
42
  setTimeout(
43
43
  () => dispatch(getJobInvocations(url)),
44
- jobInvocationsInterval,
44
+ jobInvocationsInterval
45
45
  );
46
46
  }
47
47
  }
@@ -52,10 +52,14 @@ const getJobInvocations = url => (dispatch, getState) => {
52
52
 
53
53
  if (getState().foremanRemoteExecutionReducers.jobInvocations.isPolling) {
54
54
  if (isDocumentVisible) {
55
- API.get(url)
56
- .then(onGetJobInvocationsSuccess)
57
- .catch(onGetJobInvocationsFailed)
58
- .then(triggerPolling);
55
+ try {
56
+ const data = await API.get(url);
57
+ onGetJobInvocationsSuccess(data);
58
+ } catch (error) {
59
+ onGetJobInvocationsFailed(error);
60
+ } finally {
61
+ triggerPolling();
62
+ }
59
63
  } else {
60
64
  // document is not visible, keep polling without api call
61
65
  triggerPolling();
@@ -2,5 +2,4 @@ export const JOB_INVOCATIONS_POLLING_STARTED =
2
2
  'JOB_INVOCATIONS_POLLING_STARTED';
3
3
  export const JOB_INVOCATIONS_GET_JOB_INVOCATIONS =
4
4
  'JOB_INVOCATIONS_GET_JOB_INVOCATIONS';
5
- export const JOB_INVOCATIONS_JOB_FINISHED =
6
- 'JOB_INVOCATIONS_JOB_FINISHED';
5
+ export const JOB_INVOCATIONS_JOB_FINISHED = 'JOB_INVOCATIONS_JOB_FINISHED';
@@ -15,26 +15,10 @@ export const pollingStarted = Immutable({
15
15
  export const jobInvocationsPayload = Immutable({
16
16
  jobInvocations: {
17
17
  job_invocations: [
18
- [
19
- 'Success',
20
- 100,
21
- '#B7312D',
22
- ],
23
- [
24
- 'Failed',
25
- 20,
26
- '#B7312D',
27
- ],
28
- [
29
- 'Pending',
30
- 40,
31
- '#B7312D',
32
- ],
33
- [
34
- 'Cancelled',
35
- 0,
36
- '#B7312D',
37
- ],
18
+ ['Success', 100, '#B7312D'],
19
+ ['Failed', 20, '#B7312D'],
20
+ ['Pending', 40, '#B7312D'],
21
+ ['Cancelled', 0, '#B7312D'],
38
22
  ],
39
23
  statuses: {
40
24
  cancelled: 0,
@@ -48,26 +32,10 @@ export const jobInvocationsPayload = Immutable({
48
32
  export const jobInvocationsReceived = Immutable({
49
33
  isPolling: true,
50
34
  jobInvocations: [
51
- [
52
- 'Success',
53
- 100,
54
- '#B7312D',
55
- ],
56
- [
57
- 'Failed',
58
- 20,
59
- '#B7312D',
60
- ],
61
- [
62
- 'Pending',
63
- 40,
64
- '#B7312D',
65
- ],
66
- [
67
- 'Cancelled',
68
- 0,
69
- '#B7312D',
70
- ],
35
+ ['Success', 100, '#B7312D'],
36
+ ['Failed', 20, '#B7312D'],
37
+ ['Pending', 40, '#B7312D'],
38
+ ['Cancelled', 0, '#B7312D'],
71
39
  ],
72
40
  statuses: {
73
41
  cancelled: 0,
@@ -18,20 +18,26 @@ describe('job invocations chart reducer', () => {
18
18
  expect(reducer(undefined, {})).toEqual(initialState);
19
19
  });
20
20
  it('should start polling given POLLING_STARTED', () => {
21
- expect(reducer(initialState, {
22
- type: JOB_INVOCATIONS_POLLING_STARTED,
23
- })).toEqual(pollingStarted);
21
+ expect(
22
+ reducer(initialState, {
23
+ type: JOB_INVOCATIONS_POLLING_STARTED,
24
+ })
25
+ ).toEqual(pollingStarted);
24
26
  });
25
27
  it('should stop polling given JOB_FINISHED', () => {
26
- expect(reducer(pollingStarted, {
27
- type: JOB_INVOCATIONS_JOB_FINISHED,
28
- payload: { jobInvocations: { job_invocations: [], statuses: {} } },
29
- })).toEqual(initialState);
28
+ expect(
29
+ reducer(pollingStarted, {
30
+ type: JOB_INVOCATIONS_JOB_FINISHED,
31
+ payload: { jobInvocations: { job_invocations: [], statuses: {} } },
32
+ })
33
+ ).toEqual(initialState);
30
34
  });
31
35
  it('should receive job invocations given GET_JOB_INVOCATIONS', () => {
32
- expect(reducer(pollingStarted, {
33
- type: JOB_INVOCATIONS_GET_JOB_INVOCATIONS,
34
- payload: jobInvocationsPayload,
35
- })).toEqual(jobInvocationsReceived);
36
+ expect(
37
+ reducer(pollingStarted, {
38
+ type: JOB_INVOCATIONS_GET_JOB_INVOCATIONS,
39
+ payload: jobInvocationsPayload,
40
+ })
41
+ ).toEqual(jobInvocationsReceived);
36
42
  });
37
43
  });
@@ -1,7 +1,8 @@
1
1
  import 'core-js/shim';
2
2
  import 'regenerator-runtime/runtime';
3
3
 
4
- import { configure } from 'enzyme';
4
+ import { configure } from '@theforeman/test';
5
+ // eslint-disable-next-line import/no-extraneous-dependencies
5
6
  import Adapter from 'enzyme-adapter-react-16';
6
7
 
7
8
  configure({ adapter: new Adapter() });
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_remote_execution
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.1
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Foreman Remote Execution team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-14 00:00:00.000000000 Z
11
+ date: 2020-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: deface
@@ -205,6 +205,7 @@ files:
205
205
  - app/models/template_invocation_input_value.rb
206
206
  - app/overrides/execution_interface.rb
207
207
  - app/overrides/subnet_proxies.rb
208
+ - app/services/default_proxy_proxy_selector.rb
208
209
  - app/services/remote_execution_proxy_selector.rb
209
210
  - app/services/ui_notifications/remote_execution_jobs/base_job_finish.rb
210
211
  - app/views/api/v2/foreign_input_sets/base.json.rabl
@@ -426,7 +427,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
426
427
  - !ruby/object:Gem::Version
427
428
  version: '0'
428
429
  requirements: []
429
- rubygems_version: 3.0.3
430
+ rubygems_version: 3.0.4
430
431
  signing_key:
431
432
  specification_version: 4
432
433
  summary: A plugin bringing remote execution to the Foreman, completing the config