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
         
     |