From da15105a8f4fb567b0c0c2bc276dcf8bfd7d14ef Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 9 Jul 2012 18:31:53 -0700 Subject: [PATCH] Make Box2 the new Box This involved defaulting all box searching at the moment to VirtualBox. Additionally, box upgrading is not yet handled. This needs to be done at some point. --- lib/vagrant.rb | 2 - lib/vagrant/box.rb | 76 ++++--- lib/vagrant/box2.rb | 67 ------ lib/vagrant/box_collection.rb | 242 ++++++++++++++++++---- lib/vagrant/box_collection2.rb | 220 -------------------- lib/vagrant/environment.rb | 6 +- lib/vagrant/vm.rb | 3 +- test/unit/support/isolated_environment.rb | 13 +- test/unit/vagrant/box2_test.rb | 86 -------- test/unit/vagrant/box_collection2_test.rb | 158 -------------- test/unit/vagrant/box_collection_test.rb | 176 ++++++++++++---- test/unit/vagrant/box_test.rb | 92 ++++++-- test/unit/vagrant/environment_test.rb | 2 +- 13 files changed, 483 insertions(+), 660 deletions(-) delete mode 100644 lib/vagrant/box2.rb delete mode 100644 lib/vagrant/box_collection2.rb delete mode 100644 test/unit/vagrant/box2_test.rb delete mode 100644 test/unit/vagrant/box_collection2_test.rb diff --git a/lib/vagrant.rb b/lib/vagrant.rb index 073aa2b9a..747214e85 100644 --- a/lib/vagrant.rb +++ b/lib/vagrant.rb @@ -63,9 +63,7 @@ require "vagrant/registry" module Vagrant autoload :Action, 'vagrant/action' autoload :Box, 'vagrant/box' - autoload :Box2, 'vagrant/box2' autoload :BoxCollection, 'vagrant/box_collection' - autoload :BoxCollection2, 'vagrant/box_collection2' autoload :CLI, 'vagrant/cli' autoload :Command, 'vagrant/command' autoload :Communication, 'vagrant/communication' diff --git a/lib/vagrant/box.rb b/lib/vagrant/box.rb index 816dfcdc9..aa122d93d 100644 --- a/lib/vagrant/box.rb +++ b/lib/vagrant/box.rb @@ -1,44 +1,64 @@ +require "json" + module Vagrant - # Represents a "box," which is simply a packaged vagrant environment. - # Boxes are simply `tar` files which contain an exported VirtualBox - # virtual machine, at the least. They are created with `vagrant package` - # and may contain additional files if specified by the creator. This - # class serves to help manage these boxes, although most of the logic - # is kicked out to middlewares. + # Represents a "box," which is a package Vagrant environment that is used + # as a base image when creating a new guest machine. class Box - # The name of the box. + include Comparable + + # The box name. This is the logical name used when adding the box. + # + # @return [String] attr_reader :name - # The directory where this box is stored + # This is the provider that this box is built for. + # + # @return [Symbol] + attr_reader :provider + + # This is the directory on disk where this box exists. + # + # @return [Pathname] attr_reader :directory - # Creates a new box instance. Given an optional `name` parameter, - # newly created instance will have that name, otherwise it defaults - # to `nil`. + # This is the metadata for the box. This is read from the "metadata.json" + # file that all boxes require. # - # **Note:** This method does not actually _create_ the box, but merely - # returns a new, abstract representation of it. To add a box, see {#add}. - def initialize(name, directory, action_runner) - @name = name - @directory = directory - @action_runner = action_runner + # @return [Hash] + attr_reader :metadata + + # This is used to initialize a box. + # + # @param [String] name Logical name of the box. + # @param [Symbol] provider The provider that this box implements. + # @param [Pathname] directory The directory where this box exists on + # disk. + def initialize(name, provider, directory) + @name = name + @provider = provider + @directory = directory + @metadata = JSON.parse(directory.join("metadata.json").read) end - # Begins the process of destroying this box. This cannot be undone! - def destroy - @action_runner.run(:box_remove, { :box_name => @name, :box_directory => @directory }) + # This deletes the box. This is NOT undoable. + def destroy! + # Delete the directory to delete the box. + FileUtils.rm_r(@directory) + + # Just return true always + true + rescue Errno::ENOENT + # This means the directory didn't exist. Not a problem. + return true end - # Begins sequence to repackage this box. - def repackage(options=nil) - @action_runner.run(:box_repackage, { :box_name => @name, :box_directory => @directory }) - end - - # Implemented for comparison with other boxes. Comparison is implemented - # by simply comparing name. + # Implemented for comparison with other boxes. Comparison is + # implemented by comparing names and providers. def <=>(other) return super if !other.is_a?(self.class) - name <=> other.name + + # Comparison is done by composing the name and provider + "#{@name}-#{@provider}" <=> "#{other.name}-#{other.provider}" end end end diff --git a/lib/vagrant/box2.rb b/lib/vagrant/box2.rb deleted file mode 100644 index 61af1abe8..000000000 --- a/lib/vagrant/box2.rb +++ /dev/null @@ -1,67 +0,0 @@ -require "json" - -module Vagrant - # Represents a "box," which is a package Vagrant environment that is used - # as a base image when creating a new guest machine. - # - # XXX: This will be renamed to "Box" when it is more stable and functional, - # and the old Box will be removed. - class Box2 - include Comparable - - # The box name. This is the logical name used when adding the box. - # - # @return [String] - attr_reader :name - - # This is the provider that this box is built for. - # - # @return [Symbol] - attr_reader :provider - - # This is the directory on disk where this box exists. - # - # @return [Pathname] - attr_reader :directory - - # This is the metadata for the box. This is read from the "metadata.json" - # file that all boxes require. - # - # @return [Hash] - attr_reader :metadata - - # This is used to initialize a box. - # - # @param [String] name Logical name of the box. - # @param [Symbol] provider The provider that this box implements. - # @param [Pathname] directory The directory where this box exists on - # disk. - def initialize(name, provider, directory) - @name = name - @provider = provider - @directory = directory - @metadata = JSON.parse(directory.join("metadata.json").read) - end - - # This deletes the box. This is NOT undoable. - def destroy! - # Delete the directory to delete the box. - FileUtils.rm_r(@directory) - - # Just return true always - true - rescue Errno::ENOENT - # This means the directory didn't exist. Not a problem. - return true - end - - # Implemented for comparison with other boxes. Comparison is - # implemented by comparing names and providers. - def <=>(other) - return super if !other.is_a?(self.class) - - # Comparison is done by composing the name and provider - "#{@name}-#{@provider}" <=> "#{other.name}-#{other.provider}" - end - end -end diff --git a/lib/vagrant/box_collection.rb b/lib/vagrant/box_collection.rb index 682085bc2..947b634f9 100644 --- a/lib/vagrant/box_collection.rb +++ b/lib/vagrant/box_collection.rb @@ -1,58 +1,222 @@ -require 'forwardable' +require "digest/sha1" + +require "archive/tar/minitar" +require "log4r" module Vagrant - # Represents a collection of boxes, providing helpful methods for - # finding boxes. + # Represents a collection a boxes found on disk. This provides methods + # for accessing/finding individual boxes, adding new boxes, or deleting + # boxes. class BoxCollection - include Enumerable - extend Forwardable - def_delegators :@boxes, :length, :each - - # The directory that the boxes are being searched for. + # The directory where the boxes in this collection are stored. + # + # @return [Pathname] attr_reader :directory - # Initializes the class to search for boxes in the given directory. - def initialize(directory, action_runner) - @directory = directory - @boxes = [] - @action_runner = action_runner - - reload! + # Initializes the collection. + # + # @param [Pathname] directory The directory that contains the collection + # of boxes. + def initialize(directory) + @directory = directory + @logger = Log4r::Logger.new("vagrant::box_collection") end - # Find a box in the collection by the given name. The name must - # be a string, for now. - def find(name) - @boxes.each do |box| - return box if box.name == name + # This adds a new box to the system. + # + # There are some exceptional cases: + # * BoxAlreadyExists - The box you're attempting to add already exists. + # * BoxProviderDoesntMatch - If the given box provider doesn't match the + # actual box provider in the untarred box. + # * BoxUnpackageFailure - An invalid tar file. + # * BoxUpgradeRequired - You're attempting to add a box when there is a + # V1 box with the same name that must first be upgraded. + # + # Preconditions: + # * File given in `path` must exist. + # + # @param [Pathname] path Path to the box file on disk. + # @param [String] name Logical name for the box. + # @param [Symbol] provider The provider that the box should be for. This + # will be verified with the `metadata.json` file in the box and is + # meant as a basic check. + def add(path, name, provider) + @logger.debug("Adding box: #{name} (#{provider}) from #{path}") + if find(name, provider) + @logger.error("Box already exists, can't add: #{name} #{provider}") + raise Errors::BoxAlreadyExists, :name => name, :provider => provider end + box_dir = @directory.join(name, provider.to_s) + @logger.debug("New box directory: #{box_dir}") + + # Create the directory that'll store our box + box_dir.mkpath + + # Change directory to the box directory and unpackage the tar + Dir.chdir(box_dir) do + @logger.debug("Unpacking box file into box directory...") + begin + Archive::Tar::Minitar.unpack(path.to_s, box_dir.to_s) + rescue SystemCallError + raise Errors::BoxUnpackageFailure + end + end + + # Find the box we just added + box = find(name, provider) + + # Verify that the provider matches. If not, then we need to rollback + box_provider = box.metadata["provider"] + if box_provider.to_sym != provider + @logger.error("Added box provider doesnt match expected: #{box_provider}") + + # Delete the directory + @logger.debug("Deleting the added box directory...") + box_dir.rmtree + + # Raise an exception + raise Errors::BoxProviderDoesntMatch, :expected => provider, :actual => box_provider + end + + # Return the box + box + end + + # This returns an array of all the boxes on the system, given by + # their name and their provider. + # + # @return [Array] Array of `[name, provider]` pairs of the boxes + # installed on this system. An optional third element in the array + # may specify `:v1` if the box is a version 1 box. + def all + results = [] + + @logger.debug("Finding all boxes in: #{@directory}") + @directory.children(true).each do |child| + box_name = child.basename.to_s + + # If this is a V1 box, we still return that name, but specify + # that the box is a V1 box. + if v1_box?(child) + @logger.debug("V1 box found: #{box_name}") + results << [box_name, :virtualbox, :v1] + next + end + + # Otherwise, traverse the subdirectories and see what providers + # we have. + child.children(true).each do |provider| + # Verify this is a potentially valid box. If it looks + # correct enough then include it. + if provider.directory? && provider.join("metadata.json").file? + provider_name = provider.basename.to_s.to_sym + @logger.debug("Box: #{box_name} (#{provider_name})") + results << [box_name, provider_name] + else + @logger.debug("Invalid box, ignoring: #{provider}") + end + end + end + + results + end + + # Find a box in the collection with the given name and provider. + # + # @param [String] name Name of the box (logical name). + # @Param [String] provider Provider that the box implements. + # @return [Box] The box found, or `nil` if not found. + def find(name, provider) + # First look directly for the box we're asking for. + box_directory = @directory.join(name, provider.to_s, "metadata.json") + @logger.info("Searching for box: #{name} (#{provider}) in #{box_directory}") + if box_directory.file? + @logger.debug("Box found: #{name} (#{provider})") + return Box.new(name, provider, box_directory.dirname) + end + + # Check if a V1 version of this box exists, and if so, raise an + # exception notifying the caller that the box exists but needs + # to be upgraded. We don't do the upgrade here because it can be + # a fairly intensive activity and don't want to immediately degrade + # user performance on a find. + # + # To determine if it is a V1 box we just do a simple heuristic + # based approach. + @logger.info("Searching for V1 box: #{name}") + if v1_box?(name) + @logger.warn("V1 box found: #{name}") + raise Errors::BoxUpgradeRequired, :name => name + end + + # Didn't find it, return nil + @logger.info("Box not found: #{name} (#{provider})") nil end - # Adds a box to this collection with the given name and located - # at the given URL. - def add(name, url) - raise Errors::BoxAlreadyExists, :name => name if find(name) + # Upgrades a V1 box with the given name to a V2 box. If a box with the + # given name doesn't exist, then a `BoxNotFound` exception will be raised. + # If the given box is found but is not a V1 box then `true` is returned + # because this just works fine. + # + # @return [Boolean] `true` otherwise an exception is raised. + def upgrade(name) + @logger.debug("Upgrade request for box: #{name}") + box_dir = @directory.join(name) - @action_runner.run(:box_add, - :box_name => name, - :box_url => url, - :box_directory => @directory.join(name)) + # If the box doesn't exist at all, raise an exception + raise Errors::BoxNotFound, :name => name if !box_dir.directory? + + if v1_box?(name) + @logger.debug("V1 box #{name} found. Upgrading!") + + # First, we create a temporary directory within the box to store + # the intermediary moved files. We randomize this in case there is + # already a directory named "virtualbox" in here for some reason. + temp_dir = box_dir.join("vagrant-#{Digest::SHA1.hexdigest(name)}") + @logger.debug("Temporary directory for upgrading: #{temp_dir}") + + # Make the temporary directory + temp_dir.mkpath + + # Move all the things into the temporary directory + box_dir.children(true).each do |child| + # Don't move the temp_dir + next if child == temp_dir + + # Move every other directory into the temporary directory + @logger.debug("Copying to upgrade directory: #{child}") + FileUtils.mv(child, temp_dir.join(child.basename)) + end + + # If there is no metadata.json file, make one, since this is how + # we determine if the box is a V2 box. + metadata_file = temp_dir.join("metadata.json") + if !metadata_file.file? + metadata_file.open("w") do |f| + f.write(JSON.generate({})) + end + end + + # Rename the temporary directory to the provider. + temp_dir.rename(box_dir.join("virtualbox")) + @logger.info("Box '#{name}' upgraded from V1 to V2.") + end + + # We did it! Or the v1 box didn't exist so it doesn't matter. + return true end - # Loads the list of all boxes from the source. This modifies the - # current array. - def reload! - @boxes.clear + protected - Dir.open(@directory) do |dir| - dir.each do |d| - next if d == "." || d == ".." || !@directory.join(d).directory? - @boxes << Box.new(d, @directory.join(d), @action_runner) - end - end + # This checks if the given name represents a V1 box on the system. + # + # @return [Boolean] + def v1_box?(name) + # We detect a V1 box given by whether there is a "box.ovf" which + # is a heuristic but is pretty accurate. + @directory.join(name, "box.ovf").file? end end end - diff --git a/lib/vagrant/box_collection2.rb b/lib/vagrant/box_collection2.rb deleted file mode 100644 index 04c837cdd..000000000 --- a/lib/vagrant/box_collection2.rb +++ /dev/null @@ -1,220 +0,0 @@ -require "digest/sha1" - -require "archive/tar/minitar" -require "log4r" - -module Vagrant - # Represents a collection a boxes found on disk. This provides methods - # for accessing/finding individual boxes, adding new boxes, or deleting - # boxes. - # - # XXX: This will be renamed to "BoxCollection" when it is more stable - # and functional, and the old BoxCollection will be removed. - class BoxCollection2 - # Initializes the collection. - # - # @param [Pathname] directory The directory that contains the collection - # of boxes. - def initialize(directory) - @directory = directory - @logger = Log4r::Logger.new("vagrant::box_collection") - end - - # This adds a new box to the system. - # - # There are some exceptional cases: - # * BoxAlreadyExists - The box you're attempting to add already exists. - # * BoxProviderDoesntMatch - If the given box provider doesn't match the - # actual box provider in the untarred box. - # * BoxUnpackageFailure - An invalid tar file. - # * BoxUpgradeRequired - You're attempting to add a box when there is a - # V1 box with the same name that must first be upgraded. - # - # Preconditions: - # * File given in `path` must exist. - # - # @param [Pathname] path Path to the box file on disk. - # @param [String] name Logical name for the box. - # @param [Symbol] provider The provider that the box should be for. This - # will be verified with the `metadata.json` file in the box and is - # meant as a basic check. - def add(path, name, provider) - @logger.debug("Adding box: #{name} (#{provider}) from #{path}") - if find(name, provider) - @logger.error("Box already exists, can't add: #{name} #{provider}") - raise Errors::BoxAlreadyExists, :name => name, :provider => provider - end - - box_dir = @directory.join(name, provider.to_s) - @logger.debug("New box directory: #{box_dir}") - - # Create the directory that'll store our box - box_dir.mkpath - - # Change directory to the box directory and unpackage the tar - Dir.chdir(box_dir) do - @logger.debug("Unpacking box file into box directory...") - begin - Archive::Tar::Minitar.unpack(path.to_s, box_dir.to_s) - rescue SystemCallError - raise Errors::BoxUnpackageFailure - end - end - - # Find the box we just added - box = find(name, provider) - - # Verify that the provider matches. If not, then we need to rollback - box_provider = box.metadata["provider"] - if box_provider.to_sym != provider - @logger.error("Added box provider doesnt match expected: #{box_provider}") - - # Delete the directory - @logger.debug("Deleting the added box directory...") - box_dir.rmtree - - # Raise an exception - raise Errors::BoxProviderDoesntMatch, :expected => provider, :actual => box_provider - end - - # Return the box - box - end - - # This returns an array of all the boxes on the system, given by - # their name and their provider. - # - # @return [Array] Array of `[name, provider]` pairs of the boxes - # installed on this system. An optional third element in the array - # may specify `:v1` if the box is a version 1 box. - def all - results = [] - - @logger.debug("Finding all boxes in: #{@directory}") - @directory.children(true).each do |child| - box_name = child.basename.to_s - - # If this is a V1 box, we still return that name, but specify - # that the box is a V1 box. - if v1_box?(child) - @logger.debug("V1 box found: #{box_name}") - results << [box_name, :virtualbox, :v1] - next - end - - # Otherwise, traverse the subdirectories and see what providers - # we have. - child.children(true).each do |provider| - # Verify this is a potentially valid box. If it looks - # correct enough then include it. - if provider.directory? && provider.join("metadata.json").file? - provider_name = provider.basename.to_s.to_sym - @logger.debug("Box: #{box_name} (#{provider_name})") - results << [box_name, provider_name] - else - @logger.debug("Invalid box, ignoring: #{provider}") - end - end - end - - results - end - - # Find a box in the collection with the given name and provider. - # - # @param [String] name Name of the box (logical name). - # @Param [String] provider Provider that the box implements. - # @return [Box] The box found, or `nil` if not found. - def find(name, provider) - # First look directly for the box we're asking for. - box_directory = @directory.join(name, provider.to_s, "metadata.json") - @logger.info("Searching for box: #{name} (#{provider}) in #{box_directory}") - if box_directory.file? - @logger.debug("Box found: #{name} (#{provider})") - return Box2.new(name, provider, box_directory.dirname) - end - - # Check if a V1 version of this box exists, and if so, raise an - # exception notifying the caller that the box exists but needs - # to be upgraded. We don't do the upgrade here because it can be - # a fairly intensive activity and don't want to immediately degrade - # user performance on a find. - # - # To determine if it is a V1 box we just do a simple heuristic - # based approach. - @logger.info("Searching for V1 box: #{name}") - if v1_box?(name) - @logger.warn("V1 box found: #{name}") - raise Errors::BoxUpgradeRequired, :name => name - end - - # Didn't find it, return nil - @logger.info("Box not found: #{name} (#{provider})") - nil - end - - # Upgrades a V1 box with the given name to a V2 box. If a box with the - # given name doesn't exist, then a `BoxNotFound` exception will be raised. - # If the given box is found but is not a V1 box then `true` is returned - # because this just works fine. - # - # @return [Boolean] `true` otherwise an exception is raised. - def upgrade(name) - @logger.debug("Upgrade request for box: #{name}") - box_dir = @directory.join(name) - - # If the box doesn't exist at all, raise an exception - raise Errors::BoxNotFound, :name => name if !box_dir.directory? - - if v1_box?(name) - @logger.debug("V1 box #{name} found. Upgrading!") - - # First, we create a temporary directory within the box to store - # the intermediary moved files. We randomize this in case there is - # already a directory named "virtualbox" in here for some reason. - temp_dir = box_dir.join("vagrant-#{Digest::SHA1.hexdigest(name)}") - @logger.debug("Temporary directory for upgrading: #{temp_dir}") - - # Make the temporary directory - temp_dir.mkpath - - # Move all the things into the temporary directory - box_dir.children(true).each do |child| - # Don't move the temp_dir - next if child == temp_dir - - # Move every other directory into the temporary directory - @logger.debug("Copying to upgrade directory: #{child}") - FileUtils.mv(child, temp_dir.join(child.basename)) - end - - # If there is no metadata.json file, make one, since this is how - # we determine if the box is a V2 box. - metadata_file = temp_dir.join("metadata.json") - if !metadata_file.file? - metadata_file.open("w") do |f| - f.write(JSON.generate({})) - end - end - - # Rename the temporary directory to the provider. - temp_dir.rename(box_dir.join("virtualbox")) - @logger.info("Box '#{name}' upgraded from V1 to V2.") - end - - # We did it! Or the v1 box didn't exist so it doesn't matter. - return true - end - - protected - - # This checks if the given name represents a V1 box on the system. - # - # @return [Boolean] - def v1_box?(name) - # We detect a V1 box given by whether there is a "box.ovf" which - # is a heuristic but is pretty accurate. - @directory.join(name, "box.ovf").file? - end - end -end diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb index f73678023..ef8b8b461 100644 --- a/lib/vagrant/environment.rb +++ b/lib/vagrant/environment.rb @@ -121,7 +121,7 @@ module Vagrant # # @return [BoxCollection] def boxes - @_boxes ||= BoxCollection.new(boxes_path, action_runner) + @_boxes ||= BoxCollection.new(boxes_path) end # Returns the VMs associated with this environment. @@ -425,7 +425,9 @@ module Vagrant config = inner_load[subvm] # Second pass, with the box - inner_load[subvm, boxes.find(config.vm.box)] + box = nil + box = boxes.find(config.vm.box, :virtualbox) if config.vm.box + inner_load[subvm, box] end # Finally, we have our configuration. Set it and forget it. diff --git a/lib/vagrant/vm.rb b/lib/vagrant/vm.rb index 1068a00ed..d35c25535 100644 --- a/lib/vagrant/vm.rb +++ b/lib/vagrant/vm.rb @@ -19,7 +19,8 @@ module Vagrant @vm = nil @env = env @config = config - @box = env.boxes.find(config.vm.box) + @box = nil + @box = env.boxes.find(config.vm.box, :virtualbox) if config.vm.box opts ||= {} if opts[:base] diff --git a/test/unit/support/isolated_environment.rb b/test/unit/support/isolated_environment.rb index 3de02f847..3d93d5015 100644 --- a/test/unit/support/isolated_environment.rb +++ b/test/unit/support/isolated_environment.rb @@ -60,7 +60,12 @@ module Unit # @param [String] name Name of the box # @param [Symbol] provider Provider the box was built for. # @return [Pathname] Path to the box directory. - def box2(name, provider) + def box2(name, provider, options=nil) + # Default options + options = { + :vagrantfile => "" + }.merge(options || {}) + # Make the box directory box_dir = boxes_dir.join(name, provider.to_s) box_dir.mkpath @@ -71,6 +76,12 @@ module Unit f.write("{}") end + # Create a Vagrantfile + box_vagrantfile = box_dir.join("Vagrantfile") + box_vagrantfile.open("w") do |f| + f.write(options[:vagrantfile]) + end + # Return the box directory box_dir end diff --git a/test/unit/vagrant/box2_test.rb b/test/unit/vagrant/box2_test.rb deleted file mode 100644 index 843419225..000000000 --- a/test/unit/vagrant/box2_test.rb +++ /dev/null @@ -1,86 +0,0 @@ -require File.expand_path("../../base", __FILE__) - -require "pathname" - -describe Vagrant::Box2 do - include_context "unit" - - let(:environment) { isolated_environment } - - let(:name) { "foo" } - let(:provider) { :virtualbox } - let(:directory) { environment.box2("foo", :virtualbox) } - let(:instance) { described_class.new(name, provider, directory) } - - it "provides the name" do - instance.name.should == name - end - - it "provides the provider" do - instance.provider.should == provider - end - - it "provides the directory" do - instance.directory.should == directory - end - - it "provides the metadata associated with a box" do - data = { "foo" => "bar" } - - # Write the metadata - directory.join("metadata.json").open("w") do |f| - f.write(JSON.generate(data)) - end - - # Verify the metadata - instance.metadata.should == data - end - - describe "destroying" do - it "should destroy an existing box" do - # Verify that our "box" exists - directory.exist?.should be - - # Destroy it - instance.destroy!.should be - - # Verify that it is "destroyed" - directory.exist?.should_not be - end - - it "should not error destroying a non-existent box" do - # Get the instance so that it is instantiated - box = instance - - # Delete the directory - directory.rmtree - - # Destroy it - box.destroy!.should be - end - end - - describe "comparison and ordering" do - it "should be equal if the name and provider match" do - a = described_class.new("a", :foo, directory) - b = described_class.new("a", :foo, directory) - - a.should == b - end - - it "should not be equal if the name and provider do not match" do - a = described_class.new("a", :foo, directory) - b = described_class.new("b", :foo, directory) - - a.should_not == b - end - - it "should sort them in order of name then provider" do - a = described_class.new("a", :foo, directory) - b = described_class.new("b", :foo, directory) - c = described_class.new("c", :foo2, directory) - - [c, a, b].sort.should == [a, b, c] - end - end -end diff --git a/test/unit/vagrant/box_collection2_test.rb b/test/unit/vagrant/box_collection2_test.rb deleted file mode 100644 index ee95e3694..000000000 --- a/test/unit/vagrant/box_collection2_test.rb +++ /dev/null @@ -1,158 +0,0 @@ -require File.expand_path("../../base", __FILE__) - -require "pathname" - -describe Vagrant::BoxCollection2 do - include_context "unit" - - let(:box_class) { Vagrant::Box2 } - let(:environment) { isolated_environment } - let(:instance) { described_class.new(environment.boxes_dir) } - - describe "adding" do - it "should add a valid box to the system" do - box_path = environment.box2_file(:virtualbox) - - # Add the box - box = instance.add(box_path, "foo", :virtualbox) - box.should be_kind_of(box_class) - box.name.should == "foo" - box.provider.should == :virtualbox - - # Verify we can find it as well - box = instance.find("foo", :virtualbox) - box.should_not be_nil - end - - it "should raise an exception if the box already exists" do - prev_box_name = "foo" - prev_box_provider = :virtualbox - - # Create the box we're adding - environment.box2(prev_box_name, prev_box_provider) - - # Attempt to add the box with the same name - box_path = environment.box2_file(prev_box_provider) - expect { instance.add(box_path, prev_box_name, prev_box_provider) }. - to raise_error(Vagrant::Errors::BoxAlreadyExists) - end - - it "should raise an exception if you're attempting to add a box that exists as a V1 box" do - prev_box_name = "foo" - - # Create the V1 box - environment.box1(prev_box_name) - - # Attempt to add some V2 box with the same name - box_path = environment.box2_file(:vmware) - expect { instance.add(box_path, prev_box_name, :vmware) }. - to raise_error(Vagrant::Errors::BoxUpgradeRequired) - end - - it "should raise an exception and not add the box if the provider doesn't match" do - box_name = "foo" - good_provider = :virtualbox - bad_provider = :vmware - - # Create a VirtualBox box file - box_path = environment.box2_file(good_provider) - - # Add the box but with an invalid provider, verify we get the proper - # error. - expect { instance.add(box_path, box_name, bad_provider) }. - to raise_error(Vagrant::Errors::BoxProviderDoesntMatch) - - # Verify the box doesn't exist - instance.find(box_name, bad_provider).should be_nil - end - - it "should raise an exception if you add an invalid box file" do - pending "I don't know how to generate an invalid tar." - end - end - - describe "listing all" do - it "should return an empty array when no boxes are there" do - instance.all.should == [] - end - - it "should return the boxes and their providers" do - # Create some boxes - environment.box2("foo", :virtualbox) - environment.box2("foo", :vmware) - environment.box2("bar", :ec2) - - # Verify some output - results = instance.all.sort - results.length.should == 3 - results.should == [["foo", :virtualbox], ["foo", :vmware], ["bar", :ec2]].sort - end - - it "should return V1 boxes as well" do - # Create some boxes, including a V1 box - environment.box1("bar") - environment.box2("foo", :vmware) - - # Verify some output - results = instance.all.sort - results.should == [["bar", :virtualbox, :v1], ["foo", :vmware]] - end - end - - describe "finding" do - it "should return nil if the box does not exist" do - instance.find("foo", :i_dont_exist).should be_nil - end - - it "should return a box if the box does exist" do - # Create the "box" - environment.box2("foo", :virtualbox) - - # Actual test - result = instance.find("foo", :virtualbox) - result.should_not be_nil - result.should be_kind_of(box_class) - result.name.should == "foo" - end - - it "should throw an exception if it is a v1 box" do - # Create a V1 box - environment.box1("foo") - - # Test! - expect { instance.find("foo", :virtualbox) }. - to raise_error(Vagrant::Errors::BoxUpgradeRequired) - end - end - - describe "upgrading" do - it "should upgrade a V1 box to V2" do - # Create a V1 box - environment.box1("foo") - - # Verify that only a V1 box exists - expect { instance.find("foo", :virtualbox) }. - to raise_error(Vagrant::Errors::BoxUpgradeRequired) - - # Upgrade the box - instance.upgrade("foo").should be - - # Verify the box exists - box = instance.find("foo", :virtualbox) - box.should_not be_nil - box.name.should == "foo" - end - - it "should raise a BoxNotFound exception if a non-existent box is upgraded" do - expect { instance.upgrade("i-dont-exist") }. - to raise_error(Vagrant::Errors::BoxNotFound) - end - - it "should return true if we try to upgrade a V2 box" do - # Create a V2 box - environment.box2("foo", :vmware) - - instance.upgrade("foo").should be - end - end -end diff --git a/test/unit/vagrant/box_collection_test.rb b/test/unit/vagrant/box_collection_test.rb index cc27ed209..9dc584976 100644 --- a/test/unit/vagrant/box_collection_test.rb +++ b/test/unit/vagrant/box_collection_test.rb @@ -1,56 +1,162 @@ require File.expand_path("../../base", __FILE__) +require "pathname" + describe Vagrant::BoxCollection do include_context "unit" - let(:environment) { isolated_environment } - let(:action_runner) { double("action runner") } - let(:instance) { described_class.new(environment.boxes_dir, action_runner) } + let(:box_class) { Vagrant::Box } + let(:environment) { isolated_environment } + let(:instance) { described_class.new(environment.boxes_dir) } - it "should list all available boxes" do - # No boxes yet. - instance.length.should == 0 + it "should tell us the directory it is using" do + instance.directory.should == environment.boxes_dir + end - # Add some boxes to the environment and try again - environment.box("foo") - environment.box("bar") - instance.reload! - instance.length.should == 2 + describe "adding" do + it "should add a valid box to the system" do + box_path = environment.box2_file(:virtualbox) + + # Add the box + box = instance.add(box_path, "foo", :virtualbox) + box.should be_kind_of(box_class) + box.name.should == "foo" + box.provider.should == :virtualbox + + # Verify we can find it as well + box = instance.find("foo", :virtualbox) + box.should_not be_nil + end + + it "should raise an exception if the box already exists" do + prev_box_name = "foo" + prev_box_provider = :virtualbox + + # Create the box we're adding + environment.box2(prev_box_name, prev_box_provider) + + # Attempt to add the box with the same name + box_path = environment.box2_file(prev_box_provider) + expect { instance.add(box_path, prev_box_name, prev_box_provider) }. + to raise_error(Vagrant::Errors::BoxAlreadyExists) + end + + it "should raise an exception if you're attempting to add a box that exists as a V1 box" do + prev_box_name = "foo" + + # Create the V1 box + environment.box1(prev_box_name) + + # Attempt to add some V2 box with the same name + box_path = environment.box2_file(:vmware) + expect { instance.add(box_path, prev_box_name, :vmware) }. + to raise_error(Vagrant::Errors::BoxUpgradeRequired) + end + + it "should raise an exception and not add the box if the provider doesn't match" do + box_name = "foo" + good_provider = :virtualbox + bad_provider = :vmware + + # Create a VirtualBox box file + box_path = environment.box2_file(good_provider) + + # Add the box but with an invalid provider, verify we get the proper + # error. + expect { instance.add(box_path, box_name, bad_provider) }. + to raise_error(Vagrant::Errors::BoxProviderDoesntMatch) + + # Verify the box doesn't exist + instance.find(box_name, bad_provider).should be_nil + end + + it "should raise an exception if you add an invalid box file" do + pending "I don't know how to generate an invalid tar." + end + end + + describe "listing all" do + it "should return an empty array when no boxes are there" do + instance.all.should == [] + end + + it "should return the boxes and their providers" do + # Create some boxes + environment.box2("foo", :virtualbox) + environment.box2("foo", :vmware) + environment.box2("bar", :ec2) + + # Verify some output + results = instance.all.sort + results.length.should == 3 + results.should == [["foo", :virtualbox], ["foo", :vmware], ["bar", :ec2]].sort + end + + it "should return V1 boxes as well" do + # Create some boxes, including a V1 box + environment.box1("bar") + environment.box2("foo", :vmware) + + # Verify some output + results = instance.all.sort + results.should == [["bar", :virtualbox, :v1], ["foo", :vmware]] + end end describe "finding" do - it "should return nil if it can't find the box" do - instance.find("foo").should be_nil + it "should return nil if the box does not exist" do + instance.find("foo", :i_dont_exist).should be_nil end - it "should return a box instance for any boxes it does find" do - environment.box("foo") - result = instance.find("foo") - result.should be_kind_of(Vagrant::Box) + it "should return a box if the box does exist" do + # Create the "box" + environment.box2("foo", :virtualbox) + + # Actual test + result = instance.find("foo", :virtualbox) + result.should_not be_nil + result.should be_kind_of(box_class) result.name.should == "foo" end + + it "should throw an exception if it is a v1 box" do + # Create a V1 box + environment.box1("foo") + + # Test! + expect { instance.find("foo", :virtualbox) }. + to raise_error(Vagrant::Errors::BoxUpgradeRequired) + end end - it "should throw an error if the box already exists when adding" do - environment.box("foo") - expect { instance.add("foo", "bar") }.to raise_error(Vagrant::Errors::BoxAlreadyExists) - end + describe "upgrading" do + it "should upgrade a V1 box to V2" do + # Create a V1 box + environment.box1("foo") - it "should add the box" do - name = "foo" - url = "bar" + # Verify that only a V1 box exists + expect { instance.find("foo", :virtualbox) }. + to raise_error(Vagrant::Errors::BoxUpgradeRequired) - # Test the invocation of the action runner with the proper name - # and parameters. We leave the testing of the actual stack to - # acceptance tests, and individual pieces to unit tests of each - # step. - options = { - :box_name => name, - :box_url => url, - :box_directory => instance.directory.join(name) - } - action_runner.should_receive(:run).with(:box_add, options) + # Upgrade the box + instance.upgrade("foo").should be - instance.add(name, url) + # Verify the box exists + box = instance.find("foo", :virtualbox) + box.should_not be_nil + box.name.should == "foo" + end + + it "should raise a BoxNotFound exception if a non-existent box is upgraded" do + expect { instance.upgrade("i-dont-exist") }. + to raise_error(Vagrant::Errors::BoxNotFound) + end + + it "should return true if we try to upgrade a V2 box" do + # Create a V2 box + environment.box2("foo", :vmware) + + instance.upgrade("foo").should be + end end end diff --git a/test/unit/vagrant/box_test.rb b/test/unit/vagrant/box_test.rb index a2dbfbfb0..3c0e6e5b0 100644 --- a/test/unit/vagrant/box_test.rb +++ b/test/unit/vagrant/box_test.rb @@ -1,34 +1,86 @@ require File.expand_path("../../base", __FILE__) +require "pathname" + describe Vagrant::Box do + include_context "unit" + + let(:environment) { isolated_environment } + let(:name) { "foo" } - let(:directory) { "bar" } - let(:action_runner) { double("action_runner") } - let(:instance) { described_class.new(name, directory, action_runner) } + let(:provider) { :virtualbox } + let(:directory) { environment.box2("foo", :virtualbox) } + let(:instance) { described_class.new(name, provider, directory) } it "provides the name" do instance.name.should == name end - it "can destroy itself" do - # Simply test the messages to the action runner - options = { - :box_name => name, - :box_directory => directory - } - action_runner.should_receive(:run).with(:box_remove, options) - - instance.destroy + it "provides the provider" do + instance.provider.should == provider end - it "can repackage itself" do - # Simply test the messages to the action runner - options = { - :box_name => name, - :box_directory => directory - } - action_runner.should_receive(:run).with(:box_repackage, options) + it "provides the directory" do + instance.directory.should == directory + end - instance.repackage + it "provides the metadata associated with a box" do + data = { "foo" => "bar" } + + # Write the metadata + directory.join("metadata.json").open("w") do |f| + f.write(JSON.generate(data)) + end + + # Verify the metadata + instance.metadata.should == data + end + + describe "destroying" do + it "should destroy an existing box" do + # Verify that our "box" exists + directory.exist?.should be + + # Destroy it + instance.destroy!.should be + + # Verify that it is "destroyed" + directory.exist?.should_not be + end + + it "should not error destroying a non-existent box" do + # Get the instance so that it is instantiated + box = instance + + # Delete the directory + directory.rmtree + + # Destroy it + box.destroy!.should be + end + end + + describe "comparison and ordering" do + it "should be equal if the name and provider match" do + a = described_class.new("a", :foo, directory) + b = described_class.new("a", :foo, directory) + + a.should == b + end + + it "should not be equal if the name and provider do not match" do + a = described_class.new("a", :foo, directory) + b = described_class.new("b", :foo, directory) + + a.should_not == b + end + + it "should sort them in order of name then provider" do + a = described_class.new("a", :foo, directory) + b = described_class.new("b", :foo, directory) + c = described_class.new("c", :foo2, directory) + + [c, a, b].sort.should == [a, b, c] + end end end diff --git a/test/unit/vagrant/environment_test.rb b/test/unit/vagrant/environment_test.rb index f7a231e6f..ad302da34 100644 --- a/test/unit/vagrant/environment_test.rb +++ b/test/unit/vagrant/environment_test.rb @@ -191,7 +191,7 @@ Vagrant::Config.run do |config| end VF - env.box("base", <<-VF) + env.box2("base", :virtualbox, :vagrantfile => <<-VF) Vagrant::Config.run do |config| config.ssh.port = 100 end