`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:
parent
fa4cf63462
commit
47fe278667
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue