foreman_openbolt 1.1.0 → 1.1.1
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/README.md +9 -15
- data/Rakefile +0 -0
- data/lib/foreman_openbolt/version.rb +1 -1
- data/package.json +1 -1
- data/test/acceptance/acceptance_helper.rb +146 -0
- data/test/acceptance/docker/docker-compose.yml +69 -0
- data/test/acceptance/docker/foreman/Dockerfile +45 -0
- data/test/acceptance/docker/foreman/entrypoint.sh +26 -0
- data/test/acceptance/docker/target/Dockerfile +29 -0
- data/test/acceptance/docker/target/entrypoint.sh +11 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/complex_params.json +30 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/complex_params.sh +16 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/echo.json +13 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/echo.sh +3 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/failing_task.json +8 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/failing_task.sh +3 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/noop_task.json +8 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/noop_task.sh +2 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/slow_task.json +14 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/slow_task.sh +3 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/target_conditional.json +13 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/target_conditional.sh +9 -0
- data/test/acceptance/fixtures/openbolt.yml +7 -0
- data/test/acceptance/tests/error_handling_test.rb +40 -0
- data/test/acceptance/tests/host_selector_test.rb +31 -0
- data/test/acceptance/tests/launch_task_test.rb +96 -0
- data/test/acceptance/tests/parameter_table_test.rb +61 -0
- data/test/acceptance/tests/settings_test.rb +95 -0
- data/test/acceptance/tests/ssh_options_test.rb +77 -0
- data/test/acceptance/tests/task_execution_test.rb +40 -0
- data/test/acceptance/tests/task_history_test.rb +84 -0
- data/test/acceptance/tests/transport_options_test.rb +121 -0
- data/test/test_plugin_helper.rb +17 -0
- data/test/unit/controllers/task_controller_test.rb +351 -0
- data/test/unit/docker/Dockerfile +47 -0
- data/test/unit/docker/docker-compose.yml +33 -0
- data/test/unit/docker/entrypoint.sh +4 -0
- data/test/unit/factories/foreman_openbolt_factories.rb +39 -0
- data/test/unit/lib/actions/cleanup_proxy_artifacts_test.rb +51 -0
- data/test/unit/lib/actions/poll_task_status_test.rb +141 -0
- data/test/unit/lib/proxy_api/openbolt_test.rb +174 -0
- data/test/unit/models/task_job_test.rb +278 -0
- metadata +39 -1
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test_plugin_helper'
|
|
4
|
+
|
|
5
|
+
class ProxyApiOpenboltTest < ForemanOpenbolt::PluginTestCase
|
|
6
|
+
PROXY_URL = 'https://proxy.example.com:8443'
|
|
7
|
+
|
|
8
|
+
setup do
|
|
9
|
+
@api = ProxyAPI::Openbolt.new(url: PROXY_URL)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
context 'task_names' do
|
|
13
|
+
test 'returns task names from fetched tasks' do
|
|
14
|
+
tasks = { 'mymod::install' => {}, 'mymod::mytask' => {} }
|
|
15
|
+
stub_request(:get, "#{PROXY_URL}/openbolt/tasks")
|
|
16
|
+
.to_return(status: 200, body: tasks.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
17
|
+
|
|
18
|
+
assert_equal %w[mymod::install mymod::mytask], @api.task_names
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
context 'fetch_tasks' do
|
|
23
|
+
test 'fetches and parses task list from proxy' do
|
|
24
|
+
tasks = { 'mymod::install' => { 'description' => 'Install a package' } }
|
|
25
|
+
stub_request(:get, "#{PROXY_URL}/openbolt/tasks")
|
|
26
|
+
.to_return(status: 200, body: tasks.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
27
|
+
|
|
28
|
+
result = @api.fetch_tasks
|
|
29
|
+
assert_equal tasks, result
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
test 'raises on proxy HTTP error' do
|
|
33
|
+
stub_request(:get, "#{PROXY_URL}/openbolt/tasks")
|
|
34
|
+
.to_return(status: 500, body: 'Internal Server Error')
|
|
35
|
+
|
|
36
|
+
assert_raises(RestClient::InternalServerError) { @api.fetch_tasks }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
test 'raises ProxyException on unparseable response body' do
|
|
40
|
+
stub_request(:get, "#{PROXY_URL}/openbolt/tasks")
|
|
41
|
+
.to_return(status: 200, body: nil)
|
|
42
|
+
|
|
43
|
+
assert_raises(ProxyAPI::ProxyException) { @api.fetch_tasks }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
test 'raises ProxyException on invalid JSON' do
|
|
47
|
+
stub_request(:get, "#{PROXY_URL}/openbolt/tasks")
|
|
48
|
+
.to_return(status: 200, body: 'not json', headers: { 'Content-Type' => 'text/plain' })
|
|
49
|
+
|
|
50
|
+
assert_raises(ProxyAPI::ProxyException) { @api.fetch_tasks }
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
context 'reload_tasks' do
|
|
55
|
+
test 'fetches from reload endpoint and updates cached tasks' do
|
|
56
|
+
original_tasks = { 'mymod::install' => { 'description' => 'Install something' } }
|
|
57
|
+
stub_request(:get, "#{PROXY_URL}/openbolt/tasks")
|
|
58
|
+
.to_return(status: 200, body: original_tasks.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
59
|
+
|
|
60
|
+
assert_equal original_tasks, @api.tasks
|
|
61
|
+
|
|
62
|
+
reloaded_tasks = { 'mymod::install' => {}, 'mymod::mytask' => { 'description' => 'A new task' } }
|
|
63
|
+
stub_request(:get, "#{PROXY_URL}/openbolt/tasks/reload")
|
|
64
|
+
.to_return(status: 200, body: reloaded_tasks.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
65
|
+
|
|
66
|
+
result = @api.reload_tasks
|
|
67
|
+
assert_equal reloaded_tasks, result
|
|
68
|
+
assert_equal reloaded_tasks, @api.tasks
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
context 'openbolt_options' do
|
|
73
|
+
test 'fetches and parses options from proxy' do
|
|
74
|
+
options = { 'transport' => { 'type' => 'string', 'default' => 'ssh' } }
|
|
75
|
+
stub_request(:get, "#{PROXY_URL}/openbolt/tasks/options")
|
|
76
|
+
.to_return(status: 200, body: options.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
77
|
+
|
|
78
|
+
result = @api.openbolt_options
|
|
79
|
+
assert_equal options, result
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
test 'memoizes the result' do
|
|
83
|
+
options = { 'transport' => { 'type' => 'string' } }
|
|
84
|
+
stub = stub_request(:get, "#{PROXY_URL}/openbolt/tasks/options")
|
|
85
|
+
.to_return(status: 200, body: options.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
86
|
+
|
|
87
|
+
@api.openbolt_options
|
|
88
|
+
@api.openbolt_options
|
|
89
|
+
assert_requested(stub, times: 1)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
context 'launch_task' do
|
|
94
|
+
test 'posts task request and returns parsed response' do
|
|
95
|
+
response_body = { 'id' => 'job-abc-123' }
|
|
96
|
+
stub_request(:post, "#{PROXY_URL}/openbolt/launch/task")
|
|
97
|
+
.to_return(status: 200, body: response_body.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
98
|
+
|
|
99
|
+
result = @api.launch_task(
|
|
100
|
+
name: 'mymod::install',
|
|
101
|
+
targets: 'host1.example.com',
|
|
102
|
+
parameters: { 'name' => 'nginx' },
|
|
103
|
+
options: { 'transport' => 'ssh' }
|
|
104
|
+
)
|
|
105
|
+
assert_equal response_body, result
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
test 'sends correct JSON payload with parameters, options, and targets' do
|
|
109
|
+
stub = stub_request(:post, "#{PROXY_URL}/openbolt/launch/task")
|
|
110
|
+
.with do |request|
|
|
111
|
+
body = JSON.parse(request.body)
|
|
112
|
+
body['name'] == 'mymod::mytask' &&
|
|
113
|
+
body['targets'] == 'host1.example.com,host2.example.com' &&
|
|
114
|
+
body['parameters'] == { 'name' => 'nginx', 'version' => '1.0' } &&
|
|
115
|
+
body['options'] == { 'transport' => 'ssh', 'run-as' => 'root' }
|
|
116
|
+
end
|
|
117
|
+
.to_return(status: 200, body: '{"id": "job-1"}', headers: { 'Content-Type' => 'application/json' })
|
|
118
|
+
|
|
119
|
+
@api.launch_task(
|
|
120
|
+
name: 'mymod::mytask',
|
|
121
|
+
targets: 'host1.example.com,host2.example.com',
|
|
122
|
+
parameters: { 'name' => 'nginx', 'version' => '1.0' },
|
|
123
|
+
options: { 'transport' => 'ssh', 'run-as' => 'root' }
|
|
124
|
+
)
|
|
125
|
+
assert_requested(stub)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
context 'job_status' do
|
|
130
|
+
test 'fetches job status by ID' do
|
|
131
|
+
status_body = { 'status' => 'running' }
|
|
132
|
+
stub_request(:get, "#{PROXY_URL}/openbolt/job/test-123/status")
|
|
133
|
+
.to_return(status: 200, body: status_body.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
134
|
+
|
|
135
|
+
result = @api.job_status(job_id: 'test-123')
|
|
136
|
+
assert_equal status_body, result
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
context 'job_result' do
|
|
141
|
+
test 'fetches job result by ID' do
|
|
142
|
+
result_body = { 'value' => { 'items' => [] }, 'log' => 'done' }
|
|
143
|
+
stub_request(:get, "#{PROXY_URL}/openbolt/job/test-123/result")
|
|
144
|
+
.to_return(status: 200, body: result_body.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
145
|
+
|
|
146
|
+
result = @api.job_result(job_id: 'test-123')
|
|
147
|
+
assert_equal result_body, result
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
context 'delete_job_artifacts' do
|
|
152
|
+
test 'sends DELETE for job artifacts' do
|
|
153
|
+
stub_request(:delete, "#{PROXY_URL}/openbolt/job/test-123/artifacts")
|
|
154
|
+
.to_return(status: 200, body: '{"deleted": true}', headers: { 'Content-Type' => 'application/json' })
|
|
155
|
+
|
|
156
|
+
result = @api.delete_job_artifacts(job_id: 'test-123')
|
|
157
|
+
assert_equal({ 'deleted' => true }, result)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
context 'connection errors' do
|
|
162
|
+
test 'raises on timeout' do
|
|
163
|
+
stub_request(:get, "#{PROXY_URL}/openbolt/tasks").to_timeout
|
|
164
|
+
|
|
165
|
+
assert_raises(RestClient::Exceptions::OpenTimeout) { @api.fetch_tasks }
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
test 'raises on connection refused' do
|
|
169
|
+
stub_request(:get, "#{PROXY_URL}/openbolt/tasks").to_raise(Errno::ECONNREFUSED)
|
|
170
|
+
|
|
171
|
+
assert_raises(Errno::ECONNREFUSED) { @api.fetch_tasks }
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test_plugin_helper'
|
|
4
|
+
|
|
5
|
+
class TaskJobTest < ForemanOpenbolt::PluginTestCase
|
|
6
|
+
context 'status helpers' do
|
|
7
|
+
test 'completed? returns true for completed statuses' do
|
|
8
|
+
ForemanOpenbolt::TaskJob::COMPLETED_STATUSES.each do |status|
|
|
9
|
+
job = FactoryBot.build(:task_job, status: status)
|
|
10
|
+
assert job.completed?, "Expected completed? to be true for status '#{status}'"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
test 'completed? returns false for running statuses' do
|
|
15
|
+
ForemanOpenbolt::TaskJob::RUNNING_STATUSES.each do |status|
|
|
16
|
+
job = FactoryBot.build(:task_job, status: status)
|
|
17
|
+
assert_not job.completed?, "Expected completed? to be false for status '#{status}'"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
test 'running? returns true for running statuses' do
|
|
22
|
+
ForemanOpenbolt::TaskJob::RUNNING_STATUSES.each do |status|
|
|
23
|
+
job = FactoryBot.build(:task_job, status: status)
|
|
24
|
+
assert job.running?, "Expected running? to be true for status '#{status}'"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
test 'running? returns false for completed statuses' do
|
|
29
|
+
ForemanOpenbolt::TaskJob::COMPLETED_STATUSES.each do |status|
|
|
30
|
+
job = FactoryBot.build(:task_job, status: status)
|
|
31
|
+
assert_not job.running?, "Expected running? to be false for status '#{status}'"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context 'duration' do
|
|
37
|
+
# submitted_at could be nil for old rows that predate the column
|
|
38
|
+
test 'returns nil when submitted_at is nil' do
|
|
39
|
+
job = FactoryBot.build(:task_job, submitted_at: nil)
|
|
40
|
+
assert_nil job.duration
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
test 'returns nil when completed_at is nil' do
|
|
44
|
+
job = FactoryBot.build(:task_job, completed_at: nil)
|
|
45
|
+
assert_nil job.duration
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
test 'returns difference in seconds when both timestamps present' do
|
|
49
|
+
submitted = Time.current
|
|
50
|
+
completed = submitted + 45.seconds
|
|
51
|
+
job = FactoryBot.build(:task_job, submitted_at: submitted, completed_at: completed)
|
|
52
|
+
assert_in_delta 45.0, job.duration, 0.1
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
context 'target_count' do
|
|
57
|
+
test 'returns 0 when targets is nil' do
|
|
58
|
+
job = FactoryBot.build(:task_job)
|
|
59
|
+
job.targets = nil
|
|
60
|
+
assert_equal 0, job.target_count
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
test 'returns correct count for populated targets' do
|
|
64
|
+
job = FactoryBot.build(:task_job, targets: ['a.com', 'b.com', 'c.com'])
|
|
65
|
+
assert_equal 3, job.target_count
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
context 'formatted_targets' do
|
|
70
|
+
test 'returns empty string when targets is nil' do
|
|
71
|
+
job = FactoryBot.build(:task_job)
|
|
72
|
+
job.targets = nil
|
|
73
|
+
assert_equal '', job.formatted_targets
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
test 'returns CSV string for populated targets' do
|
|
77
|
+
job = FactoryBot.build(:task_job, targets: ['host1.com', 'host2.com'])
|
|
78
|
+
assert_equal 'host1.com, host2.com', job.formatted_targets
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
context 'validations' do
|
|
83
|
+
test 'valid with all required attributes' do
|
|
84
|
+
job = FactoryBot.build(:task_job)
|
|
85
|
+
assert job.valid?
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
test 'invalid without task_name' do
|
|
89
|
+
job = FactoryBot.build(:task_job, task_name: nil)
|
|
90
|
+
assert_not job.valid?
|
|
91
|
+
assert_includes job.errors[:task_name], "can't be blank"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
test 'invalid without job_id' do
|
|
95
|
+
job = FactoryBot.build(:task_job, job_id: nil)
|
|
96
|
+
assert_not job.valid?
|
|
97
|
+
assert_includes job.errors[:job_id], "can't be blank"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
test 'invalid with duplicate job_id' do
|
|
101
|
+
FactoryBot.create(:task_job, job_id: 'unique-id')
|
|
102
|
+
duplicate = FactoryBot.build(:task_job, job_id: 'unique-id')
|
|
103
|
+
assert_not duplicate.valid?
|
|
104
|
+
assert_includes duplicate.errors[:job_id], 'has already been taken'
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
test 'invalid with unknown status' do
|
|
108
|
+
job = FactoryBot.build(:task_job, status: 'unknown')
|
|
109
|
+
assert_not job.valid?
|
|
110
|
+
assert_includes job.errors[:status], 'is not included in the list'
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
test 'invalid without targets' do
|
|
114
|
+
job = FactoryBot.build(:task_job, targets: nil)
|
|
115
|
+
assert_not job.valid?
|
|
116
|
+
assert_includes job.errors[:targets], "can't be blank"
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
context 'scopes' do
|
|
121
|
+
setup do
|
|
122
|
+
@proxy = FactoryBot.create(:smart_proxy)
|
|
123
|
+
@pending_job = FactoryBot.create(:task_job, smart_proxy: @proxy, status: 'pending')
|
|
124
|
+
@running_job = FactoryBot.create(:task_job, smart_proxy: @proxy, status: 'running')
|
|
125
|
+
@success_job = FactoryBot.create(:task_job, :success, smart_proxy: @proxy)
|
|
126
|
+
@failure_job = FactoryBot.create(:task_job, :failure, smart_proxy: @proxy)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
test 'running scope returns pending and running jobs' do
|
|
130
|
+
results = ForemanOpenbolt::TaskJob.running
|
|
131
|
+
assert_includes results, @pending_job
|
|
132
|
+
assert_includes results, @running_job
|
|
133
|
+
assert_not_includes results, @success_job
|
|
134
|
+
assert_not_includes results, @failure_job
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
test 'completed scope returns completed jobs' do
|
|
138
|
+
results = ForemanOpenbolt::TaskJob.completed
|
|
139
|
+
assert_includes results, @success_job
|
|
140
|
+
assert_includes results, @failure_job
|
|
141
|
+
assert_not_includes results, @pending_job
|
|
142
|
+
assert_not_includes results, @running_job
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
test 'recent scope orders by submitted_at' do
|
|
146
|
+
results = ForemanOpenbolt::TaskJob.recent
|
|
147
|
+
submitted_times = results.map(&:submitted_at)
|
|
148
|
+
assert_equal submitted_times, submitted_times.sort.reverse
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
test 'for_proxy scope filters by smart proxy' do
|
|
152
|
+
other_proxy = FactoryBot.create(:smart_proxy)
|
|
153
|
+
other_job = FactoryBot.create(:task_job, smart_proxy: other_proxy)
|
|
154
|
+
|
|
155
|
+
results = ForemanOpenbolt::TaskJob.for_proxy(@proxy)
|
|
156
|
+
assert_includes results, @pending_job
|
|
157
|
+
assert_not_includes results, other_job
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
context 'callbacks' do
|
|
162
|
+
test 'set_completed_at sets completed_at when status changes to completed' do
|
|
163
|
+
job = FactoryBot.create(:task_job, status: 'running')
|
|
164
|
+
assert_nil job.completed_at
|
|
165
|
+
|
|
166
|
+
job.update!(status: 'success')
|
|
167
|
+
assert_not_nil job.completed_at
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
test 'set_completed_at does not set completed_at when status stays running' do
|
|
171
|
+
job = FactoryBot.create(:task_job, status: 'running')
|
|
172
|
+
job.update!(task_name: 'different::task')
|
|
173
|
+
assert_nil job.completed_at
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
test 'cleanup_proxy_artifacts schedules Dynflow action when result is saved' do
|
|
177
|
+
job = FactoryBot.create(:task_job, status: 'success', completed_at: Time.current)
|
|
178
|
+
ForemanTasks.expects(:async_task).with(
|
|
179
|
+
::Actions::ForemanOpenbolt::CleanupProxyArtifacts,
|
|
180
|
+
job.smart_proxy_id,
|
|
181
|
+
job.job_id
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
job.update!(result: { 'items' => [] })
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
test 'cleanup_proxy_artifacts does not schedule cleanup for running jobs' do
|
|
188
|
+
job = FactoryBot.create(:task_job, status: 'running')
|
|
189
|
+
ForemanTasks.expects(:async_task).never
|
|
190
|
+
|
|
191
|
+
job.update!(result: { 'items' => [] })
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
test 'cleanup_proxy_artifacts does not raise when scheduling fails' do
|
|
195
|
+
job = FactoryBot.create(:task_job, status: 'success', completed_at: Time.current)
|
|
196
|
+
ForemanTasks.expects(:async_task).raises(StandardError, 'Dynflow unavailable')
|
|
197
|
+
|
|
198
|
+
assert_nothing_raised do
|
|
199
|
+
job.update!(result: { 'items' => [] })
|
|
200
|
+
end
|
|
201
|
+
assert_equal({ 'items' => [] }, job.reload.result)
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
context 'create_from_execution!' do
|
|
206
|
+
test 'creates a task job with the correct attributes' do
|
|
207
|
+
proxy = FactoryBot.create(:smart_proxy)
|
|
208
|
+
job = ForemanOpenbolt::TaskJob.create_from_execution!(
|
|
209
|
+
proxy: proxy,
|
|
210
|
+
task_name: 'mymod::mytask',
|
|
211
|
+
task_description: 'Restart a service',
|
|
212
|
+
targets: ['web1.example.com'],
|
|
213
|
+
job_id: 'exec-123',
|
|
214
|
+
parameters: { 'service' => 'nginx' },
|
|
215
|
+
options: { 'transport' => 'ssh' }
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
assert_equal 'exec-123', job.job_id
|
|
219
|
+
assert_equal proxy, job.smart_proxy
|
|
220
|
+
assert_equal 'mymod::mytask', job.task_name
|
|
221
|
+
assert_equal 'Restart a service', job.task_description
|
|
222
|
+
assert_equal ['web1.example.com'], job.targets
|
|
223
|
+
assert_equal({ 'service' => 'nginx' }, job.task_parameters)
|
|
224
|
+
assert_equal({ 'transport' => 'ssh' }, job.openbolt_options)
|
|
225
|
+
assert_equal 'pending', job.status
|
|
226
|
+
assert_not_nil job.submitted_at
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
context 'update_from_proxy_result!' do
|
|
231
|
+
setup do
|
|
232
|
+
@job = FactoryBot.create(:task_job, status: 'running')
|
|
233
|
+
ForemanTasks.stubs(:async_task)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
test 'updates status, command, result, and log from proxy result' do
|
|
237
|
+
@job.update_from_proxy_result!({
|
|
238
|
+
'status' => 'success',
|
|
239
|
+
'command' => 'bolt task run mymod::install',
|
|
240
|
+
'value' => { 'items' => [{ 'status' => 'success' }] },
|
|
241
|
+
'log' => 'Task completed successfully',
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
@job.reload
|
|
245
|
+
assert_equal 'success', @job.status
|
|
246
|
+
assert_equal 'bolt task run mymod::install', @job.command
|
|
247
|
+
assert_equal({ 'items' => [{ 'status' => 'success' }] }, @job.result)
|
|
248
|
+
assert_equal 'Task completed successfully', @job.log
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
test 'skips update for blank proxy result' do
|
|
252
|
+
original_status = @job.status
|
|
253
|
+
@job.update_from_proxy_result!(nil)
|
|
254
|
+
assert_equal original_status, @job.reload.status
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
test 'skips update for empty hash' do
|
|
258
|
+
original_status = @job.status
|
|
259
|
+
@job.update_from_proxy_result!({})
|
|
260
|
+
assert_equal original_status, @job.reload.status
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
test 'only updates fields present in the result' do
|
|
264
|
+
@job.update_from_proxy_result!({ 'status' => 'failure' })
|
|
265
|
+
@job.reload
|
|
266
|
+
assert_equal 'failure', @job.status
|
|
267
|
+
assert_nil @job.command
|
|
268
|
+
assert_nil @job.result
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
test 'sets result to nil when key is present with nil value' do
|
|
272
|
+
@job.update!(result: { 'items' => [{ 'status' => 'success' }] })
|
|
273
|
+
@job.update_from_proxy_result!({ 'status' => 'success', 'value' => nil })
|
|
274
|
+
@job.reload
|
|
275
|
+
assert_nil @job.result
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: foreman_openbolt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Overlook InfraTech
|
|
@@ -60,6 +60,44 @@ files:
|
|
|
60
60
|
- locale/foreman_openbolt.pot
|
|
61
61
|
- locale/gemspec.rb
|
|
62
62
|
- package.json
|
|
63
|
+
- test/acceptance/acceptance_helper.rb
|
|
64
|
+
- test/acceptance/docker/docker-compose.yml
|
|
65
|
+
- test/acceptance/docker/foreman/Dockerfile
|
|
66
|
+
- test/acceptance/docker/foreman/entrypoint.sh
|
|
67
|
+
- test/acceptance/docker/target/Dockerfile
|
|
68
|
+
- test/acceptance/docker/target/entrypoint.sh
|
|
69
|
+
- test/acceptance/fixtures/modules/acceptance/tasks/complex_params.json
|
|
70
|
+
- test/acceptance/fixtures/modules/acceptance/tasks/complex_params.sh
|
|
71
|
+
- test/acceptance/fixtures/modules/acceptance/tasks/echo.json
|
|
72
|
+
- test/acceptance/fixtures/modules/acceptance/tasks/echo.sh
|
|
73
|
+
- test/acceptance/fixtures/modules/acceptance/tasks/failing_task.json
|
|
74
|
+
- test/acceptance/fixtures/modules/acceptance/tasks/failing_task.sh
|
|
75
|
+
- test/acceptance/fixtures/modules/acceptance/tasks/noop_task.json
|
|
76
|
+
- test/acceptance/fixtures/modules/acceptance/tasks/noop_task.sh
|
|
77
|
+
- test/acceptance/fixtures/modules/acceptance/tasks/slow_task.json
|
|
78
|
+
- test/acceptance/fixtures/modules/acceptance/tasks/slow_task.sh
|
|
79
|
+
- test/acceptance/fixtures/modules/acceptance/tasks/target_conditional.json
|
|
80
|
+
- test/acceptance/fixtures/modules/acceptance/tasks/target_conditional.sh
|
|
81
|
+
- test/acceptance/fixtures/openbolt.yml
|
|
82
|
+
- test/acceptance/tests/error_handling_test.rb
|
|
83
|
+
- test/acceptance/tests/host_selector_test.rb
|
|
84
|
+
- test/acceptance/tests/launch_task_test.rb
|
|
85
|
+
- test/acceptance/tests/parameter_table_test.rb
|
|
86
|
+
- test/acceptance/tests/settings_test.rb
|
|
87
|
+
- test/acceptance/tests/ssh_options_test.rb
|
|
88
|
+
- test/acceptance/tests/task_execution_test.rb
|
|
89
|
+
- test/acceptance/tests/task_history_test.rb
|
|
90
|
+
- test/acceptance/tests/transport_options_test.rb
|
|
91
|
+
- test/test_plugin_helper.rb
|
|
92
|
+
- test/unit/controllers/task_controller_test.rb
|
|
93
|
+
- test/unit/docker/Dockerfile
|
|
94
|
+
- test/unit/docker/docker-compose.yml
|
|
95
|
+
- test/unit/docker/entrypoint.sh
|
|
96
|
+
- test/unit/factories/foreman_openbolt_factories.rb
|
|
97
|
+
- test/unit/lib/actions/cleanup_proxy_artifacts_test.rb
|
|
98
|
+
- test/unit/lib/actions/poll_task_status_test.rb
|
|
99
|
+
- test/unit/lib/proxy_api/openbolt_test.rb
|
|
100
|
+
- test/unit/models/task_job_test.rb
|
|
63
101
|
- webpack/__mocks__/foremanReact/common/I18n.js
|
|
64
102
|
- webpack/__mocks__/foremanReact/components/ToastsList/index.js
|
|
65
103
|
- webpack/__mocks__/foremanReact/redux/API/index.js
|