foreman_remote_execution 0.0.4 → 0.0.5
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/assets/javascripts/template_invocation.js +9 -0
- data/app/controllers/job_invocations_controller.rb +31 -8
- data/app/helpers/remote_execution_helper.rb +10 -0
- data/app/models/job_invocation.rb +24 -0
- data/app/models/job_invocation_composer.rb +25 -1
- data/app/models/job_template.rb +39 -0
- data/app/models/template_input.rb +6 -1
- data/app/models/template_invocation_input_value.rb +2 -0
- data/app/views/job_invocations/_form.html.erb +11 -2
- data/app/views/job_invocations/_tab_overview.html.erb +11 -1
- data/app/views/template_inputs/_form.html.erb +3 -0
- data/app/views/templates/package_action.erb +52 -0
- data/app/views/templates/puppet_run_once.erb +12 -0
- data/app/views/templates/run_command.erb +12 -0
- data/app/views/templates/service_action.erb +21 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20150827144500_change_targeting_search_query_type.rb +5 -0
- data/db/migrate/20150827152730_add_options_to_template_input.rb +5 -0
- data/db/seeds.d/70-job_templates.rb +7 -0
- data/lib/foreman_remote_execution/engine.rb +1 -1
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/test/unit/input_template_renderer_test.rb +27 -0
- data/test/unit/job_invocation_composer_test.rb +50 -2
- data/test/unit/job_template_test.rb +49 -0
- data/test/unit/template_invocation_input_value_test.rb +29 -0
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 66bde99bb7667a338eddcafc4ace45a5a033b8bd
|
4
|
+
data.tar.gz: d55a2a2d6789076c01948424f8a9548e7ed1d2de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b99fca6b8d6d9f576cc42414f6c37e91b2121887cf696692d2beec4264ac776c4d2f16c983366c914917023724ce2e8548b1ef6caeef7b7883f10380f8bba344
|
7
|
+
data.tar.gz: 85dd26a1fbbc873a0a0493672922d73067d5d73cf33d610115357a3abd59570776bb6cc06ea38b32986f08b64c914db146a61561c81718e5c1b056989dc1342c
|
@@ -19,6 +19,11 @@ function refresh_execution_form() {
|
|
19
19
|
});
|
20
20
|
}
|
21
21
|
|
22
|
+
function refresh_search_query(value){
|
23
|
+
id = value.val;
|
24
|
+
$('textarea#targeting_search_query').val($('span#bookmark_query_map span#bookmark-' + id).data('query'));
|
25
|
+
}
|
26
|
+
|
22
27
|
function job_invocation_form_binds() {
|
23
28
|
$('input.job_template_selector').on('click', function () {
|
24
29
|
parent_fieldset = $(this).closest('fieldset');
|
@@ -29,4 +34,8 @@ function job_invocation_form_binds() {
|
|
29
34
|
$('select#job_invocation_job_name').on('change', refresh_execution_form);
|
30
35
|
|
31
36
|
$('button#refresh_execution_form').on('click', refresh_execution_form);
|
37
|
+
|
38
|
+
$('textarea#targeting_search_query').on('change', refresh_execution_form);
|
39
|
+
|
40
|
+
$('select#targeting_bookmark_id').on('change', refresh_search_query);
|
32
41
|
}
|
@@ -2,16 +2,28 @@ class JobInvocationsController < ApplicationController
|
|
2
2
|
include Foreman::Controller::AutoCompleteSearch
|
3
3
|
|
4
4
|
def new
|
5
|
-
@composer = JobInvocationComposer.new(
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
@composer = JobInvocationComposer.new.compose_from_params(
|
6
|
+
:host_ids => params[:host_ids],
|
7
|
+
:targeting => {
|
8
|
+
:targeting_type => Targeting::STATIC_TYPE,
|
9
|
+
:bookmark_id => params[:bookmark_id]
|
10
|
+
})
|
11
|
+
end
|
12
|
+
|
13
|
+
def rerun
|
14
|
+
job_invocation = resource_base.find(params[:id])
|
15
|
+
@composer = JobInvocationComposer.new.compose_from_invocation(job_invocation)
|
16
|
+
|
17
|
+
if params[:failed_only]
|
18
|
+
host_ids = job_invocation.failed_host_ids
|
19
|
+
@composer.search_query = @composer.targeting.build_query_from_hosts(host_ids)
|
20
|
+
end
|
21
|
+
|
22
|
+
render :action => 'new'
|
11
23
|
end
|
12
24
|
|
13
25
|
def create
|
14
|
-
@composer = JobInvocationComposer.new(
|
26
|
+
@composer = JobInvocationComposer.new.compose_from_params(params)
|
15
27
|
if @composer.save
|
16
28
|
@task = ForemanTasks.async_task(::Actions::RemoteExecution::RunHostsJob, @composer.job_invocation)
|
17
29
|
redirect_to job_invocation_path(@composer.job_invocation)
|
@@ -30,6 +42,17 @@ class JobInvocationsController < ApplicationController
|
|
30
42
|
|
31
43
|
# refreshes the form
|
32
44
|
def refresh
|
33
|
-
@composer = JobInvocationComposer.new(
|
45
|
+
@composer = JobInvocationComposer.new.compose_from_params(params)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def action_permission
|
51
|
+
case params[:action]
|
52
|
+
when 'rerun'
|
53
|
+
'create'
|
54
|
+
else
|
55
|
+
super
|
56
|
+
end
|
34
57
|
end
|
35
58
|
end
|
@@ -57,6 +57,16 @@ module RemoteExecutionHelper
|
|
57
57
|
def job_invocation_task_buttons(task)
|
58
58
|
buttons = []
|
59
59
|
buttons << link_to(_('Refresh'), {}, :class => 'btn btn-default', :title => _('Refresh this page'))
|
60
|
+
if authorized_for(:permission => :create_job_invocations)
|
61
|
+
buttons << link_to(_("Rerun"), rerun_job_invocation_path(:id => task.locks.where(:resource_type => 'JobInvocation').first.resource),
|
62
|
+
:class => "btn btn-default",
|
63
|
+
:title => _('Rerun the job'))
|
64
|
+
end
|
65
|
+
if authorized_for(:permission => :create_job_invocations)
|
66
|
+
buttons << link_to(_("Rerun failed"), rerun_job_invocation_path(:id => task.locks.where(:resource_type => 'JobInvocation').first.resource, :failed_only => 1),
|
67
|
+
:class => "btn btn-default",
|
68
|
+
:title => _('Rerun on failed hosts'))
|
69
|
+
end
|
60
70
|
if authorized_for(:permission => :view_foreman_tasks, :auth_object => task)
|
61
71
|
buttons << link_to(_("Last Job Task"), foreman_tasks_task_path(task),
|
62
72
|
:class => "btn btn-default",
|
@@ -18,4 +18,28 @@ class JobInvocation < ActiveRecord::Base
|
|
18
18
|
def to_action_input
|
19
19
|
{ :id => id, :name => job_name }
|
20
20
|
end
|
21
|
+
|
22
|
+
def template_invocations_tasks
|
23
|
+
if last_task.present?
|
24
|
+
last_task.sub_tasks.for_action_types('Actions::RemoteExecution::RunHostJob')
|
25
|
+
else
|
26
|
+
ForemanTasks::Task.for_action_types('Actions::RemoteExecution::RunHostJob').where('1=0')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def failed_template_invocation_tasks
|
31
|
+
template_invocations_tasks.where(:result => 'warning')
|
32
|
+
end
|
33
|
+
|
34
|
+
def failed_host_ids
|
35
|
+
locks_for_resource(failed_template_invocation_tasks, 'Host::Managed').map(&:resource_id)
|
36
|
+
end
|
37
|
+
|
38
|
+
def failed_hosts
|
39
|
+
locks_for_resource(failed_template_invocation_tasks, 'Host::Managed').map(&:resource)
|
40
|
+
end
|
41
|
+
|
42
|
+
def locks_for_resource(tasks, resource_type)
|
43
|
+
tasks.map { |task| task.locks.where(:resource_type => resource_type).first }.compact
|
44
|
+
end
|
21
45
|
end
|
@@ -3,8 +3,11 @@ class JobInvocationComposer
|
|
3
3
|
attr_reader :job_template_ids
|
4
4
|
delegate :job_name, :targeting, :to => :job_invocation
|
5
5
|
|
6
|
-
def initialize(job_invocation
|
6
|
+
def initialize(job_invocation = JobInvocation.new)
|
7
7
|
@job_invocation = job_invocation
|
8
|
+
end
|
9
|
+
|
10
|
+
def compose_from_params(params)
|
8
11
|
@params = params
|
9
12
|
|
10
13
|
@host_ids = validate_host_ids(params[:host_ids])
|
@@ -15,6 +18,19 @@ class JobInvocationComposer
|
|
15
18
|
job_invocation.targeting = build_targeting
|
16
19
|
|
17
20
|
@job_template_ids = validate_job_template_ids(job_templates_base.keys.compact)
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def compose_from_invocation(invocation)
|
25
|
+
@params = {}
|
26
|
+
|
27
|
+
job_invocation.job_name = validate_job_name(invocation.job_name)
|
28
|
+
job_invocation.targeting = invocation.targeting.dup
|
29
|
+
@search_query = targeting.search_query unless targeting.bookmark_id.present?
|
30
|
+
|
31
|
+
@job_template_ids = invocation.template_invocations.map(&:template_id)
|
32
|
+
@template_invocations = dup_template_invocations(invocation)
|
33
|
+
self
|
18
34
|
end
|
19
35
|
|
20
36
|
def valid?
|
@@ -117,6 +133,14 @@ class JobInvocationComposer
|
|
117
133
|
|
118
134
|
private
|
119
135
|
|
136
|
+
def dup_template_invocations(job_invocation)
|
137
|
+
job_invocation.template_invocations.map do |template_invocation|
|
138
|
+
duplicate = template_invocation.dup
|
139
|
+
template_invocation.input_values.map { |value| duplicate.input_values.build :value => value.value, :template_input_id => value.template_input_id }
|
140
|
+
duplicate
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
120
144
|
def targeting_base
|
121
145
|
@params.fetch(:targeting, {})
|
122
146
|
end
|
data/app/models/job_template.rb
CHANGED
@@ -37,14 +37,53 @@ class JobTemplate < ::Template
|
|
37
37
|
end
|
38
38
|
self.table_name = 'templates'
|
39
39
|
|
40
|
+
# Import a template ERB, with metadata in the first YAML comment
|
41
|
+
def self.import(template, options = {})
|
42
|
+
metadata = parse_metadata(template)
|
43
|
+
return if metadata.blank? || metadata.delete('kind') != 'job_template' || self.find_by_name(metadata['name'])
|
44
|
+
|
45
|
+
inputs = metadata.delete('template_inputs')
|
46
|
+
|
47
|
+
# This awkward dance is because you can't instantiate a new template with :locked => true
|
48
|
+
template = self.create(metadata.merge(:template => template.gsub(/<%\#.+?.-?%>\n?/m, '')))
|
49
|
+
template.update_attributes(options)
|
50
|
+
template.assign_taxonomies
|
51
|
+
|
52
|
+
inputs.each do |input|
|
53
|
+
template.template_inputs << TemplateInput.create(input)
|
54
|
+
end
|
55
|
+
|
56
|
+
template
|
57
|
+
end
|
58
|
+
|
40
59
|
# Override method in Taxonomix as Template is not used attached to a Host,
|
41
60
|
# and matching a Host does not prevent removing a template from its taxonomy.
|
42
61
|
def used_taxonomy_ids(type)
|
43
62
|
[]
|
44
63
|
end
|
45
64
|
|
65
|
+
def dup
|
66
|
+
dup = super
|
67
|
+
self.template_inputs.each do |input|
|
68
|
+
dup.template_inputs.build input.attributes.except('template_id', 'id', 'created_at', 'updated_at')
|
69
|
+
end
|
70
|
+
dup
|
71
|
+
end
|
72
|
+
|
73
|
+
def assign_taxonomies
|
74
|
+
if default
|
75
|
+
organizations << Organization.all if SETTINGS[:organizations_enabled]
|
76
|
+
locations << Location.all if SETTINGS[:locations_enabled]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
46
80
|
private
|
47
81
|
|
82
|
+
def self.parse_metadata(template)
|
83
|
+
match = template.match(/<%\#(.+?).-?%>/m)
|
84
|
+
match.nil? ? {} : YAML.load(match[1])
|
85
|
+
end
|
86
|
+
|
48
87
|
# we can't use standard validator, .provider_names output can change but the validator does not reflect it
|
49
88
|
def provider_type_whitelist
|
50
89
|
errors.add :provider_type, :uniq unless RemoteExecutionProvider.provider_names.include?(self.provider_type)
|
@@ -6,7 +6,8 @@ class TemplateInput < ActiveRecord::Base
|
|
6
6
|
:puppet_parameter => N_('Puppet parameter') }.with_indifferent_access
|
7
7
|
|
8
8
|
attr_accessible :name, :required, :input_type, :fact_name, :variable_name,
|
9
|
-
:puppet_class_name, :puppet_parameter_name, :description, :job_template_id
|
9
|
+
:puppet_class_name, :puppet_parameter_name, :description, :job_template_id,
|
10
|
+
:options
|
10
11
|
|
11
12
|
belongs_to :template
|
12
13
|
has_many :template_invocation_input_values, :dependent => :destroy
|
@@ -42,6 +43,10 @@ class TemplateInput < ActiveRecord::Base
|
|
42
43
|
get_resolver(renderer).value
|
43
44
|
end
|
44
45
|
|
46
|
+
def options_array
|
47
|
+
self.options.blank? ? [] : self.options.split(/\r?\n/).map(&:strip)
|
48
|
+
end
|
49
|
+
|
45
50
|
private
|
46
51
|
|
47
52
|
def get_resolver(renderer)
|
@@ -5,4 +5,6 @@ class TemplateInvocationInputValue < ActiveRecord::Base
|
|
5
5
|
|
6
6
|
validates :value, :presence => true, :if => proc { |v| v.template_input.required? }
|
7
7
|
|
8
|
+
validates :value, :inclusion => { :in => proc { |v| v.template_input.options_array } },
|
9
|
+
:if => proc { |v| v.template_input.input_type == 'user' && v.template_input.options_array.present? }
|
8
10
|
end
|
@@ -2,7 +2,12 @@
|
|
2
2
|
<%= selectable_f f, :job_name, @composer.available_job_names %>
|
3
3
|
|
4
4
|
<%= fields_for @composer.targeting do |targeting_fields| %>
|
5
|
-
|
5
|
+
<span id="bookmark_query_map" >
|
6
|
+
<% @composer.available_bookmarks.each do |bookmark| %>
|
7
|
+
<span id="bookmark-<%= bookmark.id %>" data-query="<%= bookmark.query %>"></span>
|
8
|
+
<% end %>
|
9
|
+
</span>
|
10
|
+
<%= selectable_f targeting_fields, :bookmark_id, @composer.available_bookmarks.map {|b| [ b.name, b.id ] }, :selected => @composer.targeting.bookmark_id, :include_blank => true %>
|
6
11
|
<%= textarea_f targeting_fields, :search_query, :value => @composer.displayed_search_query, :rows => 5 %>
|
7
12
|
|
8
13
|
<div class="form-group ">
|
@@ -49,7 +54,11 @@
|
|
49
54
|
<%= job_template_fields.fields_for :input_values do |input_values_fields| %>
|
50
55
|
<% job_template.template_inputs.where(:input_type => 'user').each do |input| %>
|
51
56
|
<%= input_values_fields.fields_for input.id.to_s, @composer.template_invocation_input_value_for(input) do |input_fields| %>
|
52
|
-
|
57
|
+
<% unless input.options.blank? %>
|
58
|
+
<%= selectable_f input_fields, :value, input.options_array, {:include_blank => !input.required }, :require => input.required, :label => input.name, :help_inline => input.description %>
|
59
|
+
<% else %>
|
60
|
+
<%= textarea_f input_fields, :value, :label => input.name, :help_inline => input.description, :required => input.required, :rows => 2 %>
|
61
|
+
<% end %>
|
53
62
|
<% end %>
|
54
63
|
<% end %>
|
55
64
|
<% end %>
|
@@ -27,7 +27,17 @@
|
|
27
27
|
<h5>
|
28
28
|
<b><%= template_invocation.template.name %></b> <%= 'through' %> <%= _(RemoteExecutionProvider.provider_for(template_invocation.template.provider_type)) %>
|
29
29
|
</h5>
|
30
|
-
|
30
|
+
<% target = template_invocation.targeting.hosts.with_os.first || template_invocation.targeting.hosts.first %>
|
31
|
+
<%= _('Preview for target %s') % target.try(:name) || 'N/A' %>
|
32
|
+
|
33
|
+
<% renderer = InputTemplateRenderer.new(template_invocation.template, target, template_invocation) %>
|
34
|
+
<% if (preview = renderer.preview) %>
|
35
|
+
<pre><%= preview %></pre>
|
36
|
+
<% else %>
|
37
|
+
<%= alert :class => "alert-block alert-danger base in fade has-error",
|
38
|
+
:text => renderer.error_message.html_safe %>
|
39
|
+
<% end %>
|
40
|
+
|
31
41
|
|
32
42
|
<% if template_invocation.input_values.present? %>
|
33
43
|
<%= _('following user inputs were provided') %>
|
@@ -16,6 +16,9 @@
|
|
16
16
|
<%= text_f f, :puppet_class_name, :class => 'puppet_parameter_input_type', :required => true %>
|
17
17
|
<%= text_f f, :puppet_parameter_name, :class => 'puppet_parameter_input_type', :required => true %>
|
18
18
|
</div>
|
19
|
+
<div class="user_input_type custom_input_type_fields" style="<%= (f.object.user_template_input? || f.object.new_record?) ? '' : 'display:none' %>">
|
20
|
+
<%= textarea_f f, :options, :rows => 3, :class => 'user_input_type', :help_inline => _("A list of options the user can select from. If not provided, the user will be given a free-form field") %>
|
21
|
+
</div>
|
19
22
|
<%= textarea_f f, :description, :rows => 3 %>
|
20
23
|
<% end %>
|
21
24
|
</div>
|
@@ -0,0 +1,52 @@
|
|
1
|
+
<%#
|
2
|
+
kind: job_template
|
3
|
+
name: Package Action - SSH Default
|
4
|
+
job_name: Package Action
|
5
|
+
provider_type: Ssh
|
6
|
+
template_inputs:
|
7
|
+
- name: pre_script
|
8
|
+
description: A script to run prior to the package action
|
9
|
+
input_type: user
|
10
|
+
required: false
|
11
|
+
- name: action
|
12
|
+
description: 'The package action: install, update, or remove'
|
13
|
+
input_type: user
|
14
|
+
required: true
|
15
|
+
options: "install\nupdate\nremove"
|
16
|
+
- name: package
|
17
|
+
description: The name of the package, if any
|
18
|
+
input_type: user
|
19
|
+
required: false
|
20
|
+
- name: post_script
|
21
|
+
description: A script to run after the package action
|
22
|
+
input_type: user
|
23
|
+
required: false
|
24
|
+
%>
|
25
|
+
|
26
|
+
die() {
|
27
|
+
echo "${1}, exiting..."
|
28
|
+
exit $2
|
29
|
+
}
|
30
|
+
|
31
|
+
<% unless input("pre_script").blank? -%>
|
32
|
+
# Pre Script
|
33
|
+
<%= input("pre_script") %>
|
34
|
+
RETVAL=$?
|
35
|
+
[ $RETVAL -eq 0 ] || die "Pre script failed" $RETVAL
|
36
|
+
<% end -%>
|
37
|
+
|
38
|
+
# Action
|
39
|
+
<% if @host.operatingsystem.family == 'Redhat' -%>
|
40
|
+
yum -y <%= input("action") %> <%= input("package") %>
|
41
|
+
<% elsif @host.operatingsystem.family == 'Debian' -%>
|
42
|
+
apt-get -y <%= input("action") %> <%= input("package") %>
|
43
|
+
<% end -%>
|
44
|
+
RETVAL=$?
|
45
|
+
[ $RETVAL -eq 0 ] || die "Package action failed" $RETVAL
|
46
|
+
|
47
|
+
<% unless input("post_script").blank? -%>
|
48
|
+
# Post Script
|
49
|
+
<%= input("post_script") %>
|
50
|
+
RETVAL=$?
|
51
|
+
[ $RETVAL -eq 0 ] || die "Post script failed" $RETVAL
|
52
|
+
<% end -%>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<%#
|
2
|
+
kind: job_template
|
3
|
+
name: Puppet Run Once - SSH Default
|
4
|
+
job_name: Puppet Run Once
|
5
|
+
provider_type: Ssh
|
6
|
+
template_inputs:
|
7
|
+
- name: puppet_options
|
8
|
+
description: Additional options to pass to puppet
|
9
|
+
input_type: user
|
10
|
+
required: false
|
11
|
+
%>
|
12
|
+
puppet agent --onetime --no-usecacheonfailure <%= input("puppet_options") %>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<%#
|
2
|
+
kind: job_template
|
3
|
+
name: Service Action - SSH Default
|
4
|
+
job_name: Service Action
|
5
|
+
provider_type: Ssh
|
6
|
+
template_inputs:
|
7
|
+
- name: action
|
8
|
+
description: Action to perform on the service
|
9
|
+
input_type: user
|
10
|
+
options: "restart\nstart\nstop\nstatus"
|
11
|
+
required: true
|
12
|
+
- name: service
|
13
|
+
description: Name of the service
|
14
|
+
input_type: user
|
15
|
+
required: true
|
16
|
+
%>
|
17
|
+
<% if @host.operatingsystem.family == "Redhat" && @host.operatingsystem.major.to_i > 6 %>
|
18
|
+
systemctl <%= input("action") %> <%= input("service") %>
|
19
|
+
<% else %>
|
20
|
+
service <%= input("service") %> <%= input("action") %>
|
21
|
+
<% end -%>
|
data/config/routes.rb
CHANGED
@@ -45,7 +45,7 @@ module ForemanRemoteExecution
|
|
45
45
|
|
46
46
|
permission :view_job_invocations, { :job_invocations => [:index, :show, :auto_complete_search] }, :resource_type => 'JobInvocation'
|
47
47
|
|
48
|
-
permission :create_job_invocations, { :job_invocations => [:new, :create, :refresh] }, :resource_type => 'JobInvocation'
|
48
|
+
permission :create_job_invocations, { :job_invocations => [:new, :create, :refresh, :rerun] }, :resource_type => 'JobInvocation'
|
49
49
|
end
|
50
50
|
|
51
51
|
# Add a new role called 'ForemanRemoteExecution' if it doesn't exist
|
@@ -84,6 +84,33 @@ describe InputTemplateRenderer do
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
end
|
87
|
+
|
88
|
+
context 'with options specified' do
|
89
|
+
|
90
|
+
let(:job_invocation) { FactoryGirl.create(:job_invocation) }
|
91
|
+
let(:template_invocation) { FactoryGirl.build(:template_invocation, :template => template) }
|
92
|
+
let(:result) { renderer.render }
|
93
|
+
|
94
|
+
before do
|
95
|
+
template.template_inputs << FactoryGirl.build(:template_input, :name => 'service_name', :input_type => 'user', :options => "httpd\nforeman")
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'with a valid input defined' do
|
99
|
+
before do
|
100
|
+
job_invocation.template_invocations << template_invocation
|
101
|
+
renderer.invocation = template_invocation
|
102
|
+
|
103
|
+
FactoryGirl.create(:template_invocation_input_value,
|
104
|
+
:template_invocation => template_invocation,
|
105
|
+
:template_input => template.template_inputs.first,
|
106
|
+
:value => 'foreman')
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'can render with job invocation with corresponding value' do
|
110
|
+
renderer.render.must_equal 'service restart foreman'
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
87
114
|
end
|
88
115
|
|
89
116
|
context "renderer for template with fact input used" do
|
@@ -39,7 +39,7 @@ describe JobInvocationComposer do
|
|
39
39
|
context 'with general new invocation and empty params' do
|
40
40
|
let(:params) { {} }
|
41
41
|
let(:job_invocation) { JobInvocation.new }
|
42
|
-
let(:composer) { JobInvocationComposer.new(job_invocation
|
42
|
+
let(:composer) { JobInvocationComposer.new(job_invocation).compose_from_params(params) }
|
43
43
|
|
44
44
|
describe '#available_templates' do
|
45
45
|
it 'obeys authorization' do
|
@@ -369,7 +369,9 @@ describe JobInvocationComposer do
|
|
369
369
|
:input_values => { input1.id.to_s => { :value => 'value1' } }
|
370
370
|
} } }
|
371
371
|
end
|
372
|
-
let(:params)
|
372
|
+
let(:params) do
|
373
|
+
{ :job_invocation => { :providers => { :ssh => ssh_params } }, :targeting => { :search_query => "name = #{host.name}" } }.with_indifferent_access
|
374
|
+
end
|
373
375
|
|
374
376
|
it 'validates all associated objects even if some of the is invalid' do
|
375
377
|
composer
|
@@ -410,6 +412,52 @@ describe JobInvocationComposer do
|
|
410
412
|
end
|
411
413
|
end
|
412
414
|
|
415
|
+
describe '#compose_from_invocation(existing_invocation)' do
|
416
|
+
let(:host) { FactoryGirl.create(:host) }
|
417
|
+
let(:ssh_params) do
|
418
|
+
{ :job_template_id => testing_job_template_1.id.to_s,
|
419
|
+
:job_templates => {
|
420
|
+
testing_job_template_1.id.to_s => {
|
421
|
+
:input_values => { input1.id.to_s => { :value => 'value1' } }
|
422
|
+
} } }
|
423
|
+
end
|
424
|
+
let(:params) do
|
425
|
+
{
|
426
|
+
:job_invocation => {
|
427
|
+
:providers => { :ssh => ssh_params }
|
428
|
+
},
|
429
|
+
:targeting => {
|
430
|
+
:search_query => "name = #{host.name}",
|
431
|
+
:targeting_type => Targeting::STATIC_TYPE
|
432
|
+
}
|
433
|
+
}.with_indifferent_access
|
434
|
+
end
|
435
|
+
let(:existing) { job_invocation.reload }
|
436
|
+
let(:new_job_invocation) { JobInvocation.new }
|
437
|
+
let(:new_composer) { JobInvocationComposer.new(new_job_invocation).compose_from_invocation(job_invocation) }
|
438
|
+
|
439
|
+
before do
|
440
|
+
composer.save
|
441
|
+
end
|
442
|
+
|
443
|
+
it 'sets the same job name' do
|
444
|
+
new_composer.job_name.must_equal existing.job_name
|
445
|
+
end
|
446
|
+
|
447
|
+
it 'builds new targeting object which keeps search query' do
|
448
|
+
new_composer.targeting.wont_equal existing.targeting
|
449
|
+
new_composer.search_query.must_equal existing.targeting.search_query
|
450
|
+
end
|
451
|
+
|
452
|
+
it 'keeps job template ids' do
|
453
|
+
new_composer.job_template_ids.must_equal existing.template_invocations.map(&:template_id)
|
454
|
+
end
|
455
|
+
|
456
|
+
it 'keeps template invocations and their values' do
|
457
|
+
new_composer.template_invocations.size.must_equal existing.template_invocations.size
|
458
|
+
end
|
459
|
+
|
460
|
+
end
|
413
461
|
end
|
414
462
|
end
|
415
463
|
end
|
@@ -1,5 +1,54 @@
|
|
1
1
|
require 'test_plugin_helper'
|
2
2
|
|
3
3
|
describe JobTemplate do
|
4
|
+
context 'cloning' do
|
5
|
+
let(:job_template) { FactoryGirl.build(:job_template, :with_input) }
|
4
6
|
|
7
|
+
describe '#dup' do
|
8
|
+
it 'duplicates also template inputs' do
|
9
|
+
duplicate = job_template.dup
|
10
|
+
duplicate.wont_equal job_template
|
11
|
+
duplicate.template_inputs.wont_be_empty
|
12
|
+
duplicate.template_inputs.first.wont_equal job_template.template_inputs.first
|
13
|
+
duplicate.template_inputs.first.name.must_equal job_template.template_inputs.first.name
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'importing a template' do
|
19
|
+
let(:template) do
|
20
|
+
template = <<-END_TEMPLATE
|
21
|
+
<%#
|
22
|
+
kind: job_template
|
23
|
+
name: Service Restart
|
24
|
+
job_name: Service Restart
|
25
|
+
provider_type: Ssh
|
26
|
+
template_inputs:
|
27
|
+
- name: service_name
|
28
|
+
input_type: user
|
29
|
+
required: true
|
30
|
+
%>
|
31
|
+
|
32
|
+
service <%= input("service_name") %> restart
|
33
|
+
END_TEMPLATE
|
34
|
+
|
35
|
+
JobTemplate.import(template, :default => true)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'sets the name' do
|
39
|
+
template.name.must_equal 'Service Restart'
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'has a template' do
|
43
|
+
template.template.squish.must_equal 'service <%= input("service_name") %> restart'
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'imports inputs' do
|
47
|
+
template.template_inputs.first.name.must_equal 'service_name'
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'sets additional options' do
|
51
|
+
template.default.must_equal true
|
52
|
+
end
|
53
|
+
end
|
5
54
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'test_plugin_helper'
|
2
|
+
|
3
|
+
describe TemplateInvocationInputValue do
|
4
|
+
let(:template) { FactoryGirl.build(:job_template, :template => 'service restart <%= input("service_name") -%>') }
|
5
|
+
let(:renderer) { InputTemplateRenderer.new(template) }
|
6
|
+
let(:job_invocation) { FactoryGirl.create(:job_invocation) }
|
7
|
+
let(:template_invocation) { FactoryGirl.build(:template_invocation, :template => template) }
|
8
|
+
let(:result) { renderer.render }
|
9
|
+
|
10
|
+
context 'with selectable options' do
|
11
|
+
before do
|
12
|
+
result # let is lazy
|
13
|
+
template.template_inputs << FactoryGirl.build(:template_input, :name => 'service_name', :input_type => 'user',
|
14
|
+
:required => true, :options => "foreman\nhttpd")
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'fails with an invalid option' do
|
18
|
+
refute_valid FactoryGirl.build(:template_invocation_input_value, :template_invocation => template_invocation,
|
19
|
+
:template_input => template.template_inputs.first,
|
20
|
+
:value => 'sendmail')
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'succeeds with valid option' do
|
24
|
+
assert_valid FactoryGirl.build(:template_invocation_input_value, :template_invocation => template_invocation,
|
25
|
+
:template_input => template.template_inputs.first,
|
26
|
+
:value => 'foreman')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreman_remote_execution
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Foreman Remote Execution team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-09-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -161,6 +161,10 @@ files:
|
|
161
161
|
- app/views/job_templates/index.html.erb
|
162
162
|
- app/views/job_templates/new.html.erb
|
163
163
|
- app/views/template_inputs/_form.html.erb
|
164
|
+
- app/views/templates/package_action.erb
|
165
|
+
- app/views/templates/puppet_run_once.erb
|
166
|
+
- app/views/templates/run_command.erb
|
167
|
+
- app/views/templates/service_action.erb
|
164
168
|
- config/routes.rb
|
165
169
|
- db/migrate/20150612121541_add_job_template_to_template.rb
|
166
170
|
- db/migrate/20150616080015_create_template_input.rb
|
@@ -169,7 +173,10 @@ files:
|
|
169
173
|
- db/migrate/20150708133305_add_template_invocation.rb
|
170
174
|
- db/migrate/20150812110800_add_resolved_at_to_targeting.rb
|
171
175
|
- db/migrate/20150812145900_add_last_task_id_to_job_invocation.rb
|
176
|
+
- db/migrate/20150827144500_change_targeting_search_query_type.rb
|
177
|
+
- db/migrate/20150827152730_add_options_to_template_input.rb
|
172
178
|
- db/seeds.d/60-ssh_proxy_feature.rb
|
179
|
+
- db/seeds.d/70-job_templates.rb
|
173
180
|
- doc/Gemfile
|
174
181
|
- doc/Gemfile.lock
|
175
182
|
- doc/Rakefile
|
@@ -223,6 +230,7 @@ files:
|
|
223
230
|
- test/unit/remote_execution_provider_test.rb
|
224
231
|
- test/unit/targeting_test.rb
|
225
232
|
- test/unit/template_input_test.rb
|
233
|
+
- test/unit/template_invocation_input_value_test.rb
|
226
234
|
homepage: https://github.com/theforeman/foreman_remote_execution
|
227
235
|
licenses: []
|
228
236
|
metadata: {}
|
@@ -250,6 +258,7 @@ summary: A plugin bringing remote execution to the Foreman, completing the confi
|
|
250
258
|
test_files:
|
251
259
|
- test/test_plugin_helper.rb
|
252
260
|
- test/unit/job_template_test.rb
|
261
|
+
- test/unit/template_invocation_input_value_test.rb
|
253
262
|
- test/unit/template_input_test.rb
|
254
263
|
- test/unit/job_invocation_test.rb
|
255
264
|
- test/unit/targeting_test.rb
|