From 595e7cee0e61a8ea1ad0c97873f4f54b9af6c169 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 8 Aug 2012 21:48:51 -0700 Subject: [PATCH] Move SSH communication to a plugin --- lib/vagrant.rb | 1 - lib/vagrant/communication.rb | 7 --- lib/vagrant/communication/base.rb | 63 ------------------- .../communicators/ssh/communicator.rb | 51 ++++++++------- plugins/communicators/ssh/plugin.rb | 19 ++++++ 5 files changed, 48 insertions(+), 93 deletions(-) delete mode 100644 lib/vagrant/communication.rb delete mode 100644 lib/vagrant/communication/base.rb rename lib/vagrant/communication/ssh.rb => plugins/communicators/ssh/communicator.rb (85%) create mode 100644 plugins/communicators/ssh/plugin.rb diff --git a/lib/vagrant.rb b/lib/vagrant.rb index 550df06b1..1e160ab47 100644 --- a/lib/vagrant.rb +++ b/lib/vagrant.rb @@ -66,7 +66,6 @@ module Vagrant autoload :BoxCollection, 'vagrant/box_collection' autoload :CLI, 'vagrant/cli' autoload :Command, 'vagrant/command' - autoload :Communication, 'vagrant/communication' autoload :Config, 'vagrant/config' autoload :DataStore, 'vagrant/data_store' autoload :Downloaders, 'vagrant/downloaders' diff --git a/lib/vagrant/communication.rb b/lib/vagrant/communication.rb deleted file mode 100644 index cb9795b71..000000000 --- a/lib/vagrant/communication.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Vagrant - module Communication - autoload :Base, 'vagrant/communication/base' - - autoload :SSH, 'vagrant/communication/ssh' - end -end diff --git a/lib/vagrant/communication/base.rb b/lib/vagrant/communication/base.rb deleted file mode 100644 index b16d2626f..000000000 --- a/lib/vagrant/communication/base.rb +++ /dev/null @@ -1,63 +0,0 @@ -module Vagrant - module Communication - # The base class for any classes that provide an API for communicating - # with the virtual machine. - # - # There are various stages that require Vagrant to copy files or - # run commands on the target system, and communication classes provide - # the abstraction necessary to perform these tasks, via SSH or some - # other mechanism. - # - # Any subclasses of this class **must** implement all of the methods - # below. - class Base - # Checks if the target machine is ready for communication. - # - # @return [Boolean] - def ready? - end - - # Download a file from the virtual machine to the local machine. - # - # @param [String] from Path of the file on the virtual machine. - # @param [String] to Path to where to save the remote file. - def download(from, to) - end - - # Upload a file to the virtual machine. - # - # @param [String] from Path to a file to upload. - # @param [String] to Path to where to save this file. - def upload(from, to) - end - - # Execute a command on the remote machine. - # - # @param [String] command Command to execute. - # @yield [type, data] Realtime output of the command being executed. - # @yieldparam [String] type Type of the output, `:stdout`, `:stderr`, etc. - # @yieldparam [String] data Data for the given output. - # @return [Integer] Exit code of the command. - def execute(command, opts=nil) - end - - # Execute a comand with super user privileges. - # - # See #execute for parameter information. - def sudo(command, opts=nil) - end - - # Executes a command and returns a boolean statement if it was successful - # or not. - # - # This is implemented by default as expecting `execute` to return 0. - def test(command, opts=nil) - # Disable error checking no matter what - opts = (opts || {}).merge(:error_check => false) - - # Successful if the exit status is 0 - execute(command, opts) == 0 - end - end - end -end diff --git a/lib/vagrant/communication/ssh.rb b/plugins/communicators/ssh/communicator.rb similarity index 85% rename from lib/vagrant/communication/ssh.rb rename to plugins/communicators/ssh/communicator.rb index 353260547..1065168a5 100644 --- a/lib/vagrant/communication/ssh.rb +++ b/plugins/communicators/ssh/communicator.rb @@ -8,17 +8,23 @@ require 'vagrant/util/ansi_escape_code_remover' require 'vagrant/util/file_mode' require 'vagrant/util/platform' require 'vagrant/util/retryable' +require 'vagrant/util/ssh' -module Vagrant - module Communication - # Provides communication with the VM via SSH. - class SSH < Base +module VagrantPlugins + module CommunicatorSSH + # This class provides communication with the VM via SSH. + class Communicator < Vagrant.plugin("1", :communicator) include Util::ANSIEscapeCodeRemover include Util::Retryable - def initialize(vm) - @vm = vm - @logger = Log4r::Logger.new("vagrant::communication::ssh") + def self.match?(machine) + # All machines are currently expected to have SSH. + true + end + + def initialize(machine) + @machine = machine + @logger = Log4r::Logger.new("vagrant::communication::ssh") @connection = nil end @@ -31,7 +37,7 @@ module Vagrant # If we reached this point then we successfully connected @logger.info("SSH is ready!") true - rescue Errors::VagrantError => e + rescue Vagrant::Errors::VagrantError => e # We catch a `VagrantError` which would signal that something went # wrong expectedly in the `connect`, which means we didn't connect. @logger.info("SSH not up: #{e.inspect}") @@ -41,7 +47,7 @@ module Vagrant def execute(command, opts=nil, &block) opts = { :error_check => true, - :error_class => Errors::VagrantError, + :error_class => Vagrant::Errors::VagrantError, :error_key => :ssh_bad_exit_status, :command => command, :sudo => false @@ -93,7 +99,7 @@ module Vagrant # Otherwise, it is a permission denied, so let's raise a proper # exception - raise Errors::SCPPermissionDenied, :path => from.to_s + raise Vagrant::Errors::SCPPermissionDenied, :path => from.to_s end protected @@ -120,7 +126,8 @@ module Vagrant end end - ssh_info = @vm.ssh.info + # XXX: We need to raise some exception if SSH is not ready + ssh_info = @machine.ssh_info # Build the options we'll use to initiate the connection via Net::SSH opts = { @@ -134,38 +141,38 @@ module Vagrant } # Check that the private key permissions are valid - @vm.ssh.check_key_permissions(ssh_info[:private_key_path]) + Vagrant::Util::SSH.check_key_permissions(ssh_info[:private_key_path]) # Connect to SSH, giving it a few tries connection = nil begin exceptions = [Errno::ECONNREFUSED, Net::SSH::Disconnect, Timeout::Error] - connection = retryable(:tries => @vm.config.ssh.max_tries, :on => exceptions) do - Timeout.timeout(@vm.config.ssh.timeout) do + connection = retryable(:tries => @machine.config.ssh.max_tries, :on => exceptions) do + Timeout.timeout(@machine.config.ssh.timeout) do @logger.info("Attempting to connect to SSH: #{ssh_info[:host]}:#{ssh_info[:port]}") Net::SSH.start(ssh_info[:host], ssh_info[:username], opts) end end rescue Timeout::Error # This happens if we continued to timeout when attempting to connect. - raise Errors::SSHConnectionTimeout + raise Vagrant::Errors::SSHConnectionTimeout rescue Net::SSH::AuthenticationFailed # This happens if authentication failed. We wrap the error in our # own exception. - raise Errors::SSHAuthenticationFailed + raise Vagrant::Errors::SSHAuthenticationFailed rescue Net::SSH::Disconnect # This happens if the remote server unexpectedly closes the # connection. This is usually raised when SSH is running on the # other side but can't properly setup a connection. This is # usually a server-side issue. - raise Errors::SSHDisconnected + raise Vagrant::Errors::SSHDisconnected rescue Errno::ECONNREFUSED # This is raised if we failed to connect the max amount of times - raise Errors::SSHConnectionRefused + raise Vagrant::Errors::SSHConnectionRefused rescue NotImplementedError # This is raised if a private key type that Net-SSH doesn't support # is used. Show a nicer error. - raise Errors::SSHKeyTypeNotSupported + raise Vagrant::Errors::SSHKeyTypeNotSupported end @connection = connection @@ -178,7 +185,7 @@ module Vagrant # Yield the connection that is ready to be used and # return the value of the block return yield connection if block_given? - end + end # Executes the command on an SSH connection within a login shell. def shell_execute(connection, command, sudo=false) @@ -187,7 +194,7 @@ module Vagrant # Determine the shell to execute. If we are using `sudo` then we # need to wrap the shell in a `sudo` call. - shell = @vm.config.ssh.shell + shell = @machine.config.ssh.shell shell = "sudo -H #{shell}" if sudo # Open the channel so we can execute or command @@ -248,7 +255,7 @@ module Vagrant end rescue Net::SCP::Error => e # If we get the exit code of 127, then this means SCP is unavailable. - raise Errors::SCPUnavailable if e.message =~ /\(127\)/ + raise Vagrant::Errors::SCPUnavailable if e.message =~ /\(127\)/ # Otherwise, just raise the error up raise diff --git a/plugins/communicators/ssh/plugin.rb b/plugins/communicators/ssh/plugin.rb new file mode 100644 index 000000000..10de987a4 --- /dev/null +++ b/plugins/communicators/ssh/plugin.rb @@ -0,0 +1,19 @@ +require "vagrant" + +module VagrantPlugins + module CommunicatorSSH + class Plugin < Vagrant.plugin("1") + name "ssh communiator" + description <<-DESC + This plugin allows Vagrant to communicate with remote machines using + SSH as the underlying protocol, powered internally by Ruby's + net-ssh library. + DESC + + communicator("ssh") do + require File.expand_path("../communicator", __FILE__) + Communicator + end + end + end +end