From 91bafe78f94cd7d204073e7703da02fc49660373 Mon Sep 17 00:00:00 2001 From: Ewoud Kohl van Wijngaarden Date: Mon, 16 Aug 2021 12:48:49 +0200 Subject: [PATCH 1/6] Use load_test_settings in test helper This properly loads the test settings, which includes loading defaults. This means no workaround is needed. --- test/test_helper.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/test_helper.rb b/test/test_helper.rb index 300b10e..1533470 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -21,9 +21,7 @@ FileUtils.mkdir_p(logdir) unless File.exist?(logdir) def prepare_fake_keys - Proxy::RemoteExecution::Ssh::Plugin.settings.ssh_identity_key_file = FAKE_PRIVATE_KEY_FILE - # Workaround for Proxy::RemoteExecution::Ssh::Plugin.settings.ssh_identity_key_file returning nil - Proxy::RemoteExecution::Ssh::Plugin.settings.stubs(:ssh_identity_key_file).returns(FAKE_PRIVATE_KEY_FILE) + Proxy::RemoteExecution::Ssh::Plugin.load_test_settings(ssh_identity_key_file: FAKE_PRIVATE_KEY_FILE) FileUtils.mkdir_p(DATA_DIR) unless File.exist?(DATA_DIR) File.write(FAKE_PRIVATE_KEY_FILE, '===private-key===') File.write(FAKE_PUBLIC_KEY_FILE, '===public-key===') From c4b8160c26791c6450ede6cd070e0800d01b36a0 Mon Sep 17 00:00:00 2001 From: Ewoud Kohl van Wijngaarden Date: Mon, 16 Aug 2021 13:08:36 +0200 Subject: [PATCH 2/6] Add integration test --- test/api_test.rb | 5 ++ test/integration_test.rb | 98 ++++++++++++++++++++++++++++++++++++++++ test/test_helper.rb | 1 - 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 test/integration_test.rb diff --git a/test/api_test.rb b/test/api_test.rb index b39c8bf..2bcbb1b 100644 --- a/test/api_test.rb +++ b/test/api_test.rb @@ -25,6 +25,11 @@ module Proxy::RemoteExecution::Ssh class ApiTest < MiniTest::Spec include Rack::Test::Methods + def setup + super + Proxy::RemoteExecution::Ssh::Plugin.load_test_settings(ssh_identity_key_file: FAKE_PRIVATE_KEY_FILE) + end + let(:app) { Proxy::RemoteExecution::Ssh::Api.new } describe '/pubkey' do diff --git a/test/integration_test.rb b/test/integration_test.rb new file mode 100644 index 0000000..292ff6e --- /dev/null +++ b/test/integration_test.rb @@ -0,0 +1,98 @@ +require 'test_helper' +require 'json' +require 'root/root' +require 'root/root_v2_api' +require 'smart_proxy_remote_execution_ssh/plugin' + +class SmartProxyRemoteExecutionSshApiFeaturesTest < MiniTest::Test + include Rack::Test::Methods + + def app + Proxy::PluginInitializer.new(Proxy::Plugins.instance).initialize_plugins + Proxy::RootV2Api.new + end + + def test_features_for_default_mode_without_dynflow + Proxy::LegacyModuleLoader.any_instance.expects(:load_configuration_file).with('dynflow.yml').returns(enabled: false) + Proxy::LegacyModuleLoader.any_instance.expects(:load_configuration_file).with('remote_execution_ssh.yml').returns( + enabled: true, + ssh_identity_key_file: FAKE_PRIVATE_KEY_FILE, + ) + + get '/features' + + response = JSON.parse(last_response.body) + + mod = response['ssh'] + refute_nil(mod) + assert_equal('running', mod['state'], Proxy::LogBuffer::Buffer.instance.info[:failed_modules][:ssh]) + end + + def test_features_for_default_mode_with_dynflow + Proxy::LegacyModuleLoader.any_instance.expects(:load_configuration_file).with('dynflow.yml').returns(enabled: true) + Proxy::LegacyModuleLoader.any_instance.expects(:load_configuration_file).with('remote_execution_ssh.yml').returns( + enabled: true, + ssh_identity_key_file: FAKE_PRIVATE_KEY_FILE, + ) + + get '/features' + + response = JSON.parse(last_response.body) + + mod = response['dynflow'] + refute_nil(mod) + assert_equal('running', mod['state'], Proxy::LogBuffer::Buffer.instance.info[:failed_modules][:dynflow]) + + mod = response['ssh'] + refute_nil(mod) + assert_equal('running', mod['state'], Proxy::LogBuffer::Buffer.instance.info[:failed_modules][:ssh]) + assert_equal([], mod['capabilities'], 'Has no capabilities') + assert_equal({}, mod['settings'], 'Has no settings') + end + + def test_features_for_pull_mqtt_mode_without_required_options + Proxy::LegacyModuleLoader.any_instance.expects(:load_configuration_file).with('dynflow.yml').returns(enabled: true) + Proxy::LegacyModuleLoader.any_instance.expects(:load_configuration_file).with('remote_execution_ssh.yml').returns( + enabled: true, + ssh_identity_key_file: FAKE_PRIVATE_KEY_FILE, + mode: 'pull-mqtt', + ) + + get '/features' + + response = JSON.parse(last_response.body) + + mod = response['dynflow'] + refute_nil(mod) + assert_equal('running', mod['state'], Proxy::LogBuffer::Buffer.instance.info[:failed_modules][:dynflow]) + + mod = response['ssh'] + refute_nil(mod) + assert_equal('running', mod['state'], Proxy::LogBuffer::Buffer.instance.info[:failed_modules][:ssh]) + end + + def test_features_with_dynflow_and_required_options + Proxy::LegacyModuleLoader.any_instance.expects(:load_configuration_file).with('dynflow.yml').returns(enabled: true) + Proxy::LegacyModuleLoader.any_instance.expects(:load_configuration_file).with('remote_execution_ssh.yml').returns( + enabled: true, + ssh_identity_key_file: FAKE_PRIVATE_KEY_FILE, + mode: 'pull-mqtt', + mqtt_broker: 'broker.example.com', + mqtt_port: 1883, + ) + + get '/features' + + response = JSON.parse(last_response.body) + + mod = response['dynflow'] + refute_nil(mod) + assert_equal('running', mod['state'], Proxy::LogBuffer::Buffer.instance.info[:failed_modules][:dynflow]) + + mod = response['ssh'] + refute_nil(mod) + assert_equal('running', mod['state'], Proxy::LogBuffer::Buffer.instance.info[:failed_modules][:ssh]) + assert_equal([], mod['capabilities'], 'Has no capabilities') + assert_equal({}, mod['settings'], 'Has no settings') + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index 1533470..928f985 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -21,7 +21,6 @@ FileUtils.mkdir_p(logdir) unless File.exist?(logdir) def prepare_fake_keys - Proxy::RemoteExecution::Ssh::Plugin.load_test_settings(ssh_identity_key_file: FAKE_PRIVATE_KEY_FILE) FileUtils.mkdir_p(DATA_DIR) unless File.exist?(DATA_DIR) File.write(FAKE_PRIVATE_KEY_FILE, '===private-key===') File.write(FAKE_PUBLIC_KEY_FILE, '===public-key===') From 88b0fc31eb032e4431cd05edfd51dc3644e67402 Mon Sep 17 00:00:00 2001 From: Ewoud Kohl van Wijngaarden Date: Fri, 9 Jul 2021 16:43:29 +0200 Subject: [PATCH 3/6] Use the rackup_path helper Introduced in Smart Proxy 2.3 and this simplifies the code. --- lib/smart_proxy_remote_execution_ssh/plugin.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/smart_proxy_remote_execution_ssh/plugin.rb b/lib/smart_proxy_remote_execution_ssh/plugin.rb index f2b4b61..cb30c09 100644 --- a/lib/smart_proxy_remote_execution_ssh/plugin.rb +++ b/lib/smart_proxy_remote_execution_ssh/plugin.rb @@ -3,8 +3,7 @@ class Plugin < Proxy::Plugin SSH_LOG_LEVELS = %w[debug info error fatal].freeze MODES = %i[ssh async-ssh pull pull-mqtt].freeze - http_rackup_path File.expand_path("http_config.ru", File.expand_path("../", __FILE__)) - https_rackup_path File.expand_path("http_config.ru", File.expand_path("../", __FILE__)) + rackup_path File.expand_path("http_config.ru", __dir__) settings_file "remote_execution_ssh.yml" default_settings :ssh_identity_key_file => '~/.ssh/id_rsa_foreman_proxy', From b3d0c0d5df833586fc6d649cee39ab9574803fc7 Mon Sep 17 00:00:00 2001 From: Ewoud Kohl van Wijngaarden Date: Mon, 13 Dec 2021 17:21:13 +0100 Subject: [PATCH 4/6] Return a 404 if the public key file doesn't exist --- lib/smart_proxy_remote_execution_ssh/api.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/smart_proxy_remote_execution_ssh/api.rb b/lib/smart_proxy_remote_execution_ssh/api.rb index 7af69b4..26bb0f3 100644 --- a/lib/smart_proxy_remote_execution_ssh/api.rb +++ b/lib/smart_proxy_remote_execution_ssh/api.rb @@ -11,6 +11,8 @@ class Api < ::Sinatra::Base get "/pubkey" do File.read(Ssh.public_key_file) + rescue Errno::ENOENT + halt 404 end post "/session" do From a4e2ae393387072cdf54365eebc94f7dab22489f Mon Sep 17 00:00:00 2001 From: Ewoud Kohl van Wijngaarden Date: Thu, 24 Jun 2021 10:39:54 +0200 Subject: [PATCH 5/6] Use more native methods of SmartProxy for validation This uses more built in validators. It also creates a setting for the public key file and derives it if not specified. The only downside is that the validate_readable doesn't print a helpful command to generate it if needed. Programmable settings can be used to generate it on the fly if the parent directory is readable. Another alternative is to introduce another custom validator. It slightly abuses the dependency injection hook to configure the task launcher registry. --- lib/smart_proxy_remote_execution_ssh.rb | 77 ------------------- lib/smart_proxy_remote_execution_ssh/api.rb | 2 +- .../cockpit.rb | 2 +- .../plugin.rb | 49 +++++++++++- .../validators.rb | 42 ++++++++++ test/api_test.rb | 3 +- test/integration_test.rb | 14 ++-- 7 files changed, 101 insertions(+), 88 deletions(-) create mode 100644 lib/smart_proxy_remote_execution_ssh/validators.rb diff --git a/lib/smart_proxy_remote_execution_ssh.rb b/lib/smart_proxy_remote_execution_ssh.rb index c455a4a..0955667 100644 --- a/lib/smart_proxy_remote_execution_ssh.rb +++ b/lib/smart_proxy_remote_execution_ssh.rb @@ -6,83 +6,6 @@ module Proxy::RemoteExecution module Ssh class << self - def validate! - unless private_key_file - raise "settings for `ssh_identity_key` not set" - end - - unless File.exist?(private_key_file) - raise "Ssh public key file #{private_key_file} doesn't exist.\n"\ - "You can generate one with `ssh-keygen -t rsa -b 4096 -f #{private_key_file} -N ''`" - end - - unless File.exist?(public_key_file) - raise "Ssh public key file #{public_key_file} doesn't exist" - end - - validate_mode! - validate_ssh_log_level! - validate_mqtt_settings! - end - - def private_key_file - File.expand_path(Plugin.settings.ssh_identity_key_file) - end - - def public_key_file - File.expand_path("#{private_key_file}.pub") - end - - def validate_mode! - Plugin.settings.mode = Plugin.settings.mode.to_sym - - unless Plugin::MODES.include? Plugin.settings.mode - raise "Mode has to be one of #{Plugin::MODES.join(', ')}, given #{Plugin.settings.mode}" - end - - if Plugin.settings.async_ssh - Plugin.logger.warn('Option async_ssh is deprecated, use ssh-async mode instead.') - - case Plugin.settings.mode - when :ssh - Plugin.logger.warn('Deprecated option async_ssh used together with ssh mode, switching mode to ssh-async.') - Plugin.settings.mode = :'ssh-async' - when :'async-ssh' - # This is a noop - else - Plugin.logger.warn('Deprecated option async_ssh used together with incompatible mode, ignoring.') - end - end - end - - def validate_mqtt_settings! - return unless Plugin.settings.mode == :'pull-mqtt' - - raise 'mqtt_broker has to be set when pull-mqtt mode is used' if Plugin.settings.mqtt_broker.nil? - raise 'mqtt_port has to be set when pull-mqtt mode is used' if Plugin.settings.mqtt_port.nil? - end - - def validate_ssh_log_level! - wanted_level = Plugin.settings.ssh_log_level.to_s - levels = Plugin::SSH_LOG_LEVELS - unless levels.include? wanted_level - raise "Wrong value '#{Plugin.settings.ssh_log_level}' for ssh_log_level, must be one of #{levels.join(', ')}" - end - - current = ::Proxy::SETTINGS.log_level.to_s.downcase - - # regular log levels correspond to upcased ssh logger levels - ssh, regular = [wanted_level, current].map do |wanted| - levels.each_with_index.find { |value, _index| value == wanted }.last - end - - if ssh < regular - raise 'ssh_log_level cannot be more verbose than regular log level' - end - - Plugin.settings.ssh_log_level = Plugin.settings.ssh_log_level.to_sym - end - def job_storage @job_storage ||= Proxy::RemoteExecution::Ssh::JobStorage.new end diff --git a/lib/smart_proxy_remote_execution_ssh/api.rb b/lib/smart_proxy_remote_execution_ssh/api.rb index 26bb0f3..5ae35f7 100644 --- a/lib/smart_proxy_remote_execution_ssh/api.rb +++ b/lib/smart_proxy_remote_execution_ssh/api.rb @@ -10,7 +10,7 @@ class Api < ::Sinatra::Base include Proxy::Dynflow::Helpers get "/pubkey" do - File.read(Ssh.public_key_file) + File.read(Proxy::RemoteExecution::Plugin.settings.ssh_identity_public_key_file) rescue Errno::ENOENT halt 404 end diff --git a/lib/smart_proxy_remote_execution_ssh/cockpit.rb b/lib/smart_proxy_remote_execution_ssh/cockpit.rb index ed61899..5b6939e 100644 --- a/lib/smart_proxy_remote_execution_ssh/cockpit.rb +++ b/lib/smart_proxy_remote_execution_ssh/cockpit.rb @@ -242,7 +242,7 @@ def params end def key_file - @key_file ||= Proxy::RemoteExecution::Ssh.private_key_file + @key_file ||= Proxy::RemoteExecution::Plugin.settings.ssh_identity_key_file end def buf_socket diff --git a/lib/smart_proxy_remote_execution_ssh/plugin.rb b/lib/smart_proxy_remote_execution_ssh/plugin.rb index cb30c09..6c2f2e4 100644 --- a/lib/smart_proxy_remote_execution_ssh/plugin.rb +++ b/lib/smart_proxy_remote_execution_ssh/plugin.rb @@ -1,3 +1,5 @@ +require_relative 'validators' + module Proxy::RemoteExecution::Ssh class Plugin < Proxy::Plugin SSH_LOG_LEVELS = %w[debug info error fatal].freeze @@ -19,9 +21,49 @@ class Plugin < Proxy::Plugin # :mqtt_port => nil, :mode => :ssh + load_validators ssh_log_level: Proxy::RemoteExecution::Ssh::Validators::SshLogLevel, + rex_ssh_mode: Proxy::RemoteExecution::Ssh::Validators::RexSshMode + + load_programmable_settings do |settings| + if settings[:ssh_identity_key_file] + settings[:ssh_identity_key_file] = File.expand_path(settings[:ssh_identity_key_file]) + end + + if settings[:ssh_identity_public_key_file] + settings[:ssh_identity_public_key_file] = File.expand_path(settings[:ssh_identity_public_key_file]) + elsif settings[:ssh_identity_key_file] + settings[:ssh_identity_public_key_file] = "#{settings[:ssh_identity_key_file]}.pub" + end + + if settings[:async_ssh] + Plugin.logger.warn('Option async_ssh is deprecated, use ssh-async mode instead.') + + case setting_value + when :ssh + Plugin.logger.warn('Deprecated option async_ssh used together with ssh mode, switching mode to ssh-async.') + settings[:mode] = :'ssh-async' + when :'async-ssh' + # This is a noop + else + Plugin.logger.warn('Deprecated option async_ssh used together with incompatible mode, ignoring.') + end + end + + settings[:mode] = settings[:mode].to_sym + settings[:ssh_log_level] = settings[:ssh_log_level].to_sym + end + + validate_readable :ssh_identity_key_file, :ssh_identity_public_key_file + validate :ssh_log_level, ssh_log_level: SSH_LOG_LEVELS + validate :mode, rex_ssh_mode: MODES + validate_presence :mqtt_broker, :mqtt_port, if: ->(settings) { settings[:mode] == :'pull-mqtt' } + plugin :ssh, Proxy::RemoteExecution::Ssh::VERSION - after_activation do + + load_classes do require 'smart_proxy_dynflow' + require 'smart_proxy_dynflow/task_launcher' + require 'smart_proxy_dynflow/runner' require 'smart_proxy_remote_execution_ssh/version' require 'smart_proxy_remote_execution_ssh/cockpit' require 'smart_proxy_remote_execution_ssh/api' @@ -31,9 +73,10 @@ class Plugin < Proxy::Plugin require 'smart_proxy_remote_execution_ssh/runners' require 'smart_proxy_remote_execution_ssh/utils' require 'smart_proxy_remote_execution_ssh/job_storage' + end - Proxy::RemoteExecution::Ssh.validate! - + # Not really Smart Proxy dependency injection, but similar enough + load_dependency_injection_wirings do |container_instance, settings| Proxy::Dynflow::TaskLauncherRegistry.register('ssh', Proxy::Dynflow::TaskLauncher::Batch) end diff --git a/lib/smart_proxy_remote_execution_ssh/validators.rb b/lib/smart_proxy_remote_execution_ssh/validators.rb new file mode 100644 index 0000000..584127e --- /dev/null +++ b/lib/smart_proxy_remote_execution_ssh/validators.rb @@ -0,0 +1,42 @@ +module Proxy + module RemoteExecution + module Ssh + module Validators + class SshLogLevel < ::Proxy::PluginValidators::Base + def validate!(settings) + setting_value = settings[@setting_name].to_s + + unless @params.include?(setting_value) + raise ::Proxy::Error::ConfigurationError, "Parameter '#{@setting_name}' must be one of #{@params.join(', ')}" + end + + current = ::Proxy::SETTINGS.log_level.to_s.downcase + + # regular log levels correspond to upcased ssh logger levels + ssh, regular = [setting_value, current].map do |wanted| + @params.each_with_index.find { |value, _index| value == wanted }.last + end + + if ssh < regular + raise ::Proxy::Error::ConfigurationError, "Parameter '#{@setting_name}' cannot be more verbose than regular log level (#{current})" + end + + true + end + end + + class RexSshMode < ::Proxy::PluginValidators::Base + def validate!(settings) + setting_value = settings[@setting_name] + + unless @params.include?(setting_value) + raise ::Proxy::Error::ConfigurationError, "Parameter '#{@setting_name}' must be one of #{@params.join(', ')}" + end + + true + end + end + end + end + end +end diff --git a/test/api_test.rb b/test/api_test.rb index 2bcbb1b..2bd825d 100644 --- a/test/api_test.rb +++ b/test/api_test.rb @@ -27,7 +27,7 @@ class ApiTest < MiniTest::Spec def setup super - Proxy::RemoteExecution::Ssh::Plugin.load_test_settings(ssh_identity_key_file: FAKE_PRIVATE_KEY_FILE) + Proxy::RemoteExecution::Ssh::Plugin.load_test_settings(ssh_identity_key_file: FAKE_PRIVATE_KEY_FILE, ssh_identity_public_key_file: FAKE_PUBLIC_KEY_FILE) end let(:app) { Proxy::RemoteExecution::Ssh::Api.new } @@ -35,6 +35,7 @@ def setup describe '/pubkey' do it 'returns the content of the public key' do get '/pubkey' + _(last_response.status).must_equal 200, last_response.body _(last_response.body).must_equal '===public-key===' end end diff --git a/test/integration_test.rb b/test/integration_test.rb index 292ff6e..af52786 100644 --- a/test/integration_test.rb +++ b/test/integration_test.rb @@ -14,9 +14,11 @@ def app def test_features_for_default_mode_without_dynflow Proxy::LegacyModuleLoader.any_instance.expects(:load_configuration_file).with('dynflow.yml').returns(enabled: false) - Proxy::LegacyModuleLoader.any_instance.expects(:load_configuration_file).with('remote_execution_ssh.yml').returns( + Proxy::DefaultModuleLoader.any_instance.expects(:load_configuration_file).with('remote_execution_ssh.yml').returns( enabled: true, ssh_identity_key_file: FAKE_PRIVATE_KEY_FILE, + mqtt_broker: 'broker.example.com', + mqtt_port: 1883, ) get '/features' @@ -30,7 +32,7 @@ def test_features_for_default_mode_without_dynflow def test_features_for_default_mode_with_dynflow Proxy::LegacyModuleLoader.any_instance.expects(:load_configuration_file).with('dynflow.yml').returns(enabled: true) - Proxy::LegacyModuleLoader.any_instance.expects(:load_configuration_file).with('remote_execution_ssh.yml').returns( + Proxy::DefaultModuleLoader.any_instance.expects(:load_configuration_file).with('remote_execution_ssh.yml').returns( enabled: true, ssh_identity_key_file: FAKE_PRIVATE_KEY_FILE, ) @@ -52,7 +54,7 @@ def test_features_for_default_mode_with_dynflow def test_features_for_pull_mqtt_mode_without_required_options Proxy::LegacyModuleLoader.any_instance.expects(:load_configuration_file).with('dynflow.yml').returns(enabled: true) - Proxy::LegacyModuleLoader.any_instance.expects(:load_configuration_file).with('remote_execution_ssh.yml').returns( + Proxy::DefaultModuleLoader.any_instance.expects(:load_configuration_file).with('remote_execution_ssh.yml').returns( enabled: true, ssh_identity_key_file: FAKE_PRIVATE_KEY_FILE, mode: 'pull-mqtt', @@ -68,12 +70,14 @@ def test_features_for_pull_mqtt_mode_without_required_options mod = response['ssh'] refute_nil(mod) - assert_equal('running', mod['state'], Proxy::LogBuffer::Buffer.instance.info[:failed_modules][:ssh]) + assert_equal('failed', mod['state'], Proxy::LogBuffer::Buffer.instance.info[:failed_modules][:ssh]) + assert_equal("Disabling all modules in the group ['ssh'] due to a failure in one of them: Parameter 'mqtt_broker' is expected to have a non-empty value", + Proxy::LogBuffer::Buffer.instance.info[:failed_modules][:ssh]) end def test_features_with_dynflow_and_required_options Proxy::LegacyModuleLoader.any_instance.expects(:load_configuration_file).with('dynflow.yml').returns(enabled: true) - Proxy::LegacyModuleLoader.any_instance.expects(:load_configuration_file).with('remote_execution_ssh.yml').returns( + Proxy::DefaultModuleLoader.any_instance.expects(:load_configuration_file).with('remote_execution_ssh.yml').returns( enabled: true, ssh_identity_key_file: FAKE_PRIVATE_KEY_FILE, mode: 'pull-mqtt', From 47f71ca7b320fa22d2ac339138cf50854342cd04 Mon Sep 17 00:00:00 2001 From: Ewoud Kohl van Wijngaarden Date: Fri, 9 Jul 2021 16:43:51 +0200 Subject: [PATCH 6/6] Make dynflow an explicit dependency Loading the smart_proxy_dynflow (or any Smart Proxy plugin really) in load_classes does not work due to the way plugin initialization happens. For this plugin to work, dynflow must be running. This uses requires to validate that dynflow is actually correctly enabled and running. This also means that if dynflow was loaded but ended up in a failed state that REX SSH also ends up in a failed state. --- lib/smart_proxy_remote_execution_ssh/plugin.rb | 2 ++ test/integration_test.rb | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/smart_proxy_remote_execution_ssh/plugin.rb b/lib/smart_proxy_remote_execution_ssh/plugin.rb index 6c2f2e4..4d75fc8 100644 --- a/lib/smart_proxy_remote_execution_ssh/plugin.rb +++ b/lib/smart_proxy_remote_execution_ssh/plugin.rb @@ -60,6 +60,8 @@ class Plugin < Proxy::Plugin plugin :ssh, Proxy::RemoteExecution::Ssh::VERSION + requires :dynflow, '~> 0.5' + load_classes do require 'smart_proxy_dynflow' require 'smart_proxy_dynflow/task_launcher' diff --git a/test/integration_test.rb b/test/integration_test.rb index af52786..c2884f9 100644 --- a/test/integration_test.rb +++ b/test/integration_test.rb @@ -27,7 +27,9 @@ def test_features_for_default_mode_without_dynflow mod = response['ssh'] refute_nil(mod) - assert_equal('running', mod['state'], Proxy::LogBuffer::Buffer.instance.info[:failed_modules][:ssh]) + assert_equal('failed', mod['state'], Proxy::LogBuffer::Buffer.instance.info[:failed_modules][:ssh]) + assert_equal("Disabling all modules in the group ['ssh'] due to a failure in one of them: 'dynflow' required by 'ssh' could not be found.", + Proxy::LogBuffer::Buffer.instance.info[:failed_modules][:ssh]) end def test_features_for_default_mode_with_dynflow