foreman_remote_execution 14.0.2 → 14.1.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.
- checksums.yaml +4 -4
- data/app/controllers/api/v2/job_invocations_controller.rb +34 -17
- data/app/helpers/remote_execution_helper.rb +2 -2
- data/app/lib/actions/remote_execution/proxy_action.rb +10 -5
- data/app/lib/actions/remote_execution/run_host_job.rb +1 -1
- data/app/lib/actions/remote_execution/template_invocation_progress_logging.rb +2 -3
- data/app/views/api/v2/job_invocations/hosts.json.rabl +15 -0
- data/config/routes.rb +1 -0
- data/db/migrate/20240312133027_extend_template_invocation_events.rb +19 -0
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/webpack/JobInvocationDetail/JobInvocationActions.js +1 -1
- data/webpack/JobInvocationDetail/JobInvocationConstants.js +84 -0
- data/webpack/JobInvocationDetail/JobInvocationDetail.scss +0 -1
- data/webpack/JobInvocationDetail/JobInvocationHostTable.js +210 -0
- data/webpack/JobInvocationDetail/JobInvocationSelectors.js +2 -2
- data/webpack/JobInvocationDetail/__tests__/MainInformation.test.js +5 -1
- data/webpack/JobInvocationDetail/__tests__/fixtures.js +9 -0
- data/webpack/JobInvocationDetail/index.js +56 -34
- data/webpack/__mocks__/foremanReact/components/HostDetails/DetailsCard/DefaultLoaderEmptyState.js +1 -2
- data/webpack/react_app/components/RecentJobsCard/JobStatusIcon.js +38 -7
- data/webpack/react_app/components/RecentJobsCard/constants.js +4 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/HostStatus.test.js.snap +1 -1
- data/webpack/react_app/components/TargetingHosts/components/HostStatus.js +6 -6
- metadata +6 -94
- data/.babelrc.js +0 -3
- data/.eslintignore +0 -3
- data/.eslintrc +0 -13
- data/.github/workflows/js_ci.yml +0 -32
- data/.github/workflows/release.yml +0 -16
- data/.github/workflows/ruby_ci.yml +0 -19
- data/.gitignore +0 -19
- data/.packit.yaml +0 -45
- data/.prettierrc +0 -4
- data/.rubocop.yml +0 -105
- data/.rubocop_todo.yml +0 -516
- data/.tx/config +0 -10
- data/Gemfile +0 -5
- data/app/mailers/.gitkeep +0 -0
- data/app/views/dashboard/.gitkeep +0 -0
- data/foreman_remote_execution.gemspec +0 -33
- data/jsconfig.json +0 -8
- data/test/benchmark/run_hosts_job_benchmark.rb +0 -70
- data/test/benchmark/targeting_benchmark.rb +0 -31
- data/test/factories/foreman_remote_execution_factories.rb +0 -147
- data/test/functional/api/v2/foreign_input_sets_controller_test.rb +0 -58
- data/test/functional/api/v2/job_invocations_controller_test.rb +0 -446
- data/test/functional/api/v2/job_templates_controller_test.rb +0 -110
- data/test/functional/api/v2/registration_controller_test.rb +0 -73
- data/test/functional/api/v2/remote_execution_features_controller_test.rb +0 -34
- data/test/functional/api/v2/template_invocations_controller_test.rb +0 -33
- data/test/functional/cockpit_controller_test.rb +0 -16
- data/test/functional/job_invocations_controller_test.rb +0 -132
- data/test/functional/job_templates_controller_test.rb +0 -31
- data/test/functional/ui_job_wizard_controller_test.rb +0 -16
- data/test/graphql/mutations/job_invocations/create_test.rb +0 -58
- data/test/graphql/queries/job_invocation_query_test.rb +0 -31
- data/test/graphql/queries/job_invocations_query_test.rb +0 -35
- data/test/helpers/remote_execution_helper_test.rb +0 -46
- data/test/support/remote_execution_helper.rb +0 -5
- data/test/test_plugin_helper.rb +0 -9
- data/test/unit/actions/run_host_job_test.rb +0 -115
- data/test/unit/actions/run_hosts_job_test.rb +0 -214
- data/test/unit/api_params_test.rb +0 -25
- data/test/unit/concerns/foreman_tasks_cleaner_extensions_test.rb +0 -29
- data/test/unit/concerns/host_extensions_test.rb +0 -219
- data/test/unit/concerns/nic_extensions_test.rb +0 -9
- data/test/unit/execution_task_status_mapper_test.rb +0 -92
- data/test/unit/input_template_renderer_test.rb +0 -503
- data/test/unit/job_invocation_composer_test.rb +0 -974
- data/test/unit/job_invocation_report_template_test.rb +0 -60
- data/test/unit/job_invocation_test.rb +0 -232
- data/test/unit/job_template_effective_user_test.rb +0 -37
- data/test/unit/job_template_test.rb +0 -316
- data/test/unit/remote_execution_feature_test.rb +0 -86
- data/test/unit/remote_execution_provider_test.rb +0 -298
- data/test/unit/renderer_scope_input_test.rb +0 -49
- data/test/unit/targeting_test.rb +0 -206
- data/test/unit/template_invocation_input_value_test.rb +0 -38
@@ -1,214 +0,0 @@
|
|
1
|
-
require 'test_plugin_helper'
|
2
|
-
require 'securerandom'
|
3
|
-
|
4
|
-
module ForemanRemoteExecution
|
5
|
-
class RunHostsJobTest < ActiveSupport::TestCase
|
6
|
-
include Dynflow::Testing
|
7
|
-
|
8
|
-
# Adding run_step_id wich is needed in RunHostsJob as a quick fix
|
9
|
-
# it will be added to dynflow in the future see https://github.com/Dynflow/dynflow/pull/391
|
10
|
-
# rubocop:disable Style/ClassAndModuleChildren
|
11
|
-
class Dynflow::Testing::DummyPlannedAction
|
12
|
-
def run_step_id
|
13
|
-
Dynflow::Testing.get_id
|
14
|
-
end
|
15
|
-
end
|
16
|
-
# rubocop:enable Style/ClassAndModuleChildren
|
17
|
-
|
18
|
-
let(:host) { FactoryBot.create(:host, :with_execution) }
|
19
|
-
let(:proxy) { host.remote_execution_proxies('SSH')[:subnet].first }
|
20
|
-
let(:targeting) { FactoryBot.create(:targeting, :search_query => "name = #{host.name}", :user => User.current) }
|
21
|
-
let(:job_invocation) do
|
22
|
-
FactoryBot.build(:job_invocation, :with_template).tap do |invocation|
|
23
|
-
invocation.targeting = targeting
|
24
|
-
invocation.description = 'Some short description'
|
25
|
-
invocation.password = 'changeme'
|
26
|
-
invocation.key_passphrase = 'changemetoo'
|
27
|
-
invocation.effective_user_password = 'sudopassword'
|
28
|
-
invocation.save
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
let(:uuid) { SecureRandom.uuid }
|
33
|
-
let(:task) do
|
34
|
-
OpenStruct.new(:id => uuid).tap do |o|
|
35
|
-
o.stubs(:add_missing_task_groups)
|
36
|
-
o.stubs(:task_groups).returns([])
|
37
|
-
o.stubs(:pending?).returns(true)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
let(:action) do
|
41
|
-
create_action(Actions::RemoteExecution::RunHostsJob).tap do |action|
|
42
|
-
action.expects(:action_subject).with(job_invocation, job_features: [])
|
43
|
-
ForemanTasks::Task::DynflowTask.stubs(:where).returns(mock.tap { |m| m.stubs(:first! => task) })
|
44
|
-
end
|
45
|
-
end
|
46
|
-
let(:planned) do
|
47
|
-
plan_action action, job_invocation
|
48
|
-
end
|
49
|
-
|
50
|
-
let(:delayed) do
|
51
|
-
action.delay({ :start_at => Time.now.getlocal }, job_invocation)
|
52
|
-
action
|
53
|
-
end
|
54
|
-
|
55
|
-
before do
|
56
|
-
ProxyAPI::ForemanDynflow::DynflowProxy.any_instance.stubs(:tasks_count).returns(0)
|
57
|
-
User.current = users :admin
|
58
|
-
action
|
59
|
-
end
|
60
|
-
|
61
|
-
context 'targeting resolving' do
|
62
|
-
it 'resolves dynamic targeting in plan' do
|
63
|
-
targeting.targeting_type = 'dynamic_query'
|
64
|
-
assert_not targeting.resolved?
|
65
|
-
delayed
|
66
|
-
assert_not targeting.resolved?
|
67
|
-
planned
|
68
|
-
assert_includes targeting.hosts, host
|
69
|
-
end
|
70
|
-
|
71
|
-
it 'resolves the hosts on static targeting in delay' do
|
72
|
-
assert_not targeting.resolved?
|
73
|
-
delayed
|
74
|
-
assert_includes targeting.hosts, host
|
75
|
-
# Verify Targeting#resolve_hosts! won't be hit again
|
76
|
-
targeting.expects(:resolve_hosts!).never
|
77
|
-
planned
|
78
|
-
end
|
79
|
-
|
80
|
-
it 'resolves the hosts on static targeting in plan phase if not resolved yet' do
|
81
|
-
planned
|
82
|
-
assert_includes targeting.hosts, host
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
it 'triggers the RunHostJob actions on the resolved hosts in run phase' do
|
87
|
-
planned.expects(:output).at_most(5).returns(:planned_count => 0)
|
88
|
-
planned.expects(:trigger).with { |*args| args[0] == Actions::RemoteExecution::RunHostJob }
|
89
|
-
planned.create_sub_plans
|
90
|
-
end
|
91
|
-
|
92
|
-
it 'uses the BindJobInvocation middleware' do
|
93
|
-
planned
|
94
|
-
assert_equal job_invocation.task_id, uuid
|
95
|
-
end
|
96
|
-
|
97
|
-
# In plan phase this is handled by #action_subject
|
98
|
-
# which is expected in tests
|
99
|
-
it 'sets input in delay phase when delayed' do
|
100
|
-
job_invocation_hash = delayed.input[:job_invocation]
|
101
|
-
assert_equal job_invocation_hash['id'], job_invocation.id
|
102
|
-
assert_equal job_invocation_hash['name'], job_invocation.job_category
|
103
|
-
assert_equal job_invocation_hash['description'], job_invocation.description
|
104
|
-
planned # To make the expectations happy
|
105
|
-
end
|
106
|
-
|
107
|
-
describe '#proxy_batch_size' do
|
108
|
-
it 'defaults to Setting[foreman_tasks_proxy_batch_size]' do
|
109
|
-
Setting.expects(:[]).with('foreman_tasks_proxy_batch_size').returns(14)
|
110
|
-
planned
|
111
|
-
assert_equal 14, planned.proxy_batch_size
|
112
|
-
end
|
113
|
-
|
114
|
-
it 'gets the provider value' do
|
115
|
-
provider = mock('provider')
|
116
|
-
provider.expects(:proxy_batch_size).returns(15)
|
117
|
-
JobTemplate.any_instance.expects(:provider).returns(provider)
|
118
|
-
|
119
|
-
assert_equal 15, planned.proxy_batch_size
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
describe 'concurrency control' do
|
124
|
-
let(:level) { 5 }
|
125
|
-
|
126
|
-
it 'can be disabled' do
|
127
|
-
assert_nil planned.concurrency_limit
|
128
|
-
end
|
129
|
-
|
130
|
-
it 'can limit concurrency level' do
|
131
|
-
job_invocation.expects(:concurrency_level).twice.returns(level)
|
132
|
-
assert_equal level, planned.concurrency_limit
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
describe 'notifications' do
|
137
|
-
it 'creates drawer notification on succeess' do
|
138
|
-
blueprint = planned.job_invocation.build_notification
|
139
|
-
blueprint.expects(:deliver!)
|
140
|
-
planned.job_invocation.expects(:build_notification).returns(blueprint)
|
141
|
-
planned.notify_on_success(nil)
|
142
|
-
end
|
143
|
-
|
144
|
-
it 'creates drawer notification on failure' do
|
145
|
-
blueprint = planned.job_invocation.build_notification
|
146
|
-
blueprint.expects(:deliver!)
|
147
|
-
planned.job_invocation.expects(:build_notification).returns(blueprint)
|
148
|
-
planned.notify_on_failure(nil)
|
149
|
-
end
|
150
|
-
|
151
|
-
describe 'ignoring drawer notification' do
|
152
|
-
before do
|
153
|
-
blueprint = planned.job_invocation.build_notification
|
154
|
-
blueprint.expects(:deliver!)
|
155
|
-
planned.job_invocation.expects(:build_notification).returns(blueprint)
|
156
|
-
end
|
157
|
-
|
158
|
-
let(:mail) do
|
159
|
-
object = mock
|
160
|
-
object.stubs(:deliver_now)
|
161
|
-
object
|
162
|
-
end
|
163
|
-
|
164
|
-
describe 'for user subscribed to all' do
|
165
|
-
before do
|
166
|
-
planned.expects(:mail_notification_preference).returns(UserMailNotification.new(:interval => RexMailNotification::ALL_JOBS))
|
167
|
-
end
|
168
|
-
|
169
|
-
it 'sends the mail notification on success' do
|
170
|
-
RexJobMailer.expects(:job_finished).returns(mail)
|
171
|
-
planned.notify_on_success(nil)
|
172
|
-
end
|
173
|
-
|
174
|
-
it 'sends the mail notification on failure' do
|
175
|
-
RexJobMailer.expects(:job_finished).returns(mail)
|
176
|
-
planned.notify_on_failure(nil)
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
describe 'for user subscribed to failures' do
|
181
|
-
before do
|
182
|
-
planned.expects(:mail_notification_preference).returns(UserMailNotification.new(:interval => RexMailNotification::FAILED_JOBS))
|
183
|
-
end
|
184
|
-
|
185
|
-
it 'it does not send the mail notification on success' do
|
186
|
-
RexJobMailer.expects(:job_finished).never
|
187
|
-
planned.notify_on_success(nil)
|
188
|
-
end
|
189
|
-
|
190
|
-
it 'sends the mail notification on failure' do
|
191
|
-
RexJobMailer.expects(:job_finished).returns(mail)
|
192
|
-
planned.notify_on_failure(nil)
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
describe 'for user subscribed to successful jobs' do
|
197
|
-
before do
|
198
|
-
planned.expects(:mail_notification_preference).returns(UserMailNotification.new(:interval => RexMailNotification::SUCCEEDED_JOBS))
|
199
|
-
end
|
200
|
-
|
201
|
-
it 'sends the mail notification on success' do
|
202
|
-
RexJobMailer.expects(:job_finished).returns(mail)
|
203
|
-
planned.notify_on_success(nil)
|
204
|
-
end
|
205
|
-
|
206
|
-
it 'does not send the mail notification on failure' do
|
207
|
-
RexJobMailer.expects(:job_finished).never
|
208
|
-
planned.notify_on_failure(nil)
|
209
|
-
end
|
210
|
-
end
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'test_plugin_helper'
|
4
|
-
|
5
|
-
class ApiParamsTest < ActiveSupport::TestCase
|
6
|
-
describe '#format_datetime' do
|
7
|
-
let(:params) { JobInvocationComposer::ApiParams.allocate }
|
8
|
-
|
9
|
-
it 'leaves empty string as is' do
|
10
|
-
assert_equal params.send(:format_datetime, ''), ''
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'honors explicitly supplied time zone' do
|
14
|
-
Time.use_zone(ActiveSupport::TimeZone['America/New_York']) do
|
15
|
-
assert_equal '2022-07-08 08:53', params.send(:format_datetime, '2022-07-08 12:53:20 UTC')
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'implicitly honors current user\'s time zone' do
|
20
|
-
Time.use_zone(ActiveSupport::TimeZone['America/New_York']) do
|
21
|
-
assert_equal '2022-07-08 12:53', params.send(:format_datetime, '2022-07-08 12:53:20')
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
require 'test_plugin_helper'
|
2
|
-
|
3
|
-
class ForemanRemoteExecutionForemanTasksCleanerExtensionsTest < ActiveSupport::TestCase
|
4
|
-
# Apply the same stubbing as in foreman-tasks
|
5
|
-
before do
|
6
|
-
# To stop dynflow from backing up actions, execution_plans and steps
|
7
|
-
ForemanTasks.dynflow.world.persistence.adapter.stubs(:backup_to_csv)
|
8
|
-
ForemanTasks::Cleaner.any_instance.stubs(:say) # Make the tests silent
|
9
|
-
# Hack to make the tests pass due to ActiveRecord shenanigans
|
10
|
-
ForemanTasks::Cleaner.any_instance.stubs(:delete_orphaned_dynflow_tasks)
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'tries to delete associated job invocations' do
|
14
|
-
job = FactoryBot.create(:job_invocation, :with_task)
|
15
|
-
ForemanTasks::Cleaner.new(:filter => "id = #{job.task.id}").delete
|
16
|
-
assert_empty JobInvocation.where(:id => job.id)
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'removes orphaned job invocations' do
|
20
|
-
job = FactoryBot.create(:job_invocation, :with_task)
|
21
|
-
assert_equal 1, JobInvocation.where(:id => job.id).count
|
22
|
-
job.task.delete
|
23
|
-
job.reload
|
24
|
-
assert_nil job.task
|
25
|
-
refute_nil job.task_id
|
26
|
-
ForemanTasks::Cleaner.new(:filter => '').delete
|
27
|
-
assert_empty JobInvocation.where(:id => job.id)
|
28
|
-
end
|
29
|
-
end
|
@@ -1,219 +0,0 @@
|
|
1
|
-
require 'test_plugin_helper'
|
2
|
-
|
3
|
-
class ForemanRemoteExecutionHostExtensionsTest < ActiveSupport::TestCase
|
4
|
-
let(:provider) { 'SSH' }
|
5
|
-
|
6
|
-
before { User.current = FactoryBot.build(:user, :admin) }
|
7
|
-
after { User.current = nil }
|
8
|
-
|
9
|
-
describe 'ssh specific params' do
|
10
|
-
let(:host) { FactoryBot.create(:host, :with_execution) }
|
11
|
-
let(:sshkey) { 'ssh-rsa AAAAB3NzaC1yc2EAAAABJQ foo@example.com' }
|
12
|
-
|
13
|
-
before do
|
14
|
-
SmartProxy.any_instance.stubs(:pubkey).returns(sshkey)
|
15
|
-
Setting[:remote_execution_ssh_user] = 'root'
|
16
|
-
Setting[:remote_execution_effective_user_method] = 'sudo'
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'has ssh user in the parameters' do
|
20
|
-
assert_equal Setting[:remote_execution_ssh_user], host.host_param('remote_execution_ssh_user')
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'can override ssh user' do
|
24
|
-
host.host_parameters << FactoryBot.create(:host_parameter, :host => host, :name => 'remote_execution_ssh_user', :value => 'amy')
|
25
|
-
assert_equal 'amy', host.host_param('remote_execution_ssh_user')
|
26
|
-
end
|
27
|
-
|
28
|
-
it 'has effective user method in the parameters' do
|
29
|
-
assert_equal Setting[:remote_execution_effective_user_method], host.host_param('remote_execution_effective_user_method')
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'can override effective user method' do
|
33
|
-
host.host_parameters << FactoryBot.create(:host_parameter, :host => host, :name => 'remote_execution_effective_user_method', :value => 'su')
|
34
|
-
assert_equal 'su', host.host_param('remote_execution_effective_user_method')
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'has ssh keys in the parameters' do
|
38
|
-
assert_includes host.remote_execution_ssh_keys, sshkey
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'merges ssh keys from host parameters and proxies' do
|
42
|
-
key = 'ssh-rsa not-even-a-key something@somewhere.com'
|
43
|
-
host.host_parameters << FactoryBot.create(:host_parameter, :host => host, :name => 'remote_execution_ssh_keys', :value => [key])
|
44
|
-
assert_includes host.host_param('remote_execution_ssh_keys'), key
|
45
|
-
assert_includes host.host_param('remote_execution_ssh_keys'), sshkey
|
46
|
-
end
|
47
|
-
|
48
|
-
it 'merges ssh key as a string from host parameters and proxies' do
|
49
|
-
key = 'ssh-rsa not-even-a-key something@somewhere.com'
|
50
|
-
host.host_parameters << FactoryBot.create(:host_parameter, :host => host, :name => 'remote_execution_ssh_keys', :value => key)
|
51
|
-
assert_includes host.host_param('remote_execution_ssh_keys'), key
|
52
|
-
assert_includes host.host_param('remote_execution_ssh_keys'), sshkey
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'has ssh keys in the parameters even when no user specified' do
|
56
|
-
# this is a case, when using the helper in provisioning templates
|
57
|
-
FactoryBot.create(:smart_proxy, :ssh)
|
58
|
-
host.interfaces.first.subnet.remote_execution_proxies.clear
|
59
|
-
User.current = nil
|
60
|
-
assert_includes host.remote_execution_ssh_keys, sshkey
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
context 'host has multiple nics' do
|
65
|
-
let(:host) { FactoryBot.build(:host, :with_execution) }
|
66
|
-
|
67
|
-
it 'should only have one execution interface' do
|
68
|
-
host.interfaces << FactoryBot.build(:nic_managed)
|
69
|
-
host.interfaces.each { |interface| interface.execution = true }
|
70
|
-
assert_predicate host, :valid?
|
71
|
-
assert_equal 1, host.interfaces.count(&:execution?)
|
72
|
-
end
|
73
|
-
|
74
|
-
it 'returns the execution interface' do
|
75
|
-
assert_kind_of Nic::Managed, host.execution_interface
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
context 'scoped search' do
|
80
|
-
let(:job) do
|
81
|
-
job = FactoryBot.create(:job_invocation, :with_task)
|
82
|
-
job.template_invocations << FactoryBot.create(:template_invocation, :with_host, :with_failed_task)
|
83
|
-
job
|
84
|
-
end
|
85
|
-
|
86
|
-
let(:job2) do
|
87
|
-
job = FactoryBot.create(:job_invocation, :with_task)
|
88
|
-
job.template_invocations << FactoryBot.create(:template_invocation, :with_host, :with_failed_task)
|
89
|
-
job
|
90
|
-
end
|
91
|
-
|
92
|
-
it 'finds hosts for job_invocation.id' do
|
93
|
-
found_ids = Host.search_for("job_invocation.id = #{job.id}").map(&:id).sort
|
94
|
-
assert_equal job.template_invocations_host_ids.sort, found_ids
|
95
|
-
end
|
96
|
-
|
97
|
-
it 'finds hosts by job_invocation.result' do
|
98
|
-
success, failed = job.template_invocations
|
99
|
-
.partition { |template| template.run_host_job_task.result == 'success' }
|
100
|
-
found_ids = Host.search_for('job_invocation.result = success').map(&:id)
|
101
|
-
assert_equal success.map(&:host_id), found_ids
|
102
|
-
found_ids = Host.search_for('job_invocation.result = failed').map(&:id)
|
103
|
-
assert_equal failed.map(&:host_id), found_ids
|
104
|
-
end
|
105
|
-
|
106
|
-
it 'finds hosts by job_invocation.id and job_invocation.result' do
|
107
|
-
# Force evaluation of the jobs
|
108
|
-
job
|
109
|
-
job2
|
110
|
-
|
111
|
-
assert_equal 2, Host.search_for("job_invocation.id = #{job.id}").count
|
112
|
-
assert_equal 2, Host.search_for("job_invocation.id = #{job2.id}").count
|
113
|
-
assert_equal 2, Host.search_for('job_invocation.result = success').count
|
114
|
-
assert_equal 2, Host.search_for('job_invocation.result = failed').count
|
115
|
-
|
116
|
-
success, failed = job.template_invocations
|
117
|
-
.partition { |template| template.run_host_job_task.result == 'success' }
|
118
|
-
found_ids = Host.search_for("job_invocation.id = #{job.id} AND job_invocation.result = success").map(&:id)
|
119
|
-
assert_equal success.map(&:host_id), found_ids
|
120
|
-
found_ids = Host.search_for("job_invocation.id = #{job.id} AND job_invocation.result = failed").map(&:id)
|
121
|
-
assert_equal failed.map(&:host_id), found_ids
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
describe 'proxy determination strategies' do
|
126
|
-
context 'subnet strategy' do
|
127
|
-
let(:host) { FactoryBot.build(:host, :with_execution) }
|
128
|
-
it { assert_includes host.remote_execution_proxies(provider)[:subnet], host.subnet.remote_execution_proxies.first }
|
129
|
-
end
|
130
|
-
|
131
|
-
context 'fallback strategy' do
|
132
|
-
let(:host) { FactoryBot.build(:host, :with_tftp_subnet) }
|
133
|
-
|
134
|
-
context 'enabled' do
|
135
|
-
before do
|
136
|
-
Setting[:remote_execution_fallback_proxy] = true
|
137
|
-
host.subnet.tftp.features << FactoryBot.create(:feature, :ssh)
|
138
|
-
end
|
139
|
-
|
140
|
-
it 'returns a fallback proxy' do
|
141
|
-
assert_includes host.remote_execution_proxies(provider)[:fallback], host.subnet.tftp
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
context 'disabled' do
|
146
|
-
before do
|
147
|
-
Setting[:remote_execution_fallback_proxy] = false
|
148
|
-
host.subnet.tftp.features << FactoryBot.create(:feature, :ssh)
|
149
|
-
end
|
150
|
-
|
151
|
-
it 'returns no proxy' do
|
152
|
-
assert_empty host.remote_execution_proxies(provider)[:fallback]
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
context 'global strategy' do
|
158
|
-
let(:tax_organization) { FactoryBot.build(:organization) }
|
159
|
-
let(:tax_location) { FactoryBot.build(:location) }
|
160
|
-
let(:host) { FactoryBot.build(:host, :organization => tax_organization, :location => tax_location) }
|
161
|
-
let(:proxy_in_taxonomies) { FactoryBot.create(:smart_proxy, :ssh, :organizations => [tax_organization], :locations => [tax_location]) }
|
162
|
-
let(:proxy_no_taxonomies) { FactoryBot.create(:smart_proxy, :ssh) }
|
163
|
-
|
164
|
-
context 'enabled' do
|
165
|
-
before { Setting[:remote_execution_global_proxy] = true }
|
166
|
-
|
167
|
-
it 'returns correct proxies confined by taxonomies' do
|
168
|
-
proxy_in_taxonomies
|
169
|
-
proxy_no_taxonomies
|
170
|
-
assert_includes host.remote_execution_proxies(provider)[:global], proxy_in_taxonomies
|
171
|
-
refute_includes host.remote_execution_proxies(provider)[:global], proxy_no_taxonomies
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
context 'disabled' do
|
176
|
-
before { Setting[:remote_execution_global_proxy] = false }
|
177
|
-
it 'returns no proxy' do
|
178
|
-
assert_empty host.remote_execution_proxies(provider)[:global]
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
describe '#execution_scope' do
|
185
|
-
let(:host) { FactoryBot.create(:host) }
|
186
|
-
let(:infra_host) { FactoryBot.create(:host, :with_infrastructure_facet) }
|
187
|
-
|
188
|
-
before do
|
189
|
-
host
|
190
|
-
infra_host
|
191
|
-
end
|
192
|
-
|
193
|
-
context 'without infrastructure host permission' do
|
194
|
-
it 'omits the infrastructure host' do
|
195
|
-
setup_user('view', 'hosts')
|
196
|
-
|
197
|
-
hosts = ::Host::Managed.execution_scope
|
198
|
-
assert_includes hosts, host
|
199
|
-
refute_includes hosts, infra_host
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
context 'with infrastructure host permission' do
|
204
|
-
it 'finds the host as admin' do
|
205
|
-
assert User.current.admin?
|
206
|
-
hosts = ::Host::Managed.execution_scope
|
207
|
-
assert_includes hosts, host
|
208
|
-
assert_includes hosts, infra_host
|
209
|
-
end
|
210
|
-
|
211
|
-
it 'finds the host as user with needed permissions' do
|
212
|
-
setup_user('execute_jobs_on', 'infrastructure_hosts')
|
213
|
-
hosts = ::Host::Managed.execution_scope
|
214
|
-
assert_includes hosts, host
|
215
|
-
assert_includes hosts, infra_host
|
216
|
-
end
|
217
|
-
end
|
218
|
-
end
|
219
|
-
end
|
@@ -1,9 +0,0 @@
|
|
1
|
-
require 'test_plugin_helper'
|
2
|
-
|
3
|
-
class ForemanRemoteExecutionNicExtensionsTest < ActiveSupport::TestCase
|
4
|
-
let(:host) { FactoryBot.create(:host) }
|
5
|
-
|
6
|
-
it 'sets the first primary interface as the execution interface' do
|
7
|
-
assert_equal host.interfaces.first, host.execution_interface
|
8
|
-
end
|
9
|
-
end
|
@@ -1,92 +0,0 @@
|
|
1
|
-
require 'test_plugin_helper'
|
2
|
-
|
3
|
-
class ExecutionTaskStatusMapperTest < ActiveSupport::TestCase
|
4
|
-
describe '.sql_conditions_for(status)' do
|
5
|
-
let(:subject) { HostStatus::ExecutionStatus::ExecutionTaskStatusMapper }
|
6
|
-
|
7
|
-
it 'accepts status number as well as string representation' do
|
8
|
-
assert_equal subject.sql_conditions_for('failed'), subject.sql_conditions_for(HostStatus::ExecutionStatus::ERROR)
|
9
|
-
end
|
10
|
-
|
11
|
-
it 'does not find any task for unknown state' do
|
12
|
-
assert_equal [ '1 = 0' ], subject.sql_conditions_for(-1)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
let(:task) { ForemanTasks::Task.new }
|
17
|
-
let(:mapper) { HostStatus::ExecutionStatus::ExecutionTaskStatusMapper.new(task) }
|
18
|
-
|
19
|
-
describe '#status' do
|
20
|
-
let(:subject) { mapper }
|
21
|
-
|
22
|
-
describe 'is queued' do
|
23
|
-
context 'when there is no task' do
|
24
|
-
before { subject.task = nil }
|
25
|
-
specify { assert_equal HostStatus::ExecutionStatus::QUEUED, subject.status }
|
26
|
-
end
|
27
|
-
|
28
|
-
context 'when the task is scheduled in future' do
|
29
|
-
before { subject.task.state = 'scheduled' }
|
30
|
-
specify { assert_equal HostStatus::ExecutionStatus::QUEUED, subject.status }
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
context 'when task is stopped' do
|
35
|
-
before { subject.task.state = 'stopped' }
|
36
|
-
|
37
|
-
describe 'is succeeded' do
|
38
|
-
context 'without error' do
|
39
|
-
before { subject.task.result = 'success' }
|
40
|
-
specify { assert_equal HostStatus::ExecutionStatus::OK, subject.status }
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
describe 'is failed' do
|
45
|
-
context 'with error' do
|
46
|
-
before { subject.task.result = 'error' }
|
47
|
-
specify { assert_equal HostStatus::ExecutionStatus::ERROR, subject.status }
|
48
|
-
end
|
49
|
-
|
50
|
-
context 'without error but just with warning (sub task failed)' do
|
51
|
-
before { subject.task.result = 'warning' }
|
52
|
-
specify { assert_equal HostStatus::ExecutionStatus::ERROR, subject.status }
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
context 'when task is pending' do
|
58
|
-
before { subject.task.state = 'running' }
|
59
|
-
|
60
|
-
describe 'is pending' do
|
61
|
-
specify { assert_equal HostStatus::ExecutionStatus::RUNNING, subject.status }
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
describe '#status_label' do
|
67
|
-
let(:subject) { mapper.status_label }
|
68
|
-
|
69
|
-
context 'status is OK' do
|
70
|
-
before do
|
71
|
-
mapper.task.state = 'stopped'
|
72
|
-
mapper.task.result = 'success'
|
73
|
-
end
|
74
|
-
|
75
|
-
it 'returns ok label' do
|
76
|
-
assert_equal HostStatus::ExecutionStatus::STATUS_NAMES[HostStatus::ExecutionStatus::OK], subject
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
context 'status is ERROR' do
|
81
|
-
before do
|
82
|
-
mapper.task.state = 'stopped'
|
83
|
-
mapper.task.result = 'error'
|
84
|
-
end
|
85
|
-
|
86
|
-
it 'returns failed label' do
|
87
|
-
assert_equal HostStatus::ExecutionStatus::STATUS_NAMES[HostStatus::ExecutionStatus::ERROR], subject
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
end
|