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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6737da9f14dbea916b2e25a4584fabb33ae69c35bad16eb82d3cc88325996e2e
4
- data.tar.gz: 9c122c05a61199b05fac84b4a2a46adece501bef4e003f8ae81ff83d0b351a5d
3
+ metadata.gz: bb6aa7e8d08233993c7f908975b7688460969ad1182b008ab8697767a772fffc
4
+ data.tar.gz: 8e1096d3d25655600889f7abecd499f7157d344a82f8ead12fc226b4ba180eb4
5
5
  SHA512:
6
- metadata.gz: f2732d87a947c80a8a39acaf8e2314e4833a6e83d76a77506e789467cb489ddcc3a2aacade6a56d91df04c5e54e762bfbc0e07c45c4c430587c998cae253d7f8
7
- data.tar.gz: 872b21b191c597db3789442cc327768f83ea861b718b7a678b83c5964d6a894170454143c5cfd6e43e639239b34613ab09bf158eefdc24756ea07d32a02ce7d2
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 task.nil?
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').event : delegated_output[:exit_status]
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 planed in future, targeted hosts set may change before it</li></ul>") }, f, :targetting_type) %>
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
- message = Cockpit.read_control(StringIO.new(data))
180
- if message.is_a?(Hash) && message['command'] == 'authorize'
181
- response = {
182
- 'command' => 'authorize',
183
- 'cookie' => message['cookie'],
184
- 'response' => @params['effective_user_password'],
185
- }
186
- proxy1.enqueue(Cockpit.encode_message(response))
187
- ''
188
- else
189
- data
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: #{response}"
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'
@@ -1,3 +1,3 @@
1
1
  module ForemanRemoteExecution
2
- VERSION = '8.2.1'.freeze
2
+ VERSION = '8.3.1'.freeze
3
3
  end
@@ -23,7 +23,7 @@ export const SCHEDULE_TYPES = {
23
23
  };
24
24
 
25
25
  export const WIZARD_TITLES = {
26
- categoryAndTemplate: __('Category and Template'),
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
- setHostsSearchQuery(search);
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 Template'));
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 planed in future, targeted hosts set may change before it"
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 Template'));
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 Template'));
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 Template'));
309
+ fireEvent.click(screen.getByText('Category and template'));
310
310
  });
311
- expect(screen.getAllByText('Category and Template')).toHaveLength(3);
311
+ expect(screen.getAllByText('Category and template')).toHaveLength(3);
312
312
 
313
313
  await act(async () => {
314
314
  fireEvent.click(
@@ -119,7 +119,6 @@ export const DateTimePicker = ({
119
119
  is24Hour
120
120
  isDisabled={isDisabled || formattedDate.length === 0}
121
121
  invalidFormatErrorMessage={__('Invalid time format')}
122
- menuAppendTo={() => document.body}
123
122
  includeSeconds={includeSeconds}
124
123
  />
125
124
  </>
@@ -68,7 +68,6 @@ export const GroupedSelectField = ({
68
68
  selections={selected}
69
69
  className="without_select2"
70
70
  onClear={onClear}
71
- menuAppendTo={() => document.body}
72
71
  aria-labelledby={fieldId}
73
72
  toggleAriaLabel={`${label} toggle`}
74
73
  {...props}
@@ -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}${name}`)}
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}${name}+and+status+%3D+failed+or+status%3D+succeeded`
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}${name}+and+status+%3D+running`)}
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}${name}+and+status+%3D+queued`)}
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}+and+status%3D${status}&per_page=${JOBS_IN_CARD}`
20
+ `${JOB_API_URL}${hostId}&status=${status}&limit=${JOBS_IN_CARD}`
21
21
  );
22
22
  const {
23
- response: { results: jobs },
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=host+%3D+';
9
+ export const JOB_BASE_URL = '/job_invocations?search=targeted_host_id+%3D+';
10
10
  export const JOB_API_URL =
11
- '/api/job_invocations?order=start_at+DESC&search=targeted_host_id%3D';
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.2.1
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-02-21 00:00:00.000000000 Z
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.3.20
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.