foreman_remote_execution 0.2.3 → 0.3.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/.gitignore +2 -0
- data/.rubocop.yml +7 -0
- data/.rubocop_todo.yml +16 -5
- data/.tx/config +1 -1
- data/app/assets/javascripts/template_invocation.js +14 -1
- data/app/assets/stylesheets/job_invocations.css.scss +0 -13
- data/app/assets/stylesheets/template_invocation.css.scss +7 -13
- data/app/controllers/api/v2/foreign_input_sets_controller.rb +1 -1
- data/app/controllers/api/v2/job_invocations_controller.rb +12 -18
- data/app/controllers/api/v2/remote_execution_features_controller.rb +38 -0
- data/app/controllers/api/v2/template_inputs_controller.rb +1 -0
- data/app/controllers/job_invocations_controller.rb +3 -13
- data/app/controllers/job_templates_controller.rb +1 -1
- data/app/controllers/remote_execution_features_controller.rb +19 -0
- data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +1 -22
- data/app/helpers/remote_execution_helper.rb +30 -12
- data/app/lib/actions/remote_execution/helpers/live_output.rb +2 -2
- data/app/lib/actions/remote_execution/run_host_job.rb +7 -4
- data/app/lib/actions/remote_execution/run_hosts_job.rb +14 -0
- data/app/lib/actions/remote_execution/run_proxy_command.rb +2 -2
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +5 -4
- data/app/models/concerns/foreman_remote_execution/subnet_extensions.rb +1 -0
- data/app/models/input_template_renderer.rb +14 -3
- data/app/models/job_invocation.rb +6 -15
- data/app/models/job_invocation_composer.rb +131 -21
- data/app/models/job_template.rb +77 -22
- data/app/models/job_template_effective_user.rb +0 -2
- data/app/models/remote_execution_feature.rb +34 -0
- data/app/models/setting/remote_execution.rb +4 -1
- data/app/models/targeting.rb +2 -2
- data/app/models/template_input.rb +17 -13
- data/app/views/api/v2/remote_execution_features/base.json.rabl +3 -0
- data/app/views/api/v2/remote_execution_features/index.json.rabl +3 -0
- data/app/views/api/v2/remote_execution_features/main.json.rabl +3 -0
- data/app/views/api/v2/remote_execution_features/show.json.rabl +3 -0
- data/app/views/job_invocations/_form.html.erb +51 -40
- data/app/views/job_invocations/_preview_hosts_list.html.erb +1 -1
- data/app/views/job_invocations/_tab_hosts.html.erb +6 -3
- data/app/views/job_invocations/index.html.erb +11 -11
- data/app/views/job_templates/_custom_tabs.html.erb +4 -4
- data/app/views/job_templates/index.html.erb +5 -5
- data/app/views/overrides/nics/_execution_interface.html.erb +9 -1
- data/app/views/remote_execution_features/_form.html.erb +24 -0
- data/app/views/remote_execution_features/index.html.erb +21 -0
- data/app/views/remote_execution_features/show.html.erb +3 -0
- data/app/views/template_inputs/_form.html.erb +1 -0
- data/app/views/template_inputs/_invocation_form.html.erb +7 -0
- data/app/views/templates/package_action.erb +17 -5
- data/app/views/templates/power_action.erb +22 -0
- data/app/views/templates/puppet_run_once.erb +1 -1
- data/config/routes.rb +4 -0
- data/db/migrate/20160113162007_expand_all_template_invocations.rb +1 -1
- data/db/migrate/20160118124600_create_remote_execution_features.rb +14 -0
- data/db/migrate/20160125155108_make_job_template_name_unique.rb +12 -0
- data/db/migrate/20160127134031_add_advanced_to_template_input.rb +11 -0
- data/db/migrate/20160127162711_reword_puppet_template_description.rb +9 -0
- data/db/migrate/20160203104056_add_concurrency_options_to_job_invocation.rb +6 -0
- data/db/seeds.d/70-job_templates.rb +2 -1
- data/doc/plugins/div_tag.rb +1 -1
- data/doc/plugins/plantuml.rb +1 -1
- data/doc/plugins/tags.rb +7 -8
- data/doc/plugins/toc.rb +0 -1
- data/foreman_remote_execution.gemspec +1 -1
- data/lib/foreman_remote_execution/engine.rb +10 -2
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/locale/action_names.rb +8 -0
- data/locale/en/foreman_remote_execution.po +767 -11
- data/locale/foreman_remote_execution.pot +1026 -8
- data/test/functional/api/v2/foreign_input_sets_controller_test.rb +1 -1
- data/test/functional/api/v2/job_invocations_controller_test.rb +0 -9
- data/test/functional/api/v2/job_templates_controller_test.rb +11 -11
- data/test/functional/api/v2/remote_execution_features_controller_test.rb +35 -0
- data/test/functional/api/v2/template_inputs_controller_test.rb +1 -1
- data/test/unit/actions/run_hosts_job_test.rb +48 -7
- data/test/unit/actions/run_proxy_command_test.rb +1 -1
- data/test/unit/concerns/host_extensions_test.rb +11 -0
- data/test/unit/input_template_renderer_test.rb +4 -4
- data/test/unit/job_invocation_composer_test.rb +52 -2
- data/test/unit/job_invocation_test.rb +1 -1
- data/test/unit/job_template_test.rb +126 -3
- data/test/unit/remote_execution_feature_test.rb +42 -0
- data/test/unit/targeting_test.rb +4 -4
- metadata +26 -7
- data/app/views/job_invocation_task_groups/_job_invocation_task_group.html.erb +0 -31
- data/app/views/unattended/snippets/_remote_execution_ssh_keys.erb +0 -18
- data/db/seeds.d/80-provision_templates.rb +0 -21
|
@@ -18,7 +18,7 @@ module Api
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
test 'should get input set detail' do
|
|
21
|
-
get :show,
|
|
21
|
+
get :show, :template_id => @template.to_param, :id => @input_set.to_param
|
|
22
22
|
assert_response :success
|
|
23
23
|
input_set = ActiveSupport::JSON.decode(@response.body)
|
|
24
24
|
assert !input_set.empty?
|
|
@@ -23,15 +23,6 @@ module Api
|
|
|
23
23
|
assert_equal template['job_category'], @invocation.job_category
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
test 'should create valid without job_template_id' do
|
|
27
|
-
attrs = { :job_category => @template.job_category, :name => 'RandomName', :targeting_type => 'static_query', :search_query => 'foobar'}
|
|
28
|
-
post :create, :job_invocation => attrs
|
|
29
|
-
|
|
30
|
-
invocation = ActiveSupport::JSON.decode(@response.body)
|
|
31
|
-
assert_equal attrs[:job_category], invocation['job_category']
|
|
32
|
-
assert_response :success
|
|
33
|
-
end
|
|
34
|
-
|
|
35
26
|
test 'should create valid with job_template_id' do
|
|
36
27
|
attrs = { :job_category => @template.job_category, :name => 'RandomName', :job_template_id => @template.id,
|
|
37
28
|
:targeting_type => 'static_query', :search_query => 'foobar'}
|
|
@@ -15,7 +15,7 @@ module Api
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
test 'should get template detail' do
|
|
18
|
-
get :show,
|
|
18
|
+
get :show, :id => @template.to_param
|
|
19
19
|
assert_response :success
|
|
20
20
|
template = ActiveSupport::JSON.decode(@response.body)
|
|
21
21
|
assert !template.empty?
|
|
@@ -25,7 +25,7 @@ module Api
|
|
|
25
25
|
test 'should create valid' do
|
|
26
26
|
JobTemplate.any_instance.stubs(:valid?).returns(true)
|
|
27
27
|
valid_attrs = { :template => 'This is a test template', :name => 'RandomName', :provider_type => 'ssh' }
|
|
28
|
-
post :create,
|
|
28
|
+
post :create, :job_template => valid_attrs
|
|
29
29
|
template = ActiveSupport::JSON.decode(@response.body)
|
|
30
30
|
assert template['name'] == 'RandomName'
|
|
31
31
|
assert_response :success
|
|
@@ -38,26 +38,26 @@ module Api
|
|
|
38
38
|
|
|
39
39
|
test 'should update valid' do
|
|
40
40
|
JobTemplate.any_instance.stubs(:valid?).returns(true)
|
|
41
|
-
put :update,
|
|
42
|
-
|
|
41
|
+
put :update, :id => @template.to_param,
|
|
42
|
+
:job_template => { :template => 'blah' }
|
|
43
43
|
assert_response :ok
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
test 'should not update invalid' do
|
|
47
|
-
put :update,
|
|
48
|
-
|
|
47
|
+
put :update, :id => @template.to_param,
|
|
48
|
+
:job_template => { :name => '' }
|
|
49
49
|
assert_response :unprocessable_entity
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
test 'should destroy' do
|
|
53
|
-
delete :destroy,
|
|
53
|
+
delete :destroy, :id => @template.to_param
|
|
54
54
|
assert_response :ok
|
|
55
55
|
refute JobTemplate.exists?(@template.id)
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
test 'should clone template' do
|
|
59
|
-
post :clone,
|
|
60
|
-
|
|
59
|
+
post :clone, :id => @template.to_param,
|
|
60
|
+
:job_template => {:name => 'MyClone'}
|
|
61
61
|
assert_response :success
|
|
62
62
|
template = ActiveSupport::JSON.decode(@response.body)
|
|
63
63
|
assert_equal(template['name'], 'MyClone')
|
|
@@ -65,8 +65,8 @@ module Api
|
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
test 'clone name should not be blank' do
|
|
68
|
-
post :clone,
|
|
69
|
-
|
|
68
|
+
post :clone, :id => @template.to_param,
|
|
69
|
+
:job_template => {:name => ''}
|
|
70
70
|
assert_response :unprocessable_entity
|
|
71
71
|
end
|
|
72
72
|
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require 'test_plugin_helper'
|
|
2
|
+
|
|
3
|
+
module Api
|
|
4
|
+
module V2
|
|
5
|
+
class RemoteExecutionFeaturesControllerTest < ActionController::TestCase
|
|
6
|
+
setup do
|
|
7
|
+
@remote_execution_feature = RemoteExecutionFeature.register(:my_awesome_feature, 'My awesome feature',
|
|
8
|
+
:description => 'You will not believe what it does',
|
|
9
|
+
:provided_inputs => ['awesomeness_level'])
|
|
10
|
+
@template = FactoryGirl.create(:job_template, :with_input)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
test 'should get index' do
|
|
14
|
+
get :index
|
|
15
|
+
remote_execution_features = ActiveSupport::JSON.decode(@response.body)
|
|
16
|
+
refute remote_execution_features.empty?, 'Should respond with input sets'
|
|
17
|
+
assert_response :success
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
test 'should get input set detail' do
|
|
21
|
+
get :show, :id => @remote_execution_feature.to_param
|
|
22
|
+
assert_response :success
|
|
23
|
+
remote_execution_feature = ActiveSupport::JSON.decode(@response.body)
|
|
24
|
+
refute remote_execution_feature.empty?
|
|
25
|
+
assert_equal remote_execution_feature['name'], @remote_execution_feature.name
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
test 'should update valid' do
|
|
29
|
+
put :update, :id => @remote_execution_feature.to_param,
|
|
30
|
+
:job_template_id => @template.id
|
|
31
|
+
assert_response :ok
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -16,7 +16,7 @@ module Api
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
test 'should get input detail' do
|
|
19
|
-
get :show,
|
|
19
|
+
get :show, :template_id => @template.to_param, :id => @input.to_param
|
|
20
20
|
assert_response :success
|
|
21
21
|
input = ActiveSupport::JSON.decode(@response.body)
|
|
22
22
|
assert !input.empty?
|
|
@@ -22,10 +22,13 @@ module ForemanRemoteExecution
|
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
let(:action) do
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
create_action(Actions::RemoteExecution::RunHostsJob).tap do |action|
|
|
26
|
+
action.expects(:action_subject).with(job_invocation)
|
|
27
|
+
ForemanTasks::Task::DynflowTask.stubs(:where).returns(mock.tap { |m| m.stubs(:first! => task) })
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
let(:planned) do
|
|
31
|
+
plan_action action, job_invocation
|
|
29
32
|
end
|
|
30
33
|
|
|
31
34
|
before do
|
|
@@ -35,17 +38,55 @@ module ForemanRemoteExecution
|
|
|
35
38
|
end
|
|
36
39
|
|
|
37
40
|
it 'resolves the hosts on targeting in plan phase' do
|
|
41
|
+
planned
|
|
38
42
|
targeting.hosts.must_include(host)
|
|
39
43
|
end
|
|
40
44
|
|
|
41
45
|
it 'triggers the RunHostJob actions on the resolved hosts in run phase' do
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
planned.expects(:trigger).with() { |*args| args[0] == Actions::RemoteExecution::RunHostJob }
|
|
47
|
+
planned.create_sub_plans
|
|
44
48
|
end
|
|
45
49
|
|
|
46
50
|
it 'uses the BindJobInvocation middleware' do
|
|
47
|
-
|
|
51
|
+
planned
|
|
48
52
|
job_invocation.task_id.must_equal '123'
|
|
49
53
|
end
|
|
54
|
+
|
|
55
|
+
describe 'concurrency control' do
|
|
56
|
+
let(:level) { 5 }
|
|
57
|
+
let(:span) { 60 }
|
|
58
|
+
|
|
59
|
+
it 'can be disabled' do
|
|
60
|
+
job_invocation.expects(:concurrency_level)
|
|
61
|
+
job_invocation.expects(:time_span)
|
|
62
|
+
action.expects(:limit_concurrency_level).never
|
|
63
|
+
action.expects(:distribute_over_time).never
|
|
64
|
+
planned
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'can limit concurrency level' do
|
|
68
|
+
job_invocation.expects(:concurrency_level).returns(level).twice
|
|
69
|
+
job_invocation.expects(:time_span)
|
|
70
|
+
action.expects(:limit_concurrency_level).with(level)
|
|
71
|
+
action.expects(:distribute_over_time).never
|
|
72
|
+
planned
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'can distribute tasks over time' do
|
|
76
|
+
job_invocation.expects(:time_span).returns(span).twice
|
|
77
|
+
job_invocation.expects(:concurrency_level)
|
|
78
|
+
action.expects(:distribute_over_time).with(span)
|
|
79
|
+
action.expects(:distribute_over_time).never
|
|
80
|
+
planned
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'can use both' do
|
|
84
|
+
job_invocation.expects(:time_span).returns(span).twice
|
|
85
|
+
action.expects(:distribute_over_time).with(span)
|
|
86
|
+
job_invocation.expects(:concurrency_level).returns(level).twice
|
|
87
|
+
action.expects(:limit_concurrency_level).with(level)
|
|
88
|
+
planned
|
|
89
|
+
end
|
|
90
|
+
end
|
|
50
91
|
end
|
|
51
92
|
end
|
|
@@ -40,6 +40,17 @@ describe ForemanRemoteExecution::HostExtensions do
|
|
|
40
40
|
it 'has ssh keys in the parameters' do
|
|
41
41
|
host.remote_execution_ssh_keys.must_include sshkey
|
|
42
42
|
end
|
|
43
|
+
|
|
44
|
+
it 'has ssh keys in the parameters even when no user specified' do
|
|
45
|
+
# this is a case, when using the helper in provisioning templates
|
|
46
|
+
FactoryGirl.create(:smart_proxy, :ssh) do |proxy|
|
|
47
|
+
proxy.organizations << host.organization
|
|
48
|
+
proxy.locations << host.location
|
|
49
|
+
end
|
|
50
|
+
host.interfaces.first.subnet.remote_execution_proxies.clear
|
|
51
|
+
User.current = nil
|
|
52
|
+
host.remote_execution_ssh_keys.must_include sshkey
|
|
53
|
+
end
|
|
43
54
|
end
|
|
44
55
|
|
|
45
56
|
context 'host has multiple nics' do
|
|
@@ -2,14 +2,14 @@ require 'test_plugin_helper'
|
|
|
2
2
|
|
|
3
3
|
describe InputTemplateRenderer do
|
|
4
4
|
context 'renderer for simple template without inputs' do
|
|
5
|
-
let(:renderer) { InputTemplateRenderer.new(FactoryGirl.build(:job_template, :template => 'id')) }
|
|
5
|
+
let(:renderer) { InputTemplateRenderer.new(FactoryGirl.build(:job_template, :template => 'id <%= preview? %>')) }
|
|
6
6
|
|
|
7
7
|
it 'should render the content' do
|
|
8
|
-
renderer.render.must_equal 'id'
|
|
8
|
+
renderer.render.must_equal 'id false'
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
it 'should render preview' do
|
|
12
|
-
renderer.preview.must_equal 'id'
|
|
12
|
+
renderer.preview.must_equal 'id true'
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
|
|
@@ -147,7 +147,7 @@ describe InputTemplateRenderer do
|
|
|
147
147
|
end
|
|
148
148
|
|
|
149
149
|
let(:package_template) do
|
|
150
|
-
FactoryGirl.build(:job_template, :name => 'package action', :template => <<TEMPLATE)
|
|
150
|
+
FactoryGirl.build(:job_template, :name => 'package action', :template => <<TEMPLATE) do |template|
|
|
151
151
|
<%= render_template("command action", "command" => "yum -y \#{ input("action") } \#{ input('package') }") -%>
|
|
152
152
|
TEMPLATE
|
|
153
153
|
template.template_inputs << FactoryGirl.build(:template_input, :name => 'package', :input_type => 'user')
|
|
@@ -210,7 +210,7 @@ describe JobInvocationComposer do
|
|
|
210
210
|
{ :job_template_id => trying_job_template_1.id.to_s,
|
|
211
211
|
:job_templates => {
|
|
212
212
|
trying_job_template_1.id.to_s => {
|
|
213
|
-
:input_values => { input1.id.to_s => { :value => 'value1' },
|
|
213
|
+
:input_values => { input1.id.to_s => { :value => 'value1' }, unauthorized_input1.id.to_s => { :value => 'dropped' } }
|
|
214
214
|
}
|
|
215
215
|
}
|
|
216
216
|
}
|
|
@@ -429,6 +429,25 @@ describe JobInvocationComposer do
|
|
|
429
429
|
end
|
|
430
430
|
end
|
|
431
431
|
|
|
432
|
+
describe 'concurrency control' do
|
|
433
|
+
|
|
434
|
+
describe 'with concurrency control set' do
|
|
435
|
+
let(:params) do
|
|
436
|
+
{ :job_invocation => { :providers => { :ssh => ssh_params }, :concurrency_level => "5", :time_span => "60" } }.with_indifferent_access
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
it 'accepts the concurrency options' do
|
|
440
|
+
composer.job_invocation.concurrency_level.must_equal 5
|
|
441
|
+
composer.job_invocation.time_span.must_equal 60
|
|
442
|
+
end
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
it 'can be disabled' do
|
|
446
|
+
composer.job_invocation.concurrency_level.must_be_nil
|
|
447
|
+
composer.job_invocation.time_span.must_be_nil
|
|
448
|
+
end
|
|
449
|
+
end
|
|
450
|
+
|
|
432
451
|
describe 'triggering' do
|
|
433
452
|
let(:params) do
|
|
434
453
|
{ :job_invocation => { :providers => { :ssh => ssh_params } }, :triggering => { :mode => 'future' }}.with_indifferent_access
|
|
@@ -481,7 +500,11 @@ describe JobInvocationComposer do
|
|
|
481
500
|
let(:params) do
|
|
482
501
|
{
|
|
483
502
|
:job_invocation => {
|
|
484
|
-
:providers => { :ssh => ssh_params }
|
|
503
|
+
:providers => { :ssh => ssh_params },
|
|
504
|
+
:concurrency_control => {
|
|
505
|
+
:level => 5,
|
|
506
|
+
:time_span => 60
|
|
507
|
+
}
|
|
485
508
|
},
|
|
486
509
|
:targeting => {
|
|
487
510
|
:search_query => "name = #{host.name}",
|
|
@@ -514,6 +537,11 @@ describe JobInvocationComposer do
|
|
|
514
537
|
new_composer.pattern_template_invocations.size.must_equal existing.pattern_template_invocations.size
|
|
515
538
|
end
|
|
516
539
|
|
|
540
|
+
it 'sets the same concurrency control options' do
|
|
541
|
+
new_composer.job_invocation.concurrency_level.must_equal existing.concurrency_level
|
|
542
|
+
new_composer.job_invocation.time_span.must_equal existing.time_span
|
|
543
|
+
end
|
|
544
|
+
|
|
517
545
|
end
|
|
518
546
|
end
|
|
519
547
|
end
|
|
@@ -591,6 +619,28 @@ describe JobInvocationComposer do
|
|
|
591
619
|
end
|
|
592
620
|
end
|
|
593
621
|
|
|
622
|
+
context 'with concurrency_control' do
|
|
623
|
+
let(:level) { 5 }
|
|
624
|
+
let(:time_span) { 60 }
|
|
625
|
+
let(:params) do
|
|
626
|
+
{ :job_category => trying_job_template_1.job_category,
|
|
627
|
+
:job_template_id => trying_job_template_1.id,
|
|
628
|
+
:concurrency_control => {
|
|
629
|
+
:concurrency_level => level,
|
|
630
|
+
:time_span => time_span
|
|
631
|
+
},
|
|
632
|
+
:targeting_type => 'static_query',
|
|
633
|
+
:search_query => 'some hosts',
|
|
634
|
+
:inputs => {input1.name => 'some_value'}}
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
it 'sets the concurrency level and time span based on the input' do
|
|
638
|
+
assert composer.save!
|
|
639
|
+
composer.job_invocation.time_span.must_equal time_span
|
|
640
|
+
composer.job_invocation.concurrency_level.must_equal level
|
|
641
|
+
end
|
|
642
|
+
end
|
|
643
|
+
|
|
594
644
|
context 'with invalid targeting' do
|
|
595
645
|
let(:params) do
|
|
596
646
|
{ :job_category => trying_job_template_1.job_category,
|
|
@@ -92,7 +92,7 @@ describe JobInvocation do
|
|
|
92
92
|
context 'future execution' do
|
|
93
93
|
it 'can report host count' do
|
|
94
94
|
job_invocation.total_hosts_count.must_equal 'N/A'
|
|
95
|
-
job_invocation.targeting.expects(:resolved_at).returns(Time.now)
|
|
95
|
+
job_invocation.targeting.expects(:resolved_at).returns(Time.now.getlocal)
|
|
96
96
|
job_invocation.total_hosts_count.must_equal 0
|
|
97
97
|
end
|
|
98
98
|
|
|
@@ -10,6 +10,12 @@ describe JobTemplate do
|
|
|
10
10
|
end
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
+
it 'has a unique name' do
|
|
14
|
+
template1 = FactoryGirl.create(:job_template)
|
|
15
|
+
template2 = FactoryGirl.build(:job_template, :name => template1.name)
|
|
16
|
+
refute template2.valid?
|
|
17
|
+
end
|
|
18
|
+
|
|
13
19
|
it 'needs a job_category' do
|
|
14
20
|
refute job_template.valid?
|
|
15
21
|
end
|
|
@@ -45,7 +51,7 @@ describe JobTemplate do
|
|
|
45
51
|
|
|
46
52
|
it 'generates the description_format if not set or blank and has inputs' do
|
|
47
53
|
input_name = template.template_inputs.first.name
|
|
48
|
-
expected_result = %
|
|
54
|
+
expected_result = %(%{job_category} with inputs #{input_name}="%{#{input_name}}")
|
|
49
55
|
template.generate_description_format.must_equal expected_result
|
|
50
56
|
template.description_format = ''
|
|
51
57
|
template.generate_description_format.must_equal expected_result
|
|
@@ -66,7 +72,7 @@ describe JobTemplate do
|
|
|
66
72
|
end
|
|
67
73
|
end
|
|
68
74
|
|
|
69
|
-
context 'importing a template' do
|
|
75
|
+
context 'importing a new template' do
|
|
70
76
|
let(:template) do
|
|
71
77
|
template = <<-END_TEMPLATE
|
|
72
78
|
<%#
|
|
@@ -78,12 +84,32 @@ describe JobTemplate do
|
|
|
78
84
|
- name: service_name
|
|
79
85
|
input_type: user
|
|
80
86
|
required: true
|
|
87
|
+
- name: verbose
|
|
88
|
+
input_type: user
|
|
81
89
|
%>
|
|
82
90
|
|
|
83
91
|
service <%= input("service_name") %> restart
|
|
84
92
|
END_TEMPLATE
|
|
85
93
|
|
|
86
|
-
JobTemplate.import(template, :default => true)
|
|
94
|
+
JobTemplate.import!(template, :default => true)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
let(:template_with_input_sets) do
|
|
98
|
+
template_with_input_sets = <<-END_TEMPLATE
|
|
99
|
+
<%#
|
|
100
|
+
kind: job_template
|
|
101
|
+
name: Service Restart - Custom
|
|
102
|
+
job_category: Service Restart
|
|
103
|
+
provider_type: SSH
|
|
104
|
+
foreign_input_sets:
|
|
105
|
+
- template: #{template.name}
|
|
106
|
+
exclude: verbose
|
|
107
|
+
%>
|
|
108
|
+
|
|
109
|
+
service <%= input("service_name") %> restart
|
|
110
|
+
END_TEMPLATE
|
|
111
|
+
|
|
112
|
+
JobTemplate.import!(template_with_input_sets, :default => true)
|
|
87
113
|
end
|
|
88
114
|
|
|
89
115
|
it 'sets the name' do
|
|
@@ -98,11 +124,108 @@ describe JobTemplate do
|
|
|
98
124
|
template.template_inputs.first.name.must_equal 'service_name'
|
|
99
125
|
end
|
|
100
126
|
|
|
127
|
+
it 'imports input sets' do
|
|
128
|
+
template_with_input_sets.foreign_input_sets.first.target_template.must_equal template
|
|
129
|
+
template_with_input_sets.template_inputs_with_foreign.map(&:name).must_equal ['service_name']
|
|
130
|
+
end
|
|
131
|
+
|
|
101
132
|
it 'sets additional options' do
|
|
102
133
|
template.default.must_equal true
|
|
103
134
|
end
|
|
104
135
|
end
|
|
105
136
|
|
|
137
|
+
context 'importing an existing template' do
|
|
138
|
+
let(:included) do
|
|
139
|
+
template = <<-END_TEMPLATE
|
|
140
|
+
<%#
|
|
141
|
+
kind: job_template
|
|
142
|
+
name: Banner
|
|
143
|
+
job_category: Commands
|
|
144
|
+
provider_type: SSH
|
|
145
|
+
template_inputs:
|
|
146
|
+
- name: banner_message
|
|
147
|
+
input_type: user
|
|
148
|
+
required: true
|
|
149
|
+
%>
|
|
150
|
+
|
|
151
|
+
echo input(:banner_message)
|
|
152
|
+
END_TEMPLATE
|
|
153
|
+
|
|
154
|
+
JobTemplate.import!(template, :default => true)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
let(:existing) do
|
|
158
|
+
template = <<-END_TEMPLATE
|
|
159
|
+
<%#
|
|
160
|
+
kind: job_template
|
|
161
|
+
name: Ping a Thing
|
|
162
|
+
job_category: Commands
|
|
163
|
+
provider_type: SSH
|
|
164
|
+
template_inputs:
|
|
165
|
+
- name: hostname
|
|
166
|
+
input_type: user
|
|
167
|
+
options: "www.google.com"
|
|
168
|
+
required: true
|
|
169
|
+
foreign_input_sets:
|
|
170
|
+
- template: #{included.name}
|
|
171
|
+
%>
|
|
172
|
+
|
|
173
|
+
ping -c 5 <%= input("hostname") %>
|
|
174
|
+
END_TEMPLATE
|
|
175
|
+
|
|
176
|
+
JobTemplate.import!(template, :default => true)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
let(:updated) do
|
|
180
|
+
<<-END_TEMPLATE
|
|
181
|
+
<%#
|
|
182
|
+
kind: job_template
|
|
183
|
+
name: Ping a Thing
|
|
184
|
+
job_category: Commands
|
|
185
|
+
provider_type: SSH
|
|
186
|
+
template_inputs:
|
|
187
|
+
- name: hostname
|
|
188
|
+
input_type: user
|
|
189
|
+
options: 'www.redhat.com'
|
|
190
|
+
required: true
|
|
191
|
+
- name: count
|
|
192
|
+
input_type: user
|
|
193
|
+
required: true
|
|
194
|
+
foreign_input_sets:
|
|
195
|
+
- template: #{included.name}
|
|
196
|
+
exclude: banner_message
|
|
197
|
+
%>
|
|
198
|
+
|
|
199
|
+
ping -c <%= input('count') %> <%= input('hostname') %>
|
|
200
|
+
END_TEMPLATE
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
it 'will not overwrite by default' do
|
|
204
|
+
existing
|
|
205
|
+
refute JobTemplate.import!(updated)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
let(:synced_template) do
|
|
209
|
+
existing
|
|
210
|
+
JobTemplate.import!(updated, :update => true)
|
|
211
|
+
existing.reload
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
it 'syncs inputs' do
|
|
215
|
+
hostname = synced_template.template_inputs.find { |input| input.name == 'hostname' }
|
|
216
|
+
hostname.options.must_equal 'www.redhat.com'
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
it 'syncs content' do
|
|
220
|
+
synced_template.template.must_match(/\s+ping -c <%= input\('count'\) %> <%= input\('hostname'\) %>\s+/m)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
it 'syncs input sets' do
|
|
224
|
+
synced_template.foreign_input_sets.first.target_template.must_equal included
|
|
225
|
+
synced_template.template_inputs_with_foreign.map(&:name).must_equal ["hostname", "count"]
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
106
229
|
context 'there is existing template invocation of a job template' do
|
|
107
230
|
let(:job_invocation) { FactoryGirl.create(:job_invocation, :with_template) }
|
|
108
231
|
let(:job_template) { job_invocation.pattern_template_invocations.first.template }
|