foreman_remote_execution 0.3.0 → 0.3.1
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/.rubocop.yml +3 -5
- data/.rubocop_todo.yml +90 -92
- data/app/assets/javascripts/job_templates.js +10 -0
- data/app/assets/stylesheets/modal_window.css.scss +4 -0
- data/app/controllers/api/v2/job_templates_controller.rb +22 -1
- data/app/controllers/job_templates_controller.rb +21 -1
- data/app/helpers/concerns/foreman_remote_execution/job_templates_extensions.rb +3 -2
- data/app/helpers/remote_execution_helper.rb +5 -3
- data/app/lib/actions/middleware/bind_job_invocation.rb +1 -1
- data/app/lib/actions/remote_execution/run_host_job.rb +15 -8
- data/app/lib/actions/remote_execution/run_hosts_job.rb +2 -1
- data/app/models/concerns/foreman_remote_execution/exportable.rb +71 -0
- data/app/models/foreign_input_set.rb +3 -0
- data/app/models/job_invocation.rb +6 -4
- data/app/models/job_invocation_composer.rb +1 -1
- data/app/models/job_template.rb +22 -6
- data/app/models/template_input.rb +4 -0
- data/app/models/template_invocation.rb +1 -1
- data/app/services/proxy_load_balancer.rb +3 -0
- data/app/views/job_invocations/_preview_hosts_modal.html.erb +3 -1
- data/app/views/job_templates/_import_job_template_modal.html.erb +20 -0
- data/app/views/job_templates/index.html.erb +5 -1
- data/config/routes.rb +4 -0
- data/lib/foreman_remote_execution/engine.rb +4 -4
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/locale/Makefile +18 -19
- data/locale/action_names.rb +2 -2
- data/locale/de/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/de/foreman_remote_execution.po +823 -0
- data/locale/en/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/en/foreman_remote_execution.po +332 -283
- data/locale/en_GB/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/en_GB/foreman_remote_execution.po +824 -0
- data/locale/foreman_remote_execution.pot +277 -131
- data/locale/fr/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/fr/foreman_remote_execution.po +823 -0
- data/test/functional/api/v2/job_templates_controller_test.rb +15 -0
- data/test/unit/actions/run_host_job_test.rb +50 -0
- data/test/unit/concerns/exportable_test.rb +88 -0
- data/test/unit/job_invocation_composer_test.rb +1 -1
- data/test/unit/job_invocation_test.rb +3 -6
- data/test/unit/job_template_test.rb +34 -2
- data/test/unit/proxy_load_balancer_test.rb +8 -6
- data/test/unit/template_input_test.rb +15 -0
- metadata +17 -2
| @@ -69,6 +69,21 @@ module Api | |
| 69 69 | 
             
                                 :job_template => {:name => ''}
         | 
| 70 70 | 
             
                    assert_response :unprocessable_entity
         | 
| 71 71 | 
             
                  end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                  test 'should export template' do
         | 
| 74 | 
            +
                    get :export, :id => @template.to_param
         | 
| 75 | 
            +
                    assert_equal @response.body, @template.to_erb
         | 
| 76 | 
            +
                    assert_response :success
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  test 'should import template' do
         | 
| 80 | 
            +
                    new_name = @template.name = "#{@template.name}_renamed"
         | 
| 81 | 
            +
                    erb_data = @template.to_erb
         | 
| 82 | 
            +
                    post :import, :template => erb_data
         | 
| 83 | 
            +
                    assert_response :success
         | 
| 84 | 
            +
                    assert JobTemplate.find_by_name(new_name)
         | 
| 85 | 
            +
                  end
         | 
| 72 86 | 
             
                end
         | 
| 87 | 
            +
             | 
| 73 88 | 
             
              end
         | 
| 74 89 | 
             
            end
         | 
| @@ -0,0 +1,50 @@ | |
| 1 | 
            +
            require 'test_plugin_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module ForemanRemoteExecution
         | 
| 4 | 
            +
              class RunHostJobTest < ActiveSupport::TestCase
         | 
| 5 | 
            +
                include Dynflow::Testing
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                let(:action) do
         | 
| 8 | 
            +
                  create_action(Actions::RemoteExecution::RunHostJob)
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                describe '#find_ip_or_hostname' do
         | 
| 12 | 
            +
                  let(:host) { FactoryGirl.create(:host) }
         | 
| 13 | 
            +
                  let(:flags) do
         | 
| 14 | 
            +
                    { :primary => false, :provision => false, :execution => false }
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  it 'finds hostname' do
         | 
| 18 | 
            +
                    host.expects(:fqdn).returns('somehost.somedomain.org')
         | 
| 19 | 
            +
                    action.send(:find_ip_or_hostname, host).must_equal 'somehost.somedomain.org'
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  it 'gets ip from unflagged interfaces' do
         | 
| 23 | 
            +
                    ip = '127.0.0.1'
         | 
| 24 | 
            +
                    host.interfaces = [FactoryGirl.build(:nic_managed, flags.merge(:ip => nil)),
         | 
| 25 | 
            +
                                       FactoryGirl.build(:nic_managed, flags.merge(:ip => ip))]
         | 
| 26 | 
            +
                    action.send(:find_ip_or_hostname, host).must_equal ip
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  it 'gets ip from flagged interfaces' do
         | 
| 30 | 
            +
                    unflagged_interface = FactoryGirl.build(:nic_managed, flags)
         | 
| 31 | 
            +
                    host.interfaces = [unflagged_interface]
         | 
| 32 | 
            +
                    action.send(:find_ip_or_hostname, host).must_equal unflagged_interface.ip
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    provision_interface = FactoryGirl.build(:nic_managed, flags.merge(:provision => true))
         | 
| 35 | 
            +
                    host.interfaces << provision_interface
         | 
| 36 | 
            +
                    action.send(:find_ip_or_hostname, host).must_equal provision_interface.ip
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    primary_interface = FactoryGirl.build(:nic_managed, flags.merge(:primary => true))
         | 
| 39 | 
            +
                    host.interfaces << primary_interface
         | 
| 40 | 
            +
                    # Workaround, execution flag got enabled when adding the interface to the host
         | 
| 41 | 
            +
                    host.interfaces.last.execution = false
         | 
| 42 | 
            +
                    action.send(:find_ip_or_hostname, host).must_equal primary_interface.ip
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    execution_interface = FactoryGirl.build(:nic_managed, flags.merge(:execution => true))
         | 
| 45 | 
            +
                    host.interfaces << execution_interface
         | 
| 46 | 
            +
                    action.send(:find_ip_or_hostname, host).must_equal execution_interface.ip
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
            end
         | 
| @@ -0,0 +1,88 @@ | |
| 1 | 
            +
            require 'test_plugin_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module ForemanRemoteExecution
         | 
| 4 | 
            +
              class ExportableTest < ActiveSupport::TestCase
         | 
| 5 | 
            +
                class SampleModel
         | 
| 6 | 
            +
                  include ::ForemanRemoteExecution::Exportable
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  attr_accessor :name, :attrs, :subnet, :mac, :password, :subnet
         | 
| 9 | 
            +
                  attr_exportable :name, :attrs, :mac, :subnet, :mac => ->(m) { m.mac.upcase if m.mac },
         | 
| 10 | 
            +
                                                                :custom_attr => ->(m) { 'hello world' }
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def attributes
         | 
| 13 | 
            +
                    {
         | 
| 14 | 
            +
                      'name' => name,
         | 
| 15 | 
            +
                      'attrs' => attrs,
         | 
| 16 | 
            +
                      'mac' => mac,
         | 
| 17 | 
            +
                      'password' => password,
         | 
| 18 | 
            +
                      'subnet' => subnet
         | 
| 19 | 
            +
                    }
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                class SampleSubnet
         | 
| 24 | 
            +
                  include ::ForemanRemoteExecution::Exportable
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  attr_accessor :name, :network
         | 
| 27 | 
            +
                  attr_exportable :name, :network
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  def attributes
         | 
| 30 | 
            +
                    {
         | 
| 31 | 
            +
                      'name' => name,
         | 
| 32 | 
            +
                      'network' => network
         | 
| 33 | 
            +
                    }
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def setup
         | 
| 38 | 
            +
                  @subnet = SampleSubnet.new
         | 
| 39 | 
            +
                  @subnet.name = 'pxe'
         | 
| 40 | 
            +
                  @subnet.network = '192.168.122.0'
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  @sample = SampleModel.new
         | 
| 43 | 
            +
                  @sample.name = 'name'
         | 
| 44 | 
            +
                  @sample.attrs = {'nested' => 'hash'}
         | 
| 45 | 
            +
                  @sample.subnet = @subnet
         | 
| 46 | 
            +
                  @sample.mac = 'aa:bb:cc:dd:ee:ff'
         | 
| 47 | 
            +
                  @sample.password = 'password'
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                test '#to_export includes all specified attributes' do
         | 
| 51 | 
            +
                  assert_equal %w(name attrs mac subnet custom_attr), @sample.to_export.keys
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                test '#to_export does not include all attributes' do
         | 
| 55 | 
            +
                  assert_not_include @sample.to_export.keys, 'password'
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                test '#to_export calls the lambda' do
         | 
| 59 | 
            +
                  export = @sample.to_export
         | 
| 60 | 
            +
                  assert_equal('AA:BB:CC:DD:EE:FF', export['mac'])
         | 
| 61 | 
            +
                  assert_equal(export['custom_attr'], 'hello world')
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                test '#to_export values are exported recursively' do
         | 
| 65 | 
            +
                  export = @sample.to_export
         | 
| 66 | 
            +
                  assert_equal('pxe', export['subnet']['name'])
         | 
| 67 | 
            +
                  assert_equal('192.168.122.0', export['subnet']['network'])
         | 
| 68 | 
            +
                end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                test '#to_export nested hashes are primitive' do
         | 
| 71 | 
            +
                  @sample.attrs = {:foo => 'bar', :baz => 'qux'}.with_indifferent_access
         | 
| 72 | 
            +
                  export = @sample.to_export
         | 
| 73 | 
            +
                  assert_instance_of Hash, export['attrs']
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                test '#to_export includes blank values' do
         | 
| 77 | 
            +
                  @sample.attrs = {}
         | 
| 78 | 
            +
                  export = @sample.to_export
         | 
| 79 | 
            +
                  assert_instance_of Hash, export['attrs']
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                test '#to_export(false) does not include blank values' do
         | 
| 83 | 
            +
                  @sample.attrs = {}
         | 
| 84 | 
            +
                  export = @sample.to_export(false)
         | 
| 85 | 
            +
                  assert_nil export['attrs']
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
              end
         | 
| 88 | 
            +
            end
         | 
| @@ -433,7 +433,7 @@ describe JobInvocationComposer do | |
| 433 433 |  | 
| 434 434 | 
             
                    describe 'with concurrency control set' do
         | 
| 435 435 | 
             
                      let(:params) do
         | 
| 436 | 
            -
                        { :job_invocation => { :providers => { :ssh => ssh_params }, :concurrency_level =>  | 
| 436 | 
            +
                        { :job_invocation => { :providers => { :ssh => ssh_params }, :concurrency_level => '5', :time_span => '60' } }.with_indifferent_access
         | 
| 437 437 | 
             
                      end
         | 
| 438 438 |  | 
| 439 439 | 
             
                      it 'accepts the concurrency options' do
         | 
| @@ -63,16 +63,14 @@ describe JobInvocation do | |
| 63 63 |  | 
| 64 64 | 
             
                describe 'descriptions' do
         | 
| 65 65 | 
             
                  it 'generates description from input values' do
         | 
| 66 | 
            -
                    job_invocation.expects(:save!)
         | 
| 67 66 | 
             
                    job_invocation.description_format = '%{job_category} - %{foo}'
         | 
| 68 | 
            -
                    job_invocation.generate_description | 
| 67 | 
            +
                    job_invocation.generate_description
         | 
| 69 68 | 
             
                    job_invocation.description.must_equal "#{job_invocation.job_category} - #{@input_value.value}"
         | 
| 70 69 | 
             
                  end
         | 
| 71 70 |  | 
| 72 71 | 
             
                  it 'handles missing keys correctly' do
         | 
| 73 | 
            -
                    job_invocation.expects(:save!)
         | 
| 74 72 | 
             
                    job_invocation.description_format = '%{job_category} - %{missing_key}'
         | 
| 75 | 
            -
                    job_invocation.generate_description | 
| 73 | 
            +
                    job_invocation.generate_description
         | 
| 76 74 | 
             
                    job_invocation.description.must_equal "#{job_invocation.job_category} - %{missing_key}"
         | 
| 77 75 | 
             
                  end
         | 
| 78 76 |  | 
| @@ -80,10 +78,9 @@ describe JobInvocation do | |
| 80 78 | 
             
                    column_limit = 255
         | 
| 81 79 | 
             
                    expected_result = 'a' * column_limit
         | 
| 82 80 | 
             
                    JobInvocation.columns_hash['description'].expects(:limit).returns(column_limit)
         | 
| 83 | 
            -
                    job_invocation.expects(:save!)
         | 
| 84 81 | 
             
                    job_invocation.description_format = '%{job_category}'
         | 
| 85 82 | 
             
                    job_invocation.job_category = 'a' * 1000
         | 
| 86 | 
            -
                    job_invocation.generate_description | 
| 83 | 
            +
                    job_invocation.generate_description
         | 
| 87 84 | 
             
                    job_invocation.description.must_equal expected_result
         | 
| 88 85 | 
             
                  end
         | 
| 89 86 | 
             
                end
         | 
| @@ -217,12 +217,44 @@ describe JobTemplate do | |
| 217 217 | 
             
                end
         | 
| 218 218 |  | 
| 219 219 | 
             
                it 'syncs content' do
         | 
| 220 | 
            -
                  synced_template.template.must_match( | 
| 220 | 
            +
                  synced_template.template.must_match(/ping -c <%= input\('count'\) %> <%= input\('hostname'\) %>/m)
         | 
| 221 221 | 
             
                end
         | 
| 222 222 |  | 
| 223 223 | 
             
                it 'syncs input sets' do
         | 
| 224 224 | 
             
                  synced_template.foreign_input_sets.first.target_template.must_equal included
         | 
| 225 | 
            -
                  synced_template.template_inputs_with_foreign.map(&:name).must_equal [ | 
| 225 | 
            +
                  synced_template.template_inputs_with_foreign.map(&:name).must_equal ['hostname', 'count']
         | 
| 226 | 
            +
                end
         | 
| 227 | 
            +
              end
         | 
| 228 | 
            +
             | 
| 229 | 
            +
              context 'template export' do
         | 
| 230 | 
            +
                let(:exportable_template) do
         | 
| 231 | 
            +
                  FactoryGirl.create(:job_template, :with_input)
         | 
| 232 | 
            +
                end
         | 
| 233 | 
            +
             | 
| 234 | 
            +
                let(:erb) do
         | 
| 235 | 
            +
                  exportable_template.to_erb
         | 
| 236 | 
            +
                end
         | 
| 237 | 
            +
             | 
| 238 | 
            +
                it 'exports name' do
         | 
| 239 | 
            +
                  erb.must_match(/^name: #{exportable_template.name}$/)
         | 
| 240 | 
            +
                end
         | 
| 241 | 
            +
             | 
| 242 | 
            +
                it 'includes template inputs' do
         | 
| 243 | 
            +
                  erb.must_match(/^template_inputs:$/)
         | 
| 244 | 
            +
                end
         | 
| 245 | 
            +
             | 
| 246 | 
            +
                it 'includes template contents' do
         | 
| 247 | 
            +
                  erb.must_include exportable_template.template
         | 
| 248 | 
            +
                end
         | 
| 249 | 
            +
             | 
| 250 | 
            +
                it 'is importable' do
         | 
| 251 | 
            +
                  erb
         | 
| 252 | 
            +
                  old_name = exportable_template.name
         | 
| 253 | 
            +
                  exportable_template.update_attributes(:name => "#{old_name}_renamed")
         | 
| 254 | 
            +
             | 
| 255 | 
            +
                  imported = JobTemplate.import!(erb)
         | 
| 256 | 
            +
                  imported.name.must_equal old_name
         | 
| 257 | 
            +
                  imported.template_inputs.first.to_export.must_equal exportable_template.template_inputs.first.to_export
         | 
| 226 258 | 
             
                end
         | 
| 227 259 | 
             
              end
         | 
| 228 260 |  | 
| @@ -8,15 +8,17 @@ describe ProxyLoadBalancer do | |
| 8 8 | 
             
              end
         | 
| 9 9 |  | 
| 10 10 | 
             
              it 'load balances' do
         | 
| 11 | 
            -
                 | 
| 12 | 
            -
                 | 
| 11 | 
            +
                count = 3
         | 
| 12 | 
            +
                ProxyAPI::ForemanDynflow::DynflowProxy.any_instance.expects(:tasks_count).raises.then.times(count - 1).returns(0)
         | 
| 13 | 
            +
                proxies = FactoryGirl.create_list(:smart_proxy, count, :ssh)
         | 
| 13 14 |  | 
| 14 | 
            -
                 | 
| 15 | 
            -
                  found  | 
| 16 | 
            -
                  not_yet_seen.delete(found)
         | 
| 15 | 
            +
                available = proxies.reduce([]) do |found, _|
         | 
| 16 | 
            +
                  found << load_balancer.next(proxies)
         | 
| 17 17 | 
             
                end
         | 
| 18 18 |  | 
| 19 | 
            -
                 | 
| 19 | 
            +
                available.count.must_equal count
         | 
| 20 | 
            +
                available.uniq.count.must_equal count - 1
         | 
| 21 | 
            +
                load_balancer.offline.count.must_equal 1
         | 
| 20 22 | 
             
              end
         | 
| 21 23 |  | 
| 22 24 | 
             
              it 'returns nil for if no proxy is available' do
         | 
| @@ -3,6 +3,21 @@ require 'test_plugin_helper' | |
| 3 3 | 
             
            describe TemplateInput do
         | 
| 4 4 | 
             
              let(:template_input) { FactoryGirl.build(:template_input) }
         | 
| 5 5 |  | 
| 6 | 
            +
              context 'export' do
         | 
| 7 | 
            +
                before do
         | 
| 8 | 
            +
                  template_input.input_type = 'user'
         | 
| 9 | 
            +
                  template_input.options = "foo\nbar\nbaz"
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                it 'exports type' do
         | 
| 13 | 
            +
                  template_input.to_export['input_type'].must_equal template_input.input_type
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                it 'exports options' do
         | 
| 17 | 
            +
                  template_input.to_export['options'].must_equal template_input.options
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
             | 
| 6 21 | 
             
              context 'user input' do
         | 
| 7 22 | 
             
                before { template_input.input_type = 'user' }
         | 
| 8 23 | 
             
                it { assert template_input.user_template_input? }
         | 
    
        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.3. | 
| 4 | 
            +
              version: 0.3.1
         | 
| 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: 2016- | 
| 11 | 
            +
            date: 2016-04-27 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: deface
         | 
| @@ -136,9 +136,11 @@ files: | |
| 136 136 | 
             
            - README.md
         | 
| 137 137 | 
             
            - Rakefile
         | 
| 138 138 | 
             
            - app/assets/javascripts/execution_interface.js
         | 
| 139 | 
            +
            - app/assets/javascripts/job_templates.js
         | 
| 139 140 | 
             
            - app/assets/javascripts/template_input.js
         | 
| 140 141 | 
             
            - app/assets/javascripts/template_invocation.js
         | 
| 141 142 | 
             
            - app/assets/stylesheets/job_invocations.css.scss
         | 
| 143 | 
            +
            - app/assets/stylesheets/modal_window.css.scss
         | 
| 142 144 | 
             
            - app/assets/stylesheets/template_invocation.css.scss
         | 
| 143 145 | 
             
            - app/controllers/api/v2/foreign_input_sets_controller.rb
         | 
| 144 146 | 
             
            - app/controllers/api/v2/job_invocations_controller.rb
         | 
| @@ -161,6 +163,7 @@ files: | |
| 161 163 | 
             
            - app/mailers/.gitkeep
         | 
| 162 164 | 
             
            - app/models/concerns/foreman_remote_execution/bookmark_extensions.rb
         | 
| 163 165 | 
             
            - app/models/concerns/foreman_remote_execution/errors_flattener.rb
         | 
| 166 | 
            +
            - app/models/concerns/foreman_remote_execution/exportable.rb
         | 
| 164 167 | 
             
            - app/models/concerns/foreman_remote_execution/foreman_tasks_task_extensions.rb
         | 
| 165 168 | 
             
            - app/models/concerns/foreman_remote_execution/foreman_tasks_triggering_extensions.rb
         | 
| 166 169 | 
             
            - app/models/concerns/foreman_remote_execution/host_extensions.rb
         | 
| @@ -234,6 +237,7 @@ files: | |
| 234 237 | 
             
            - app/views/job_invocations/show.js.erb
         | 
| 235 238 | 
             
            - app/views/job_templates/_custom_tab_headers.html.erb
         | 
| 236 239 | 
             
            - app/views/job_templates/_custom_tabs.html.erb
         | 
| 240 | 
            +
            - app/views/job_templates/_import_job_template_modal.html.erb
         | 
| 237 241 | 
             
            - app/views/job_templates/edit.html.erb
         | 
| 238 242 | 
             
            - app/views/job_templates/index.html.erb
         | 
| 239 243 | 
             
            - app/views/job_templates/new.html.erb
         | 
| @@ -335,8 +339,15 @@ files: | |
| 335 339 | 
             
            - lib/tasks/foreman_remote_execution_tasks.rake
         | 
| 336 340 | 
             
            - locale/Makefile
         | 
| 337 341 | 
             
            - locale/action_names.rb
         | 
| 342 | 
            +
            - locale/de/LC_MESSAGES/foreman_remote_execution.mo
         | 
| 343 | 
            +
            - locale/de/foreman_remote_execution.po
         | 
| 344 | 
            +
            - locale/en/LC_MESSAGES/foreman_remote_execution.mo
         | 
| 338 345 | 
             
            - locale/en/foreman_remote_execution.po
         | 
| 346 | 
            +
            - locale/en_GB/LC_MESSAGES/foreman_remote_execution.mo
         | 
| 347 | 
            +
            - locale/en_GB/foreman_remote_execution.po
         | 
| 339 348 | 
             
            - locale/foreman_remote_execution.pot
         | 
| 349 | 
            +
            - locale/fr/LC_MESSAGES/foreman_remote_execution.mo
         | 
| 350 | 
            +
            - locale/fr/foreman_remote_execution.po
         | 
| 340 351 | 
             
            - locale/gemspec.rb
         | 
| 341 352 | 
             
            - test/factories/foreman_remote_execution_factories.rb
         | 
| 342 353 | 
             
            - test/functional/api/v2/foreign_input_sets_controller_test.rb
         | 
| @@ -345,8 +356,10 @@ files: | |
| 345 356 | 
             
            - test/functional/api/v2/remote_execution_features_controller_test.rb
         | 
| 346 357 | 
             
            - test/functional/api/v2/template_inputs_controller_test.rb
         | 
| 347 358 | 
             
            - test/test_plugin_helper.rb
         | 
| 359 | 
            +
            - test/unit/actions/run_host_job_test.rb
         | 
| 348 360 | 
             
            - test/unit/actions/run_hosts_job_test.rb
         | 
| 349 361 | 
             
            - test/unit/actions/run_proxy_command_test.rb
         | 
| 362 | 
            +
            - test/unit/concerns/exportable_test.rb
         | 
| 350 363 | 
             
            - test/unit/concerns/host_extensions_test.rb
         | 
| 351 364 | 
             
            - test/unit/concerns/nic_extensions_test.rb
         | 
| 352 365 | 
             
            - test/unit/execution_task_status_mapper_test.rb
         | 
| @@ -393,8 +406,10 @@ test_files: | |
| 393 406 | 
             
            - test/functional/api/v2/remote_execution_features_controller_test.rb
         | 
| 394 407 | 
             
            - test/functional/api/v2/template_inputs_controller_test.rb
         | 
| 395 408 | 
             
            - test/test_plugin_helper.rb
         | 
| 409 | 
            +
            - test/unit/actions/run_host_job_test.rb
         | 
| 396 410 | 
             
            - test/unit/actions/run_hosts_job_test.rb
         | 
| 397 411 | 
             
            - test/unit/actions/run_proxy_command_test.rb
         | 
| 412 | 
            +
            - test/unit/concerns/exportable_test.rb
         | 
| 398 413 | 
             
            - test/unit/concerns/host_extensions_test.rb
         | 
| 399 414 | 
             
            - test/unit/concerns/nic_extensions_test.rb
         | 
| 400 415 | 
             
            - test/unit/execution_task_status_mapper_test.rb
         |