diff --git a/bin/vagrant-package b/bin/vagrant-package index e83f69873..b55781d6c 100755 --- a/bin/vagrant-package +++ b/bin/vagrant-package @@ -21,10 +21,7 @@ Usage: #{command.full_name} #{all_options_string} Package the current vagrant environment EOS - - opt :file, "the name of the resulting packaged file" - run do |command| - Vagrant::VM.package((command.argv[0] || 'vagrant.box')) + Vagrant::Commands.package(command.argv[0]) end end diff --git a/config/default.rb b/config/default.rb index ed3e6d887..80a95bf57 100644 --- a/config/default.rb +++ b/config/default.rb @@ -14,6 +14,9 @@ Vagrant::Config.run do |config| config.vm.base_mac = "0800279C2E41" config.vm.project_directory = "/vagrant" config.vm.forward_port("ssh", 22, 2222) + config.vm.disk_image_format = 'VMDK' + + config.package.name = 'vagrant' config.chef.cookbooks_path = "cookbooks" config.chef.provisioning_path = "/tmp/vagrant-chef" @@ -21,4 +24,4 @@ Vagrant::Config.run do |config| :instance_role => "vagrant", :recipes => ["vagrant_main"] } -end \ No newline at end of file +end diff --git a/lib/vagrant/commands.rb b/lib/vagrant/commands.rb index 7c529ce0a..c89a0c851 100644 --- a/lib/vagrant/commands.rb +++ b/lib/vagrant/commands.rb @@ -6,7 +6,6 @@ module Vagrant class Commands extend Vagrant::Util - class << self # Initializes a directory for use with vagrant. This command copies an # initial `Vagrantfile` into the current working directory so you can @@ -98,6 +97,18 @@ suspended state. error Env.persisted_vm.start end + + # Export and package the current vm + # + # This command requires that an instance be powered off + def package(name=nil) + Env.load! + Env.require_persisted_vm + error_and_exit(<<-error) unless Env.persisted_vm.powered_off? +The vagrant virtual environment you are trying to package must be powered off +error + Env.persisted_vm.package(name || Vagrant.config[:package][:name], FileUtils.pwd) + end end end end diff --git a/lib/vagrant/config.rb b/lib/vagrant/config.rb index a86427890..7993b207e 100644 --- a/lib/vagrant/config.rb +++ b/lib/vagrant/config.rb @@ -51,6 +51,8 @@ module Vagrant attr_accessor :project_directory attr_reader :forwarded_ports attr_accessor :hd_location + attr_accessor :disk_image_format + def initialize @forwarded_ports = {} @@ -70,6 +72,10 @@ module Vagrant end end + class PackageConfig < Base + attr_accessor :name + end + class ChefConfig < Base attr_accessor :cookbooks_path attr_accessor :provisioning_path @@ -82,6 +88,7 @@ module Vagrant class Top < Base attr_accessor :dotfile_name + attr_reader :package attr_reader :ssh attr_reader :vm attr_reader :chef @@ -92,6 +99,7 @@ module Vagrant @vm = VMConfig.new @chef = ChefConfig.new @vagrant = VagrantConfig.new + @package = PackageConfig.new @loaded = false end diff --git a/lib/vagrant/packaged.rb b/lib/vagrant/packaged.rb deleted file mode 100644 index e16450a0c..000000000 --- a/lib/vagrant/packaged.rb +++ /dev/null @@ -1,51 +0,0 @@ -module Vagrant - class Packaged - attr_reader :vm, :file, :name - - def initialize(name, params) - @vm = params[:vm] - @file = params[:file] - @name = name - end - - def compressed? - @file - end - - def decompress(to) - # move folder unless compressed? - # decompress - # return File object of ovf for import - end - - def compress(to) - folder = FileUtils.mkpath(File.join(to, @name)) - - return @file if compressed? - - ovf_path = File.join(folder, "#{@name}.ovf") - tar_path = "#{folder}.tar" - - @vm.export(ovf_path) - - # TODO use zlib ... - Tar.open(tar_path, File::CREAT | File::WRONLY, 0644, Tar::GNU) do |tar| - begin - working_dir = FileUtils.pwd - FileUtils.cd(to) - tar.append_tree(@name) - ensure - FileUtils.cd(working_dir) - - end - end - - # TODO remove directory - - - tar_path - end - - def ovf; "#{@name}.ovf" end - end -end diff --git a/lib/vagrant/util.rb b/lib/vagrant/util.rb index 334b44914..c34f0203b 100644 --- a/lib/vagrant/util.rb +++ b/lib/vagrant/util.rb @@ -1,5 +1,9 @@ module Vagrant module Util + def self.included?(base) + base.extend Vagrant::Util + end + def error_and_exit(error) puts <<-error ===================================================================== diff --git a/lib/vagrant/vm.rb b/lib/vagrant/vm.rb index ec6338eb2..d8ab2f383 100644 --- a/lib/vagrant/vm.rb +++ b/lib/vagrant/vm.rb @@ -1,10 +1,7 @@ module Vagrant class VM - HD_EXT_DEFAULT = 'VMDK' - attr_reader :vm - - extend Vagrant::Util include Vagrant::Util + attr_reader :vm class << self # Bring up the virtual machine. Imports the base image and @@ -20,15 +17,6 @@ module Vagrant return nil if vm.nil? new(vm) end - - def package(name, to=FileUtils.pwd) - Env.require_persisted_vm - error_and_exit(<<-error) unless Env.persisted_vm.powered_off? -The vagrant virtual environment you are trying to package must be powered off -error - - Packaged.new(name, :vm => Env.persisted_vm).compress(to) - end end def initialize(vm=nil) @@ -76,7 +64,7 @@ error logger.info "Cloning current VM Disk to new location (#{ new_image_file })..." # TODO image extension default? - new_image = hd.image.clone(new_image_file , HD_EXT_DEFAULT, true) + new_image = hd.image.clone(new_image_file , Vagrant.config[:vm][:disk_image_format], true) hd.image = new_image logger.info "Attaching new disk to VM ..." @@ -183,6 +171,37 @@ error @vm.save_state(true) end + # TODO the longest method, needs to be split up + def package(name, to) + folder = FileUtils.mkpath(File.join(to, name)) + logger.info "Creating working directory: #{folder} ..." + + ovf_path = File.join(folder, "#{name}.ovf") + tar_path = "#{folder}.box" + + logger.info "Exporting required VM files to working directory ..." + @vm.export(ovf_path) + + # TODO use zlib ... + logger.info "Packaging VM into #{name}.box ..." + Tar.open(tar_path, File::CREAT | File::WRONLY, 0644, Tar::GNU) do |tar| + begin + # appending the expanded file path adds the whole folder tree + # to the tar archive there must be a better way + working_dir = FileUtils.pwd + FileUtils.cd(to) + tar.append_tree(name) + ensure + FileUtils.cd(working_dir) + end + end + + logger.info "Removiding working directory ..." + FileUtils.rm_r(folder) + + tar_path + end + # TODO need a better way to which controller is the hd def hd @vm.storage_controllers.first.devices.first diff --git a/test/vagrant/commands_test.rb b/test/vagrant/commands_test.rb index ecfc0b1b5..e99cc516e 100644 --- a/test/vagrant/commands_test.rb +++ b/test/vagrant/commands_test.rb @@ -131,4 +131,33 @@ class CommandsTest < Test::Unit::TestCase Vagrant::Commands.resume end end + + context "package" do + setup do + @persisted_vm.stubs(:package) + @persisted_vm.stubs(:powered_off?).returns(true) + end + + should "require a persisted vm" do + Vagrant::Env.expects(:require_persisted_vm).once + Vagrant::Commands.package + end + + should "error and exit if the VM is not powered off" do + @persisted_vm.stubs(:powered_off?).returns(false) + Vagrant::Commands.expects(:error_and_exit).once + @persisted_vm.expects(:package).never + Vagrant::Commands.package + end + + should "package the vm with the default name and the current directory" do + @persisted_vm.expects(:package).with(Vagrant.config[:package][:name], FileUtils.pwd).once + Vagrant::Commands.package + end + + should "package the vm with the specified name" do + @persisted_vm.expects(:package).with('foo', FileUtils.pwd).once + Vagrant::Commands.package('foo') + end + end end diff --git a/test/vagrant/packaged_test.rb b/test/vagrant/packaged_test.rb deleted file mode 100644 index 6f6019eb7..000000000 --- a/test/vagrant/packaged_test.rb +++ /dev/null @@ -1,18 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', 'test_helper') - -class BoxedTest< Test::Unit::TestCase - context "exporting a vm" do - should "create a tar in the specified directory" do - vm = mock('vm') - location = '/Users/johnbender/Desktop' - name = 'my_box' - new_dir = File.join(location, name) - vm.expects(:export).with(File.join(new_dir, "#{name}.ovf")) - FileUtils.expects(:mkpath).with(new_dir).returns(new_dir) - Tar.expects(:open) - - # TODO test whats passed to the open tar.append_tree - assert_equal Vagrant::Packaged.new(name, :vm => vm).compress(location), "#{new_dir}.tar" - end - end -end diff --git a/test/vagrant/vm_test.rb b/test/vagrant/vm_test.rb index 66aa3b1f8..12c7bb27d 100644 --- a/test/vagrant/vm_test.rb +++ b/test/vagrant/vm_test.rb @@ -226,7 +226,7 @@ class VMTest < Test::Unit::TestCase image, hd = mock('image'), mock('hd') Vagrant.config[:vm].expects(:hd_location).at_least_once.returns('/locations/') - image.expects(:clone).with(Vagrant.config[:vm][:hd_location] + 'foo', Vagrant::VM::HD_EXT_DEFAULT, true).returns(image) + image.expects(:clone).with(Vagrant.config[:vm][:hd_location] + 'foo', Vagrant.config[:vm][:disk_image_format], true).returns(image) image.expects(:filename).twice.returns('foo') image.expects(:destroy) @@ -241,4 +241,19 @@ class VMTest < Test::Unit::TestCase end end end + + context "packaging a vm" do + should "dump the three necessary files to a tar in the current working dir" do + location = FileUtils.pwd + name = 'vagrant' + new_dir = File.join(location, name) + @mock_vm.expects(:export).with(File.join(new_dir, "#{name}.ovf")) + FileUtils.expects(:mkpath).with(new_dir).returns(new_dir) + FileUtils.expects(:rm_r).with(new_dir) + Tar.expects(:open) + + # TODO test whats passed to the open tar.append_tree + assert_equal Vagrant::VM.new(@mock_vm).package(name, location), "#{new_dir}.box" + end + end end