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.
- checksums.yaml +4 -4
- data/.eslintrc +5 -30
- data/.github/workflows/ci.yml +100 -0
- data/.gitignore +1 -0
- data/.rubocop_todo.yml +3 -0
- data/Gemfile +1 -0
- data/app/controllers/api/v2/job_invocations_controller.rb +22 -1
- data/app/controllers/api/v2/template_invocations_controller.rb +4 -1
- data/app/helpers/job_invocations_chart_helper.rb +2 -0
- data/app/helpers/job_invocations_helper.rb +6 -1
- data/app/helpers/remote_execution_helper.rb +39 -30
- data/app/lib/actions/remote_execution/run_host_job.rb +3 -2
- data/app/lib/proxy_api/remote_execution_ssh.rb +6 -0
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +1 -1
- data/app/models/concerns/foreman_remote_execution/orchestration/ssh.rb +63 -0
- data/app/models/concerns/foreman_remote_execution/smart_proxy_extensions.rb +4 -0
- data/app/models/host_status/execution_status.rb +9 -1
- data/app/models/remote_execution_provider.rb +5 -0
- data/app/services/default_proxy_proxy_selector.rb +18 -0
- data/app/views/api/v2/job_invocations/main.json.rabl +8 -2
- data/app/views/job_invocations/_card_results.html.erb +1 -0
- data/app/views/job_invocations/_card_user_input.html.erb +1 -1
- data/app/views/job_invocations/_tab_hosts.html.erb +3 -23
- data/app/views/job_invocations/_tab_overview.html.erb +1 -1
- data/app/views/job_invocations/_user_input.html.erb +1 -1
- data/app/views/job_invocations/show.html.erb +1 -7
- data/app/views/job_invocations/show.json.erb +4 -0
- data/config/routes.rb +2 -1
- data/db/seeds.d/70-job_templates.rb +1 -1
- data/foreman_remote_execution.gemspec +5 -6
- data/lib/foreman_remote_execution/engine.rb +2 -0
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/locale/action_names.rb +3 -3
- data/locale/de/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/de/foreman_remote_execution.po +65 -16
- data/locale/en/foreman_remote_execution.po +63 -15
- data/locale/en_GB/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/en_GB/foreman_remote_execution.po +64 -15
- data/locale/es/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/es/foreman_remote_execution.po +66 -17
- data/locale/foreman_remote_execution.pot +193 -148
- data/locale/fr/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/fr/foreman_remote_execution.po +66 -17
- data/locale/ja/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ja/foreman_remote_execution.po +66 -17
- data/locale/ko/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ko/foreman_remote_execution.po +65 -15
- data/locale/pt_BR/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/pt_BR/foreman_remote_execution.po +66 -17
- data/locale/ru/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ru/foreman_remote_execution.po +65 -18
- data/locale/zh_CN/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/zh_CN/foreman_remote_execution.po +66 -17
- data/locale/zh_TW/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/zh_TW/foreman_remote_execution.po +65 -16
- data/package.json +16 -33
- data/test/functional/api/v2/job_invocations_controller_test.rb +42 -14
- data/test/functional/job_invocations_controller_test.rb +12 -0
- data/test/models/orchestration/ssh_test.rb +56 -0
- data/test/unit/concerns/host_extensions_test.rb +7 -0
- data/test/unit/remote_execution_provider_test.rb +4 -1
- data/webpack/__mocks__/foremanReact/common/I18n.js +1 -0
- data/webpack/__mocks__/foremanReact/components/common/ActionButtons/ActionButtons.js +3 -0
- data/webpack/__mocks__/foremanReact/constants.js +3 -0
- data/webpack/index.js +9 -22
- data/webpack/react_app/components/TargetingHosts/TargetingHosts.js +52 -0
- data/webpack/react_app/components/TargetingHosts/TargetingHostsActions.js +8 -0
- data/webpack/react_app/components/TargetingHosts/TargetingHostsConsts.js +1 -0
- data/webpack/react_app/components/TargetingHosts/TargetingHostsSelectors.js +12 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/HostItem.test.js +6 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/HostStatus.test.js +6 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHosts.test.js +6 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/HostItem.test.js.snap +31 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/HostStatus.test.js.snap +12 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHosts.test.js.snap +81 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/fixtures.js +43 -0
- data/webpack/react_app/components/TargetingHosts/components/HostItem.js +39 -0
- data/webpack/react_app/components/TargetingHosts/components/HostStatus.js +54 -0
- data/webpack/react_app/components/TargetingHosts/index.js +37 -0
- data/webpack/react_app/components/jobInvocations/AggregateStatus/index.js +10 -0
- data/webpack/react_app/components/jobInvocations/AggregateStatus/index.test.js +6 -3
- data/webpack/react_app/components/jobInvocations/index.js +19 -7
- data/webpack/react_app/redux/actions/jobInvocations/index.js +12 -8
- data/webpack/react_app/redux/consts.js +1 -2
- data/webpack/react_app/redux/reducers/jobInvocations/index.fixtures.js +8 -40
- data/webpack/react_app/redux/reducers/jobInvocations/index.test.js +17 -11
- data/webpack/test_setup.js +2 -1
- metadata +31 -14
- data/.hound.yml +0 -19
- data/.travis.yml +0 -6
- data/app/views/job_invocations/_host_actions_td.html.erb +0 -3
- data/app/views/job_invocations/_host_name_td.html.erb +0 -8
- data/app/views/job_invocations/_host_status_td.html.erb +0 -1
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d093d837d0009e477549fba4700f3e0090045ba23f8b2c50fb05439fee53e403
|
4
|
+
data.tar.gz: 54dffc56caa3fa1bb72bb159b152d267d2e348b64b57322ea74cdf0ad9289cc1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3524503031c73f86a0f3ed0b6c8dd39499f85df47052eca941f455552665b93ff041e5d6df9870cfb95ac1219017a561a483556306905bdc005caf252a5e088
|
7
|
+
data.tar.gz: 537da427406ce7c794d5e589e7e82f8c6fbda33a3a4231129905b69171421f41e98563c0496d9c7cc748f675d200729ec52f4d9067a8f7879972922358a2a868
|
data/.eslintrc
CHANGED
@@ -1,32 +1,7 @@
|
|
1
1
|
{
|
2
|
-
"
|
3
|
-
"extends": [
|
4
|
-
|
5
|
-
"
|
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
data/.rubocop_todo.yml
CHANGED
@@ -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
@@ -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 =
|
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
|
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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|