foreman_remote_execution 3.2.1 → 3.3.3

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc +5 -30
  3. data/.github/workflows/ci.yml +101 -0
  4. data/.gitignore +1 -0
  5. data/.rubocop_todo.yml +3 -0
  6. data/Gemfile +1 -0
  7. data/app/assets/stylesheets/foreman_remote_execution/job_invocations.scss +6 -5
  8. data/app/controllers/api/v2/job_invocations_controller.rb +22 -1
  9. data/app/controllers/api/v2/template_invocations_controller.rb +4 -1
  10. data/app/helpers/job_invocations_helper.rb +1 -1
  11. data/app/helpers/remote_execution_helper.rb +38 -33
  12. data/app/lib/actions/remote_execution/run_host_job.rb +3 -2
  13. data/app/models/concerns/foreman_remote_execution/host_extensions.rb +1 -1
  14. data/app/models/concerns/foreman_remote_execution/orchestration/ssh.rb +10 -5
  15. data/app/models/remote_execution_provider.rb +5 -0
  16. data/app/services/default_proxy_proxy_selector.rb +18 -0
  17. data/app/views/api/v2/job_invocations/main.json.rabl +8 -2
  18. data/app/views/job_invocations/_card_target_hosts.html.erb +1 -1
  19. data/app/views/job_invocations/_tab_hosts.html.erb +3 -23
  20. data/app/views/job_invocations/index.html.erb +2 -1
  21. data/app/views/job_invocations/show.html.erb +0 -6
  22. data/app/views/job_invocations/show.json.erb +4 -0
  23. data/app/views/templates/ssh/package_action.erb +1 -0
  24. data/app/views/templates/ssh/puppet_agent_disable.erb +3 -0
  25. data/app/views/templates/ssh/puppet_agent_enable.erb +3 -0
  26. data/app/views/templates/ssh/puppet_install_modules_from_forge.erb +3 -0
  27. data/app/views/templates/ssh/puppet_run_once.erb +3 -0
  28. data/db/seeds.d/70-job_templates.rb +1 -1
  29. data/foreman_remote_execution.gemspec +4 -5
  30. data/lib/foreman_remote_execution/version.rb +1 -1
  31. data/locale/action_names.rb +0 -1
  32. data/package.json +16 -33
  33. data/test/functional/api/v2/job_invocations_controller_test.rb +42 -14
  34. data/test/models/orchestration/ssh_test.rb +32 -0
  35. data/test/unit/concerns/host_extensions_test.rb +7 -0
  36. data/webpack/__mocks__/foremanReact/common/I18n.js +1 -0
  37. data/webpack/__mocks__/foremanReact/components/common/ActionButtons/ActionButtons.js +3 -0
  38. data/webpack/__mocks__/foremanReact/constants.js +3 -0
  39. data/webpack/index.js +9 -7
  40. data/webpack/react_app/components/TargetingHosts/TargetingHosts.js +52 -0
  41. data/webpack/react_app/components/TargetingHosts/TargetingHostsActions.js +8 -0
  42. data/webpack/react_app/components/TargetingHosts/TargetingHostsConsts.js +1 -0
  43. data/webpack/react_app/components/TargetingHosts/TargetingHostsSelectors.js +12 -0
  44. data/webpack/react_app/components/TargetingHosts/__tests__/HostItem.test.js +6 -0
  45. data/webpack/react_app/components/TargetingHosts/__tests__/HostStatus.test.js +6 -0
  46. data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHosts.test.js +6 -0
  47. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/HostItem.test.js.snap +31 -0
  48. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/HostStatus.test.js.snap +12 -0
  49. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHosts.test.js.snap +81 -0
  50. data/webpack/react_app/components/TargetingHosts/__tests__/fixtures.js +43 -0
  51. data/webpack/react_app/components/TargetingHosts/components/HostItem.js +39 -0
  52. data/webpack/react_app/components/TargetingHosts/components/HostStatus.js +54 -0
  53. data/webpack/react_app/components/TargetingHosts/index.js +37 -0
  54. data/webpack/react_app/components/jobInvocations/AggregateStatus/index.js +10 -0
  55. data/webpack/react_app/components/jobInvocations/AggregateStatus/index.test.js +6 -3
  56. data/webpack/react_app/components/jobInvocations/index.js +19 -7
  57. data/webpack/react_app/redux/actions/jobInvocations/index.js +12 -8
  58. data/webpack/react_app/redux/consts.js +1 -2
  59. data/webpack/react_app/redux/reducers/jobInvocations/index.fixtures.js +8 -40
  60. data/webpack/react_app/redux/reducers/jobInvocations/index.test.js +17 -11
  61. data/webpack/test_setup.js +2 -1
  62. metadata +26 -12
  63. data/.hound.yml +0 -19
  64. data/.travis.yml +0 -6
  65. data/app/views/job_invocations/_host_actions_td.html.erb +0 -3
  66. data/app/views/job_invocations/_host_name_td.html.erb +0 -8
  67. data/app/views/job_invocations/_host_status_td.html.erb +0 -1
  68. data/app/views/job_invocations/show.js.erb +0 -23
@@ -19,8 +19,14 @@ 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 do
23
23
  extends 'api/v2/hosts/base'
24
+
25
+ if params[:host_status] == 'true'
26
+ node :job_status do |host|
27
+ @host_statuses[host.id]
28
+ end
29
+ end
24
30
  end
25
31
  end
26
32
 
@@ -28,7 +34,7 @@ child :task do
28
34
  attributes :id, :state
29
35
  end
30
36
 
31
- child :template_invocations do
37
+ child @template_invocations do
32
38
  attributes :template_id, :template_name
33
39
  child :input_values do
34
40
  attributes :template_input_name, :template_input_id
@@ -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') %>
@@ -15,29 +15,9 @@
15
15
  <% end %>
16
16
  <br>
17
17
 
18
- <table class="<%= table_css_classes('table-condensed') %>">
19
- <thead>
20
- <tr>
21
- <th><%= sort :name, :as => _('Host') %></th>
22
- <th><%= _('Status') %></th>
23
- <th><%= _('Actions') %></th>
24
- </tr>
25
- </thead>
26
-
27
- <tbody>
28
- <% hosts.each do |host| %>
29
- <% template_invocation = job_invocation.template_invocations.find { |template_invocation| template_invocation.host_id == host.id } %>
30
- <% task = template_invocation.try(:run_host_job_task) %>
31
- <tr>
32
- <% options = { :host => host, :task => task, :job_invocation => job_invocation, :template_invocation => template_invocation } %>
33
- <%= render 'host_name_td', options %>
34
- <%= render 'host_status_td', options %>
35
- <%= render 'host_actions_td', options %>
36
- </tr>
37
- <% end %>
38
- </tbody>
39
- </table>
40
-
18
+ <div id="targeting_hosts">
19
+ <%= mount_react_component('TargetingHosts', '#targeting_hosts') %>
20
+ </div>
41
21
  <%= will_paginate_with_info @hosts, :container => true %>
42
22
  <% else %>
43
23
  <div class="alert alert-warning">
@@ -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>
@@ -41,9 +41,3 @@
41
41
  <% end %>
42
42
  <%= render_tab_content_for(:main_tabs, subject: @job_invocation) %>
43
43
  </div>
44
-
45
- <script id="job_invocation_refresh" data-refresh-url="<%= job_invocation_path(@job_invocation) %>">
46
- <% if @auto_refresh %>
47
- delayed_refresh($('script#job_invocation_refresh').data('refresh-url'), job_invocation_refresh_data());
48
- <% end %>
49
- </script>
@@ -0,0 +1,4 @@
1
+ {
2
+ "autoRefresh": "<%= @auto_refresh %>",
3
+ "hosts": <%= targeting_hosts(@job_invocation, @hosts).to_json.html_safe %>
4
+ }
@@ -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")"
@@ -7,4 +7,7 @@ snippet: false
7
7
  provider_type: SSH
8
8
  kind: job_template
9
9
  -%>
10
+ <% if @host.operatingsystem.family == 'Debian' -%>
11
+ export PATH=/opt/puppetlabs/bin:$PATH
12
+ <% end -%>
10
13
  puppet agent --enable
@@ -33,4 +33,7 @@ template_inputs:
33
33
  provider_type: SSH
34
34
  kind: job_template
35
35
  -%>
36
+ <% if @host.operatingsystem.family == 'Debian' -%>
37
+ export PATH=/opt/puppetlabs/bin:$PATH
38
+ <% end -%>
36
39
  puppet module install <%= input('puppet_module') %> <%= "--target-dir #{input('target_dir')}" if input('target_dir').present? %> <%= "--version #{input('version')}" if input('version').present? %> <%= "--force" if input('force') == "true" %> <%= "--ignore-dependencies" if input('ignore_dependencies') == "true" %>
@@ -11,4 +11,7 @@ template_inputs:
11
11
  input_type: user
12
12
  required: false
13
13
  %>
14
+ <% if @host.operatingsystem.family == 'Debian' -%>
15
+ export PATH=/opt/puppetlabs/bin:$PATH
16
+ <% end -%>
14
17
  puppet agent --onetime --no-usecacheonfailure --no-daemonize <%= input("puppet_options") %>
@@ -4,7 +4,7 @@ User.as_anonymous_admin do
4
4
  JobTemplate.without_auditing do
5
5
  Dir[File.join("#{ForemanRemoteExecution::Engine.root}/app/views/templates/**/*.erb")].each do |template|
6
6
  sync = !Rails.env.test? && Setting[:remote_execution_sync_templates]
7
- template = JobTemplate.import_raw!(File.read(template), :default => true, :locked => true, :update => sync)
7
+ template = JobTemplate.import_raw!(File.read(template), :default => true, :lock => true, :update => sync)
8
8
  template.organizations = organizations if template.present?
9
9
  template.locations = locations if template.present?
10
10
  end
@@ -15,10 +15,9 @@ Gem::Specification.new do |s|
15
15
  s.description = 'A plugin bringing remote execution to the Foreman, completing the config ' +
16
16
  'management functionality with remote management functionality.'
17
17
 
18
- s.files = `git ls-files`.split("\n").reject do |file|
19
- file =~ /^scripts/ ||
20
- file.start_with?('lib/foreman_remote_execution_core') ||
21
- file == 'foreman_remote_execution_core.gemspec'
18
+ s.files = `git ls-files`.split("\n").reject do |file|
19
+ file.start_with?('scripts', 'lib/foreman_remote_execution_core') ||
20
+ file == 'foreman_remote_execution_core.gemspec'
22
21
  end
23
22
 
24
23
  s.test_files = `git ls-files test`.split("\n")
@@ -30,6 +29,6 @@ Gem::Specification.new do |s|
30
29
  s.add_dependency 'foreman-tasks', '>= 0.15.1'
31
30
 
32
31
  s.add_development_dependency 'factory_bot_rails', '~> 4.8.0'
33
- s.add_development_dependency 'rubocop'
32
+ s.add_development_dependency 'rubocop', '~> 0.80.0'
34
33
  s.add_development_dependency 'rdoc'
35
34
  end
@@ -1,3 +1,3 @@
1
1
  module ForemanRemoteExecution
2
- VERSION = '3.2.1'.freeze
2
+ VERSION = '3.3.3'.freeze
3
3
  end
@@ -3,4 +3,3 @@ _("Action with sub plans")
3
3
  _("Import facts")
4
4
  _("Import Puppet classes")
5
5
  _("Remote action:")
6
-
@@ -3,25 +3,14 @@
3
3
  "version": "1.0.0",
4
4
  "license": "GPL-3.0",
5
5
  "scripts": {
6
- "lint": "./node_modules/.bin/eslint -c .eslintrc webpack/ script/",
7
- "test": "node node_modules/.bin/jest webpack",
8
- "test:watch": "node node_modules/.bin/jest webpack --watchAll",
9
- "test:current": "node node_modules/.bin/jest webpack --watch"
10
- },
11
- "jest": {
12
- "verbose": true,
13
- "moduleDirectories": [
14
- "node_modules",
15
- "webpack"
16
- ],
17
- "setupFiles": [
18
- "raf/polyfill",
19
- "./webpack/test_setup.js"
20
- ],
21
- "testPathIgnorePatterns": [
22
- "/node_modules/",
23
- "<rootDir>/foreman/"
24
- ]
6
+ "lint": "tfm-lint --plugin -d /webpack",
7
+ "test": "tfm-test --plugin",
8
+ "test:watch": "tfm-test --plugin --watchAll",
9
+ "test:current": "tfm-test --plugin --watch",
10
+ "publish-coverage": "tfm-publish-coverage",
11
+ "stories": "tfm-stories --plugin",
12
+ "stories:build": "tfm-build-stories --plugin",
13
+ "stories:deploy": "surge --project .storybook-dist"
25
14
  },
26
15
  "repository": {
27
16
  "type": "git",
@@ -32,22 +21,16 @@
32
21
  },
33
22
  "devDependencies": {
34
23
  "@babel/core": "^7.7.0",
35
- "@theforeman/builder": "^4.0.2",
36
- "@theforeman/vendor-dev": "^4.0.2",
24
+ "@theforeman/builder": "^4.2.1",
25
+ "@theforeman/eslint-plugin-foreman": "^4.2.1",
26
+ "@theforeman/stories": "^4.2.1",
27
+ "@theforeman/test": "^4.2.1",
28
+ "@theforeman/vendor-dev": "^4.2.1",
37
29
  "babel-eslint": "^10.0.0",
38
- "babel-jest": "^24.9.0",
39
- "enzyme": "^3.2.0",
40
- "enzyme-adapter-react-16": "^1.1.0",
41
- "enzyme-to-json": "^3.1.2",
42
- "eslint": "^4.10.0",
43
- "eslint-config-airbnb": "^16.0.0",
44
- "eslint-plugin-import": "^2.8.0",
45
- "eslint-plugin-jest": "^21.2.0",
46
- "eslint-plugin-jsx-a11y": "^6.0.2",
47
- "eslint-plugin-react": "^7.4.0",
48
- "jest": "^24.9.0"
30
+ "eslint": "^6.8.0",
31
+ "prettier": "^1.19.1"
49
32
  },
50
33
  "peerDependencies": {
51
- "@theforeman/vendor": ">= 4.0.2"
34
+ "@theforeman/vendor": ">= 4.2.1"
52
35
  }
53
36
  }
@@ -4,7 +4,7 @@ module Api
4
4
  module V2
5
5
  class JobInvocationsControllerTest < ActionController::TestCase
6
6
  setup do
7
- @invocation = FactoryBot.create(:job_invocation, :with_template, :with_task)
7
+ @invocation = FactoryBot.create(:job_invocation, :with_template, :with_task, :with_unplanned_host)
8
8
  @template = FactoryBot.create(:job_template, :with_input)
9
9
 
10
10
  # Without this the template in template_invocations and in pattern_template_invocations
@@ -20,18 +20,32 @@ module Api
20
20
  assert_response :success
21
21
  end
22
22
 
23
- test 'should get invocation detail' do
24
- get :show, params: { :id => @invocation.id }
25
- assert_response :success
26
- template = ActiveSupport::JSON.decode(@response.body)
27
- assert_not_empty template
28
- assert_equal template['job_category'], @invocation.job_category
29
- end
23
+ describe 'show' do
24
+ test 'should get invocation detail' do
25
+ get :show, params: { :id => @invocation.id }
26
+ assert_response :success
27
+ template = ActiveSupport::JSON.decode(@response.body)
28
+ assert_not_empty template
29
+ assert_equal template['job_category'], @invocation.job_category
30
+ assert_not_empty template['targeting']['hosts']
31
+ end
30
32
 
31
- test 'should get invocation detail when taxonomies are set' do
32
- taxonomy_params = %w(organization location).reduce({}) { |acc, cur| acc.merge("#{cur}_id" => FactoryBot.create(cur)) }
33
- get :show, params: taxonomy_params.merge(:id => @invocation.id)
34
- assert_response :success
33
+ test 'should get invocation detail when taxonomies are set' do
34
+ taxonomy_params = %w(organization location).reduce({}) { |acc, cur| acc.merge("#{cur}_id" => FactoryBot.create(cur)) }
35
+ get :show, params: taxonomy_params.merge(:id => @invocation.id)
36
+ assert_response :success
37
+ end
38
+
39
+ test 'should see only permitted hosts' do
40
+ @user = FactoryBot.create(:user, admin: false)
41
+ setup_user('view', 'job_invocations', nil, @user)
42
+ setup_user('view', 'hosts', 'name ~ nope.example.com', @user)
43
+
44
+ get :show, params: { :id => @invocation.id }, session: set_session_user(@user)
45
+ assert_response :success
46
+ response = ActiveSupport::JSON.decode(@response.body)
47
+ assert_empty response['targeting']['hosts']
48
+ end
35
49
  end
36
50
 
37
51
  context 'creation' do
@@ -108,7 +122,7 @@ module Api
108
122
  end
109
123
 
110
124
  describe '#output' do
111
- let(:host) { @invocation.template_invocations_hosts.first }
125
+ let(:host) { @invocation.targeting.hosts.first }
112
126
 
113
127
  test 'should provide output for delayed task' do
114
128
  ForemanTasks::Task.any_instance.expects(:scheduled?).returns(true)
@@ -137,6 +151,12 @@ module Api
137
151
  assert_equal result['message'], "Job invocation not found by id '#{invocation_id}'"
138
152
  assert_response :missing
139
153
  end
154
+
155
+ test 'should get output only for host in job invocation' do
156
+ get :output, params: { job_invocation_id: @invocation.id,
157
+ host_id: FactoryBot.create(:host).id }
158
+ assert_response :missing
159
+ end
140
160
  end
141
161
 
142
162
  describe 'raw output' do
@@ -148,7 +168,7 @@ module Api
148
168
  let(:fake_task) do
149
169
  OpenStruct.new :pending? => false, :main_action => OpenStruct.new(:live_output => fake_output)
150
170
  end
151
- let(:host) { @invocation.template_invocations_hosts.first }
171
+ let(:host) { @invocation.targeting.hosts.first }
152
172
 
153
173
  test 'should provide raw output for a host' do
154
174
  JobInvocation.any_instance.expects(:task).returns(OpenStruct.new(:scheduled? => false))
@@ -184,6 +204,12 @@ module Api
184
204
  assert_nil result['output']
185
205
  assert_response :success
186
206
  end
207
+
208
+ test 'should get raw output only for host in job invocation' do
209
+ get :raw_output, params: { job_invocation_id: @invocation.id,
210
+ host_id: FactoryBot.create(:host).id }
211
+ assert_response :missing
212
+ end
187
213
  end
188
214
 
189
215
  test 'should cancel a job' do
@@ -232,11 +258,13 @@ module Api
232
258
  end
233
259
 
234
260
  test 'should not raise an exception when reruning failed has no hosts' do
261
+ @invocation.targeting.hosts.first.destroy
235
262
  JobInvocation.any_instance.expects(:generate_description)
236
263
  JobInvocationComposer.any_instance
237
264
  .expects(:validate_job_category)
238
265
  .with(@invocation.job_category)
239
266
  .returns(@invocation.job_category)
267
+
240
268
  post :rerun, params: { :id => @invocation.id, :failed_only => true }
241
269
  assert_response :success
242
270
  result = ActiveSupport::JSON.decode(@response.body)
@@ -15,10 +15,42 @@ class SSHOrchestrationTest < ActiveSupport::TestCase
15
15
  end
16
16
 
17
17
  it 'attempts to drop IP address and hostname from smart proxies on rebuild' do
18
+ host.stubs(:skip_orchestration?).returns false
19
+ SmartProxy.any_instance.expects(:drop_host_from_known_hosts).with(interface.ip)
20
+ SmartProxy.any_instance.expects(:drop_host_from_known_hosts).with(host.name)
21
+
18
22
  host.build = true
19
23
  host.save!
24
+
20
25
  ids = ["ssh_remove_known_hosts_interface_#{interface.ip}_#{proxy.id}",
21
26
  "ssh_remove_known_hosts_host_#{host.name}_#{proxy.id}"]
22
27
  _(host.queue.task_ids).must_equal ids
28
+ _(host.queue.items.map(&:status)).must_equal %w(completed completed)
29
+ end
30
+
31
+ it 'does not fail on 404 from the smart proxy' do
32
+ host.stubs(:skip_orchestration?).returns false
33
+ SmartProxy.any_instance.expects(:drop_host_from_known_hosts).raises(RestClient::ResourceNotFound).twice
34
+ host.build = true
35
+ host.save!
36
+ ids = ["ssh_remove_known_hosts_interface_#{interface.ip}_#{proxy.id}",
37
+ "ssh_remove_known_hosts_host_#{host.name}_#{proxy.id}"]
38
+ _(host.queue.task_ids).must_equal ids
39
+ _(host.queue.items.map(&:status)).must_equal %w(completed completed)
40
+ end
41
+
42
+ it 'does not trigger the removal when creating a new host' do
43
+ SmartProxy.any_instance.expects(:drop_host_from_known_hosts).never
44
+ host = Host::Managed.new(:name => 'test', :ip => '127.0.0.1')
45
+ host.stubs(:skip_orchestration?).returns false
46
+ _(host.queue.task_ids).must_equal []
47
+ end
48
+
49
+ it 'does not call to the proxy when target is nil' do
50
+ host.stubs(:skip_orchestration?).returns false
51
+ SmartProxy.any_instance.expects(:drop_host_from_known_hosts).with(host.name)
52
+ host.interfaces.first.stubs(:ip)
53
+ host.destroy
54
+ _(host.queue.items.map(&:status)).must_equal %w(completed completed)
23
55
  end
24
56
  end
@@ -48,6 +48,13 @@ class ForemanRemoteExecutionHostExtensionsTest < ActiveSupport::TestCase
48
48
  _(host.host_param('remote_execution_ssh_keys')).must_include sshkey
49
49
  end
50
50
 
51
+ it 'merges ssh key as a string from host parameters and proxies' do
52
+ key = 'ssh-rsa not-even-a-key something@somewhere.com'
53
+ host.host_parameters << FactoryBot.create(:host_parameter, :host => host, :name => 'remote_execution_ssh_keys', :value => key)
54
+ _(host.host_param('remote_execution_ssh_keys')).must_include key
55
+ _(host.host_param('remote_execution_ssh_keys')).must_include sshkey
56
+ end
57
+
51
58
  it 'has ssh keys in the parameters even when no user specified' do
52
59
  # this is a case, when using the helper in provisioning templates
53
60
  FactoryBot.create(:smart_proxy, :ssh)
@@ -0,0 +1 @@
1
+ export const translate = s => s;
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+
3
+ export const ActionButtons = () => <div />;
@@ -0,0 +1,3 @@
1
+ export const STATUS = {
2
+ ERROR: 'ERROR',
3
+ };
@@ -1,14 +1,16 @@
1
- import URI from 'urijs';
2
- // eslint-disable-next-line import/no-extraneous-dependencies
3
- import { mount, registerReducer } from 'foremanReact/common/MountingService';
4
- // eslint-disable-next-line import/no-extraneous-dependencies
1
+ import { registerReducer } from 'foremanReact/common/MountingService';
5
2
  import componentRegistry from 'foremanReact/components/componentRegistry';
6
3
  import JobInvocationContainer from './react_app/components/jobInvocations';
4
+ import TargetingHosts from './react_app/components/TargetingHosts';
7
5
  import rootReducer from './react_app/redux/reducers';
8
6
 
9
- componentRegistry.register({
10
- name: 'JobInvocationContainer',
11
- type: JobInvocationContainer,
7
+ const components = [
8
+ { name: 'JobInvocationContainer', type: JobInvocationContainer },
9
+ { name: 'TargetingHosts', type: TargetingHosts },
10
+ ];
11
+
12
+ components.forEach(component => {
13
+ componentRegistry.register(component);
12
14
  });
13
15
 
14
16
  registerReducer('foremanRemoteExecutionReducers', rootReducer);