foreman_remote_execution 3.3.2 → 4.0.0

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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -1
  3. data/app/assets/stylesheets/foreman_remote_execution/job_invocations.scss +6 -5
  4. data/app/controllers/api/v2/job_invocations_controller.rb +3 -2
  5. data/app/controllers/job_invocations_controller.rb +22 -8
  6. data/app/lib/actions/remote_execution/run_host_job.rb +1 -1
  7. data/app/lib/actions/remote_execution/run_hosts_job.rb +1 -1
  8. data/app/lib/foreman_remote_execution/renderer/scope/input.rb +35 -0
  9. data/app/models/concerns/foreman_remote_execution/orchestration/ssh.rb +11 -4
  10. data/app/models/job_invocation.rb +6 -3
  11. data/app/models/job_invocation_composer.rb +2 -2
  12. data/app/models/remote_execution_provider.rb +2 -2
  13. data/app/models/setting/remote_execution.rb +2 -2
  14. data/app/models/ssh_execution_provider.rb +1 -1
  15. data/app/services/default_proxy_proxy_selector.rb +3 -1
  16. data/app/views/api/v2/job_invocations/main.json.rabl +2 -2
  17. data/app/views/job_invocations/_card_target_hosts.html.erb +1 -1
  18. data/app/views/job_invocations/_form.html.erb +1 -1
  19. data/app/views/job_invocations/_tab_hosts.html.erb +1 -20
  20. data/app/views/job_invocations/index.html.erb +2 -1
  21. data/app/views/job_invocations/show.html.erb +9 -0
  22. data/app/views/job_invocations/show.js.erb +5 -0
  23. data/app/views/job_invocations/show.json.erb +2 -1
  24. data/app/views/templates/ssh/package_action.erb +1 -0
  25. data/app/views/templates/ssh/puppet_agent_disable.erb +3 -0
  26. data/app/views/templates/ssh/puppet_agent_enable.erb +3 -0
  27. data/app/views/templates/ssh/puppet_install_modules_from_forge.erb +3 -0
  28. data/app/views/templates/ssh/puppet_run_once.erb +3 -0
  29. data/db/migrate/20200623073022_rename_sudo_password_to_effective_user_password.rb +34 -0
  30. data/db/seeds.d/20-permissions.rb +9 -0
  31. data/lib/foreman_remote_execution/engine.rb +4 -1
  32. data/lib/foreman_remote_execution/version.rb +1 -1
  33. data/test/functional/api/v2/job_invocations_controller_test.rb +65 -2
  34. data/test/functional/job_invocations_controller_test.rb +71 -0
  35. data/test/models/orchestration/ssh_test.rb +1 -1
  36. data/test/support/remote_execution_helper.rb +5 -0
  37. data/test/unit/actions/run_host_job_test.rb +3 -3
  38. data/test/unit/actions/run_hosts_job_test.rb +1 -1
  39. data/test/unit/job_invocation_composer_test.rb +5 -5
  40. data/test/unit/remote_execution_provider_test.rb +6 -6
  41. data/webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js +2 -0
  42. data/webpack/__mocks__/foremanReact/components/SearchBar.js +2 -0
  43. data/webpack/__mocks__/foremanReact/constants.js +21 -0
  44. data/webpack/__mocks__/foremanReact/redux/API/APISelectors.js +2 -0
  45. data/webpack/__mocks__/foremanReact/redux/middlewares/IntervalMiddleware/IntervalSelectors.js +1 -0
  46. data/webpack/react_app/components/TargetingHosts/TargetingHosts.js +21 -15
  47. data/webpack/react_app/components/TargetingHosts/TargetingHostsHelpers.js +10 -0
  48. data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.js +62 -0
  49. data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.scss +6 -0
  50. data/webpack/react_app/components/TargetingHosts/TargetingHostsSelectors.js +10 -2
  51. data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsPage.test.js +9 -0
  52. data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsSelectors.test.js +26 -0
  53. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHosts.test.js.snap +16 -1
  54. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +68 -0
  55. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsSelectors.test.js.snap +11 -0
  56. data/webpack/react_app/components/TargetingHosts/__tests__/fixtures.js +35 -19
  57. data/webpack/react_app/components/TargetingHosts/index.js +73 -13
  58. metadata +18 -3
  59. data/webpack/react_app/components/TargetingHosts/TargetingHostsActions.js +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d093d837d0009e477549fba4700f3e0090045ba23f8b2c50fb05439fee53e403
4
- data.tar.gz: 54dffc56caa3fa1bb72bb159b152d267d2e348b64b57322ea74cdf0ad9289cc1
3
+ metadata.gz: 93860edb8974a0691383b3a199a1a2d079d10401d7780b99aa6275b75125eb58
4
+ data.tar.gz: d333f8eb85a42b36694dee932447d4364b57ed2c095ad5a055d1e7adf69a9866
5
5
  SHA512:
6
- metadata.gz: e3524503031c73f86a0f3ed0b6c8dd39499f85df47052eca941f455552665b93ff041e5d6df9870cfb95ac1219017a561a483556306905bdc005caf252a5e088
7
- data.tar.gz: 537da427406ce7c794d5e589e7e82f8c6fbda33a3a4231129905b69171421f41e98563c0496d9c7cc748f675d200729ec52f4d9067a8f7879972922358a2a868
6
+ metadata.gz: 70c300e719d6587639719a4d95aa23da2d3f3a16d51692392c789b70cd381b31320789db0f45b57c129e4b90d36bd1a8ddadd29db4f38f2d9e40a39219b1d130
7
+ data.tar.gz: 1feab0c5cfd3fee6054a004eba812b7257df80a2dce50aa200d15dd20797f24f8a614d802668ac6daf043c189b7caa8e8d5f0d4a997726764c80c91173d9437b
@@ -30,10 +30,11 @@ jobs:
30
30
  strategy:
31
31
  fail-fast: false
32
32
  matrix:
33
- foreman-core-branch: [2.1-stable, develop]
33
+ foreman-core-branch: [develop]
34
34
  ruby-version: [2.5, 2.6]
35
35
  node-version: [12]
36
36
  steps:
37
+ - run: sudo apt-get update
37
38
  - run: sudo apt-get install build-essential libcurl4-openssl-dev zlib1g-dev libpq-dev
38
39
  - uses: actions/checkout@v2
39
40
  with:
@@ -1,9 +1,6 @@
1
- div.infoblock {
2
- margin-bottom: 20px;
3
- line-height: 2;
4
-
1
+ .target-hosts-card {
5
2
  pre {
6
- margin-top: 5px;
3
+ white-space:pre-line;
7
4
  }
8
5
  }
9
6
 
@@ -29,3 +26,7 @@ div.infoblock {
29
26
  }
30
27
  }
31
28
  }
29
+
30
+ .text_warp{
31
+ word-wrap: break-word;
32
+ }
@@ -18,14 +18,14 @@ module Api
18
18
 
19
19
  api :GET, '/job_invocations/:id', N_('Show job invocation')
20
20
  param :id, :identifier, :required => true
21
- param :host_status, String, required: false, allow_blank: true, desc: N_('Show Job status for the hosts.')
21
+ param :host_status, :bool, required: false, desc: N_('Show Job status for the hosts')
22
22
  def show
23
23
  @hosts = @job_invocation.targeting.hosts.authorized(:view_hosts, Host)
24
24
  @template_invocations = @job_invocation.template_invocations
25
25
  .where(host: @hosts)
26
26
  .includes(:input_values)
27
27
 
28
- if params[:host_status]
28
+ if params[:host_status] == 'true'
29
29
  template_invocations = @template_invocations.includes(:run_host_job_task).to_a
30
30
  @host_statuses = Hash[template_invocations.map { |ti| [ti.host_id, template_invocation_status(ti)] }]
31
31
  end
@@ -80,6 +80,7 @@ module Api
80
80
  end
81
81
  composer.trigger!
82
82
  @job_invocation = composer.job_invocation
83
+ @hosts = @job_invocation.targeting.hosts
83
84
  process_response @job_invocation
84
85
  end
85
86
 
@@ -52,16 +52,18 @@ class JobInvocationsController < ApplicationController
52
52
 
53
53
  def show
54
54
  @job_invocation = resource_base.includes(:template_invocations => :run_host_job_task).find(params[:id])
55
- @auto_refresh = @job_invocation.task.try(:pending?)
56
- @resource_base = @job_invocation.targeting.hosts.authorized(:view_hosts, Host)
57
- # There's no need to do the joining if we're not filtering
58
- unless params[:search].nil?
59
- @resource_base = @resource_base.joins(:template_invocations)
60
- .where(:template_invocations => { :job_invocation_id => @job_invocation.id})
61
- end
62
- @hosts = resource_base_search_and_page
63
55
  @job_organization = Taxonomy.find_by(id: @job_invocation.task.input[:current_organization_id])
64
56
  @job_location = Taxonomy.find_by(id: @job_invocation.task.input[:current_location_id])
57
+ @auto_refresh = @job_invocation.task.try(:pending?)
58
+
59
+ respond_to do |format|
60
+ format.json do
61
+ targeting_hosts_resources
62
+ end
63
+
64
+ format.html
65
+ format.js
66
+ end
65
67
  end
66
68
 
67
69
  def index
@@ -153,4 +155,16 @@ class JobInvocationsController < ApplicationController
153
155
  JobInvocationComposer.from_ui_params(with_triggering)
154
156
  end
155
157
  end
158
+
159
+ def targeting_hosts_resources
160
+ @auto_refresh = @job_invocation.task.try(:pending?)
161
+ @resource_base = @job_invocation.targeting.hosts.authorized(:view_hosts, Host)
162
+
163
+ unless params[:search].nil?
164
+ @resource_base = @resource_base.joins(:template_invocations)
165
+ .where(:template_invocations => { :job_invocation_id => @job_invocation.id})
166
+ end
167
+ @hosts = resource_base_search_and_page
168
+ @total_hosts = resource_base_with_search.size
169
+ end
156
170
  end
@@ -60,7 +60,7 @@ module Actions
60
60
  def secrets(host, job_invocation, provider)
61
61
  job_secrets = { :ssh_password => job_invocation.password,
62
62
  :key_passphrase => job_invocation.key_passphrase,
63
- :sudo_password => job_invocation.sudo_password }
63
+ :effective_user_password => job_invocation.effective_user_password }
64
64
 
65
65
  job_secrets.merge(provider.secrets(host)) { |_key, job_secret, provider_secret| job_secret || provider_secret }
66
66
  end
@@ -47,7 +47,7 @@ module Actions
47
47
  end
48
48
 
49
49
  def finalize
50
- job_invocation.password = job_invocation.key_passphrase = job_invocation.sudo_password = nil
50
+ job_invocation.password = job_invocation.key_passphrase = job_invocation.effective_user_password = nil
51
51
  job_invocation.save!
52
52
 
53
53
  Rails.logger.debug "cleaning cache for keys that begin with 'job_invocation_#{job_invocation.id}'"
@@ -3,14 +3,33 @@ module ForemanRemoteExecution
3
3
  module Scope
4
4
  class Input < ::Foreman::Renderer::Scope::Template
5
5
  include Foreman::Renderer::Scope::Macros::HostTemplate
6
+ extend ApipieDSL::Class
6
7
 
7
8
  attr_reader :template, :host, :invocation, :input_template_instance, :current_user
8
9
  delegate :input, to: :input_template_instance
9
10
 
11
+ apipie :class, 'Macros related to template rendering' do
12
+ name 'Template Input Render'
13
+ sections only: %w[all jobs]
14
+ end
15
+
16
+ apipie :method, 'Always raises an error with a description provided as an argument' do
17
+ desc 'This method is useful for aborting script execution if some of the conditions are not met'
18
+ required :message, String, desc: 'Description for the error'
19
+ raises error: ::InputTemplateRenderer::RenderError, desc: 'The error is always being raised'
20
+ returns nil, desc: "Doesn't return anything"
21
+ example "<%
22
+ @host.operatingsystem #=> nil
23
+ render_error(N_('Unsupported or no operating system found for this host.')) unless @host.operatingsystem #=> InputTemplateRenderer::RenderError is raised and the execution of the script is aborted
24
+ %>"
25
+ end
10
26
  def render_error(message)
11
27
  raise ::InputTemplateRenderer::RenderError.new(message)
12
28
  end
13
29
 
30
+ apipie :method, 'Check whether the template in preview mode or not' do
31
+ returns one_of: [true, false], desc: 'Returns true if the template in preview mode, false otherwise'
32
+ end
14
33
  def preview?
15
34
  !!@preview
16
35
  end
@@ -23,6 +42,16 @@ module ForemanRemoteExecution
23
42
  Rails.cache.fetch(cache_key, &block)
24
43
  end
25
44
 
45
+ # rubocop:disable Lint/InterpolationCheck
46
+ apipie :method, 'Render template by given name' do
47
+ required :template_name, String, desc: 'name of the template to render'
48
+ optional :input_values, Hash, desc: 'key:value list of input values for the template'
49
+ optional :options, Hash, desc: 'Additional options such as :with_foreign_input_set. Set to true if a foreign input set should be considered when rendering the template'
50
+ raises error: StandardError, desc: 'raises an error if there is no template or template input with such name'
51
+ returns String, desc: 'Rendered template'
52
+ example '<%= render_template("Run Command - Ansible Default", command: "yum -y group install #{input("package")}") %>'
53
+ end
54
+ # rubocop:enable Lint/InterpolationCheck
26
55
  def render_template(template_name, input_values = {}, options = {})
27
56
  options.assert_valid_keys(:with_foreign_input_set)
28
57
  with_foreign_input_set = options.fetch(:with_foreign_input_set, true)
@@ -59,6 +88,12 @@ module ForemanRemoteExecution
59
88
  input_values.merge(overrides).with_indifferent_access
60
89
  end
61
90
 
91
+ apipie :method, 'Returns the value of template input' do
92
+ required :name, String, desc: 'name of the template input'
93
+ raises error: UndefinedInput, desc: 'when there is no input with such name defined for the current template'
94
+ returns Object, desc: 'The value of template input'
95
+ example 'input("Include Facts") #=> "yes"'
96
+ end
62
97
  def input(name)
63
98
  return template_input_values[name.to_s] if template_input_values.key?(name.to_s)
64
99
 
@@ -15,9 +15,13 @@ module ForemanRemoteExecution
15
15
  proxy = ::SmartProxy.find(proxy_id)
16
16
  begin
17
17
  proxy.drop_host_from_known_hosts(target)
18
- rescue RestClient::ResourceNotFound => e
19
- # ignore 404 when known_hosts entry is missing or the module was not enabled
20
- Foreman::Logging.exception "Proxy failed to delete SSH known_hosts for #{name}, #{ip}", e, :level => :error
18
+ rescue ::ProxyAPI::ProxyException => e
19
+ if e.wrapped_exception.is_a?(RestClient::NotFound)
20
+ # ignore 404 when known_hosts entry is missing or the module was not enabled
21
+ Foreman::Logging.exception "Proxy failed to delete SSH known_hosts for #{name}, #{ip}", e, :level => :error
22
+ else
23
+ raise e
24
+ end
21
25
  rescue => e
22
26
  Rails.logger.warn e.message
23
27
  return false
@@ -29,7 +33,10 @@ module ForemanRemoteExecution
29
33
  logger.debug "Scheduling SSH known_hosts cleanup"
30
34
 
31
35
  host, _kind, _target = host_kind_target
32
- proxies = host.remote_execution_proxies('SSH').values
36
+ # #remote_execution_proxies may not be defined on the host object in some case
37
+ # for example Host::Discovered does not have it defined, even though these hosts
38
+ # have Nic::Managed interfaces associated with them
39
+ proxies = (host.try(:remote_execution_proxies, 'SSH') || {}).values
33
40
  proxies.flatten.uniq.each do |proxy|
34
41
  queue.create(id: queue_id(proxy.id), name: _("Remove SSH known hosts for %s") % self,
35
42
  priority: 200, action: [self, :drop_from_known_hosts, proxy.id])
@@ -41,7 +41,10 @@ class JobInvocation < ApplicationRecord
41
41
  has_many :template_invocation_tasks, :through => :template_invocations,
42
42
  :class_name => 'ForemanTasks::Task',
43
43
  :source => 'run_host_job_task'
44
-
44
+ has_one :user, through: :task
45
+ scoped_search relation: :user, on: :login, rename: 'user', complete_value: true,
46
+ value_translation: ->(value) { value == 'current_user' ? User.current.login : value },
47
+ special_values: [:current_user], aliases: ['owner'], :only_explicit => true
45
48
  scoped_search :relation => :task, :on => :started_at, :rename => 'started_at', :complete_value => true
46
49
  scoped_search :relation => :task, :on => :start_at, :rename => 'start_at', :complete_value => true
47
50
  scoped_search :relation => :task, :on => :ended_at, :rename => 'ended_at', :complete_value => true
@@ -70,7 +73,7 @@ class JobInvocation < ApplicationRecord
70
73
 
71
74
  delegate :start_at, :to => :task, :allow_nil => true
72
75
 
73
- encrypts :password, :key_passphrase, :sudo_password
76
+ encrypts :password, :key_passphrase, :effective_user_password
74
77
 
75
78
  def self.search_by_status(key, operator, value)
76
79
  conditions = HostStatus::ExecutionStatus::ExecutionTaskStatusMapper.sql_conditions_for(value)
@@ -139,7 +142,7 @@ class JobInvocation < ApplicationRecord
139
142
  invocation.pattern_template_invocations = self.pattern_template_invocations.map(&:deep_clone)
140
143
  invocation.password = self.password
141
144
  invocation.key_passphrase = self.key_passphrase
142
- invocation.sudo_password = self.sudo_password
145
+ invocation.effective_user_password = self.effective_user_password
143
146
  end
144
147
  end
145
148
 
@@ -15,7 +15,7 @@ class JobInvocationComposer
15
15
  :description_format => job_invocation_base[:description_format],
16
16
  :password => blank_to_nil(job_invocation_base[:password]),
17
17
  :key_passphrase => blank_to_nil(job_invocation_base[:key_passphrase]),
18
- :sudo_password => blank_to_nil(job_invocation_base[:sudo_password]),
18
+ :effective_user_password => blank_to_nil(job_invocation_base[:effective_user_password]),
19
19
  :concurrency_control => concurrency_control_params,
20
20
  :execution_timeout_interval => execution_timeout_interval,
21
21
  :template_invocations => template_invocations_params }.with_indifferent_access
@@ -348,7 +348,7 @@ class JobInvocationComposer
348
348
  job_invocation.execution_timeout_interval = params[:execution_timeout_interval]
349
349
  job_invocation.password = params[:password]
350
350
  job_invocation.key_passphrase = params[:key_passphrase]
351
- job_invocation.sudo_password = params[:sudo_password]
351
+ job_invocation.effective_user_password = params[:effective_user_password]
352
352
 
353
353
  if @reruns && job_invocation.targeting.static?
354
354
  job_invocation.targeting.host_ids = JobInvocation.find(@reruns).targeting.host_ids
@@ -57,8 +57,8 @@ class RemoteExecutionProvider
57
57
  [true, 'true', 'True', 'TRUE', '1'].include?(setting)
58
58
  end
59
59
 
60
- def sudo_password(host)
61
- host_setting(host, :remote_execution_sudo_password)
60
+ def effective_user_password(host)
61
+ host_setting(host, :remote_execution_effective_user_password)
62
62
  end
63
63
 
64
64
  def effective_interfaces(host)
@@ -1,6 +1,6 @@
1
1
  class Setting::RemoteExecution < Setting
2
2
 
3
- ::Setting::BLANK_ATTRS.concat %w{remote_execution_ssh_password remote_execution_ssh_key_passphrase remote_execution_sudo_password remote_execution_cockpit_url remote_execution_form_job_template}
3
+ ::Setting::BLANK_ATTRS.concat %w{remote_execution_ssh_password remote_execution_ssh_key_passphrase remote_execution_sudo_password remote_execution_effective_user_password remote_execution_cockpit_url remote_execution_form_job_template}
4
4
 
5
5
  def self.default_settings
6
6
  [
@@ -27,7 +27,7 @@ class Setting::RemoteExecution < Setting
27
27
  N_('Effective User Method'),
28
28
  nil,
29
29
  { :collection => proc { Hash[SSHExecutionProvider::EFFECTIVE_USER_METHODS.map { |method| [method, method] }] } }),
30
- self.set('remote_execution_sudo_password', N_("Sudo password"), '', N_("Sudo password"), nil, {:encrypted => true}),
30
+ self.set('remote_execution_effective_user_password', N_("Effective user password"), '', N_("Effective user password"), nil, {:encrypted => true}),
31
31
  self.set('remote_execution_sync_templates',
32
32
  N_('Whether we should sync templates from disk when running db:seed.'),
33
33
  true,
@@ -32,7 +32,7 @@ class SSHExecutionProvider < RemoteExecutionProvider
32
32
  {
33
33
  :ssh_password => ssh_password(host),
34
34
  :key_passphrase => ssh_key_passphrase(host),
35
- :sudo_password => sudo_password(host),
35
+ :effective_user_password => effective_user_password(host),
36
36
  }
37
37
  end
38
38
 
@@ -10,7 +10,9 @@ class DefaultProxyProxySelector < ::RemoteExecutionProxySelector
10
10
  def available_proxies(host, provider)
11
11
  # TODO: Once we have a internal proxy marker/feature on the proxy, we can
12
12
  # swap the implementation
13
- internal_proxy = ::Katello.default_capsule
13
+ raise _('default_capsule method missing from SmartProxy') unless ::SmartProxy.respond_to?(:default_capsule)
14
+
15
+ internal_proxy = ::SmartProxy.default_capsule
14
16
  super.reduce({}) do |acc, (key, proxies)|
15
17
  acc.merge(key => proxies.select { |proxy| proxy == internal_proxy })
16
18
  end
@@ -19,10 +19,10 @@ child :targeting do
19
19
  attributes :bookmark_id, :search_query, :targeting_type, :user_id, :status, :status_label,
20
20
  :randomized_ordering
21
21
 
22
- child @hosts do
22
+ child @hosts => :hosts do
23
23
  extends 'api/v2/hosts/base'
24
24
 
25
- if params[:host_status]
25
+ if params[:host_status] == 'true'
26
26
  node :job_status do |host|
27
27
  @host_statuses[host.id]
28
28
  end
@@ -1,5 +1,5 @@
1
1
  <% template_invocations = job_invocation.pattern_template_invocations %>
2
- <div class="card-pf card-pf-accented">
2
+ <div class="card-pf card-pf-accented target-hosts-card">
3
3
  <div class="card-pf-title">
4
4
  <h2 style="height: 18px;" class="card-pf-title">
5
5
  <%= _('Target hosts') %>
@@ -95,7 +95,7 @@
95
95
  <div class="advanced hidden">
96
96
  <%= password_f f, :password, :placeholder => '*****', :label => _('Password'), :label_help => N_('Password is stored encrypted in DB until the job finishes. For future or recurring executions, it is removed after the last execution.') %>
97
97
  <%= password_f f, :key_passphrase, :placeholder => '*****', :label => _('Private key passphrase'), :label_help => N_('Key passhprase is only applicable for SSH provider. Other providers ignore this field. <br> Passphrase is stored encrypted in DB until the job finishes. For future or recurring executions, it is removed after the last execution.') %>
98
- <%= password_f f, :sudo_password, :placeholder => '*****', :label => _('Sudo password'), :label_help => N_('Sudo password is only applicable for SSH provider. Other providers ignore this field. <br> Password is stored encrypted in DB until the job finishes. For future or recurring executions, it is removed after the last execution.') %>
98
+ <%= password_f f, :effective_user_password, :placeholder => '*****', :label => _('Effective user password'), :label_help => N_('Effective user password is only applicable for SSH provider. Other providers ignore this field. <br> Password is stored encrypted in DB until the job finishes. For future or recurring executions, it is removed after the last execution.') %>
99
99
  </div>
100
100
 
101
101
  <div class="advanced hidden">
@@ -1,24 +1,5 @@
1
1
  <% if job_invocation.resolved? %>
2
- <%= form_with url: job_invocation_path(job_invocation), method: "get", id: "search-form" do |f| %>
3
- <div class="row">
4
- <div class="title_filter col-md-6">
5
- <div class="input-group">
6
- <%= autocomplete_f(f, :search, value: params[:search].try(:squeeze, " "), placeholder: _("Filter") + ' ...', path: hosts_path, only_input: true) %>
7
- <span class="input-group-btn">
8
- <button class="btn btn-default" type="submit">
9
- <%= icon_text('search', content_tag(:span, _('Search'), :class => 'hidden-xs', :kind => 'fa')) %>
10
- </button>
11
- </span>
12
- </div>
13
- </div>
14
- </div>
15
- <% end %>
16
- <br>
17
-
18
- <div id="targeting_hosts">
19
- <%= mount_react_component('TargetingHosts', '#targeting_hosts') %>
20
- </div>
21
- <%= will_paginate_with_info @hosts, :container => true %>
2
+ <%= react_component('TargetingHosts' ) %>
22
3
  <% else %>
23
4
  <div class="alert alert-warning">
24
5
  <%=
@@ -1,3 +1,4 @@
1
+ <% stylesheet 'foreman_remote_execution/foreman_remote_execution' %>
1
2
  <% title _('Job invocations') %>
2
3
 
3
4
  <% title_actions(job_invocations_buttons) %>
@@ -19,7 +20,7 @@
19
20
  <tbody>
20
21
  <% @job_invocations.each do |invocation| %>
21
22
  <tr>
22
- <td><%= link_to_if_authorized invocation_description(invocation), hash_for_job_invocation_path(invocation).merge(:auth_object => invocation, :permission => :view_job_invocations, :authorizer => authorizer) %></td>
23
+ <td class="text_warp"><%= link_to_if_authorized invocation_description(invocation), hash_for_job_invocation_path(invocation).merge(:auth_object => invocation, :permission => :view_job_invocations, :authorizer => authorizer) %></td>
23
24
  <td><%= trunc_with_tooltip(invocation&.targeting&.search_query, 15) %></td>
24
25
  <td><%= link_to_invocation_task_if_authorized(invocation) %></td>
25
26
  <td><%= invocation_result(invocation, :success_count) %></td>
@@ -2,6 +2,9 @@
2
2
  <% stylesheet 'foreman_remote_execution/foreman_remote_execution' %>
3
3
  <% javascript 'charts', 'foreman_remote_execution/template_invocation' %>
4
4
  <% javascript *webpack_asset_paths('foreman_remote_execution', :extension => 'js') %>
5
+ <% content_for(:stylesheets) do %>
6
+ <%= webpacked_plugins_css_for :foreman_remote_execution %>
7
+ <% end %>
5
8
 
6
9
  <%= breadcrumbs name_field: 'description' %>
7
10
 
@@ -41,3 +44,9 @@
41
44
  <% end %>
42
45
  <%= render_tab_content_for(:main_tabs, subject: @job_invocation) %>
43
46
  </div>
47
+
48
+ <script id="job_invocation_refresh" data-refresh-url="<%= job_invocation_path(@job_invocation) %>">
49
+ <% if @auto_refresh %>
50
+ delayed_refresh($('script#job_invocation_refresh').data('refresh-url'), {});
51
+ <% end %>
52
+ </script>
@@ -0,0 +1,5 @@
1
+ $('div#title_action div.btn-group').html('<%= button_group(job_invocation_task_buttons(@job_invocation.task)).html_safe %>');
2
+
3
+ <% if @auto_refresh %>
4
+ delayed_refresh($('script#job_invocation_refresh').data('refresh-url'), job_invocation_refresh_data());
5
+ <% end %>
@@ -1,4 +1,5 @@
1
1
  {
2
2
  "autoRefresh": "<%= @auto_refresh %>",
3
- "hosts": <%= targeting_hosts(@job_invocation, @hosts).to_json.html_safe %>
3
+ "hosts": <%= targeting_hosts(@job_invocation, @hosts).to_json.html_safe %>,
4
+ "total_hosts": <%= @total_hosts %>
4
5
  }
@@ -97,6 +97,7 @@ handle_zypp_res_codes () {
97
97
  end
98
98
  end
99
99
  -%>
100
+ [ -x "$(command -v subscription-manager)" ] && subscription-manager refresh
100
101
  apt-get -y update
101
102
  apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -y <%= action %> <%= input("package") %>
102
103
  <% elsif package_manager == 'zypper' -%>
@@ -13,4 +13,7 @@ template_inputs:
13
13
  provider_type: SSH
14
14
  kind: job_template
15
15
  -%>
16
+ <% if @host.operatingsystem.family == 'Debian' -%>
17
+ export PATH=/opt/puppetlabs/bin:$PATH
18
+ <% end -%>
16
19
  puppet agent --disable "<%= input("comment").present? ? input("comment") : "Disabled using Foreman Remote Execution" %> - <%= current_user %> - $(date "+%d/%m/%Y %H:%M")"