From aa2e02b97326acf360a4de5532e4c66d22cae2bf Mon Sep 17 00:00:00 2001 From: Matt Brictson Date: Fri, 22 Dec 2023 14:57:49 -0600 Subject: [PATCH 01/10] Extract SCP logic into separate strategy class This will make it easier to swap in SFTP in a later commit. --- lib/sshkit/backends/netssh.rb | 16 ++++++++----- lib/sshkit/backends/netssh/scp_transfer.rb | 26 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 lib/sshkit/backends/netssh/scp_transfer.rb diff --git a/lib/sshkit/backends/netssh.rb b/lib/sshkit/backends/netssh.rb index fe08fa7c..b24fdc1c 100644 --- a/lib/sshkit/backends/netssh.rb +++ b/lib/sshkit/backends/netssh.rb @@ -2,7 +2,6 @@ require 'strscan' require 'mutex_m' require 'net/ssh' -require 'net/scp' module Net module SSH @@ -64,16 +63,16 @@ def assign_defaults def upload!(local, remote, options = {}) summarizer = transfer_summarizer('Uploading', options) remote = File.join(pwd_path, remote) unless remote.to_s.start_with?("/") || pwd_path.nil? - with_ssh do |ssh| - ssh.scp.upload!(local, remote, options, &summarizer) + with_transfer(summarizer) do |transfer| + transfer.upload!(local, remote, options) end end def download!(remote, local=nil, options = {}) summarizer = transfer_summarizer('Downloading', options) remote = File.join(pwd_path, remote) unless remote.to_s.start_with?("/") || pwd_path.nil? - with_ssh do |ssh| - ssh.scp.download!(remote, local, options, &summarizer) + with_transfer(summarizer) do |transfer| + transfer.download!(remote, local, options) end end @@ -183,6 +182,13 @@ def with_ssh(&block) ) end + def with_transfer(summarizer) + require_relative "netssh/scp_transfer" + + with_ssh do |ssh| + yield(ScpTransfer.new(ssh, summarizer)) + end + end end end diff --git a/lib/sshkit/backends/netssh/scp_transfer.rb b/lib/sshkit/backends/netssh/scp_transfer.rb new file mode 100644 index 00000000..d8ed4737 --- /dev/null +++ b/lib/sshkit/backends/netssh/scp_transfer.rb @@ -0,0 +1,26 @@ +require "net/scp" + +module SSHKit + module Backend + class Netssh < Abstract + class ScpTransfer + def initialize(ssh, summarizer) + @ssh = ssh + @summarizer = summarizer + end + + def upload!(local, remote, options) + ssh.scp.upload!(local, remote, options, &summarizer) + end + + def download!(remote, local, options) + ssh.scp.download!(remote, local, options, &summarizer) + end + + private + + attr_reader :ssh, :summarizer + end + end + end +end From 3015787d0d0e2820fc681b4150145dc98780d10d Mon Sep 17 00:00:00 2001 From: Matt Brictson Date: Fri, 22 Dec 2023 14:59:05 -0600 Subject: [PATCH 02/10] Guard against divide-by-zero --- lib/sshkit/backends/netssh.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sshkit/backends/netssh.rb b/lib/sshkit/backends/netssh.rb index b24fdc1c..b98943db 100644 --- a/lib/sshkit/backends/netssh.rb +++ b/lib/sshkit/backends/netssh.rb @@ -104,7 +104,7 @@ def transfer_summarizer(action, options = {}) last_percentage = nil proc do |_ch, name, transferred, total| percentage = (transferred.to_f * 100 / total.to_f) - unless percentage.nan? + unless percentage.nan? || percentage.infinite? message = "#{action} #{name} #{percentage.round(2)}%" percentage_r = (percentage / log_percent).truncate * log_percent if percentage_r > 0 && (last_name != name || last_percentage != percentage_r) From d963bda1c53cf37a1c87ca928bdb6724f090fc4d Mon Sep 17 00:00:00 2001 From: Matt Brictson Date: Fri, 22 Dec 2023 14:59:51 -0600 Subject: [PATCH 03/10] Add net-sftp gem dependency --- sshkit.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/sshkit.gemspec b/sshkit.gemspec index 7ce539dd..c363c3f6 100644 --- a/sshkit.gemspec +++ b/sshkit.gemspec @@ -22,6 +22,7 @@ Gem::Specification.new do |gem| gem.add_runtime_dependency('net-ssh', '>= 2.8.0') gem.add_runtime_dependency('net-scp', '>= 1.1.2') + gem.add_runtime_dependency('net-sftp', '>= 2.1.2') gem.add_development_dependency('danger') gem.add_development_dependency('minitest', '>= 5.0.0') From 0d65d88d6d9ccd5dabccdeaabd98fff943f61e43 Mon Sep 17 00:00:00 2001 From: Matt Brictson Date: Fri, 22 Dec 2023 15:02:17 -0600 Subject: [PATCH 04/10] Allow :sftp or :scp transfer method to be configured --- EXAMPLES.md | 17 +++++++++++++++++ README.md | 3 ++- lib/sshkit/backends/netssh.rb | 17 +++++++++++++++++ lib/sshkit/host.rb | 7 +++++++ 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 2c3a575b..4403579a 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -162,6 +162,23 @@ end In this case the `recursive: true` option mirrors the same options which are available to [`Net::{SCP,SFTP}`](http://net-ssh.github.io/net-scp/). +## Set the upload/download method (SCP or SFTP). + +SSHKit can use SCP or SFTP for file transfers. The default is SCP, but this can be changed to SFTP per host: + +```ruby +host = SSHKit::Host.new('user@example.com') +host.transfer_method = :sftp +``` + +or globally: + +```ruby +SSHKit::Backend::Netssh.configure do |ssh| + ssh.transfer_method = :sftp +end +``` + ## Setting global SSH options Setting global SSH options, these will be overwritten by options set on the diff --git a/README.md b/README.md index 31f16d16..bb95334d 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,8 @@ you can pass the `strip: false` option: `capture(:ls, '-l', strip: false)` ### Transferring files All backends also support the `upload!` and `download!` methods for transferring files. -For the remote backend, the file is transferred with scp. +For the remote backend, the file is transferred with scp by default, but sftp is also +supported. See [EXAMPLES.md](EXAMPLES.md) for details. ```ruby on '1.example.com' do diff --git a/lib/sshkit/backends/netssh.rb b/lib/sshkit/backends/netssh.rb index b98943db..4656101c 100644 --- a/lib/sshkit/backends/netssh.rb +++ b/lib/sshkit/backends/netssh.rb @@ -22,10 +22,27 @@ module SSHKit module Backend class Netssh < Abstract + def self.assert_valid_transfer_method!(method) + return if [nil, :scp, :sftp].include?(method) + + raise ArgumentError, "#{method.inspect} is not a valid transfer method. Supported methods are :scp, :sftp." + end + class Configuration attr_accessor :connection_timeout, :pty + attr_reader :transfer_method attr_writer :ssh_options + def initialize + self.transfer_method = :scp + end + + def transfer_method=(method) + Netssh.assert_valid_transfer_method!(method) + + @transfer_method = method + end + def ssh_options default_options.merge(@ssh_options ||= {}) end diff --git a/lib/sshkit/host.rb b/lib/sshkit/host.rb index 732972ab..d5219776 100644 --- a/lib/sshkit/host.rb +++ b/lib/sshkit/host.rb @@ -7,6 +7,7 @@ module SSHKit class Host attr_accessor :password, :hostname, :port, :user, :ssh_options + attr_reader :transfer_method def key=(new_key) @keys = [new_key] @@ -41,6 +42,12 @@ def initialize(host_string_or_options_hash) end end + def transfer_method=(method) + Backend::Netssh.assert_valid_transfer_method!(method) + + @transfer_method = method + end + def local? @local end From ced903d7df3bb9c75db2b235377ac385ba56c680 Mon Sep 17 00:00:00 2001 From: Matt Brictson Date: Fri, 22 Dec 2023 15:02:37 -0600 Subject: [PATCH 05/10] Implement SFTP transfer strategy --- lib/sshkit/backends/netssh.rb | 11 ++++- lib/sshkit/backends/netssh/sftp_transfer.rb | 46 +++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 lib/sshkit/backends/netssh/sftp_transfer.rb diff --git a/lib/sshkit/backends/netssh.rb b/lib/sshkit/backends/netssh.rb index 4656101c..506dddd5 100644 --- a/lib/sshkit/backends/netssh.rb +++ b/lib/sshkit/backends/netssh.rb @@ -200,10 +200,17 @@ def with_ssh(&block) end def with_transfer(summarizer) - require_relative "netssh/scp_transfer" + transfer_method = host.transfer_method || self.class.config.transfer_method + transfer_class = if transfer_method == :sftp + require_relative "netssh/sftp_transfer" + SftpTransfer + else + require_relative "netssh/scp_transfer" + ScpTransfer + end with_ssh do |ssh| - yield(ScpTransfer.new(ssh, summarizer)) + yield(transfer_class.new(ssh, summarizer)) end end end diff --git a/lib/sshkit/backends/netssh/sftp_transfer.rb b/lib/sshkit/backends/netssh/sftp_transfer.rb new file mode 100644 index 00000000..20c230ba --- /dev/null +++ b/lib/sshkit/backends/netssh/sftp_transfer.rb @@ -0,0 +1,46 @@ +require "net/sftp" + +module SSHKit + module Backend + class Netssh < Abstract + class SftpTransfer + def initialize(ssh, summarizer) + @ssh = ssh + @summarizer = summarizer + end + + def upload!(local, remote, options) + options = { progress: self }.merge(options || {}) + ssh.sftp.connect! + ssh.sftp.upload!(local, remote, options) + ensure + ssh.sftp.close_channel + end + + def download!(remote, local, options) + options = { progress: self }.merge(options || {}) + destination = local ? local : StringIO.new.tap { |io| io.set_encoding('BINARY') } + + ssh.sftp.connect! + ssh.sftp.download!(remote, destination, options) + local ? true : destination.string + ensure + ssh.sftp.close_channel + end + + def on_get(download, entry, offset, data) + entry.size ||= download.sftp.file.open(entry.remote, &:size) + summarizer.call(nil, entry.remote, offset + data.bytesize, entry.size) + end + + def on_put(_upload, file, offset, data) + summarizer.call(nil, file.local, offset + data.bytesize, file.size) + end + + private + + attr_reader :ssh, :summarizer + end + end + end +end From cf985cf882c876110ee53bfdcf4d2e1250a53a3e Mon Sep 17 00:00:00 2001 From: Matt Brictson Date: Fri, 22 Dec 2023 15:10:14 -0600 Subject: [PATCH 06/10] Extract upload/download tests into module for reuse --- .../backends/netssh_transfer_tests.rb | 83 +++++++++++++++++++ test/functional/backends/test_netssh.rb | 66 --------------- test/functional/backends/test_netssh_scp.rb | 17 ++++ 3 files changed, 100 insertions(+), 66 deletions(-) create mode 100644 test/functional/backends/netssh_transfer_tests.rb create mode 100644 test/functional/backends/test_netssh_scp.rb diff --git a/test/functional/backends/netssh_transfer_tests.rb b/test/functional/backends/netssh_transfer_tests.rb new file mode 100644 index 00000000..32562443 --- /dev/null +++ b/test/functional/backends/netssh_transfer_tests.rb @@ -0,0 +1,83 @@ +require 'securerandom' + +module SSHKit + module Backend + module NetsshTransferTests + def setup + super + @output = String.new + SSHKit.config.output_verbosity = :debug + SSHKit.config.output = SSHKit::Formatter::SimpleText.new(@output) + end + + def a_host + VagrantWrapper.hosts['one'] + end + + def test_upload_and_then_capture_file_contents + actual_file_contents = "" + file_name = File.join("/tmp", SecureRandom.uuid) + File.open file_name, 'w+' do |f| + f.write "Some Content\nWith a newline and trailing spaces \n " + end + Netssh.new(a_host) do + upload!(file_name, file_name) + actual_file_contents = capture(:cat, file_name, strip: false) + end.run + assert_equal "Some Content\nWith a newline and trailing spaces \n ", actual_file_contents + end + + def test_upload_within + file_name = SecureRandom.uuid + file_contents = "Some Content" + dir_name = SecureRandom.uuid + actual_file_contents = "" + Netssh.new(a_host) do |_host| + within("/tmp") do + execute :mkdir, "-p", dir_name + within(dir_name) do + upload!(StringIO.new(file_contents), file_name) + end + end + actual_file_contents = capture(:cat, "/tmp/#{dir_name}/#{file_name}", strip: false) + end.run + assert_equal file_contents, actual_file_contents + end + + def test_upload_string_io + file_contents = "" + Netssh.new(a_host) do |_host| + file_name = File.join("/tmp", SecureRandom.uuid) + upload!(StringIO.new('example_io'), file_name) + file_contents = download!(file_name) + end.run + assert_equal "example_io", file_contents + end + + def test_upload_large_file + size = 25 + fills = SecureRandom.random_bytes(1024*1024) + file_name = "/tmp/file-#{size}.txt" + File.open(file_name, 'wb') do |f| + (size).times {f.write(fills) } + end + file_contents = "" + Netssh.new(a_host) do + upload!(file_name, file_name) + file_contents = download!(file_name) + end.run + assert_equal File.open(file_name, 'rb').read, file_contents + end + + def test_upload_via_pathname + file_contents = "" + Netssh.new(a_host) do |_host| + file_name = Pathname.new(File.join("/tmp", SecureRandom.uuid)) + upload!(StringIO.new('example_io'), file_name) + file_contents = download!(file_name) + end.run + assert_equal "example_io", file_contents + end + end + end +end diff --git a/test/functional/backends/test_netssh.rb b/test/functional/backends/test_netssh.rb index b003f81a..3a2f03d6 100644 --- a/test/functional/backends/test_netssh.rb +++ b/test/functional/backends/test_netssh.rb @@ -1,5 +1,4 @@ require 'helper' -require 'securerandom' require 'benchmark' module SSHKit @@ -136,71 +135,6 @@ def test_test_does_not_raise_on_non_zero_exit_status end.run end - def test_upload_and_then_capture_file_contents - actual_file_contents = "" - file_name = File.join("/tmp", SecureRandom.uuid) - File.open file_name, 'w+' do |f| - f.write "Some Content\nWith a newline and trailing spaces \n " - end - Netssh.new(a_host) do - upload!(file_name, file_name) - actual_file_contents = capture(:cat, file_name, strip: false) - end.run - assert_equal "Some Content\nWith a newline and trailing spaces \n ", actual_file_contents - end - - def test_upload_within - file_name = SecureRandom.uuid - file_contents = "Some Content" - dir_name = SecureRandom.uuid - actual_file_contents = "" - Netssh.new(a_host) do |_host| - within("/tmp") do - execute :mkdir, "-p", dir_name - within(dir_name) do - upload!(StringIO.new(file_contents), file_name) - end - end - actual_file_contents = capture(:cat, "/tmp/#{dir_name}/#{file_name}", strip: false) - end.run - assert_equal file_contents, actual_file_contents - end - - def test_upload_string_io - file_contents = "" - Netssh.new(a_host) do |_host| - file_name = File.join("/tmp", SecureRandom.uuid) - upload!(StringIO.new('example_io'), file_name) - file_contents = download!(file_name) - end.run - assert_equal "example_io", file_contents - end - - def test_upload_large_file - size = 25 - fills = SecureRandom.random_bytes(1024*1024) - file_name = "/tmp/file-#{size}.txt" - File.open(file_name, 'wb') do |f| - (size).times {f.write(fills) } - end - file_contents = "" - Netssh.new(a_host) do - upload!(file_name, file_name) - file_contents = download!(file_name) - end.run - assert_equal File.open(file_name, 'rb').read, file_contents - end - - def test_upload_via_pathname - file_contents = "" - Netssh.new(a_host) do |_host| - file_name = Pathname.new(File.join("/tmp", SecureRandom.uuid)) - upload!(StringIO.new('example_io'), file_name) - file_contents = download!(file_name) - end.run - assert_equal "example_io", file_contents - end - def test_interaction_handler captured_command_result = nil Netssh.new(a_host) do diff --git a/test/functional/backends/test_netssh_scp.rb b/test/functional/backends/test_netssh_scp.rb new file mode 100644 index 00000000..de02c48b --- /dev/null +++ b/test/functional/backends/test_netssh_scp.rb @@ -0,0 +1,17 @@ +require 'helper' +require_relative 'netssh_transfer_tests' + +module SSHKit + module Backend + class TestNetsshScp < FunctionalTest + include NetsshTransferTests + + def setup + super + SSHKit::Backend::Netssh.configure do |ssh| + ssh.transfer_method = :scp + end + end + end + end +end From 0306386610dfe7d0899ed3b7d3c8c9da75e5e89b Mon Sep 17 00:00:00 2001 From: Matt Brictson Date: Fri, 22 Dec 2023 15:20:46 -0600 Subject: [PATCH 07/10] Test SFTP transfer method --- test/functional/backends/test_netssh_sftp.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 test/functional/backends/test_netssh_sftp.rb diff --git a/test/functional/backends/test_netssh_sftp.rb b/test/functional/backends/test_netssh_sftp.rb new file mode 100644 index 00000000..1575fb14 --- /dev/null +++ b/test/functional/backends/test_netssh_sftp.rb @@ -0,0 +1,17 @@ +require 'helper' +require_relative 'netssh_transfer_tests' + +module SSHKit + module Backend + class TestNetsshSftp < FunctionalTest + include NetsshTransferTests + + def setup + super + SSHKit::Backend::Netssh.configure do |ssh| + ssh.transfer_method = :sftp + end + end + end + end +end From 1a6b3cd5710f11598dd72de2fc0432c466f93146 Mon Sep 17 00:00:00 2001 From: Matt Brictson Date: Wed, 27 Dec 2023 15:10:54 -0600 Subject: [PATCH 08/10] Test that transfer_method can be configured --- test/unit/backends/test_netssh.rb | 12 ++++++++++++ test/unit/test_host.rb | 27 +++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/test/unit/backends/test_netssh.rb b/test/unit/backends/test_netssh.rb index 54aa9894..3e787e27 100644 --- a/test/unit/backends/test_netssh.rb +++ b/test/unit/backends/test_netssh.rb @@ -13,6 +13,7 @@ def test_net_ssh_configuration_options backend.configure do |ssh| ssh.pty = true ssh.connection_timeout = 30 + ssh.transfer_method = :sftp ssh.ssh_options = { keys: %w(/home/user/.ssh/id_rsa), forward_agent: false, @@ -21,6 +22,7 @@ def test_net_ssh_configuration_options end assert_equal 30, backend.config.connection_timeout + assert_equal :sftp, backend.config.transfer_method assert_equal true, backend.config.pty assert_equal %w(/home/user/.ssh/id_rsa), backend.config.ssh_options[:keys] @@ -29,6 +31,16 @@ def test_net_ssh_configuration_options assert_instance_of backend::KnownHosts, backend.config.ssh_options[:known_hosts] end + def test_transfer_method_prohibits_invalid_values + error = assert_raises ArgumentError do + backend.configure do |ssh| + ssh.transfer_method = :nope + end + end + + assert_match ":nope is not a valid transfer method", error.message + end + def test_netssh_ext assert_includes Net::SSH::Config.default_files, "#{Dir.pwd}/.ssh/config" end diff --git a/test/unit/test_host.rb b/test/unit/test_host.rb index 8bc4e5d3..8ce489fa 100644 --- a/test/unit/test_host.rb +++ b/test/unit/test_host.rb @@ -142,6 +142,33 @@ def test_turning_a_host_into_ssh_options_when_extra_options_are_set end end + def test_transfer_method_defaults_to_nil + host = Host.new 'example.com' + assert_nil host.transfer_method + end + + def test_transfer_method_can_be_configured + host = Host.new 'example.com' + + host.transfer_method = :scp + assert_equal :scp, host.transfer_method + + host.transfer_method = :sftp + assert_equal :sftp, host.transfer_method + + host.transfer_method = nil + assert_nil host.transfer_method + end + + def test_transfer_method_prohibits_invalid_values + host = Host.new 'example.com' + + error = assert_raises ArgumentError do + host.transfer_method = :nope + end + + assert_match ":nope is not a valid transfer method", error.message + end end end From 53ca44ef643fc5e78e80c9333b7f2d5f079e1ce0 Mon Sep 17 00:00:00 2001 From: Matt Brictson Date: Wed, 27 Dec 2023 15:28:40 -0600 Subject: [PATCH 09/10] Test that expected implementation is actually used --- test/functional/backends/test_netssh_scp.rb | 6 +++++ test/functional/backends/test_netssh_sftp.rb | 6 +++++ test/unit/backends/test_netssh.rb | 26 ++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/test/functional/backends/test_netssh_scp.rb b/test/functional/backends/test_netssh_scp.rb index de02c48b..dfded0ac 100644 --- a/test/functional/backends/test_netssh_scp.rb +++ b/test/functional/backends/test_netssh_scp.rb @@ -12,6 +12,12 @@ def setup ssh.transfer_method = :scp end end + + def test_scp_implementation_is_used + Netssh.new(a_host).send(:with_transfer, nil) do |transfer| + assert_instance_of Netssh::ScpTransfer, transfer + end + end end end end diff --git a/test/functional/backends/test_netssh_sftp.rb b/test/functional/backends/test_netssh_sftp.rb index 1575fb14..73fa5af7 100644 --- a/test/functional/backends/test_netssh_sftp.rb +++ b/test/functional/backends/test_netssh_sftp.rb @@ -12,6 +12,12 @@ def setup ssh.transfer_method = :sftp end end + + def test_sftp_implementation_is_used + Netssh.new(a_host).send(:with_transfer, nil) do |transfer| + assert_instance_of Netssh::SftpTransfer, transfer + end + end end end end diff --git a/test/unit/backends/test_netssh.rb b/test/unit/backends/test_netssh.rb index 3e787e27..6d735c45 100644 --- a/test/unit/backends/test_netssh.rb +++ b/test/unit/backends/test_netssh.rb @@ -5,6 +5,12 @@ module SSHKit module Backend class TestNetssh < UnitTest + def teardown + super + # Reset config to defaults after each test + backend.instance_variable_set :@config, nil + end + def backend @backend ||= Netssh end @@ -41,6 +47,26 @@ def test_transfer_method_prohibits_invalid_values assert_match ":nope is not a valid transfer method", error.message end + def test_transfer_method_defaults_to_scp + assert_equal :scp, backend.config.transfer_method + end + + def test_host_can_override_transfer_method + backend.configure do |ssh| + ssh.transfer_method = :scp + end + + host = Host.new("fake") + host.transfer_method = :sftp + + netssh = backend.new(host) + netssh.stubs(:with_ssh).yields(nil) + + netssh.send(:with_transfer, nil) do |transfer| + assert_instance_of Netssh::SftpTransfer, transfer + end + end + def test_netssh_ext assert_includes Net::SSH::Config.default_files, "#{Dir.pwd}/.ssh/config" end From 9bbcc2cc1e98d8171fe8abe70fa77942ef43ea60 Mon Sep 17 00:00:00 2001 From: Matt Brictson Date: Fri, 29 Dec 2023 17:52:26 -0600 Subject: [PATCH 10/10] Don't allow global transfer_method to be nil --- lib/sshkit/backends/netssh.rb | 2 +- lib/sshkit/host.rb | 2 +- test/unit/backends/test_netssh.rb | 10 ++++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/sshkit/backends/netssh.rb b/lib/sshkit/backends/netssh.rb index 506dddd5..7211c7c9 100644 --- a/lib/sshkit/backends/netssh.rb +++ b/lib/sshkit/backends/netssh.rb @@ -23,7 +23,7 @@ module Backend class Netssh < Abstract def self.assert_valid_transfer_method!(method) - return if [nil, :scp, :sftp].include?(method) + return if [:scp, :sftp].include?(method) raise ArgumentError, "#{method.inspect} is not a valid transfer method. Supported methods are :scp, :sftp." end diff --git a/lib/sshkit/host.rb b/lib/sshkit/host.rb index d5219776..fc33be20 100644 --- a/lib/sshkit/host.rb +++ b/lib/sshkit/host.rb @@ -43,7 +43,7 @@ def initialize(host_string_or_options_hash) end def transfer_method=(method) - Backend::Netssh.assert_valid_transfer_method!(method) + Backend::Netssh.assert_valid_transfer_method!(method) unless method.nil? @transfer_method = method end diff --git a/test/unit/backends/test_netssh.rb b/test/unit/backends/test_netssh.rb index 6d735c45..3d7503a7 100644 --- a/test/unit/backends/test_netssh.rb +++ b/test/unit/backends/test_netssh.rb @@ -47,6 +47,16 @@ def test_transfer_method_prohibits_invalid_values assert_match ":nope is not a valid transfer method", error.message end + def test_transfer_method_does_not_allow_nil + error = assert_raises ArgumentError do + backend.configure do |ssh| + ssh.transfer_method = nil + end + end + + assert_match "nil is not a valid transfer method", error.message + end + def test_transfer_method_defaults_to_scp assert_equal :scp, backend.config.transfer_method end