foreman_remote_execution 8.2.1 → 8.3.1
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/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.
|