SSH class converted to instance methods. now loads on the environment

This commit is contained in:
Mitchell Hashimoto 2010-03-19 01:51:27 -07:00
parent 689f69bd4c
commit 260b099dba
5 changed files with 145 additions and 78 deletions

View File

@ -28,27 +28,27 @@ module Vagrant
# useful information such as whether or not the environment is created # useful information such as whether or not the environment is created
# and if its running, suspended, etc. # and if its running, suspended, etc.
def status def status
Env.load! env = Environment.load!
wrap_output do wrap_output do
if !Env.persisted_vm if !env.vm
puts <<-msg puts <<-msg
The environment has not yet been created. Run `vagrant up` to create the The environment has not yet been created. Run `vagrant up` to create the
environment. environment.
msg msg
else else
additional_msg = "" additional_msg = ""
if Env.persisted_vm.vm.running? if env.vm.vm.running?
additional_msg = <<-msg additional_msg = <<-msg
To stop this VM, you can run `vagrant halt` to shut it down forcefully, To stop this VM, you can run `vagrant halt` to shut it down forcefully,
or you can run `vagrant suspend` to simply suspend the virtual machine. or you can run `vagrant suspend` to simply suspend the virtual machine.
In either case, to restart it again, simply run a `vagrant up`. In either case, to restart it again, simply run a `vagrant up`.
msg msg
elsif Env.persisted_vm.vm.saved? elsif env.vm.vm.saved?
additional_msg = <<-msg additional_msg = <<-msg
To resume this VM, simply run `vagrant up`. To resume this VM, simply run `vagrant up`.
msg msg
elsif Env.persisted_vm.vm.powered_off? elsif env.vm.vm.powered_off?
additional_msg = <<-msg additional_msg = <<-msg
To restart this VM, simply run `vagrant up`. To restart this VM, simply run `vagrant up`.
msg msg
@ -61,7 +61,7 @@ msg
puts <<-msg puts <<-msg
The environment has been created. The status of the current environment's The environment has been created. The status of the current environment's
virtual machine is: "#{Env.persisted_vm.vm.state}."#{additional_msg} virtual machine is: "#{env.vm.vm.state}."#{additional_msg}
msg msg
end end
end end

View File

@ -13,6 +13,7 @@ module Vagrant
attr_reader :config attr_reader :config
attr_reader :box attr_reader :box
attr_reader :vm attr_reader :vm
attr_reader :ssh
#--------------------------------------------------------------- #---------------------------------------------------------------
# Class Methods # Class Methods
@ -84,6 +85,7 @@ module Vagrant
load_config! load_config!
self.class.check_virtualbox! self.class.check_virtualbox!
load_vm! load_vm!
load_ssh!
self self
end end
@ -167,6 +169,11 @@ module Vagrant
@vm = nil @vm = nil
end end
# Loads/initializes the SSH object
def load_ssh!
@ssh = SSH.new(self)
end
#--------------------------------------------------------------- #---------------------------------------------------------------
# Methods to manage VM # Methods to manage VM
#--------------------------------------------------------------- #---------------------------------------------------------------

View File

@ -1,54 +1,77 @@
module Vagrant module Vagrant
# Manages SSH access to a specific environment. Allows an environment to
# replace the process with SSH itself, run a specific set of commands,
# upload files, or even check if a host is up.
class SSH class SSH
include Vagrant::Util include Vagrant::Util
class << self # Reference back up to the environment which this SSH object belongs
def connect(opts={}) # to
options = {} attr_accessor :env
[:host, :username, :private_key_path].each do |param|
options[param] = opts[param] || Vagrant.config.ssh.send(param)
end
Kernel.exec "ssh -p #{port(opts)} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i #{options[:private_key_path]} #{options[:username]}@#{options[:host]}".strip def initialize(environment)
@env = environment
end
# Connects to the environment's virtual machine, replacing the ruby
# process with an SSH process. This method optionally takes a hash
# of options which override the configuration values.
def connect(opts={})
options = {}
[:host, :username, :private_key_path].each do |param|
options[param] = opts[param] || env.config.ssh.send(param)
end end
def execute(opts={}) Kernel.exec "ssh -p #{port(opts)} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i #{options[:private_key_path]} #{options[:username]}@#{options[:host]}".strip
Net::SSH.start(Vagrant.config.ssh.host, end
Vagrant.config[:ssh][:username],
opts.merge( :port => port,
:keys => [Vagrant.config.ssh.private_key_path])) do |ssh|
yield ssh
end
end
def upload!(from, to) # Opens an SSH connection to this environment's virtual machine and yields
execute do |ssh| # a Net::SSH object which can be used to execute remote commands.
scp = Net::SCP.new(ssh) def execute(opts={})
scp.upload!(from, to) Net::SSH.start(env.config.ssh.host,
end env.config[:ssh][:username],
opts.merge( :port => port,
:keys => [env.config.ssh.private_key_path])) do |ssh|
yield ssh
end end
end
def up? # Uploads a file from `from` to `to`. `from` is expected to be a filename
check_thread = Thread.new do # or StringIO, and `to` is expected to be a path. This method simply forwards
begin # the arguments to `Net::SCP#upload!` so view that for more information.
Thread.current[:result] = false def upload!(from, to)
execute(:timeout => Vagrant.config.ssh.timeout) do |ssh| execute do |ssh|
Thread.current[:result] = true scp = Net::SCP.new(ssh)
end scp.upload!(from, to)
rescue Errno::ECONNREFUSED, Net::SSH::Disconnect end
# False, its defaulted above end
# Checks if this environment's machine is up (i.e. responding to SSH).
#
# @return [Boolean]
def up?
check_thread = Thread.new do
begin
Thread.current[:result] = false
execute(:timeout => env.config.ssh.timeout) do |ssh|
Thread.current[:result] = true
end end
rescue Errno::ECONNREFUSED, Net::SSH::Disconnect
# False, its defaulted above
end end
check_thread.join(Vagrant.config.ssh.timeout)
return check_thread[:result]
rescue Net::SSH::AuthenticationFailed
error_and_exit(:vm_ssh_auth_failed)
end end
def port(opts={}) check_thread.join(env.config.ssh.timeout)
opts[:port] || Vagrant.config.vm.forwarded_ports[Vagrant.config.ssh.forwarded_port_key][:hostport] return check_thread[:result]
end rescue Net::SSH::AuthenticationFailed
error_and_exit(:vm_ssh_auth_failed)
end
# Returns the port which is either given in the options hash or taken from
# the config by finding it in the forwarded ports hash based on the
# `config.ssh.forwarded_port_key`
def port(opts={})
opts[:port] || env.config.vm.forwarded_ports[env.config.ssh.forwarded_port_key][:hostport]
end end
end end
end end

View File

@ -128,6 +128,7 @@ class EnvironmentTest < Test::Unit::TestCase
@env.expects(:load_config!).once.in_sequence(call_seq) @env.expects(:load_config!).once.in_sequence(call_seq)
Vagrant::Environment.expects(:check_virtualbox!).once.in_sequence(call_seq) Vagrant::Environment.expects(:check_virtualbox!).once.in_sequence(call_seq)
@env.expects(:load_vm!).once.in_sequence(call_seq) @env.expects(:load_vm!).once.in_sequence(call_seq)
@env.expects(:load_ssh!).once.in_sequence(call_seq)
assert_equal @env, @env.load! assert_equal @env, @env.load!
end end
end end
@ -368,6 +369,19 @@ class EnvironmentTest < Test::Unit::TestCase
assert_nil @env.vm assert_nil @env.vm
end end
end end
context "loading SSH" do
setup do
@env = mock_environment
end
should "initialize the SSH object with the given environment" do
ssh = mock("ssh")
Vagrant::SSH.expects(:new).with(@env).returns(ssh)
@env.load_ssh!
assert_equal ssh, @env.ssh
end
end
end end
context "requiring properties" do context "requiring properties" do

View File

@ -5,24 +5,35 @@ class SshTest < Test::Unit::TestCase
mock_config mock_config
end end
context "connecting to SSH" do def mock_ssh
test "should call exec with defaults when no options are supplied" do @env = mock_environment do |config|
ssh = Vagrant.config.ssh yield config if block_given?
ssh_exec_expect(Vagrant::SSH.port,
Vagrant.config.ssh.private_key_path,
Vagrant.config.ssh.username,
Vagrant.config.ssh.host)
Vagrant::SSH.connect
end end
test "should call exec with supplied params" do @ssh = Vagrant::SSH.new(@env)
end
context "connecting to external SSH" do
setup do
mock_ssh
end
should "call exec with defaults when no options are supplied" do
ssh_exec_expect(@ssh.port,
@env.config.ssh.private_key_path,
@env.config.ssh.username,
@env.config.ssh.host)
@ssh.connect
end
should "call exec with supplied params" do
args = {:username => 'bar', :private_key_path => 'baz', :host => 'bak', :port => 'bag'} args = {:username => 'bar', :private_key_path => 'baz', :host => 'bak', :port => 'bag'}
ssh_exec_expect(args[:port], args[:private_key_path], args[:username], args[:host]) ssh_exec_expect(args[:port], args[:private_key_path], args[:username], args[:host])
Vagrant::SSH.connect(args) @ssh.connect(args)
end end
def ssh_exec_expect(port, key_path, uname, host) def ssh_exec_expect(port, key_path, uname, host)
Kernel.expects(:exec).with() do |arg| Kernel.expects(:exec).with() do |arg|
assert arg =~ /^ssh/ assert arg =~ /^ssh/
assert arg =~ /-p #{port}/ assert arg =~ /-p #{port}/
assert arg =~ /-i #{key_path}/ assert arg =~ /-i #{key_path}/
@ -34,91 +45,103 @@ class SshTest < Test::Unit::TestCase
end end
context "executing ssh commands" do context "executing ssh commands" do
setup do
mock_ssh
end
should "call net::ssh.start with the proper names" do should "call net::ssh.start with the proper names" do
Net::SSH.expects(:start).once.with() do |host, username, opts| Net::SSH.expects(:start).once.with() do |host, username, opts|
assert_equal Vagrant.config.ssh.host, host assert_equal @env.config.ssh.host, host
assert_equal Vagrant.config.ssh.username, username assert_equal @env.config.ssh.username, username
assert_equal Vagrant::SSH.port, opts[:port] assert_equal @ssh.port, opts[:port]
assert_equal [Vagrant.config.ssh.private_key_path], opts[:keys] assert_equal [@env.config.ssh.private_key_path], opts[:keys]
true true
end end
Vagrant::SSH.execute @ssh.execute
end end
should "use custom host if set" do should "use custom host if set" do
Vagrant.config.ssh.host = "foo" @env.config.ssh.host = "foo"
Net::SSH.expects(:start).with(Vagrant.config.ssh.host, Vagrant.config.ssh.username, anything).once Net::SSH.expects(:start).with(@env.config.ssh.host, @env.config.ssh.username, anything).once
Vagrant::SSH.execute @ssh.execute
end end
end end
context "SCPing files to the remote host" do context "SCPing files to the remote host" do
setup do
mock_ssh
end
should "use Vagrant::SSH execute to setup an SCP connection and upload" do should "use Vagrant::SSH execute to setup an SCP connection and upload" do
scp = mock("scp") scp = mock("scp")
ssh = mock("ssh") ssh = mock("ssh")
scp.expects(:upload!).with("foo", "bar").once scp.expects(:upload!).with("foo", "bar").once
Net::SCP.expects(:new).with(ssh).returns(scp).once Net::SCP.expects(:new).with(ssh).returns(scp).once
Vagrant::SSH.expects(:execute).yields(ssh).once @ssh.expects(:execute).yields(ssh).once
Vagrant::SSH.upload!("foo", "bar") @ssh.upload!("foo", "bar")
end end
end end
context "checking if host is up" do context "checking if host is up" do
setup do setup do
mock_config mock_ssh
end end
should "return true if SSH connection works" do should "return true if SSH connection works" do
Net::SSH.expects(:start).yields("success") Net::SSH.expects(:start).yields("success")
assert Vagrant::SSH.up? assert @ssh.up?
end end
should "return false if SSH connection times out" do should "return false if SSH connection times out" do
Net::SSH.expects(:start) Net::SSH.expects(:start)
assert !Vagrant::SSH.up? assert !@ssh.up?
end end
should "allow the thread the configured timeout time" do should "allow the thread the configured timeout time" do
@thread = mock("thread") @thread = mock("thread")
@thread.stubs(:[]) @thread.stubs(:[])
Thread.expects(:new).returns(@thread) Thread.expects(:new).returns(@thread)
@thread.expects(:join).with(Vagrant.config.ssh.timeout).once @thread.expects(:join).with(@env.config.ssh.timeout).once
Vagrant::SSH.up? @ssh.up?
end end
should "return false if the connection is refused" do should "return false if the connection is refused" do
Net::SSH.expects(:start).raises(Errno::ECONNREFUSED) Net::SSH.expects(:start).raises(Errno::ECONNREFUSED)
assert_nothing_raised { assert_nothing_raised {
assert !Vagrant::SSH.up? assert !@ssh.up?
} }
end end
should "return false if the connection is dropped" do should "return false if the connection is dropped" do
Net::SSH.expects(:start).raises(Net::SSH::Disconnect) Net::SSH.expects(:start).raises(Net::SSH::Disconnect)
assert_nothing_raised { assert_nothing_raised {
assert !Vagrant::SSH.up? assert !@ssh.up?
} }
end end
should "specifity the timeout as an option to execute" do should "specifity the timeout as an option to execute" do
Vagrant::SSH.expects(:execute).with(:timeout => Vagrant.config.ssh.timeout).yields(true) @ssh.expects(:execute).with(:timeout => @env.config.ssh.timeout).yields(true)
assert Vagrant::SSH.up? assert @ssh.up?
end end
should "error and exit if a Net::SSH::AuthenticationFailed is raised" do should "error and exit if a Net::SSH::AuthenticationFailed is raised" do
Vagrant::SSH.expects(:execute).raises(Net::SSH::AuthenticationFailed) @ssh.expects(:execute).raises(Net::SSH::AuthenticationFailed)
Vagrant::SSH.expects(:error_and_exit).with(:vm_ssh_auth_failed).once @ssh.expects(:error_and_exit).with(:vm_ssh_auth_failed).once
Vagrant::SSH.up? @ssh.up?
end end
end end
context "getting the ssh port" do context "getting the ssh port" do
setup do
mock_ssh
end
should "return the configured port by default" do should "return the configured port by default" do
assert_equal Vagrant.config.vm.forwarded_ports[Vagrant.config.ssh.forwarded_port_key][:hostport], Vagrant::SSH.port assert_equal @env.config.vm.forwarded_ports[@env.config.ssh.forwarded_port_key][:hostport], @ssh.port
end end
should "return the port given in options if it exists" do should "return the port given in options if it exists" do
assert_equal "47", Vagrant::SSH.port({ :port => "47" }) assert_equal "47", @ssh.port({ :port => "47" })
end end
end end
end end