From 1753d97d458766448dc2e957472d0d6abf631393 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 14 Feb 2010 22:27:06 -0800 Subject: [PATCH] Move hard drive action. --- lib/vagrant/actions/move_hard_drive.rb | 43 +++++++++ test/test_helper.rb | 7 ++ test/vagrant/actions/move_hard_drive_test.rb | 93 ++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 lib/vagrant/actions/move_hard_drive.rb create mode 100644 test/vagrant/actions/move_hard_drive_test.rb diff --git a/lib/vagrant/actions/move_hard_drive.rb b/lib/vagrant/actions/move_hard_drive.rb new file mode 100644 index 000000000..518aa5922 --- /dev/null +++ b/lib/vagrant/actions/move_hard_drive.rb @@ -0,0 +1,43 @@ +module Vagrant + module Actions + class MoveHardDrive < Base + def execute! + unless @vm.powered_off? + error_and_exit(<<-error) +The virtual machine must be powered off to move its disk. +error + return + end + + destroy_drive_after { clone_and_attach } + end + + # TODO: Better way to detect main bootup drive? + def hard_drive + @vm.storage_controllers.first.devices.first + end + + 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) + + logger.info "Attaching new disk to VM ..." + @vm.vm.save + end + + def destroy_drive_after + old_image = hard_drive.image + + yield + + logger.info "Destroying old VM Disk (#{old_image.filename})..." + old_image.destroy(true) + end + + # 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) + end + end + end +end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index 000a60413..d283c731a 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -35,6 +35,7 @@ class Test::Unit::TestCase config.vm.base = "foo" config.vm.base_mac = "42" config.vm.project_directory = "/hobo" + config.vm.disk_image_format = 'VMDK' config.vm.forward_port("ssh", 22, 2222) config.chef.cookbooks_path = "cookbooks" @@ -44,6 +45,12 @@ class Test::Unit::TestCase } end + if block_given? + Vagrant::Config.run do |config| + yield config + end + end + Vagrant::Config.execute! end diff --git a/test/vagrant/actions/move_hard_drive_test.rb b/test/vagrant/actions/move_hard_drive_test.rb new file mode 100644 index 000000000..2adec5bf4 --- /dev/null +++ b/test/vagrant/actions/move_hard_drive_test.rb @@ -0,0 +1,93 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'test_helper') + +class MoveHardDriveActionTest < Test::Unit::TestCase + setup do + @mock_vm, @vm, @action = mock_action(Vagrant::Actions::MoveHardDrive) + @hd_location = "/foo" + mock_config do |config| + File.expects(:directory?).with(@hd_location).returns(true) + config.vm.hd_location = @hd_location + end + end + + context "execution" do + should "error and exit if the vm is not powered off" do + @mock_vm.expects(:powered_off?).returns(false) + @action.expects(:error_and_exit).once + @action.execute! + end + + should "move the hard drive if vm is powered off" do + @mock_vm.expects(:powered_off?).returns(true) + @action.expects(:error_and_exit).never + @action.expects(:destroy_drive_after).once + @action.execute! + end + end + + context "new image path" do + setup do + @hd = mock("hd") + @image = mock("image") + @filename = "foo" + @hd.stubs(:image).returns(@image) + @image.stubs(:filename).returns(@filename) + @action.stubs(:hard_drive).returns(@hd) + end + + should "be the configured hd location and the existing hard drive filename" do + joined = File.join(Vagrant.config.vm.hd_location, @filename) + assert_equal joined, @action.new_image_path + end + end + + context "cloning and attaching new image" do + setup do + @hd = mock("hd") + @image = mock("image") + @hd.stubs(:image).returns(@image) + @action.stubs(:hard_drive).returns(@hd) + @new_image_path = "foo" + @action.stubs(:new_image_path).returns(@new_image_path) + end + + should "clone to the new path" do + new_image = mock("new_image") + @image.expects(:clone).with(@new_image_path, Vagrant.config.vm.disk_image_format, true).returns(new_image).once + @hd.expects(:image=).with(new_image).once + @vm.expects(:save).once + @action.clone_and_attach + end + end + + context "destroying the old image" do + setup do + @hd = mock("hd") + @action.stubs(:hard_drive).returns(@hd) + end + + should "yield the block, and destroy the old image after" do + image = mock("image") + image.stubs(:filename).returns("foo") + destroy_seq = sequence("destroy_seq") + @hd.expects(:image).returns(image).in_sequence(destroy_seq) + @hd.expects(:foo).once.in_sequence(destroy_seq) + image.expects(:destroy).with(true).once.in_sequence(destroy_seq) + + @action.destroy_drive_after { @hd.foo } + end + + # Ensures that the image is not destroyed in an "ensure" block + should "not destroy the image if an exception is raised" do + image = mock("image") + image.expects(:destroy).never + @hd.expects(:image).returns(image) + + assert_raises(Exception) do + @action.destroy_drive_after do + raise Exception.new("FOO") + end + end + end + end +end