`vagrant box add` works again. Box verification remove temporarily.

The built-in middleware sequences will now be hardcoded onto
Vagrant::Action. Other plugins can hook into these sequences to provide
verification and so on. So the VirtualBox plugin will hook into that
action sequence and add verification.
This commit is contained in:
Mitchell Hashimoto 2012-08-18 16:13:14 -07:00
parent fa4cf63462
commit 47fe278667
11 changed files with 108 additions and 330 deletions

View File

@ -6,25 +6,26 @@ module Vagrant
autoload :Runner, 'vagrant/action/runner'
autoload :Warden, 'vagrant/action/warden'
module Box
autoload :Add, 'vagrant/action/box/add'
autoload :Download, 'vagrant/action/box/download'
autoload :Verify, 'vagrant/action/box/verify'
end
# Builtin contains middleware classes that are shipped with Vagrant-core
# and are thus available to all plugins as a "standard library" of sorts.
module Builtin
autoload :Call, "vagrant/action/builtin/call"
autoload :BoxAdd, "vagrant/action/builtin/box_add"
autoload :Call, "vagrant/action/builtin/call"
autoload :Confirm, "vagrant/action/builtin/confirm"
autoload :EnvSet, "vagrant/action/builtin/env_set"
autoload :SSHExec, "vagrant/action/builtin/ssh_exec"
autoload :SSHRun, "vagrant/action/builtin/ssh_run"
autoload :SSHRun, "vagrant/action/builtin/ssh_run"
end
module General
autoload :Package, 'vagrant/action/general/package'
autoload :Validate, 'vagrant/action/general/validate'
end
def self.action_box_add
Builder.new.tap do |b|
b.use Builtin::BoxAdd
end
end
end
end

View File

@ -1,31 +0,0 @@
module Vagrant
module Action
module Box
# Adds a downloaded box file to the environment's box collection.
# This handles unpacking the box. See {BoxCollection#add} for more
# information.
class Add
def initialize(app, env)
@app = app
@env = env
end
def call(env)
@env[:ui].info I18n.t("vagrant.actions.box.add.adding", :name => env[:box_name])
begin
env[:box_collection].add(env[:box_download_temp_path], env[:box_name])
rescue Vagrant::Errors::BoxUpgradeRequired
# Upgrade the box
env[:box_collection].upgrade(env[:box_name])
# Try adding it again
retry
end
@app.call(env)
end
end
end
end
end

View File

@ -1,84 +0,0 @@
module Vagrant
module Action
module Box
class Download
BASENAME = "box"
include Util
attr_reader :temp_path
def initialize(app, env)
@app = app
@env = env
@env["download.classes"] ||= []
@env["download.classes"] += [Downloaders::HTTP, Downloaders::File]
@downloader = nil
end
def call(env)
@env = env
download if instantiate_downloader
@app.call(@env)
recover(env) # called in both cases to cleanup workspace
end
def instantiate_downloader
# Assign to a temporary variable since this is easier to type out,
# since it is used so many times.
classes = @env["download.classes"]
# Find the class to use.
classes.each_index do |i|
klass = classes[i]
# Use the class if it matches the given URI or if this
# is the last class...
if classes.length == (i + 1) || klass.match?(@env[:box_url])
@env[:ui].info I18n.t("vagrant.actions.box.download.with", :class => klass.to_s)
@downloader = klass.new(@env[:ui])
break
end
end
# This line should never be reached, but we'll keep this here
# just in case for now.
raise Errors::BoxDownloadUnknownType if !@downloader
@downloader.prepare(@env[:box_url])
true
end
def download
with_tempfile do |tempfile|
download_to(tempfile)
@temp_path = @env[:box_download_temp_path] = tempfile.path
end
end
def recover(env)
if temp_path && File.exist?(temp_path)
env[:ui].info I18n.t("vagrant.actions.box.download.cleaning")
File.unlink(temp_path)
end
end
def with_tempfile
File.open(box_temp_path, Platform.tar_file_options) do |tempfile|
yield tempfile
end
end
def box_temp_path
@env[:tmp_path].join(BASENAME + Time.now.to_i.to_s)
end
def download_to(f)
@downloader.download!(@env[:box_url], f)
end
end
end
end
end

View File

@ -1,24 +0,0 @@
module Vagrant
module Action
module Box
class Verify
def initialize(app, env)
@app = app
@env = env
end
def call(env)
@env[:ui].info I18n.t("vagrant.actions.box.verify.verifying")
box = env[:box_collection].find(env[:box_name], :virtualbox)
driver = Driver::VirtualBox.new(nil)
if !driver.verify_image(box.directory.join("box.ovf").to_s)
raise Errors::BoxVerificationFailed
end
@app.call(env)
end
end
end
end
end

View File

@ -1,157 +0,0 @@
module Vagrant
module Action
# A registry object containing the built-in middleware stacks.
class Builtin < Registry
def initialize
# Properly initialize the registry object
super
# Register all the built-in stacks
register_builtin!
end
protected
def register_builtin!
# We do this so that the blocks below have a variable to access the
# outer registry.
registry = self
# provision - Provisions a running VM
register(:provision) do
Builder.new do
use General::CheckVirtualbox
use General::Validate
use VM::CheckAccessible
use VM::Provision
end
end
# start - Starts a VM, assuming it already exists on the
# environment.
register(:start) do
Builder.new do
use General::CheckVirtualbox
use General::Validate
use VM::CheckAccessible
use VM::CleanMachineFolder
use VM::ClearForwardedPorts
use VM::CheckPortCollisions, :port_collision_handler => :correct
use VM::ForwardPorts
use VM::Provision
use VM::PruneNFSExports
use VM::NFS
use VM::ClearSharedFolders
use VM::ShareFolders
use VM::ClearNetworkInterfaces
use VM::Network
use VM::HostName
use VM::SaneDefaults
use VM::Customize
use VM::Boot
end
end
# halt - Halts the VM, attempting gracefully but then forcing
# a restart if fails.
register(:halt) do
Builder.new do
use General::CheckVirtualbox
use General::Validate
use VM::CheckAccessible
use VM::DiscardState
use VM::Halt
end
end
# suspend - Suspends the VM
register(:suspend) do
Builder.new do
use General::CheckVirtualbox
use General::Validate
use VM::CheckAccessible
use VM::Suspend
end
end
# resume - Resume a VM
register(:resume) do
Builder.new do
use General::CheckVirtualbox
use General::Validate
use VM::CheckAccessible
use VM::CheckPortCollisions
use VM::Resume
end
end
# reload - Halts then restarts the VM
register(:reload) do
Builder.new do
use General::CheckVirtualbox
use General::Validate
use VM::CheckAccessible
use registry.get(:halt)
use registry.get(:start)
end
end
# up - Imports, prepares, then starts a fresh VM.
register(:up) do
Builder.new do
use General::CheckVirtualbox
use General::Validate
use VM::CheckAccessible
use VM::CheckBox
use VM::Import
use VM::CheckGuestAdditions
use VM::DefaultName
use VM::MatchMACAddress
use registry.get(:start)
end
end
# destroy - Halts, cleans up, and destroys an existing VM
register(:destroy) do
Builder.new do
use General::CheckVirtualbox
use General::Validate
use VM::CheckAccessible
use registry.get(:halt), :force => true
use VM::ProvisionerCleanup
use VM::PruneNFSExports
use VM::Destroy
use VM::CleanMachineFolder
use VM::DestroyUnusedNetworkInterfaces
end
end
# package - Export and package the VM
register(:package) do
Builder.new do
use General::CheckVirtualbox
use General::Validate
use VM::SetupPackageFiles
use VM::CheckAccessible
use registry.get(:halt)
use VM::ClearForwardedPorts
use VM::ClearSharedFolders
use VM::Export
use VM::PackageVagrantfile
use VM::Package
end
end
# box_add - Download and add a box.
register(:box_add) do
Builder.new do
use General::CheckVirtualbox
use Box::Download
use Box::Add
use Box::Verify
end
end
end
end
end
end

View File

@ -0,0 +1,75 @@
require "vagrant/util/platform"
module Vagrant
module Action
module Builtin
# This middleware will download a remote box and add it to the
# given box collection.
class BoxAdd
def initialize(app, env)
@app = app
end
def call(env)
# Instantiate the downloader
downloader = download_klass(env[:box_url]).new(env[:ui])
env[:ui].info I18n.t("vagrant.actions.box.download.with",
:class => downloader.class.to_s)
# Download the box to a temporary path. We store the temporary
# path as an instance variable so that the `#recover` method can
# access it.
@temp_path = env[:tmp_path].join("box" + Time.now.to_i.to_s)
File.open(@temp_path, Vagrant::Util::Platform.tar_file_options) do |f|
downloader.download!(env[:box_url], f)
end
# Add the box
env[:ui].info I18n.t("vagrant.actions.box.add.adding", :name => env[:box_name])
begin
env[:box_collection].add(@temp_path, env[:box_name])
rescue Vagrant::Errors::BoxUpgradeRequired
# Upgrade the box
env[:box_collection].upgrade(env[:box_name])
# Try adding it again
retry
end
# Call the 'recover' method in all cases to clean up the
# downloaded temporary file.
recover(env)
# Carry on!
@app.call(env)
end
def download_klass(url)
# This is hardcoded for now. In the future I'd like to make this
# pluginnable as well.
classes = [Downloaders::HTTP, Downloaders::File]
# Find the class to use.
classes.each_index do |i|
klass = classes[i]
# Use the class if it matches the given URI or if this
# is the last class...
return klass if classes.length == (i + 1) || klass.match?(url)
end
# If no downloader knows how to download this file, then we
# raise an exception.
raise Errors::BoxDownloadUnknownType
end
def recover(env)
if @temp_path && File.exist?(@temp_path)
env[:ui].info I18n.t("vagrant.actions.box.download.cleaning")
File.unlink(@temp_path)
end
end
end
end
end
end

View File

@ -14,9 +14,6 @@ module Vagrant
# handle.
def self.match?(url); false; end
# Called prior to execution so any error checks can be done
def prepare(source_url); end
# Downloads the source file to the destination file. It is up to
# implementors of this class to handle the logic.
def download!(source_url, destination_file); end

View File

@ -9,11 +9,9 @@ module Vagrant
::File.file?(::File.expand_path(uri))
end
def prepare(source_url)
raise Errors::DownloaderFileDoesntExist if !::File.file?(::File.expand_path(source_url))
end
def download!(source_url, destination_file)
raise Errors::DownloaderFileDoesntExist if !::File.file?(::File.expand_path(source_url))
@ui.info I18n.t("vagrant.downloaders.file.download")
FileUtils.cp(::File.expand_path(source_url), destination_file.path)
end

View File

@ -7,11 +7,11 @@ module VagrantPlugins
def execute
options = {}
opts = OptionParser.new do |opts|
opts.banner = "Usage: vagrant box add <name> <url>"
opts.separator ""
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant box add <name> <url>"
o.separator ""
opts.on("-f", "--force", "Overwrite an existing box if it exists.") do |f|
o.on("-f", "--force", "Overwrite an existing box if it exists.") do |f|
options[:force] = f
end
end
@ -28,8 +28,7 @@ module VagrantPlugins
existing.destroy! if existing
end
# Invoke the "box_add" middleware sequence.
@env.action_runner.run(:box_add, {
@env.action_runner.run(Vagrant::Action.action_box_add, {
:box_name => argv[0],
:box_provider => :virtualbox,
:box_url => argv[1]

View File

@ -8,10 +8,6 @@ describe Vagrant::Downloaders::Base do
described_class.match?("foo").should_not be
end
it "should implement `prepare`" do
instance.prepare("foo").should be_nil
end
it "should implement `download!`" do
instance.download!("foo", "bar").should be_nil
end

View File

@ -3,9 +3,15 @@ require File.expand_path("../../../base", __FILE__)
require "tempfile"
describe Vagrant::Downloaders::File do
include_context "unit"
let(:ui) { double("ui") }
let(:instance) { described_class.new(ui) }
before(:each) do
ui.stub(:info)
end
describe "matching" do
it "should match an existing file" do
described_class.match?(__FILE__).should be
@ -19,33 +25,37 @@ describe Vagrant::Downloaders::File do
old_home = ENV["HOME"]
begin
# Create a temporary file
temp = Tempfile.new("vagrant")
temp = temporary_file
# Set our home directory to be this directory so we can use
# "~" paths
ENV["HOME"] = File.dirname(temp.path)
ENV["HOME"] = File.dirname(temp.to_s)
# Test that we can find the temp file
described_class.match?("~/#{File.basename(temp.path)}").should be
described_class.match?("~/#{File.basename(temp.to_s)}").should be
ensure
ENV["HOME"] = old_home
end
end
end
describe "preparing" do
describe "downloading" do
let(:destination_file) { temporary_file.open("w") }
it "should raise an exception if the file does not exist" do
path = File.join(__FILE__, "nopenopenope")
File.exist?(path).should_not be
expect { instance.prepare(path) }.to raise_error(Vagrant::Errors::DownloaderFileDoesntExist)
expect { instance.download!(path, destination_file) }.
to raise_error(Vagrant::Errors::DownloaderFileDoesntExist)
end
it "should raise an exception if the file is a directory" do
path = File.dirname(__FILE__)
File.should be_directory(path)
expect { instance.prepare(path) }.to raise_error(Vagrant::Errors::DownloaderFileDoesntExist)
expect { instance.download!(path, destination_file) }.
to raise_error(Vagrant::Errors::DownloaderFileDoesntExist)
end
it "should find files that use shell expansions" do
@ -59,15 +69,13 @@ describe Vagrant::Downloaders::File do
ENV["HOME"] = File.dirname(temp.path)
# Test that we can find the temp file
expect { instance.prepare("~/#{File.basename(temp.path)}") }.
expect { instance.download!("~/#{File.basename(temp.path)}", destination_file) }.
to_not raise_error
ensure
ENV["HOME"] = old_home
end
end
end
describe "downloading" do
it "should copy the source to the destination" do
pending "setup paths"
end