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