From 7419563b807d51c1880eda91b5c1b181b6128f6c Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 7 May 2010 21:19:11 -0700 Subject: [PATCH 01/26] Added `config.vm.define` which will be used for multi-VMs --- lib/vagrant/config.rb | 8 ++++++++ test/vagrant/config_test.rb | 25 +++++++++++++++++++------ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/lib/vagrant/config.rb b/lib/vagrant/config.rb index cdb3b91af..2d72265e5 100644 --- a/lib/vagrant/config.rb +++ b/lib/vagrant/config.rb @@ -125,6 +125,14 @@ module Vagrant def customize(&block) push_proc(&block) end + + def defined_vms + @defined_vms ||= {} + end + + def define(name, &block) + defined_vms[name.to_sym] = block + end end class PackageConfig < Base diff --git a/test/vagrant/config_test.rb b/test/vagrant/config_test.rb index ac2a1a7e4..3a2d4f340 100644 --- a/test/vagrant/config_test.rb +++ b/test/vagrant/config_test.rb @@ -229,14 +229,27 @@ class ConfigTest < Test::Unit::TestCase @env.config.ssh.username = @username end - should "include the stacked proc runner module" do - assert @config.class.included_modules.include?(Vagrant::Util::StackedProcRunner) + context "defining VMs" do + should "store the proc by name but not run it" do + foo = mock("proc") + foo.expects(:call).never + + proc = Proc.new { foo.call } + @config.define(:name, &proc) + assert_equal proc, @config.defined_vms[:name] + end end - should "add the customize proc to the proc stack" do - proc = Proc.new {} - @config.customize(&proc) - assert_equal [proc], @config.proc_stack + context "customizing" do + should "include the stacked proc runner module" do + assert @config.class.included_modules.include?(Vagrant::Util::StackedProcRunner) + end + + should "add the customize proc to the proc stack" do + proc = Proc.new {} + @config.customize(&proc) + assert_equal [proc], @config.proc_stack + end end context "uid/gid" do From 2e547bda26f555d52413be46ba20f1d7b293f173 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 7 May 2010 21:40:32 -0700 Subject: [PATCH 02/26] SSH object is now on the VM, rather than the environment --- lib/vagrant/actions/vm/boot.rb | 2 +- lib/vagrant/provisioners/base.rb | 16 ++++++++++---- lib/vagrant/provisioners/chef.rb | 6 ++--- lib/vagrant/provisioners/chef_server.rb | 2 +- lib/vagrant/systems/linux.rb | 2 +- lib/vagrant/vm.rb | 18 +++++++++++---- test/test_helper.rb | 13 +++++++++++ test/vagrant/actions/vm/boot_test.rb | 6 ++--- test/vagrant/provisioners/base_test.rb | 8 +++---- test/vagrant/provisioners/chef_server_test.rb | 5 +++-- test/vagrant/provisioners/chef_solo_test.rb | 5 +++-- test/vagrant/provisioners/chef_test.rb | 15 +++++++------ test/vagrant/systems/linux_test.rb | 5 ++--- test/vagrant/vm_test.rb | 22 +++++++++++++++++++ 14 files changed, 90 insertions(+), 35 deletions(-) diff --git a/lib/vagrant/actions/vm/boot.rb b/lib/vagrant/actions/vm/boot.rb index 4832660d4..d7f035bb3 100644 --- a/lib/vagrant/actions/vm/boot.rb +++ b/lib/vagrant/actions/vm/boot.rb @@ -30,7 +30,7 @@ module Vagrant @runner.env.config.ssh.max_tries.to_i.times do |i| logger.info "Trying to connect (attempt ##{i+1} of #{@runner.env.config[:ssh][:max_tries]})..." - if @runner.env.ssh.up? + if @runner.ssh.up? logger.info "VM booted and ready for use!" return true end diff --git a/lib/vagrant/provisioners/base.rb b/lib/vagrant/provisioners/base.rb index 063322e55..79b675576 100644 --- a/lib/vagrant/provisioners/base.rb +++ b/lib/vagrant/provisioners/base.rb @@ -7,11 +7,19 @@ module Vagrant class Base include Vagrant::Util - # The environment which this is being provisioned in - attr_reader :env + # The VM which this is being provisioned for + attr_reader :vm - def initialize(env) - @env = env + def initialize(vm) + @vm = vm + end + + # This method returns the environment which the provisioner is working + # on. This is also the environment of the VM. This method is provided + # as a simple helper since the environment is often used throughout the + # provisioner. + def env + @vm.env end # This is the method called to "prepare" the provisioner. This is called diff --git a/lib/vagrant/provisioners/chef.rb b/lib/vagrant/provisioners/chef.rb index 35ba58d43..2be95980b 100644 --- a/lib/vagrant/provisioners/chef.rb +++ b/lib/vagrant/provisioners/chef.rb @@ -77,7 +77,7 @@ module Vagrant def chown_provisioning_folder logger.info "Setting permissions on chef provisioning folder..." - env.ssh.execute do |ssh| + vm.ssh.execute do |ssh| ssh.exec!("sudo mkdir -p #{env.config.chef.provisioning_path}") ssh.exec!("sudo chown #{env.config.ssh.username} #{env.config.chef.provisioning_path}") end @@ -89,7 +89,7 @@ module Vagrant }.merge(template_vars)) logger.info "Uploading chef configuration script..." - env.ssh.upload!(StringIO.new(config_file), File.join(env.config.chef.provisioning_path, filename)) + vm.ssh.upload!(StringIO.new(config_file), File.join(env.config.chef.provisioning_path, filename)) end def setup_json @@ -110,7 +110,7 @@ module Vagrant json = data.to_json - env.ssh.upload!(StringIO.new(json), File.join(env.config.chef.provisioning_path, "dna.json")) + vm.ssh.upload!(StringIO.new(json), File.join(env.config.chef.provisioning_path, "dna.json")) end end end diff --git a/lib/vagrant/provisioners/chef_server.rb b/lib/vagrant/provisioners/chef_server.rb index 6ae70b221..f69cf9547 100644 --- a/lib/vagrant/provisioners/chef_server.rb +++ b/lib/vagrant/provisioners/chef_server.rb @@ -64,7 +64,7 @@ module Vagrant end def guest_validation_key_path - File.join(@env.config.chef.provisioning_path, "validation.pem") + File.join(env.config.chef.provisioning_path, "validation.pem") end end end diff --git a/lib/vagrant/systems/linux.rb b/lib/vagrant/systems/linux.rb index 563ee7d4f..cf6fdc46c 100644 --- a/lib/vagrant/systems/linux.rb +++ b/lib/vagrant/systems/linux.rb @@ -30,7 +30,7 @@ module Vagrant #------------------------------------------------------------------- def halt logger.info "Attempting graceful shutdown of linux..." - vm.env.ssh.execute do |ssh| + vm.ssh.execute do |ssh| ssh.exec!("sudo halt") end diff --git a/lib/vagrant/vm.rb b/lib/vagrant/vm.rb index 175bd19c5..0183b776c 100644 --- a/lib/vagrant/vm.rb +++ b/lib/vagrant/vm.rb @@ -20,9 +20,14 @@ module Vagrant @env = env @vm = vm - load_system! unless @env.nil? + load_system! if !@env.nil? end + # Loads the system associated with the VM. The system class is + # responsible for OS-specific functionality. More information + # can be found by reading the documentation on {Vagrant::Systems::Base}. + # + # **This method should never be called manually.** def load_system! system = env.config.vm.system @@ -31,9 +36,7 @@ module Vagrant error_and_exit(:system_invalid_class, :system => system.to_s) unless @system.is_a?(Systems::Base) elsif system.is_a?(Symbol) # Hard-coded internal systems - mapping = { - :linux => Systems::Linux - } + mapping = { :linux => Systems::Linux } if !mapping.has_key?(system) error_and_exit(:system_unknown_type, :system => system.to_s) @@ -46,6 +49,13 @@ module Vagrant end end + # Access the {Vagrant::SSH} object associated with this VM. + # On the initial call, this will initialize the object. On + # subsequent calls it will reuse the existing object. + def ssh + @ssh ||= SSH.new(env) + end + def uuid vm ? vm.uuid : nil end diff --git a/test/test_helper.rb b/test/test_helper.rb index 6f5eebd3c..6464a660d 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -73,6 +73,14 @@ class Test::Unit::TestCase environment end + # Sets up the mocks for a VM + def mock_vm + vm = Vagrant::VM.new(nil, nil) + vm.stubs(:env).returns(mock_environment) + vm.stubs(:ssh).returns(Vagrant::SSH.new(vm.env)) + vm + end + # Sets up the mocks and instantiates an action for testing def mock_action(action_klass, *args) vm = mock("vboxvm") @@ -87,6 +95,11 @@ class Test::Unit::TestCase mock_vm.stubs(:actions).returns([action]) mock_vm.stubs(:env).returns(mock_environment) + mock_ssh = Vagrant::SSH.new(mock_vm.env) + mock_ssh.stubs(:execute) + + mock_vm.stubs(:ssh).returns(mock_ssh) + vm.stubs(:env).returns(mock_vm.env) [mock_vm, vm, action] diff --git a/test/vagrant/actions/vm/boot_test.rb b/test/vagrant/actions/vm/boot_test.rb index f98dabcd5..82034acb3 100644 --- a/test/vagrant/actions/vm/boot_test.rb +++ b/test/vagrant/actions/vm/boot_test.rb @@ -43,13 +43,13 @@ class BootActionTest < Test::Unit::TestCase context "waiting for boot" do should "repeatedly ping the SSH port and return false with no response" do seq = sequence('pings') - @runner.env.ssh.expects(:up?).times(@runner.env.config.ssh.max_tries.to_i - 1).returns(false).in_sequence(seq) - @runner.env.ssh.expects(:up?).once.returns(true).in_sequence(seq) + @runner.ssh.expects(:up?).times(@runner.env.config.ssh.max_tries.to_i - 1).returns(false).in_sequence(seq) + @runner.ssh.expects(:up?).once.returns(true).in_sequence(seq) assert @action.wait_for_boot(0) end should "ping the max number of times then just return" do - @runner.env.ssh.expects(:up?).times(@runner.env.config.ssh.max_tries.to_i).returns(false) + @runner.ssh.expects(:up?).times(@runner.env.config.ssh.max_tries.to_i).returns(false) assert !@action.wait_for_boot(0) end end diff --git a/test/vagrant/provisioners/base_test.rb b/test/vagrant/provisioners/base_test.rb index 316d93cca..1820bfa46 100644 --- a/test/vagrant/provisioners/base_test.rb +++ b/test/vagrant/provisioners/base_test.rb @@ -7,13 +7,13 @@ class BaseProvisionerTest < Test::Unit::TestCase context "base instance" do setup do - @env = mock_environment - @base = Vagrant::Provisioners::Base.new(@env) + @vm = mock("vm") + @base = Vagrant::Provisioners::Base.new(@vm) end should "set the environment" do - base = Vagrant::Provisioners::Base.new(@env) - assert_equal @env, base.env + base = Vagrant::Provisioners::Base.new(@vm) + assert_equal @vm, base.vm end should "implement provision! which does nothing" do diff --git a/test/vagrant/provisioners/chef_server_test.rb b/test/vagrant/provisioners/chef_server_test.rb index 348a47366..fae97a331 100644 --- a/test/vagrant/provisioners/chef_server_test.rb +++ b/test/vagrant/provisioners/chef_server_test.rb @@ -2,8 +2,9 @@ require File.join(File.dirname(__FILE__), '..', '..', 'test_helper') class ChefServerProvisionerTest < Test::Unit::TestCase setup do - @env = mock_environment - @action = Vagrant::Provisioners::ChefServer.new(@env) + @vm = mock_vm + @env = @vm.env + @action = Vagrant::Provisioners::ChefServer.new(@vm) end context "provisioning" do diff --git a/test/vagrant/provisioners/chef_solo_test.rb b/test/vagrant/provisioners/chef_solo_test.rb index 83b495eb8..91d3d6ca5 100644 --- a/test/vagrant/provisioners/chef_solo_test.rb +++ b/test/vagrant/provisioners/chef_solo_test.rb @@ -2,8 +2,9 @@ require File.join(File.dirname(__FILE__), '..', '..', 'test_helper') class ChefSoloProvisionerTest < Test::Unit::TestCase setup do - @env = mock_environment - @action = Vagrant::Provisioners::ChefSolo.new(@env) + @vm = mock_vm + @env = @vm.env + @action = Vagrant::Provisioners::ChefSolo.new(@vm) end context "preparing" do diff --git a/test/vagrant/provisioners/chef_test.rb b/test/vagrant/provisioners/chef_test.rb index 1eda93676..d21fcd486 100644 --- a/test/vagrant/provisioners/chef_test.rb +++ b/test/vagrant/provisioners/chef_test.rb @@ -2,8 +2,9 @@ require File.join(File.dirname(__FILE__), '..', '..', 'test_helper') class ChefProvisionerTest < Test::Unit::TestCase setup do - @env = mock_environment - @action = Vagrant::Provisioners::Chef.new(@env) + @vm = mock_vm + @env = @vm.env + @action = Vagrant::Provisioners::Chef.new(@vm) end context "preparing" do @@ -67,14 +68,14 @@ class ChefProvisionerTest < Test::Unit::TestCase ssh = mock("ssh") ssh.expects(:exec!).with("sudo mkdir -p #{@env.config.chef.provisioning_path}").once.in_sequence(ssh_seq) ssh.expects(:exec!).with("sudo chown #{@env.config.ssh.username} #{@env.config.chef.provisioning_path}").once.in_sequence(ssh_seq) - @env.ssh.expects(:execute).yields(ssh) + @vm.ssh.expects(:execute).yields(ssh) @action.chown_provisioning_folder end end context "generating and uploading chef configuration file" do setup do - @env.ssh.stubs(:upload!) + @vm.ssh.stubs(:upload!) @template = "template" @filename = "foo.rb" @@ -88,7 +89,7 @@ class ChefProvisionerTest < Test::Unit::TestCase Vagrant::Util::TemplateRenderer.expects(:render).with(@template, anything).returns(template_data) StringIO.expects(:new).with(template_data).returns(string_io) File.expects(:join).with(@env.config.chef.provisioning_path, @filename).once.returns("bar") - @env.ssh.expects(:upload!).with(string_io, "bar") + @vm.ssh.expects(:upload!).with(string_io, "bar") @action.setup_config(@template, @filename, {}) end @@ -124,7 +125,7 @@ class ChefProvisionerTest < Test::Unit::TestCase context "generating and uploading json" do def assert_json - @env.ssh.expects(:upload!).with do |json, path| + @vm.ssh.expects(:upload!).with do |json, path| data = JSON.parse(json.read) yield data true @@ -155,7 +156,7 @@ class ChefProvisionerTest < Test::Unit::TestCase should "upload a StringIO to dna.json" do StringIO.expects(:new).with(anything).returns("bar") File.expects(:join).with(@env.config.chef.provisioning_path, "dna.json").once.returns("baz") - @env.ssh.expects(:upload!).with("bar", "baz").once + @vm.ssh.expects(:upload!).with("bar", "baz").once @action.setup_json end end diff --git a/test/vagrant/systems/linux_test.rb b/test/vagrant/systems/linux_test.rb index 6c654944f..f66b0499b 100644 --- a/test/vagrant/systems/linux_test.rb +++ b/test/vagrant/systems/linux_test.rb @@ -4,8 +4,7 @@ class LinuxSystemTest < Test::Unit::TestCase setup do @klass = Vagrant::Systems::Linux - @vm = mock("vm") - @vm.stubs(:env).returns(mock_environment) + @vm = mock_vm @instance = @klass.new(@vm) end @@ -14,7 +13,7 @@ class LinuxSystemTest < Test::Unit::TestCase @ssh_session = mock("ssh_session") @ssh = mock("ssh") @ssh.stubs(:execute).yields(@ssh_session) - @vm.env.stubs(:ssh).returns(@ssh) + @vm.stubs(:ssh).returns(@ssh) @real_vm = mock("real_vm") @real_vm.stubs(:state).returns(:powered_off) diff --git a/test/vagrant/vm_test.rb b/test/vagrant/vm_test.rb index 4791318e0..1a4b902b6 100644 --- a/test/vagrant/vm_test.rb +++ b/test/vagrant/vm_test.rb @@ -39,6 +39,28 @@ class VMTest < Test::Unit::TestCase @mock_vm.stubs(:uuid).returns("foo") end + context "accessing the SSH object" do + setup do + # Reset this to nil to force the reload + @vm.instance_variable_set(:@ssh, nil) + + @ssh = mock("ssh") + Vagrant::SSH.stubs(:new).returns(@ssh) + end + + should "load it the first time" do + Vagrant::SSH.expects(:new).with(@env).once.returns(@ssh) + @vm.ssh + @vm.ssh + @vm.ssh + end + + should "use the same value once its loaded" do + result = @vm.ssh + assert_equal result, @vm.ssh + end + end + context "loading associated system" do should "error and exit if system is not specified" do @vm.env.config.vm.system = nil From b1b4ae2077d73021ae2d84886f379f2e2b23d027 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 7 May 2010 21:45:40 -0700 Subject: [PATCH 03/26] Remove SSH object from environment --- lib/vagrant/actions/vm/shared_folders.rb | 2 +- lib/vagrant/commands/ssh.rb | 2 +- lib/vagrant/environment.rb | 7 ------- lib/vagrant/provisioners/chef_server.rb | 6 +++--- lib/vagrant/provisioners/chef_solo.rb | 2 +- test/test_helper.rb | 5 +++-- test/vagrant/actions/vm/shared_folders_test.rb | 2 +- test/vagrant/commands/ssh_test.rb | 10 +++++----- test/vagrant/environment_test.rb | 14 -------------- test/vagrant/provisioners/chef_server_test.rb | 6 +++--- test/vagrant/provisioners/chef_solo_test.rb | 4 ++-- 11 files changed, 20 insertions(+), 40 deletions(-) diff --git a/lib/vagrant/actions/vm/shared_folders.rb b/lib/vagrant/actions/vm/shared_folders.rb index 12b7923b1..2945743d8 100644 --- a/lib/vagrant/actions/vm/shared_folders.rb +++ b/lib/vagrant/actions/vm/shared_folders.rb @@ -17,7 +17,7 @@ module Vagrant def after_boot logger.info "Mounting shared folders..." - @runner.env.ssh.execute do |ssh| + @runner.ssh.execute do |ssh| shared_folders.each do |name, hostpath, guestpath| logger.info "-- #{name}: #{guestpath}" @runner.system.mount_shared_folder(ssh, name, guestpath) diff --git a/lib/vagrant/commands/ssh.rb b/lib/vagrant/commands/ssh.rb index 876e3f0aa..3192842bd 100644 --- a/lib/vagrant/commands/ssh.rb +++ b/lib/vagrant/commands/ssh.rb @@ -11,7 +11,7 @@ module Vagrant def execute(args=[]) env.require_persisted_vm - env.ssh.connect + env.vm.ssh.connect end def options_spec(opts) diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb index 73aebce84..878cdb2d2 100644 --- a/lib/vagrant/environment.rb +++ b/lib/vagrant/environment.rb @@ -13,7 +13,6 @@ module Vagrant attr_reader :config attr_reader :box attr_accessor :vm - attr_reader :ssh attr_reader :active_list attr_reader :commands @@ -95,7 +94,6 @@ module Vagrant load_config! self.class.check_virtualbox! load_vm! - load_ssh! load_active_list! load_commands! self @@ -180,11 +178,6 @@ module Vagrant @vm = nil end - # Loads/initializes the SSH object - def load_ssh! - @ssh = SSH.new(self) - end - # Loads the activelist for this environment def load_active_list! @active_list = ActiveList.new(self) diff --git a/lib/vagrant/provisioners/chef_server.rb b/lib/vagrant/provisioners/chef_server.rb index f69cf9547..560c01f0e 100644 --- a/lib/vagrant/provisioners/chef_server.rb +++ b/lib/vagrant/provisioners/chef_server.rb @@ -28,14 +28,14 @@ module Vagrant logger.info "Creating folder to hold client key..." path = Pathname.new(env.config.chef.client_key_path) - env.ssh.execute do |ssh| + vm.ssh.execute do |ssh| ssh.exec!("sudo mkdir -p #{path.dirname}") end end def upload_validation_key logger.info "Uploading chef client validation key..." - env.ssh.upload!(validation_key_path, guest_validation_key_path) + vm.ssh.upload!(validation_key_path, guest_validation_key_path) end def setup_server_config @@ -50,7 +50,7 @@ module Vagrant def run_chef_client logger.info "Running chef-client..." - env.ssh.execute do |ssh| + vm.ssh.execute do |ssh| ssh.exec!("cd #{env.config.chef.provisioning_path} && sudo chef-client -c client.rb -j dna.json") do |channel, data, stream| # TODO: Very verbose. It would be easier to save the data and only show it during # an error, or when verbosity level is set high diff --git a/lib/vagrant/provisioners/chef_solo.rb b/lib/vagrant/provisioners/chef_solo.rb index c6f646a2d..146e1542a 100644 --- a/lib/vagrant/provisioners/chef_solo.rb +++ b/lib/vagrant/provisioners/chef_solo.rb @@ -36,7 +36,7 @@ module Vagrant def run_chef_solo logger.info "Running chef-solo..." - env.ssh.execute do |ssh| + vm.ssh.execute do |ssh| ssh.exec!("cd #{env.config.chef.provisioning_path} && sudo chef-solo -c solo.rb -j dna.json") do |channel, data, stream| # TODO: Very verbose. It would be easier to save the data and only show it during # an error, or when verbosity level is set high diff --git a/test/test_helper.rb b/test/test_helper.rb index 6464a660d..16c87585b 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -74,9 +74,10 @@ class Test::Unit::TestCase end # Sets up the mocks for a VM - def mock_vm + def mock_vm(env=nil) + env ||= mock_environment vm = Vagrant::VM.new(nil, nil) - vm.stubs(:env).returns(mock_environment) + vm.stubs(:env).returns(env) vm.stubs(:ssh).returns(Vagrant::SSH.new(vm.env)) vm end diff --git a/test/vagrant/actions/vm/shared_folders_test.rb b/test/vagrant/actions/vm/shared_folders_test.rb index d5fc41676..4d7871ba4 100644 --- a/test/vagrant/actions/vm/shared_folders_test.rb +++ b/test/vagrant/actions/vm/shared_folders_test.rb @@ -99,7 +99,7 @@ class SharedFoldersActionTest < Test::Unit::TestCase @folders.each do |name, hostpath, guestpath| @runner.system.expects(:mount_shared_folder).with(ssh, name, guestpath).in_sequence(mount_seq) end - @runner.env.ssh.expects(:execute).yields(ssh) + @runner.ssh.expects(:execute).yields(ssh) @action.after_boot end diff --git a/test/vagrant/commands/ssh_test.rb b/test/vagrant/commands/ssh_test.rb index ac06ba63e..87d28043b 100644 --- a/test/vagrant/commands/ssh_test.rb +++ b/test/vagrant/commands/ssh_test.rb @@ -4,11 +4,11 @@ class CommandsSSHTest < Test::Unit::TestCase setup do @klass = Vagrant::Commands::SSH - @persisted_vm = mock("persisted_vm") - @persisted_vm.stubs(:execute!) - @env = mock_environment @env.stubs(:require_persisted_vm) + + @persisted_vm = mock_vm(@env) + @persisted_vm.stubs(:execute!) @env.stubs(:vm).returns(@persisted_vm) @instance = @klass.new(@env) @@ -16,7 +16,7 @@ class CommandsSSHTest < Test::Unit::TestCase context "executing" do setup do - @env.ssh.stubs(:connect) + @persisted_vm.ssh.stubs(:connect) end should "require a persisted VM" do @@ -25,7 +25,7 @@ class CommandsSSHTest < Test::Unit::TestCase end should "connect to SSH" do - @env.ssh.expects(:connect).once + @persisted_vm.ssh.expects(:connect).once @instance.execute end end diff --git a/test/vagrant/environment_test.rb b/test/vagrant/environment_test.rb index aa3c90216..4f75c090c 100644 --- a/test/vagrant/environment_test.rb +++ b/test/vagrant/environment_test.rb @@ -139,7 +139,6 @@ class EnvironmentTest < Test::Unit::TestCase @env.expects(:load_config!).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_ssh!).once.in_sequence(call_seq) @env.expects(:load_active_list!).once.in_sequence(call_seq) @env.expects(:load_commands!).once.in_sequence(call_seq) assert_equal @env, @env.load! @@ -397,19 +396,6 @@ class EnvironmentTest < Test::Unit::TestCase 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 - context "loading the active list" do setup do @env = mock_environment diff --git a/test/vagrant/provisioners/chef_server_test.rb b/test/vagrant/provisioners/chef_server_test.rb index fae97a331..dfab93850 100644 --- a/test/vagrant/provisioners/chef_server_test.rb +++ b/test/vagrant/provisioners/chef_server_test.rb @@ -107,7 +107,7 @@ class ChefServerProvisionerTest < Test::Unit::TestCase should "create the folder using the dirname of the path" do ssh = mock("ssh") ssh.expects(:exec!).with("sudo mkdir -p #{@path.dirname}").once - @env.ssh.expects(:execute).yields(ssh) + @vm.ssh.expects(:execute).yields(ssh) @action.create_client_key_folder end end @@ -116,7 +116,7 @@ class ChefServerProvisionerTest < Test::Unit::TestCase should "upload the validation key to the provisioning path" do @action.expects(:validation_key_path).once.returns("foo") @action.expects(:guest_validation_key_path).once.returns("bar") - @env.ssh.expects(:upload!).with("foo", "bar").once + @vm.ssh.expects(:upload!).with("foo", "bar").once @action.upload_validation_key end end @@ -159,7 +159,7 @@ class ChefServerProvisionerTest < Test::Unit::TestCase should "cd into the provisioning directory and run chef client" do ssh = mock("ssh") ssh.expects(:exec!).with("cd #{@env.config.chef.provisioning_path} && sudo chef-client -c client.rb -j dna.json").once - @env.ssh.expects(:execute).yields(ssh) + @vm.ssh.expects(:execute).yields(ssh) @action.run_chef_client end end diff --git a/test/vagrant/provisioners/chef_solo_test.rb b/test/vagrant/provisioners/chef_solo_test.rb index 91d3d6ca5..e326632c8 100644 --- a/test/vagrant/provisioners/chef_solo_test.rb +++ b/test/vagrant/provisioners/chef_solo_test.rb @@ -149,7 +149,7 @@ class ChefSoloProvisionerTest < Test::Unit::TestCase context "generating and uploading chef solo configuration file" do setup do - @env.ssh.stubs(:upload!) + @vm.ssh.stubs(:upload!) end should "call setup_config with proper variables" do @@ -167,7 +167,7 @@ class ChefSoloProvisionerTest < Test::Unit::TestCase should "cd into the provisioning directory and run chef solo" do ssh = mock("ssh") ssh.expects(:exec!).with("cd #{@env.config.chef.provisioning_path} && sudo chef-solo -c solo.rb -j dna.json").once - @env.ssh.expects(:execute).yields(ssh) + @vm.ssh.expects(:execute).yields(ssh) @action.run_chef_solo end end From af383e222a0ae224c952c1f4710dae139c05269d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 7 May 2010 22:57:25 -0700 Subject: [PATCH 04/26] Get rid of more references to Vagrant.config. Only one remains. --- lib/vagrant/actions/vm/move_hard_drive.rb | 4 ++-- lib/vagrant/config.rb | 10 ++++++---- lib/vagrant/vm.rb | 4 ++-- test/vagrant/config_test.rb | 20 ++++++++------------ 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/lib/vagrant/actions/vm/move_hard_drive.rb b/lib/vagrant/actions/vm/move_hard_drive.rb index b1e07e07e..65caadb1d 100644 --- a/lib/vagrant/actions/vm/move_hard_drive.rb +++ b/lib/vagrant/actions/vm/move_hard_drive.rb @@ -26,7 +26,7 @@ module Vagrant def clone_and_attach logger.info "Cloning current VM Disk to new location (#{new_image_path})..." - hard_drive.image = hard_drive.image.clone(new_image_path, Vagrant.config.vm.disk_image_format, true) + hard_drive.image = hard_drive.image.clone(new_image_path, @runner.env.config.vm.disk_image_format, true) logger.info "Attaching new disk to VM ..." @runner.vm.save @@ -43,7 +43,7 @@ module Vagrant # Returns the path to the new location for the hard drive def new_image_path - File.join(Vagrant.config.vm.hd_location, hard_drive.image.filename) + File.join(@runner.env.config.vm.hd_location, hard_drive.image.filename) end end end diff --git a/lib/vagrant/config.rb b/lib/vagrant/config.rb index 2d72265e5..b6e9c21e3 100644 --- a/lib/vagrant/config.rb +++ b/lib/vagrant/config.rb @@ -29,10 +29,12 @@ module Vagrant push_proc(&block) end - def execute! - run_procs!(config) - config.loaded! - config + def execute!(config_object=nil) + config_object ||= config + + run_procs!(config_object) + config_object.loaded! + config_object end end end diff --git a/lib/vagrant/vm.rb b/lib/vagrant/vm.rb index 0183b776c..66d605214 100644 --- a/lib/vagrant/vm.rb +++ b/lib/vagrant/vm.rb @@ -6,7 +6,7 @@ module Vagrant attr_reader :system attr_accessor :vm - class << self + class < system.to_s) unless @system.is_a?(Systems::Base) elsif system.is_a?(Symbol) # Hard-coded internal systems - mapping = { :linux => Systems::Linux } + mapping = { :linux => Systems::Linux } if !mapping.has_key?(system) error_and_exit(:system_unknown_type, :system => system.to_s) diff --git a/test/vagrant/config_test.rb b/test/vagrant/config_test.rb index 3a2d4f340..848195638 100644 --- a/test/vagrant/config_test.rb +++ b/test/vagrant/config_test.rb @@ -52,17 +52,6 @@ class ConfigTest < Test::Unit::TestCase end end - context "accessing configuration" do - setup do - Vagrant::Config.run { |config| } - Vagrant::Config.execute! - end - - should "forward config to the class method" do - assert_equal Vagrant.config, Vagrant::Config.config - end - end - context "initializing" do setup do Vagrant::Config.reset! @@ -92,7 +81,14 @@ class ConfigTest < Test::Unit::TestCase should "return the configuration on execute!" do Vagrant::Config.run {} result = Vagrant::Config.execute! - assert result.equal?(Vagrant.config) + assert result.is_a?(Vagrant::Config::Top) + end + + should "use given configuration object if given" do + fake_env = mock("env") + config = Vagrant::Config::Top.new(fake_env) + result = Vagrant::Config.execute!(config) + assert_equal config.env, result.env end end From b313d34d5bbca22f7cd2619279a9a33eaecc9bf4 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 8 May 2010 14:57:31 -0700 Subject: [PATCH 05/26] Environment config loading loads the subconfig if specified. VMs now create a new env for themselves. --- lib/vagrant/environment.rb | 48 +++++++++++++++++++++++--------- lib/vagrant/vm.rb | 20 +++++++++---- test/test_helper.rb | 2 +- test/vagrant/environment_test.rb | 41 ++++++++++++++++++++------- test/vagrant/vm_test.rb | 6 ++-- 5 files changed, 85 insertions(+), 32 deletions(-) diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb index 878cdb2d2..db09a66bf 100644 --- a/lib/vagrant/environment.rb +++ b/lib/vagrant/environment.rb @@ -8,6 +8,9 @@ module Vagrant include Util + attr_reader :parent # Parent environment (in the case of multi-VMs) + attr_reader :vm_name # The name of the VM (internal name) which this environment represents + attr_accessor :cwd attr_reader :root_path attr_reader :config @@ -24,7 +27,7 @@ module Vagrant # directory. If a working directory is not given, it will default # to the pwd. def load!(cwd=nil) - Environment.new(cwd).load! + Environment.new(:cwd => cwd).load! end # Verifies that VirtualBox is installed and that the version of @@ -42,8 +45,17 @@ module Vagrant end end - def initialize(cwd=nil) - @cwd = cwd + def initialize(opts=nil) + defaults = { + :parent => nil, + :vm_name => nil, + :cwd => nil + } + + opts = defaults.merge(opts || {}) + @cwd = opts[:cwd] + @parent = opts[:parent] + @vm_name = opts[:vm_name] end #--------------------------------------------------------------- @@ -123,20 +135,30 @@ module Vagrant # this environment, meaning that it will use the given root directory # to load the Vagrantfile into that context. def load_config! - # Prepare load paths for config files - load_paths = [File.join(PROJECT_ROOT, "config", "default.rb")] - load_paths << File.join(box.directory, ROOTFILE_NAME) if box - load_paths << File.join(home_path, ROOTFILE_NAME) if home_path - load_paths << File.join(root_path, ROOTFILE_NAME) if root_path + # Prepare load paths for config files and append to config queue + config_queue = [File.join(PROJECT_ROOT, "config", "default.rb")] + config_queue << File.join(box.directory, ROOTFILE_NAME) if box + config_queue << File.join(home_path, ROOTFILE_NAME) if home_path + config_queue << File.join(root_path, ROOTFILE_NAME) if root_path + + # If this environment represents some VM in a multi-VM environment, + # we push that VM's configuration onto the config_queue. + config_queue << parent.config.vm.defined_vms[vm_name] if vm_name # Clear out the old data Config.reset!(self) # Load each of the config files in order - load_paths.each do |path| - if File.exist?(path) - logger.info "Loading config from #{path}..." - load path + config_queue.each do |item| + if item.is_a?(String) && File.exist?(item) + logger.info "Loading config from #{item}..." + load item + next + end + + if item.is_a?(Proc) + # Just push the proc straight onto the config runnable stack + Config.run(&item) end end @@ -198,7 +220,7 @@ module Vagrant # in {Command.up}. This will very likely be refactored at a later # time. def create_vm - @vm = VM.new(self) + @vm = VM.new(:env => self) end # Persists this environment's VM to the dotfile so it can be diff --git a/lib/vagrant/vm.rb b/lib/vagrant/vm.rb index 66d605214..8abed1888 100644 --- a/lib/vagrant/vm.rb +++ b/lib/vagrant/vm.rb @@ -12,15 +12,25 @@ module Vagrant def find(uuid, env=nil) vm = VirtualBox::VM.find(uuid) return nil if vm.nil? - new(env, vm) + new(:vm => vm, :env => env) end end - def initialize(env, vm=nil) - @env = env - @vm = vm + def initialize(opts=nil) + defaults = { + :vm => nil, + :env => nil, + :vm_name => nil + } - load_system! if !@env.nil? + opts = defaults.merge(opts || {}) + + @vm = opts[:vm] + + if !opts[:env].nil? + @env = Vagrant::Environment.new(:cwd => opts[:env].cwd, :parent => opts[:env], :vm_name => opts[:vm_name]).load! + load_system! + end end # Loads the system associated with the VM. The system class is diff --git a/test/test_helper.rb b/test/test_helper.rb index 16c87585b..6058edc1a 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -76,7 +76,7 @@ class Test::Unit::TestCase # Sets up the mocks for a VM def mock_vm(env=nil) env ||= mock_environment - vm = Vagrant::VM.new(nil, nil) + vm = Vagrant::VM.new vm.stubs(:env).returns(env) vm.stubs(:ssh).returns(Vagrant::SSH.new(vm.env)) vm diff --git a/test/vagrant/environment_test.rb b/test/vagrant/environment_test.rb index 4f75c090c..708459f0a 100644 --- a/test/vagrant/environment_test.rb +++ b/test/vagrant/environment_test.rb @@ -42,13 +42,13 @@ class EnvironmentTest < Test::Unit::TestCase end should "create the environment with given cwd, load it, and return it" do - Vagrant::Environment.expects(:new).with(@cwd).once.returns(@env) + Vagrant::Environment.expects(:new).with(:cwd => @cwd).once.returns(@env) @env.expects(:load!).returns(@env) assert_equal @env, Vagrant::Environment.load!(@cwd) end should "work without a given cwd" do - Vagrant::Environment.expects(:new).with(nil).returns(@env) + Vagrant::Environment.expects(:new).with(:cwd => nil).returns(@env) assert_nothing_raised { env = Vagrant::Environment.load! @@ -60,7 +60,7 @@ class EnvironmentTest < Test::Unit::TestCase context "initialization" do should "set the cwd if given" do cwd = "foobarbaz" - env = Vagrant::Environment.new(cwd) + env = Vagrant::Environment.new(:cwd => cwd) assert_equal cwd, env.cwd end @@ -215,9 +215,9 @@ class EnvironmentTest < Test::Unit::TestCase @env.stubs(:root_path).returns(@root_path) @env.stubs(:home_path).returns(@home_path) + @parent_env = mock_environment + File.stubs(:exist?).returns(false) - Vagrant::Config.stubs(:execute!) - Vagrant::Config.stubs(:reset!) end should "reset the configuration object" do @@ -267,6 +267,25 @@ class EnvironmentTest < Test::Unit::TestCase @env.load_config! end + should "load a sub-VM configuration if specified" do + vm_name = :foo + sub_box = :YO + @parent_env.config.vm.box = :NO + @parent_env.config.vm.define(vm_name) do |config| + config.vm.box = sub_box + end + + # Sanity + assert_equal :NO, @parent_env.config.vm.box + + @env.stubs(:vm_name).returns(vm_name) + @env.stubs(:parent).returns(@parent_env) + + @env.load_config! + + assert_equal sub_box, @env.config.vm.box + end + should "load the files only if exist? returns true" do File.expects(:exist?).once.returns(true) @env.expects(:load).once @@ -517,6 +536,13 @@ class EnvironmentTest < Test::Unit::TestCase end context "creating a new VM" do + should "create a new VM with the given environment" do + result = mock("result") + Vagrant::VM.expects(:new).with(:env => @env).once.returns(result) + @env.create_vm + assert_equal result, @env.vm + end + should "create a new VM" do assert_nil @env.vm @env.create_vm @@ -524,11 +550,6 @@ class EnvironmentTest < Test::Unit::TestCase assert @env.vm.is_a?(Vagrant::VM) end - should "set the new VM's environment to the env" do - @env.create_vm - assert_equal @env, @env.vm.env - end - should "return the new VM" do result = @env.create_vm assert result.is_a?(Vagrant::VM) diff --git a/test/vagrant/vm_test.rb b/test/vagrant/vm_test.rb index 1a4b902b6..a200d9bde 100644 --- a/test/vagrant/vm_test.rb +++ b/test/vagrant/vm_test.rb @@ -14,7 +14,7 @@ class VMTest < Test::Unit::TestCase context "being an action runner" do should "be an action runner" do - vm = Vagrant::VM.new(@env) + vm = Vagrant::VM.new(:env => @env) assert vm.is_a?(Vagrant::Actions::Runner) end end @@ -35,7 +35,7 @@ class VMTest < Test::Unit::TestCase context "vagrant VM instance" do setup do - @vm = Vagrant::VM.new(@env, @mock_vm) + @vm = Vagrant::VM.new(:env => @env, :vm => @mock_vm) @mock_vm.stubs(:uuid).returns("foo") end @@ -49,7 +49,7 @@ class VMTest < Test::Unit::TestCase end should "load it the first time" do - Vagrant::SSH.expects(:new).with(@env).once.returns(@ssh) + Vagrant::SSH.expects(:new).with(@vm.env).once.returns(@ssh) @vm.ssh @vm.ssh @vm.ssh From 124a9ab19d97c8c9a6c34c908b2123c9a3249486 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 8 May 2010 15:34:43 -0700 Subject: [PATCH 06/26] Environment properly loads VMs --- lib/vagrant/config.rb | 4 ++++ lib/vagrant/environment.rb | 39 +++++++++++++++++++++++++++----- lib/vagrant/vm.rb | 11 ++++++--- test/vagrant/config_test.rb | 9 ++++++++ test/vagrant/environment_test.rb | 33 ++++++++++++++++++++------- 5 files changed, 79 insertions(+), 17 deletions(-) diff --git a/lib/vagrant/config.rb b/lib/vagrant/config.rb index b6e9c21e3..c75b6ac26 100644 --- a/lib/vagrant/config.rb +++ b/lib/vagrant/config.rb @@ -128,6 +128,10 @@ module Vagrant push_proc(&block) end + def has_multi_vms? + !defined_vms.empty? + end + def defined_vms @defined_vms ||= {} end diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb index db09a66bf..84f4276b1 100644 --- a/lib/vagrant/environment.rb +++ b/lib/vagrant/environment.rb @@ -16,6 +16,7 @@ module Vagrant attr_reader :config attr_reader :box attr_accessor :vm + attr_reader :vms attr_reader :active_list attr_reader :commands @@ -49,17 +50,19 @@ module Vagrant defaults = { :parent => nil, :vm_name => nil, + :vm => nil, :cwd => nil } opts = defaults.merge(opts || {}) - @cwd = opts[:cwd] - @parent = opts[:parent] - @vm_name = opts[:vm_name] + + defaults.each do |key, value| + instance_variable_set("@#{key}".to_sym, opts[key]) + end end #--------------------------------------------------------------- - # Path Helpers + # Helpers #--------------------------------------------------------------- # Specifies the "current working directory" for this environment. @@ -91,6 +94,11 @@ module Vagrant File.join(home_path, "boxes") end + # Returns the VMs associated with this environment. + def vms + @vms ||= {} + end + #--------------------------------------------------------------- # Load Methods #--------------------------------------------------------------- @@ -191,13 +199,32 @@ module Vagrant # Loads the persisted VM (if it exists) for this environment. def load_vm! + # This environment represents a single sub VM. The VM is then + # probably (read: should be) set on the VM attribute, so we do + # nothing. + return if vm_name return if !root_path || !File.file?(dotfile_path) + # Empty out previously loaded vms + vms.clear + File.open(dotfile_path) do |f| - @vm = Vagrant::VM.find(f.read, self) + data = { :__vagrant => f.read } + + begin + data = JSON.parse(data[:__vagrant]) + rescue JSON::ParserError + # Most likely an older (<= 0.3.x) dotfile. Try to load it + # as the :__vagrant VM. + end + + data.each do |key, value| + key = key.to_sym + vms[key] = Vagrant::VM.find(value, self, key) + end end rescue Errno::ENOENT - @vm = nil + # Just rescue it. end # Loads the activelist for this environment diff --git a/lib/vagrant/vm.rb b/lib/vagrant/vm.rb index 8abed1888..566aa3182 100644 --- a/lib/vagrant/vm.rb +++ b/lib/vagrant/vm.rb @@ -9,10 +9,10 @@ module Vagrant class < vm, :env => env) + new(:vm => vm, :env => env, :vm_name => vm_name) end end @@ -28,7 +28,12 @@ module Vagrant @vm = opts[:vm] if !opts[:env].nil? - @env = Vagrant::Environment.new(:cwd => opts[:env].cwd, :parent => opts[:env], :vm_name => opts[:vm_name]).load! + @env = Vagrant::Environment.new({ + :cwd => opts[:env].cwd, + :parent => opts[:env], + :vm_name => opts[:vm_name], + :vm => self + }).load! load_system! end end diff --git a/test/vagrant/config_test.rb b/test/vagrant/config_test.rb index 848195638..d9d338212 100644 --- a/test/vagrant/config_test.rb +++ b/test/vagrant/config_test.rb @@ -234,6 +234,15 @@ class ConfigTest < Test::Unit::TestCase @config.define(:name, &proc) assert_equal proc, @config.defined_vms[:name] end + + should "not have multi-VMs by default" do + assert !@config.has_multi_vms? + end + + should "have multi-VMs once one is specified" do + @config.define(:foo) {} + assert @config.has_multi_vms? + end end context "customizing" do diff --git a/test/vagrant/environment_test.rb b/test/vagrant/environment_test.rb index 708459f0a..1097c87d8 100644 --- a/test/vagrant/environment_test.rb +++ b/test/vagrant/environment_test.rb @@ -372,28 +372,45 @@ class EnvironmentTest < Test::Unit::TestCase File.stubs(:file?).returns(true) end - should "loading of the uuid from the dotfile" do + should "load the UUID if the JSON parsing fails" do vm = mock("vm") filemock = mock("filemock") filemock.expects(:read).returns("foo") - Vagrant::VM.expects(:find).with("foo", @env).returns(vm) + Vagrant::VM.expects(:find).with("foo", @env, :__vagrant).returns(vm) File.expects(:open).with(@env.dotfile_path).once.yields(filemock) File.expects(:file?).with(@env.dotfile_path).once.returns(true) @env.load_vm! - assert_equal vm, @env.vm + assert_equal vm, @env.vms.values.first end - should "not set the environment if the VM is nil" do + should "load all the VMs from the dotfile" do + vms = { :foo => "bar", :bar => "baz" } + results = {} + filemock = mock("filemock") - filemock.expects(:read).returns("foo") - Vagrant::VM.expects(:find).with("foo", @env).returns(nil) + filemock.expects(:read).returns(vms.to_json) File.expects(:open).with(@env.dotfile_path).once.yields(filemock) File.expects(:file?).with(@env.dotfile_path).once.returns(true) - assert_nothing_raised { @env.load_vm! } - assert_nil @env.vm + vms.each do |key, value| + vm = mock("vm#{key}") + Vagrant::VM.expects(:find).with(value, @env, key.to_sym).returns(vm) + results[key] = vm + end + + @env.load_vm! + + results.each do |key, value| + assert_equal value, @env.vms[key] + end + end + + should "do nothing if the vm_name is set" do + @env.stubs(:vm_name).returns(:foo) + File.expects(:open).never + @env.load_vm! end should "do nothing if the root path is nil" do From 14bb07e08fbe3697500f578dca79941b253e5608 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 8 May 2010 20:28:42 -0700 Subject: [PATCH 07/26] Fix tests to not clear line --- test/vagrant/actions/vm/export_test.rb | 1 + test/vagrant/actions/vm/import_test.rb | 2 ++ 2 files changed, 3 insertions(+) diff --git a/test/vagrant/actions/vm/export_test.rb b/test/vagrant/actions/vm/export_test.rb index 8c8bc72f9..d0ecababe 100644 --- a/test/vagrant/actions/vm/export_test.rb +++ b/test/vagrant/actions/vm/export_test.rb @@ -3,6 +3,7 @@ require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') class ExportActionTest < Test::Unit::TestCase setup do @runner, @vm, @action = mock_action(Vagrant::Actions::VM::Export) + @action.stubs(:complete_progress) end context "executing" do diff --git a/test/vagrant/actions/vm/import_test.rb b/test/vagrant/actions/vm/import_test.rb index c06803ba4..2b1cebef9 100644 --- a/test/vagrant/actions/vm/import_test.rb +++ b/test/vagrant/actions/vm/import_test.rb @@ -10,6 +10,8 @@ class ImportActionTest < Test::Unit::TestCase @runner.env.stubs(:box).returns(@box) VirtualBox::VM.stubs(:import) + + @import.stubs(:complete_progress) end should "run in a busy block" do From c8e36274d61c88afd51e91cfd499fc3647858950 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 8 May 2010 20:31:12 -0700 Subject: [PATCH 08/26] Get rid of Environment#create_vm. VMs instances are now always available --- lib/vagrant/environment.rb | 7 ------- lib/vagrant/vm.rb | 9 ++++++++- test/vagrant/environment_test.rb | 21 --------------------- test/vagrant/vm_test.rb | 20 +++++++++++++++++--- 4 files changed, 25 insertions(+), 32 deletions(-) diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb index 84f4276b1..5aab4c005 100644 --- a/lib/vagrant/environment.rb +++ b/lib/vagrant/environment.rb @@ -243,13 +243,6 @@ module Vagrant # Methods to manage VM #--------------------------------------------------------------- - # Sets the VM to a new VM. This is not too useful but is used - # in {Command.up}. This will very likely be refactored at a later - # time. - def create_vm - @vm = VM.new(:env => self) - end - # Persists this environment's VM to the dotfile so it can be # re-loaded at a later time. def persist_vm diff --git a/lib/vagrant/vm.rb b/lib/vagrant/vm.rb index 566aa3182..e3fa18985 100644 --- a/lib/vagrant/vm.rb +++ b/lib/vagrant/vm.rb @@ -11,7 +11,6 @@ module Vagrant # a Vagrant::VM object or returns nil. def find(uuid, env=nil, vm_name=nil) vm = VirtualBox::VM.find(uuid) - return nil if vm.nil? new(:vm => vm, :env => env, :vm_name => vm_name) end end @@ -71,6 +70,14 @@ module Vagrant @ssh ||= SSH.new(env) end + # Returns a boolean true if the VM has been created, otherwise + # returns false. + # + # @return [Boolean] + def created? + !vm.nil? + end + def uuid vm ? vm.uuid : nil end diff --git a/test/vagrant/environment_test.rb b/test/vagrant/environment_test.rb index 1097c87d8..4260ce7b7 100644 --- a/test/vagrant/environment_test.rb +++ b/test/vagrant/environment_test.rb @@ -552,27 +552,6 @@ class EnvironmentTest < Test::Unit::TestCase @env.stubs(:vm).returns(@vm) end - context "creating a new VM" do - should "create a new VM with the given environment" do - result = mock("result") - Vagrant::VM.expects(:new).with(:env => @env).once.returns(result) - @env.create_vm - assert_equal result, @env.vm - end - - should "create a new VM" do - assert_nil @env.vm - @env.create_vm - assert !@env.vm.nil? - assert @env.vm.is_a?(Vagrant::VM) - end - - should "return the new VM" do - result = @env.create_vm - assert result.is_a?(Vagrant::VM) - end - end - context "persisting the VM into a file" do setup do mock_vm diff --git a/test/vagrant/vm_test.rb b/test/vagrant/vm_test.rb index a200d9bde..c62548f51 100644 --- a/test/vagrant/vm_test.rb +++ b/test/vagrant/vm_test.rb @@ -20,12 +20,14 @@ class VMTest < Test::Unit::TestCase end context "finding a VM" do - should "return nil if the VM is not found" do + should "return return an uncreated VM object if the VM is not found" do VirtualBox::VM.expects(:find).returns(nil) - assert_nil Vagrant::VM.find("foo") + result = Vagrant::VM.find("foo") + assert result.is_a?(Vagrant::VM) + assert !result.created? end - should "return a Vagrant::VM object for that VM otherwise" do + should "return a Vagrant::VM object for that VM if found" do VirtualBox::VM.expects(:find).with("foo").returns("bar") result = Vagrant::VM.find("foo", mock_environment) assert result.is_a?(Vagrant::VM) @@ -39,6 +41,18 @@ class VMTest < Test::Unit::TestCase @mock_vm.stubs(:uuid).returns("foo") end + context "checking if created" do + should "return true if the VM object is not nil" do + @vm.stubs(:vm).returns(:foo) + assert @vm.created? + end + + should "return false if the VM object is nil" do + @vm.stubs(:vm).returns(nil) + assert !@vm.created? + end + end + context "accessing the SSH object" do setup do # Reset this to nil to force the reload From 3e4a20d919c9793c6055c681a403e6313e92b936 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 14 May 2010 23:25:12 -0700 Subject: [PATCH 09/26] Slight cosmetic changes to get things working with emacs highlighter and commenting --- lib/vagrant/environment.rb | 6 +++++- lib/vagrant/vm.rb | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb index 5aab4c005..632d290c2 100644 --- a/lib/vagrant/environment.rb +++ b/lib/vagrant/environment.rb @@ -23,7 +23,7 @@ module Vagrant #--------------------------------------------------------------- # Class Methods #--------------------------------------------------------------- - class < f.read } diff --git a/lib/vagrant/vm.rb b/lib/vagrant/vm.rb index e3fa18985..25744163f 100644 --- a/lib/vagrant/vm.rb +++ b/lib/vagrant/vm.rb @@ -6,7 +6,7 @@ module Vagrant attr_reader :system attr_accessor :vm - class < opts[:env].cwd, :parent => opts[:env], :vm_name => opts[:vm_name], :vm => self }).load! + + # Load the associated system. load_system! end end From 689a41680974c5df215af8e51218d6c203bada8d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 13 May 2010 13:00:00 -0700 Subject: [PATCH 10/26] Fixed issue with JSON 1.4.3 [closes GH-62] --- Gemfile | 4 ++-- lib/vagrant/config.rb | 4 ++-- lib/vagrant/provisioners/chef.rb | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index 4dc65e273..9018dc64f 100644 --- a/Gemfile +++ b/Gemfile @@ -1,10 +1,10 @@ source :gemcutter # Gems required for the lib to even run -gem "virtualbox", "~> 0.6.2" +gem "virtualbox", :git => "git://github.com/mitchellh/virtualbox.git" gem "net-ssh", ">= 2.0.19" gem "net-scp", ">= 1.0.2" -gem "json", ">= 1.2.0" +gem "json", ">= 1.2.4" gem "archive-tar-minitar", "= 0.5.2" gem "mario", "~> 0.0.6" diff --git a/lib/vagrant/config.rb b/lib/vagrant/config.rb index c75b6ac26..9cd8c32a2 100644 --- a/lib/vagrant/config.rb +++ b/lib/vagrant/config.rb @@ -47,8 +47,8 @@ module Vagrant send(key) end - def to_json - instance_variables_hash.to_json + def to_json(*a) + instance_variables_hash.to_json(*a) end def instance_variables_hash diff --git a/lib/vagrant/provisioners/chef.rb b/lib/vagrant/provisioners/chef.rb index 2be95980b..f554fd98a 100644 --- a/lib/vagrant/provisioners/chef.rb +++ b/lib/vagrant/provisioners/chef.rb @@ -59,12 +59,12 @@ module Vagrant run_list << name end - def to_json + def to_json(*a) # Overridden so that the 'json' key could be removed, since its just # merged into the config anyways data = instance_variables_hash data.delete(:json) - data.to_json + data.to_json(*a) end end From 33bfe75cbde872a529a3b0b42f1e770cef3158db Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 15 May 2010 01:34:31 -0700 Subject: [PATCH 11/26] Environment loads blank VMs for non-created VMs. --- Gemfile | 1 + VERSION | 2 +- lib/vagrant/command.rb | 4 +- lib/vagrant/commands/up.rb | 20 +++--- lib/vagrant/environment.rb | 30 ++++++--- test/vagrant/environment_test.rb | 50 +++++++++++++-- vagrant.gemspec | 102 +++++++++++++++---------------- 7 files changed, 136 insertions(+), 73 deletions(-) diff --git a/Gemfile b/Gemfile index 9018dc64f..2cfcecca7 100644 --- a/Gemfile +++ b/Gemfile @@ -14,4 +14,5 @@ group :test do gem "contest", ">= 0.1.2" gem "mocha" gem "ruby-debug", ">= 0.10.3" if RUBY_VERSION < '1.9' + gem "jeweler", "~> 1.4.0" end diff --git a/VERSION b/VERSION index 162013faa..02bee0ac4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.3.dev \ No newline at end of file +0.3.4.dev \ No newline at end of file diff --git a/lib/vagrant/command.rb b/lib/vagrant/command.rb index 1f63e20fb..686c2d6e2 100644 --- a/lib/vagrant/command.rb +++ b/lib/vagrant/command.rb @@ -5,7 +5,7 @@ module Vagrant class Command attr_reader :env - class < f.read } + data = { DEFAULT_VM => f.read } begin - data = JSON.parse(data[:__vagrant]) + data = JSON.parse(data[DEFAULT_VM]) rescue JSON::ParserError # Most likely an older (<= 0.3.x) dotfile. Try to load it # as the :__vagrant VM. @@ -231,6 +231,20 @@ module Vagrant # Just rescue it. end + # Loads blank VMs into the `vms` attribute. + def load_blank_vms! + # Clear existing vms + vms.clear + + # Load up the blank VMs + defined_vms = config.vm.defined_vms.keys + defined_vms = [DEFAULT_VM] if defined_vms.empty? + + defined_vms.each do |name| + vms[name] = Vagrant::VM.new(:vm_name => name, :env => self) + end + end + # Loads the activelist for this environment def load_active_list! @active_list = ActiveList.new(self) diff --git a/test/vagrant/environment_test.rb b/test/vagrant/environment_test.rb index 4260ce7b7..32a085431 100644 --- a/test/vagrant/environment_test.rb +++ b/test/vagrant/environment_test.rb @@ -372,12 +372,20 @@ class EnvironmentTest < Test::Unit::TestCase File.stubs(:file?).returns(true) end + should "blank the VMs" do + load_seq = sequence("load_seq") + @env.stubs(:root_path).returns("foo") + @env.expects(:load_blank_vms!).in_sequence(load_seq) + File.expects(:open).in_sequence(load_seq) + @env.load_vm! + end + should "load the UUID if the JSON parsing fails" do vm = mock("vm") filemock = mock("filemock") filemock.expects(:read).returns("foo") - Vagrant::VM.expects(:find).with("foo", @env, :__vagrant).returns(vm) + Vagrant::VM.expects(:find).with("foo", @env, Vagrant::Environment::DEFAULT_VM).returns(vm) File.expects(:open).with(@env.dotfile_path).once.yields(filemock) File.expects(:file?).with(@env.dotfile_path).once.returns(true) @env.load_vm! @@ -413,10 +421,13 @@ class EnvironmentTest < Test::Unit::TestCase @env.load_vm! end - should "do nothing if the root path is nil" do + should "do nothing if the dotfile is nil" do + @env.stubs(:dotfile_path).returns(nil) File.expects(:open).never - @env.stubs(:root_path).returns(nil) - @env.load_vm! + + assert_nothing_raised { + @env.load_vm! + } end should "do nothing if dotfile is not a file" do @@ -432,6 +443,37 @@ class EnvironmentTest < Test::Unit::TestCase end end + context "loading blank VMs" do + setup do + @env = mock_environment + end + + should "blank the VMs" do + @env = mock_environment do |config| + config.vm.define :foo do |config| + end + + config.vm.define :bar do |config| + end + end + + @env.load_blank_vms! + + assert_equal 2, @env.vms.length + assert_equal [:foo, :bar], @env.vms.keys + assert(@env.vms.all? { |name, vm| !vm.created? }) + end + + should "load the default VM blank if no multi-VMs are specified" do + assert @env.config.vm.defined_vms.empty? # sanity + + @env.load_blank_vms! + + assert_equal 1, @env.vms.length + assert !@env.vms.values.first.created? + end + end + context "loading the active list" do setup do @env = mock_environment diff --git a/vagrant.gemspec b/vagrant.gemspec index fd2fd4d2f..d005ca06d 100644 --- a/vagrant.gemspec +++ b/vagrant.gemspec @@ -5,11 +5,11 @@ Gem::Specification.new do |s| s.name = %q{vagrant} - s.version = "0.3.3.dev" + s.version = "0.3.4.dev" s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version= s.authors = ["Mitchell Hashimoto", "John Bender"] - s.date = %q{2010-05-01} + s.date = %q{2010-05-15} s.default_executable = %q{vagrant} s.description = %q{Vagrant is a tool for building and distributing virtualized development environments.} s.email = ["mitchell.hashimoto@gmail.com", "john.m.bender@gmail.com"] @@ -175,67 +175,67 @@ Gem::Specification.new do |s| s.summary = %q{Vagrant is a tool for building and distributing virtualized development environments.} s.test_files = [ "test/test_helper.rb", - "test/vagrant/actions/base_test.rb", - "test/vagrant/actions/box/add_test.rb", - "test/vagrant/actions/box/destroy_test.rb", - "test/vagrant/actions/box/download_test.rb", - "test/vagrant/actions/box/unpackage_test.rb", - "test/vagrant/actions/box/verify_test.rb", - "test/vagrant/actions/collection_test.rb", - "test/vagrant/actions/runner_test.rb", - "test/vagrant/actions/vm/boot_test.rb", - "test/vagrant/actions/vm/customize_test.rb", - "test/vagrant/actions/vm/destroy_test.rb", - "test/vagrant/actions/vm/down_test.rb", - "test/vagrant/actions/vm/export_test.rb", - "test/vagrant/actions/vm/forward_ports_test.rb", - "test/vagrant/actions/vm/halt_test.rb", - "test/vagrant/actions/vm/import_test.rb", - "test/vagrant/actions/vm/move_hard_drive_test.rb", - "test/vagrant/actions/vm/package_test.rb", - "test/vagrant/actions/vm/provision_test.rb", - "test/vagrant/actions/vm/reload_test.rb", - "test/vagrant/actions/vm/resume_test.rb", - "test/vagrant/actions/vm/shared_folders_test.rb", - "test/vagrant/actions/vm/start_test.rb", - "test/vagrant/actions/vm/suspend_test.rb", - "test/vagrant/actions/vm/up_test.rb", - "test/vagrant/active_list_test.rb", + "test/vagrant/vm_test.rb", + "test/vagrant/command_test.rb", + "test/vagrant/environment_test.rb", + "test/vagrant/util_test.rb", "test/vagrant/box_test.rb", "test/vagrant/busy_test.rb", - "test/vagrant/command_test.rb", + "test/vagrant/provisioners/base_test.rb", + "test/vagrant/provisioners/chef_test.rb", + "test/vagrant/provisioners/chef_server_test.rb", + "test/vagrant/provisioners/chef_solo_test.rb", + "test/vagrant/systems/linux_test.rb", + "test/vagrant/config_test.rb", + "test/vagrant/actions/base_test.rb", + "test/vagrant/actions/runner_test.rb", + "test/vagrant/actions/box/verify_test.rb", + "test/vagrant/actions/box/destroy_test.rb", + "test/vagrant/actions/box/add_test.rb", + "test/vagrant/actions/box/unpackage_test.rb", + "test/vagrant/actions/box/download_test.rb", + "test/vagrant/actions/collection_test.rb", + "test/vagrant/actions/vm/reload_test.rb", + "test/vagrant/actions/vm/suspend_test.rb", + "test/vagrant/actions/vm/boot_test.rb", + "test/vagrant/actions/vm/package_test.rb", + "test/vagrant/actions/vm/down_test.rb", + "test/vagrant/actions/vm/shared_folders_test.rb", + "test/vagrant/actions/vm/destroy_test.rb", + "test/vagrant/actions/vm/halt_test.rb", + "test/vagrant/actions/vm/import_test.rb", + "test/vagrant/actions/vm/customize_test.rb", + "test/vagrant/actions/vm/start_test.rb", + "test/vagrant/actions/vm/move_hard_drive_test.rb", + "test/vagrant/actions/vm/up_test.rb", + "test/vagrant/actions/vm/export_test.rb", + "test/vagrant/actions/vm/provision_test.rb", + "test/vagrant/actions/vm/resume_test.rb", + "test/vagrant/actions/vm/forward_ports_test.rb", + "test/vagrant/active_list_test.rb", "test/vagrant/commands/base_test.rb", - "test/vagrant/commands/box/add_test.rb", - "test/vagrant/commands/box/list_test.rb", - "test/vagrant/commands/box/remove_test.rb", + "test/vagrant/commands/reload_test.rb", + "test/vagrant/commands/ssh_config_test.rb", + "test/vagrant/commands/suspend_test.rb", + "test/vagrant/commands/package_test.rb", + "test/vagrant/commands/status_test.rb", + "test/vagrant/commands/init_test.rb", "test/vagrant/commands/destroy_test.rb", "test/vagrant/commands/halt_test.rb", - "test/vagrant/commands/init_test.rb", - "test/vagrant/commands/package_test.rb", - "test/vagrant/commands/reload_test.rb", - "test/vagrant/commands/resume_test.rb", - "test/vagrant/commands/ssh_config_test.rb", - "test/vagrant/commands/ssh_test.rb", - "test/vagrant/commands/status_test.rb", - "test/vagrant/commands/suspend_test.rb", + "test/vagrant/commands/box/remove_test.rb", + "test/vagrant/commands/box/add_test.rb", + "test/vagrant/commands/box/list_test.rb", "test/vagrant/commands/up_test.rb", - "test/vagrant/config_test.rb", + "test/vagrant/commands/resume_test.rb", + "test/vagrant/commands/ssh_test.rb", "test/vagrant/downloaders/base_test.rb", "test/vagrant/downloaders/file_test.rb", "test/vagrant/downloaders/http_test.rb", - "test/vagrant/environment_test.rb", - "test/vagrant/provisioners/base_test.rb", - "test/vagrant/provisioners/chef_server_test.rb", - "test/vagrant/provisioners/chef_solo_test.rb", - "test/vagrant/provisioners/chef_test.rb", - "test/vagrant/ssh_test.rb", - "test/vagrant/systems/linux_test.rb", - "test/vagrant/util/progress_meter_test.rb", "test/vagrant/util/stacked_proc_runner_test.rb", + "test/vagrant/util/progress_meter_test.rb", "test/vagrant/util/template_renderer_test.rb", "test/vagrant/util/translator_test.rb", - "test/vagrant/util_test.rb", - "test/vagrant/vm_test.rb" + "test/vagrant/ssh_test.rb" ] if s.respond_to? :specification_version then From a56f4a43ddc3bc38874376cb8cf365014c18ad97 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 15 May 2010 01:51:45 -0700 Subject: [PATCH 12/26] Up command will up multiple VMs (persisting doesn't work yet) --- lib/vagrant/commands/up.rb | 4 +-- test/vagrant/commands/up_test.rb | 45 +++++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/lib/vagrant/commands/up.rb b/lib/vagrant/commands/up.rb index d8519a15b..6a15a52e2 100644 --- a/lib/vagrant/commands/up.rb +++ b/lib/vagrant/commands/up.rb @@ -16,10 +16,10 @@ module Vagrant env.vms.each do |name, vm| if vm.created? logger.info "VM '#{name}' already created. Booting if its not already running..." - # vm.start + vm.start else logger.info "Creating VM '#{name}'" - # env.create_vm.execute!(Actions::VM::Up) + vm.execute!(Actions::VM::Up) end end end diff --git a/test/vagrant/commands/up_test.rb b/test/vagrant/commands/up_test.rb index 777df4cbf..ba2aed82b 100644 --- a/test/vagrant/commands/up_test.rb +++ b/test/vagrant/commands/up_test.rb @@ -16,25 +16,50 @@ class CommandsUpTest < Test::Unit::TestCase @new_vm = mock("vm") @new_vm.stubs(:execute!) - @env.stubs(:vm).returns(nil) + @vms = {} + + @env.stubs(:vms).returns(@vms) @env.stubs(:require_box) - @env.stubs(:create_vm).returns(@new_vm) end - should "require a box" do - @env.expects(:require_box).once + def create_vm + env = mock_environment + env.stubs(:require_box) + + vm = mock("vm") + vm.stubs(:env).returns(env) + vm.stubs(:execute!) + vm.stubs(:created?).returns(false) + vm + end + + should "require a box for all VMs" do + @vms[:foo] = create_vm + @vms[:bar] = create_vm + + @vms.each do |name, vm| + vm.env.expects(:require_box).once + end + @instance.execute end - should "call the up action on VM if it doesn't exist" do - @new_vm.expects(:execute!).with(Vagrant::Actions::VM::Up).once + should "start created VMs" do + vm = create_vm + vm.stubs(:created?).returns(true) + + @vms[:foo] = vm + + vm.expects(:start).once @instance.execute end - should "call start on the persisted vm if it exists" do - @env.stubs(:vm).returns(@persisted_vm) - @persisted_vm.expects(:start).once - @env.expects(:create_vm).never + should "up non-created VMs" do + vm = create_vm + vm.expects(:execute!).with(Vagrant::Actions::VM::Up).once + vm.expects(:start).never + + @vms[:foo] = vm @instance.execute end end From 0314e6ef6cab8bc495cf590c143534c055bdbf34 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 16 May 2010 16:53:56 -0700 Subject: [PATCH 13/26] Persist/depersist VM methods replaced with dotfile updates. Active list information disabled for now. --- lib/vagrant/actions/vm/up.rb | 6 +-- lib/vagrant/environment.rb | 28 +++++----- test/vagrant/actions/vm/up_test.rb | 12 ++--- test/vagrant/environment_test.rb | 85 +++++++++++++++--------------- 4 files changed, 67 insertions(+), 64 deletions(-) diff --git a/lib/vagrant/actions/vm/up.rb b/lib/vagrant/actions/vm/up.rb index 65b8ea6ff..7922e4275 100644 --- a/lib/vagrant/actions/vm/up.rb +++ b/lib/vagrant/actions/vm/up.rb @@ -19,13 +19,13 @@ module Vagrant end def after_import - persist + update_dotfile setup_mac_address end - def persist + def update_dotfile logger.info "Persisting the VM UUID (#{@runner.uuid})..." - @runner.env.persist_vm + @runner.env.update_dotfile end def setup_mac_address diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb index f5c19fd50..bcd220d95 100644 --- a/lib/vagrant/environment.rb +++ b/lib/vagrant/environment.rb @@ -263,23 +263,25 @@ module Vagrant # Persists this environment's VM to the dotfile so it can be # re-loaded at a later time. - def persist_vm - # Save to the dotfile for this project + def update_dotfile + if parent + parent.update_dotfile + return + end + + # Generate and save the persisted VM info + data = vms.inject({}) do |acc, data| + key, value = data + acc[key] = value.uuid if value.created? + acc + end + File.open(dotfile_path, 'w+') do |f| - f.write(vm.uuid) + f.write(data.to_json) end # Also add to the global store - active_list.add(vm) - end - - # Removes this environment's VM from the dotfile. - def depersist_vm - # Delete the dotfile if it exists - File.delete(dotfile_path) if File.exist?(dotfile_path) - - # Remove from the global store - active_list.remove(vm) + # active_list.add(vm) end #--------------------------------------------------------------- diff --git a/test/vagrant/actions/vm/up_test.rb b/test/vagrant/actions/vm/up_test.rb index c7fde915d..a52dcb382 100644 --- a/test/vagrant/actions/vm/up_test.rb +++ b/test/vagrant/actions/vm/up_test.rb @@ -63,19 +63,19 @@ class UpActionTest < Test::Unit::TestCase end context "callbacks" do - should "call persist and mac address setup after import" do + should "call update dotfile and mac address setup after import" do boot_seq = sequence("boot") - @action.expects(:persist).once.in_sequence(boot_seq) + @action.expects(:update_dotfile).once.in_sequence(boot_seq) @action.expects(:setup_mac_address).once.in_sequence(boot_seq) @action.after_import end end - context "persisting" do - should "persist the VM with Env" do + context "updating the dotfile" do + should "call update dotfile on the VM's environment" do @runner.stubs(:uuid) - @runner.env.expects(:persist_vm).once - @action.persist + @runner.env.expects(:update_dotfile).once + @action.update_dotfile end end diff --git a/test/vagrant/environment_test.rb b/test/vagrant/environment_test.rb index 32a085431..f82dfd87a 100644 --- a/test/vagrant/environment_test.rb +++ b/test/vagrant/environment_test.rb @@ -460,8 +460,10 @@ class EnvironmentTest < Test::Unit::TestCase @env.load_blank_vms! assert_equal 2, @env.vms.length - assert_equal [:foo, :bar], @env.vms.keys assert(@env.vms.all? { |name, vm| !vm.created? }) + + sorted_vms = @env.vms.keys.sort { |a,b| a.to_s <=> b.to_s } + assert_equal [:bar, :foo], sorted_vms end should "load the default VM blank if no multi-VMs are specified" do @@ -593,54 +595,53 @@ class EnvironmentTest < Test::Unit::TestCase @vm.stubs(:uuid).returns("foo") @env.stubs(:vm).returns(@vm) end + end - context "persisting the VM into a file" do - setup do - mock_vm - - File.stubs(:open) - @env.active_list.stubs(:add) - end - - should "should save it to the dotfile path" do - filemock = mock("filemock") - filemock.expects(:write).with(@vm.uuid) - File.expects(:open).with(@env.dotfile_path, 'w+').once.yields(filemock) - @env.persist_vm - end - - should "add the VM to the activelist" do - @env.active_list.expects(:add).with(@vm) - @env.persist_vm - end + context "updating the dotfile" do + setup do + @env = mock_environment + @env.stubs(:parent).returns(nil) + @env.stubs(:dotfile_path).returns("foo") + File.stubs(:open) end - context "depersisting the VM" do - setup do - mock_vm + def create_vm(created) + vm = mock("vm") + vm.stubs(:created?).returns(created) + vm.stubs(:uuid).returns("foo") + vm + end - File.stubs(:exist?).returns(false) - File.stubs(:delete) + should "call parent if exists" do + parent = mock("parent") + @env.stubs(:parent).returns(parent) + parent.expects(:update_dotfile).once - @env.active_list.stubs(:remove) + @env.update_dotfile + end + + should "write the proper data to dotfile" do + vms = { + :foo => create_vm(false), + :bar => create_vm(true), + :baz => create_vm(true) + } + + f = mock("f") + @env.stubs(:vms).returns(vms) + File.expects(:open).with(@env.dotfile_path, 'w+').yields(f) + f.expects(:write).with() do |json| + assert_nothing_raised { + data = JSON.parse(json) + assert_equal 2, data.length + assert_equal vms[:bar].uuid, data["bar"] + assert_equal vms[:baz].uuid, data["baz"] + } + + true end - should "remove the dotfile if it exists" do - File.expects(:exist?).with(@env.dotfile_path).returns(true) - File.expects(:delete).with(@env.dotfile_path).once - @env.depersist_vm - end - - should "not remove the dotfile if it doesn't exist" do - File.expects(:exist?).returns(false) - File.expects(:delete).never - @env.depersist_vm - end - - should "remove from the active list" do - @env.active_list.expects(:remove).with(@vm) - @env.depersist_vm - end + @env.update_dotfile end end end From 709c50e7b63df0dba3b808178bd5a2429262ebcc Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 16 May 2010 17:40:40 -0700 Subject: [PATCH 14/26] `vagrant status` updated to work with multi-VM. Global status not yet functional. --- lib/vagrant/commands/base.rb | 5 +- lib/vagrant/commands/status.rb | 72 ++++++++++++++++++++++------ lib/vagrant/environment.rb | 11 +++++ lib/vagrant/vm.rb | 4 +- templates/strings.yml | 4 ++ test/vagrant/commands/base_test.rb | 5 +- test/vagrant/commands/status_test.rb | 10 ++++ test/vagrant/environment_test.rb | 35 ++++++++++++++ vagrant.gemspec | 2 +- 9 files changed, 126 insertions(+), 22 deletions(-) diff --git a/lib/vagrant/commands/base.rb b/lib/vagrant/commands/base.rb index 664a38b03..f58e8e8fa 100644 --- a/lib/vagrant/commands/base.rb +++ b/lib/vagrant/commands/base.rb @@ -12,7 +12,7 @@ module Vagrant attr_reader :env - class < 1 + # There should never be more than 1 arg + show_help + return + end if !options[:global] - show_local_status + show_local_status(*args) else show_global_status end end # Shows the status of the CURRENT environment (the current working - # directory). This prints out a human friendly sentence or paragraph - # describing the state of the Vagrant environment represented by the - # current working directory. - def show_local_status + # directory). If a specific VM was given, it will print out + # detailed information regarding that VM. If no single VM was + # specified and it is a multi-VM environment, it will simply + # show a listing of all the VMs and their short one word + # statuses. + def show_local_status(vm=nil) + if !env.root_path + wrap_output { puts Translator.t(:status_no_environment) } + return + end + + if vm.nil? + if env.multivm? + # No specific VM was specified in a multi-vm environment, + # so show short info for each VM + show_list + else + # Set the VM to just be the root VM + vm = env.vms.values.first + end + else + # Try to get the vm based on the name. If the specified VM + # doesn't exist, then error saying so + vm = env.vms[vm.to_sym] + end + + show_single(vm) + end + + # Lists the available VMs and brief statuses about each. + def show_list + wrap_output do + puts Translator.t(:status_listing) + + env.vms.each do |name, vm| + puts "#{name.ljust(20)}#{vm.state}" + end + end + end + + # Shows a paragraph of information based on the current state of + # a single, specified VM. + def show_single(vm) string_key = nil - if !env.root_path - string_key = :status_no_environment - elsif !env.vm + if !vm.created? string_key = :status_not_created else additional_key = nil - if env.vm.vm.running? + if vm.vm.running? additional_key = :status_created_running - elsif env.vm.vm.saved? + elsif vm.vm.saved? additional_key = :status_created_saved - elsif env.vm.vm.powered_off? + elsif vm.vm.powered_off? additional_key = :status_created_powered_off end string_key = [:status_created, { - :vm_state => env.vm.vm.state, + :vm_state => vm.vm.state, :additional_message => additional_key ? Translator.t(additional_key) : "" }] end - string_key = [string_key, {}] unless string_key.is_a?(Array) wrap_output { puts Translator.t(*string_key) } end @@ -70,6 +111,7 @@ module Vagrant # Defaults options[:global] = false + options[:vm] = nil opts.on("-g", "--global", "Show global status of Vagrant (running VMs managed by Vagrant)") do |v| options[:global] = true @@ -77,4 +119,4 @@ module Vagrant end end end -end \ No newline at end of file +end diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb index bcd220d95..5e485396b 100644 --- a/lib/vagrant/environment.rb +++ b/lib/vagrant/environment.rb @@ -100,6 +100,17 @@ module Vagrant @vms ||= {} end + # Returns a boolean whether this environment represents a multi-VM + # environment or not. This will work even when called on child + # environments. + def multivm? + if parent + parent.multivm? + else + vms.length > 1 + end + end + #--------------------------------------------------------------- # Load Methods #--------------------------------------------------------------- diff --git a/lib/vagrant/vm.rb b/lib/vagrant/vm.rb index 25744163f..51059bd72 100644 --- a/lib/vagrant/vm.rb +++ b/lib/vagrant/vm.rb @@ -4,6 +4,7 @@ module Vagrant attr_reader :env attr_reader :system + attr_reader :name attr_accessor :vm class << self @@ -25,6 +26,7 @@ module Vagrant opts = defaults.merge(opts || {}) @vm = opts[:vm] + @name = opts[:vm_name] if !opts[:env].nil? # We have an environment, so we create a new child environment @@ -37,7 +39,7 @@ module Vagrant :vm => self }).load! - # Load the associated system. + # Load the associated system. load_system! end end diff --git a/templates/strings.yml b/templates/strings.yml index 7cac40e82..1f1450582 100644 --- a/templates/strings.yml +++ b/templates/strings.yml @@ -6,6 +6,10 @@ #--------------------------------------------------------------------- # CATEGORY: Status Messages #--------------------------------------------------------------------- +:status_listing: |- + This environment represents multiple VMs. The VMs will be listed + below with a short status. For more detailed information about a + VM, run `vagrant status NAME`. :status_no_environment: |- No vagrant environment detected. Run `vagrant init` to setup a Vagrantfile in the current directory to get started with Vagrant. diff --git a/test/vagrant/commands/base_test.rb b/test/vagrant/commands/base_test.rb index c2a011451..2eca395e3 100644 --- a/test/vagrant/commands/base_test.rb +++ b/test/vagrant/commands/base_test.rb @@ -112,8 +112,9 @@ class CommandsBaseTest < Test::Unit::TestCase end should "parse the options with the args" do - @option_parser.expects(:parse!).with(@args).once - assert_equal @options, @instance.parse_options(@args) + result = mock("result") + @option_parser.expects(:parse!).with(@args).once.returns(result) + assert_equal result, @instance.parse_options(@args) end end end diff --git a/test/vagrant/commands/status_test.rb b/test/vagrant/commands/status_test.rb index f975d5885..f79f66d27 100644 --- a/test/vagrant/commands/status_test.rb +++ b/test/vagrant/commands/status_test.rb @@ -26,5 +26,15 @@ class CommandsStatusTest < Test::Unit::TestCase @instance.expects(:show_global_status).once @instance.execute(["--global"]) end + + should "show help if too many args are given" do + @instance.expects(:show_help).once + @instance.execute(["1","2","3"]) + end + + should "pass the VM name to local status if given" do + @instance.expects(:show_local_status).with("foo").once + @instance.execute(["foo"]) + end end end diff --git a/test/vagrant/environment_test.rb b/test/vagrant/environment_test.rb index f82dfd87a..a1baf5a70 100644 --- a/test/vagrant/environment_test.rb +++ b/test/vagrant/environment_test.rb @@ -124,6 +124,41 @@ class EnvironmentTest < Test::Unit::TestCase end end + context "multivm? helper" do + setup do + @env = mock_environment + end + + context "with a parent" do + setup do + @parent = mock('parent') + @env.stubs(:parent).returns(@parent) + end + + should "return the value of multivm? from the parent" do + result = mock("result") + @parent.stubs(:multivm?).returns(result) + assert_equal result, @env.multivm? + end + end + + context "without a parent" do + setup do + @env.stubs(:parent).returns(nil) + end + + should "return true if VM length greater than 1" do + @env.stubs(:vms).returns([1,2,3]) + assert @env.multivm? + end + + should "return false if VM length is 1" do + @env.stubs(:vms).returns([1]) + assert !@env.multivm? + end + end + end + context "loading" do setup do @env = mock_environment diff --git a/vagrant.gemspec b/vagrant.gemspec index d005ca06d..1d545c6af 100644 --- a/vagrant.gemspec +++ b/vagrant.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |s| s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version= s.authors = ["Mitchell Hashimoto", "John Bender"] - s.date = %q{2010-05-15} + s.date = %q{2010-05-16} s.default_executable = %q{vagrant} s.description = %q{Vagrant is a tool for building and distributing virtualized development environments.} s.email = ["mitchell.hashimoto@gmail.com", "john.m.bender@gmail.com"] From dec46235cb5b12cb1d01f339197b7a928c2807b0 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 16 May 2010 17:46:20 -0700 Subject: [PATCH 15/26] Error properly if an invalid VM name is given --- lib/vagrant/commands/status.rb | 2 +- templates/strings.yml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/vagrant/commands/status.rb b/lib/vagrant/commands/status.rb index e5480ea42..adfafaf3c 100644 --- a/lib/vagrant/commands/status.rb +++ b/lib/vagrant/commands/status.rb @@ -46,7 +46,7 @@ module Vagrant else # Try to get the vm based on the name. If the specified VM # doesn't exist, then error saying so - vm = env.vms[vm.to_sym] + vm = env.vms[vm.to_sym] || error_and_exit(:unknown_vm, :vm => vm) end show_single(vm) diff --git a/templates/strings.yml b/templates/strings.yml index 1f1450582..b8025edec 100644 --- a/templates/strings.yml +++ b/templates/strings.yml @@ -154,6 +154,8 @@ :system_unspecified: |- A VM system type must be specified! This is done via the `config.vm.system` configuration value. Please read the documentation online for more information. +:unknown_vm: |- + The specified VM could not be found: <%= vm %> :virtualbox_import_failure: |- The VM import failed! Try running `VBoxManage import` on the box file manually for more verbose error output. From 07e1fb72385a11a0556eae700ff1c7c8100f966e Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 16 May 2010 17:59:18 -0700 Subject: [PATCH 16/26] `vagrant status` updated to work with multi-VM --- lib/vagrant/commands/destroy.rb | 33 +++++++++++++++-- test/vagrant/commands/destroy_test.rb | 53 +++++++++++++++++++++------ 2 files changed, 71 insertions(+), 15 deletions(-) diff --git a/lib/vagrant/commands/destroy.rb b/lib/vagrant/commands/destroy.rb index a67d61b1d..55c601950 100644 --- a/lib/vagrant/commands/destroy.rb +++ b/lib/vagrant/commands/destroy.rb @@ -11,8 +11,35 @@ module Vagrant description "Destroys the vagrant environment" def execute(args=[]) - env.require_persisted_vm - env.vm.destroy + args = parse_options(args) + + if args[0] + destroy_single(args[0]) + else + destroy_all + end + end + + # Destroys a single VM by name. + def destroy_single(name) + vm = env.vms[name.to_sym] + if vm.nil? + error_and_exit(:unknown_vm, :vm => name) + return # for tests + end + + if vm.created? + vm.destroy + else + logger.info "VM '#{name}' not created. Ignoring." + end + end + + # Destroys all VMs represented by the current environment. + def destroy_all + env.vms.each do |name, vm| + destroy_single(name) + end end def options_spec(opts) @@ -20,4 +47,4 @@ module Vagrant end end end -end \ No newline at end of file +end diff --git a/test/vagrant/commands/destroy_test.rb b/test/vagrant/commands/destroy_test.rb index e3d770563..561695deb 100644 --- a/test/vagrant/commands/destroy_test.rb +++ b/test/vagrant/commands/destroy_test.rb @@ -4,29 +4,58 @@ class CommandsDestroyTest < Test::Unit::TestCase setup do @klass = Vagrant::Commands::Destroy - @persisted_vm = mock("persisted_vm") - @persisted_vm.stubs(:execute!) - @env = mock_environment - @env.stubs(:require_persisted_vm) - @env.stubs(:vm).returns(@persisted_vm) @instance = @klass.new(@env) end context "executing" do + should "call destroy_all if no name is given" do + @instance.expects(:destroy_all).once + @instance.execute + end + + should "call destroy_single if a name is given" do + @instance.expects(:destroy_single).with("foo").once + @instance.execute(["foo"]) + end + end + + context "destroying all" do + should "destroy each VM" do + vms = { :foo => nil, :bar => nil, :baz => nil } + @env.stubs(:vms).returns(vms) + + vms.each do |name, value| + @instance.expects(:destroy_single).with(name).once + end + + @instance.destroy_all + end + end + + context "destroying a single VM" do setup do - @persisted_vm.stubs(:destroy) + @foo_vm = mock("vm") + vms = { :foo => @foo_vm } + @env.stubs(:vms).returns(vms) + end + should "error and exit if the VM doesn't exist" do + @env.stubs(:vms).returns({}) + @instance.expects(:error_and_exit).with(:unknown_vm, :vm => :foo).once + @instance.destroy_single(:foo) end - should "require a persisted VM" do - @env.expects(:require_persisted_vm).once - @instance.execute + should "destroy if its created" do + @foo_vm.stubs(:created?).returns(true) + @foo_vm.expects(:destroy).once + @instance.destroy_single(:foo) end - should "destroy the persisted VM and the VM image" do - @persisted_vm.expects(:destroy).once - @instance.execute + should "do nothing if its not created" do + @foo_vm.stubs(:created?).returns(false) + @foo_vm.expects(:destroy).never + @instance.destroy_single(:foo) end end end From 5be932bf6c09b27f624e89c243774807fe4a4670 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 16 May 2010 18:01:53 -0700 Subject: [PATCH 17/26] Destroying properly updates the dotfile --- lib/vagrant/actions/vm/destroy.rb | 7 ++++--- test/vagrant/actions/vm/destroy_test.rb | 11 ++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/vagrant/actions/vm/destroy.rb b/lib/vagrant/actions/vm/destroy.rb index 45fc92438..b65f883de 100644 --- a/lib/vagrant/actions/vm/destroy.rb +++ b/lib/vagrant/actions/vm/destroy.rb @@ -5,17 +5,18 @@ module Vagrant def execute! @runner.invoke_around_callback(:destroy) do destroy_vm - depersist + update_dotfile end end def destroy_vm logger.info "Destroying VM and associated drives..." @runner.vm.destroy(:destroy_medium => :delete) + @runner.vm = nil end - def depersist - @runner.env.depersist_vm + def update_dotfile + @runner.env.update_dotfile end end end diff --git a/test/vagrant/actions/vm/destroy_test.rb b/test/vagrant/actions/vm/destroy_test.rb index 4b747dc53..896c9d583 100644 --- a/test/vagrant/actions/vm/destroy_test.rb +++ b/test/vagrant/actions/vm/destroy_test.rb @@ -15,7 +15,7 @@ class DestroyActionTest < Test::Unit::TestCase @runner.stubs(:invoke_around_callback).yields clear_seq = sequence("clear") @action.expects(:destroy_vm).in_sequence(clear_seq) - @action.expects(:depersist).in_sequence(clear_seq) + @action.expects(:update_dotfile).in_sequence(clear_seq) @action.execute! end end @@ -23,14 +23,15 @@ class DestroyActionTest < Test::Unit::TestCase context "destroying the VM" do should "destroy VM and attached images" do @vm.expects(:destroy).with(:destroy_medium => :delete).once + @runner.expects(:vm=).with(nil).once @action.destroy_vm end end - context "depersisting" do - should "call depersist_vm on Env" do - @runner.env.expects(:depersist_vm).once - @action.depersist + context "updating the dotfile" do + should "update the environment dotfile" do + @runner.env.expects(:update_dotfile).once + @action.update_dotfile end end end From 50086423eeecbc46e2282acebf8696cea5cf5908 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 16 May 2010 18:11:51 -0700 Subject: [PATCH 18/26] Fixed some formatting issues with `vagrant status` --- lib/vagrant/commands/status.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/vagrant/commands/status.rb b/lib/vagrant/commands/status.rb index adfafaf3c..795779042 100644 --- a/lib/vagrant/commands/status.rb +++ b/lib/vagrant/commands/status.rb @@ -39,6 +39,7 @@ module Vagrant # No specific VM was specified in a multi-vm environment, # so show short info for each VM show_list + return else # Set the VM to just be the root VM vm = env.vms.values.first @@ -56,9 +57,11 @@ module Vagrant def show_list wrap_output do puts Translator.t(:status_listing) + puts "" env.vms.each do |name, vm| - puts "#{name.ljust(20)}#{vm.state}" + state = vm.created? ? vm.vm.state : "not created" + puts "#{name.to_s.ljust(30)}#{state}" end end end From 3b4d2ab7951b296bdc52ea5d61efadd03fc70c64 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 16 May 2010 18:22:28 -0700 Subject: [PATCH 19/26] `vagrant halt` updated to work with multi-VM --- lib/vagrant/commands/halt.rb | 31 ++++++++++++++--- lib/vagrant/vm.rb | 4 +++ test/vagrant/commands/halt_test.rb | 56 +++++++++++++++++++++++++----- test/vagrant/vm_test.rb | 12 +++++++ 4 files changed, 90 insertions(+), 13 deletions(-) diff --git a/lib/vagrant/commands/halt.rb b/lib/vagrant/commands/halt.rb index 4572aa2b8..337dff2b5 100644 --- a/lib/vagrant/commands/halt.rb +++ b/lib/vagrant/commands/halt.rb @@ -11,10 +11,33 @@ module Vagrant description "Halts the currently running vagrant environment" def execute(args=[]) - parse_options(args) + args = parse_options(args) - env.require_persisted_vm - env.vm.execute!(Actions::VM::Halt, options[:force]) + if args[0] + halt_single(args[0]) + else + halt_all + end + end + + def halt_single(name) + vm = env.vms[name.to_sym] + if vm.nil? + error_and_exit(:unknown_vm, :vm => name) + return # for tests + end + + if vm.created? + vm.halt(options[:force]) + else + logger.info "VM '#{name}' not created. Ignoring." + end + end + + def halt_all + env.vms.keys.each do |name| + halt_single(name) + end end def options_spec(opts) @@ -29,4 +52,4 @@ module Vagrant end end end -end \ No newline at end of file +end diff --git a/lib/vagrant/vm.rb b/lib/vagrant/vm.rb index 51059bd72..618213519 100644 --- a/lib/vagrant/vm.rb +++ b/lib/vagrant/vm.rb @@ -105,6 +105,10 @@ module Vagrant execute!(Actions::VM::Start) end + def halt(force=false) + execute!(Actions::VM::Halt, force) + end + def destroy execute!(Actions::VM::Down) end diff --git a/test/vagrant/commands/halt_test.rb b/test/vagrant/commands/halt_test.rb index 5b97f2273..08ef2b069 100644 --- a/test/vagrant/commands/halt_test.rb +++ b/test/vagrant/commands/halt_test.rb @@ -15,21 +15,59 @@ class CommandsHaltTest < Test::Unit::TestCase end context "executing" do - should "require a persisted VM" do - @env.expects(:require_persisted_vm).once + should "call halt_all if no name is given" do + @instance.expects(:halt_all).once @instance.execute end - should "call the `halt` action on the VM" do - @persisted_vm.expects(:execute!).with(Vagrant::Actions::VM::Halt, false).once - @instance.execute + should "call halt_single if a name is given" do + @instance.expects(:halt_single).with("foo").once + @instance.execute(["foo"]) end + end - should "be forceful if -f flag is sent" do - %w{--force -f}.each do |flag| - @persisted_vm.expects(:execute!).with(Vagrant::Actions::VM::Halt, true).once - @instance.execute([flag]) + context "halting all" do + should "halt each VM" do + vms = { :foo => nil, :bar => nil, :baz => nil } + @env.stubs(:vms).returns(vms) + + vms.each do |name, value| + @instance.expects(:halt_single).with(name).once end + + @instance.halt_all + end + end + + context "halting a single VM" do + setup do + @foo_vm = mock("vm") + vms = { :foo => @foo_vm } + @env.stubs(:vms).returns(vms) + end + + should "error and exit if the VM doesn't exist" do + @env.stubs(:vms).returns({}) + @instance.expects(:error_and_exit).with(:unknown_vm, :vm => :foo).once + @instance.halt_single(:foo) + end + + should "halt if its created" do + @foo_vm.stubs(:created?).returns(true) + @foo_vm.expects(:halt).with(false).once + @instance.execute(["foo"]) + end + + should "halt and force if specified" do + @foo_vm.stubs(:created?).returns(true) + @foo_vm.expects(:halt).with(true).once + @instance.execute(["foo", "--force"]) + end + + should "do nothing if its not created" do + @foo_vm.stubs(:created?).returns(false) + @foo_vm.expects(:halt).never + @instance.halt_single(:foo) end end end diff --git a/test/vagrant/vm_test.rb b/test/vagrant/vm_test.rb index c62548f51..597f95e9c 100644 --- a/test/vagrant/vm_test.rb +++ b/test/vagrant/vm_test.rb @@ -161,6 +161,18 @@ class VMTest < Test::Unit::TestCase end end + context "halting" do + should "execute the halt action" do + @vm.expects(:execute!).with(Vagrant::Actions::VM::Halt, false).once + @vm.halt + end + + should "force if specified" do + @vm.expects(:execute!).with(Vagrant::Actions::VM::Halt, true).once + @vm.halt(true) + end + end + context "destroying" do should "execute the down action" do @vm.expects(:execute!).with(Vagrant::Actions::VM::Down).once From 81e1e8932a563ab87bc0f1852c606f3984fe3828 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 16 May 2010 18:34:35 -0700 Subject: [PATCH 20/26] Can now specify which VMs to up with `vagrant up` or will up all by default --- lib/vagrant/commands/up.rb | 38 ++++++++++++--- lib/vagrant/vm.rb | 4 ++ test/vagrant/commands/halt_test.rb | 6 --- test/vagrant/commands/up_test.rb | 78 +++++++++++++++++------------- test/vagrant/vm_test.rb | 7 +++ 5 files changed, 86 insertions(+), 47 deletions(-) diff --git a/lib/vagrant/commands/up.rb b/lib/vagrant/commands/up.rb index 6a15a52e2..41336ece7 100644 --- a/lib/vagrant/commands/up.rb +++ b/lib/vagrant/commands/up.rb @@ -9,18 +9,40 @@ module Vagrant description "Creates the vagrant environment" def execute(args=[]) + args = parse_options(args) + + if args[0] + up_single(args[0]) + else + up_all + end + end + + def up_single(name) + vm = env.vms[name.to_sym] + if vm.nil? + error_and_exit(:unknown_vm, :vm => name) + return # for tests + end + + if vm.created? + logger.info "VM '#{name}' already created. Booting if its not already running..." + vm.start + else + vm.env.require_box + + logger.info "Creating VM '#{name}'" + vm.up + end + end + + def up_all # First verify that all VMs have valid boxes env.vms.each { |name, vm| vm.env.require_box unless vm.created? } # Next, handle each VM - env.vms.each do |name, vm| - if vm.created? - logger.info "VM '#{name}' already created. Booting if its not already running..." - vm.start - else - logger.info "Creating VM '#{name}'" - vm.execute!(Actions::VM::Up) - end + env.vms.keys.each do |name| + up_single(name) end end diff --git a/lib/vagrant/vm.rb b/lib/vagrant/vm.rb index 618213519..15e2f2415 100644 --- a/lib/vagrant/vm.rb +++ b/lib/vagrant/vm.rb @@ -99,6 +99,10 @@ module Vagrant execute! end + def up + execute!(Actions::VM::Up) + end + def start return if @vm.running? diff --git a/test/vagrant/commands/halt_test.rb b/test/vagrant/commands/halt_test.rb index 08ef2b069..3cbe61b45 100644 --- a/test/vagrant/commands/halt_test.rb +++ b/test/vagrant/commands/halt_test.rb @@ -4,13 +4,7 @@ class CommandsHaltTest < Test::Unit::TestCase setup do @klass = Vagrant::Commands::Halt - @persisted_vm = mock("persisted_vm") - @persisted_vm.stubs(:execute!) - @env = mock_environment - @env.stubs(:require_persisted_vm) - @env.stubs(:vm).returns(@persisted_vm) - @instance = @klass.new(@env) end diff --git a/test/vagrant/commands/up_test.rb b/test/vagrant/commands/up_test.rb index ba2aed82b..9a0d0c5f0 100644 --- a/test/vagrant/commands/up_test.rb +++ b/test/vagrant/commands/up_test.rb @@ -6,29 +6,59 @@ class CommandsUpTest < Test::Unit::TestCase @env = mock_environment @instance = @klass.new(@env) - - @persisted_vm = mock("persisted_vm") - @persisted_vm.stubs(:execute!) end context "executing" do + should "call up_all if no name is given" do + @instance.expects(:up_all).once + @instance.execute + end + + should "call up_single if a name is given" do + @instance.expects(:up_single).with("foo").once + @instance.execute(["foo"]) + end + end + + context "upping a single VM" do setup do - @new_vm = mock("vm") - @new_vm.stubs(:execute!) - - @vms = {} + @vm = mock("vm") + @vm.stubs(:env).returns(@env) + @vms = {:foo => @vm} + @env.stubs(:vms).returns(@vms) + end + + should "error and exit if the VM doesn't exist" do + @env.stubs(:vms).returns({}) + @instance.expects(:error_and_exit).with(:unknown_vm, :vm => :foo).once + @instance.up_single(:foo) + end + + should "start created VMs" do + @vm.stubs(:created?).returns(true) + @vm.expects(:start).once + @instance.up_single(:foo) + end + + should "up non-created VMs" do + @vm.stubs(:created?).returns(false) + @vm.env.expects(:require_box).once + @vm.expects(:up).once + @vm.expects(:start).never + @instance.up_single(:foo) + end + end + + context "upping all VMs" do + setup do + @vms = {} @env.stubs(:vms).returns(@vms) - @env.stubs(:require_box) end def create_vm - env = mock_environment - env.stubs(:require_box) - vm = mock("vm") - vm.stubs(:env).returns(env) - vm.stubs(:execute!) + vm.stubs(:env).returns(mock_environment) vm.stubs(:created?).returns(false) vm end @@ -39,28 +69,10 @@ class CommandsUpTest < Test::Unit::TestCase @vms.each do |name, vm| vm.env.expects(:require_box).once + @instance.expects(:up_single).with(name).once end - @instance.execute - end - - should "start created VMs" do - vm = create_vm - vm.stubs(:created?).returns(true) - - @vms[:foo] = vm - - vm.expects(:start).once - @instance.execute - end - - should "up non-created VMs" do - vm = create_vm - vm.expects(:execute!).with(Vagrant::Actions::VM::Up).once - vm.expects(:start).never - - @vms[:foo] = vm - @instance.execute + @instance.up_all end end end diff --git a/test/vagrant/vm_test.rb b/test/vagrant/vm_test.rb index 597f95e9c..075cb3c7c 100644 --- a/test/vagrant/vm_test.rb +++ b/test/vagrant/vm_test.rb @@ -161,6 +161,13 @@ class VMTest < Test::Unit::TestCase end end + context "upping" do + should "execute the up action" do + @vm.expects(:execute!).with(Vagrant::Actions::VM::Up).once + @vm.up + end + end + context "halting" do should "execute the halt action" do @vm.expects(:execute!).with(Vagrant::Actions::VM::Halt, false).once From fd12018114989ef213f55730eb42d7004d1dcd82 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 17 May 2010 01:07:42 -0700 Subject: [PATCH 21/26] `vagrant ssh` works with multi-VM --- lib/vagrant/commands/ssh.rb | 26 ++++++++++++-- templates/strings.yml | 5 +++ test/vagrant/commands/ssh_test.rb | 56 ++++++++++++++++++++++--------- vagrant.gemspec | 2 +- 4 files changed, 70 insertions(+), 19 deletions(-) diff --git a/lib/vagrant/commands/ssh.rb b/lib/vagrant/commands/ssh.rb index 3192842bd..9a01edb26 100644 --- a/lib/vagrant/commands/ssh.rb +++ b/lib/vagrant/commands/ssh.rb @@ -10,8 +10,28 @@ module Vagrant description "SSH into the currently running environment" def execute(args=[]) - env.require_persisted_vm - env.vm.ssh.connect + args = parse_options(args) + ssh_connect(args[0]) + end + + def ssh_connect(name) + if name.nil? && env.multivm? + error_and_exit(:ssh_multivm) + return # for tests + end + + vm = name.nil? ? env.vms.values.first : env.vms[name.to_sym] + if vm.nil? + error_and_exit(:unknown_vm, :vm => name) + return # for tests + end + + if !vm.created? + error_and_exit(:environment_not_created) + return + else + vm.ssh.connect + end end def options_spec(opts) @@ -19,4 +39,4 @@ module Vagrant end end end -end \ No newline at end of file +end diff --git a/templates/strings.yml b/templates/strings.yml index b8025edec..d325c5d72 100644 --- a/templates/strings.yml +++ b/templates/strings.yml @@ -143,6 +143,11 @@ For a more detailed guide please consult: http://vagrantup.com/docs/getting-started/windows.html +:ssh_multivm: |- + Because this Vagrant environment represents multiple VMs, a + specific VM must be specified. This can be done by calling + `vagrant ssh NAME` where NAME is a valid VM represented by + your Vagrantfile. :system_invalid_class: |- The specified system does not inherit from `Vagrant::Systems::Base`. The specified system class must inherit from this class. diff --git a/test/vagrant/commands/ssh_test.rb b/test/vagrant/commands/ssh_test.rb index 87d28043b..e12c3ea75 100644 --- a/test/vagrant/commands/ssh_test.rb +++ b/test/vagrant/commands/ssh_test.rb @@ -5,28 +5,54 @@ class CommandsSSHTest < Test::Unit::TestCase @klass = Vagrant::Commands::SSH @env = mock_environment - @env.stubs(:require_persisted_vm) - - @persisted_vm = mock_vm(@env) - @persisted_vm.stubs(:execute!) - @env.stubs(:vm).returns(@persisted_vm) - @instance = @klass.new(@env) end context "executing" do - setup do - @persisted_vm.ssh.stubs(:connect) + should "connect to the given argument" do + @instance.expects(:ssh_connect).with("foo").once + @instance.execute(["foo"]) end - should "require a persisted VM" do - @env.expects(:require_persisted_vm).once - @instance.execute - end - - should "connect to SSH" do - @persisted_vm.ssh.expects(:connect).once + should "connect with nil name if none is given" do + @instance.expects(:ssh_connect).with(nil).once @instance.execute end end + + context "ssh connecting" do + setup do + @vm = mock("vm") + @vm.stubs(:created?).returns(true) + + @vms = {:bar => @vm} + @env.stubs(:vms).returns(@vms) + @env.stubs(:multivm?).returns(false) + end + + should "error and exit if no VM is specified and multivm" do + @env.stubs(:multivm?).returns(true) + @instance.expects(:error_and_exit).with(:ssh_multivm).once + @instance.ssh_connect(nil) + end + + should "error and exit if VM is nil" do + @instance.expects(:error_and_exit).with(:unknown_vm, :vm => :foo).once + @instance.ssh_connect(:foo) + end + + should "error and exit if VM isn't created" do + @vm.stubs(:created?).returns(false) + @instance.expects(:error_and_exit).with(:environment_not_created).once + @instance.ssh_connect(:bar) + end + + should "ssh connect" do + ssh = mock("ssh") + @vm.stubs(:ssh).returns(ssh) + ssh.expects(:connect) + + @instance.ssh_connect(:bar) + end + end end diff --git a/vagrant.gemspec b/vagrant.gemspec index 1d545c6af..27db581ce 100644 --- a/vagrant.gemspec +++ b/vagrant.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |s| s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version= s.authors = ["Mitchell Hashimoto", "John Bender"] - s.date = %q{2010-05-16} + s.date = %q{2010-05-17} s.default_executable = %q{vagrant} s.description = %q{Vagrant is a tool for building and distributing virtualized development environments.} s.email = ["mitchell.hashimoto@gmail.com", "john.m.bender@gmail.com"] From 228327c0ca82437e4376e1922d807468342e4d5e Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 17 May 2010 01:45:57 -0700 Subject: [PATCH 22/26] `vagrant suspend` works with multi-vms --- lib/vagrant/commands/suspend.rb | 31 ++++++++++++-- test/vagrant/commands/suspend_test.rb | 62 +++++++++++++++++++++------ 2 files changed, 76 insertions(+), 17 deletions(-) diff --git a/lib/vagrant/commands/suspend.rb b/lib/vagrant/commands/suspend.rb index 9bdaa3d11..f45410cbd 100644 --- a/lib/vagrant/commands/suspend.rb +++ b/lib/vagrant/commands/suspend.rb @@ -11,8 +11,33 @@ module Vagrant description "Suspends the currently running vagrant environment" def execute(args=[]) - env.require_persisted_vm - env.vm.suspend + args = parse_options(args) + + if args[0] + suspend_single(args[0]) + else + suspend_all + end + end + + def suspend_single(name) + vm = env.vms[name.to_sym] + if vm.nil? + error_and_exit(:unknown_vm, :vm => name) + return # for tests + end + + if vm.created? + vm.suspend + else + logger.info "VM '#{name}' not created. Ignoring." + end + end + + def suspend_all + env.vms.keys.each do |name| + suspend_single(name) + end end def options_spec(opts) @@ -20,4 +45,4 @@ module Vagrant end end end -end \ No newline at end of file +end diff --git a/test/vagrant/commands/suspend_test.rb b/test/vagrant/commands/suspend_test.rb index ec0f037f1..20703c54d 100644 --- a/test/vagrant/commands/suspend_test.rb +++ b/test/vagrant/commands/suspend_test.rb @@ -4,30 +4,64 @@ class CommandsSuspendTest < Test::Unit::TestCase setup do @klass = Vagrant::Commands::Suspend - @persisted_vm = mock("persisted_vm") - @persisted_vm.stubs(:execute!) - @env = mock_environment - @env.stubs(:require_persisted_vm) - @env.stubs(:vm).returns(@persisted_vm) - @instance = @klass.new(@env) end context "executing" do + should "call on all if no name is given" do + @instance.expects(:suspend_all).once + @instance.execute + end + + should "call on single if a name is given" do + @instance.expects(:suspend_single).with("foo").once + @instance.execute(["foo"]) + end + end + + context "suspending all" do + should "suspend each VM" do + vms = { :foo => nil, :bar => nil, :baz => nil } + @env.stubs(:vms).returns(vms) + + vms.each do |name, value| + @instance.expects(:suspend_single).with(name).once + end + + @instance.suspend_all + end + end + + context "suspending a single VM" do setup do - @persisted_vm.stubs(:suspend) - @persisted_vm.stubs(:saved?).returns(false) + @foo_vm = mock("vm") + vms = { :foo => @foo_vm } + @env.stubs(:vms).returns(vms) end - should "require a persisted VM" do - @env.expects(:require_persisted_vm).once - @instance.execute + should "error and exit if the VM doesn't exist" do + @env.stubs(:vms).returns({}) + @instance.expects(:error_and_exit).with(:unknown_vm, :vm => :foo).once + @instance.suspend_single(:foo) end - should "suspend the VM" do - @persisted_vm.expects(:suspend).once - @instance.execute + should "halt if its created" do + @foo_vm.stubs(:created?).returns(true) + @foo_vm.expects(:suspend).once + @instance.execute(["foo"]) + end + + should "halt and force if specified" do + @foo_vm.stubs(:created?).returns(true) + @foo_vm.expects(:suspend).once + @instance.execute(["foo"]) + end + + should "do nothing if its not created" do + @foo_vm.stubs(:created?).returns(false) + @foo_vm.expects(:suspend).never + @instance.suspend_single(:foo) end end end From 38d485c29b8659fa42228234af59185042ac1853 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 17 May 2010 10:36:34 -0700 Subject: [PATCH 23/26] `vagrant resume` with multiple VMs --- lib/vagrant/commands/resume.rb | 31 +++++++++++++-- test/vagrant/commands/resume_test.rb | 56 ++++++++++++++++++++------- test/vagrant/commands/suspend_test.rb | 8 +--- 3 files changed, 71 insertions(+), 24 deletions(-) diff --git a/lib/vagrant/commands/resume.rb b/lib/vagrant/commands/resume.rb index afb8a51ca..937c3c5cc 100644 --- a/lib/vagrant/commands/resume.rb +++ b/lib/vagrant/commands/resume.rb @@ -10,8 +10,33 @@ module Vagrant description "Resumes a suspend vagrant environment" def execute(args=[]) - env.require_persisted_vm - env.vm.resume + args = parse_options(args) + + if args[0] + resume_single(args[0]) + else + resume_all + end + end + + def resume_single(name) + vm = env.vms[name.to_sym] + if vm.nil? + error_and_exit(:unknown_vm, :vm => name) + return # for tests + end + + if vm.created? + vm.resume + else + logger.info "VM '#{name}' not created. Ignoring." + end + end + + def resume_all + env.vms.keys.each do |name| + resume_single(name) + end end def options_spec(opts) @@ -19,4 +44,4 @@ module Vagrant end end end -end \ No newline at end of file +end diff --git a/test/vagrant/commands/resume_test.rb b/test/vagrant/commands/resume_test.rb index 336f86ec7..0671a34ff 100644 --- a/test/vagrant/commands/resume_test.rb +++ b/test/vagrant/commands/resume_test.rb @@ -4,30 +4,58 @@ class CommandsResumeTest < Test::Unit::TestCase setup do @klass = Vagrant::Commands::Resume - @persisted_vm = mock("persisted_vm") - @persisted_vm.stubs(:execute!) - @env = mock_environment - @env.stubs(:require_persisted_vm) - @env.stubs(:vm).returns(@persisted_vm) - @instance = @klass.new(@env) end context "executing" do + should "call on all if no name is given" do + @instance.expects(:resume_all).once + @instance.execute + end + + should "call on single if a name is given" do + @instance.expects(:resume_single).with("foo").once + @instance.execute(["foo"]) + end + end + + context "resume all" do + should "resume each VM" do + vms = { :foo => nil, :bar => nil, :baz => nil } + @env.stubs(:vms).returns(vms) + + vms.each do |name, value| + @instance.expects(:resume_single).with(name).once + end + + @instance.resume_all + end + end + + context "resuming a single VM" do setup do - @persisted_vm.stubs(:resume) - @persisted_vm.stubs(:saved?).returns(true) + @foo_vm = mock("vm") + vms = { :foo => @foo_vm } + @env.stubs(:vms).returns(vms) end - should "require a persisted VM" do - @env.expects(:require_persisted_vm).once - @instance.execute + should "error and exit if the VM doesn't exist" do + @env.stubs(:vms).returns({}) + @instance.expects(:error_and_exit).with(:unknown_vm, :vm => :foo).once + @instance.resume_single(:foo) end - should "save the state of the VM" do - @persisted_vm.expects(:resume).once - @instance.execute + should "resume if its created" do + @foo_vm.stubs(:created?).returns(true) + @foo_vm.expects(:resume).once + @instance.execute(["foo"]) + end + + should "do nothing if its not created" do + @foo_vm.stubs(:created?).returns(false) + @foo_vm.expects(:resume).never + @instance.resume_single(:foo) end end end diff --git a/test/vagrant/commands/suspend_test.rb b/test/vagrant/commands/suspend_test.rb index 20703c54d..84a9feaad 100644 --- a/test/vagrant/commands/suspend_test.rb +++ b/test/vagrant/commands/suspend_test.rb @@ -46,13 +46,7 @@ class CommandsSuspendTest < Test::Unit::TestCase @instance.suspend_single(:foo) end - should "halt if its created" do - @foo_vm.stubs(:created?).returns(true) - @foo_vm.expects(:suspend).once - @instance.execute(["foo"]) - end - - should "halt and force if specified" do + should "suspend if its created" do @foo_vm.stubs(:created?).returns(true) @foo_vm.expects(:suspend).once @instance.execute(["foo"]) From 586a56b5b56525167b41a58b71e52e155b35557e Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 17 May 2010 10:41:19 -0700 Subject: [PATCH 24/26] `vagrant reload` works with multi-vm --- lib/vagrant/commands/reload.rb | 31 ++++++++++++++-- lib/vagrant/vm.rb | 4 ++ test/vagrant/commands/reload_test.rb | 55 ++++++++++++++++++++++------ test/vagrant/vm_test.rb | 7 ++++ 4 files changed, 83 insertions(+), 14 deletions(-) diff --git a/lib/vagrant/commands/reload.rb b/lib/vagrant/commands/reload.rb index 6ace6983d..f541142d1 100644 --- a/lib/vagrant/commands/reload.rb +++ b/lib/vagrant/commands/reload.rb @@ -10,8 +10,33 @@ module Vagrant description "Reload the vagrant environment" def execute(args=[]) - env.require_persisted_vm - env.vm.execute!(Actions::VM::Reload) + args = parse_options(args) + + if args[0] + reload_single(args[0]) + else + reload_all + end + end + + def reload_single(name) + vm = env.vms[name.to_sym] + if vm.nil? + error_and_exit(:unknown_vm, :vm => name) + return # for tests + end + + if vm.created? + vm.reload + else + logger.info "VM '#{name}' not created. Ignoring." + end + end + + def reload_all + env.vms.keys.each do |name| + reload_single(name) + end end def options_spec(opts) @@ -19,4 +44,4 @@ module Vagrant end end end -end \ No newline at end of file +end diff --git a/lib/vagrant/vm.rb b/lib/vagrant/vm.rb index 15e2f2415..e77cb17dc 100644 --- a/lib/vagrant/vm.rb +++ b/lib/vagrant/vm.rb @@ -113,6 +113,10 @@ module Vagrant execute!(Actions::VM::Halt, force) end + def reload + execute!(Actions::VM::Reload) + end + def destroy execute!(Actions::VM::Down) end diff --git a/test/vagrant/commands/reload_test.rb b/test/vagrant/commands/reload_test.rb index f0e005ded..6ab2fa42a 100644 --- a/test/vagrant/commands/reload_test.rb +++ b/test/vagrant/commands/reload_test.rb @@ -4,25 +4,58 @@ class CommandsReloadTest < Test::Unit::TestCase setup do @klass = Vagrant::Commands::Reload - @persisted_vm = mock("persisted_vm") - @persisted_vm.stubs(:execute!) - @env = mock_environment - @env.stubs(:require_persisted_vm) - @env.stubs(:vm).returns(@persisted_vm) - @instance = @klass.new(@env) end context "executing" do - should "require a persisted VM" do - @env.expects(:require_persisted_vm).once + should "call on all if no name is given" do + @instance.expects(:reload_all).once @instance.execute end - should "call the `reload` action on the VM" do - @persisted_vm.expects(:execute!).with(Vagrant::Actions::VM::Reload).once - @instance.execute + should "call on single if a name is given" do + @instance.expects(:reload_single).with("foo").once + @instance.execute(["foo"]) + end + end + + context "reloading all" do + should "reload each VM" do + vms = { :foo => nil, :bar => nil, :baz => nil } + @env.stubs(:vms).returns(vms) + + vms.each do |name, value| + @instance.expects(:reload_single).with(name).once + end + + @instance.reload_all + end + end + + context "reloading a single VM" do + setup do + @foo_vm = mock("vm") + vms = { :foo => @foo_vm } + @env.stubs(:vms).returns(vms) + end + + should "error and exit if the VM doesn't exist" do + @env.stubs(:vms).returns({}) + @instance.expects(:error_and_exit).with(:unknown_vm, :vm => :foo).once + @instance.reload_single(:foo) + end + + should "reload if its created" do + @foo_vm.stubs(:created?).returns(true) + @foo_vm.expects(:reload).once + @instance.execute(["foo"]) + end + + should "do nothing if its not created" do + @foo_vm.stubs(:created?).returns(false) + @foo_vm.expects(:reload).never + @instance.reload_single(:foo) end end end diff --git a/test/vagrant/vm_test.rb b/test/vagrant/vm_test.rb index 075cb3c7c..4c6fb2de8 100644 --- a/test/vagrant/vm_test.rb +++ b/test/vagrant/vm_test.rb @@ -180,6 +180,13 @@ class VMTest < Test::Unit::TestCase end end + context "reloading" do + should "execute the reload action" do + @vm.expects(:execute!).with(Vagrant::Actions::VM::Reload).once + @vm.reload + end + end + context "destroying" do should "execute the down action" do @vm.expects(:execute!).with(Vagrant::Actions::VM::Down).once From f30645a967e3b17d7edf28a9b3c4a1869f7666d9 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 17 May 2010 10:52:07 -0700 Subject: [PATCH 25/26] `vagrant ssh-config` is now multi-vm friendly --- lib/vagrant/commands/ssh_config.rb | 25 +++++++-- templates/strings.yml | 5 ++ test/vagrant/commands/ssh_config_test.rb | 65 ++++++++++++++++-------- 3 files changed, 69 insertions(+), 26 deletions(-) diff --git a/lib/vagrant/commands/ssh_config.rb b/lib/vagrant/commands/ssh_config.rb index 364f42689..4b76a13bd 100644 --- a/lib/vagrant/commands/ssh_config.rb +++ b/lib/vagrant/commands/ssh_config.rb @@ -9,12 +9,27 @@ module Vagrant def execute(args=[]) env.require_root_path - parse_options(args) + args = parse_options(args) + show_single(args[0]) + end + + def show_single(name) + if name.nil? && env.multivm? + error_and_exit(:ssh_config_multivm) + return # for tests + end + + vm = name.nil? ? env.vms.values.first : env.vms[name.to_sym] + if vm.nil? + error_and_exit(:unknown_vm, :vm => name) + return # for tests + end + puts TemplateRenderer.render("ssh_config", { :host_key => options[:host] || "vagrant", - :ssh_user => env.config.ssh.username, - :ssh_port => env.ssh.port, - :private_key_path => env.config.ssh.private_key_path + :ssh_user => vm.env.config.ssh.username, + :ssh_port => vm.ssh.port, + :private_key_path => vm.env.config.ssh.private_key_path }) end @@ -27,4 +42,4 @@ module Vagrant end end end -end \ No newline at end of file +end diff --git a/templates/strings.yml b/templates/strings.yml index d325c5d72..6be52ea14 100644 --- a/templates/strings.yml +++ b/templates/strings.yml @@ -148,6 +148,11 @@ specific VM must be specified. This can be done by calling `vagrant ssh NAME` where NAME is a valid VM represented by your Vagrantfile. +:ssh_config_multivm: |- + Because this Vagrant environment represents multiple VMs, a + specific VM must be specified. This can be done by calling + `vagrant ssh-config NAME` where NAME is a valid VM represented by + your Vagrantfile. :system_invalid_class: |- The specified system does not inherit from `Vagrant::Systems::Base`. The specified system class must inherit from this class. diff --git a/test/vagrant/commands/ssh_config_test.rb b/test/vagrant/commands/ssh_config_test.rb index 5e8673f30..3fab1dd4a 100644 --- a/test/vagrant/commands/ssh_config_test.rb +++ b/test/vagrant/commands/ssh_config_test.rb @@ -4,31 +4,14 @@ class CommandsSSHConfigTest < Test::Unit::TestCase setup do @klass = Vagrant::Commands::SSHConfig - @persisted_vm = mock("persisted_vm") - @persisted_vm.stubs(:execute!) - @env = mock_environment - @env.stubs(:require_persisted_vm) - @env.stubs(:vm).returns(@persisted_vm) - + @env.stubs(:require_root_path) @instance = @klass.new(@env) end context "executing" do setup do - @ssh = mock("ssh") - @ssh.stubs(:port).returns(2197) - @env.stubs(:ssh).returns(@ssh) - @env.stubs(:require_root_path) - - @instance.stubs(:puts) - - @data = { - :host_key => "vagrant", - :ssh_user => @env.config.ssh.username, - :ssh_port => @env.ssh.port, - :private_key_path => @env.config.ssh.private_key_path - } + @instance.stubs(:show_single) end should "require root path" do @@ -36,19 +19,59 @@ class CommandsSSHConfigTest < Test::Unit::TestCase @instance.execute end + should "call show_single with given argument" do + @instance.expects(:show_single).with("foo").once + @instance.execute(["foo"]) + end + end + + context "showing a single entry" do + setup do + @ssh = mock("ssh") + @ssh.stubs(:port).returns(2197) + + @bar = mock("vm") + @bar.stubs(:env).returns(mock_environment) + @bar.stubs(:ssh).returns(@ssh) + + @vms = {:bar => @bar} + @env.stubs(:multivm?).returns(true) + @env.stubs(:vms).returns(@vms) + + @data = { + :host_key => "vagrant", + :ssh_user => @bar.env.config.ssh.username, + :ssh_port => @bar.ssh.port, + :private_key_path => @bar.env.config.ssh.private_key_path + } + + @instance.stubs(:puts) + end + + should "error if name is nil and multivm" do + @env.stubs(:multivm?).returns(true) + @instance.expects(:error_and_exit).with(:ssh_config_multivm).once + @instance.show_single(nil) + end + + should "error if the VM is not found" do + @instance.expects(:error_and_exit).with(:unknown_vm, :vm => "foo").once + @instance.show_single("foo") + end + should "output rendered template" do result = mock("result") Vagrant::Util::TemplateRenderer.expects(:render).with("ssh_config", @data).returns(result) @instance.expects(:puts).with(result).once - @instance.execute + @instance.show_single(:bar) end should "render with the given host name if given" do host = "foo" @data[:host_key] = host Vagrant::Util::TemplateRenderer.expects(:render).with("ssh_config", @data) - @instance.execute(["--host", host]) + @instance.execute(["bar", "--host", host]) end end end From 19be5f141eb854941ebf71674998f7aa88132ad7 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 17 May 2010 14:12:11 -0700 Subject: [PATCH 26/26] `vagrant package` works with multi-VM environments --- lib/vagrant/commands/package.rb | 69 ++++++++++--- templates/strings.yml | 5 + test/vagrant/commands/package_test.rb | 141 +++++++++++++++----------- 3 files changed, 141 insertions(+), 74 deletions(-) diff --git a/lib/vagrant/commands/package.rb b/lib/vagrant/commands/package.rb index 7b6ca6295..197342575 100644 --- a/lib/vagrant/commands/package.rb +++ b/lib/vagrant/commands/package.rb @@ -8,23 +8,58 @@ module Vagrant description "Packages a vagrant environment for distribution" def execute(args=[]) - parse_options(args) + args = parse_options(args) - if !options[:base] - # Packaging a pre-existing environment - env.require_persisted_vm + if options[:base] + package_base else - # Packaging a base box; that is a VM not tied to a specific - # vagrant environment - vm = VM.find(options[:base]) - vm.env = env if vm - env.vm = vm + package_single(args[0]) + end + end - error_and_exit(:vm_base_not_found, :name => options[:base]) unless vm + def package_base + # Packaging a base box; that is a VM not tied to a specific + # vagrant environment + vm = VM.find(options[:base]) + if !vm + error_and_exit(:vm_base_not_found, :name => options[:base]) + return # for tests end - error_and_exit(:vm_power_off_to_package) unless env.vm.powered_off? - env.vm.package(args[0], options[:include]) + vm.env = env + package_vm(vm) + end + + def package_single(name) + if name.nil? && env.multivm? + error_and_exit(:package_multivm) + return + end + + vm = if name.nil? + env.vms.values.first + else + env.vms[name.to_sym] + end + + if vm.nil? + error_and_exit(:unknown_vm, :vm => name) + return + elsif !vm.created? + error_and_exit(:environment_not_created) + return + end + + package_vm(vm) + end + + def package_vm(vm) + if !vm.powered_off? + error_and_exit(:vm_power_off_to_package) + return # for tests + end + + vm.package(options[:output], options[:include]) end def options_spec(opts) @@ -32,15 +67,21 @@ module Vagrant # Defaults options[:include] = [] + options[:base] = nil + options[:output] = nil - opts.on("--base [BASE]", "Name or UUID of VM to create a base box from") do |v| + opts.on("--base BASE", "Name or UUID of VM to create a base box from") do |v| options[:base] = v end opts.on("--include x,y,z", Array, "List of files to include in the package") do |v| options[:include] = v end + + opts.on("-o", "--output FILE", "File to save the package as.") do |v| + options[:output] = v + end end end end -end \ No newline at end of file +end diff --git a/templates/strings.yml b/templates/strings.yml index 6be52ea14..1e5f87c65 100644 --- a/templates/strings.yml +++ b/templates/strings.yml @@ -108,6 +108,11 @@ <%= Vagrant::Environment::ROOTFILE_NAME %> and running `vagrant up` :package_include_file_doesnt_exist: |- File specified to include: '<%= filename %>' does not exist! +:package_multivm: |- + Because this Vagrant environment represents multiple VMs, a + specific VM must be specified. This can be done by calling + `vagrant package NAME` where NAME is a valid VM represented by + your Vagrantfile. :package_requires_export: |- Package must be used in conjunction with export. :provisioner_invalid_class: |- diff --git a/test/vagrant/commands/package_test.rb b/test/vagrant/commands/package_test.rb index 064d00a02..14d035670 100644 --- a/test/vagrant/commands/package_test.rb +++ b/test/vagrant/commands/package_test.rb @@ -4,81 +4,102 @@ class CommandsPackageTest < Test::Unit::TestCase setup do @klass = Vagrant::Commands::Package - @persisted_vm = mock("persisted_vm") - @persisted_vm.stubs(:execute!) - @env = mock_environment - @env.stubs(:require_persisted_vm) - @env.stubs(:vm).returns(@persisted_vm) - @instance = @klass.new(@env) end context "executing" do + should "package base if a base is given" do + @instance.expects(:package_base).once + @instance.execute(["--base","foo"]) + end + + should "package single if no name is given" do + @instance.expects(:package_single).with(nil).once + @instance.execute + end + + should "package single if a name is given" do + @instance.expects(:package_single).with("foo").once + @instance.execute(["foo"]) + end + end + + context "packaging base" do + should "error and exit if no VM is found" do + Vagrant::VM.expects(:find).with("foo").returns(nil) + @instance.expects(:error_and_exit).with(:vm_base_not_found, :name => "foo").once + @instance.execute(["--base", "foo"]) + end + + should "package the VM like any other VM" do + vm = mock("vm") + Vagrant::VM.expects(:find).with("foo").returns(vm) + vm.expects(:env=).with(@env).once + @instance.expects(:package_vm).with(vm).once + @instance.execute(["--base", "foo"]) + end + end + + context "packaging a single VM" do setup do - @persisted_vm.stubs(:package) - @persisted_vm.stubs(:powered_off?).returns(true) + @vm = mock("vm") + @vm.stubs(:created?).returns(true) + + @vms = {:bar => @vm} + @env.stubs(:vms).returns(@vms) + @env.stubs(:multivm?).returns(false) end - context "with no base specified" do - should "require a persisted vm" do - @env.expects(:require_persisted_vm).once - @instance.execute - end + should "error and exit if no name is given in a multi-vm env" do + @env.stubs(:multivm?).returns(true) + @instance.expects(:error_and_exit).with(:package_multivm).once + @instance.package_single(nil) end - context "with base specified" do - setup do - @vm = mock("vm") - - Vagrant::VM.stubs(:find).with(@name).returns(@vm) - @vm.stubs(:env=).with(@env) - @env.stubs(:vm=) - - @name = "bar" - end - - should "find the given base and set it on the env" do - Vagrant::VM.expects(:find).with(@name).returns(@vm) - @vm.expects(:env=).with(@env) - @env.expects(:vm=).with(@vm) - - @instance.execute(["foo", "--base", @name]) - end - - should "error if the VM is not found" do - Vagrant::VM.expects(:find).with(@name).returns(nil) - @instance.expects(:error_and_exit).with(:vm_base_not_found, :name => @name).once - - @instance.execute(["foo", "--base", @name]) - end + should "error and exit if the VM doesn't exist" do + @instance.expects(:error_and_exit).with(:unknown_vm, :vm => :foo).once + @instance.package_single(:foo) end - context "shared (with and without base specified)" do - should "error and exit if the VM is not powered off" do - @persisted_vm.stubs(:powered_off?).returns(false) - @instance.expects(:error_and_exit).with(:vm_power_off_to_package).once - @persisted_vm.expects(:package).never - @instance.execute - end + should "error and exit if the VM is not created" do + @vm.stubs(:created?).returns(false) + @instance.expects(:error_and_exit).with(:environment_not_created).once + @instance.package_single(:bar) + end - should "call package on the persisted VM" do - @persisted_vm.expects(:package).once - @instance.execute - end + should "use the first VM is no name is given in a single VM environment" do + @instance.expects(:package_vm).with(@vm).once + @instance.package_single(nil) + end - should "pass the out path and include_files to the package method" do - out_path = mock("out_path") - include_files = "foo" - @persisted_vm.expects(:package).with(out_path, [include_files]).once - @instance.execute([out_path, "--include", include_files]) - end + should "package the VM" do + @instance.expects(:package_vm).with(@vm).once + @instance.package_single(:bar) + end + end - should "default to an empty array when not include_files are specified" do - out_path = mock("out_path") - @persisted_vm.expects(:package).with(out_path, []).once - @instance.execute([out_path]) - end + context "packaging a VM" do + setup do + @vm = mock("vm") + @vm.stubs(:powered_off?).returns(true) + + @options = {} + @instance.stubs(:options).returns(@options) + end + + should "error and exit if VM is not powered off" do + @vm.stubs(:powered_off?).returns(false) + @instance.expects(:error_and_exit).with(:vm_power_off_to_package).once + @instance.package_vm(@vm) + end + + should "package the VM with the proper arguments" do + @options[:output] = "foo.box" + @options[:include] = :bar + + @vm.expects(:package).with(@options[:output], @options[:include]).once + @instance.package_vm(@vm) end end end