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,60 +0,0 @@
|
|
1
|
-
require 'test_plugin_helper'
|
2
|
-
|
3
|
-
class JobReportTemplateTest < ActiveSupport::TestCase
|
4
|
-
class FakeTask < OpenStruct
|
5
|
-
class Jail < Safemode::Jail
|
6
|
-
allow :action_continuous_output, :result, :ended_at
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
context 'with valid job invocation report template' do
|
11
|
-
let(:job_invocation_template) do
|
12
|
-
file_path = File.read(File.expand_path(Rails.root + "app/views/unattended/report_templates/job_-_invocation_report.erb"))
|
13
|
-
template = ReportTemplate.import_without_save("Job Invocation Report Template", file_path)
|
14
|
-
template.save!
|
15
|
-
template
|
16
|
-
end
|
17
|
-
|
18
|
-
describe 'template setting' do
|
19
|
-
it 'in settings includes only report templates with job_id input' do
|
20
|
-
FactoryBot.create(:report_template, name: 'Template 1')
|
21
|
-
job_invocation_template
|
22
|
-
templates = ForemanRemoteExecution.job_invocation_report_templates_select
|
23
|
-
|
24
|
-
assert_include templates, 'Job Invocation Report Template'
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
describe 'task reporting' do
|
29
|
-
let(:fake_outputs) do
|
30
|
-
[
|
31
|
-
{ 'output_type' => 'stderr', 'output' => "error" },
|
32
|
-
{ 'output_type' => 'stdout', 'output' => "output" },
|
33
|
-
{ 'output_type' => 'debug', 'output' => "debug" },
|
34
|
-
]
|
35
|
-
end
|
36
|
-
let(:fake_task) { FakeTask.new(result: 'success', action_continuous_output: fake_outputs, :ended_at => Time.new(2020, 12, 1, 0, 0, 0).utc) }
|
37
|
-
let(:job_invocation) { FactoryBot.create(:job_invocation, :with_task) }
|
38
|
-
let(:host) { job_invocation.template_invocations.first.host }
|
39
|
-
|
40
|
-
it 'should render task outputs' do
|
41
|
-
JobInvocation.any_instance.expects(:sub_task_for_host).returns(fake_task)
|
42
|
-
|
43
|
-
input = job_invocation_template.template_inputs.first
|
44
|
-
composer_params = { template_id: job_invocation_template.id, input_values: { input.id.to_s => { value: job_invocation.id.to_s } } }
|
45
|
-
result = ReportComposer.new(composer_params).render
|
46
|
-
|
47
|
-
# parsing the CSV result
|
48
|
-
rows = CSV.parse(result.strip, headers: true)
|
49
|
-
assert_equal 1, rows.count
|
50
|
-
row = rows.first
|
51
|
-
assert_equal host.name, row['Host']
|
52
|
-
assert_equal 'success', row['Result']
|
53
|
-
assert_equal 'error', row['stderr']
|
54
|
-
assert_equal 'output', row['stdout']
|
55
|
-
assert_equal 'debug', row['debug']
|
56
|
-
assert_kind_of Time, Time.zone.parse(row['Finished']), 'Parsing of time column failed'
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
@@ -1,232 +0,0 @@
|
|
1
|
-
require 'test_plugin_helper'
|
2
|
-
|
3
|
-
class JobInvocationTest < ActiveSupport::TestCase
|
4
|
-
let(:job_invocation) { FactoryBot.build(:job_invocation, :description => 'A text with "quotes"') }
|
5
|
-
let(:template) { FactoryBot.create(:job_template, :with_input) }
|
6
|
-
|
7
|
-
context 'search for job invocations' do
|
8
|
-
before do
|
9
|
-
job_invocation.save
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'is able to perform search through job invocations' do
|
13
|
-
found_jobs = JobInvocation.search_for(%{job_category = "#{job_invocation.job_category}"}).paginate(:page => 1).order('job_invocations.id DESC')
|
14
|
-
assert_equal [job_invocation], found_jobs
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'is able to auto complete description' do
|
18
|
-
expected = 'description = "A text with \"quotes\""'
|
19
|
-
assert_equal [expected], JobInvocation.complete_for('description = ')
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
context 'able to be created' do
|
24
|
-
it { assert job_invocation.save }
|
25
|
-
end
|
26
|
-
|
27
|
-
context 'requires targeting' do
|
28
|
-
before { job_invocation.targeting = nil }
|
29
|
-
|
30
|
-
it { refute_valid job_invocation }
|
31
|
-
end
|
32
|
-
|
33
|
-
context 'can delete a host' do
|
34
|
-
let(:host) do
|
35
|
-
FactoryBot.create(:host)
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'can remove a host' do
|
39
|
-
job_invocation.template_invocations.build(:host_id => host.id, :template_id => template.id)
|
40
|
-
job_invocation.save!
|
41
|
-
host.destroy
|
42
|
-
job_invocation.reload
|
43
|
-
assert_empty job_invocation.template_invocations
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
context 'has template invocations with input values' do
|
48
|
-
let(:job_invocation) { FactoryBot.create(:job_invocation, :with_template) }
|
49
|
-
|
50
|
-
before do
|
51
|
-
input = job_invocation.pattern_template_invocations.first.template.template_inputs.create!(:name => 'foo', :required => true, :input_type => 'user')
|
52
|
-
input2 = job_invocation.pattern_template_invocations.first.template.template_inputs.create!(:name => 'bar', :required => true, :input_type => 'user')
|
53
|
-
FactoryBot.create(:template_invocation_input_value,
|
54
|
-
:template_invocation => job_invocation.pattern_template_invocations.first,
|
55
|
-
:template_input => input2)
|
56
|
-
@input_value = FactoryBot.create(:template_invocation_input_value,
|
57
|
-
:template_invocation => job_invocation.pattern_template_invocations.first,
|
58
|
-
:template_input => input)
|
59
|
-
job_invocation.reload
|
60
|
-
job_invocation.pattern_template_invocations.first.reload
|
61
|
-
end
|
62
|
-
|
63
|
-
it { assert_not job_invocation.reload.pattern_template_invocations.empty? }
|
64
|
-
it { assert_not job_invocation.reload.pattern_template_invocations.first.input_values.empty? }
|
65
|
-
|
66
|
-
it "can look up templates not belonging to user's organization" do
|
67
|
-
organization = job_invocation.pattern_template_invocations.first.template.organizations.first
|
68
|
-
Organization.current = organization
|
69
|
-
job_invocation.pattern_template_invocations.first.template.organizations = []
|
70
|
-
# The following line raises UndefinedMethod if the user can't look up the template
|
71
|
-
job_invocation.pattern_template_invocations.first.template.name
|
72
|
-
|
73
|
-
# Restore things to original state
|
74
|
-
job_invocation.pattern_template_invocations.first.template.organizations = [organization]
|
75
|
-
Organization.current = nil
|
76
|
-
end
|
77
|
-
|
78
|
-
it 'validates required inputs have values' do
|
79
|
-
assert job_invocation.valid?
|
80
|
-
@input_value.destroy
|
81
|
-
assert_not job_invocation.reload.valid?
|
82
|
-
end
|
83
|
-
|
84
|
-
describe 'descriptions' do
|
85
|
-
it 'generates description from input values' do
|
86
|
-
job_invocation.description_format = '%{job_category} - %{foo}'
|
87
|
-
job_invocation.generate_description
|
88
|
-
assert_equal "#{job_invocation.job_category} - #{@input_value.value}", job_invocation.description
|
89
|
-
end
|
90
|
-
|
91
|
-
it 'handles missing keys correctly' do
|
92
|
-
job_invocation.description_format = '%{job_category} - %{missing_key}'
|
93
|
-
job_invocation.generate_description
|
94
|
-
assert_equal "#{job_invocation.job_category} - ''", job_invocation.description
|
95
|
-
end
|
96
|
-
|
97
|
-
it 'truncates generated description to 255 characters' do
|
98
|
-
column_limit = 255 # There is a 255 character limit on the database level
|
99
|
-
expected_result = 'a' * column_limit
|
100
|
-
job_invocation.description_format = '%{job_category}'
|
101
|
-
job_invocation.job_category = 'a' * 1000
|
102
|
-
job_invocation.generate_description
|
103
|
-
assert_equal expected_result, job_invocation.description
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
context 'future execution' do
|
109
|
-
it 'can report host count' do
|
110
|
-
assert_equal 'N/A', job_invocation.total_hosts_count
|
111
|
-
job_invocation.targeting.expects(:resolved_at).returns(Time.now.getlocal)
|
112
|
-
assert_equal 0, job_invocation.total_hosts_count
|
113
|
-
end
|
114
|
-
|
115
|
-
# task does not exist
|
116
|
-
specify { assert_equal HostStatus::ExecutionStatus::QUEUED, job_invocation.status }
|
117
|
-
specify { assert_equal HostStatus::ExecutionStatus::STATUS_NAMES[HostStatus::ExecutionStatus::QUEUED], job_invocation.status_label }
|
118
|
-
specify { assert_equal 0, job_invocation.progress }
|
119
|
-
end
|
120
|
-
|
121
|
-
context 'with task' do
|
122
|
-
let(:task) { ForemanTasks::Task.new }
|
123
|
-
let(:progress_report_without_sub_tasks) do
|
124
|
-
{
|
125
|
-
:error => 0,
|
126
|
-
:warning => 0,
|
127
|
-
:total => 0,
|
128
|
-
:success => 0,
|
129
|
-
:cancelled => 0,
|
130
|
-
:failed => 0,
|
131
|
-
:pending => 0,
|
132
|
-
:progress => 0,
|
133
|
-
:running => 0,
|
134
|
-
}
|
135
|
-
end
|
136
|
-
before { job_invocation.task = task }
|
137
|
-
|
138
|
-
context 'which is scheduled' do
|
139
|
-
before { task.state = 'scheduled' }
|
140
|
-
|
141
|
-
specify { assert_equal HostStatus::ExecutionStatus::QUEUED, job_invocation.status }
|
142
|
-
specify { assert job_invocation.queued? }
|
143
|
-
specify { assert_equal 0, job_invocation.progress }
|
144
|
-
specify { assert_equal progress_report_without_sub_tasks, job_invocation.progress_report }
|
145
|
-
end
|
146
|
-
|
147
|
-
context 'with cancelled task' do
|
148
|
-
before do
|
149
|
-
task.state = 'stopped'
|
150
|
-
task.result = 'error'
|
151
|
-
end
|
152
|
-
|
153
|
-
it 'calculates the progress correctly' do
|
154
|
-
job_invocation.targeting.stubs(:resolved?).returns(true)
|
155
|
-
task.expects(:sub_tasks_counts).never
|
156
|
-
assert_equal progress_report_without_sub_tasks, job_invocation.progress_report
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
context 'with succeeded task' do
|
161
|
-
before do
|
162
|
-
task.state = 'stopped'
|
163
|
-
task.result = 'success'
|
164
|
-
end
|
165
|
-
|
166
|
-
specify { assert_equal HostStatus::ExecutionStatus::OK, job_invocation.status }
|
167
|
-
specify { refute job_invocation.queued? }
|
168
|
-
|
169
|
-
it 'calculates the progress correctly' do
|
170
|
-
sub_tasks = [ForemanTasks::Task.new]
|
171
|
-
job_invocation.targeting.expects(:resolved?).returns(true)
|
172
|
-
job_invocation.targeting.expects(:hosts).returns([1])
|
173
|
-
sub_tasks.expects(:where).with(:result => %w(success warning error)).returns(sub_tasks)
|
174
|
-
job_invocation.stubs(:sub_tasks).returns(sub_tasks)
|
175
|
-
|
176
|
-
assert_equal 100, job_invocation.progress
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
describe '#finished?' do
|
182
|
-
let(:task) { ForemanTasks::Task.new }
|
183
|
-
before { job_invocation.task = task }
|
184
|
-
|
185
|
-
it 'returns false if task state is pending' do
|
186
|
-
job_invocation.task.expects(:pending?).returns(true)
|
187
|
-
assert_not job_invocation.finished?
|
188
|
-
end
|
189
|
-
|
190
|
-
it 'returns true if task is not pending' do
|
191
|
-
job_invocation.task.expects(:pending?).returns(false)
|
192
|
-
assert job_invocation.finished?
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
describe '#failed_hosts' do
|
197
|
-
let(:invocation) do
|
198
|
-
invocation = FactoryBot.build(:job_invocation, :with_template, :with_task, :with_failed_task, :with_unplanned_host)
|
199
|
-
invocation.template_invocations.each { |ti| invocation.targeting.hosts << ti.host }
|
200
|
-
invocation.save!
|
201
|
-
invocation
|
202
|
-
end
|
203
|
-
|
204
|
-
it 'returns only failed hosts when not #finished?' do
|
205
|
-
invocation.stubs(:finished?).returns(false)
|
206
|
-
assert_equal 1, invocation.failed_hosts.count
|
207
|
-
end
|
208
|
-
|
209
|
-
it 'returns failed hosts and hosts without task when #finished?' do
|
210
|
-
invocation.stubs(:finished?).returns(true)
|
211
|
-
assert_equal 2, invocation.failed_hosts.count
|
212
|
-
end
|
213
|
-
|
214
|
-
describe '#failed_template_invocations' do
|
215
|
-
it 'finds only failed template invocations' do
|
216
|
-
template_invocations = invocation.send(:failed_template_invocations)
|
217
|
-
assert_equal 1, template_invocations.count
|
218
|
-
template_invocation = template_invocations.first
|
219
|
-
assert_equal 'error', template_invocation.run_host_job_task.result
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
describe '#not_failed_template_invocations' do
|
224
|
-
it 'finds only non-failed template invocations' do
|
225
|
-
template_invocations = invocation.send(:not_failed_template_invocations)
|
226
|
-
assert_equal 1, template_invocations.count
|
227
|
-
template_invocation = template_invocations.first
|
228
|
-
assert_equal 'success', template_invocation.run_host_job_task.result
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
require 'test_plugin_helper'
|
2
|
-
|
3
|
-
class JobTemplateEffectiveUserTest < ActiveSupport::TestCase
|
4
|
-
let(:job_template) { FactoryBot.build(:job_template, :job_category => '') }
|
5
|
-
let(:effective_user) { job_template.effective_user }
|
6
|
-
|
7
|
-
describe 'by default' do
|
8
|
-
it 'is overridable' do
|
9
|
-
assert effective_user.overridable?
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'does not use the current user' do
|
13
|
-
assert_not effective_user.current_user?
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
describe 'compute value' do
|
18
|
-
it 'computes the value based on the current user when current_user set to true' do
|
19
|
-
user = FactoryBot.create(:user)
|
20
|
-
User.current = user
|
21
|
-
effective_user.current_user = true
|
22
|
-
assert_equal user.login, effective_user.compute_value
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'returns the value when not current user is set to true' do
|
26
|
-
effective_user.current_user = false
|
27
|
-
effective_user.value = 'testuser'
|
28
|
-
assert_equal 'testuser', effective_user.compute_value
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'returns a default value when no value is specified for the user' do
|
32
|
-
effective_user.value = ''
|
33
|
-
Setting[:remote_execution_effective_user] = 'myuser'
|
34
|
-
assert_equal 'myuser', effective_user.compute_value
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,316 +0,0 @@
|
|
1
|
-
require 'test_plugin_helper'
|
2
|
-
|
3
|
-
class JobTemplateTest < ActiveSupport::TestCase
|
4
|
-
context 'when creating a template' do
|
5
|
-
let(:job_template) { FactoryBot.build(:job_template, :job_category => '') }
|
6
|
-
let(:template_with_inputs) do
|
7
|
-
FactoryBot.build(:job_template, :template => 'test').tap do |template|
|
8
|
-
template.template_inputs << FactoryBot.build(:template_input, :name => 'command', :input_type => 'user')
|
9
|
-
template.save!
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'has a unique name' do
|
14
|
-
template1 = FactoryBot.create(:job_template)
|
15
|
-
template2 = FactoryBot.build(:job_template, :name => template1.name)
|
16
|
-
assert_not template2.valid?
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'needs a job_category' do
|
20
|
-
assert_not job_template.valid?
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'does not need a job_category if it is a snippet' do
|
24
|
-
job_template.snippet = true
|
25
|
-
assert job_template.valid?
|
26
|
-
end
|
27
|
-
|
28
|
-
it 'validates the inputs are uniq in the template' do
|
29
|
-
job_template.job_category = 'Miscellaneous'
|
30
|
-
job_template.foreign_input_sets << FactoryBot.build(:foreign_input_set, :target_template => template_with_inputs)
|
31
|
-
job_template.foreign_input_sets << FactoryBot.build(:foreign_input_set, :target_template => template_with_inputs)
|
32
|
-
assert_not job_template.valid?
|
33
|
-
assert_includes job_template.errors.full_messages.first, 'Duplicated inputs detected: ["command"]'
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
context 'description format' do
|
38
|
-
let(:template_with_description) { FactoryBot.build(:job_template, :with_description_format, :job_category => 'test job') }
|
39
|
-
let(:template) { FactoryBot.build(:job_template, :with_input, :job_category => 'test job') }
|
40
|
-
let(:minimal_template) { FactoryBot.build(:job_template) }
|
41
|
-
|
42
|
-
it 'uses the description_format attribute if set' do
|
43
|
-
assert_equal template_with_description.generate_description_format, template_with_description.description_format
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'uses the job name as description_format if not set or blank and has no inputs' do
|
47
|
-
assert_equal minimal_template.generate_description_format, '%{template_name}'
|
48
|
-
minimal_template.description_format = ''
|
49
|
-
assert_equal minimal_template.generate_description_format, '%{template_name}'
|
50
|
-
end
|
51
|
-
|
52
|
-
it 'generates the description_format if not set or blank and has inputs' do
|
53
|
-
input_name = template.template_inputs.first.name
|
54
|
-
expected_result = %(%{template_name} with inputs #{input_name}="%{#{input_name}}")
|
55
|
-
assert_equal template.generate_description_format, expected_result
|
56
|
-
template.description_format = ''
|
57
|
-
assert_equal template.generate_description_format, expected_result
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
context 'cloning' do
|
62
|
-
let(:job_template) { FactoryBot.build(:job_template, :with_input) }
|
63
|
-
|
64
|
-
describe '#dup' do
|
65
|
-
it 'duplicates also template inputs' do
|
66
|
-
duplicate = job_template.dup
|
67
|
-
refute_equal duplicate, job_template
|
68
|
-
refute_empty duplicate.template_inputs
|
69
|
-
refute_equal duplicate.template_inputs.first, job_template.template_inputs.first
|
70
|
-
assert_equal duplicate.template_inputs.first.name, job_template.template_inputs.first.name
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
context 'importing a new template' do
|
76
|
-
let(:remote_execution_feature) do
|
77
|
-
FactoryBot.create(:remote_execution_feature)
|
78
|
-
end
|
79
|
-
|
80
|
-
let(:template) do
|
81
|
-
template = <<-END_TEMPLATE
|
82
|
-
<%#
|
83
|
-
kind: job_template
|
84
|
-
name: Service Restart
|
85
|
-
job_category: Service Restart
|
86
|
-
provider_type: SSH
|
87
|
-
feature: #{remote_execution_feature.label}
|
88
|
-
template_inputs:
|
89
|
-
- name: service_name
|
90
|
-
input_type: user
|
91
|
-
required: true
|
92
|
-
- name: verbose
|
93
|
-
input_type: user
|
94
|
-
%>
|
95
|
-
|
96
|
-
service <%= input("service_name") %> restart
|
97
|
-
|
98
|
-
<%# test comment %>
|
99
|
-
END_TEMPLATE
|
100
|
-
|
101
|
-
JobTemplate.import_raw!(template, :default => true)
|
102
|
-
end
|
103
|
-
|
104
|
-
let(:template_with_input_sets) do
|
105
|
-
template_with_input_sets = <<-END_TEMPLATE
|
106
|
-
<%#
|
107
|
-
kind: job_template
|
108
|
-
name: Service Restart - Custom
|
109
|
-
job_category: Service Restart
|
110
|
-
provider_type: SSH
|
111
|
-
foreign_input_sets:
|
112
|
-
- template: #{template.name}
|
113
|
-
exclude: verbose
|
114
|
-
%>
|
115
|
-
|
116
|
-
service <%= input("service_name") %> restart
|
117
|
-
END_TEMPLATE
|
118
|
-
|
119
|
-
JobTemplate.import_raw!(template_with_input_sets, :default => true)
|
120
|
-
end
|
121
|
-
|
122
|
-
it 'sets the name' do
|
123
|
-
assert_equal template.name, 'Service Restart'
|
124
|
-
end
|
125
|
-
|
126
|
-
it 'has a template' do
|
127
|
-
assert_equal template.template.squish, 'service <%= input("service_name") %> restart <%# test comment %>'
|
128
|
-
end
|
129
|
-
|
130
|
-
it 'imports inputs' do
|
131
|
-
assert_equal template.template_inputs.first.name, 'service_name'
|
132
|
-
end
|
133
|
-
|
134
|
-
it 'imports input sets' do
|
135
|
-
assert_equal template_with_input_sets.foreign_input_sets.first.target_template, template
|
136
|
-
assert_equal template_with_input_sets.template_inputs_with_foreign.map(&:name), ['service_name']
|
137
|
-
end
|
138
|
-
|
139
|
-
it 'imports feature' do
|
140
|
-
template # let is lazy
|
141
|
-
remote_execution_feature.reload
|
142
|
-
assert_equal remote_execution_feature.job_template, template
|
143
|
-
end
|
144
|
-
|
145
|
-
it 'sets additional options' do
|
146
|
-
assert_equal template.default, true
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
context 'importing an existing template' do
|
151
|
-
let(:included) do
|
152
|
-
template = <<-END_TEMPLATE
|
153
|
-
<%#
|
154
|
-
kind: job_template
|
155
|
-
name: Banner
|
156
|
-
job_category: Commands
|
157
|
-
provider_type: SSH
|
158
|
-
template_inputs:
|
159
|
-
- name: banner_message
|
160
|
-
input_type: user
|
161
|
-
required: true
|
162
|
-
%>
|
163
|
-
|
164
|
-
echo input(:banner_message)
|
165
|
-
END_TEMPLATE
|
166
|
-
|
167
|
-
JobTemplate.import_raw!(template, :default => true)
|
168
|
-
end
|
169
|
-
|
170
|
-
let(:existing) do
|
171
|
-
template = <<-END_TEMPLATE
|
172
|
-
<%#
|
173
|
-
kind: job_template
|
174
|
-
name: Ping a Thing
|
175
|
-
job_category: Commands
|
176
|
-
provider_type: SSH
|
177
|
-
template_inputs:
|
178
|
-
- name: hostname
|
179
|
-
input_type: user
|
180
|
-
options: "www.google.com"
|
181
|
-
required: true
|
182
|
-
foreign_input_sets:
|
183
|
-
- template: #{included.name}
|
184
|
-
%>
|
185
|
-
|
186
|
-
ping -c 5 <%= input("hostname") %>
|
187
|
-
END_TEMPLATE
|
188
|
-
|
189
|
-
JobTemplate.import_raw!(template, :default => true)
|
190
|
-
end
|
191
|
-
|
192
|
-
let(:updated) do
|
193
|
-
<<-END_TEMPLATE
|
194
|
-
<%#
|
195
|
-
kind: job_template
|
196
|
-
name: Ping a Thing
|
197
|
-
job_category: Commands
|
198
|
-
provider_type: SSH
|
199
|
-
template_inputs:
|
200
|
-
- name: hostname
|
201
|
-
input_type: user
|
202
|
-
options: 'www.redhat.com'
|
203
|
-
required: true
|
204
|
-
- name: count
|
205
|
-
input_type: user
|
206
|
-
required: true
|
207
|
-
foreign_input_sets:
|
208
|
-
- template: #{included.name}
|
209
|
-
exclude: banner_message
|
210
|
-
%>
|
211
|
-
|
212
|
-
ping -c <%= input('count') %> <%= input('hostname') %>
|
213
|
-
END_TEMPLATE
|
214
|
-
end
|
215
|
-
|
216
|
-
it 'will not overwrite by default' do
|
217
|
-
existing
|
218
|
-
assert_not JobTemplate.import_raw!(updated)
|
219
|
-
end
|
220
|
-
|
221
|
-
let(:synced_template) do
|
222
|
-
existing
|
223
|
-
JobTemplate.import_raw!(updated, :update => true)
|
224
|
-
existing.reload
|
225
|
-
end
|
226
|
-
|
227
|
-
it 'syncs inputs' do
|
228
|
-
hostname = synced_template.template_inputs.find { |input| input.name == 'hostname' }
|
229
|
-
assert_equal hostname.options, 'www.redhat.com'
|
230
|
-
end
|
231
|
-
|
232
|
-
it 'syncs content' do
|
233
|
-
assert_match(/ping -c <%= input\('count'\) %> <%= input\('hostname'\) %>/m, synced_template.template)
|
234
|
-
end
|
235
|
-
|
236
|
-
it 'syncs input sets' do
|
237
|
-
assert_equal synced_template.foreign_input_sets.first.target_template, included
|
238
|
-
assert_equal synced_template.template_inputs_with_foreign.map(&:name), ['hostname', 'count']
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
context 'template export' do
|
243
|
-
let(:exportable_template) do
|
244
|
-
FactoryBot.create(:job_template, :with_input)
|
245
|
-
end
|
246
|
-
|
247
|
-
let(:erb) do
|
248
|
-
exportable_template.to_erb
|
249
|
-
end
|
250
|
-
|
251
|
-
it 'exports name' do
|
252
|
-
assert_match(/^name: #{exportable_template.name}$/, erb)
|
253
|
-
end
|
254
|
-
|
255
|
-
it 'includes template inputs' do
|
256
|
-
assert_match(/^template_inputs:$/, erb)
|
257
|
-
end
|
258
|
-
|
259
|
-
it 'includes template contents' do
|
260
|
-
assert_includes erb, exportable_template.template
|
261
|
-
end
|
262
|
-
|
263
|
-
it 'is importable' do
|
264
|
-
erb
|
265
|
-
old_name = exportable_template.name
|
266
|
-
exportable_template.update(:name => "#{old_name}_renamed")
|
267
|
-
|
268
|
-
imported = JobTemplate.import_raw!(erb)
|
269
|
-
assert_equal imported.name, old_name
|
270
|
-
assert_equal imported.template_inputs.first.to_export, exportable_template.template_inputs.first.to_export
|
271
|
-
end
|
272
|
-
|
273
|
-
it 'has taxonomies in metadata' do
|
274
|
-
assert_equal 'Organization 1', exportable_template.to_export["organizations"].first
|
275
|
-
assert_equal 'Location 1', exportable_template.to_export["locations"].first
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
context 'there is existing template invocation of a job template' do
|
280
|
-
let(:job_invocation) { FactoryBot.create(:job_invocation, :with_template) }
|
281
|
-
let(:job_template) { job_invocation.pattern_template_invocations.first.template }
|
282
|
-
|
283
|
-
describe 'job template deletion' do
|
284
|
-
it 'succeeds' do
|
285
|
-
refute_empty job_template.pattern_template_invocations
|
286
|
-
assert job_template.destroy
|
287
|
-
end
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
context 'template locked' do
|
292
|
-
it 'inputs cannot be changed' do
|
293
|
-
job_template = FactoryBot.create(:job_template, :with_input, :locked => true)
|
294
|
-
Foreman.expects(:in_rake?).returns(false).at_least_once
|
295
|
-
assert_valid job_template
|
296
|
-
job_template.template_inputs.first.name = 'something else'
|
297
|
-
refute_valid job_template
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
|
-
context 'rendering' do
|
302
|
-
it 'renders nested template as a non-admin user' do
|
303
|
-
inner = FactoryBot.create(:job_template)
|
304
|
-
template_invocation = FactoryBot.create(:template_invocation)
|
305
|
-
template_invocation.template.template = "<wrap><%= render_template('#{inner.name}') %></wrap>"
|
306
|
-
template_invocation.template.save!
|
307
|
-
|
308
|
-
setup_user('view', 'job_templates')
|
309
|
-
renderer = InputTemplateRenderer.new template_invocation.template,
|
310
|
-
template_invocation.host,
|
311
|
-
template_invocation
|
312
|
-
result = renderer.render
|
313
|
-
assert_equal result, "<wrap>#{inner.template}</wrap>"
|
314
|
-
end
|
315
|
-
end
|
316
|
-
end
|