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 +4 -4
- data/.eslintrc +5 -30
- data/.gitignore +1 -0
- data/.hound.yml +0 -5
- data/.travis.yml +2 -3
- data/app/lib/actions/remote_execution/run_host_job.rb +3 -2
- data/app/models/concerns/foreman_remote_execution/orchestration/ssh.rb +3 -0
- data/app/models/remote_execution_provider.rb +5 -0
- data/app/services/default_proxy_proxy_selector.rb +18 -0
- data/db/seeds.d/70-job_templates.rb +1 -1
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/package.json +16 -33
- data/webpack/index.js +1 -2
- data/webpack/react_app/components/jobInvocations/AggregateStatus/index.js +10 -0
- data/webpack/react_app/components/jobInvocations/AggregateStatus/index.test.js +6 -3
- data/webpack/react_app/components/jobInvocations/index.js +19 -7
- data/webpack/react_app/redux/actions/jobInvocations/index.js +12 -8
- data/webpack/react_app/redux/consts.js +1 -2
- data/webpack/react_app/redux/reducers/jobInvocations/index.fixtures.js +8 -40
- data/webpack/react_app/redux/reducers/jobInvocations/index.test.js +17 -11
- data/webpack/test_setup.js +2 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d00f4722f74258ad947432c2d8104522d3fb53749f7bac4e7c52914862af3ecc
|
4
|
+
data.tar.gz: 187645d51578339523b94fb83fe781cba865477a616c2d8c0980255be1fd0377
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 04c6a44e96bb03d75310d7dd48acd249503f04778c62f520d1101bf4719d195c2cd4d11e9a6ae63c97e24d8cbd50884dd090ec56a54aff933ee19f3379bbeec1
|
7
|
+
data.tar.gz: 165ba49b54b18ed4128c7b72a53ef52fea60f6f9a619ee7bd0a22545910cf68997b763e27f3569892b20dd528e70236c44b241c1d1b6c5fcec15a5a21f76af7e
|
data/.eslintrc
CHANGED
@@ -1,32 +1,7 @@
|
|
1
1
|
{
|
2
|
-
"
|
3
|
-
"extends": [
|
4
|
-
|
5
|
-
"
|
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
data/.hound.yml
CHANGED
data/.travis.yml
CHANGED
@@ -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, :
|
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
|
data/package.json
CHANGED
@@ -3,25 +3,14 @@
|
|
3
3
|
"version": "1.0.0",
|
4
4
|
"license": "GPL-3.0",
|
5
5
|
"scripts": {
|
6
|
-
"lint": "
|
7
|
-
"test": "
|
8
|
-
"test:watch": "
|
9
|
-
"test:current": "
|
10
|
-
|
11
|
-
|
12
|
-
"
|
13
|
-
"
|
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.
|
36
|
-
"@theforeman/
|
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
|
-
"
|
39
|
-
"
|
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.
|
34
|
+
"@theforeman/vendor": ">= 4.2.1"
|
52
35
|
}
|
53
36
|
}
|
data/webpack/index.js
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
import URI from 'urijs';
|
2
1
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
3
|
-
import {
|
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 '
|
3
|
-
import AggregateStatus from './index
|
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,
|
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
|
7
|
+
import AggregateStatus from './AggregateStatus';
|
8
8
|
import * as JobInvocationActions from '../../redux/actions/jobInvocations';
|
9
9
|
|
10
|
-
const colIndexOfMaxValue = columns =>
|
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 {
|
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
|
26
|
-
|
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 =
|
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(
|
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 =
|
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
|
-
|
56
|
-
.
|
57
|
-
|
58
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
53
|
-
|
54
|
-
|
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(
|
22
|
-
|
23
|
-
|
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(
|
27
|
-
|
28
|
-
|
29
|
-
|
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(
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
+
expect(
|
37
|
+
reducer(pollingStarted, {
|
38
|
+
type: JOB_INVOCATIONS_GET_JOB_INVOCATIONS,
|
39
|
+
payload: jobInvocationsPayload,
|
40
|
+
})
|
41
|
+
).toEqual(jobInvocationsReceived);
|
36
42
|
});
|
37
43
|
});
|
data/webpack/test_setup.js
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
import 'core-js/shim';
|
2
2
|
import 'regenerator-runtime/runtime';
|
3
3
|
|
4
|
-
import { configure } from '
|
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.
|
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-
|
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.
|
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
|