Add the Communicator plugin API.

This allows communication protocols to be defined for the machine. This
is how things like SSH will be implemented.
This commit is contained in:
Mitchell Hashimoto 2012-08-08 21:28:28 -07:00
parent f1c1dfad2f
commit a1cef830e3
6 changed files with 129 additions and 8 deletions

View File

@ -85,14 +85,16 @@ module Vagrant
# These are the various plugin versions and their components in
# a lazy loaded Hash-like structure.
c = PLUGIN_COMPONENTS = Registry.new
c.register(:"1") { Plugin::V1::Plugin }
c.register([:"1", :command]) { Plugin::V1::Command }
c.register([:"1", :config]) { Plugin::V1::Config }
c.register([:"1", :guest]) { Plugin::V1::Guest }
c.register([:"1", :host]) { Plugin::V1::Host }
c.register([:"1", :provider]) { Plugin::V1::Provider }
c.register([:"1", :provisioner]) { Plugin::V1::Provisioner }
PLUGIN_COMPONENTS = Registry.new.tap do |c|
c.register(:"1") { Plugin::V1::Plugin }
c.register([:"1", :command]) { Plugin::V1::Command }
c.register([:"1", :communicator]) { Plugin::V1::Communicator }
c.register([:"1", :config]) { Plugin::V1::Config }
c.register([:"1", :guest]) { Plugin::V1::Guest }
c.register([:"1", :host]) { Plugin::V1::Host }
c.register([:"1", :provider]) { Plugin::V1::Provider }
c.register([:"1", :provisioner]) { Plugin::V1::Provisioner }
end
# The source root is the path to the root directory of
# the Vagrant gem.

View File

@ -92,6 +92,25 @@ module Vagrant
@env.action_runner.run(callable, env)
end
# Returns a communication object for executing commands on the remote
# machine. Note that the _exact_ semantics of this are up to the
# communication provider itself. Despite this, the semantics are expected
# to be consistent across operating systems. For example, all linux-based
# systems should have similar communication (usually a shell). All
# Windows systems should have similar communication as well. Therefore,
# prior to communicating with the machine, users of this method are
# expected to check the guest OS to determine their behavior.
#
# This method will _always_ return some valid communication object.
# The `ready?` API can be used on the object to check if communication
# is actually ready.
#
# @return [Object]
def communicate
# For now, we always return SSH. In the future, we'll abstract
# this and allow plugins to define new methods of communication.
end
# This sets the unique ID associated with this machine. This will
# persist this ID so that in the future Vagrant will be able to find
# this machine again. The unique ID must be absolutely unique to the

View File

@ -6,6 +6,7 @@ module Vagrant
module Plugin
module V1
autoload :Command, "vagrant/plugin/v1/command"
autoload :Communicator, "vagrant/plugin/v1/communicator"
autoload :Config, "vagrant/plugin/v1/config"
autoload :Guest, "vagrant/plugin/v1/guest"
autoload :Host, "vagrant/plugin/v1/host"

View File

@ -0,0 +1,89 @@
module Vagrant
module Plugin
module V1
# Base class for a communicator in Vagrant. A communicator is
# responsible for communicating with a machine in some way. There
# are various stages of Vagrant that require things such as uploading
# files to the machine, executing shell commands, etc. Implementors
# of this class are expected to provide this functionality in some
# way.
#
# Note that a communicator must provide **all** of the methods
# in this base class. There is currently no way for one communicator
# to provide say a more efficient way of uploading a file, but not
# provide shell execution. This sort of thing will come in a future
# version.
class Communicator
# This returns true/false depending on if the given machine
# can be communicated with using this communicator. If this returns
# `true`, then this class will be used as the primary communication
# method for the machine.
#
# @return [Boolean]
def self.match?(machine)
false
end
# Initializes the communicator with the machine that we will be
# communicating with. This base method does nothing (it doesn't
# even store the machine in an instance variable for you), so you're
# expected to override this and do something with the machine if
# you care about it.
#
# @param [Machine] machine The machine this instance is expected to
# communicate with.
def initialize(machine)
end
# Checks if the target machine is ready for communication. If this
# returns true, then all the other methods for communicating with
# the machine are expected to be functional.
#
# @return [Boolean]
def ready?
false
end
# Download a file from the remote machine to the local machine.
#
# @param [String] from Path of the file on the remote machine.
# @param [String] to Path of where to save the file locally.
def download(from, to)
end
# Upload a file to the remote machine.
#
# @param [String] from Path of the file locally to upload.
# @param [String] to Path of where to save the file on the remote
# machine.
def upload(from, to)
end
# Execute a command on the remote machine. The exact semantics
# of this method are up to the implementor, but in general the
# users of this class will expect this to be a shell.
#
# This method gives you no way to write data back to the remote
# machine, so only execute commands that don't expect input.
#
# @param [String] command Command to execute.
# @yield [type, data] Realtime output of the command being executed.
# @yieldparam [String] type Type of the output. This can be
# `:stdout`, `:stderr`, etc. The exact types are up to the
# implementor.
# @yieldparam [String] data Data for the given output.
# @return [Integer] Exit code of the command.
def execute(command, opts=nil)
end
# Executes a command on the remote machine with administrative
# privileges. See {#execute} for documentation, as the API is the
# same.
#
# @see #execute
def sudo(command, opts=nil)
end
end
end
end
end

View File

@ -0,0 +1,9 @@
require File.expand_path("../../../../base", __FILE__)
describe Vagrant::Plugin::V1::Communicator do
let(:machine) { Object.new }
it "should not match by default" do
described_class.match?(machine).should_not be
end
end

View File

@ -13,6 +13,7 @@ describe Vagrant do
it "returns the proper components for version 1" do
described_class.plugin("1", :command).should == Vagrant::Plugin::V1::Command
described_class.plugin("1", :communicator).should == Vagrant::Plugin::V1::Communicator
described_class.plugin("1", :config).should == Vagrant::Plugin::V1::Config
described_class.plugin("1", :guest).should == Vagrant::Plugin::V1::Guest
described_class.plugin("1", :host).should == Vagrant::Plugin::V1::Host