foreman_remote_execution 3.2.1 → 3.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/api/v2/job_invocations_controller.rb +5 -1
- data/app/controllers/api/v2/template_invocations_controller.rb +4 -1
- data/app/helpers/job_invocations_helper.rb +1 -1
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +1 -1
- data/app/models/concerns/foreman_remote_execution/orchestration/ssh.rb +10 -5
- data/app/views/api/v2/job_invocations/main.json.rabl +2 -2
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/test/functional/api/v2/job_invocations_controller_test.rb +42 -14
- data/test/models/orchestration/ssh_test.rb +32 -0
- data/test/unit/concerns/host_extensions_test.rb +7 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 19c80592b90b1ba20ac213bae2ff36e9608202161e29718a063d3ac52ed249b3
|
4
|
+
data.tar.gz: daa58fc0cfe1a9b788a49372c1d200579502331ef9e1f1424fa4a81270898a8b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3b9ee1922a5ece712f8f431eb84de7ab5793d5108fe4959ce690a0acd6edb14f21ab5135cc1b7bcf37d3fbe81af45731fe1900e1803683c131bc3dba13aa931
|
7
|
+
data.tar.gz: 5db356770b3017319ef2e4f0d1a16598025c9227b540661b2f3e6104f2bd522dbbfb351266eae0bc63d19667a2edc079de0136b50e648e6abddf973bbbe58ead
|
@@ -19,6 +19,10 @@ module Api
|
|
19
19
|
api :GET, '/job_invocations/:id', N_('Show job invocation')
|
20
20
|
param :id, :identifier, :required => true
|
21
21
|
def show
|
22
|
+
@hosts = @job_invocation.targeting.hosts.authorized(:view_hosts, Host)
|
23
|
+
@template_invocations = @job_invocation.template_invocations
|
24
|
+
.where(host: @hosts)
|
25
|
+
.includes(:input_values)
|
22
26
|
end
|
23
27
|
|
24
28
|
def_param_group :job_invocation do
|
@@ -146,7 +150,7 @@ module Api
|
|
146
150
|
end
|
147
151
|
|
148
152
|
def find_host
|
149
|
-
@host =
|
153
|
+
@host = @nested_obj.targeting.hosts.authorized(:view_hosts, Host).find(params['host_id'])
|
150
154
|
rescue ActiveRecord::RecordNotFound
|
151
155
|
not_found({ :error => { :message => (_("Host with id '%{id}' was not found") % { :id => params['host_id'] }) } })
|
152
156
|
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
|
@@ -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',
|
@@ -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,
|
@@ -8,11 +8,16 @@ module ForemanRemoteExecution
|
|
8
8
|
register_rebuild(:queue_ssh_destroy, N_("SSH_#{self.to_s.split('::').first}"))
|
9
9
|
end
|
10
10
|
|
11
|
-
def drop_from_known_hosts(
|
12
|
-
|
11
|
+
def drop_from_known_hosts(proxy_id)
|
12
|
+
_, _, target = host_kind_target
|
13
|
+
return true if target.nil?
|
14
|
+
|
13
15
|
proxy = ::SmartProxy.find(proxy_id)
|
14
16
|
begin
|
15
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
|
16
21
|
rescue => e
|
17
22
|
Rails.logger.warn e.message
|
18
23
|
return false
|
@@ -23,11 +28,11 @@ module ForemanRemoteExecution
|
|
23
28
|
def ssh_destroy
|
24
29
|
logger.debug "Scheduling SSH known_hosts cleanup"
|
25
30
|
|
26
|
-
host, _kind,
|
31
|
+
host, _kind, _target = host_kind_target
|
27
32
|
proxies = host.remote_execution_proxies('SSH').values
|
28
33
|
proxies.flatten.uniq.each do |proxy|
|
29
34
|
queue.create(id: queue_id(proxy.id), name: _("Remove SSH known hosts for %s") % self,
|
30
|
-
priority: 200, action: [self, :drop_from_known_hosts,
|
35
|
+
priority: 200, action: [self, :drop_from_known_hosts, proxy.id])
|
31
36
|
end
|
32
37
|
end
|
33
38
|
|
@@ -37,7 +42,7 @@ module ForemanRemoteExecution
|
|
37
42
|
|
38
43
|
def should_drop_from_known_hosts?
|
39
44
|
host, = host_kind_target
|
40
|
-
host
|
45
|
+
host && !host.new_record? && host.build && host.changes.key?('build')
|
41
46
|
end
|
42
47
|
|
43
48
|
private
|
@@ -19,7 +19,7 @@ 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
|
22
|
+
child @hosts do
|
23
23
|
extends 'api/v2/hosts/base'
|
24
24
|
end
|
25
25
|
end
|
@@ -28,7 +28,7 @@ child :task do
|
|
28
28
|
attributes :id, :state
|
29
29
|
end
|
30
30
|
|
31
|
-
child
|
31
|
+
child @template_invocations do
|
32
32
|
attributes :template_id, :template_name
|
33
33
|
child :input_values do
|
34
34
|
attributes :template_input_name, :template_input_id
|
@@ -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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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.
|
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.
|
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)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreman_remote_execution
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.2.
|
4
|
+
version: 3.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Foreman Remote Execution team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-06-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: deface
|