diff --git a/bin/vagrant-package b/bin/vagrant-package index d1e11e9d6..89710f3fc 100755 --- a/bin/vagrant-package +++ b/bin/vagrant-package @@ -21,9 +21,10 @@ Package the current vagrant environment into a box. EOS + opt :base, "name of the VM to create a base package", :type => :string opt :include, "files to include in the package", :type => :strings run do |command| - Vagrant::Commands.execute(:package, command.argv[0], command.opts[:include]) + Vagrant::Commands.execute(:package, command.argv[0], command.opts) end end diff --git a/lib/vagrant/commands.rb b/lib/vagrant/commands.rb index 33dd0201e..2a358c3eb 100644 --- a/lib/vagrant/commands.rb +++ b/lib/vagrant/commands.rb @@ -96,11 +96,24 @@ module Vagrant # Export and package the current vm # # This command requires that an instance be powered off - def package(out_path=nil, include_files=[]) - env.require_persisted_vm - error_and_exit(:vm_power_off_to_package) unless env.vm.powered_off? + def package(out_path=nil, opts={}) + opts[:include] ||= [] - env.vm.package(out_path, include_files) + if !opts[:base] + # Packaging a pre-existing environment + env.require_persisted_vm + else + # Packaging a base box; that is a VM not tied to a specific + # vagrant environment + vm = VM.find(opts[:base]) + vm.env = env if vm + env.vm = vm + + error_and_exit(:vm_base_not_found, :name => opts[:base]) unless vm + end + + error_and_exit(:vm_power_off_to_package) unless env.vm.powered_off? + env.vm.package(out_path, opts[:include]) end # Manages the `vagrant box` command, allowing the user to add diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb index 423620c47..d6b8429cd 100644 --- a/lib/vagrant/environment.rb +++ b/lib/vagrant/environment.rb @@ -12,7 +12,7 @@ module Vagrant attr_reader :root_path attr_reader :config attr_reader :box - attr_reader :vm + attr_accessor :vm attr_reader :ssh attr_reader :active_list attr_reader :commands diff --git a/templates/errors.yml b/templates/errors.yml index 434ab0598..18959e875 100644 --- a/templates/errors.yml +++ b/templates/errors.yml @@ -111,6 +111,8 @@ only supports VirtualBox 3.1.x. Please install the proper version to continue. :vm_failed_to_boot: |- Failed to connect to VM! Failed to boot? +:vm_base_not_found: |- + The specified base VM "<%= name %>" was not found. :vm_not_running: |- VM is not running! Nothing to shut down! :vm_not_running_for_suspend: |- diff --git a/test/vagrant/commands_test.rb b/test/vagrant/commands_test.rb index 06678b003..7567a8915 100644 --- a/test/vagrant/commands_test.rb +++ b/test/vagrant/commands_test.rb @@ -148,34 +148,67 @@ class CommandsTest < Test::Unit::TestCase @persisted_vm.stubs(:powered_off?).returns(true) end - should "require a persisted vm" do - @env.expects(:require_persisted_vm).once - @commands.package + context "with no base specified" do + should "require a persisted vm" do + @env.expects(:require_persisted_vm).once + @commands.package + end end - should "error and exit if the VM is not powered off" do - @persisted_vm.stubs(:powered_off?).returns(false) - @commands.expects(:error_and_exit).with(:vm_power_off_to_package).once - @persisted_vm.expects(:package).never - @commands.package + 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) + + @commands.package("foo", { :base => @name }) + end + + should "error if the VM is not found" do + Vagrant::VM.expects(:find).with(@name).returns(nil) + @commands.expects(:error_and_exit).with(:vm_base_not_found, :name => @name).once + + @commands.package("foo", { :base => @name }) + end end - should "call package on the persisted VM" do - @persisted_vm.expects(:package).once - @commands.package - 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) + @commands.expects(:error_and_exit).with(:vm_power_off_to_package).once + @persisted_vm.expects(:package).never + @commands.package + end - should "pass the out path and include_files to the package method" do - out_path = mock("out_path") - include_files = mock("include_files") - @persisted_vm.expects(:package).with(out_path, include_files).once - @commands.package(out_path, include_files) - end + should "call package on the persisted VM" do + @persisted_vm.expects(:package).once + @commands.package + 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 - @commands.package(out_path) + should "pass the out path and include_files to the package method" do + out_path = mock("out_path") + include_files = mock("include_files") + @persisted_vm.expects(:package).with(out_path, include_files).once + @commands.package(out_path, { + :include => include_files + }) + 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 + @commands.package(out_path) + end end end