diff --git a/lib/vagrant/easy_command.rb b/lib/vagrant/easy_command.rb index 5c380da44..17f264f51 100644 --- a/lib/vagrant/easy_command.rb +++ b/lib/vagrant/easy_command.rb @@ -1,7 +1,8 @@ -require "log4r" - module Vagrant - class EasyCommand + module EasyCommand + autoload :Base, "vagrant/easy_command/base" + autoload :Operations, "vagrant/easy_command/operations" + # This creates a new easy command. This typically is not called # directly. Instead, the plugin interface's `easy_command` is # used to create one of these. @@ -11,47 +12,5 @@ module Vagrant command.configure(name, &block) command end - - # Base class for all easy commands. This contains the basic code - # that knows how to run the easy commands. - class Base < Vagrant::Command::Base - @@command = nil - @@runner = nil - - # This is called by the {EasyCommand.create} method when creating - # an easy command to set the invocation command. - def self.configure(name, &block) - @@command = name - @@runner = block - end - - def initialize(*args, &block) - super - - @logger = Log4r::Logger.new("vagrant::easy_command::#{@@command}") - end - - def execute - # Build up a basic little option parser - opts = OptionParser.new do |opts| - opts.banner = "Usage: vagrant #{@@command}" - end - - # Parse the options - argv = parse_options(opts) - return if !argv - - # Run the action for each VM. - @logger.info("Running easy command: #{@@command}") - with_target_vms(argv) do |vm| - @logger.debug("Running easy command for VM: #{vm.name}") - - @@runner.call(nil) - end - - # Exit status 0 every time for now - 0 - end - end end end diff --git a/lib/vagrant/easy_command/base.rb b/lib/vagrant/easy_command/base.rb new file mode 100644 index 000000000..61ce29aa6 --- /dev/null +++ b/lib/vagrant/easy_command/base.rb @@ -0,0 +1,46 @@ +require "log4r" + +module Vagrant + module EasyCommand + # Base class for all easy commands. This contains the basic code + # that knows how to run the easy commands. + class Base < Vagrant::Command::Base + @@command = nil + @@runner = nil + + # This is called by the {EasyCommand.create} method when creating + # an easy command to set the invocation command. + def self.configure(name, &block) + @@command = name + @@runner = block + end + + def initialize(*args, &block) + super + + @logger = Log4r::Logger.new("vagrant::easy_command::#{@@command}") + end + + def execute + # Build up a basic little option parser + opts = OptionParser.new do |opts| + opts.banner = "Usage: vagrant #{@@command}" + end + + # Parse the options + argv = parse_options(opts) + return if !argv + + # Run the action for each VM. + @logger.info("Running easy command: #{@@command}") + with_target_vms(argv) do |vm| + @logger.debug("Running easy command for VM: #{vm.name}") + @@runner.call(Operations.new(vm)) + end + + # Exit status 0 every time for now + 0 + end + end + end +end diff --git a/lib/vagrant/easy_command/operations.rb b/lib/vagrant/easy_command/operations.rb new file mode 100644 index 000000000..efc5a11a6 --- /dev/null +++ b/lib/vagrant/easy_command/operations.rb @@ -0,0 +1,86 @@ +require "ostruct" + +require "log4r" + +require "vagrant/util/subprocess" + +module Vagrant + module EasyCommand + # This class contains all the "operations" that easy commands are able + # to run. An instance of this is class is what is sent into the callback + # for all easy commands. + class Operations + def initialize(vm) + @logger = Log4r::Logger.new("vagrant::easy_command::operations") + @vm = vm + end + + # Runs a command on the local machine. This will return an object where + # you can access the `exit_code`, `stdout`, and `stderr` easiy: + # + # output = local("echo foo") + # puts "Output was #{output.stdout}" + # + # (Likewise, `exit_code` and `stderr` are attributes on the return value) + # + # It is recommended you use this `local` method rather than trying to + # manually use Ruby's underlying subprocess tools because this will use + # the Vagrant `Subprocess` class which has been refined over the years + # to work equally well on Windows, Mac OS X, Linux as well as on many + # runtimes such as CRuby and JRuby. + # + # @param [String] command Command to run + def local(command) + @logger.info("local: #{command}") + Vagrant::Util::Subprocess.execute(command) + end + + # Run a shell command within the VM. The command will run within a + # shell environment, and the output and exit code will be returned + # as an object with attributes: `exit_code, `stdout`, and `stderr`. + # Example: + # + # output = run("echo foo") + # puts "Output was #{output.stdout}" + # + # @param [String] command Command to run + def run(command) + @logger.info("run: #{command}") + remote_command(:execute, command) + end + + # Same as {run} except runs the command with superuser privileges + # via `sudo`. + # + # @param [String] command Command + def sudo(command) + @logger.info("sudo: #{command}") + remote_command(:sudo, command) + end + + protected + + # Runs a command on the remote host. + def remote_command(type, command) + # If the VM is not running, then we can't run SSH commands... + raise Errors::VMNotRunningError if @vm.state != :running + + # Initialize the result object, execute, and store the data + result = OpenStruct.new + result.stderr = "" + result.stdout = "" + result.exit_code = @vm.channel.send(type, command, + :error_check => false) do |type, data| + if type == :stdout + result.stdout += data + elsif type == :stderr + result.stderr += data + end + end + + # Return the result + result + end + end + end +end