foreman_remote_execution 3.1.0 → 3.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc +5 -30
  3. data/.github/workflows/ci.yml +100 -0
  4. data/.gitignore +1 -0
  5. data/.rubocop_todo.yml +3 -0
  6. data/Gemfile +1 -0
  7. data/app/controllers/api/v2/job_invocations_controller.rb +22 -1
  8. data/app/controllers/api/v2/template_invocations_controller.rb +4 -1
  9. data/app/helpers/job_invocations_chart_helper.rb +2 -0
  10. data/app/helpers/job_invocations_helper.rb +6 -1
  11. data/app/helpers/remote_execution_helper.rb +39 -30
  12. data/app/lib/actions/remote_execution/run_host_job.rb +3 -2
  13. data/app/lib/proxy_api/remote_execution_ssh.rb +6 -0
  14. data/app/models/concerns/foreman_remote_execution/host_extensions.rb +1 -1
  15. data/app/models/concerns/foreman_remote_execution/orchestration/ssh.rb +63 -0
  16. data/app/models/concerns/foreman_remote_execution/smart_proxy_extensions.rb +4 -0
  17. data/app/models/host_status/execution_status.rb +9 -1
  18. data/app/models/remote_execution_provider.rb +5 -0
  19. data/app/services/default_proxy_proxy_selector.rb +18 -0
  20. data/app/views/api/v2/job_invocations/main.json.rabl +8 -2
  21. data/app/views/job_invocations/_card_results.html.erb +1 -0
  22. data/app/views/job_invocations/_card_user_input.html.erb +1 -1
  23. data/app/views/job_invocations/_tab_hosts.html.erb +3 -23
  24. data/app/views/job_invocations/_tab_overview.html.erb +1 -1
  25. data/app/views/job_invocations/_user_input.html.erb +1 -1
  26. data/app/views/job_invocations/show.html.erb +1 -7
  27. data/app/views/job_invocations/show.json.erb +4 -0
  28. data/config/routes.rb +2 -1
  29. data/db/seeds.d/70-job_templates.rb +1 -1
  30. data/foreman_remote_execution.gemspec +5 -6
  31. data/lib/foreman_remote_execution/engine.rb +2 -0
  32. data/lib/foreman_remote_execution/version.rb +1 -1
  33. data/locale/action_names.rb +3 -3
  34. data/locale/de/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  35. data/locale/de/foreman_remote_execution.po +65 -16
  36. data/locale/en/foreman_remote_execution.po +63 -15
  37. data/locale/en_GB/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  38. data/locale/en_GB/foreman_remote_execution.po +64 -15
  39. data/locale/es/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  40. data/locale/es/foreman_remote_execution.po +66 -17
  41. data/locale/foreman_remote_execution.pot +193 -148
  42. data/locale/fr/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  43. data/locale/fr/foreman_remote_execution.po +66 -17
  44. data/locale/ja/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  45. data/locale/ja/foreman_remote_execution.po +66 -17
  46. data/locale/ko/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  47. data/locale/ko/foreman_remote_execution.po +65 -15
  48. data/locale/pt_BR/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  49. data/locale/pt_BR/foreman_remote_execution.po +66 -17
  50. data/locale/ru/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  51. data/locale/ru/foreman_remote_execution.po +65 -18
  52. data/locale/zh_CN/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  53. data/locale/zh_CN/foreman_remote_execution.po +66 -17
  54. data/locale/zh_TW/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  55. data/locale/zh_TW/foreman_remote_execution.po +65 -16
  56. data/package.json +16 -33
  57. data/test/functional/api/v2/job_invocations_controller_test.rb +42 -14
  58. data/test/functional/job_invocations_controller_test.rb +12 -0
  59. data/test/models/orchestration/ssh_test.rb +56 -0
  60. data/test/unit/concerns/host_extensions_test.rb +7 -0
  61. data/test/unit/remote_execution_provider_test.rb +4 -1
  62. data/webpack/__mocks__/foremanReact/common/I18n.js +1 -0
  63. data/webpack/__mocks__/foremanReact/components/common/ActionButtons/ActionButtons.js +3 -0
  64. data/webpack/__mocks__/foremanReact/constants.js +3 -0
  65. data/webpack/index.js +9 -22
  66. data/webpack/react_app/components/TargetingHosts/TargetingHosts.js +52 -0
  67. data/webpack/react_app/components/TargetingHosts/TargetingHostsActions.js +8 -0
  68. data/webpack/react_app/components/TargetingHosts/TargetingHostsConsts.js +1 -0
  69. data/webpack/react_app/components/TargetingHosts/TargetingHostsSelectors.js +12 -0
  70. data/webpack/react_app/components/TargetingHosts/__tests__/HostItem.test.js +6 -0
  71. data/webpack/react_app/components/TargetingHosts/__tests__/HostStatus.test.js +6 -0
  72. data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHosts.test.js +6 -0
  73. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/HostItem.test.js.snap +31 -0
  74. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/HostStatus.test.js.snap +12 -0
  75. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHosts.test.js.snap +81 -0
  76. data/webpack/react_app/components/TargetingHosts/__tests__/fixtures.js +43 -0
  77. data/webpack/react_app/components/TargetingHosts/components/HostItem.js +39 -0
  78. data/webpack/react_app/components/TargetingHosts/components/HostStatus.js +54 -0
  79. data/webpack/react_app/components/TargetingHosts/index.js +37 -0
  80. data/webpack/react_app/components/jobInvocations/AggregateStatus/index.js +10 -0
  81. data/webpack/react_app/components/jobInvocations/AggregateStatus/index.test.js +6 -3
  82. data/webpack/react_app/components/jobInvocations/index.js +19 -7
  83. data/webpack/react_app/redux/actions/jobInvocations/index.js +12 -8
  84. data/webpack/react_app/redux/consts.js +1 -2
  85. data/webpack/react_app/redux/reducers/jobInvocations/index.fixtures.js +8 -40
  86. data/webpack/react_app/redux/reducers/jobInvocations/index.test.js +17 -11
  87. data/webpack/test_setup.js +2 -1
  88. metadata +31 -14
  89. data/.hound.yml +0 -19
  90. data/.travis.yml +0 -6
  91. data/app/views/job_invocations/_host_actions_td.html.erb +0 -3
  92. data/app/views/job_invocations/_host_name_td.html.erb +0 -8
  93. data/app/views/job_invocations/_host_status_td.html.erb +0 -1
  94. data/app/views/job_invocations/show.js.erb +0 -23
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36a15d0142caa911a59ff7d9797ceab5dabb0e601f605c0fee3d547d1be168f5
4
- data.tar.gz: d41aba8d661cae6ef74cfee2bd9e007a37f218c3056e9d9b8ae3a1de5f243bc6
3
+ metadata.gz: d093d837d0009e477549fba4700f3e0090045ba23f8b2c50fb05439fee53e403
4
+ data.tar.gz: 54dffc56caa3fa1bb72bb159b152d267d2e348b64b57322ea74cdf0ad9289cc1
5
5
  SHA512:
6
- metadata.gz: 20d61e7658cda93e3f24340ae6caa65c1c7868f37e699d7e6a9da524f2acaef9cccdf86cd47a7d2a1386037ad5aac0900935f3c3b2b4706926f16e60245f1b64
7
- data.tar.gz: 66e0c4b3af2d1feab711fe90b2af6a5ce08d4159501a1725a98243647a022431d871081bf2d1e82d46f8490c41589a1df6f15f2ee6f13391fb10df240bd41fe4
6
+ metadata.gz: e3524503031c73f86a0f3ed0b6c8dd39499f85df47052eca941f455552665b93ff041e5d6df9870cfb95ac1219017a561a483556306905bdc005caf252a5e088
7
+ data.tar.gz: 537da427406ce7c794d5e589e7e82f8c6fbda33a3a4231129905b69171421f41e98563c0496d9c7cc748f675d200729ec52f4d9067a8f7879972922358a2a868
data/.eslintrc CHANGED
@@ -1,32 +1,7 @@
1
1
  {
2
- "root": true,
3
- "extends": ["airbnb-base", "./node_modules/@theforeman/vendor-dev/eslint.extends.js"],
4
- "plugins": [
5
- "react"
6
- ],
7
- "env": {
8
- "browser": true,
9
- "es6": true,
10
- "node": true,
11
- "jasmine": true,
12
- "jest": true
13
- },
14
- "parser": "babel-eslint",
15
- "rules": {
16
- "react/jsx-uses-vars": "error",
17
- "react/jsx-uses-react": "error",
18
- "no-unused-vars": [
19
- "error",
20
- {
21
- "vars": "all",
22
- "args": "none"
23
- }
24
- ],
25
- "no-underscore-dangle": "off",
26
- "no-use-before-define": "off",
27
- "import/prefer-default-export": "off",
28
- // Import rules off for now due to HoundCI issue
29
- "import/no-unresolved": "off",
30
- "import/extensions": "off"
31
- }
2
+ "plugins": ["@theforeman/foreman"],
3
+ "extends": [
4
+ "plugin:@theforeman/foreman/core",
5
+ "plugin:@theforeman/foreman/plugins"
6
+ ]
32
7
  }
@@ -0,0 +1,100 @@
1
+ name: CI
2
+ on: [pull_request]
3
+ env:
4
+ RAILS_ENV: test
5
+ DATABASE_URL: postgresql://postgres:@localhost/test
6
+ DATABASE_CLEANER_ALLOW_REMOTE_DATABASE_URL: true
7
+ jobs:
8
+ rubocop:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v2
12
+ - name: Setup Ruby
13
+ uses: ruby/setup-ruby@v1
14
+ with:
15
+ ruby-version: 2.6
16
+ - name: Setup
17
+ run: |
18
+ gem install bundler
19
+ bundle install --jobs=3 --retry=3
20
+ - name: Run rubocop
21
+ run: bundle exec rubocop
22
+ test_ruby:
23
+ runs-on: ubuntu-latest
24
+ needs: rubocop
25
+ services:
26
+ postgres:
27
+ image: postgres:12.1
28
+ ports: ['5432:5432']
29
+ options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
30
+ strategy:
31
+ fail-fast: false
32
+ matrix:
33
+ foreman-core-branch: [2.1-stable, develop]
34
+ ruby-version: [2.5, 2.6]
35
+ node-version: [12]
36
+ steps:
37
+ - run: sudo apt-get install build-essential libcurl4-openssl-dev zlib1g-dev libpq-dev
38
+ - uses: actions/checkout@v2
39
+ with:
40
+ repository: theforeman/foreman
41
+ ref: ${{ matrix.foreman-core-branch }}
42
+ - uses: actions/checkout@v2
43
+ with:
44
+ path: foreman_remote_execution
45
+ - name: Setup Ruby
46
+ uses: ruby/setup-ruby@v1
47
+ with:
48
+ ruby-version: ${{ matrix.ruby-version }}
49
+ - name: Setup Node
50
+ uses: actions/setup-node@v1
51
+ with:
52
+ node-version: ${{ matrix.node-version }}
53
+ - uses: actions/cache@v1
54
+ with:
55
+ path: vendor/bundle
56
+ key: ${{ runner.os }}-fgems-${{ matrix.ruby-version }}-${{ hashFiles('Gemfile.lock') }}
57
+ restore-keys: |
58
+ ${{ runner.os }}-fgems-${{ matrix.ruby-version }}-
59
+ - name: Setup Bundler
60
+ run: |
61
+ echo "gem 'foreman_remote_execution', path: './foreman_remote_execution'" > bundler.d/foreman_remote_execution.local.rb
62
+ gem install bundler
63
+ bundle config set without journald development console libvirt
64
+ bundle config set path vendor/bundle
65
+ - name: Prepare test env
66
+ run: |
67
+ bundle install --jobs=3 --retry=3
68
+ bundle exec rake db:create
69
+ bundle exec rake db:migrate
70
+ - name: Run plugin tests
71
+ run: |
72
+ bundle exec rake test:foreman_remote_execution
73
+ bundle exec rake test TEST="test/unit/foreman/access_permissions_test.rb"
74
+ test_js:
75
+ runs-on: ubuntu-latest
76
+ needs: rubocop
77
+ strategy:
78
+ fail-fast: false
79
+ matrix:
80
+ ruby-version: [2.6]
81
+ node-version: [10, 12]
82
+ steps:
83
+ - uses: actions/checkout@v2
84
+ - name: Setup Ruby
85
+ uses: ruby/setup-ruby@v1
86
+ with:
87
+ ruby-version: ${{ matrix.ruby-version }}
88
+ - name: Setup Node
89
+ uses: actions/setup-node@v1
90
+ with:
91
+ node-version: ${{ matrix.node-version }}
92
+ - name: Nmp install
93
+ run: |
94
+ npm install
95
+ - name: Run plugin linter
96
+ run: |
97
+ npm run lint
98
+ - name: Run plugin tests
99
+ run: |
100
+ npm run test
data/.gitignore CHANGED
@@ -14,3 +14,4 @@ locale/*/*.po.time_stamp
14
14
  Gemfile.lock
15
15
  node_modules/
16
16
  package-lock.json
17
+ coverage/
@@ -6,6 +6,9 @@
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
+ Minitest/GlobalExpectations:
10
+ Enabled: false
11
+
9
12
  # Offense count: 2
10
13
  # Cop supports --auto-correct.
11
14
  # Configuration parameters: TreatCommentsAsGroupSeparators, Include.
data/Gemfile CHANGED
@@ -2,6 +2,7 @@ source 'http://rubygems.org'
2
2
 
3
3
  gemspec :name => 'foreman_remote_execution'
4
4
 
5
+ gem 'rubocop', '~> 0.80.0'
5
6
  gem 'rubocop-minitest'
6
7
  gem 'rubocop-performance'
7
8
  gem 'rubocop-rails'
@@ -18,7 +18,17 @@ 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
22
  def show
23
+ @hosts = @job_invocation.targeting.hosts.authorized(:view_hosts, Host)
24
+ @template_invocations = @job_invocation.template_invocations
25
+ .where(host: @hosts)
26
+ .includes(:input_values)
27
+
28
+ if params[:host_status]
29
+ template_invocations = @template_invocations.includes(:run_host_job_task).to_a
30
+ @host_statuses = Hash[template_invocations.map { |ti| [ti.host_id, template_invocation_status(ti)] }]
31
+ end
22
32
  end
23
33
 
24
34
  def_param_group :job_invocation do
@@ -146,7 +156,7 @@ module Api
146
156
  end
147
157
 
148
158
  def find_host
149
- @host = Host.authorized(:view_hosts).find(params['host_id'])
159
+ @host = @nested_obj.targeting.hosts.authorized(:view_hosts, Host).find(params['host_id'])
150
160
  rescue ActiveRecord::RecordNotFound
151
161
  not_found({ :error => { :message => (_("Host with id '%{id}' was not found") % { :id => params['host_id'] }) } })
152
162
  end
@@ -204,6 +214,17 @@ module Api
204
214
  def parent_scope
205
215
  resource_class.where(nil)
206
216
  end
217
+
218
+ def template_invocation_status(template_invocation)
219
+ task = template_invocation.try(:run_host_job_task)
220
+ parent_task = @job_invocation.task
221
+
222
+ return(parent_task.result == 'cancelled' ? 'cancelled' : 'N/A') if task.nil?
223
+ return task.state if task.state == 'running' || task.state == 'planned'
224
+ return 'error' if task.result == 'warning'
225
+
226
+ task.result
227
+ end
207
228
  end
208
229
  end
209
230
  end
@@ -26,7 +26,10 @@ module Api
26
26
  private
27
27
 
28
28
  def resource_scope_for_template_invocations
29
- @job_invocation.template_invocations.search_for(*search_options)
29
+ @job_invocation.template_invocations
30
+ .includes(:host)
31
+ .where(host: Host.authorized(:view_hosts, Host))
32
+ .search_for(*search_options)
30
33
  end
31
34
 
32
35
  def find_job_invocation
@@ -37,6 +37,8 @@ module JobInvocationsChartHelper
37
37
  _('running %{percent}%%') % {:percent => percent}
38
38
  when HostStatus::ExecutionStatus::OK
39
39
  _('succeeded')
40
+ when HostStatus::ExecutionStatus::CANCELLED
41
+ _('cancelled')
40
42
  when HostStatus::ExecutionStatus::ERROR
41
43
  _('failed')
42
44
  else
@@ -29,7 +29,7 @@ module JobInvocationsHelper
29
29
  end
30
30
 
31
31
  def preview_hosts(template_invocation)
32
- hosts = template_invocation.targeting.hosts.take(20)
32
+ hosts = template_invocation.targeting.hosts.authorized(:view_hosts, Host).take(20)
33
33
  hosts.map do |host|
34
34
  collapsed_preview(host) +
35
35
  render(:partial => 'job_invocations/user_input',
@@ -55,4 +55,9 @@ module JobInvocationsHelper
55
55
  def show_job_location(location)
56
56
  location.presence || _('Any Location')
57
57
  end
58
+
59
+ def input_safe_value(input)
60
+ template_input = input.template_input
61
+ template_input.respond_to?(:hidden_value) && template_input.hidden_value ? '*' * 5 : input.value
62
+ end
58
63
  end
@@ -13,40 +13,36 @@ module RemoteExecutionHelper
13
13
  end
14
14
  end
15
15
 
16
- def template_invocation_status(task)
17
- if task.nil?
18
- icon_text('question', 'N/A', :kind => 'fa')
19
- elsif task.state == 'running'
20
- icon_text('running', _('running'), :kind => 'pficon')
21
- elsif task.state == 'planned'
22
- icon_text('build', _('planned'), :kind => 'pficon')
23
- else
24
- case task.result
25
- when 'warning', 'error'
26
- icon_text('error-circle-o', _('failed'), :kind => 'pficon')
27
- when 'cancelled'
28
- icon_text('warning-triangle-o', _('cancelled'), :kind => 'pficon')
29
- when 'success'
30
- icon_text('ok', _('success'), :kind => 'pficon')
31
- else
32
- task.result
33
- end
34
- end
16
+ def template_invocation_status(task, parent_task)
17
+ return(parent_task.result == 'cancelled' ? _('cancelled') : 'N/A') if task.nil?
18
+ return task.state if task.state == 'running' || task.state == 'planned'
19
+ return _('error') if task.result == 'warning'
20
+
21
+ task.result
35
22
  end
36
23
 
37
24
  def template_invocation_actions(task, host, job_invocation, template_invocation)
25
+ links = []
38
26
  host_task = template_invocation.try(:run_host_job_task)
39
- [
40
- display_link_if_authorized(_('Host detail'), hash_for_host_path(host).merge(:auth_object => host, :permission => :view_hosts, :authorizer => job_hosts_authorizer)),
41
- display_link_if_authorized(_('Rerun on %s') % host.name, hash_for_rerun_job_invocation_path(:id => job_invocation, :host_ids => [ host.id ], :authorizer => job_hosts_authorizer)),
42
- if host_task.present?
43
- display_link_if_authorized(
44
- _('Host task'),
45
- hash_for_foreman_tasks_task_path(host_task)
46
- .merge(:auth_object => host_task, :permission => :view_foreman_tasks)
47
- )
48
- end,
49
- ]
27
+
28
+ if authorized_for(hash_for_host_path(host).merge(auth_object: host, permission: :view_hosts, authorizer: job_hosts_authorizer))
29
+ links << { title: _('Host detail'),
30
+ action: { href: host_path(host), 'data-method': 'get', id: "#{host.name}-actions-detail" } }
31
+ end
32
+
33
+ if authorized_for(hash_for_rerun_job_invocation_path(id: job_invocation, host_ids: [ host.id ], authorizer: job_hosts_authorizer))
34
+ links << { title: (_('Rerun on %s') % host.name),
35
+ action: { href: rerun_job_invocation_path(job_invocation, host_ids: [ host.id ]),
36
+ 'data-method': 'get', id: "#{host.name}-actions-rerun" } }
37
+ end
38
+
39
+ if host_task.present? && authorized_for(hash_for_foreman_tasks_task_path(host_task).merge(auth_object: host_task, permission: :view_foreman_tasks))
40
+ links << { title: _('Host task'),
41
+ action: { href: foreman_tasks_task_path(host_task),
42
+ 'data-method': 'get', id: "#{host.name}-actions-task" } }
43
+ end
44
+
45
+ links
50
46
  end
51
47
 
52
48
  def remote_execution_provider_for(template_invocation)
@@ -233,4 +229,17 @@ module RemoteExecutionHelper
233
229
 
234
230
  task.execution_plan.actions[1].try(:input).try(:[], 'script')
235
231
  end
232
+
233
+ def targeting_hosts(job_invocation, hosts)
234
+ hosts.map do |host|
235
+ template_invocation = job_invocation.template_invocations.find { |template_inv| template_inv.host_id == host.id }
236
+ task = template_invocation.try(:run_host_job_task)
237
+ link_authorized = !task.nil? && authorized_for(hash_for_template_invocation_path(:id => template_invocation).merge(:auth_object => host, :permission => :view_hosts, :authorizer => job_hosts_authorizer))
238
+
239
+ { name: host.name,
240
+ link: link_authorized ? template_invocation_path(:id => template_invocation) : '',
241
+ status: template_invocation_status(task, job_invocation.task),
242
+ actions: template_invocation_actions(task, host, job_invocation, template_invocation) }
243
+ end
244
+ end
236
245
  end
@@ -29,6 +29,9 @@ module Actions
29
29
 
30
30
  raise _('Could not use any template used in the job invocation') if template_invocation.blank?
31
31
 
32
+ provider = template_invocation.template.provider
33
+ proxy_selector = provider.required_proxy_selector_for(template_invocation.template) || proxy_selector
34
+
32
35
  provider_type = template_invocation.template.provider_type.to_s
33
36
  proxy = determine_proxy!(proxy_selector, provider_type, host)
34
37
 
@@ -36,8 +39,6 @@ module Actions
36
39
  script = renderer.render
37
40
  raise _('Failed rendering template: %s') % renderer.error_message unless script
38
41
 
39
- provider = template_invocation.template.provider
40
-
41
42
  additional_options = { :hostname => provider.find_ip_or_hostname(host),
42
43
  :script => script,
43
44
  :execution_timeout_interval => job_invocation.execution_timeout_interval,
@@ -10,5 +10,11 @@ module ::ProxyAPI
10
10
  rescue => e
11
11
  raise ProxyException.new(url, e, N_('Unable to fetch public key'))
12
12
  end
13
+
14
+ def drop_from_known_hosts(hostname)
15
+ delete('known_hosts/' + hostname)
16
+ rescue => e
17
+ raise ProxyException.new(url, e, N_('Unable to remove host from known hosts'))
18
+ end
13
19
  end
14
20
  end
@@ -49,7 +49,7 @@ module ForemanRemoteExecution
49
49
  keys = remote_execution_ssh_keys
50
50
  source = 'global'
51
51
  if keys.present?
52
- value, safe_value = params.fetch('remote_execution_ssh_keys', {}).values_at(:value, :safe_value).map { |v| v || [] }
52
+ value, safe_value = params.fetch('remote_execution_ssh_keys', {}).values_at(:value, :safe_value).map { |v| [v].flatten.compact }
53
53
  params['remote_execution_ssh_keys'] = {:value => value + keys, :safe_value => safe_value + keys, :source => source}
54
54
  end
55
55
  [:remote_execution_ssh_user, :remote_execution_effective_user_method,
@@ -0,0 +1,63 @@
1
+ module ForemanRemoteExecution
2
+ module Orchestration::SSH
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ before_destroy :ssh_destroy
7
+ after_validation :queue_ssh_destroy
8
+ register_rebuild(:queue_ssh_destroy, N_("SSH_#{self.to_s.split('::').first}"))
9
+ end
10
+
11
+ def drop_from_known_hosts(proxy_id)
12
+ _, _, target = host_kind_target
13
+ return true if target.nil?
14
+
15
+ proxy = ::SmartProxy.find(proxy_id)
16
+ begin
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
21
+ rescue => e
22
+ Rails.logger.warn e.message
23
+ return false
24
+ end
25
+ true
26
+ end
27
+
28
+ def ssh_destroy
29
+ logger.debug "Scheduling SSH known_hosts cleanup"
30
+
31
+ host, _kind, _target = host_kind_target
32
+ proxies = host.remote_execution_proxies('SSH').values
33
+ proxies.flatten.uniq.each do |proxy|
34
+ queue.create(id: queue_id(proxy.id), name: _("Remove SSH known hosts for %s") % self,
35
+ priority: 200, action: [self, :drop_from_known_hosts, proxy.id])
36
+ end
37
+ end
38
+
39
+ def queue_ssh_destroy
40
+ should_drop_from_known_hosts? && ssh_destroy
41
+ end
42
+
43
+ def should_drop_from_known_hosts?
44
+ host, = host_kind_target
45
+ host && !host.new_record? && host.build && host.changes.key?('build')
46
+ end
47
+
48
+ private
49
+
50
+ def host_kind_target
51
+ if self.is_a?(::Host::Base)
52
+ [self, 'host', name]
53
+ else
54
+ [self.host, 'interface', ip]
55
+ end
56
+ end
57
+
58
+ def queue_id(proxy_id)
59
+ _, kind, id = host_kind_target
60
+ "ssh_remove_known_hosts_#{kind}_#{id}_#{proxy_id}"
61
+ end
62
+ end
63
+ end