foreman_remote_execution 10.0.4 → 10.0.6
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 +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
|