foreman_remote_execution 0.0.6 → 0.0.7

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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/execution_interface.js +6 -0
  3. data/app/assets/javascripts/template_invocation.js +18 -0
  4. data/app/assets/stylesheets/template_invocation.css.scss +53 -0
  5. data/app/controllers/api/v2/job_invocations_controller.rb +64 -0
  6. data/app/controllers/job_invocations_controller.rb +11 -2
  7. data/app/controllers/template_invocations_controller.rb +16 -0
  8. data/app/helpers/remote_execution_helper.rb +78 -15
  9. data/app/lib/actions/middleware/bind_job_invocation.rb +29 -0
  10. data/app/lib/actions/remote_execution/run_host_job.rb +20 -53
  11. data/app/lib/actions/remote_execution/run_hosts_job.rb +30 -9
  12. data/app/lib/actions/remote_execution/run_proxy_command.rb +64 -3
  13. data/app/models/concerns/foreman_remote_execution/host_extensions.rb +43 -5
  14. data/app/models/concerns/foreman_remote_execution/nic_extensions.rb +25 -0
  15. data/app/models/concerns/foreman_remote_execution/subnet_extensions.rb +10 -0
  16. data/app/models/concerns/foreman_remote_execution/taxonomy_extensions.rb +16 -0
  17. data/app/models/input_template_renderer.rb +5 -1
  18. data/app/models/job_invocation.rb +74 -7
  19. data/app/models/job_invocation_api_composer.rb +62 -0
  20. data/app/models/job_invocation_composer.rb +4 -1
  21. data/app/models/job_template.rb +2 -4
  22. data/app/models/setting/remote_execution.rb +6 -2
  23. data/app/models/target_remote_execution_proxy.rb +4 -0
  24. data/app/models/template_input.rb +14 -2
  25. data/app/models/template_invocation.rb +12 -1
  26. data/app/models/template_invocation_input_value.rb +1 -1
  27. data/app/overrides/execution_interface.rb +9 -0
  28. data/app/overrides/foreman/nics/_execution_interface.html.erb +1 -0
  29. data/app/overrides/foreman/subnets/_rex_tab.html.erb +1 -0
  30. data/app/overrides/foreman/subnets/_rex_tab_pane.html.erb +5 -0
  31. data/app/overrides/subnet_proxies.rb +9 -0
  32. data/app/services/proxy_load_balancer.rb +30 -0
  33. data/app/views/api/v2/job_invocations/base.json.rabl +3 -0
  34. data/app/views/api/v2/job_invocations/create.json.rabl +3 -0
  35. data/app/views/api/v2/job_invocations/index.json.rabl +3 -0
  36. data/app/views/api/v2/job_invocations/main.json.rabl +3 -0
  37. data/app/views/api/v2/job_invocations/show.json.rabl +18 -0
  38. data/app/views/api/v2/job_templates/base.json.rabl +1 -1
  39. data/app/views/job_invocations/_form.html.erb +9 -0
  40. data/app/views/job_invocations/_tab_hosts.html.erb +10 -14
  41. data/app/views/job_invocations/_tab_overview.html.erb +2 -9
  42. data/app/views/job_invocations/show.html.erb +8 -1
  43. data/app/views/job_invocations/show.js.erb +6 -0
  44. data/app/views/template_invocations/_output_line_set.html.erb +7 -0
  45. data/app/views/template_invocations/_refresh.js.erb +7 -0
  46. data/app/views/template_invocations/show.html.erb +38 -0
  47. data/app/views/template_invocations/show.js.erb +16 -0
  48. data/config/routes.rb +4 -0
  49. data/db/migrate/20150826191632_create_target_remote_execution_proxies.rb +14 -0
  50. data/db/migrate/20150903192731_add_execution_to_interface.rb +22 -0
  51. data/doc/source/design/index.md +5 -0
  52. data/doc/source/design/wireframes.pdf +0 -0
  53. data/lib/foreman_remote_execution.rb +1 -1
  54. data/lib/foreman_remote_execution/engine.rb +13 -4
  55. data/lib/foreman_remote_execution/version.rb +1 -1
  56. data/test/factories/foreman_remote_execution_factories.rb +46 -1
  57. data/test/functional/api/v2/job_invocations_controller_test.rb +45 -0
  58. data/test/test_plugin_helper.rb +18 -0
  59. data/test/unit/actions/run_hosts_job_test.rb +13 -8
  60. data/test/unit/actions/run_proxy_command_test.rb +109 -5
  61. data/test/unit/concerns/host_extensions_test.rb +90 -0
  62. data/test/unit/concerns/nic_extensions_test.rb +9 -0
  63. data/test/unit/input_template_renderer_test.rb +24 -10
  64. data/test/unit/job_invocation_api_composer_test.rb +117 -0
  65. data/test/unit/job_invocation_composer_test.rb +16 -1
  66. data/test/unit/job_invocation_test.rb +48 -3
  67. data/test/unit/proxy_load_balancer_test.rb +25 -0
  68. metadata +57 -4
data/config/routes.rb CHANGED
@@ -23,8 +23,12 @@ Rails.application.routes.draw do
23
23
  end
24
24
  end
25
25
 
26
+ resources :template_invocations, :only => [:show]
27
+
26
28
  namespace :api, :defaults => {:format => 'json'} do
27
29
  scope "(:apiv)", :module => :v2, :defaults => {:apiv => 'v2'}, :apiv => /v1|v2/, :constraints => ApiConstraints.new(:version => 2, :default => true) do
30
+ resources :job_invocations, :except => [:new, :edit, :update, :destroy]
31
+
28
32
  resources :job_templates, :except => [:new, :edit] do
29
33
  (resources :locations, :only => [:index, :show]) if SETTINGS[:locations_enabled]
30
34
  (resources :organizations, :only => [:index, :show]) if SETTINGS[:organizations_enabled]
@@ -0,0 +1,14 @@
1
+ class CreateTargetRemoteExecutionProxies < ActiveRecord::Migration
2
+ def change
3
+ create_table :target_remote_execution_proxies do |t|
4
+ t.integer :remote_execution_proxy_id
5
+ t.integer :target_id
6
+ t.string :target_type
7
+
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :target_remote_execution_proxies, :remote_execution_proxy_id, :name => 'target_remote_execution_proxies_proxy_id'
12
+ add_index :target_remote_execution_proxies, [:target_id, :target_type], :name => 'target_remote_execution_proxies_target_id_target_type'
13
+ end
14
+ end
@@ -0,0 +1,22 @@
1
+ class FakeNic < ActiveRecord::Base
2
+ self.table_name = 'nics'
3
+
4
+ def type
5
+ Nic::Managed
6
+ end
7
+ end
8
+
9
+ class AddExecutionToInterface < ActiveRecord::Migration
10
+ def up
11
+ add_column :nics, :execution, :boolean, :default => false
12
+
13
+ FakeNic.reset_column_information
14
+ FakeNic.all.each do |nic|
15
+ nic.update_column(:execution, true) if nic.primary
16
+ end
17
+ end
18
+
19
+ def down
20
+ remove_column :nics, :execution
21
+ end
22
+ end
@@ -1315,3 +1315,8 @@ package "Developer API" {
1315
1315
  }
1316
1316
 
1317
1317
  {% endplantuml %}
1318
+
1319
+ Wireframes
1320
+ ===========
1321
+
1322
+ Here are [wireframes PDF](/foreman_remote_execution/design/wireframes.pdf) from 2015-08-14 which we follow where underlaying backends allow us.
Binary file
@@ -1,5 +1,5 @@
1
+ require 'deface'
1
2
  require 'foreman-tasks'
2
-
3
3
  require 'foreman_remote_execution/engine'
4
4
 
5
5
  module ForemanRemoteExecution
@@ -42,10 +42,10 @@ module ForemanRemoteExecution
42
42
  permission :destroy_job_templates, { :job_templates => [:destroy],
43
43
  :'api/v2/job_templates' => [:destroy] }, :resource_type => 'JobTemplate'
44
44
  permission :lock_job_templates, { :job_templates => [:lock, :unlock] }, :resource_type => 'JobTemplate'
45
-
46
- permission :view_job_invocations, { :job_invocations => [:index, :show, :auto_complete_search] }, :resource_type => 'JobInvocation'
47
-
48
- permission :create_job_invocations, { :job_invocations => [:new, :create, :refresh, :rerun] }, :resource_type => 'JobInvocation'
45
+ permission :create_job_invocations, { :job_invocations => [:new, :create, :refresh, :rerun],
46
+ 'api/v2/job_invocations' => [:create] }, :resource_type => 'JobInvocation'
47
+ permission :view_job_invocations, { :job_invocations => [:index, :show, :auto_complete_search], :template_invocations => [:show],
48
+ 'api/v2/job_invocations' => [:index, :show] }, :resource_type => 'JobInvocation'
49
49
  end
50
50
 
51
51
  # Add a new role called 'ForemanRemoteExecution' if it doesn't exist
@@ -100,14 +100,23 @@ module ForemanRemoteExecution
100
100
  # similarly, attr_accessible :template_inputs_attributes does not work with STI
101
101
  (Template.descendants + [Template]).each { |klass| klass.send(:include, ForemanRemoteExecution::TemplateExtensions) }
102
102
 
103
+ (Taxonomy.descendants + [Taxonomy]).each { |klass| klass.send(:include, ForemanRemoteExecution::TaxonomyExtensions) }
104
+
103
105
  User.send(:include, ForemanRemoteExecution::UserExtensions)
104
106
  (Host::Base.descendants + [Host::Base]).each do |klass|
105
107
  klass.send(:include, ForemanRemoteExecution::HostExtensions)
106
108
  klass.send(:include, ForemanTasks::Concerns::HostActionSubject)
107
109
  end
110
+
111
+ (Nic::Base.descendants + [Nic::Base]).each do |klass|
112
+ klass.send(:include, ForemanRemoteExecution::NicExtensions)
113
+ end
114
+
108
115
  Bookmark.send(:include, ForemanRemoteExecution::BookmarkExtensions)
109
116
  HostsHelper.send(:include, ForemanRemoteExecution::HostsHelperExtensions)
110
117
 
118
+ Subnet.send(:include, ForemanRemoteExecution::SubnetExtensions)
119
+
111
120
  # We need to explicitly force to load the Task model due to Rails loader
112
121
  # having issues with resolving it to Rake::Task otherwise
113
122
  require_dependency 'foreman_tasks/task'
@@ -1,3 +1,3 @@
1
1
  module ForemanRemoteExecution
2
- VERSION = '0.0.6'
2
+ VERSION = '0.0.7'
3
3
  end
@@ -1,6 +1,8 @@
1
1
  FactoryGirl.define do
2
+
2
3
  factory :job_template do |f|
3
4
  f.sequence(:name) { |n| "Job template #{n}" }
5
+ sequence(:job_name) { |n| "job name #{n}" }
4
6
  f.template 'id'
5
7
  f.provider_type 'Ssh'
6
8
 
@@ -13,7 +15,7 @@ FactoryGirl.define do
13
15
 
14
16
  factory :template_input do |f|
15
17
  f.sequence(:name) { |n| "Template input #{n}" }
16
- f.input_type TemplateInput::TYPES.keys.first
18
+ f.input_type 'user'
17
19
  end
18
20
 
19
21
  factory :targeting do |f|
@@ -46,3 +48,46 @@ FactoryGirl.define do
46
48
  f.sequence(:value) { |n| "Input Value #{n}" }
47
49
  end
48
50
  end
51
+
52
+ FactoryGirl.modify do
53
+ factory :feature do
54
+ trait :ssh do
55
+ name 'Ssh'
56
+ end
57
+ end
58
+
59
+ factory :smart_proxy do
60
+ trait :ssh do
61
+ features { [FactoryGirl.build(:feature, :ssh)] }
62
+ end
63
+ end
64
+
65
+ factory :subnet do
66
+ trait :execution do
67
+ remote_execution_proxies { [FactoryGirl.build(:smart_proxy, :ssh)] }
68
+ end
69
+ end
70
+
71
+ factory :host do
72
+ trait :with_execution do
73
+ managed
74
+ domain
75
+ subnet do
76
+ overrides = {
77
+ :remote_execution_proxies => [FactoryGirl.create(:smart_proxy, :ssh)]
78
+ }
79
+
80
+ overrides[:locations] = [location] unless location.nil?
81
+ overrides[:organizations] = [organization] unless organization.nil?
82
+
83
+ FactoryGirl.create(
84
+ :subnet,
85
+ overrides
86
+ )
87
+ end
88
+ interfaces do
89
+ [FactoryGirl.build(:nic_primary_and_provision, :ip => subnet.network.sub(/0\Z/, '1'), :execution => true)]
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,45 @@
1
+ require 'test_plugin_helper'
2
+
3
+ module Api
4
+ module V2
5
+ class JobInvocationsControllerTest < ActionController::TestCase
6
+ setup do
7
+ @invocation = FactoryGirl.create(:job_invocation, :with_template)
8
+ @template = FactoryGirl.create(:job_template, :with_input)
9
+ end
10
+
11
+ test "should get index" do
12
+ get :index
13
+ invocations = ActiveSupport::JSON.decode(@response.body)
14
+ refute_empty invocations, "Should response with invocation"
15
+ assert_response :success
16
+ end
17
+
18
+ test "should get invocation detail" do
19
+ get :show, :id => @invocation.id
20
+ assert_response :success
21
+ template = ActiveSupport::JSON.decode(@response.body)
22
+ refute_empty template
23
+ assert_equal template["job_name"], @invocation.job_name
24
+ end
25
+
26
+ test "should create valid without template_id" do
27
+ attrs = { :job_name => @template.job_name, :name => 'RandomName', :targeting_type => 'static_query', :search_query => "foobar"}
28
+ post :create, :job_invocation => attrs
29
+
30
+ invocation = ActiveSupport::JSON.decode(@response.body)
31
+ assert_equal attrs[:job_name], invocation['job_name']
32
+ assert_response 200
33
+ end
34
+
35
+ test "should create valid with template_id" do
36
+ attrs = { :job_name => @template.job_name, :name => 'RandomName', :template_id => @template.id,:targeting_type => 'static_query', :search_query => "foobar"}
37
+ post :create, :job_invocation => attrs
38
+
39
+ invocation = ActiveSupport::JSON.decode(@response.body)
40
+ assert_equal attrs[:job_name], invocation['job_name']
41
+ assert_response 200
42
+ end
43
+ end
44
+ end
45
+ end
@@ -6,3 +6,21 @@ require 'dynflow/testing'
6
6
  # Add plugin to FactoryGirl's paths
7
7
  FactoryGirl.definition_file_paths << File.join(File.dirname(__FILE__), 'factories')
8
8
  FactoryGirl.reload
9
+
10
+ # Foreman's setup doesn't handle cleaning up for Minitest::Spec
11
+ DatabaseCleaner.strategy = :transaction
12
+
13
+ class Minitest::Spec
14
+ class << self
15
+ alias_method :context, :describe
16
+ end
17
+
18
+ before :each do
19
+ DatabaseCleaner.start
20
+ Setting::RemoteExecution.load_defaults
21
+ end
22
+
23
+ after :each do
24
+ DatabaseCleaner.clean
25
+ end
26
+ end
@@ -2,28 +2,27 @@
2
2
  require "test_plugin_helper"
3
3
 
4
4
  module ForemanRemoteExecution
5
- class RunHostsJobTest < ActiveSupport::TestCase
5
+ class RunHostsJobTest < ActiveSupport::TestCase
6
6
  include Dynflow::Testing
7
7
 
8
- let(:proxy) { FactoryGirl.build(:smart_proxy) }
9
- let(:hostname) { 'myhost.example.com' }
10
- let(:script) { 'ping -c 5 redhat.com' }
8
+ let(:host) { FactoryGirl.create(:host, :with_execution) }
9
+ let(:proxy) { host.remote_execution_proxies('Ssh')[:subnet].first }
11
10
  let(:targeting) { FactoryGirl.create(:targeting, :search_query => "name = #{host.name}", :user => User.current) }
12
11
  let(:job_invocation) do
13
- FactoryGirl.build(:job_invocation).tap do |invocation|
12
+ FactoryGirl.build(:job_invocation, :with_template).tap do |invocation|
14
13
  invocation.targeting = targeting
15
14
  invocation.save
16
15
  end
17
16
  end
18
- let(:host) { FactoryGirl.create(:host) }
19
17
  let(:action) do
20
18
  action = create_action(Actions::RemoteExecution::RunHostsJob)
21
19
  action.expects(:action_subject).with(job_invocation)
22
- action.expects(:task).returns(OpenStruct.new(:id => '123'))
20
+ ForemanTasks::Task::DynflowTask.stubs(:find_by_external_id!).returns(OpenStruct.new(:id => '123'))
23
21
  plan_action(action, job_invocation)
24
22
  end
25
23
 
26
24
  before do
25
+ ProxyAPI::ForemanDynflow::DynflowProxy.any_instance.stubs(:tasks_count).returns(0)
27
26
  User.current = users :admin
28
27
  action
29
28
  end
@@ -33,8 +32,14 @@ module ForemanRemoteExecution
33
32
  end
34
33
 
35
34
  it 'triggers the RunHostJob actions on the resolved hosts in run phase' do
36
- action.expects(:trigger).with(Actions::RemoteExecution::RunHostJob, job_invocation, host)
35
+ template_invocation = job_invocation.template_invocation_for_host(host)
36
+ action.expects(:trigger).with(Actions::RemoteExecution::RunHostJob, job_invocation, host, template_invocation, proxy, {})
37
37
  action.create_sub_plans
38
38
  end
39
+
40
+ it 'uses the BindJobInvocation middleware' do
41
+ action
42
+ job_invocation.last_task_id.must_equal '123'
43
+ end
39
44
  end
40
45
  end
@@ -1,21 +1,25 @@
1
1
  require "test_plugin_helper"
2
2
 
3
3
  module ForemanRemoteExecution
4
- class RunProxyCommandTest < ActiveSupport::TestCase
4
+ class RunProxyCommandTest < ActiveSupport::TestCase
5
5
  include Dynflow::Testing
6
6
 
7
- let(:proxy) { FactoryGirl.build(:smart_proxy) }
7
+ let(:host) { FactoryGirl.build(:host, :with_execution) }
8
+ let(:proxy) { host.remote_execution_proxies('Ssh')[:subnet].first }
8
9
  let(:hostname) { 'myhost.example.com' }
9
10
  let(:script) { 'ping -c 5 redhat.com' }
11
+ let(:connection_options) { { 'retry_interval' => 15, 'retry_count' => 4, 'timeout' => 60 } }
10
12
  let(:action) do
11
- create_and_plan_action(Actions::RemoteExecution::RunProxyCommand, proxy, hostname, script)
13
+ create_and_plan_action(Actions::RemoteExecution::RunProxyCommand, proxy, host.name, script)
12
14
  end
15
+ let(:timestamp) { 1_443_194_805.9192207 }
13
16
 
14
17
  it 'plans for running the command action on server' do
15
- assert_run_phase action, { :hostname => hostname,
18
+ assert_run_phase action, { :hostname => host.name,
16
19
  :script => script,
17
20
  :proxy_url => proxy.url,
18
- :effective_user => nil }
21
+ :effective_user => nil,
22
+ :connection_options => connection_options }
19
23
  end
20
24
 
21
25
  it 'sends to command to ssh provider' do
@@ -26,5 +30,105 @@ module ForemanRemoteExecution
26
30
  action.rescue_strategy.must_equal ::Dynflow::Action::Rescue::Skip
27
31
  end
28
32
 
33
+ describe '#live_output' do
34
+ let(:task) { ForemanTasks::Task.new }
35
+
36
+ let(:action) do
37
+ planned_action = create_and_plan_action(Actions::RemoteExecution::RunProxyCommand, proxy, hostname, script)
38
+ create_action_presentation(Actions::RemoteExecution::RunProxyCommand).tap do |action|
39
+ action.stubs(:task).returns(task)
40
+ action.stubs(:proxy).returns(ProxyAPI::ForemanDynflow::DynflowProxy.new(:url => proxy.url))
41
+ action.instance_variable_set('@input', planned_action.input)
42
+ action.output.merge!(:proxy_task_id => '123')
43
+ end
44
+ end
45
+
46
+ let(:live_output) do
47
+ action.live_output
48
+ end
49
+
50
+ describe 'when the task is finished' do
51
+ before do
52
+ task.state = 'stopped'
53
+ task.ended_at = timestamp + 1
54
+ end
55
+
56
+ describe 'the task finished sucessfully' do
57
+ before do
58
+ action.output.merge!(:proxy_output => { :result => [{ 'output_type' => 'stdout', 'output' => 'Hello', 'timestamp' => timestamp}],
59
+ :exit_status => 0 })
60
+ end
61
+
62
+ it "doesn't fetch data from proxy anymore" do
63
+ action.proxy.expects(:status_of_task).never
64
+ live_output.size.must_equal 2
65
+ live_output[0]['output_type'].must_equal 'stdout'
66
+ live_output[0]['output'].must_equal 'Hello'
67
+ live_output[0]['timestamp'].must_be_kind_of Float
68
+ live_output[1]['output_type'].must_equal 'stdout'
69
+ live_output[1]['output'].must_equal 'Exit status: 0'
70
+ live_output[1]['timestamp'].must_be_kind_of Float
71
+ end
72
+ end
73
+
74
+ describe 'there was not output data from proxy' do
75
+ before do
76
+ action.output.merge!(:proxy_output => {})
77
+ end
78
+
79
+ it "doesn't fetch data from proxy anymore" do
80
+ action.proxy.expects(:status_of_task).never
81
+ live_output.size.must_equal 1
82
+ live_output[0]['output_type'].must_equal 'debug'
83
+ live_output[0]['output'].must_equal 'No output'
84
+ live_output[0]['timestamp'].must_be_kind_of Float
85
+ end
86
+ end
87
+ end
88
+
89
+ describe 'when something went wrong while fetching the data' do
90
+ before do
91
+ action.proxy.expects(:status_of_task).raises("Something went wrong")
92
+ end
93
+
94
+ it "reports the failure as part of the live output" do
95
+ live_output.size.must_equal 1
96
+ live_output.first['output_type'].must_equal 'debug'
97
+ live_output.first['output'].must_equal 'Error loading data from proxy: RuntimeError - Something went wrong'
98
+ live_output.first['timestamp'].must_be_kind_of Float
99
+ end
100
+ end
101
+
102
+ describe 'when there was some connection error while running the command' do
103
+ before do
104
+ action.output.merge!(:proxy_task_id => nil,
105
+ :metadata => { :failed_proxy_tasks => [action.send(:format_exception, RuntimeError.new('Connection error'))]})
106
+ end
107
+
108
+ it "reports the failure as part of the live output" do
109
+ live_output.size.must_equal 1
110
+ live_output.first['output_type'].must_equal 'debug'
111
+ live_output.first['output'].must_equal 'Initialization error: RuntimeError - Connection error'
112
+ live_output.first['timestamp'].must_be_kind_of Float
113
+ end
114
+ end
115
+
116
+ describe 'when proxy returns valid data' do
117
+ before do
118
+ action.proxy.expects(:status_of_task).returns('actions' =>
119
+ [{ 'class' => 'Proxy::RemoteExecution::Ssh::CommandAction',
120
+ 'output' => { 'result' => [ { 'output_type' => 'stdout',
121
+ 'output' => 'Hello',
122
+ 'timestamp' => timestamp }]}}])
123
+ end
124
+
125
+ it "reports the failure as part of the live output" do
126
+ live_output.size.must_equal 1
127
+ live_output.first['output_type'].must_equal 'stdout'
128
+ live_output.first['output'].must_equal 'Hello'
129
+ live_output.first['timestamp'].must_be_kind_of Float
130
+ end
131
+ end
132
+ end
29
133
  end
30
134
  end
@@ -0,0 +1,90 @@
1
+ require 'test_plugin_helper'
2
+
3
+ describe ForemanRemoteExecution::HostExtensions do
4
+ let(:provider) { 'Ssh' }
5
+
6
+ before do
7
+ User.current = FactoryGirl.build(:user, :admin)
8
+ end
9
+
10
+ after { User.current = nil }
11
+
12
+ context 'host has multiple nics' do
13
+ let(:host) { FactoryGirl.build(:host, :with_execution) }
14
+
15
+ it 'should only have one execution interface' do
16
+ host.interfaces << FactoryGirl.build(:nic_managed)
17
+ host.interfaces.each { |interface| interface.execution = true }
18
+ host.wont_be :valid?
19
+ end
20
+
21
+ it 'returns the execution interface' do
22
+ host.execution_interface.must_be_kind_of Nic::Managed
23
+ end
24
+ end
25
+
26
+ describe 'proxy determination strategies' do
27
+ context 'subnet strategy' do
28
+ let(:host) { FactoryGirl.build(:host, :with_execution) }
29
+ it { host.remote_execution_proxies(provider)[:subnet].must_include host.subnet.remote_execution_proxies.first }
30
+ end
31
+
32
+ context 'fallback strategy' do
33
+ let(:host) { FactoryGirl.build(:host, :with_puppet) }
34
+
35
+ context 'enabled' do
36
+ before do
37
+ Setting[:remote_execution_fallback_proxy] = true
38
+ host.puppet_proxy.features << FactoryGirl.create(:feature, :ssh)
39
+ end
40
+
41
+ it 'returns a fallback proxy' do
42
+ host.remote_execution_proxies(provider)[:fallback].must_include host.puppet_proxy
43
+ end
44
+ end
45
+
46
+ context 'disabled' do
47
+ before do
48
+ Setting[:remote_execution_fallback_proxy] = false
49
+ host.puppet_proxy.features << FactoryGirl.create(:feature, :ssh)
50
+ end
51
+
52
+ it 'returns no proxy' do
53
+ host.remote_execution_proxies(provider)[:fallback].must_be_empty
54
+ end
55
+ end
56
+ end
57
+
58
+ context 'global strategy' do
59
+ let(:organization) { FactoryGirl.build(:organization) }
60
+ let(:location) { FactoryGirl.build(:location) }
61
+ let(:host) { FactoryGirl.build(:host, :organization => organization, :location => location) }
62
+ let(:proxy_in_taxonomies) { FactoryGirl.create(:smart_proxy, :ssh, :organizations => [organization], :locations => [location]) }
63
+ let(:proxy_no_taxonomies) { FactoryGirl.create(:smart_proxy, :ssh) }
64
+
65
+ context 'enabled' do
66
+ before { Setting[:remote_execution_global_proxy] = true }
67
+
68
+ it 'returns correct proxies confined by taxonomies' do
69
+ proxy_in_taxonomies
70
+ proxy_no_taxonomies
71
+ host.remote_execution_proxies(provider)[:global].must_include proxy_in_taxonomies
72
+ host.remote_execution_proxies(provider)[:global].wont_include proxy_no_taxonomies
73
+ end
74
+
75
+ it 'returns all proxies when there\'s no taxonomies' do
76
+ Taxonomy.stubs(:enabled_taxonomies).returns([])
77
+ host.remote_execution_proxies(provider)[:global].must_include proxy_in_taxonomies
78
+ host.remote_execution_proxies(provider)[:global].must_include proxy_no_taxonomies
79
+ end
80
+ end
81
+
82
+ context 'disabled' do
83
+ before { Setting[:remote_execution_global_proxy] = false }
84
+ it 'returns no proxy' do
85
+ host.remote_execution_proxies(provider)[:global].must_be_empty
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end