Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow runner specifications for local connections #225

Merged
merged 4 commits into from
Dec 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 45 additions & 16 deletions lib/train/transports/local.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ module Train::Transports
class Local < Train.plugin(1)
name 'local'

include_options Train::Extras::CommandWrapper

class PipeError < ::StandardError; end

def connection(_ = nil)
Expand All @@ -22,18 +20,11 @@ class Connection < BaseConnection
def initialize(options)
super(options)

# While OS is being discovered, use the GenericRunner
@runner = GenericRunner.new
@runner.cmd_wrapper = CommandWrapper.load(self, options)

if os.windows?
# Attempt to use a named pipe but fallback to ShellOut if that fails
begin
@runner = WindowsPipeRunner.new
rescue PipeError
@runner = WindowsShellRunner.new
end
end
@runner = if options[:command_runner]
force_runner(options[:command_runner])
else
select_runner(options)
end
end

def local?
Expand All @@ -50,8 +41,42 @@ def uri

private

def select_runner(options)
if os.windows?
# Attempt to use a named pipe but fallback to ShellOut if that fails
begin
WindowsPipeRunner.new
rescue PipeError
WindowsShellRunner.new
end
else
GenericRunner.new(self, options)
end
end

def force_runner(command_runner)
case command_runner
when :generic
GenericRunner.new(self, options)
when :windows_pipe
WindowsPipeRunner.new
when :windows_shell
WindowsShellRunner.new
else
fail "Runner type `#{command_runner}` not supported"
end
end

def run_command_via_connection(cmd)
@runner.run_command(cmd)
# Use the runner if it is available
return @runner.run_command(cmd) if defined?(@runner)

# If we don't have a runner, such as at the beginning of setting up the
# transport and performing the first few steps of OS detection, fall
# back to shelling out.
res = Mixlib::ShellOut.new(cmd)
res.run_command
Local::CommandResult.new(res.stdout, res.stderr, res.exitstatus)
rescue Errno::ENOENT => _
CommandResult.new('', '', 1)
end
Expand All @@ -65,7 +90,11 @@ def file_via_connection(path)
end

class GenericRunner
attr_writer :cmd_wrapper
include_options Train::Extras::CommandWrapper

def initialize(connection, options)
@cmd_wrapper = Local::CommandWrapper.load(connection, options)
end

def run_command(cmd)
if defined?(@cmd_wrapper) && !@cmd_wrapper.nil?
Expand Down
54 changes: 53 additions & 1 deletion test/unit/transports/local_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def initialize(user_opts = {})
plat.family_hierarchy = opts[:family_hierarchy]
plat.add_platform_methods
Train::Platforms::Detect.stubs(:scan).returns(plat)
@transport = Train::Transports::Local.new
@transport = Train::Transports::Local.new(user_opts)
end
end

Expand Down Expand Up @@ -55,6 +55,58 @@ def initialize(user_opts = {})
methods.include?(:file_via_connection).must_equal true
end

describe 'when overriding runner selection' do
it 'can select the `GenericRunner`' do
Train::Transports::Local::Connection::GenericRunner
.expects(:new)

Train::Transports::Local::Connection::WindowsPipeRunner
.expects(:new)
.never

Train::Transports::Local::Connection::WindowsShellRunner
.expects(:new)
.never

Train::Transports::Local::Connection.new(command_runner: :generic)
end

it 'can select the `WindowsPipeRunner`' do
Train::Transports::Local::Connection::GenericRunner
.expects(:new)
.never

Train::Transports::Local::Connection::WindowsPipeRunner
.expects(:new)

Train::Transports::Local::Connection::WindowsShellRunner
.expects(:new)
.never

Train::Transports::Local::Connection.new(command_runner: :windows_pipe)
end

it 'can select the `WindowsShellRunner`' do
Train::Transports::Local::Connection::GenericRunner
.expects(:new)
.never

Train::Transports::Local::Connection::WindowsPipeRunner
.expects(:new)
.never

Train::Transports::Local::Connection::WindowsShellRunner
.expects(:new)

Train::Transports::Local::Connection.new(command_runner: :windows_shell)
end

it 'throws a RuntimeError when an invalid runner type is passed' do
proc { Train::Transports::Local::Connection.new(command_runner: :nope ) }
.must_raise(RuntimeError, "Runner type `:nope` not supported")
end
end

describe 'when running a local command' do
let(:cmd_runner) { Minitest::Mock.new }

Expand Down