foreman_remote_execution 8.2.1 → 8.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/job_invocations_controller.rb +20 -1
- data/app/controllers/ui_job_wizard_controller.rb +0 -3
- data/app/helpers/remote_execution_helper.rb +1 -1
- data/app/lib/actions/remote_execution/run_host_job.rb +1 -1
- data/app/views/job_invocations/_form.html.erb +1 -1
- data/config/routes.rb +1 -0
- data/extra/cockpit/foreman-cockpit-session +29 -12
- data/lib/foreman_remote_execution/engine.rb +1 -1
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/webpack/JobWizard/JobWizardConstants.js +1 -1
- data/webpack/JobWizard/autofill.js +5 -2
- data/webpack/JobWizard/steps/HostsAndInputs/__tests__/HostsAndInputs.test.js +1 -1
- data/webpack/JobWizard/steps/Schedule/QueryType.js +1 -1
- data/webpack/JobWizard/steps/Schedule/RepeatHour.js +0 -1
- data/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js +4 -4
- data/webpack/JobWizard/steps/form/DateTimePicker.js +0 -1
- data/webpack/JobWizard/steps/form/GroupedSelectField.js +0 -1
- data/webpack/JobWizard/steps/form/SelectField.js +0 -1
- data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +4 -4
- data/webpack/react_app/components/RecentJobsCard/RecentJobsTable.js +2 -2
- data/webpack/react_app/components/RecentJobsCard/constants.js +2 -2
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bb6aa7e8d08233993c7f908975b7688460969ad1182b008ab8697767a772fffc
|
4
|
+
data.tar.gz: 8e1096d3d25655600889f7abecd499f7157d344a82f8ead12fc226b4ba180eb4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa738c870c881c464e22ca4d59361e6ca8c28c878dfa368c09c3c31ea7766fabfe2ddfac585dfe1fdb401d16b56a80bf1a645d53d7df633a8a527b2bc56fec57
|
7
|
+
data.tar.gz: 2db1f5515cc9b3570d629a0a9149883b7691d4bcd3ebfb8e7383604665bd424b78df968949848c661f5ab22350fc4769ceaa524e74550f6402c20c759ab9e212
|
@@ -120,6 +120,25 @@ class JobInvocationsController < ApplicationController
|
|
120
120
|
redirect_back(:fallback_location => job_invocation_path(@job_invocation))
|
121
121
|
end
|
122
122
|
|
123
|
+
def preview_job_invocations_per_host
|
124
|
+
job_invocations = resource_base.search_for("targeted_host_id = #{params[:host_id]} and (status=#{params[:status]})").limit(params[:limit] || 3)
|
125
|
+
|
126
|
+
job_invocations = job_invocations.map do |job|
|
127
|
+
@job_invocation = job
|
128
|
+
template_invocation = job.template_invocations.find { |template_inv| template_inv.host_id == params[:host_id].to_i }
|
129
|
+
task = template_invocation.try(:run_host_job_task)
|
130
|
+
status_mapper = task ? HostStatus::ExecutionStatus::ExecutionTaskStatusMapper.new(task) : job
|
131
|
+
{
|
132
|
+
start_at: job.start_at,
|
133
|
+
description: job.description,
|
134
|
+
id: job.id,
|
135
|
+
status: status_mapper.status,
|
136
|
+
status_label: status_mapper.status_label,
|
137
|
+
}
|
138
|
+
end
|
139
|
+
render :json => {:job_invocations => job_invocations}
|
140
|
+
end
|
141
|
+
|
123
142
|
private
|
124
143
|
|
125
144
|
def action_permission
|
@@ -130,7 +149,7 @@ class JobInvocationsController < ApplicationController
|
|
130
149
|
'create'
|
131
150
|
when 'cancel'
|
132
151
|
'cancel'
|
133
|
-
when 'chart'
|
152
|
+
when 'chart', 'preview_job_invocations_per_host'
|
134
153
|
'view'
|
135
154
|
else
|
136
155
|
super
|
@@ -68,9 +68,6 @@ class UiJobWizardController < ApplicationController
|
|
68
68
|
job_organization = Taxonomy.find_by(id: job.task.input[:current_organization_id])
|
69
69
|
job_location = Taxonomy.find_by(id: job.task.input[:current_location_id])
|
70
70
|
render :json => {
|
71
|
-
:provider_input_values => composer[:template_invocations][0][:provider_input_values],
|
72
|
-
:provider_input_values1 => job[:provider_input_values],
|
73
|
-
:job2 => composer[:template_invocations][0],
|
74
71
|
:job => composer,
|
75
72
|
:job_organization => job_organization,
|
76
73
|
:job_location => job_location,
|
@@ -238,7 +238,7 @@ module RemoteExecutionHelper
|
|
238
238
|
|
239
239
|
def load_template_from_task(template_invocation, target)
|
240
240
|
task = template_invocation.job_invocation.sub_task_for_host(target)
|
241
|
-
return if
|
241
|
+
return if [nil, 'scheduled', 'planning'].include?(task&.state)
|
242
242
|
|
243
243
|
task.execution_plan.actions[1].try(:input).try(:[], 'script')
|
244
244
|
end
|
@@ -180,7 +180,7 @@ module Actions
|
|
180
180
|
end
|
181
181
|
|
182
182
|
def exit_status
|
183
|
-
input[:with_event_logging] ? task.template_invocation.template_invocation_events.find_by(event_type: 'exit')
|
183
|
+
input[:with_event_logging] ? task.template_invocation.template_invocation_events.find_by(event_type: 'exit')&.event : delegated_output[:exit_status]
|
184
184
|
end
|
185
185
|
|
186
186
|
def host_id
|
@@ -127,7 +127,7 @@
|
|
127
127
|
</div>
|
128
128
|
|
129
129
|
<div class="form-group">
|
130
|
-
<%= add_label({ :label => _('Type of query'), :label_help => _("Type has impact on when is the query evaluated to hosts.<br><ul><li><b>Static</b> - evaluates just after you submit this form</li><li><b>Dynamic</b> - evaluates just before the execution is started, so if it's
|
130
|
+
<%= add_label({ :label => _('Type of query'), :label_help => _("Type has impact on when is the query evaluated to hosts.<br><ul><li><b>Static</b> - evaluates just after you submit this form</li><li><b>Dynamic</b> - evaluates just before the execution is started, so if it's planned in future, targeted hosts set may change before it</li></ul>") }, f, :targetting_type) %>
|
131
131
|
|
132
132
|
<div class="col-md-4">
|
133
133
|
<%= radio_button_f targeting_fields, :targeting_type, :value => Targeting::STATIC_TYPE, :text => _(Targeting::TYPES[Targeting::STATIC_TYPE]) %>
|
data/config/routes.rb
CHANGED
@@ -23,6 +23,7 @@ Rails.application.routes.draw do
|
|
23
23
|
match 'old/job_invocations/:id/rerun', to: 'job_invocations#rerun', via: [:get, :post], as: 'form_rerun_job_invocation'
|
24
24
|
resources :job_invocations, :only => [:create, :show, :index] do
|
25
25
|
collection do
|
26
|
+
get 'preview_job_invocations_per_host'
|
26
27
|
post 'refresh'
|
27
28
|
get 'chart'
|
28
29
|
get 'preview_hosts'
|
@@ -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
|
@@ -183,7 +183,7 @@ module ForemanRemoteExecution
|
|
183
183
|
permission :lock_job_templates, { :job_templates => [:lock, :unlock] }, :resource_type => 'JobTemplate'
|
184
184
|
permission :create_job_invocations, { :job_invocations => [:new, :create, :refresh, :rerun, :preview_hosts],
|
185
185
|
'api/v2/job_invocations' => [:create, :rerun] }, :resource_type => 'JobInvocation'
|
186
|
-
permission :view_job_invocations, { :job_invocations => [:index, :chart, :show, :auto_complete_search], :template_invocations => [:show],
|
186
|
+
permission :view_job_invocations, { :job_invocations => [:index, :chart, :show, :auto_complete_search, :preview_job_invocations_per_host], :template_invocations => [:show],
|
187
187
|
'api/v2/job_invocations' => [:index, :show, :output, :raw_output, :outputs] }, :resource_type => 'JobInvocation'
|
188
188
|
permission :view_template_invocations, { :template_invocations => [:show],
|
189
189
|
'api/v2/template_invocations' => [:template_invocations], :ui_job_wizard => [:job_invocation] }, :resource_type => 'TemplateInvocation'
|
@@ -23,7 +23,7 @@ export const SCHEDULE_TYPES = {
|
|
23
23
|
};
|
24
24
|
|
25
25
|
export const WIZARD_TITLES = {
|
26
|
-
categoryAndTemplate: __('Category and
|
26
|
+
categoryAndTemplate: __('Category and template'),
|
27
27
|
hostsAndInputs: __('Target hosts and inputs'),
|
28
28
|
advanced: __('Advanced fields'),
|
29
29
|
schedule: __('Schedule'),
|
@@ -48,8 +48,11 @@ export const useAutoFill = ({
|
|
48
48
|
})
|
49
49
|
);
|
50
50
|
}
|
51
|
-
if (search && !hostIds?.length) {
|
52
|
-
|
51
|
+
if ((search || search === '') && !hostIds?.length) {
|
52
|
+
// replace an empty string search with a dummy search query to match all hosts
|
53
|
+
// but only if search query was entered (based on presence of :search parameter)
|
54
|
+
const hostSearch = search === '' ? "name != ''" : search;
|
55
|
+
setHostsSearchQuery(hostSearch);
|
53
56
|
}
|
54
57
|
if (templateID) {
|
55
58
|
setJobTemplateID(+templateID);
|
@@ -91,7 +91,7 @@ describe('Hosts', () => {
|
|
91
91
|
expect(screen.queryAllByText('host_collection1')).toHaveLength(1);
|
92
92
|
|
93
93
|
await act(async () => {
|
94
|
-
fireEvent.click(screen.getByText('Category and
|
94
|
+
fireEvent.click(screen.getByText('Category and template'));
|
95
95
|
});
|
96
96
|
await act(async () => {
|
97
97
|
fireEvent.click(screen.getByText('Target hosts and inputs'));
|
@@ -31,7 +31,7 @@ export const QueryType = ({ isTypeStatic, setIsTypeStatic }) => (
|
|
31
31
|
id="query-type-dynamic"
|
32
32
|
label={__('Dynamic query')}
|
33
33
|
body={__(
|
34
|
-
"evaluates just before the execution is started, so if it's
|
34
|
+
"evaluates just before the execution is started, so if it's planned in future, targeted hosts set may change before it"
|
35
35
|
)}
|
36
36
|
/>
|
37
37
|
</FormGroup>
|
@@ -47,7 +47,6 @@ export const RepeatHour = ({ repeatData, setRepeatData }) => {
|
|
47
47
|
}}
|
48
48
|
isOpen={minuteOpen}
|
49
49
|
width={125}
|
50
|
-
menuAppendTo={() => document.querySelector('.pf-c-form.schedule-tab')}
|
51
50
|
toggleAriaLabel="select minute toggle"
|
52
51
|
validated={
|
53
52
|
isValidMinute(minute)
|
@@ -139,7 +139,7 @@ describe('Schedule', () => {
|
|
139
139
|
});
|
140
140
|
|
141
141
|
act(() => {
|
142
|
-
fireEvent.click(screen.getByText('Category and
|
142
|
+
fireEvent.click(screen.getByText('Category and template'));
|
143
143
|
});
|
144
144
|
act(() => {
|
145
145
|
fireEvent.click(screen.getByRole('button', { name: 'Future execution' }));
|
@@ -246,7 +246,7 @@ describe('Schedule', () => {
|
|
246
246
|
});
|
247
247
|
|
248
248
|
act(() => {
|
249
|
-
fireEvent.click(screen.getByText('Category and
|
249
|
+
fireEvent.click(screen.getByText('Category and template'));
|
250
250
|
});
|
251
251
|
act(() => {
|
252
252
|
fireEvent.click(
|
@@ -306,9 +306,9 @@ describe('Schedule', () => {
|
|
306
306
|
expect(screen.getByText('Review details').disabled).toBeFalsy();
|
307
307
|
|
308
308
|
await act(async () => {
|
309
|
-
fireEvent.click(screen.getByText('Category and
|
309
|
+
fireEvent.click(screen.getByText('Category and template'));
|
310
310
|
});
|
311
|
-
expect(screen.getAllByText('Category and
|
311
|
+
expect(screen.getAllByText('Category and template')).toHaveLength(3);
|
312
312
|
|
313
313
|
await act(async () => {
|
314
314
|
fireEvent.click(
|
@@ -43,7 +43,6 @@ export const SelectField = ({
|
|
43
43
|
isOpen={isOpen}
|
44
44
|
className="without_select2"
|
45
45
|
maxHeight="45vh"
|
46
|
-
menuAppendTo={() => document.body}
|
47
46
|
placeholderText=" " // To prevent showing first option as selected
|
48
47
|
aria-labelledby={fieldId}
|
49
48
|
toggleAriaLabel={`${label} toggle`}
|
@@ -25,7 +25,7 @@ const RecentJobsCard = ({ hostDetails: { name, id } }) => {
|
|
25
25
|
header={__('Recent jobs')}
|
26
26
|
dropdownItems={[
|
27
27
|
<DropdownItem
|
28
|
-
href={foremanUrl(`${JOB_BASE_URL}${
|
28
|
+
href={foremanUrl(`${JOB_BASE_URL}${id}`)}
|
29
29
|
key="link-to-all"
|
30
30
|
ouiaId="link-to-all-dropdown-item"
|
31
31
|
>
|
@@ -33,7 +33,7 @@ const RecentJobsCard = ({ hostDetails: { name, id } }) => {
|
|
33
33
|
</DropdownItem>,
|
34
34
|
<DropdownItem
|
35
35
|
href={foremanUrl(
|
36
|
-
`${JOB_BASE_URL}${
|
36
|
+
`${JOB_BASE_URL}${id}+and+status+%3D+failed+or+status%3D+succeeded`
|
37
37
|
)}
|
38
38
|
key="link-to-finished"
|
39
39
|
ouiaId="link-to-finished-dropdown-item"
|
@@ -41,14 +41,14 @@ const RecentJobsCard = ({ hostDetails: { name, id } }) => {
|
|
41
41
|
{__('View finished jobs')}
|
42
42
|
</DropdownItem>,
|
43
43
|
<DropdownItem
|
44
|
-
href={foremanUrl(`${JOB_BASE_URL}${
|
44
|
+
href={foremanUrl(`${JOB_BASE_URL}${id}+and+status+%3D+running`)}
|
45
45
|
key="link-to-running"
|
46
46
|
ouiaId="link-to-running-dropdown-item"
|
47
47
|
>
|
48
48
|
{__('View running jobs')}
|
49
49
|
</DropdownItem>,
|
50
50
|
<DropdownItem
|
51
|
-
href={foremanUrl(`${JOB_BASE_URL}${
|
51
|
+
href={foremanUrl(`${JOB_BASE_URL}${id}+and+status+%3D+queued`)}
|
52
52
|
key="link-to-scheduled"
|
53
53
|
ouiaId="link-to-scheduled-dropdown-item"
|
54
54
|
>
|
@@ -17,10 +17,10 @@ const RecentJobsTable = ({ status, hostId }) => {
|
|
17
17
|
const jobsUrl =
|
18
18
|
hostId &&
|
19
19
|
foremanUrl(
|
20
|
-
`${JOB_API_URL}${hostId}
|
20
|
+
`${JOB_API_URL}${hostId}&status=${status}&limit=${JOBS_IN_CARD}`
|
21
21
|
);
|
22
22
|
const {
|
23
|
-
response: {
|
23
|
+
response: { job_invocations: jobs },
|
24
24
|
status: responseStatus,
|
25
25
|
} = useAPI('get', jobsUrl, RECENT_JOBS_KEY);
|
26
26
|
|
@@ -6,8 +6,8 @@ export const SCHEDULED_TAB = 2;
|
|
6
6
|
export const JOB_SUCCESS_STATUS = 0;
|
7
7
|
export const JOB_ERROR_STATUS = 1;
|
8
8
|
|
9
|
-
export const JOB_BASE_URL = '/job_invocations?search=
|
9
|
+
export const JOB_BASE_URL = '/job_invocations?search=targeted_host_id+%3D+';
|
10
10
|
export const JOB_API_URL =
|
11
|
-
'/
|
11
|
+
'/job_invocations/preview_job_invocations_per_host?host_id=';
|
12
12
|
export const JOBS_IN_CARD = 3;
|
13
13
|
export const RECENT_JOBS_KEY = { key: 'RECENT_JOBS_KEY' };
|
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: 8.
|
4
|
+
version: 8.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Foreman Remote Execution team
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-08-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: deface
|
@@ -543,7 +543,7 @@ homepage: https://github.com/theforeman/foreman_remote_execution
|
|
543
543
|
licenses:
|
544
544
|
- GPL-3.0
|
545
545
|
metadata: {}
|
546
|
-
post_install_message:
|
546
|
+
post_install_message:
|
547
547
|
rdoc_options: []
|
548
548
|
require_paths:
|
549
549
|
- lib
|
@@ -558,8 +558,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
558
558
|
- !ruby/object:Gem::Version
|
559
559
|
version: '0'
|
560
560
|
requirements: []
|
561
|
-
rubygems_version: 3.
|
562
|
-
signing_key:
|
561
|
+
rubygems_version: 3.1.6
|
562
|
+
signing_key:
|
563
563
|
specification_version: 4
|
564
564
|
summary: A plugin bringing remote execution to the Foreman, completing the config
|
565
565
|
management functionality with remote management functionality.
|