`vagrant up`

This commit is contained in:
Mitchell Hashimoto 2011-12-17 09:14:05 -08:00
parent 7191a54ed7
commit 43cadfe830
9 changed files with 212 additions and 54 deletions

View File

@ -46,6 +46,14 @@ module Vagrant
@source_root ||= Pathname.new(File.expand_path('../../', __FILE__))
end
# Global registry of commands that are available via the CLI.
#
# This registry is used to look up the sub-commands that are available
# to Vagrant.
def self.commands
@commands ||= Registry.new
end
# Global registry of config keys that are available.
#
# This registry is used to look up the keys for `config` objects.
@ -85,7 +93,10 @@ end
# # Default I18n to load the en locale
I18n.load_path << File.expand_path("templates/locales/en.yml", Vagrant.source_root)
# Registry the build-in config keys
# Register the built-in commands
Vagrant.commands.register(:up) { Vagrant::Command::Up }
# Register the built-in config keys
Vagrant.config_keys.register(:vagrant) { Vagrant::Config::VagrantConfig }
Vagrant.config_keys.register(:ssh) { Vagrant::Config::SSHConfig }
Vagrant.config_keys.register(:nfs) { Vagrant::Config::NFSConfig }

View File

@ -1,11 +1,16 @@
require 'log4r'
require 'optparse'
module Vagrant
# Manages the command line interface to Vagrant.
class CLI < Command::Base
def initialize(argv, env)
@env = env
super
@logger = Log4r::Logger.new("vagrant::cli")
@main_args, @sub_command, @sub_args = split_main_and_subcommand(argv)
@logger.info("CLI: #{@main_args.inspect} #{@sub_command.inspect} #{@sub_args.inspect}")
end
def execute
@ -22,9 +27,18 @@ module Vagrant
# the help and exit.
return help
end
# If we reached this far then we must have a subcommand. If not,
# then we also just print the help and exit.
command_class = Vagrant.commands.get(@sub_command.to_sym)
return help if !command_class || !@sub_command
@logger.debug("Invoking command class: #{command_class} #{@sub_args.inspect}")
# Initialize and execute the command class.
command_class.new(@sub_args, @env).execute
end
# This prints the help for the CLI out.
# This prints out the help for the CLI.
def help
# We use the optionparser for this. Its just easier. We don't use
# an optionparser above because I don't think the performance hits

View File

@ -1,5 +1,7 @@
module Vagrant
module Command
autoload :Base, 'vagrant/command/base'
autoload :Up, 'vagrant/command/up'
end
end

View File

@ -1,8 +1,63 @@
require 'log4r'
module Vagrant
module Command
# Base class for any CLI commands.
#
# This class provides documentation on the interface as well as helper
# functions that a command has.
class Base
def initialize(argv, env)
@argv = argv
@env = env
@logger = Log4r::Logger.new("vagrant::command::#{self.class.to_s.downcase}")
end
# This is what is called on the class to actually execute it. Any
# subclasses should implement this method and do any option parsing
# and validation here.
def execute; end
protected
# Parses the options given an OptionParser instance.
#
# This is a convenience method that properly handles duping the
# originally argv array so that it is not destroyed. That is all.
def parse_options(opts)
argv = @argv.dup
opts.parse!(argv)
return argv
end
# Yields a VM for each target VM for the command.
#
# This is a convenience method for easily implementing methods that
# take a target VM (in the case of multi-VM) or every VM if no
# specific VM name is specified.
#
# @param [String] name The name of the VM. Nil if every VM.
def with_target_vms(name=nil)
# First determine the proper array of VMs.
vms = []
if name
raise Errors::MultiVMEnvironmentRequired if !@env.multivm?
vms << @env.vms[name.to_sym]
raise Errors::VMNotFoundError, :name => name if !vms[0]
else
vms = @env.vms_ordered
end
# Go through each VM and yield it!
vms.each do |old_vm|
# We get a new VM from the environment here to avoid potentially
# stale VMs (if there was a config reload on the environment
# or something).
vm = @env.vms[old_vm.name]
yield vm
end
end
# This method will split the argv given into three parts: the
# flags to this command, the subcommand, and the flags to the
# subcommand. For example:
@ -36,6 +91,10 @@ module Vagrant
main_args = argv[0, i]
sub_command = argv[i]
sub_args = argv[i + 1, argv.length - i + 1]
# Break so we don't find the next non flag and shift our
# main args.
break
end
end

View File

@ -1,43 +0,0 @@
module Vagrant
module Command
module Helpers
# Initializes the environment by pulling the environment out of
# the configuration hash and sets up the UI if necessary.
def initialize_environment(args, options, config)
raise Errors::CLIMissingEnvironment if !config[:env]
@env = config[:env]
end
# This returns an array of {VM} objects depending on the arguments
# given to the command.
def target_vms(name=nil)
raise Errors::NoEnvironmentError if !env.root_path
name ||= self.name rescue nil
@target_vms ||= begin
if env.multivm?
return env.vms_ordered if !name
vm = env.vms[name.to_sym]
raise Errors::VMNotFoundError, :name => name if !vm
else
raise Errors::MultiVMEnvironmentRequired if name
vm = env.vms.values.first
end
[vm]
end
end
# This will yield for each target VM to the command. The VM is guaranteed
# to be loaded on each iteration.
def with_target_vms
target_vms.each do |old_vm|
# We get a new VM here to avoid potentially stale VMs
vm = env.vms[old_vm.name]
yield vm
end
end
end
end
end

View File

@ -1,15 +1,38 @@
require 'optparse'
module Vagrant
module Command
class UpCommand < NamedBase
class_option :provision, :type => :boolean, :default => true
register "up", "Creates the Vagrant environment"
class Up < Base
def execute
with_target_vms do |vm|
options = {}
opts = OptionParser.new do |opts|
opts.banner = "Usage: vagrant up [vm-name] [--[no-]provision] [-h]"
opts.separator ""
opts.on("--[no-]provision", "Enable or disable provisioning") do |p|
options[:provision] = p
end
opts.on("-h", "--help", "Print this help") do
puts opts.help
return
end
end
# Parse the options
argv = parse_options(opts)
# Go over each VM and bring it up
@logger.debug("'Up' each target VM...")
with_target_vms(argv[0]) do |vm|
if vm.created?
vm.env.ui.info I18n.t("vagrant.commands.up.vm_created")
@logger.info("Booting: #{vm.name}")
vm.ui.info I18n.t("vagrant.commands.up.vm_created")
vm.start("provision.enabled" => options[:provision])
else
@logger.info("Creating: #{vm.name}")
vm.up("provision.enabled" => options[:provision])
end
end

View File

@ -139,6 +139,7 @@ module Vagrant
#
# @return [Array<VM>]
def vms_ordered
return @vms.values if !multivm?
@vms_enum ||= config.global.vm.defined_vm_keys.map { |name| @vms[name] }
end

View File

@ -1,12 +1,94 @@
require File.expand_path("../../../base", __FILE__)
require 'optparse'
describe Vagrant::Command::Base do
describe "parsing options" do
let(:klass) do
Class.new(described_class) do
# Make the method public since it is normally protected
public :parse_options
end
end
it "returns the remaining arguments" do
options = {}
opts = OptionParser.new do |opts|
opts.on("-f") do |f|
options[:f] = f
end
end
result = klass.new(["-f", "foo"], nil).parse_options(opts)
# Check the results
options[:f].should be
result.should == ["foo"]
end
end
describe "target VMs" do
let(:klass) do
Class.new(described_class) do
# Make the method public since it is normally protected
public :with_target_vms
end
end
let(:environment) { double("environment") }
let(:instance) { klass.new([], environment) }
it "should raise an exception if a name is given in a non-multivm environment" do
environment.stub(:multivm?).and_return(false)
expect { instance.with_target_vms("foo") }.
to raise_error(Vagrant::Errors::MultiVMEnvironmentRequired)
end
it "should yield every VM in order is no name is given" do
foo_vm = double("foo")
foo_vm.stub(:name).and_return("foo")
bar_vm = double("bar")
bar_vm.stub(:name).and_return("bar")
environment.stub(:multivm? => true,
:vms => { "foo" => foo_vm, "bar" => bar_vm },
:vms_ordered => [foo_vm, bar_vm])
vms = []
instance.with_target_vms do |vm|
vms << vm
end
vms.should == [foo_vm, bar_vm]
end
it "raises an exception if the named VM doesn't exist" do
environment.stub(:multivm? => true, :vms => {})
expect { instance.with_target_vms("foo") }.
to raise_error(Vagrant::Errors::VMNotFoundError)
end
it "yields the given VM if a name is given" do
foo_vm = double("foo")
foo_vm.stub(:name).and_return(:foo)
environment.stub(:multivm? => true,
:vms => { :foo => foo_vm, :bar => nil })
vms = []
instance.with_target_vms("foo") { |vm| vms << vm }
vms.should == [foo_vm]
end
end
describe "splitting the main and subcommand args" do
let(:instance) do
Class.new(described_class) do
# Make the method public since it is normal protected
# Make the method public since it is normally protected
public :split_main_and_subcommand
end.new
end.new(nil, nil)
end
it "should work when given all 3 parts" do
@ -28,5 +110,10 @@ describe Vagrant::Command::Base do
result = instance.split_main_and_subcommand(["status"])
result.should == [[], "status", []]
end
it "works when there are other non-flag args after the subcommand" do
result = instance.split_main_and_subcommand(["-v", "box", "add", "-h"])
result.should == [["-v"], "box", ["add", "-h"]]
end
end
end

View File

@ -5,6 +5,10 @@ describe Vagrant do
described_class.source_root.should == Pathname.new(File.expand_path("../../../", __FILE__))
end
it "has a registry for commands" do
described_class.commands.should be_a(Vagrant::Registry)
end
it "has a registry for config keys" do
described_class.config_keys.should be_a(Vagrant::Registry)
end