foreman_remote_execution 1.4.5 → 1.4.6
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 +5 -5
 - data/.hound.yml +13 -0
 - data/.rubocop.yml +9 -0
 - data/app/controllers/api/v2/job_invocations_controller.rb +49 -6
 - data/app/controllers/job_invocations_controller.rb +29 -1
 - data/app/helpers/remote_execution_helper.rb +5 -5
 - data/app/lib/actions/remote_execution/run_host_job.rb +32 -20
 - data/app/lib/actions/remote_execution/run_hosts_job.rb +13 -8
 - data/app/models/foreign_input_set.rb +1 -1
 - data/app/models/job_invocation.rb +23 -1
 - data/app/models/job_invocation_composer.rb +25 -3
 - data/app/models/job_template.rb +2 -2
 - data/app/models/remote_execution_feature.rb +22 -10
 - data/app/models/setting/remote_execution.rb +34 -10
 - data/app/models/ssh_execution_provider.rb +8 -0
 - data/app/models/template_input.rb +1 -1
 - data/app/views/api/v2/job_templates/base.json.rabl +1 -1
 - data/app/views/job_invocations/_form.html.erb +11 -5
 - data/app/views/job_invocations/_tab_hosts.html.erb +1 -1
 - data/app/views/job_invocations/index.html.erb +1 -1
 - data/app/views/job_templates/index.html.erb +1 -1
 - data/app/views/remote_execution_features/index.html.erb +1 -1
 - data/app/views/template_inputs/_invocation_form.html.erb +2 -2
 - data/app/views/template_invocations/show.html.erb +1 -1
 - data/config/routes.rb +5 -0
 - data/db/migrate/20160113162007_expand_all_template_invocations.rb +1 -1
 - data/db/migrate/20171129103615_add_secrets_to_job_invocations.rb +6 -0
 - data/db/migrate/20180202072115_add_notification_builder_to_remote_execution_feature.rb +5 -0
 - data/db/migrate/20180202123215_add_feature_id_to_job_invocation.rb +6 -0
 - data/db/migrate/20180226095631_change_task_id_to_uuid.rb +31 -0
 - data/foreman_remote_execution.gemspec +1 -1
 - data/lib/foreman_remote_execution/engine.rb +3 -1
 - data/lib/foreman_remote_execution/version.rb +1 -1
 - data/test/factories/foreman_remote_execution_factories.rb +6 -0
 - data/test/functional/api/v2/job_invocations_controller_test.rb +139 -32
 - data/test/functional/job_invocations_controller_test.rb +49 -0
 - data/test/unit/actions/run_hosts_job_test.rb +10 -6
 - data/test/unit/concerns/foreman_tasks_cleaner_extensions_test.rb +1 -1
 - data/test/unit/job_invocation_composer_test.rb +43 -1
 - metadata +14 -10
 - data/app/models/concerns/foreman_remote_execution/exportable.rb +0 -71
 - data/test/unit/concerns/exportable_test.rb +0 -88
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 2 
     | 
    
         
            +
            SHA256:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 3a0c3ef241880bf26fcf76f004b430ad451324b4d544302a423f92f61ed354c9
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 3fd717b03faf2baa425c23150cba6aee3ecd0025d39a1233eab8b27307141d03
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 65801bb36a9f54ae4c01bab9b4926e28066f7b5f0e608bfeff0f858262d6d432d9c380c5c2b982ea2d5375649fdcfe8d8fce485b820af6091b01b30323120f07
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: e37a034355a6967cc5e760e37299de3ba0afed9845715ae4628e83665606b1e21f558fb677392cee93b396036a420d48fbaaee1b6f5397d75a3a645dd6a39c34
         
     | 
    
        data/.hound.yml
    ADDED
    
    
    
        data/.rubocop.yml
    CHANGED
    
    | 
         @@ -91,5 +91,14 @@ Naming/HeredocDelimiterNaming: 
     | 
|
| 
       91 
91 
     | 
    
         
             
            Style/RescueStandardError:
         
     | 
| 
       92 
92 
     | 
    
         
             
              Enabled: false
         
     | 
| 
       93 
93 
     | 
    
         | 
| 
      
 94 
     | 
    
         
            +
            Style/SafeNavigation:
         
     | 
| 
      
 95 
     | 
    
         
            +
              Enabled: false
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
       94 
97 
     | 
    
         
             
            Lint/BooleanSymbol:
         
     | 
| 
       95 
98 
     | 
    
         
             
              Enabled: false
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
            Metrics/PerceivedComplexity:
         
     | 
| 
      
 101 
     | 
    
         
            +
              Max: 8
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
            Metrics/AbcSize:
         
     | 
| 
      
 104 
     | 
    
         
            +
              Max: 45
         
     | 
| 
         @@ -6,8 +6,7 @@ module Api 
     | 
|
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
                  before_action :find_optional_nested_object
         
     | 
| 
       8 
8 
     | 
    
         
             
                  before_action :find_host, :only => %w{output}
         
     | 
| 
       9 
     | 
    
         
            -
                  before_action :find_resource, :only => %w{show update destroy clone}
         
     | 
| 
       10 
     | 
    
         
            -
                  before_action :validate_template, :only => :create
         
     | 
| 
      
 9 
     | 
    
         
            +
                  before_action :find_resource, :only => %w{show update destroy clone cancel rerun}
         
     | 
| 
       11 
10 
     | 
    
         | 
| 
       12 
11 
     | 
    
         
             
                  wrap_parameters JobInvocation, :include => (JobInvocation.attribute_names + [:ssh])
         
     | 
| 
       13 
12 
     | 
    
         | 
| 
         @@ -24,7 +23,7 @@ module Api 
     | 
|
| 
       24 
23 
     | 
    
         
             
                  # rubocop:disable Metrics/BlockLength
         
     | 
| 
       25 
24 
     | 
    
         
             
                  def_param_group :job_invocation do
         
     | 
| 
       26 
25 
     | 
    
         
             
                    param :job_invocation, Hash, :required => true, :action_aware => true do
         
     | 
| 
       27 
     | 
    
         
            -
                      param :job_template_id, String, :required =>  
     | 
| 
      
 26 
     | 
    
         
            +
                      param :job_template_id, String, :required => false, :desc => N_('The job template to use, parameter is required unless feature was specified')
         
     | 
| 
       28 
27 
     | 
    
         
             
                      param :targeting_type, String, :required => true, :desc => N_('Invocation type, one of %s') % Targeting::TYPES
         
     | 
| 
       29 
28 
     | 
    
         
             
                      param :inputs, Hash, :required => false, :desc => N_('Inputs to use')
         
     | 
| 
       30 
29 
     | 
    
         
             
                      param :ssh, Hash, :desc => N_('SSH provider specific options') do
         
     | 
| 
         @@ -50,16 +49,24 @@ module Api 
     | 
|
| 
       50 
49 
     | 
    
         
             
                      end
         
     | 
| 
       51 
50 
     | 
    
         | 
| 
       52 
51 
     | 
    
         
             
                      param :bookmark_id, Integer, :required => false
         
     | 
| 
       53 
     | 
    
         
            -
                      param :search_query,  
     | 
| 
      
 52 
     | 
    
         
            +
                      param :search_query, String, :required => false
         
     | 
| 
       54 
53 
     | 
    
         
             
                      param :description_format, String, :required => false, :desc => N_('Override the description format from the template for this invocation only')
         
     | 
| 
       55 
54 
     | 
    
         
             
                      param :execution_timeout_interval, Integer, :required => false, :desc => N_('Override the timeout interval from the template for this invocation only')
         
     | 
| 
      
 55 
     | 
    
         
            +
                      param :feature, String, :required => false, :desc => N_('Remote execution feature label that should be triggered, job template assigned to this feature will be used')
         
     | 
| 
       56 
56 
     | 
    
         
             
                    end
         
     | 
| 
       57 
57 
     | 
    
         
             
                  end
         
     | 
| 
       58 
58 
     | 
    
         | 
| 
       59 
59 
     | 
    
         
             
                  api :POST, '/job_invocations/', N_('Create a job invocation')
         
     | 
| 
       60 
60 
     | 
    
         
             
                  param_group :job_invocation, :as => :create
         
     | 
| 
       61 
61 
     | 
    
         
             
                  def create
         
     | 
| 
       62 
     | 
    
         
            -
                     
     | 
| 
      
 62 
     | 
    
         
            +
                    if job_invocation_params[:feature].present?
         
     | 
| 
      
 63 
     | 
    
         
            +
                      composer = composer_for_feature
         
     | 
| 
      
 64 
     | 
    
         
            +
                    else
         
     | 
| 
      
 65 
     | 
    
         
            +
                      validate_template
         
     | 
| 
      
 66 
     | 
    
         
            +
                      composer = JobInvocationComposer.from_api_params(
         
     | 
| 
      
 67 
     | 
    
         
            +
                        job_invocation_params
         
     | 
| 
      
 68 
     | 
    
         
            +
                      )
         
     | 
| 
      
 69 
     | 
    
         
            +
                    end
         
     | 
| 
       63 
70 
     | 
    
         
             
                    composer.trigger!
         
     | 
| 
       64 
71 
     | 
    
         
             
                    @job_invocation = composer.job_invocation
         
     | 
| 
       65 
72 
     | 
    
         
             
                    process_response @job_invocation
         
     | 
| 
         @@ -88,12 +95,39 @@ module Api 
     | 
|
| 
       88 
95 
     | 
    
         
             
                    end
         
     | 
| 
       89 
96 
     | 
    
         
             
                  end
         
     | 
| 
       90 
97 
     | 
    
         | 
| 
      
 98 
     | 
    
         
            +
                  api :POST, '/job_invocations/:id/cancel', N_('Cancel job invocation')
         
     | 
| 
      
 99 
     | 
    
         
            +
                  param :id, :identifier, :required => true
         
     | 
| 
      
 100 
     | 
    
         
            +
                  param :force, :bool
         
     | 
| 
      
 101 
     | 
    
         
            +
                  def cancel
         
     | 
| 
      
 102 
     | 
    
         
            +
                    if @job_invocation.task.cancellable?
         
     | 
| 
      
 103 
     | 
    
         
            +
                      result = @job_invocation.cancel(params.fetch('force', false))
         
     | 
| 
      
 104 
     | 
    
         
            +
                      render :json => { :cancelled => result, :id => @job_invocation.id }
         
     | 
| 
      
 105 
     | 
    
         
            +
                    else
         
     | 
| 
      
 106 
     | 
    
         
            +
                      render :json => { :message => _('The job could not be cancelled.') },
         
     | 
| 
      
 107 
     | 
    
         
            +
                             :status => 422
         
     | 
| 
      
 108 
     | 
    
         
            +
                    end
         
     | 
| 
      
 109 
     | 
    
         
            +
                  end
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                  api :POST, '/job_invocations/:id/rerun', N_('Rerun job on failed hosts')
         
     | 
| 
      
 112 
     | 
    
         
            +
                  param :id, :identifier, :required => true
         
     | 
| 
      
 113 
     | 
    
         
            +
                  param :failed_only, :bool
         
     | 
| 
      
 114 
     | 
    
         
            +
                  def rerun
         
     | 
| 
      
 115 
     | 
    
         
            +
                    composer = JobInvocationComposer.from_job_invocation(@job_invocation, params)
         
     | 
| 
      
 116 
     | 
    
         
            +
                    composer.trigger!
         
     | 
| 
      
 117 
     | 
    
         
            +
                    @job_invocation = composer.job_invocation
         
     | 
| 
      
 118 
     | 
    
         
            +
                    process_response @job_invocation
         
     | 
| 
      
 119 
     | 
    
         
            +
                  end
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
       91 
121 
     | 
    
         
             
                  private
         
     | 
| 
       92 
122 
     | 
    
         | 
| 
       93 
123 
     | 
    
         
             
                  def action_permission
         
     | 
| 
       94 
124 
     | 
    
         
             
                    case params[:action]
         
     | 
| 
       95 
125 
     | 
    
         
             
                    when 'output'
         
     | 
| 
       96 
126 
     | 
    
         
             
                      :view
         
     | 
| 
      
 127 
     | 
    
         
            +
                    when 'cancel'
         
     | 
| 
      
 128 
     | 
    
         
            +
                      :cancel
         
     | 
| 
      
 129 
     | 
    
         
            +
                    when 'rerun'
         
     | 
| 
      
 130 
     | 
    
         
            +
                      :create
         
     | 
| 
       97 
131 
     | 
    
         
             
                    else
         
     | 
| 
       98 
132 
     | 
    
         
             
                      super
         
     | 
| 
       99 
133 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -112,11 +146,20 @@ module Api 
     | 
|
| 
       112 
146 
     | 
    
         
             
                  end
         
     | 
| 
       113 
147 
     | 
    
         | 
| 
       114 
148 
     | 
    
         
             
                  def job_invocation_params
         
     | 
| 
      
 149 
     | 
    
         
            +
                    return @job_invocation_params if @job_invocation_params.present?
         
     | 
| 
       115 
150 
     | 
    
         
             
                    job_invocation_params = params.fetch(:job_invocation, {}).dup
         
     | 
| 
       116 
151 
     | 
    
         
             
                    job_invocation_params.merge!(job_invocation_params.delete(:ssh)) if job_invocation_params.key?(:ssh)
         
     | 
| 
       117 
152 
     | 
    
         
             
                    job_invocation_params[:inputs] ||= {}
         
     | 
| 
       118 
153 
     | 
    
         
             
                    job_invocation_params[:inputs].permit!
         
     | 
| 
       119 
     | 
    
         
            -
                    job_invocation_params
         
     | 
| 
      
 154 
     | 
    
         
            +
                    @job_invocation_params = job_invocation_params
         
     | 
| 
      
 155 
     | 
    
         
            +
                  end
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                  def composer_for_feature
         
     | 
| 
      
 158 
     | 
    
         
            +
                    JobInvocationComposer.for_feature(
         
     | 
| 
      
 159 
     | 
    
         
            +
                      job_invocation_params[:feature],
         
     | 
| 
      
 160 
     | 
    
         
            +
                      job_invocation_params[:host_ids],
         
     | 
| 
      
 161 
     | 
    
         
            +
                      job_invocation_params[:inputs].to_hash
         
     | 
| 
      
 162 
     | 
    
         
            +
                    )
         
     | 
| 
       120 
163 
     | 
    
         
             
                  end
         
     | 
| 
       121 
164 
     | 
    
         
             
                end
         
     | 
| 
       122 
165 
     | 
    
         
             
              end
         
     | 
| 
         @@ -3,6 +3,7 @@ class JobInvocationsController < ApplicationController 
     | 
|
| 
       3 
3 
     | 
    
         
             
              include ::ForemanTasks::Concerns::Parameters::Triggering
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
              def new
         
     | 
| 
      
 6 
     | 
    
         
            +
                return @composer = prepare_composer if params[:feature].present?
         
     | 
| 
       6 
7 
     | 
    
         
             
                ui_params = {
         
     | 
| 
       7 
8 
     | 
    
         
             
                  :host_ids => params[:host_ids],
         
     | 
| 
       8 
9 
     | 
    
         
             
                  :targeting => {
         
     | 
| 
         @@ -72,6 +73,26 @@ class JobInvocationsController < ApplicationController 
     | 
|
| 
       72 
73 
     | 
    
         
             
                render :partial => 'job_invocations/preview_hosts_list'
         
     | 
| 
       73 
74 
     | 
    
         
             
              end
         
     | 
| 
       74 
75 
     | 
    
         | 
| 
      
 76 
     | 
    
         
            +
              def cancel
         
     | 
| 
      
 77 
     | 
    
         
            +
                @job_invocation = resource_base.find(params[:id])
         
     | 
| 
      
 78 
     | 
    
         
            +
                result = @job_invocation.cancel(params[:force])
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                if result
         
     | 
| 
      
 81 
     | 
    
         
            +
                  flash[:notice] = if params[:force]
         
     | 
| 
      
 82 
     | 
    
         
            +
                                     _('Trying to abort the job')
         
     | 
| 
      
 83 
     | 
    
         
            +
                                   else
         
     | 
| 
      
 84 
     | 
    
         
            +
                                     _('Trying to cancel the job')
         
     | 
| 
      
 85 
     | 
    
         
            +
                                   end
         
     | 
| 
      
 86 
     | 
    
         
            +
                else
         
     | 
| 
      
 87 
     | 
    
         
            +
                  flash[:warning] = if params[:force]
         
     | 
| 
      
 88 
     | 
    
         
            +
                                      _('The job cannot be aborted at the moment.')
         
     | 
| 
      
 89 
     | 
    
         
            +
                                    else
         
     | 
| 
      
 90 
     | 
    
         
            +
                                      _('The job cannot be cancelled at the moment.')
         
     | 
| 
      
 91 
     | 
    
         
            +
                                    end
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
      
 93 
     | 
    
         
            +
                redirect_back(:fallback_location => job_invocation_path(@job_invocation))
         
     | 
| 
      
 94 
     | 
    
         
            +
              end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
       75 
96 
     | 
    
         
             
              private
         
     | 
| 
       76 
97 
     | 
    
         | 
| 
       77 
98 
     | 
    
         
             
              def action_permission
         
     | 
| 
         @@ -80,6 +101,8 @@ class JobInvocationsController < ApplicationController 
     | 
|
| 
       80 
101 
     | 
    
         
             
                    'create'
         
     | 
| 
       81 
102 
     | 
    
         
             
                  when 'preview_hosts'
         
     | 
| 
       82 
103 
     | 
    
         
             
                    'create'
         
     | 
| 
      
 104 
     | 
    
         
            +
                  when 'cancel'
         
     | 
| 
      
 105 
     | 
    
         
            +
                    'cancel'
         
     | 
| 
       83 
106 
     | 
    
         
             
                  else
         
     | 
| 
       84 
107 
     | 
    
         
             
                    super
         
     | 
| 
       85 
108 
     | 
    
         
             
                end
         
     | 
| 
         @@ -87,7 +110,12 @@ class JobInvocationsController < ApplicationController 
     | 
|
| 
       87 
110 
     | 
    
         | 
| 
       88 
111 
     | 
    
         
             
              def prepare_composer
         
     | 
| 
       89 
112 
     | 
    
         
             
                if params[:feature].present?
         
     | 
| 
       90 
     | 
    
         
            -
                   
     | 
| 
      
 113 
     | 
    
         
            +
                  inputs = params[:inputs].permit!.to_hash if params.include?(:inputs)
         
     | 
| 
      
 114 
     | 
    
         
            +
                  JobInvocationComposer.for_feature(
         
     | 
| 
      
 115 
     | 
    
         
            +
                    params[:feature],
         
     | 
| 
      
 116 
     | 
    
         
            +
                    params[:host_ids],
         
     | 
| 
      
 117 
     | 
    
         
            +
                    inputs
         
     | 
| 
      
 118 
     | 
    
         
            +
                  )
         
     | 
| 
       91 
119 
     | 
    
         
             
                else
         
     | 
| 
       92 
120 
     | 
    
         
             
                  # triggering_params is a Hash
         
     | 
| 
       93 
121 
     | 
    
         
             
                  #   when a hash is merged into ActionController::Parameters,
         
     | 
| 
         @@ -116,13 +116,13 @@ module RemoteExecutionHelper 
     | 
|
| 
       116 
116 
     | 
    
         
             
                                     :class => 'btn btn-default',
         
     | 
| 
       117 
117 
     | 
    
         
             
                                     :title => _('See the last task details'))
         
     | 
| 
       118 
118 
     | 
    
         
             
                end
         
     | 
| 
       119 
     | 
    
         
            -
                if authorized_for(:permission => : 
     | 
| 
       120 
     | 
    
         
            -
                  buttons << link_to(_('Cancel Job'),  
     | 
| 
      
 119 
     | 
    
         
            +
                if authorized_for(:permission => :cancel_job_invocations, :auth_object => job_invocation)
         
     | 
| 
      
 120 
     | 
    
         
            +
                  buttons << link_to(_('Cancel Job'), cancel_job_invocation_path(job_invocation),
         
     | 
| 
       121 
121 
     | 
    
         
             
                                     :class => 'btn btn-danger',
         
     | 
| 
       122 
122 
     | 
    
         
             
                                     :title => _('Try to cancel the job'),
         
     | 
| 
       123 
123 
     | 
    
         
             
                                     :disabled => !task.cancellable?,
         
     | 
| 
       124 
124 
     | 
    
         
             
                                     :method => :post)
         
     | 
| 
       125 
     | 
    
         
            -
                  buttons << link_to(_('Abort Job'),  
     | 
| 
      
 125 
     | 
    
         
            +
                  buttons << link_to(_('Abort Job'), cancel_job_invocation_path(job_invocation, :force => true),
         
     | 
| 
       126 
126 
     | 
    
         
             
                                     :class => 'btn btn-danger',
         
     | 
| 
       127 
127 
     | 
    
         
             
                                     :title => _('Try to abort the job without waiting for the results from the remote hosts'),
         
     | 
| 
       128 
128 
     | 
    
         
             
                                     :disabled => !task.cancellable?,
         
     | 
| 
         @@ -132,14 +132,14 @@ module RemoteExecutionHelper 
     | 
|
| 
       132 
132 
     | 
    
         
             
              end
         
     | 
| 
       133 
133 
     | 
    
         
             
              # rubocop:enable Metrics/AbcSize
         
     | 
| 
       134 
134 
     | 
    
         | 
| 
       135 
     | 
    
         
            -
              def template_invocation_task_buttons(task)
         
     | 
| 
      
 135 
     | 
    
         
            +
              def template_invocation_task_buttons(task, invocation)
         
     | 
| 
       136 
136 
     | 
    
         
             
                buttons = []
         
     | 
| 
       137 
137 
     | 
    
         
             
                if authorized_for(:permission => :view_foreman_tasks, :auth_object => task)
         
     | 
| 
       138 
138 
     | 
    
         
             
                  buttons << link_to(_('Task Details'), foreman_tasks_task_path(task),
         
     | 
| 
       139 
139 
     | 
    
         
             
                                     :class => 'btn btn-default',
         
     | 
| 
       140 
140 
     | 
    
         
             
                                     :title => _('See the task details'))
         
     | 
| 
       141 
141 
     | 
    
         
             
                end
         
     | 
| 
       142 
     | 
    
         
            -
                if authorized_for(:permission => : 
     | 
| 
      
 142 
     | 
    
         
            +
                if authorized_for(:permission => :cancel_job_invocations, :auth_object => invocation)
         
     | 
| 
       143 
143 
     | 
    
         
             
                  buttons << link_to(_('Cancel Job'), cancel_foreman_tasks_task_path(task),
         
     | 
| 
       144 
144 
     | 
    
         
             
                                     :class => 'btn btn-danger',
         
     | 
| 
       145 
145 
     | 
    
         
             
                                     :title => _('Try to cancel the job on a host'),
         
     | 
| 
         @@ -5,12 +5,12 @@ module Actions 
     | 
|
| 
       5 
5 
     | 
    
         
             
                  include ::Actions::Helpers::WithDelegatedAction
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
                  middleware.do_not_use Dynflow::Middleware::Common::Transaction
         
     | 
| 
      
 8 
     | 
    
         
            +
                  middleware.use Actions::Middleware::HideSecrets
         
     | 
| 
       8 
9 
     | 
    
         | 
| 
       9 
10 
     | 
    
         
             
                  def resource_locks
         
     | 
| 
       10 
11 
     | 
    
         
             
                    :link
         
     | 
| 
       11 
12 
     | 
    
         
             
                  end
         
     | 
| 
       12 
13 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
                  # rubocop:disable Metrics/AbcSize
         
     | 
| 
       14 
14 
     | 
    
         
             
                  def plan(job_invocation, host, template_invocation, proxy_selector = ::RemoteExecutionProxySelector.new, options = {})
         
     | 
| 
       15 
15 
     | 
    
         
             
                    action_subject(host, :job_category => job_invocation.job_category, :description => job_invocation.description)
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
         @@ -25,32 +25,25 @@ module Actions 
     | 
|
| 
       25 
25 
     | 
    
         | 
| 
       26 
26 
     | 
    
         
             
                    raise _('Could not use any template used in the job invocation') if template_invocation.blank?
         
     | 
| 
       27 
27 
     | 
    
         | 
| 
       28 
     | 
    
         
            -
                     
     | 
| 
       29 
     | 
    
         
            -
                    proxy =  
     | 
| 
       30 
     | 
    
         
            -
                    if proxy == :not_available
         
     | 
| 
       31 
     | 
    
         
            -
                      offline_proxies = proxy_selector.offline
         
     | 
| 
       32 
     | 
    
         
            -
                      settings = { :count => offline_proxies.count, :proxy_names => offline_proxies.map(&:name).join(', ') }
         
     | 
| 
       33 
     | 
    
         
            -
                      raise n_('The only applicable proxy %{proxy_names} is down',
         
     | 
| 
       34 
     | 
    
         
            -
                               'All %{count} applicable proxies are down. Tried %{proxy_names}',
         
     | 
| 
       35 
     | 
    
         
            -
                               offline_proxies.count) % settings
         
     | 
| 
       36 
     | 
    
         
            -
                    elsif proxy == :not_defined && !Setting['remote_execution_without_proxy']
         
     | 
| 
       37 
     | 
    
         
            -
                      settings = { :global_proxy => 'remote_execution_global_proxy',
         
     | 
| 
       38 
     | 
    
         
            -
                                   :fallback_proxy => 'remote_execution_fallback_proxy',
         
     | 
| 
       39 
     | 
    
         
            -
                                   :no_proxy => 'remote_execution_no_proxy' }
         
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
       41 
     | 
    
         
            -
                      raise _('Could not use any proxy. Consider configuring %{global_proxy}, ' +
         
     | 
| 
       42 
     | 
    
         
            -
                              '%{fallback_proxy} or %{no_proxy} in settings') % settings
         
     | 
| 
       43 
     | 
    
         
            -
                    end
         
     | 
| 
      
 28 
     | 
    
         
            +
                    provider_type = template_invocation.template.provider_type.to_s
         
     | 
| 
      
 29 
     | 
    
         
            +
                    proxy = determine_proxy!(proxy_selector, provider_type, host)
         
     | 
| 
       44 
30 
     | 
    
         | 
| 
       45 
31 
     | 
    
         
             
                    renderer = InputTemplateRenderer.new(template_invocation.template, host, template_invocation)
         
     | 
| 
       46 
32 
     | 
    
         
             
                    script = renderer.render
         
     | 
| 
       47 
33 
     | 
    
         
             
                    raise _('Failed rendering template: %s') % renderer.error_message unless script
         
     | 
| 
       48 
34 
     | 
    
         | 
| 
       49 
35 
     | 
    
         
             
                    provider = template_invocation.template.provider
         
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                    secrets = { :ssh_password => job_invocation.password || provider.ssh_password(host),
         
     | 
| 
      
 38 
     | 
    
         
            +
                                :key_passphrase => job_invocation.key_passphrase || provider.ssh_key_passphrase(host) }
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                    additional_options = { :hostname => provider.find_ip_or_hostname(host),
         
     | 
| 
      
 41 
     | 
    
         
            +
                                           :script => script,
         
     | 
| 
      
 42 
     | 
    
         
            +
                                           :execution_timeout_interval => job_invocation.execution_timeout_interval,
         
     | 
| 
      
 43 
     | 
    
         
            +
                                           :secrets => secrets }
         
     | 
| 
       51 
44 
     | 
    
         
             
                    action_options = provider.proxy_command_options(template_invocation, host)
         
     | 
| 
       52 
     | 
    
         
            -
                                             .merge( 
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
      
 45 
     | 
    
         
            +
                                             .merge(additional_options)
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
       54 
47 
     | 
    
         
             
                    plan_delegated_action(proxy, ForemanRemoteExecutionCore::Actions::RunScript, action_options)
         
     | 
| 
       55 
48 
     | 
    
         
             
                    plan_self
         
     | 
| 
       56 
49 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -144,6 +137,25 @@ module Actions 
     | 
|
| 
       144 
137 
     | 
    
         | 
| 
       145 
138 
     | 
    
         
             
                    true
         
     | 
| 
       146 
139 
     | 
    
         
             
                  end
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                  def determine_proxy!(proxy_selector, provider, host)
         
     | 
| 
      
 142 
     | 
    
         
            +
                    proxy = proxy_selector.determine_proxy(host, provider)
         
     | 
| 
      
 143 
     | 
    
         
            +
                    if proxy == :not_available
         
     | 
| 
      
 144 
     | 
    
         
            +
                      offline_proxies = proxy_selector.offline
         
     | 
| 
      
 145 
     | 
    
         
            +
                      settings = { :count => offline_proxies.count, :proxy_names => offline_proxies.map(&:name).join(', ') }
         
     | 
| 
      
 146 
     | 
    
         
            +
                      raise n_('The only applicable proxy %{proxy_names} is down',
         
     | 
| 
      
 147 
     | 
    
         
            +
                               'All %{count} applicable proxies are down. Tried %{proxy_names}',
         
     | 
| 
      
 148 
     | 
    
         
            +
                               offline_proxies.count) % settings
         
     | 
| 
      
 149 
     | 
    
         
            +
                    elsif proxy == :not_defined && !Setting['remote_execution_without_proxy']
         
     | 
| 
      
 150 
     | 
    
         
            +
                      settings = { :global_proxy => 'remote_execution_global_proxy',
         
     | 
| 
      
 151 
     | 
    
         
            +
                                   :fallback_proxy => 'remote_execution_fallback_proxy',
         
     | 
| 
      
 152 
     | 
    
         
            +
                                   :no_proxy => 'remote_execution_no_proxy' }
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
                      raise _('Could not use any proxy. Consider configuring %{global_proxy}, ' +
         
     | 
| 
      
 155 
     | 
    
         
            +
                              '%{fallback_proxy} or %{no_proxy} in settings') % settings
         
     | 
| 
      
 156 
     | 
    
         
            +
                    end
         
     | 
| 
      
 157 
     | 
    
         
            +
                    proxy
         
     | 
| 
      
 158 
     | 
    
         
            +
                  end
         
     | 
| 
       147 
159 
     | 
    
         
             
                end
         
     | 
| 
       148 
160 
     | 
    
         
             
              end
         
     | 
| 
       149 
161 
     | 
    
         
             
            end
         
     | 
| 
         @@ -26,7 +26,6 @@ module Actions 
     | 
|
| 
       26 
26 
     | 
    
         
             
                  end
         
     | 
| 
       27 
27 
     | 
    
         | 
| 
       28 
28 
     | 
    
         
             
                  def create_sub_plans
         
     | 
| 
       29 
     | 
    
         
            -
                    job_invocation = JobInvocation.find(input[:job_invocation_id])
         
     | 
| 
       30 
29 
     | 
    
         
             
                    proxy_selector = RemoteExecutionProxySelector.new
         
     | 
| 
       31 
30 
     | 
    
         | 
| 
       32 
31 
     | 
    
         
             
                    current_batch.map do |host|
         
     | 
| 
         @@ -38,6 +37,18 @@ module Actions 
     | 
|
| 
       38 
37 
     | 
    
         
             
                    end
         
     | 
| 
       39 
38 
     | 
    
         
             
                  end
         
     | 
| 
       40 
39 
     | 
    
         | 
| 
      
 40 
     | 
    
         
            +
                  def finalize
         
     | 
| 
      
 41 
     | 
    
         
            +
                    job_invocation.password = job_invocation.key_passphrase = nil
         
     | 
| 
      
 42 
     | 
    
         
            +
                    job_invocation.save!
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                    # creating the success notification should be the very last thing this tasks do
         
     | 
| 
      
 45 
     | 
    
         
            +
                    job_invocation.build_notification.deliver!
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  def job_invocation
         
     | 
| 
      
 49 
     | 
    
         
            +
                    @job_invocation ||= JobInvocation.find(input[:job_invocation_id])
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
       41 
52 
     | 
    
         
             
                  def batch(from, size)
         
     | 
| 
       42 
53 
     | 
    
         
             
                    hosts.offset(from).limit(size)
         
     | 
| 
       43 
54 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -47,7 +58,7 @@ module Actions 
     | 
|
| 
       47 
58 
     | 
    
         
             
                  end
         
     | 
| 
       48 
59 
     | 
    
         | 
| 
       49 
60 
     | 
    
         
             
                  def hosts
         
     | 
| 
       50 
     | 
    
         
            -
                     
     | 
| 
      
 61 
     | 
    
         
            +
                    job_invocation.targeting.hosts.order(:name, :id)
         
     | 
| 
       51 
62 
     | 
    
         
             
                  end
         
     | 
| 
       52 
63 
     | 
    
         | 
| 
       53 
64 
     | 
    
         
             
                  def set_up_concurrency_control(invocation)
         
     | 
| 
         @@ -66,12 +77,6 @@ module Actions 
     | 
|
| 
       66 
77 
     | 
    
         
             
                    super unless event == Dynflow::Action::Skip
         
     | 
| 
       67 
78 
     | 
    
         
             
                  end
         
     | 
| 
       68 
79 
     | 
    
         | 
| 
       69 
     | 
    
         
            -
                  def finalize
         
     | 
| 
       70 
     | 
    
         
            -
                    # creating the success notification should be the very last thing this tasks do
         
     | 
| 
       71 
     | 
    
         
            -
                    job_invocation = JobInvocation.find(input[:job_invocation_id])
         
     | 
| 
       72 
     | 
    
         
            -
                    job_invocation.build_notification.deliver!
         
     | 
| 
       73 
     | 
    
         
            -
                  end
         
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
80 
     | 
    
         
             
                  def humanized_input
         
     | 
| 
       76 
81 
     | 
    
         
             
                    input.fetch(:job_invocation, {}).fetch(:description, '')
         
     | 
| 
       77 
82 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -1,5 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            class JobInvocation < ApplicationRecord
         
     | 
| 
       2 
2 
     | 
    
         
             
              include Authorizable
         
     | 
| 
      
 3 
     | 
    
         
            +
              include Encryptable
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
       3 
5 
     | 
    
         
             
              audited :except => [ :task_id, :targeting_id, :task_group_id, :triggering_id ]
         
     | 
| 
       4 
6 
     | 
    
         | 
| 
       5 
7 
     | 
    
         
             
              include ForemanRemoteExecution::ErrorsFlattener
         
     | 
| 
         @@ -47,6 +49,8 @@ class JobInvocation < ApplicationRecord 
     | 
|
| 
       47 
49 
     | 
    
         
             
              belongs_to :triggering, :class_name => 'ForemanTasks::Triggering'
         
     | 
| 
       48 
50 
     | 
    
         
             
              has_one :recurring_logic, :through => :triggering, :class_name => 'ForemanTasks::RecurringLogic'
         
     | 
| 
       49 
51 
     | 
    
         | 
| 
      
 52 
     | 
    
         
            +
              belongs_to :remote_execution_feature
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
       50 
54 
     | 
    
         
             
              scope :with_task, -> { references(:task) }
         
     | 
| 
       51 
55 
     | 
    
         | 
| 
       52 
56 
     | 
    
         
             
              scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring_logic.id'
         
     | 
| 
         @@ -64,6 +68,8 @@ class JobInvocation < ApplicationRecord 
     | 
|
| 
       64 
68 
     | 
    
         | 
| 
       65 
69 
     | 
    
         
             
              delegate :start_at, :to => :task, :allow_nil => true
         
     | 
| 
       66 
70 
     | 
    
         | 
| 
      
 71 
     | 
    
         
            +
              encrypts :password, :key_passphrase
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
       67 
73 
     | 
    
         
             
              def self.search_by_status(key, operator, value)
         
     | 
| 
       68 
74 
     | 
    
         
             
                conditions = HostStatus::ExecutionStatus::ExecutionTaskStatusMapper.sql_conditions_for(value)
         
     | 
| 
       69 
75 
     | 
    
         
             
                conditions[0] = "NOT (#{conditions[0]})" if operator == '<>'
         
     | 
| 
         @@ -83,7 +89,16 @@ class JobInvocation < ApplicationRecord 
     | 
|
| 
       83 
89 
     | 
    
         
             
              end
         
     | 
| 
       84 
90 
     | 
    
         | 
| 
       85 
91 
     | 
    
         
             
              def build_notification
         
     | 
| 
       86 
     | 
    
         
            -
                 
     | 
| 
      
 92 
     | 
    
         
            +
                klass = nil
         
     | 
| 
      
 93 
     | 
    
         
            +
                if self.remote_execution_feature && self.remote_execution_feature.notification_builder.present?
         
     | 
| 
      
 94 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 95 
     | 
    
         
            +
                    klass = remote_execution_feature.notification_builder.constantize
         
     | 
| 
      
 96 
     | 
    
         
            +
                  rescue NameError => e
         
     | 
| 
      
 97 
     | 
    
         
            +
                    logger.exception "REX feature defines unknown notification builder class", e
         
     | 
| 
      
 98 
     | 
    
         
            +
                  end
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
                klass ||= UINotifications::RemoteExecutionJobs::BaseJobFinish
         
     | 
| 
      
 101 
     | 
    
         
            +
                klass.new(self)
         
     | 
| 
       87 
102 
     | 
    
         
             
              end
         
     | 
| 
       88 
103 
     | 
    
         | 
| 
       89 
104 
     | 
    
         
             
              def status
         
     | 
| 
         @@ -120,6 +135,8 @@ class JobInvocation < ApplicationRecord 
     | 
|
| 
       120 
135 
     | 
    
         
             
                  invocation.description_format = self.description_format
         
     | 
| 
       121 
136 
     | 
    
         
             
                  invocation.description = self.description
         
     | 
| 
       122 
137 
     | 
    
         
             
                  invocation.pattern_template_invocations = self.pattern_template_invocations.map(&:deep_clone)
         
     | 
| 
      
 138 
     | 
    
         
            +
                  invocation.password = self.password
         
     | 
| 
      
 139 
     | 
    
         
            +
                  invocation.key_passphrase = self.key_passphrase
         
     | 
| 
       123 
140 
     | 
    
         
             
                end
         
     | 
| 
       124 
141 
     | 
    
         
             
              end
         
     | 
| 
       125 
142 
     | 
    
         | 
| 
         @@ -208,6 +225,11 @@ class JobInvocation < ApplicationRecord 
     | 
|
| 
       208 
225 
     | 
    
         
             
                end
         
     | 
| 
       209 
226 
     | 
    
         
             
              end
         
     | 
| 
       210 
227 
     | 
    
         | 
| 
      
 228 
     | 
    
         
            +
              def cancel(force = false)
         
     | 
| 
      
 229 
     | 
    
         
            +
                method = force ? :abort : :cancel
         
     | 
| 
      
 230 
     | 
    
         
            +
                task.send(method)
         
     | 
| 
      
 231 
     | 
    
         
            +
              end
         
     | 
| 
      
 232 
     | 
    
         
            +
             
     | 
| 
       211 
233 
     | 
    
         
             
              private
         
     | 
| 
       212 
234 
     | 
    
         | 
| 
       213 
235 
     | 
    
         
             
              def failed_template_invocations
         
     | 
| 
         @@ -11,7 +11,10 @@ class JobInvocationComposer 
     | 
|
| 
       11 
11 
     | 
    
         
             
                    :targeting => ui_params.fetch(:targeting, {}).merge(:user_id => User.current.id),
         
     | 
| 
       12 
12 
     | 
    
         
             
                    :triggering => triggering,
         
     | 
| 
       13 
13 
     | 
    
         
             
                    :host_ids => ui_params[:host_ids],
         
     | 
| 
      
 14 
     | 
    
         
            +
                    :remote_execution_feature_id => ui_params[:remote_execution_feature_id],
         
     | 
| 
       14 
15 
     | 
    
         
             
                    :description_format => job_invocation_base[:description_format],
         
     | 
| 
      
 16 
     | 
    
         
            +
                    :password => blank_to_nil(job_invocation_base[:password]),
         
     | 
| 
      
 17 
     | 
    
         
            +
                    :key_passphrase => blank_to_nil(job_invocation_base[:key_passphrase]),
         
     | 
| 
       15 
18 
     | 
    
         
             
                    :concurrency_control => concurrency_control_params,
         
     | 
| 
       16 
19 
     | 
    
         
             
                    :execution_timeout_interval => execution_timeout_interval,
         
     | 
| 
       17 
20 
     | 
    
         
             
                    :template_invocations => template_invocations_params }.with_indifferent_access
         
     | 
| 
         @@ -32,6 +35,10 @@ class JobInvocationComposer 
     | 
|
| 
       32 
35 
     | 
    
         
             
                  end.first
         
     | 
| 
       33 
36 
     | 
    
         
             
                end
         
     | 
| 
       34 
37 
     | 
    
         | 
| 
      
 38 
     | 
    
         
            +
                def blank_to_nil(thing)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  thing.blank? ? nil : thing
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
       35 
42 
     | 
    
         
             
                # TODO: Fix this comment
         
     | 
| 
       36 
43 
     | 
    
         
             
                # parses params to get job templates in form of id => attributes for selected job templates, e.g.
         
     | 
| 
       37 
44 
     | 
    
         
             
                # {
         
     | 
| 
         @@ -88,6 +95,7 @@ class JobInvocationComposer 
     | 
|
| 
       88 
95 
     | 
    
         
             
                    :targeting => targeting_params,
         
     | 
| 
       89 
96 
     | 
    
         
             
                    :triggering => triggering_params,
         
     | 
| 
       90 
97 
     | 
    
         
             
                    :description_format => api_params[:description_format],
         
     | 
| 
      
 98 
     | 
    
         
            +
                    :remote_execution_feature_id => api_params[:remote_execution_feature_id],
         
     | 
| 
       91 
99 
     | 
    
         
             
                    :concurrency_control => concurrency_control_params,
         
     | 
| 
       92 
100 
     | 
    
         
             
                    :execution_timeout_interval => api_params[:execution_timeout_interval] || template.execution_timeout_interval,
         
     | 
| 
       93 
101 
     | 
    
         
             
                    :template_invocations => template_invocations_params }.with_indifferent_access
         
     | 
| 
         @@ -172,6 +180,7 @@ class JobInvocationComposer 
     | 
|
| 
       172 
180 
     | 
    
         
             
                    :description_format => job_invocation.description_format,
         
     | 
| 
       173 
181 
     | 
    
         
             
                    :concurrency_control => concurrency_control_params,
         
     | 
| 
       174 
182 
     | 
    
         
             
                    :execution_timeout_interval => job_invocation.execution_timeout_interval,
         
     | 
| 
      
 183 
     | 
    
         
            +
                    :remote_execution_feature_id => job_invocation.remote_execution_feature_id,
         
     | 
| 
       175 
184 
     | 
    
         
             
                    :template_invocations => template_invocations_params }.with_indifferent_access
         
     | 
| 
       176 
185 
     | 
    
         
             
                end
         
     | 
| 
       177 
186 
     | 
    
         | 
| 
         @@ -185,10 +194,12 @@ class JobInvocationComposer 
     | 
|
| 
       185 
194 
     | 
    
         
             
                end
         
     | 
| 
       186 
195 
     | 
    
         | 
| 
       187 
196 
     | 
    
         
             
                def targeting_params
         
     | 
| 
      
 197 
     | 
    
         
            +
                  base = { :user_id => User.current.id }
         
     | 
| 
       188 
198 
     | 
    
         
             
                  if @host_ids
         
     | 
| 
       189 
     | 
    
         
            -
                     
     | 
| 
      
 199 
     | 
    
         
            +
                    search_query = @host_ids.empty? ? 'name ^ ()' : Targeting.build_query_from_hosts(@host_ids)
         
     | 
| 
      
 200 
     | 
    
         
            +
                    base.merge(:search_query => search_query, :targeting_type => job_invocation.targeting.targeting_type)
         
     | 
| 
       190 
201 
     | 
    
         
             
                  else
         
     | 
| 
       191 
     | 
    
         
            -
                    job_invocation.targeting.attributes.slice('search_query', 'bookmark_id', ' 
     | 
| 
      
 202 
     | 
    
         
            +
                    base.merge job_invocation.targeting.attributes.slice('search_query', 'bookmark_id', 'targeting_type')
         
     | 
| 
       192 
203 
     | 
    
         
             
                  end
         
     | 
| 
       193 
204 
     | 
    
         
             
                end
         
     | 
| 
       194 
205 
     | 
    
         | 
| 
         @@ -215,6 +226,10 @@ class JobInvocationComposer 
     | 
|
| 
       215 
226 
     | 
    
         
             
                    @host_bookmark = hosts
         
     | 
| 
       216 
227 
     | 
    
         
             
                  elsif hosts.is_a? Host::Base
         
     | 
| 
       217 
228 
     | 
    
         
             
                    @host_objects = [hosts]
         
     | 
| 
      
 229 
     | 
    
         
            +
                  elsif hosts.is_a? Array
         
     | 
| 
      
 230 
     | 
    
         
            +
                    @host_objects = hosts.map do |id|
         
     | 
| 
      
 231 
     | 
    
         
            +
                      Host::Managed.authorized.friendly.find(id)
         
     | 
| 
      
 232 
     | 
    
         
            +
                    end
         
     | 
| 
       218 
233 
     | 
    
         
             
                  elsif hosts.is_a? String
         
     | 
| 
       219 
234 
     | 
    
         
             
                    @host_scoped_search = hosts
         
     | 
| 
       220 
235 
     | 
    
         
             
                  else
         
     | 
| 
         @@ -227,6 +242,7 @@ class JobInvocationComposer 
     | 
|
| 
       227 
242 
     | 
    
         
             
                    :targeting => targeting_params,
         
     | 
| 
       228 
243 
     | 
    
         
             
                    :triggering => {},
         
     | 
| 
       229 
244 
     | 
    
         
             
                    :concurrency_control => {},
         
     | 
| 
      
 245 
     | 
    
         
            +
                    :remote_execution_feature_id => @feature.id,
         
     | 
| 
       230 
246 
     | 
    
         
             
                    :template_invocations => template_invocations_params }.with_indifferent_access
         
     | 
| 
       231 
247 
     | 
    
         
             
                end
         
     | 
| 
       232 
248 
     | 
    
         | 
| 
         @@ -248,6 +264,7 @@ class JobInvocationComposer 
     | 
|
| 
       248 
264 
     | 
    
         
             
                end
         
     | 
| 
       249 
265 
     | 
    
         | 
| 
       250 
266 
     | 
    
         
             
                def input_values_params
         
     | 
| 
      
 267 
     | 
    
         
            +
                  return {} if @provided_inputs.blank?
         
     | 
| 
       251 
268 
     | 
    
         
             
                  @provided_inputs.map do |key, value|
         
     | 
| 
       252 
269 
     | 
    
         
             
                    input = job_template.template_inputs_with_foreign.find { |i| i.name == key.to_s }
         
     | 
| 
       253 
270 
     | 
    
         
             
                    unless input
         
     | 
| 
         @@ -275,7 +292,7 @@ class JobInvocationComposer 
     | 
|
| 
       275 
292 
     | 
    
         
             
              end
         
     | 
| 
       276 
293 
     | 
    
         | 
| 
       277 
294 
     | 
    
         
             
              attr_accessor :params, :job_invocation, :host_ids, :search_query
         
     | 
| 
       278 
     | 
    
         
            -
              delegate :job_category, :pattern_template_invocations, :template_invocations, :targeting, :triggering, :to => :job_invocation
         
     | 
| 
      
 295 
     | 
    
         
            +
              delegate :job_category, :remote_execution_feature_id, :pattern_template_invocations, :template_invocations, :targeting, :triggering, :to => :job_invocation
         
     | 
| 
       279 
296 
     | 
    
         | 
| 
       280 
297 
     | 
    
         
             
              def initialize(params, set_defaults = false)
         
     | 
| 
       281 
298 
     | 
    
         
             
                @params = params
         
     | 
| 
         @@ -304,9 +321,11 @@ class JobInvocationComposer 
     | 
|
| 
       304 
321 
     | 
    
         
             
                self.new(ParamsForFeature.new(feature_label, hosts, provided_inputs).params)
         
     | 
| 
       305 
322 
     | 
    
         
             
              end
         
     | 
| 
       306 
323 
     | 
    
         | 
| 
      
 324 
     | 
    
         
            +
              # rubocop:disable Metrics/AbcSize
         
     | 
| 
       307 
325 
     | 
    
         
             
              def compose
         
     | 
| 
       308 
326 
     | 
    
         
             
                job_invocation.job_category = validate_job_category(params[:job_category])
         
     | 
| 
       309 
327 
     | 
    
         
             
                job_invocation.job_category ||= available_job_categories.first if @set_defaults
         
     | 
| 
      
 328 
     | 
    
         
            +
                job_invocation.remote_execution_feature_id = params[:remote_execution_feature_id]
         
     | 
| 
       310 
329 
     | 
    
         
             
                job_invocation.targeting = build_targeting
         
     | 
| 
       311 
330 
     | 
    
         
             
                job_invocation.triggering = build_triggering
         
     | 
| 
       312 
331 
     | 
    
         
             
                job_invocation.pattern_template_invocations = build_template_invocations
         
     | 
| 
         @@ -314,9 +333,12 @@ class JobInvocationComposer 
     | 
|
| 
       314 
333 
     | 
    
         
             
                job_invocation.time_span = params[:concurrency_control][:time_span].to_i if params[:concurrency_control][:time_span].present?
         
     | 
| 
       315 
334 
     | 
    
         
             
                job_invocation.concurrency_level = params[:concurrency_control][:level].to_i if params[:concurrency_control][:level].present?
         
     | 
| 
       316 
335 
     | 
    
         
             
                job_invocation.execution_timeout_interval = params[:execution_timeout_interval]
         
     | 
| 
      
 336 
     | 
    
         
            +
                job_invocation.password = params[:password]
         
     | 
| 
      
 337 
     | 
    
         
            +
                job_invocation.key_passphrase = params[:key_passphrase]
         
     | 
| 
       317 
338 
     | 
    
         | 
| 
       318 
339 
     | 
    
         
             
                self
         
     | 
| 
       319 
340 
     | 
    
         
             
              end
         
     | 
| 
      
 341 
     | 
    
         
            +
              # rubocop:enable Metrics/AbcSize
         
     | 
| 
       320 
342 
     | 
    
         | 
| 
       321 
343 
     | 
    
         
             
              def trigger(raise_on_error = false)
         
     | 
| 
       322 
344 
     | 
    
         
             
                generate_description
         
     |