foreman_remote_execution 3.2.1 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
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