foreman_remote_execution 10.0.4 → 10.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/models/job_template.rb +1 -1
- data/extra/cockpit/foreman-cockpit-session +29 -12
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/package.json +1 -0
- data/test/unit/job_template_test.rb +3 -1
- data/webpack/JobWizard/JobWizard.js +19 -2
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +11 -1
- data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +3 -0
- data/webpack/JobWizard/steps/Schedule/RepeatCron.js +25 -1
- data/webpack/JobWizard/steps/Schedule/RepeatWeek.js +3 -2
- data/webpack/__mocks__/foremanReact/Root/Context/ForemanContext/index.js +1 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0d1dfb7b78f7d6b17a97fe16984983f966bb6f4bb8598601923fabccb31a31ff
|
4
|
+
data.tar.gz: ba03ab056e8d4fd77b61f3dee350c9aa8c029849e1837abe687fd138c5bf8f7a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d246ff28f82ce483e189979542a02c7e5e35e850ca61761d73ae11468cfc152593571c5e9aee562b2869b80c3201885812bc779fb82d2138c61725ac203d5c50
|
7
|
+
data.tar.gz: f970e87c94079104ea58f7654559d3bf4b0ca02af756e7ac63201a0608ac3010c6502a68492d635f1daa62690c4e501739c53932c9da2a300aa56d84801e6a55
|
data/app/models/job_template.rb
CHANGED
@@ -192,7 +192,7 @@ class JobTemplate < ::Template
|
|
192
192
|
self.default = options[:default] unless options[:default].nil?
|
193
193
|
|
194
194
|
# job templates have too long metadata, we remove them on parsing until it's stored in separate attribute
|
195
|
-
self.template = self.template.
|
195
|
+
self.template = self.template.sub(/<%\#.+?.-?%>\n?/m, '').strip
|
196
196
|
end
|
197
197
|
|
198
198
|
def default_input_values(ignore_keys)
|
@@ -170,24 +170,41 @@ class Relay
|
|
170
170
|
def initialize(proxy, params)
|
171
171
|
@proxy = proxy
|
172
172
|
@params = params
|
173
|
+
@inject_authorization = @params['ssh_user'] != 'root' && @params['effective_user_password']
|
173
174
|
end
|
174
175
|
|
175
176
|
def proxy_loop
|
176
177
|
proxy1 = ProxyBuffer.new($stdin, @sock)
|
177
178
|
proxy2 = ProxyBuffer.new(@sock, $stdout)
|
178
179
|
proxy2.on_data do |data|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
'
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
180
|
+
if @inject_authorization
|
181
|
+
sio = StringIO.new(data)
|
182
|
+
begin
|
183
|
+
message = Cockpit.read_control(sio)
|
184
|
+
rescue StandardError
|
185
|
+
# We're looking for one specific message, but the expectation that one
|
186
|
+
# invocation of this callback processes one message doesn't really
|
187
|
+
# hold. The message we're looking for is sent quite early in the
|
188
|
+
# communication, if at all, so the chance that it will be aligned with
|
189
|
+
# the beginning of the buffer is quite high. If we somehow fail to
|
190
|
+
# process the contents of the buffer, we should just carry on.
|
191
|
+
#
|
192
|
+
# With the authorization injection check in place, this is more of a
|
193
|
+
# precaution so that unexpectedly big message won't bring the entire
|
194
|
+
# thing down.
|
195
|
+
end
|
196
|
+
if message.is_a?(Hash) && message['command'] == 'authorize'
|
197
|
+
response = {
|
198
|
+
'command' => 'authorize',
|
199
|
+
'cookie' => message['cookie'],
|
200
|
+
'response' => @params['effective_user_password'],
|
201
|
+
}
|
202
|
+
proxy1.enqueue(Cockpit.encode_message(response))
|
203
|
+
@inject_authorization = false
|
204
|
+
data = sio.read # Return whatever was left unread after read_control
|
205
|
+
end
|
190
206
|
end
|
207
|
+
data
|
191
208
|
end
|
192
209
|
|
193
210
|
proxies = [proxy1, proxy2]
|
@@ -271,7 +288,7 @@ class Relay
|
|
271
288
|
end
|
272
289
|
raise AccessDeniedError, message
|
273
290
|
else
|
274
|
-
raise CockpitError, "Error talking to smart proxy: #{
|
291
|
+
raise CockpitError, "Error talking to smart proxy: #{body}"
|
275
292
|
end
|
276
293
|
end
|
277
294
|
end
|
data/package.json
CHANGED
@@ -21,6 +21,7 @@
|
|
21
21
|
"url": "http://projects.theforeman.org/projects/foreman_remote_execution/issues"
|
22
22
|
},
|
23
23
|
"devDependencies": {
|
24
|
+
"@adobe/css-tools": "~4.2.0",
|
24
25
|
"@babel/core": "^7.7.0",
|
25
26
|
"@theforeman/builder": "^12.0.1",
|
26
27
|
"@theforeman/eslint-plugin-foreman": "^12.0.1",
|
@@ -94,6 +94,8 @@ class JobTemplateTest < ActiveSupport::TestCase
|
|
94
94
|
%>
|
95
95
|
|
96
96
|
service <%= input("service_name") %> restart
|
97
|
+
|
98
|
+
<%# test comment %>
|
97
99
|
END_TEMPLATE
|
98
100
|
|
99
101
|
JobTemplate.import_raw!(template, :default => true)
|
@@ -122,7 +124,7 @@ class JobTemplateTest < ActiveSupport::TestCase
|
|
122
124
|
end
|
123
125
|
|
124
126
|
it 'has a template' do
|
125
|
-
_(template.template.squish).must_equal 'service <%= input("service_name") %> restart'
|
127
|
+
_(template.template.squish).must_equal 'service <%= input("service_name") %> restart <%# test comment %>'
|
126
128
|
end
|
127
129
|
|
128
130
|
it 'imports inputs' do
|
@@ -41,6 +41,8 @@ import { Footer } from './Footer';
|
|
41
41
|
import './JobWizard.scss';
|
42
42
|
|
43
43
|
export const JobWizard = ({ rerunData }) => {
|
44
|
+
const routerSearch = useSelector(selectRouterSearch);
|
45
|
+
const [feature, setFeature] = useState(routerSearch.feature);
|
44
46
|
const jobCategoriesResponse = useSelector(selectJobCategoriesResponse);
|
45
47
|
const [jobTemplateID, setJobTemplateID] = useState(
|
46
48
|
rerunData?.template_invocations?.[0]?.template_id ||
|
@@ -58,7 +60,6 @@ export const JobWizard = ({ rerunData }) => {
|
|
58
60
|
hostGroups: [],
|
59
61
|
});
|
60
62
|
const [hostsSearchQuery, setHostsSearchQuery] = useState('');
|
61
|
-
const routerSearch = useSelector(selectRouterSearch);
|
62
63
|
const [fills, setFills] = useState(
|
63
64
|
rerunData
|
64
65
|
? {
|
@@ -169,8 +170,23 @@ export const JobWizard = ({ rerunData }) => {
|
|
169
170
|
job_template: { name, description_format },
|
170
171
|
},
|
171
172
|
}) => {
|
173
|
+
const allowedInputs = template_inputs
|
174
|
+
.map(({ name: _name }) => _name)
|
175
|
+
.concat(
|
176
|
+
advanced_template_inputs.map(({ name: _name }) => _name)
|
177
|
+
);
|
178
|
+
const prune = inputs =>
|
179
|
+
allowedInputs.reduce(
|
180
|
+
(acc, key) =>
|
181
|
+
inputs.hasOwnProperty(key)
|
182
|
+
? { [key]: inputs[key], ...acc }
|
183
|
+
: acc,
|
184
|
+
{}
|
185
|
+
);
|
186
|
+
setTemplateValues(prune);
|
172
187
|
setAdvancedValues(currentAdvancedValues => ({
|
173
188
|
...currentAdvancedValues,
|
189
|
+
templateValues: prune(currentAdvancedValues.templateValues),
|
174
190
|
description:
|
175
191
|
generateDefaultDescription({
|
176
192
|
description_format,
|
@@ -243,6 +259,7 @@ export const JobWizard = ({ rerunData }) => {
|
|
243
259
|
setJobTemplate={setJobTemplateID}
|
244
260
|
category={category}
|
245
261
|
setCategory={setCategory}
|
262
|
+
setFeature={setFeature}
|
246
263
|
isCategoryPreselected={
|
247
264
|
!!rerunData || !!fills.feature || !!fills.template_id
|
248
265
|
}
|
@@ -432,7 +449,7 @@ export const JobWizard = ({ rerunData }) => {
|
|
432
449
|
hostsSearchQuery,
|
433
450
|
location,
|
434
451
|
organization,
|
435
|
-
feature
|
452
|
+
feature,
|
436
453
|
provider: templateResponse.provider_name,
|
437
454
|
advancedInputs: templateResponse.advanced_template_inputs,
|
438
455
|
});
|
@@ -16,6 +16,7 @@ export const CategoryAndTemplate = ({
|
|
16
16
|
selectedTemplateID,
|
17
17
|
selectedCategory,
|
18
18
|
setCategory,
|
19
|
+
setFeature,
|
19
20
|
errors,
|
20
21
|
}) => {
|
21
22
|
const templatesGroups = {};
|
@@ -45,8 +46,16 @@ export const CategoryAndTemplate = ({
|
|
45
46
|
if (selectedCategory !== newCategory) {
|
46
47
|
setCategory(newCategory);
|
47
48
|
setJobTemplate(null);
|
49
|
+
setFeature(null);
|
48
50
|
}
|
49
51
|
};
|
52
|
+
const onSelectTemplate = newTemplate => {
|
53
|
+
if (selectedTemplate !== newTemplate) {
|
54
|
+
setJobTemplate(newTemplate);
|
55
|
+
setFeature(null);
|
56
|
+
}
|
57
|
+
};
|
58
|
+
|
50
59
|
const { categoryError, allTemplatesError, templateError } = errors;
|
51
60
|
const isError = !!(categoryError || allTemplatesError || templateError);
|
52
61
|
return (
|
@@ -68,7 +77,7 @@ export const CategoryAndTemplate = ({
|
|
68
77
|
label={__('Job template')}
|
69
78
|
fieldId="job_template"
|
70
79
|
groups={Object.values(templatesGroups)}
|
71
|
-
setSelected={
|
80
|
+
setSelected={onSelectTemplate}
|
72
81
|
selected={isTemplatesLoading ? [] : selectedTemplate}
|
73
82
|
isDisabled={
|
74
83
|
!!(categoryError || allTemplatesError || isTemplatesLoading)
|
@@ -103,6 +112,7 @@ CategoryAndTemplate.propTypes = {
|
|
103
112
|
jobCategories: PropTypes.array,
|
104
113
|
jobTemplates: PropTypes.array,
|
105
114
|
setJobTemplate: PropTypes.func.isRequired,
|
115
|
+
setFeature: PropTypes.func.isRequired,
|
106
116
|
selectedTemplateID: PropTypes.number,
|
107
117
|
setCategory: PropTypes.func.isRequired,
|
108
118
|
selectedCategory: PropTypes.string,
|
@@ -26,6 +26,7 @@ const ConnectedCategoryAndTemplate = ({
|
|
26
26
|
setJobTemplate,
|
27
27
|
category,
|
28
28
|
setCategory,
|
29
|
+
setFeature,
|
29
30
|
isCategoryPreselected,
|
30
31
|
}) => {
|
31
32
|
const dispatch = useDispatch();
|
@@ -104,6 +105,7 @@ const ConnectedCategoryAndTemplate = ({
|
|
104
105
|
selectedTemplateID={jobTemplate}
|
105
106
|
setCategory={setCategory}
|
106
107
|
selectedCategory={category}
|
108
|
+
setFeature={setFeature}
|
107
109
|
errors={errors}
|
108
110
|
/>
|
109
111
|
);
|
@@ -114,6 +116,7 @@ ConnectedCategoryAndTemplate.propTypes = {
|
|
114
116
|
setJobTemplate: PropTypes.func.isRequired,
|
115
117
|
category: PropTypes.string.isRequired,
|
116
118
|
setCategory: PropTypes.func.isRequired,
|
119
|
+
setFeature: PropTypes.func.isRequired,
|
117
120
|
isCategoryPreselected: PropTypes.bool.isRequired,
|
118
121
|
};
|
119
122
|
ConnectedCategoryAndTemplate.defaultProps = { jobTemplate: null };
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import URI from 'urijs';
|
1
2
|
import React, { useEffect } from 'react';
|
2
3
|
import PropTypes from 'prop-types';
|
3
4
|
import {
|
@@ -9,10 +10,25 @@ import {
|
|
9
10
|
FormGroup,
|
10
11
|
ValidatedOptions,
|
11
12
|
} from '@patternfly/react-core';
|
13
|
+
import { foremanUrl } from 'foremanReact/common/helpers';
|
14
|
+
import { useForemanVersion } from 'foremanReact/Root/Context/ForemanContext';
|
12
15
|
import { translate as __ } from 'foremanReact/common/I18n';
|
13
16
|
import { helpLabel } from '../form/FormHelpers';
|
14
17
|
|
18
|
+
const docUrl = foremanVersion => {
|
19
|
+
const rootUrl = `https://docs.theforeman.org/${foremanVersion}/`;
|
20
|
+
const section =
|
21
|
+
'Managing_Hosts/index-foreman-el.html#using-extended-cron-lines_managing-hosts';
|
22
|
+
|
23
|
+
const url = new URI({
|
24
|
+
path: '/links/manual',
|
25
|
+
query: { root_url: rootUrl, section },
|
26
|
+
});
|
27
|
+
return foremanUrl(url.href());
|
28
|
+
};
|
29
|
+
|
15
30
|
export const RepeatCron = ({ repeatData, setRepeatData, setValid }) => {
|
31
|
+
const foremanVersion = useForemanVersion();
|
16
32
|
const { cronline } = repeatData;
|
17
33
|
useEffect(() => {
|
18
34
|
if (cronline) {
|
@@ -24,7 +40,7 @@ export const RepeatCron = ({ repeatData, setRepeatData, setValid }) => {
|
|
24
40
|
}, [setValid, cronline]);
|
25
41
|
return (
|
26
42
|
<FormGroup
|
27
|
-
label={__('Cron line')}
|
43
|
+
label={__('Cron line (extended)')}
|
28
44
|
labelIcon={helpLabel(
|
29
45
|
<div>
|
30
46
|
{__("Cron line format '1 2 3 4 5', where:")}
|
@@ -36,6 +52,14 @@ export const RepeatCron = ({ repeatData, setRepeatData, setValid }) => {
|
|
36
52
|
<ListItem>{__('is month (range: 1-12)')}</ListItem>
|
37
53
|
<ListItem>{__('is day of week (range: 0-6)')}</ListItem>
|
38
54
|
</List>
|
55
|
+
<br />
|
56
|
+
{__(
|
57
|
+
'The cron line supports extended cron line syntax. For details please refer to the '
|
58
|
+
)}
|
59
|
+
<a href={docUrl(foremanVersion)} target="_blank" rel="noreferrer">
|
60
|
+
{__('documentation')}
|
61
|
+
</a>
|
62
|
+
.
|
39
63
|
</div>
|
40
64
|
)}
|
41
65
|
isRequired
|
@@ -9,11 +9,12 @@ export const getWeekDays = () => {
|
|
9
9
|
const locale = documentLocale().replace(/-/g, '_');
|
10
10
|
const baseDate = new Date(Date.UTC(2017, 0, 1)); // just a Sunday
|
11
11
|
const weekDays = [];
|
12
|
+
const formatOptions = { weekday: 'short', timeZone: 'UTC' };
|
12
13
|
for (let i = 0; i < 7; i++) {
|
13
14
|
try {
|
14
|
-
weekDays.push(baseDate.toLocaleDateString(locale,
|
15
|
+
weekDays.push(baseDate.toLocaleDateString(locale, formatOptions));
|
15
16
|
} catch {
|
16
|
-
weekDays.push(baseDate.toLocaleDateString('en',
|
17
|
+
weekDays.push(baseDate.toLocaleDateString('en', formatOptions));
|
17
18
|
}
|
18
19
|
baseDate.setDate(baseDate.getDate() + 1);
|
19
20
|
}
|
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: 10.0.
|
4
|
+
version: 10.0.6
|
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: 2023-
|
11
|
+
date: 2023-08-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: deface
|
@@ -576,7 +576,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
576
576
|
- !ruby/object:Gem::Version
|
577
577
|
version: '0'
|
578
578
|
requirements: []
|
579
|
-
rubygems_version: 3.4.
|
579
|
+
rubygems_version: 3.4.17
|
580
580
|
signing_key:
|
581
581
|
specification_version: 4
|
582
582
|
summary: A plugin bringing remote execution to the Foreman, completing the config
|