Merge pull request #2943 from mitchellh/f-boxes-2
Boxes Revamp: Short URLs, Versions, Updates! * **Box versioning** - All boxes can have versions now. This version information is available in metadata requested by Vagrant. New commands such as `vagrant box outdated` and new settings such as `config.vm.box_check_update` will configure how/when Vagrant checks for box updates. Boxes that don't support versions will always have version "0". * **Shortnames for boxes** - You can now do things like `vagrant box add hashicorp/precise64`. The name and the URL are one and the same. This requires a backend. We'll be hosting one [for free public access] in the coming month or two. Of course, you don't need to use the backend we'll provide to support versions or unversioned boxes. You would need to for shortnames. * **Multiple providers at one URL** - A single URL can now represent multiple providers. In this case, Vagrant will ask you what provider you want to download if it can't figure it out on its own. All of this is _fully backwards compatible_. I verified this as best I could with the tests in vagrant-spec. I have to write new acceptance tests for the new features, however. Also, this PR adds around 100 unit tests.
This commit is contained in:
commit
0f2fff2420
|
@ -6,6 +6,7 @@ acceptance_config.yml
|
|||
boxes/*
|
||||
/Vagrantfile
|
||||
/.vagrant
|
||||
/website/docs/.vagrant
|
||||
/website/www/.vagrant
|
||||
/vagrant-spec.config.rb
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ module Vagrant
|
|||
# and are thus available to all plugins as a "standard library" of sorts.
|
||||
module Builtin
|
||||
autoload :BoxAdd, "vagrant/action/builtin/box_add"
|
||||
autoload :BoxCheckOutdated, "vagrant/action/builtin/box_check_outdated"
|
||||
autoload :BoxRemove, "vagrant/action/builtin/box_remove"
|
||||
autoload :Call, "vagrant/action/builtin/call"
|
||||
autoload :Confirm, "vagrant/action/builtin/confirm"
|
||||
|
@ -16,6 +17,7 @@ module Vagrant
|
|||
autoload :DestroyConfirm, "vagrant/action/builtin/destroy_confirm"
|
||||
autoload :EnvSet, "vagrant/action/builtin/env_set"
|
||||
autoload :GracefulHalt, "vagrant/action/builtin/graceful_halt"
|
||||
autoload :HandleBox, "vagrant/action/builtin/handle_box"
|
||||
autoload :HandleBoxUrl, "vagrant/action/builtin/handle_box_url"
|
||||
autoload :HandleForwardedPortCollisions, "vagrant/action/builtin/handle_forwarded_port_collisions"
|
||||
autoload :Lock, "vagrant/action/builtin/lock"
|
||||
|
@ -44,6 +46,14 @@ module Vagrant
|
|||
end
|
||||
end
|
||||
|
||||
# This actions checks if a box is outdated in a given Vagrant
|
||||
# environment for a single machine.
|
||||
def self.action_box_outdated
|
||||
Builder.new.tap do |b|
|
||||
b.use Builtin::BoxCheckOutdated
|
||||
end
|
||||
end
|
||||
|
||||
# This is the action that will remove a box given a name (and optionally
|
||||
# a provider). This middleware sequence is built-in to Vagrant. Plugins
|
||||
# can hook into this like any other middleware sequence.
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
require "digest/sha1"
|
||||
require "log4r"
|
||||
require "pathname"
|
||||
require "uri"
|
||||
|
||||
require "vagrant/box_metadata"
|
||||
require "vagrant/util/downloader"
|
||||
require "vagrant/util/file_checksum"
|
||||
require "vagrant/util/platform"
|
||||
|
@ -19,34 +22,205 @@ module Vagrant
|
|||
def call(env)
|
||||
@download_interrupted = false
|
||||
|
||||
box_name = env[:box_name]
|
||||
box_formats = env[:box_provider]
|
||||
if box_formats
|
||||
# Determine the formats a box can support and allow the box to
|
||||
# be any of those formats.
|
||||
provider_plugin = Vagrant.plugin("2").manager.providers[env[:box_provider]]
|
||||
if provider_plugin
|
||||
box_formats = provider_plugin[1][:box_format]
|
||||
box_formats ||= env[:box_provider]
|
||||
url = Array(env[:box_url]).map do |u|
|
||||
u = u.gsub("\\", "/")
|
||||
if Util::Platform.windows? && u =~ /^[a-z]:/i
|
||||
# On Windows, we need to be careful about drive letters
|
||||
u = "file://#{u}"
|
||||
end
|
||||
|
||||
if u =~ /^[a-z0-9]+:.*$/i && !u.start_with?("file://")
|
||||
# This is not a file URL... carry on
|
||||
next u
|
||||
end
|
||||
|
||||
# Expand the path and try to use that, if possible
|
||||
p = File.expand_path(u.gsub(/^file:\/\//, ""))
|
||||
p = Util::Platform.cygwin_windows_path(p)
|
||||
next "file://#{p}" if File.file?(p)
|
||||
|
||||
u
|
||||
end
|
||||
|
||||
# If we received a shorthand URL ("mitchellh/precise64"),
|
||||
# then expand it properly.
|
||||
expanded = false
|
||||
url.each_index do |i|
|
||||
next if url[i] !~ /^[^\/]+\/[^\/]+$/
|
||||
|
||||
if !File.file?(url[i])
|
||||
server = Vagrant.server_url
|
||||
raise Errors::BoxServerNotSet if !server
|
||||
|
||||
expanded = true
|
||||
url[i] = "#{server}/#{url[i]}"
|
||||
end
|
||||
end
|
||||
|
||||
# Determine if we already have the box before downloading
|
||||
# it again. We can only do this if we specify a format
|
||||
if box_formats && !env[:box_force]
|
||||
# Test if any of our URLs point to metadata
|
||||
is_metadata_results = url.map do |u|
|
||||
begin
|
||||
if env[:box_collection].find(box_name, box_formats)
|
||||
raise Errors::BoxAlreadyExists,
|
||||
:name => box_name,
|
||||
:formats => [box_formats].flatten.join(", ")
|
||||
end
|
||||
rescue Vagrant::Errors::BoxUpgradeRequired
|
||||
# If the box needs to be upgraded, do it.
|
||||
env[:box_collection].upgrade(box_name)
|
||||
retry
|
||||
metadata_url?(u, env)
|
||||
rescue Errors::DownloaderError => e
|
||||
e
|
||||
end
|
||||
end
|
||||
|
||||
if expanded && url.length == 1
|
||||
is_error = is_metadata_results.find do |b|
|
||||
b.is_a?(Errors::DownloaderError)
|
||||
end
|
||||
|
||||
if is_error
|
||||
raise Errors::BoxAddShortNotFound,
|
||||
error: is_error.extra_data[:message],
|
||||
name: env[:box_url],
|
||||
url: url
|
||||
end
|
||||
end
|
||||
|
||||
is_metadata = is_metadata_results.any? { |b| b === true }
|
||||
if is_metadata && url.length > 1
|
||||
raise Errors::BoxAddMetadataMultiURL,
|
||||
urls: url.join(", ")
|
||||
end
|
||||
|
||||
if is_metadata
|
||||
add_from_metadata(url.first, env, expanded)
|
||||
else
|
||||
add_direct(url, env)
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
# Adds a box file directly (no metadata component, versioning,
|
||||
# etc.)
|
||||
#
|
||||
# @param [Array<String>] urls
|
||||
# @param [Hash] env
|
||||
def add_direct(urls, env)
|
||||
name = env[:box_name]
|
||||
if !name || name == ""
|
||||
raise Errors::BoxAddNameRequired
|
||||
end
|
||||
|
||||
provider = env[:box_provider]
|
||||
provider = Array(provider) if provider
|
||||
|
||||
box_add(
|
||||
urls,
|
||||
name,
|
||||
"0",
|
||||
provider,
|
||||
nil,
|
||||
env)
|
||||
end
|
||||
|
||||
# Adds a box given that the URL is a metadata document.
|
||||
def add_from_metadata(url, env, expanded)
|
||||
original_url = env[:box_url]
|
||||
provider = env[:box_provider]
|
||||
provider = Array(provider) if provider
|
||||
version = env[:box_version]
|
||||
|
||||
env[:ui].output(I18n.t(
|
||||
"vagrant.box_loading_metadata",
|
||||
name: Array(original_url).first))
|
||||
if original_url != url
|
||||
env[:ui].detail(I18n.t(
|
||||
"vagrant.box_expanding_url", url: url))
|
||||
end
|
||||
|
||||
metadata = nil
|
||||
begin
|
||||
metadata_path = download(url, env, ui: false)
|
||||
|
||||
File.open(metadata_path) do |f|
|
||||
metadata = BoxMetadata.new(f)
|
||||
end
|
||||
rescue Errors::DownloaderError => e
|
||||
raise if !expanded
|
||||
raise Errors::BoxAddShortNotFound,
|
||||
error: e.extra_data[:message],
|
||||
name: original_url,
|
||||
url: url
|
||||
ensure
|
||||
metadata_path.delete if metadata_path && metadata_path.file?
|
||||
end
|
||||
|
||||
if env[:box_name] && metadata.name != env[:box_name]
|
||||
raise Errors::BoxAddNameMismatch,
|
||||
actual_name: metadata.name,
|
||||
requested_name: env[:box_name]
|
||||
end
|
||||
|
||||
metadata_version = metadata.version(
|
||||
version || ">= 0", provider: provider)
|
||||
if !metadata_version
|
||||
if !provider
|
||||
raise Errors::BoxAddNoMatchingVersion,
|
||||
constraints: version || ">= 0",
|
||||
name: metadata.name,
|
||||
url: url,
|
||||
versions: metadata.versions.join(", ")
|
||||
else
|
||||
# TODO: show supported providers
|
||||
raise Errors::BoxAddNoMatchingProvider,
|
||||
name: metadata.name,
|
||||
requested: provider,
|
||||
url: url
|
||||
end
|
||||
end
|
||||
|
||||
metadata_provider = nil
|
||||
if provider
|
||||
# If a provider was specified, make sure we get that specific
|
||||
# version.
|
||||
provider.each do |p|
|
||||
metadata_provider = metadata_version.provider(p)
|
||||
break if metadata_provider
|
||||
end
|
||||
elsif metadata_version.providers.length == 1
|
||||
# If we have only one provider in the metadata, just use that
|
||||
# provider.
|
||||
metadata_provider = metadata_version.provider(
|
||||
metadata_version.providers.first)
|
||||
else
|
||||
providers = metadata_version.providers.sort
|
||||
|
||||
choice = 0
|
||||
options = providers.map do |p|
|
||||
choice += 1
|
||||
"#{choice}) #{p}"
|
||||
end.join("\n")
|
||||
|
||||
# We have more than one provider, ask the user what they want
|
||||
choice = env[:ui].ask(I18n.t(
|
||||
"vagrant.box_add_choose_provider",
|
||||
options: options) + " ", prefix: false)
|
||||
choice = choice.to_i if choice
|
||||
while !choice || choice <= 0 || choice > providers.length
|
||||
choice = env[:ui].ask(I18n.t(
|
||||
"vagrant.box_add_choose_provider_again") + " ",
|
||||
prefix: false)
|
||||
choice = choice.to_i if choice
|
||||
end
|
||||
|
||||
metadata_provider = metadata_version.provider(
|
||||
providers[choice-1])
|
||||
end
|
||||
|
||||
box_add(
|
||||
[metadata_provider.url],
|
||||
metadata.name,
|
||||
metadata_version.version,
|
||||
metadata_provider.name,
|
||||
url,
|
||||
env)
|
||||
end
|
||||
|
||||
=begin
|
||||
# Determine the checksum type to use
|
||||
checksum = (env[:box_checksum] || "").to_s
|
||||
checksum_klass = nil
|
||||
|
@ -104,46 +278,89 @@ module Vagrant
|
|||
expected: checksum
|
||||
end
|
||||
end
|
||||
end
|
||||
=end
|
||||
|
||||
# Add the box
|
||||
env[:ui].info I18n.t("vagrant.actions.box.add.adding", :name => box_name)
|
||||
box_added = nil
|
||||
protected
|
||||
|
||||
# Shared helper to add a box once you know various details
|
||||
# about it. Shared between adding via metadata or by direct.
|
||||
#
|
||||
# @param [Array<String>] urls
|
||||
# @param [String] name
|
||||
# @param [String] version
|
||||
# @param [String] provider
|
||||
# @param [Hash] env
|
||||
# @return [Box]
|
||||
def box_add(urls, name, version, provider, md_url, env, **opts)
|
||||
env[:ui].output(I18n.t(
|
||||
"vagrant.box_add_with_version",
|
||||
name: name,
|
||||
version: version,
|
||||
providers: Array(provider).join(", ")))
|
||||
|
||||
# Verify the box we're adding doesn't already exist
|
||||
if provider && !env[:box_force]
|
||||
box = env[:box_collection].find(
|
||||
name, provider, version)
|
||||
if box
|
||||
raise Errors::BoxAlreadyExists,
|
||||
name: name,
|
||||
provider: provider,
|
||||
version: version
|
||||
end
|
||||
end
|
||||
|
||||
# Now we have a URL, we have to download this URL.
|
||||
box = nil
|
||||
begin
|
||||
box_added = env[:box_collection].add(
|
||||
@temp_path, box_name, box_formats, env[:box_force])
|
||||
rescue Vagrant::Errors::BoxUpgradeRequired
|
||||
# Upgrade the box
|
||||
env[:box_collection].upgrade(box_name)
|
||||
box_url = nil
|
||||
|
||||
# Try adding it again
|
||||
retry
|
||||
urls.each do |url|
|
||||
begin
|
||||
box_url = download(url, env)
|
||||
break
|
||||
rescue Errors::DownloaderError => e
|
||||
env[:ui].error(I18n.t(
|
||||
"vagrant.box_download_error", message: e.message))
|
||||
box_url = nil
|
||||
end
|
||||
end
|
||||
|
||||
# Add the box!
|
||||
box = env[:box_collection].add(
|
||||
box_url, name, version,
|
||||
force: env[:box_force],
|
||||
metadata_url: md_url,
|
||||
providers: provider)
|
||||
ensure
|
||||
# Make sure we delete the temporary file after we add it,
|
||||
# unless we were interrupted, in which case we keep it around
|
||||
# so we can resume the download later.
|
||||
if !@download_interrupted
|
||||
@logger.debug("Deleting temporary box: #{box_url}")
|
||||
box_url.delete if box_url
|
||||
end
|
||||
end
|
||||
|
||||
# Call the 'recover' method in all cases to clean up the
|
||||
# downloaded temporary file.
|
||||
recover(env)
|
||||
env[:ui].success(I18n.t(
|
||||
"vagrant.box_added",
|
||||
name: box.name,
|
||||
version: box.version,
|
||||
provider: box.provider))
|
||||
|
||||
# Success, we added a box!
|
||||
env[:ui].success(
|
||||
I18n.t("vagrant.actions.box.add.added", name: box_added.name, provider: box_added.provider))
|
||||
# Store the added box in the env for future middleware
|
||||
env[:box_added] = box
|
||||
|
||||
# Persists URL used on download and the time it was added
|
||||
write_extra_info(box_added, download_url)
|
||||
|
||||
# Passes on the newly added box to the rest of the middleware chain
|
||||
env[:box_added] = box_added
|
||||
|
||||
# Carry on!
|
||||
@app.call(env)
|
||||
box
|
||||
end
|
||||
|
||||
def recover(env)
|
||||
if @temp_path && File.exist?(@temp_path) && !@download_interrupted
|
||||
File.unlink(@temp_path)
|
||||
end
|
||||
end
|
||||
# Returns the download options for the download.
|
||||
#
|
||||
# @return [Hash]
|
||||
def downloader(url, env, **opts)
|
||||
opts[:ui] = true if !opts.has_key?(:ui)
|
||||
|
||||
def download_box_url(url, env)
|
||||
temp_path = env[:tmp_path].join("box" + Digest::SHA1.hexdigest(url))
|
||||
@logger.info("Downloading box: #{url} => #{temp_path}")
|
||||
|
||||
|
@ -154,13 +371,6 @@ module Vagrant
|
|||
url = "file:#{file_path}"
|
||||
end
|
||||
|
||||
downloader_options = {}
|
||||
downloader_options[:ca_cert] = env[:box_download_ca_cert]
|
||||
downloader_options[:continue] = true
|
||||
downloader_options[:insecure] = env[:box_download_insecure]
|
||||
downloader_options[:ui] = env[:ui]
|
||||
downloader_options[:client_cert] = env[:box_client_cert]
|
||||
|
||||
# If the temporary path exists, verify it is not too old. If its
|
||||
# too old, delete it first because the data may have changed.
|
||||
if temp_path.file?
|
||||
|
@ -176,19 +386,35 @@ module Vagrant
|
|||
temp_path.unlink if delete
|
||||
end
|
||||
|
||||
downloader_options = {}
|
||||
downloader_options[:ca_cert] = env[:box_download_ca_cert]
|
||||
downloader_options[:continue] = true
|
||||
downloader_options[:insecure] = env[:box_download_insecure]
|
||||
downloader_options[:client_cert] = env[:box_client_cert]
|
||||
downloader_options[:ui] = env[:ui] if opts[:ui]
|
||||
|
||||
Util::Downloader.new(url, temp_path, downloader_options)
|
||||
end
|
||||
|
||||
def download(url, env, **opts)
|
||||
opts[:ui] = true if !opts.has_key?(:ui)
|
||||
|
||||
d = downloader(url, env, **opts)
|
||||
|
||||
# Download the box to a temporary path. We store the temporary
|
||||
# path as an instance variable so that the `#recover` method can
|
||||
# access it.
|
||||
env[:ui].info(I18n.t(
|
||||
"vagrant.actions.box.download.downloading",
|
||||
url: url))
|
||||
if temp_path.file?
|
||||
env[:ui].info(I18n.t("vagrant.actions.box.download.resuming"))
|
||||
if opts[:ui]
|
||||
env[:ui].detail(I18n.t(
|
||||
"vagrant.box_downloading",
|
||||
url: url))
|
||||
if File.file?(d.destination)
|
||||
env[:ui].info(I18n.t("vagrant.actions.box.download.resuming"))
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
downloader = Util::Downloader.new(url, temp_path, downloader_options)
|
||||
downloader.download!
|
||||
d.download!
|
||||
rescue Errors::DownloaderInterrupted
|
||||
# The downloader was interrupted, so just return, because that
|
||||
# means we were interrupted as well.
|
||||
|
@ -196,18 +422,45 @@ module Vagrant
|
|||
env[:ui].info(I18n.t("vagrant.actions.box.download.interrupted"))
|
||||
rescue Errors::DownloaderError
|
||||
# The download failed for some reason, clean out the temp path
|
||||
temp_path.unlink if temp_path.file?
|
||||
File.unlink(d.destination) if File.file?(d.destination)
|
||||
raise
|
||||
end
|
||||
|
||||
temp_path
|
||||
Pathname.new(d.destination)
|
||||
end
|
||||
|
||||
def write_extra_info(box_added, url)
|
||||
info = {'url' => url, 'downloaded_at' => Time.now.utc}
|
||||
box_added.directory.join('info.json').open("w+") do |f|
|
||||
f.write(JSON.dump(info))
|
||||
# Tests whether the given URL points to a metadata file or a
|
||||
# box file without completely downloading the file.
|
||||
#
|
||||
# @param [String] url
|
||||
# @return [Boolean] true if metadata
|
||||
def metadata_url?(url, env)
|
||||
d = downloader(url, env, ui: false)
|
||||
|
||||
# If we're downloading a file, cURL just returns no
|
||||
# content-type (makes sense), so we just test if it is JSON
|
||||
# by trying to parse JSON!
|
||||
uri = URI.parse(d.source)
|
||||
if uri.scheme == "file"
|
||||
url = uri.path
|
||||
url ||= uri.opaque
|
||||
|
||||
begin
|
||||
File.open(url, "r") do |f|
|
||||
BoxMetadata.new(f)
|
||||
end
|
||||
return true
|
||||
rescue Errors::BoxMetadataMalformed
|
||||
return false
|
||||
rescue Errno::ENOENT
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
output = d.head
|
||||
match = output.scan(/^Content-Type: (.+?)$/).last
|
||||
return false if !match
|
||||
match.last.chomp == "application/json"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
require "log4r"
|
||||
|
||||
module Vagrant
|
||||
module Action
|
||||
module Builtin
|
||||
# This middleware checks if there are outdated boxes. By default,
|
||||
# it only checks locally, but if `box_outdated_refresh` is set, it
|
||||
# will refresh the metadata associated with a box.
|
||||
class BoxCheckOutdated
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@logger = Log4r::Logger.new(
|
||||
"vagrant::action::builtin::box_check_outdated")
|
||||
end
|
||||
|
||||
def call(env)
|
||||
machine = env[:machine]
|
||||
|
||||
if !env[:box_outdated_force]
|
||||
if !machine.config.vm.box_check_update
|
||||
return @app.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
if !machine.box
|
||||
# The box doesn't exist. I suppose technically that means
|
||||
# that it is "outdated" but we show a specialized error
|
||||
# message anyways.
|
||||
raise Errors::BoxOutdatedNoBox, name: machine.config.vm.box
|
||||
end
|
||||
|
||||
box = machine.box
|
||||
if box.version == "0" && !box.metadata_url
|
||||
return @app.call(env)
|
||||
end
|
||||
|
||||
constraints = machine.config.vm.box_version
|
||||
|
||||
env[:ui].output(I18n.t(
|
||||
"vagrant.box_outdated_checking_with_refresh",
|
||||
name: box.name))
|
||||
update = nil
|
||||
begin
|
||||
update = box.has_update?(constraints)
|
||||
rescue Errors::VagrantError => e
|
||||
raise if !env[:box_outdated_ignore_errors]
|
||||
env[:ui].detail(I18n.t(
|
||||
"vagrant.box_outdated_metadata_error_single",
|
||||
message: e.message))
|
||||
end
|
||||
env[:box_outdated] = update != nil
|
||||
if update
|
||||
env[:ui].warn(I18n.t(
|
||||
"vagrant.box_outdated_single",
|
||||
name: update[0].name,
|
||||
current: box.version,
|
||||
latest: update[1].version))
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
def check_outdated_local(env)
|
||||
machine = env[:machine]
|
||||
box = env[:box_collection].find(
|
||||
machine.box.name, machine.box.provider,
|
||||
"> #{machine.box.version}")
|
||||
if box
|
||||
env[:ui].warn(I18n.t(
|
||||
"vagrant.box_outdated_local",
|
||||
name: box.name,
|
||||
old: machine.box.version,
|
||||
new: box.version))
|
||||
env[:box_outdated] = true
|
||||
return
|
||||
end
|
||||
|
||||
env[:box_outdated] = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -12,24 +12,67 @@ module Vagrant
|
|||
|
||||
def call(env)
|
||||
box_name = env[:box_name]
|
||||
box_provider = env[:box_provider].to_sym
|
||||
box_provider = env[:box_provider]
|
||||
box_provider = box_provider.to_sym if box_provider
|
||||
box_version = env[:box_version]
|
||||
|
||||
box = nil
|
||||
begin
|
||||
box = env[:box_collection].find(box_name, box_provider)
|
||||
rescue Vagrant::Errors::BoxUpgradeRequired
|
||||
env[:box_collection].upgrade(box_name)
|
||||
retry
|
||||
boxes = {}
|
||||
env[:box_collection].all.each do |n, v, p|
|
||||
boxes[n] ||= {}
|
||||
boxes[n][p] ||= []
|
||||
boxes[n][p] << v
|
||||
end
|
||||
|
||||
raise Vagrant::Errors::BoxNotFound, :name => box_name, :provider => box_provider if !box
|
||||
all_box = boxes[box_name]
|
||||
if !all_box
|
||||
raise Errors::BoxRemoveNotFound, name: box_name
|
||||
end
|
||||
|
||||
all_versions = nil
|
||||
if !box_provider
|
||||
if all_box.length == 1
|
||||
# There is only one provider, just use that.
|
||||
all_versions = all_box.values.first
|
||||
box_provider = all_box.keys.first
|
||||
else
|
||||
raise Errors::BoxRemoveMultiProvider,
|
||||
name: box_name,
|
||||
providers: all_box.keys.map(&:to_s).sort.join(", ")
|
||||
end
|
||||
else
|
||||
all_versions = all_box[box_provider]
|
||||
if !all_versions
|
||||
raise Errors::BoxRemoveProviderNotFound,
|
||||
name: box_name,
|
||||
provider: box_provider.to_s,
|
||||
providers: all_box.keys.map(&:to_s).sort.join(", ")
|
||||
end
|
||||
end
|
||||
|
||||
if !box_version
|
||||
if all_versions.length == 1
|
||||
# There is only one version, just use that.
|
||||
box_version = all_versions.first
|
||||
else
|
||||
# There are multiple versions, we can't choose.
|
||||
raise Errors::BoxRemoveMultiVersion,
|
||||
name: box_name,
|
||||
provider: box_provider.to_s,
|
||||
versions: all_versions.join(", ")
|
||||
end
|
||||
end
|
||||
|
||||
box = env[:box_collection].find(
|
||||
box_name, box_provider, box_version)
|
||||
|
||||
env[:ui].info(I18n.t("vagrant.commands.box.removing",
|
||||
:name => box_name,
|
||||
:provider => box_provider))
|
||||
:name => box.name,
|
||||
:provider => box.provider))
|
||||
box.destroy!
|
||||
|
||||
# Passes on the removed box to the rest of the middleware chain
|
||||
env[:box_removed] = box
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
require "log4r"
|
||||
|
||||
module Vagrant
|
||||
module Action
|
||||
module Builtin
|
||||
# This middleware updates a specific box if there are updates available.
|
||||
class BoxUpdate
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@logger = Log4r::Logger.new(
|
||||
"vagrant::action::builtin::box_update")
|
||||
end
|
||||
|
||||
def call(env)
|
||||
machine = env[:machine]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,84 @@
|
|||
require "thread"
|
||||
|
||||
require "log4r"
|
||||
|
||||
module Vagrant
|
||||
module Action
|
||||
module Builtin
|
||||
# This built-in middleware handles the `box` setting by verifying
|
||||
# the box is already installed, dowloading the box if it isn't,
|
||||
# updating the box if it is requested, etc.
|
||||
class HandleBox
|
||||
@@big_lock = Mutex.new
|
||||
@@small_locks = Hash.new { |h,k| h[k] = Mutex.new }
|
||||
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@logger = Log4r::Logger.new("vagrant::action::builtin::handle_box")
|
||||
end
|
||||
|
||||
def call(env)
|
||||
machine = env[:machine]
|
||||
|
||||
if !machine.config.vm.box
|
||||
@logger.info("Skipping HandleBox because no box is set")
|
||||
return @app.call(env)
|
||||
end
|
||||
|
||||
# Acquire a lock for this box to handle multi-threaded
|
||||
# environments.
|
||||
lock = nil
|
||||
@@big_lock.synchronize do
|
||||
lock = @@small_locks[machine.config.vm.box]
|
||||
end
|
||||
|
||||
lock.synchronize do
|
||||
handle_box(env)
|
||||
end
|
||||
|
||||
# Reload the environment and set the VM to be the new loaded VM.
|
||||
env[:machine] = env[:machine].env.machine(
|
||||
env[:machine].name, env[:machine].provider_name, true)
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
def handle_box(env)
|
||||
machine = env[:machine]
|
||||
|
||||
if machine.box
|
||||
@logger.info("Machine already has box. HandleBox will not run.")
|
||||
return
|
||||
end
|
||||
|
||||
# Determine the set of formats that this box can be in
|
||||
box_download_ca_cert = env[:machine].config.vm.box_download_ca_cert
|
||||
box_download_client_cert = env[:machine].config.vm.box_download_client_cert
|
||||
box_download_insecure = env[:machine].config.vm.box_download_insecure
|
||||
box_formats = env[:machine].provider_options[:box_format] ||
|
||||
env[:machine].provider_name
|
||||
|
||||
env[:ui].output(I18n.t(
|
||||
"vagrant.box_auto_adding", name: machine.config.vm.box))
|
||||
env[:ui].detail("Box Provider: #{Array(box_formats).join(", ")}")
|
||||
env[:ui].detail("Box Version: #{machine.config.vm.box_version}")
|
||||
|
||||
begin
|
||||
env[:action_runner].run(Vagrant::Action.action_box_add, env.merge({
|
||||
box_name: machine.config.vm.box,
|
||||
box_url: machine.config.vm.box_url || machine.config.vm.box,
|
||||
box_provider: box_formats,
|
||||
box_version: machine.config.vm.box_version,
|
||||
box_client_cert: box_download_client_cert,
|
||||
box_download_ca_cert: box_download_ca_cert,
|
||||
box_download_insecure: box_download_insecure,
|
||||
}))
|
||||
rescue Errors::BoxAlreadyExists
|
||||
# Just ignore this, since it means the next part will succeed!
|
||||
# This can happen in a multi-threaded environment.
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,96 +1,12 @@
|
|||
require "thread"
|
||||
|
||||
require "log4r"
|
||||
|
||||
module Vagrant
|
||||
module Action
|
||||
module Builtin
|
||||
# This built-in middleware handles the `box_url` setting, downloading
|
||||
# the box if necessary. You should place this early in your middleware
|
||||
# sequence for a provider after configuration validation but before
|
||||
# you attempt to use any box.
|
||||
class HandleBoxUrl
|
||||
@@big_lock = Mutex.new
|
||||
@@handle_box_url_locks = Hash.new { |h,k| h[k] = Mutex.new }
|
||||
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@logger = Log4r::Logger.new("vagrant::action::builtin::handle_box_url")
|
||||
end
|
||||
|
||||
class HandleBoxUrl < HandleBox
|
||||
def call(env)
|
||||
if !env[:machine].config.vm.box || !env[:machine].config.vm.box_url
|
||||
@logger.info("Skipping HandleBoxUrl because box or box_url not set.")
|
||||
@app.call(env)
|
||||
return
|
||||
end
|
||||
|
||||
if env[:machine].box
|
||||
@logger.info("Skipping HandleBoxUrl because box is already available")
|
||||
@app.call(env)
|
||||
return
|
||||
end
|
||||
|
||||
# Get a "big lock" to make sure that our more fine grained
|
||||
# lock access is thread safe.
|
||||
lock = nil
|
||||
@@big_lock.synchronize do
|
||||
lock = @@handle_box_url_locks[env[:machine].config.vm.box]
|
||||
end
|
||||
|
||||
box_name = env[:machine].config.vm.box
|
||||
box_url = env[:machine].config.vm.box_url
|
||||
box_download_ca_cert = env[:machine].config.vm.box_download_ca_cert
|
||||
box_download_checksum = env[:machine].config.vm.box_download_checksum
|
||||
box_download_checksum_type = env[:machine].config.vm.box_download_checksum_type
|
||||
box_download_client_cert = env[:machine].config.vm.box_download_client_cert
|
||||
box_download_insecure = env[:machine].config.vm.box_download_insecure
|
||||
|
||||
# Expand the CA cert file relative to the Vagrantfile path, if
|
||||
# there is one.
|
||||
if box_download_ca_cert
|
||||
box_download_ca_cert = File.expand_path(
|
||||
box_download_ca_cert, env[:machine].env.root_path)
|
||||
end
|
||||
|
||||
lock.synchronize do
|
||||
# Check that we don't already have the box, which can happen
|
||||
# if we're slow to acquire the lock because of another thread
|
||||
box_formats = env[:machine].provider_options[:box_format] ||
|
||||
env[:machine].provider_name
|
||||
if env[:box_collection].find(box_name, box_formats)
|
||||
break
|
||||
end
|
||||
|
||||
# Add the box then reload the box collection so that it becomes
|
||||
# aware of it.
|
||||
env[:ui].info I18n.t(
|
||||
"vagrant.actions.vm.check_box.not_found",
|
||||
:name => box_name,
|
||||
:provider => env[:machine].provider_name)
|
||||
|
||||
begin
|
||||
env[:action_runner].run(Vagrant::Action.action_box_add, {
|
||||
:box_checksum => box_download_checksum,
|
||||
:box_checksum_type => box_download_checksum_type,
|
||||
:box_client_cert => box_download_client_cert,
|
||||
:box_download_ca_cert => box_download_ca_cert,
|
||||
:box_download_insecure => box_download_insecure,
|
||||
:box_name => box_name,
|
||||
:box_provider => box_formats,
|
||||
:box_url => box_url,
|
||||
})
|
||||
rescue Errors::BoxAlreadyExists
|
||||
# Just ignore this, since it means the next part will succeed!
|
||||
# This can happen in a multi-threaded environment.
|
||||
end
|
||||
end
|
||||
|
||||
# Reload the environment and set the VM to be the new loaded VM.
|
||||
env[:machine] = env[:machine].env.machine(
|
||||
env[:machine].name, env[:machine].provider_name, true)
|
||||
|
||||
@app.call(env)
|
||||
env[:ui].warn("HandleBoxUrl middleware is deprecated. Use HandleBox instead.")
|
||||
env[:ui].warn("This is a bug with the provider. Please contact the creator")
|
||||
env[:ui].warn("of the provider you use to fix this.")
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
require 'fileutils'
|
||||
require "tempfile"
|
||||
|
||||
require "json"
|
||||
require "log4r"
|
||||
|
||||
require "vagrant/box_metadata"
|
||||
require "vagrant/util/downloader"
|
||||
require "vagrant/util/platform"
|
||||
require "vagrant/util/safe_chdir"
|
||||
require "vagrant/util/subprocess"
|
||||
|
@ -23,6 +26,11 @@ module Vagrant
|
|||
# @return [Symbol]
|
||||
attr_reader :provider
|
||||
|
||||
# The version of this box.
|
||||
#
|
||||
# @return [String]
|
||||
attr_reader :version
|
||||
|
||||
# This is the directory on disk where this box exists.
|
||||
#
|
||||
# @return [Pathname]
|
||||
|
@ -34,16 +42,24 @@ module Vagrant
|
|||
# @return [Hash]
|
||||
attr_reader :metadata
|
||||
|
||||
# This is the URL to the version info and other metadata for this
|
||||
# box.
|
||||
#
|
||||
# @return [String]
|
||||
attr_reader :metadata_url
|
||||
|
||||
# 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)
|
||||
def initialize(name, provider, version, directory, **opts)
|
||||
@name = name
|
||||
@version = version
|
||||
@provider = provider
|
||||
@directory = directory
|
||||
@metadata_url = opts[:metadata_url]
|
||||
|
||||
metadata_file = directory.join("metadata.json")
|
||||
raise Errors::BoxMetadataFileNotFound, :name => @name if !metadata_file.file?
|
||||
|
@ -69,6 +85,51 @@ module Vagrant
|
|||
return true
|
||||
end
|
||||
|
||||
# Loads the metadata URL and returns the latest metadata associated
|
||||
# with this box.
|
||||
#
|
||||
# @return [BoxMetadata]
|
||||
def load_metadata
|
||||
tf = Tempfile.new("vagrant")
|
||||
tf.close
|
||||
|
||||
url = @metadata_url
|
||||
if File.file?(url) || url !~ /^[a-z0-9]+:.*$/i
|
||||
url = File.expand_path(url)
|
||||
url = Util::Platform.cygwin_windows_path(url)
|
||||
url = "file:#{url}"
|
||||
end
|
||||
|
||||
Util::Downloader.new(url, tf.path).download!
|
||||
BoxMetadata.new(File.open(tf.path, "r"))
|
||||
end
|
||||
|
||||
# Checks if the box has an update and returns the metadata, version,
|
||||
# and provider. If the box doesn't have an update that satisfies the
|
||||
# constraints, it will return nil.
|
||||
#
|
||||
# This will potentially make a network call if it has to load the
|
||||
# metadata from the network.
|
||||
#
|
||||
# @param [String] version Version constraints the update must
|
||||
# satisfy. If nil, the version constrain defaults to being a
|
||||
# larger version than this box.
|
||||
# @return [Array]
|
||||
def has_update?(version=nil)
|
||||
if !@metadata_url
|
||||
raise Errors::BoxUpdateNoMetadata, name: @name
|
||||
end
|
||||
|
||||
version += ", " if version
|
||||
version ||= ""
|
||||
version += "> #{@version}"
|
||||
md = self.load_metadata
|
||||
newer = md.version(version, provider: @provider)
|
||||
return nil if !newer
|
||||
|
||||
[md, newer, newer.provider(@provider)]
|
||||
end
|
||||
|
||||
# This repackages this box and outputs it to the given path.
|
||||
#
|
||||
# @param [Pathname] path The full path (filename included) of where
|
||||
|
@ -96,7 +157,8 @@ module Vagrant
|
|||
return super if !other.is_a?(self.class)
|
||||
|
||||
# Comparison is done by composing the name and provider
|
||||
"#{@name}-#{@provider}" <=> "#{other.name}-#{other.provider}"
|
||||
"#{@name}-#{@version}-#{@provider}" <=>
|
||||
"#{other.name}-#{other.version}-#{other.provider}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
require "digest/sha1"
|
||||
require "thread"
|
||||
require "monitor"
|
||||
require "tmpdir"
|
||||
|
||||
require "log4r"
|
||||
|
@ -44,7 +44,7 @@ module Vagrant
|
|||
options ||= {}
|
||||
|
||||
@directory = directory
|
||||
@lock = Mutex.new
|
||||
@lock = Monitor.new
|
||||
@temp_root = options[:temp_dir_root]
|
||||
@logger = Log4r::Logger.new("vagrant::box_collection")
|
||||
end
|
||||
|
@ -56,53 +56,54 @@ module Vagrant
|
|||
# * 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
|
||||
# @param [String] version The version of this box.
|
||||
# @param [Array<String>] providers The providers that this box can
|
||||
# be a part of. This will be verified with the `metadata.json` and is
|
||||
# meant as a basic check. If this isn't given, then whatever provider
|
||||
# the box represents will be added.
|
||||
# @param [Boolean] force If true, any existing box with the same name
|
||||
# and provider will be replaced.
|
||||
def add(path, name, formats=nil, force=false)
|
||||
formats = [formats] if formats && !formats.is_a?(Array)
|
||||
def add(path, name, version, **opts)
|
||||
providers = opts[:providers]
|
||||
providers = Array(providers) if providers
|
||||
provider = nil
|
||||
|
||||
with_collection_lock do
|
||||
# A helper to check if a box exists. We store this in a variable
|
||||
# since we call it multiple times.
|
||||
check_box_exists = lambda do |box_formats|
|
||||
box = find(name, box_formats)
|
||||
next if !box
|
||||
# A helper to check if a box exists. We store this in a variable
|
||||
# since we call it multiple times.
|
||||
check_box_exists = lambda do |box_formats|
|
||||
box = find(name, box_formats, version)
|
||||
next if !box
|
||||
|
||||
if !force
|
||||
@logger.error("Box already exists, can't add: #{name} #{box_formats.join(", ")}")
|
||||
raise Errors::BoxAlreadyExists, :name => name, :formats => box_formats.join(", ")
|
||||
end
|
||||
|
||||
# We're forcing, so just delete the old box
|
||||
@logger.info(
|
||||
"Box already exists, but forcing so removing: #{name} #{box_formats.join(", ")}")
|
||||
box.destroy!
|
||||
if !opts[:force]
|
||||
@logger.error(
|
||||
"Box already exists, can't add: #{name} v#{version} #{box_formats.join(", ")}")
|
||||
raise Errors::BoxAlreadyExists,
|
||||
name: name,
|
||||
provider: box_formats.join(", "),
|
||||
version: version
|
||||
end
|
||||
|
||||
log_provider = formats ? formats.join(", ") : "any provider"
|
||||
# We're forcing, so just delete the old box
|
||||
@logger.info(
|
||||
"Box already exists, but forcing so removing: " +
|
||||
"#{name} v#{version} #{box_formats.join(", ")}")
|
||||
box.destroy!
|
||||
end
|
||||
|
||||
with_collection_lock do
|
||||
log_provider = providers ? providers.join(", ") : "any provider"
|
||||
@logger.debug("Adding box: #{name} (#{log_provider}) from #{path}")
|
||||
|
||||
# Verify the box doesn't exist early if we're given a provider. This
|
||||
# can potentially speed things up considerably since we don't need
|
||||
# to unpack any files.
|
||||
check_box_exists.call(formats) if formats
|
||||
|
||||
# Verify that a V1 box doesn't exist. If it does, then we signal
|
||||
# to the user that we need an upgrade.
|
||||
raise Errors::BoxUpgradeRequired, :name => name if v1_box?(@directory.join(name))
|
||||
check_box_exists.call(providers) if providers
|
||||
|
||||
# Create a temporary directory since we're not sure at this point if
|
||||
# the box we're unpackaging already exists (if no provider was given)
|
||||
|
@ -111,7 +112,10 @@ module Vagrant
|
|||
@logger.debug("Unpacking box into temporary directory: #{temp_dir}")
|
||||
result = Util::Subprocess.execute(
|
||||
"bsdtar", "-v", "-x", "-m", "-C", temp_dir.to_s, "-f", path.to_s)
|
||||
raise Errors::BoxUnpackageFailure, :output => result.stderr.to_s if result.exit_code != 0
|
||||
if result.exit_code != 0
|
||||
raise Errors::BoxUnpackageFailure,
|
||||
output: result.stderr.to_s
|
||||
end
|
||||
|
||||
# If we get a V1 box, we want to update it in place
|
||||
if v1_box?(temp_dir)
|
||||
|
@ -125,22 +129,14 @@ module Vagrant
|
|||
with_temp_dir(temp_dir) do |final_temp_dir|
|
||||
# Get an instance of the box we just added before it is finalized
|
||||
# in the system so we can inspect and use its metadata.
|
||||
box = Box.new(name, nil, final_temp_dir)
|
||||
box = Box.new(name, nil, version, final_temp_dir)
|
||||
|
||||
# Get the provider, since we'll need that to at the least add it
|
||||
# to the system or check that it matches what is given to us.
|
||||
box_provider = box.metadata["provider"]
|
||||
|
||||
if formats
|
||||
found = false
|
||||
formats.each do |format|
|
||||
# Verify that the given provider matches what the box has.
|
||||
if box_provider.to_sym == format.to_sym
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if providers
|
||||
found = providers.find { |p| p.to_sym == box_provider.to_sym }
|
||||
if !found
|
||||
@logger.error("Added box provider doesnt match expected: #{log_provider}")
|
||||
raise Errors::BoxProviderDoesntMatch,
|
||||
|
@ -155,7 +151,8 @@ module Vagrant
|
|||
provider = box_provider.to_sym
|
||||
|
||||
# Create the directory for this box, not including the provider
|
||||
box_dir = @directory.join(name)
|
||||
root_box_dir = @directory.join(dir_name(name))
|
||||
box_dir = root_box_dir.join(version)
|
||||
box_dir.mkpath
|
||||
@logger.debug("Box directory: #{box_dir}")
|
||||
|
||||
|
@ -177,20 +174,25 @@ module Vagrant
|
|||
@logger.debug("Moving: #{f} => #{destination}")
|
||||
FileUtils.mv(f, destination)
|
||||
end
|
||||
|
||||
if opts[:metadata_url]
|
||||
root_box_dir.join("metadata_url").open("w") do |f|
|
||||
f.write(opts[:metadata_url])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Return the box
|
||||
find(name, provider)
|
||||
find(name, provider, version)
|
||||
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.
|
||||
# @return [Array] Array of `[name, version, provider]` of the boxes
|
||||
# installed on this system.
|
||||
def all
|
||||
results = []
|
||||
|
||||
|
@ -201,27 +203,25 @@ module Vagrant
|
|||
# us in our folder structure.
|
||||
next if !child.directory?
|
||||
|
||||
box_name = child.basename.to_s
|
||||
box_name = undir_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
|
||||
# Otherwise, traverse the subdirectories and see what versions
|
||||
# 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}")
|
||||
child.children(true).each do |versiondir|
|
||||
next if !versiondir.directory?
|
||||
|
||||
version = versiondir.basename.to_s
|
||||
|
||||
versiondir.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, version, provider_name]
|
||||
else
|
||||
@logger.debug("Invalid box, ignoring: #{provider}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -234,78 +234,108 @@ module Vagrant
|
|||
#
|
||||
# @param [String] name Name of the box (logical name).
|
||||
# @param [Array] providers Providers that the box implements.
|
||||
# @param [String] version Version constraints to adhere to. Example:
|
||||
# "~> 1.0" or "= 1.0, ~> 1.1"
|
||||
# @return [Box] The box found, or `nil` if not found.
|
||||
def find(name, providers)
|
||||
providers = [providers].flatten
|
||||
def find(name, providers, version)
|
||||
providers = Array(providers)
|
||||
|
||||
# Build up the requirements we have
|
||||
requirements = version.split(",").map do |v|
|
||||
Gem::Requirement.new(v.strip)
|
||||
end
|
||||
|
||||
with_collection_lock do
|
||||
providers.each do |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.info("Box found: #{name} (#{provider})")
|
||||
return Box.new(name, provider, box_directory.dirname)
|
||||
box_directory = @directory.join(dir_name(name))
|
||||
if !box_directory.directory?
|
||||
@logger.info("Box not found: #{name} (#{providers.join(", ")})")
|
||||
return nil
|
||||
end
|
||||
|
||||
versions = box_directory.children(true).map do |versiondir|
|
||||
next if !versiondir.directory?
|
||||
version = versiondir.basename.to_s
|
||||
Gem::Version.new(version)
|
||||
end.compact
|
||||
|
||||
# Traverse through versions with the latest version first
|
||||
versions.sort.reverse.each do |v|
|
||||
if !requirements.all? { |r| r.satisfied_by?(v) }
|
||||
# Unsatisfied version requirements
|
||||
next
|
||||
end
|
||||
|
||||
# If we're looking for a VirtualBox box, then we check if there is
|
||||
# a V1 box.
|
||||
if provider.to_sym == :virtualbox
|
||||
# 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?(@directory.join(name))
|
||||
@logger.warn("V1 box found: #{name}")
|
||||
raise Errors::BoxUpgradeRequired, :name => name
|
||||
end
|
||||
versiondir = box_directory.join(v.to_s)
|
||||
providers.each do |provider|
|
||||
provider_dir = versiondir.join(provider.to_s)
|
||||
next if !provider_dir.directory?
|
||||
@logger.info("Box found: #{name} (#{provider})")
|
||||
|
||||
metadata_url = nil
|
||||
metadata_url_file = box_directory.join("metadata_url")
|
||||
metadata_url = metadata_url_file.read if metadata_url_file.file?
|
||||
|
||||
return Box.new(
|
||||
name, provider, v.to_s, provider_dir,
|
||||
metadata_url: metadata_url,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Didn't find it, return nil
|
||||
@logger.info("Box not found: #{name} (#{providers.join(", ")})")
|
||||
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.
|
||||
#
|
||||
# @param [String] name Name of the box (logical name).
|
||||
# @return [Boolean] `true` otherwise an exception is raised.
|
||||
def upgrade(name)
|
||||
# This upgrades a v1.1 - v1.4 box directory structure up to a v1.5
|
||||
# directory structure. This will raise exceptions if it fails in any
|
||||
# way.
|
||||
def upgrade_v1_1_v1_5
|
||||
with_collection_lock do
|
||||
@logger.debug("Upgrade request for box: #{name}")
|
||||
box_dir = @directory.join(name)
|
||||
temp_dir = Pathname.new(Dir.mktmpdir(TEMP_PREFIX, @temp_root))
|
||||
|
||||
# If the box doesn't exist at all, raise an exception
|
||||
raise Errors::BoxNotFound, :name => name, :provider => "virtualbox" if !box_dir.directory?
|
||||
@directory.children(true).each do |boxdir|
|
||||
# Ignore all non-directories because they can't be boxes
|
||||
next if !boxdir.directory?
|
||||
|
||||
if v1_box?(box_dir)
|
||||
@logger.debug("V1 box #{name} found. Upgrading!")
|
||||
box_name = boxdir.basename.to_s
|
||||
|
||||
# First we actually perform the upgrade
|
||||
temp_dir = v1_upgrade(box_dir)
|
||||
# If it is a v1 box, then we need to upgrade it first
|
||||
if v1_box?(boxdir)
|
||||
upgrade_dir = v1_upgrade(boxdir)
|
||||
FileUtils.mv(upgrade_dir, boxdir.join("virtualbox"))
|
||||
end
|
||||
|
||||
# Rename the temporary directory to the provider.
|
||||
FileUtils.mv(temp_dir.to_s, box_dir.join("virtualbox").to_s)
|
||||
@logger.info("Box '#{name}' upgraded from V1 to V2.")
|
||||
# Create the directory for this box
|
||||
new_box_dir = temp_dir.join(dir_name(box_name), "0")
|
||||
new_box_dir.mkpath
|
||||
|
||||
# Go through each provider and move it
|
||||
boxdir.children(true).each do |providerdir|
|
||||
FileUtils.cp_r(providerdir, new_box_dir.join(providerdir.basename))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# We did it! Or the v1 box didn't exist so it doesn't matter.
|
||||
return true
|
||||
# Move the folder into place
|
||||
@directory.rmtree
|
||||
FileUtils.mv(temp_dir.to_s, @directory.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Returns the directory name for the box of the given name.
|
||||
#
|
||||
# @param [String] name
|
||||
# @return [String]
|
||||
def dir_name(name)
|
||||
name.gsub("/", "-VAGRANTSLASH-")
|
||||
end
|
||||
|
||||
# Returns the directory name for the box cleaned up
|
||||
def undir_name(name)
|
||||
name.gsub("-VAGRANTSLASH-", "/")
|
||||
end
|
||||
|
||||
# This checks if the given directory represents a V1 box on the
|
||||
# system.
|
||||
#
|
||||
|
@ -361,17 +391,7 @@ module Vagrant
|
|||
# This locks the region given by the block with a lock on this
|
||||
# collection.
|
||||
def with_collection_lock
|
||||
lock = @lock
|
||||
|
||||
begin
|
||||
lock.synchronize {}
|
||||
rescue ThreadError
|
||||
# If we already hold the lock, just create a new lock so
|
||||
# we definitely don't block and don't get an error.
|
||||
lock = Mutex.new
|
||||
end
|
||||
|
||||
lock.synchronize do
|
||||
@lock.synchronize do
|
||||
return yield
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
require "json"
|
||||
|
||||
module Vagrant
|
||||
# BoxMetadata represents metadata about a box, including the name
|
||||
# it should have, a description of it, the versions it has, and
|
||||
# more.
|
||||
class BoxMetadata
|
||||
# The name that the box should be if it is added.
|
||||
#
|
||||
# @return [String]
|
||||
attr_accessor :name
|
||||
|
||||
# The long-form human-readable description of a box.
|
||||
#
|
||||
# @return [String]
|
||||
attr_accessor :description
|
||||
|
||||
# Loads the metadata associated with the box from the given
|
||||
# IO.
|
||||
#
|
||||
# @param [IO] io An IO object to read the metadata from.
|
||||
def initialize(io)
|
||||
begin
|
||||
@raw = JSON.load(io)
|
||||
rescue JSON::ParserError => e
|
||||
raise Errors::BoxMetadataMalformed,
|
||||
error: e.to_s
|
||||
end
|
||||
|
||||
@raw ||= {}
|
||||
@name = @raw["name"]
|
||||
@description = @raw["description"]
|
||||
@version_map = (@raw["versions"] || []).map do |v|
|
||||
[Gem::Version.new(v["version"]), v]
|
||||
end
|
||||
@version_map = Hash[@version_map]
|
||||
|
||||
# TODO: check for corruption:
|
||||
# - malformed version
|
||||
end
|
||||
|
||||
# Returns data about a single version that is included in this
|
||||
# metadata.
|
||||
#
|
||||
# @param [String] version The version to return, this can also
|
||||
# be a constraint.
|
||||
# @return [Version] The matching version or nil if a matching
|
||||
# version was not found.
|
||||
def version(version, **opts)
|
||||
requirements = version.split(",").map do |v|
|
||||
Gem::Requirement.new(v.strip)
|
||||
end
|
||||
|
||||
providers = nil
|
||||
providers = Array(opts[:provider]).map(&:to_sym) if opts[:provider]
|
||||
|
||||
@version_map.keys.sort.reverse.each do |v|
|
||||
next if !requirements.all? { |r| r.satisfied_by?(v) }
|
||||
version = Version.new(@version_map[v])
|
||||
next if (providers & version.providers).empty? if providers
|
||||
return version
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
# Returns all the versions supported by this metadata. These
|
||||
# versions are sorted so the last element of the list is the
|
||||
# latest version.
|
||||
#
|
||||
# @return[Array<String>]
|
||||
def versions
|
||||
@version_map.keys.sort.map(&:to_s)
|
||||
end
|
||||
|
||||
# Represents a single version within the metadata.
|
||||
class Version
|
||||
# The version that this Version object represents.
|
||||
#
|
||||
# @return [String]
|
||||
attr_accessor :version
|
||||
|
||||
def initialize(raw=nil)
|
||||
return if !raw
|
||||
|
||||
@version = raw["version"]
|
||||
@provider_map = (raw["providers"] || []).map do |p|
|
||||
[p["name"].to_sym, p]
|
||||
end
|
||||
@provider_map = Hash[@provider_map]
|
||||
end
|
||||
|
||||
# Returns a [Provider] for the given name, or nil if it isn't
|
||||
# supported by this version.
|
||||
def provider(name)
|
||||
p = @provider_map[name.to_sym]
|
||||
return nil if !p
|
||||
Provider.new(p)
|
||||
end
|
||||
|
||||
# Returns the providers that are available for this version
|
||||
# of the box.
|
||||
#
|
||||
# @return [Array<Symbol>]
|
||||
def providers
|
||||
@provider_map.keys.map(&:to_sym)
|
||||
end
|
||||
end
|
||||
|
||||
# Provider represents a single provider-specific box available
|
||||
# for a version for a box.
|
||||
class Provider
|
||||
# The name of the provider.
|
||||
#
|
||||
# @return [String]
|
||||
attr_accessor :name
|
||||
|
||||
# The URL of the box.
|
||||
#
|
||||
# @return [String]
|
||||
attr_accessor :url
|
||||
|
||||
def initialize(raw)
|
||||
@name = raw["name"]
|
||||
@url = raw["url"]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -14,6 +14,12 @@ module Vagrant
|
|||
# defined as basically a folder with a "Vagrantfile." This class allows
|
||||
# access to the VMs, CLI, etc. all in the scope of this environment.
|
||||
class Environment
|
||||
# This is the current version that this version of Vagrant is
|
||||
# compatible with in the home directory.
|
||||
#
|
||||
# @return [String]
|
||||
CURRENT_SETUP_VERSION = "1.5"
|
||||
|
||||
DEFAULT_LOCAL_DATA = ".vagrant"
|
||||
|
||||
# The `cwd` that this environment represents
|
||||
|
@ -342,14 +348,8 @@ module Vagrant
|
|||
load_box_and_overrides = lambda do
|
||||
box = nil
|
||||
if config.vm.box
|
||||
begin
|
||||
box = boxes.find(config.vm.box, box_formats)
|
||||
rescue Errors::BoxUpgradeRequired
|
||||
# Upgrade the box if we must
|
||||
@logger.info("Upgrading box during config load: #{config.vm.box}")
|
||||
boxes.upgrade(config.vm.box)
|
||||
retry
|
||||
end
|
||||
box = boxes.find(
|
||||
config.vm.box, box_formats, config.vm.box_version)
|
||||
end
|
||||
|
||||
# If a box was found, then we attempt to load the Vagrantfile for
|
||||
|
@ -630,13 +630,37 @@ module Vagrant
|
|||
raise Errors::HomeDirectoryNotAccessible, home_path: @home_path.to_s
|
||||
end
|
||||
|
||||
# Create the version file to mark the version of the home directory
|
||||
# we're using.
|
||||
# Create the version file that we use to track the structure of
|
||||
# the home directory. If we have an old version, we need to explicitly
|
||||
# upgrade it. Otherwise, we just mark that its the current version.
|
||||
version_file = @home_path.join("setup_version")
|
||||
if version_file.file?
|
||||
version = version_file.read
|
||||
if version > CURRENT_SETUP_VERSION
|
||||
raise Errors::HomeDirectoryLaterVersion
|
||||
end
|
||||
|
||||
case version
|
||||
when CURRENT_SETUP_VERSION
|
||||
# We're already good, at the latest version.
|
||||
when "1.1"
|
||||
# We need to update our directory structure
|
||||
upgrade_home_path_v1_1
|
||||
|
||||
# Delete the version file so we put our latest version in
|
||||
version_file.delete
|
||||
else
|
||||
raise Errors::HomeDirectoryUnknownVersion,
|
||||
path: @home_path.to_s,
|
||||
version: version
|
||||
end
|
||||
end
|
||||
|
||||
if !version_file.file?
|
||||
@logger.debug("Setting up the version file.")
|
||||
@logger.debug(
|
||||
"Creating home directory version file: #{CURRENT_SETUP_VERSION}")
|
||||
version_file.open("w") do |f|
|
||||
f.write("1.1")
|
||||
f.write(CURRENT_SETUP_VERSION)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -723,6 +747,14 @@ module Vagrant
|
|||
nil
|
||||
end
|
||||
|
||||
# This upgrades a home directory that was in the v1.1 format to the
|
||||
# v1.5 format. It will raise exceptions if anything fails.
|
||||
def upgrade_home_path_v1_1
|
||||
collection = BoxCollection.new(
|
||||
@home_path.join("boxes"), temp_dir_root: tmp_path)
|
||||
collection.upgrade_v1_1_v1_5
|
||||
end
|
||||
|
||||
# This upgrades a Vagrant 1.0.x "dotfile" to the new V2 format.
|
||||
#
|
||||
# This is a destructive process. Once the upgrade is complete, the
|
||||
|
|
|
@ -120,8 +120,32 @@ module Vagrant
|
|||
error_key(:batch_multi_error)
|
||||
end
|
||||
|
||||
class BoxAddMetadataMultiURL < VagrantError
|
||||
error_key(:box_add_metadata_multi_url)
|
||||
end
|
||||
|
||||
class BoxAddNameMismatch < VagrantError
|
||||
error_key(:box_add_name_mismatch)
|
||||
end
|
||||
|
||||
class BoxAddNameRequired < VagrantError
|
||||
error_key(:box_add_name_required)
|
||||
end
|
||||
|
||||
class BoxAddNoMatchingProvider < VagrantError
|
||||
error_key(:box_add_no_matching_provider)
|
||||
end
|
||||
|
||||
class BoxAddNoMatchingVersion < VagrantError
|
||||
error_key(:box_add_no_matching_version)
|
||||
end
|
||||
|
||||
class BoxAddShortNotFound < VagrantError
|
||||
error_key(:box_add_short_not_found)
|
||||
end
|
||||
|
||||
class BoxAlreadyExists < VagrantError
|
||||
error_key(:already_exists, "vagrant.actions.box.unpackage")
|
||||
error_key(:box_add_exists)
|
||||
end
|
||||
|
||||
class BoxChecksumInvalidType < VagrantError
|
||||
|
@ -144,20 +168,56 @@ module Vagrant
|
|||
error_key(:box_metadata_file_not_found)
|
||||
end
|
||||
|
||||
class BoxMetadataMalformed < VagrantError
|
||||
error_key(:box_metadata_malformed)
|
||||
end
|
||||
|
||||
class BoxNotFound < VagrantError
|
||||
error_key(:box_not_found)
|
||||
end
|
||||
|
||||
class BoxNotFoundWithProvider < VagrantError
|
||||
error_key(:box_not_found_with_provider)
|
||||
end
|
||||
|
||||
class BoxOutdatedNoBox < VagrantError
|
||||
error_key(:box_outdated_no_box)
|
||||
end
|
||||
|
||||
class BoxProviderDoesntMatch < VagrantError
|
||||
error_key(:box_provider_doesnt_match)
|
||||
end
|
||||
|
||||
class BoxRemoveNotFound < VagrantError
|
||||
error_key(:box_remove_not_found)
|
||||
end
|
||||
|
||||
class BoxRemoveProviderNotFound < VagrantError
|
||||
error_key(:box_remove_provider_not_found)
|
||||
end
|
||||
|
||||
class BoxRemoveMultiProvider < VagrantError
|
||||
error_key(:box_remove_multi_provider)
|
||||
end
|
||||
|
||||
class BoxRemoveMultiVersion < VagrantError
|
||||
error_key(:box_remove_multi_version)
|
||||
end
|
||||
|
||||
class BoxServerNotSet < VagrantError
|
||||
error_key(:box_server_not_set)
|
||||
end
|
||||
|
||||
class BoxUnpackageFailure < VagrantError
|
||||
error_key(:untar_failure, "vagrant.actions.box.unpackage")
|
||||
end
|
||||
|
||||
class BoxUpgradeRequired < VagrantError
|
||||
error_key(:box_upgrade_required)
|
||||
class BoxUpdateMultiProvider < VagrantError
|
||||
error_key(:box_update_multi_provider)
|
||||
end
|
||||
|
||||
class BoxUpdateNoMetadata < VagrantError
|
||||
error_key(:box_update_no_metadata)
|
||||
end
|
||||
|
||||
class BoxVerificationFailed < VagrantError
|
||||
|
@ -260,10 +320,18 @@ module Vagrant
|
|||
error_key(:environment_locked)
|
||||
end
|
||||
|
||||
class HomeDirectoryLaterVersion < VagrantError
|
||||
error_key(:home_dir_later_version)
|
||||
end
|
||||
|
||||
class HomeDirectoryNotAccessible < VagrantError
|
||||
error_key(:home_dir_not_accessible)
|
||||
end
|
||||
|
||||
class HomeDirectoryUnknownVersion < VagrantError
|
||||
error_key(:home_dir_unknown_version)
|
||||
end
|
||||
|
||||
class ForwardPortAdapterNotFound < VagrantError
|
||||
error_key(:forward_port_adapter_not_found)
|
||||
end
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
require "pathname"
|
||||
|
||||
module Vagrant
|
||||
# This is the default endpoint of the Vagrant Cloud in
|
||||
# use. API calls will be made to this for various functions
|
||||
# of Vagrant that may require remote access.
|
||||
#
|
||||
# @return [String]
|
||||
DEFAULT_SERVER_URL = "http://www.vagrantcloud.com"
|
||||
|
||||
# This returns whether or not 3rd party plugins should be loaded.
|
||||
#
|
||||
# @return [Boolean]
|
||||
|
@ -8,6 +15,13 @@ module Vagrant
|
|||
!ENV["VAGRANT_NO_PLUGINS"]
|
||||
end
|
||||
|
||||
# Returns the URL prefix to the server.
|
||||
#
|
||||
# @return [String]
|
||||
def self.server_url
|
||||
ENV["VAGRANT_SERVER_URL"] || DEFAULT_SERVER_URL
|
||||
end
|
||||
|
||||
# The source root is the path to the root directory of the Vagrant source.
|
||||
#
|
||||
# @return [Pathname]
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require "delegate"
|
||||
require "io/console"
|
||||
require "thread"
|
||||
|
||||
require "log4r"
|
||||
|
@ -123,17 +124,28 @@ module Vagrant
|
|||
|
||||
# Setup the options so that the new line is suppressed
|
||||
opts ||= {}
|
||||
opts[:echo] = true if !opts.has_key?(:echo)
|
||||
opts[:new_line] = false if !opts.has_key?(:new_line)
|
||||
opts[:prefix] = false if !opts.has_key?(:prefix)
|
||||
|
||||
# Output the data
|
||||
say(:info, message, opts)
|
||||
|
||||
input = nil
|
||||
if opts[:echo]
|
||||
input = $stdin.gets
|
||||
else
|
||||
input = $stdin.noecho(&:gets)
|
||||
|
||||
# Output a newline because without echo, the newline isn't
|
||||
# echoed either.
|
||||
say(:info, "\n", opts)
|
||||
end
|
||||
|
||||
# Get the results and chomp off the newline. We do a logical OR
|
||||
# here because `gets` can return a nil, for example in the case
|
||||
# that ctrl-D is pressed on the input.
|
||||
input = $stdin.gets || ""
|
||||
input.chomp
|
||||
(input || "").chomp
|
||||
end
|
||||
|
||||
# This is used to output progress reports to the UI.
|
||||
|
@ -209,7 +221,10 @@ module Vagrant
|
|||
class_eval <<-CODE
|
||||
def #{method}(message, *args, **opts)
|
||||
super(message)
|
||||
opts[:bold] = #{method.inspect} != :detail if !opts.has_key?(:bold)
|
||||
if !opts.has_key?(:bold)
|
||||
opts[:bold] = #{method.inspect} != :detail && \
|
||||
#{method.inspect} != :ask
|
||||
end
|
||||
@ui.#{method}(format_message(#{method.inspect}, message, **opts), *args, **opts)
|
||||
end
|
||||
CODE
|
||||
|
@ -241,7 +256,8 @@ module Vagrant
|
|||
prefix = ""
|
||||
if !opts.has_key?(:prefix) || opts[:prefix]
|
||||
prefix = OUTPUT_PREFIX
|
||||
prefix = " " * OUTPUT_PREFIX.length if type == :detail
|
||||
prefix = " " * OUTPUT_PREFIX.length if \
|
||||
type == :detail || type == :ask
|
||||
end
|
||||
|
||||
# Fast-path if there is no prefix
|
||||
|
@ -281,16 +297,17 @@ module Vagrant
|
|||
opts[:color] = :green if type == :success
|
||||
opts[:color] = :yellow if type == :warn
|
||||
|
||||
# If there is no color specified, exit early
|
||||
return message if !opts.has_key?(:color)
|
||||
|
||||
# If it is a detail, it is not bold. Every other message type
|
||||
# is bolded.
|
||||
bold = !!opts[:bold]
|
||||
color = COLORS[opts[:color]]
|
||||
colorseq = "#{bold ? 1 : 0 }"
|
||||
if opts[:color]
|
||||
color = COLORS[opts[:color]]
|
||||
colorseq += ";#{color}"
|
||||
end
|
||||
|
||||
# Color the message and make sure to reset the color at the end
|
||||
"\033[#{bold ? 1 : 0};#{color}m#{message}\033[0m"
|
||||
"\033[#{colorseq}m#{message}\033[0m"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
require "log4r"
|
||||
|
||||
require "vagrant/util/busy"
|
||||
require "vagrant/util/platform"
|
||||
require "vagrant/util/subprocess"
|
||||
|
||||
module Vagrant
|
||||
|
@ -13,6 +14,9 @@ module Vagrant
|
|||
# are properly tracked.
|
||||
USER_AGENT = "Vagrant/#{VERSION}"
|
||||
|
||||
attr_reader :source
|
||||
attr_reader :destination
|
||||
|
||||
def initialize(source, destination, options=nil)
|
||||
@logger = Log4r::Logger.new("vagrant::util::downloader")
|
||||
@source = source.to_s
|
||||
|
@ -34,31 +38,10 @@ module Vagrant
|
|||
# If this method returns without an exception, the download
|
||||
# succeeded. An exception will be raised if the download failed.
|
||||
def download!
|
||||
# Build the list of parameters to execute with cURL
|
||||
options = [
|
||||
"--fail",
|
||||
"--location",
|
||||
"--max-redirs", "10",
|
||||
"--user-agent", USER_AGENT,
|
||||
"--output", @destination,
|
||||
]
|
||||
|
||||
options += ["--cacert", @ca_cert] if @ca_cert
|
||||
options += ["--continue-at", "-"] if @continue
|
||||
options << "--insecure" if @insecure
|
||||
options << "--cert" << @client_cert if @client_cert
|
||||
options, subprocess_options = self.options
|
||||
options += ["--output", @destination]
|
||||
options << @source
|
||||
|
||||
# Specify some options for the subprocess
|
||||
subprocess_options = {}
|
||||
|
||||
# If we're in Vagrant, then we use the packaged CA bundle
|
||||
if Vagrant.in_installer?
|
||||
subprocess_options[:env] ||= {}
|
||||
subprocess_options[:env]["CURL_CA_BUNDLE"] =
|
||||
File.expand_path("cacert.pem", ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"])
|
||||
end
|
||||
|
||||
# This variable can contain the proc that'll be sent to
|
||||
# the subprocess execute.
|
||||
data_proc = nil
|
||||
|
@ -109,12 +92,48 @@ module Vagrant
|
|||
|
||||
output = "Progress: #{columns[0]}% (Rate: #{columns[11]}/s, Estimated time remaining: #{columns[10]})"
|
||||
@ui.clear_line
|
||||
@ui.info(output, :new_line => false)
|
||||
@ui.detail(output, :new_line => false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Add the subprocess options onto the options we'll execute with
|
||||
@logger.info("Downloader starting download: ")
|
||||
@logger.info(" -- Source: #{@source}")
|
||||
@logger.info(" -- Destination: #{@destination}")
|
||||
|
||||
begin
|
||||
execute_curl(options, subprocess_options, &data_proc)
|
||||
ensure
|
||||
# If we're outputting to the UI, clear the output to
|
||||
# avoid lingering progress meters.
|
||||
if @ui
|
||||
@ui.clear_line
|
||||
|
||||
# Windows doesn't clear properly for some reason, so we just
|
||||
# output one more newline.
|
||||
@ui.detail("") if Platform.windows?
|
||||
end
|
||||
end
|
||||
|
||||
# Everything succeeded
|
||||
true
|
||||
end
|
||||
|
||||
# Does a HEAD request of the URL and returns the output.
|
||||
def head
|
||||
options, subprocess_options = self.options
|
||||
options.unshift("-I")
|
||||
options << @source
|
||||
|
||||
@logger.info("HEAD: #{@source}")
|
||||
result = execute_curl(options, subprocess_options)
|
||||
result.stdout
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def execute_curl(options, subprocess_options, &data_proc)
|
||||
options = options.dup
|
||||
options << subprocess_options
|
||||
|
||||
# Create the callback that is called if we are interrupted
|
||||
|
@ -124,10 +143,6 @@ module Vagrant
|
|||
interrupted = true
|
||||
end
|
||||
|
||||
@logger.info("Downloader starting download: ")
|
||||
@logger.info(" -- Source: #{@source}")
|
||||
@logger.info(" -- Destination: #{@destination}")
|
||||
|
||||
# Execute!
|
||||
result = Busy.busy(int_callback) do
|
||||
Subprocess.execute("curl", *options, &data_proc)
|
||||
|
@ -136,10 +151,6 @@ module Vagrant
|
|||
# If the download was interrupted, then raise a specific error
|
||||
raise Errors::DownloaderInterrupted if interrupted
|
||||
|
||||
# If we're outputting to the UI, clear the output to
|
||||
# avoid lingering progress meters.
|
||||
@ui.clear_line if @ui
|
||||
|
||||
# If it didn't exit successfully, we need to parse the data and
|
||||
# show an error message.
|
||||
if result.exit_code != 0
|
||||
|
@ -149,8 +160,37 @@ module Vagrant
|
|||
raise Errors::DownloaderError, :message => parts[1].chomp
|
||||
end
|
||||
|
||||
# Everything succeeded
|
||||
true
|
||||
result
|
||||
end
|
||||
|
||||
# Returns the varoius cURL and subprocess options.
|
||||
#
|
||||
# @return [Array<Array, Hash>]
|
||||
def options
|
||||
# Build the list of parameters to execute with cURL
|
||||
options = [
|
||||
"--fail",
|
||||
"--location",
|
||||
"--max-redirs", "10",
|
||||
"--user-agent", USER_AGENT,
|
||||
]
|
||||
|
||||
options += ["--cacert", @ca_cert] if @ca_cert
|
||||
options += ["--continue-at", "-"] if @continue
|
||||
options << "--insecure" if @insecure
|
||||
options << "--cert" << @client_cert if @client_cert
|
||||
|
||||
# Specify some options for the subprocess
|
||||
subprocess_options = {}
|
||||
|
||||
# If we're in Vagrant, then we use the packaged CA bundle
|
||||
if Vagrant.in_installer?
|
||||
subprocess_options[:env] ||= {}
|
||||
subprocess_options[:env]["CURL_CA_BUNDLE"] =
|
||||
File.expand_path("cacert.pem", ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"])
|
||||
end
|
||||
|
||||
return [options, subprocess_options]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,18 +8,10 @@ module VagrantPlugins
|
|||
options = {}
|
||||
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant box add <name> <url> [--provider provider] [-h]"
|
||||
o.banner = "Usage: vagrant box add <url> [-h]"
|
||||
o.separator ""
|
||||
|
||||
o.on("--checksum VALUE", String, "Checksum") do |c|
|
||||
options[:checksum] = c
|
||||
end
|
||||
|
||||
o.on("--checksum-type VALUE", String, "Checksum type") do |c|
|
||||
options[:checksum_type] = c.to_sym
|
||||
end
|
||||
|
||||
o.on("-c", "--clean", "Remove old temporary download if it exists.") do |c|
|
||||
o.on("-c", "--clean", "Clean any temporary download files") do |c|
|
||||
options[:clean] = c
|
||||
end
|
||||
|
||||
|
@ -27,45 +19,72 @@ module VagrantPlugins
|
|||
options[:force] = f
|
||||
end
|
||||
|
||||
o.on("--insecure", "If set, SSL certs will not be validated.") do |i|
|
||||
o.on("--insecure", "Do not validate SSL certificates") do |i|
|
||||
options[:insecure] = i
|
||||
end
|
||||
|
||||
o.on("--cacert certfile", String, "CA certificate") do |c|
|
||||
o.on("--cacert certfile", String, "CA certificate for SSL download") do |c|
|
||||
options[:ca_cert] = c
|
||||
end
|
||||
|
||||
o.on("--cert certfile", String,
|
||||
"The client SSL cert") do |c|
|
||||
"A client SSL cert, if needed") do |c|
|
||||
options[:client_cert] = c
|
||||
end
|
||||
|
||||
o.on("--provider provider", String,
|
||||
"The provider that backs the box.") do |p|
|
||||
o.on("--provider VALUE", String, "Provider the box should satisfy") do |p|
|
||||
options[:provider] = p
|
||||
end
|
||||
|
||||
o.on("--box-version VALUE", String, "Constrain version of the added box") do |v|
|
||||
options[:version] = v
|
||||
end
|
||||
|
||||
o.separator ""
|
||||
o.separator "The options below only apply if you're adding a box file directly,"
|
||||
o.separator "and not using a Vagrant server or a box structured like 'user/box':"
|
||||
o.separator ""
|
||||
|
||||
o.on("--checksum VALUE", String, "Checksum for the box") do |c|
|
||||
options[:checksum] = c
|
||||
end
|
||||
|
||||
o.on("--checksum-type VALUE", String, "Checksum type (md5, sha1, sha256)") do |c|
|
||||
options[:checksum_type] = c.to_sym
|
||||
end
|
||||
|
||||
o.on("--name VALUE", String, "Name of the box") do |n|
|
||||
options[:name] = n
|
||||
end
|
||||
end
|
||||
|
||||
# Parse the options
|
||||
argv = parse_options(opts)
|
||||
return if !argv
|
||||
raise Vagrant::Errors::CLIInvalidUsage, :help => opts.help.chomp if argv.length < 2
|
||||
if argv.empty? || argv.length > 2
|
||||
raise Vagrant::Errors::CLIInvalidUsage,
|
||||
help: opts.help.chomp
|
||||
end
|
||||
|
||||
# Get the provider if one was set
|
||||
provider = nil
|
||||
provider = options[:provider].to_sym if options[:provider]
|
||||
url = argv[0]
|
||||
if argv.length == 2
|
||||
options[:name] = argv[0]
|
||||
url = argv[1]
|
||||
end
|
||||
|
||||
@env.action_runner.run(Vagrant::Action.action_box_add, {
|
||||
:box_name => argv[0],
|
||||
:box_provider => provider,
|
||||
:box_url => argv[1],
|
||||
:box_checksum_type => options[:checksum_type],
|
||||
:box_checksum => options[:checksum],
|
||||
:box_clean => options[:clean],
|
||||
:box_force => options[:force],
|
||||
:box_download_ca_cert => options[:ca_cert],
|
||||
:box_download_client_cert => options[:client_cert],
|
||||
:box_download_insecure => options[:insecure],
|
||||
box_url: url,
|
||||
box_name: options[:name],
|
||||
box_provider: options[:provider],
|
||||
box_version: options[:version],
|
||||
box_checksum_type: options[:checksum_type],
|
||||
box_checksum: options[:checksum],
|
||||
box_clean: options[:clean],
|
||||
box_force: options[:force],
|
||||
box_download_ca_cert: options[:ca_cert],
|
||||
box_download_client_cert: options[:client_cert],
|
||||
box_download_insecure: options[:insecure],
|
||||
ui: Vagrant::UI::Prefixed.new(@env.ui, "box"),
|
||||
})
|
||||
|
||||
# Success, exit status 0
|
||||
|
|
|
@ -42,13 +42,15 @@ module VagrantPlugins
|
|||
# ignore the "v1" param for now since I'm not yet sure if its
|
||||
# important for the user to know what boxes need to be upgraded
|
||||
# and which don't, since we plan on doing that transparently.
|
||||
boxes.each do |name, provider, _v1|
|
||||
@env.ui.info("#{name.ljust(longest_box_length)} (#{provider})", :prefix => false)
|
||||
boxes.each do |name, version, provider|
|
||||
@env.ui.info("#{name.ljust(longest_box_length)} (#{provider})")
|
||||
|
||||
@env.ui.machine("box-name", name)
|
||||
@env.ui.machine("box-provider", provider)
|
||||
@env.ui.machine("box-version", version)
|
||||
|
||||
info_file = @env.boxes.find(name, provider).directory.join("info.json")
|
||||
info_file = @env.boxes.find(name, provider, version).
|
||||
directory.join("info.json")
|
||||
if info_file.file?
|
||||
info = JSON.parse(info_file.read)
|
||||
info.each do |k, v|
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
require 'optparse'
|
||||
|
||||
module VagrantPlugins
|
||||
module CommandBox
|
||||
module Command
|
||||
class Outdated < Vagrant.plugin("2", :command)
|
||||
def execute
|
||||
options = {}
|
||||
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant box outdated [options]"
|
||||
o.separator ""
|
||||
o.separator "Checks if there is a new version available for the box"
|
||||
o.separator "that are you are using. If you pass in the --global flag,"
|
||||
o.separator "all boxes will be checked for updates."
|
||||
o.separator ""
|
||||
o.separator "Options:"
|
||||
o.separator ""
|
||||
|
||||
o.on("--global", "Check all boxes installed.") do |g|
|
||||
options[:global] = g
|
||||
end
|
||||
end
|
||||
|
||||
argv = parse_options(opts)
|
||||
return if !argv
|
||||
|
||||
# If we're checking the boxes globally, then do that.
|
||||
if options[:global]
|
||||
outdated_global
|
||||
return 0
|
||||
end
|
||||
|
||||
with_target_vms(argv) do |machine|
|
||||
@env.action_runner.run(Vagrant::Action.action_box_outdated, {
|
||||
box_outdated_force: true,
|
||||
box_outdated_refresh: true,
|
||||
box_outdated_success_ui: true,
|
||||
machine: machine,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def outdated_global
|
||||
boxes = {}
|
||||
@env.boxes.all.reverse.each do |name, version, provider|
|
||||
next if boxes[name]
|
||||
boxes[name] = @env.boxes.find(name, provider, version)
|
||||
end
|
||||
|
||||
boxes.values.each do |box|
|
||||
if !box.metadata_url
|
||||
@env.ui.output(I18n.t(
|
||||
"vagrant.box_outdated_no_metadata",
|
||||
name: box.name))
|
||||
next
|
||||
end
|
||||
|
||||
md = nil
|
||||
begin
|
||||
md = box.load_metadata
|
||||
rescue Vagrant::Errors::DownloaderError => e
|
||||
@env.ui.error(I18n.t(
|
||||
"vagrant.box_outdated_metadata_error",
|
||||
name: box.name,
|
||||
message: e.extra_data[:message]))
|
||||
next
|
||||
end
|
||||
|
||||
current = Gem::Version.new(box.version)
|
||||
latest = Gem::Version.new(md.versions.last)
|
||||
if latest <= current
|
||||
@env.ui.success(I18n.t(
|
||||
"vagrant.box_up_to_date",
|
||||
name: box.name,
|
||||
version: box.version))
|
||||
else
|
||||
@env.ui.warn(I18n.t(
|
||||
"vagrant.box_outdated",
|
||||
name: box.name,
|
||||
current: box.version,
|
||||
latest: latest.to_s,))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,38 +5,42 @@ module VagrantPlugins
|
|||
module Command
|
||||
class Remove < Vagrant.plugin("2", :command)
|
||||
def execute
|
||||
options = {}
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant box remove <name> <provider>"
|
||||
o.banner = "Usage: vagrant box remove <name>"
|
||||
o.separator ""
|
||||
|
||||
o.on("--provider VALUE", String,
|
||||
"The specific provider type for the box to remove.") do |p|
|
||||
options[:provider] = p
|
||||
end
|
||||
|
||||
o.on("--box-version VALUE", String,
|
||||
"The specific version of the box to remove.") do |v|
|
||||
options[:version] = v
|
||||
end
|
||||
end
|
||||
|
||||
# Parse the options
|
||||
argv = parse_options(opts)
|
||||
return if !argv
|
||||
raise Vagrant::Errors::CLIInvalidUsage, :help => opts.help.chomp if argv.length < 1
|
||||
if argv.empty? || argv.length > 2
|
||||
raise Vagrant::Errors::CLIInvalidUsage,
|
||||
help: opts.help.chomp
|
||||
end
|
||||
|
||||
if !argv[1]
|
||||
# Try to automatically determine the provider.
|
||||
providers = []
|
||||
@env.boxes.all.each do |name, provider|
|
||||
if name == argv[0]
|
||||
providers << provider
|
||||
end
|
||||
end
|
||||
|
||||
if providers.length > 1
|
||||
@env.ui.error(
|
||||
I18n.t("vagrant.commands.box.remove_must_specify_provider",
|
||||
name: argv[0],
|
||||
providers: providers.join(", ")))
|
||||
return 1
|
||||
end
|
||||
|
||||
argv[1] = providers[0] || ""
|
||||
if argv.length == 2
|
||||
# @deprecated
|
||||
@env.ui.warn("WARNING: The second argument to `vagrant box remove`")
|
||||
@env.ui.warn("is deprecated. Please use the --provider flag. This")
|
||||
@env.ui.warn("feature will stop working in the next version.")
|
||||
options[:provider] = argv[1]
|
||||
end
|
||||
|
||||
@env.action_runner.run(Vagrant::Action.action_box_remove, {
|
||||
:box_name => argv[0],
|
||||
:box_provider => argv[1]
|
||||
:box_provider => options[:provider],
|
||||
:box_version => options[:version],
|
||||
})
|
||||
|
||||
# Success, exit status 0
|
||||
|
|
|
@ -14,7 +14,7 @@ module VagrantPlugins
|
|||
# Parse the options
|
||||
argv = parse_options(opts)
|
||||
return if !argv
|
||||
raise Vagrant::Errors::CLIInvalidUsage, :help => opts.help.chomp if argv.length < 2
|
||||
raise Vagrant::Errors::CLIInvalidUsage, :help => opts.help.chomp if argv.length != 2
|
||||
|
||||
box_name = argv[0]
|
||||
box_provider = argv[1].to_sym
|
||||
|
|
|
@ -24,6 +24,11 @@ module VagrantPlugins
|
|||
List
|
||||
end
|
||||
|
||||
@subcommands.register(:outdated) do
|
||||
require_relative "outdated"
|
||||
Outdated
|
||||
end
|
||||
|
||||
@subcommands.register(:remove) do
|
||||
require File.expand_path("../remove", __FILE__)
|
||||
Remove
|
||||
|
@ -33,6 +38,11 @@ module VagrantPlugins
|
|||
require File.expand_path("../repackage", __FILE__)
|
||||
Repackage
|
||||
end
|
||||
|
||||
@subcommands.register(:update) do
|
||||
require_relative "update"
|
||||
Update
|
||||
end
|
||||
end
|
||||
|
||||
def execute
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
require 'optparse'
|
||||
|
||||
module VagrantPlugins
|
||||
module CommandBox
|
||||
module Command
|
||||
class Update < Vagrant.plugin("2", :command)
|
||||
def execute
|
||||
options = {}
|
||||
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant box update [options]"
|
||||
o.separator ""
|
||||
o.separator "Updates the box that is in use in the current Vagrant environment,"
|
||||
o.separator "if there any updates available. This does not destroy/recreate the"
|
||||
o.separator "machine, so you'll have to do that to see changes."
|
||||
o.separator ""
|
||||
o.separator "To update a specific box (not tied to a Vagrant environment), use the"
|
||||
o.separator "--box flag."
|
||||
o.separator ""
|
||||
o.separator "Options:"
|
||||
o.separator ""
|
||||
|
||||
o.on("--box VALUE", String, "Update a specific box") do |b|
|
||||
options[:box] = b
|
||||
end
|
||||
|
||||
o.on("--provider VALUE", String, "Update box with specific provider.") do |p|
|
||||
options[:provider] = p.to_sym
|
||||
end
|
||||
end
|
||||
|
||||
argv = parse_options(opts)
|
||||
return if !argv
|
||||
|
||||
if options[:box]
|
||||
update_specific(options[:box], options[:provider])
|
||||
else
|
||||
update_vms(argv)
|
||||
end
|
||||
|
||||
0
|
||||
end
|
||||
|
||||
def update_specific(name, provider)
|
||||
boxes = {}
|
||||
@env.boxes.all.each do |n, v, p|
|
||||
boxes[n] ||= {}
|
||||
boxes[n][p] ||= []
|
||||
boxes[n][p] << v
|
||||
end
|
||||
|
||||
if !boxes[name]
|
||||
raise Vagrant::Errors::BoxNotFound, name: name.to_s
|
||||
end
|
||||
|
||||
if !provider
|
||||
if boxes[name].length > 1
|
||||
raise Vagrant::Errors::BoxUpdateMultiProvider,
|
||||
name: name.to_s,
|
||||
providers: boxes[name].keys.map(&:to_s).sort.join(", ")
|
||||
end
|
||||
|
||||
provider = boxes[name].keys.first
|
||||
elsif !boxes[name][provider]
|
||||
raise Vagrant::Errors::BoxNotFoundWithProvider,
|
||||
name: name.to_s,
|
||||
provider: provider.to_s,
|
||||
providers: boxes[name].keys.map(&:to_s).sort.join(", ")
|
||||
end
|
||||
|
||||
to_update = [
|
||||
[name, provider, boxes[name][provider].last],
|
||||
]
|
||||
|
||||
to_update.each do |n, p, v|
|
||||
box = @env.boxes.find(n, p, v)
|
||||
box_update(box, "> #{v}", @env.ui)
|
||||
end
|
||||
end
|
||||
|
||||
def update_vms(argv)
|
||||
with_target_vms(argv) do |machine|
|
||||
if !machine.box
|
||||
machine.ui.output(I18n.t(
|
||||
"vagrant.errors.box_update_no_box",
|
||||
name: machine.config.vm.box))
|
||||
next
|
||||
end
|
||||
|
||||
box = machine.box
|
||||
version = machine.config.vm.box_version
|
||||
box_update(box, version, machine.ui)
|
||||
end
|
||||
end
|
||||
|
||||
def box_update(box, version, ui)
|
||||
ui.output(I18n.t("vagrant.box_update_checking", name: box.name))
|
||||
ui.detail("Version constraints: #{version}")
|
||||
ui.detail("Provider: #{box.provider}")
|
||||
|
||||
update = box.has_update?(version)
|
||||
if !update
|
||||
ui.success(I18n.t(
|
||||
"vagrant.box_up_to_date_single",
|
||||
name: box.name, version: box.version))
|
||||
return
|
||||
end
|
||||
|
||||
ui.output(I18n.t(
|
||||
"vagrant.box_updating",
|
||||
name: update[0].name,
|
||||
provider: update[2].name,
|
||||
old: box.version,
|
||||
new: update[1].version))
|
||||
@env.action_runner.run(Vagrant::Action.action_box_add, {
|
||||
box_url: box.metadata_url,
|
||||
box_provider: update[2].name,
|
||||
box_version: update[1].version,
|
||||
ui: ui,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -17,7 +17,9 @@ module VagrantPlugins
|
|||
attr_accessor :base_mac
|
||||
attr_accessor :boot_timeout
|
||||
attr_accessor :box
|
||||
attr_accessor :box_check_update
|
||||
attr_accessor :box_url
|
||||
attr_accessor :box_version
|
||||
attr_accessor :box_download_ca_cert
|
||||
attr_accessor :box_download_checksum
|
||||
attr_accessor :box_download_checksum_type
|
||||
|
@ -32,12 +34,14 @@ module VagrantPlugins
|
|||
def initialize
|
||||
@base_mac = UNSET_VALUE
|
||||
@boot_timeout = UNSET_VALUE
|
||||
@box_check_update = UNSET_VALUE
|
||||
@box_download_ca_cert = UNSET_VALUE
|
||||
@box_download_checksum = UNSET_VALUE
|
||||
@box_download_checksum_type = UNSET_VALUE
|
||||
@box_download_client_cert = UNSET_VALUE
|
||||
@box_download_insecure = UNSET_VALUE
|
||||
@box_url = UNSET_VALUE
|
||||
@box_version = UNSET_VALUE
|
||||
@graceful_halt_timeout = UNSET_VALUE
|
||||
@guest = UNSET_VALUE
|
||||
@hostname = UNSET_VALUE
|
||||
|
@ -303,12 +307,14 @@ module VagrantPlugins
|
|||
# Defaults
|
||||
@base_mac = nil if @base_mac == UNSET_VALUE
|
||||
@boot_timeout = 300 if @boot_timeout == UNSET_VALUE
|
||||
@box_check_update = true if @box_check_update == UNSET_VALUE
|
||||
@box_download_ca_cert = nil if @box_download_ca_cert == UNSET_VALUE
|
||||
@box_download_checksum = nil if @box_download_checksum == UNSET_VALUE
|
||||
@box_download_checksum_type = nil if @box_download_checksum_type == UNSET_VALUE
|
||||
@box_download_client_cert = nil if @box_download_client_cert == UNSET_VALUE
|
||||
@box_download_insecure = false if @box_download_insecure == UNSET_VALUE
|
||||
@box_url = nil if @box_url == UNSET_VALUE
|
||||
@box_version = ">= 0" if @box_version == UNSET_VALUE
|
||||
@graceful_halt_timeout = 60 if @graceful_halt_timeout == UNSET_VALUE
|
||||
@guest = nil if @guest == UNSET_VALUE
|
||||
@hostname = nil if @hostname == UNSET_VALUE
|
||||
|
@ -326,9 +332,7 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
# Make sure the box URL is an array if it is set
|
||||
if @box_url && !@box_url.is_a?(Array)
|
||||
@box_url = [@box_url]
|
||||
end
|
||||
@box_url = Array(@box_url) if @box_url
|
||||
|
||||
# Set the guest properly
|
||||
@guest = @guest.to_sym if @guest
|
||||
|
@ -449,11 +453,20 @@ module VagrantPlugins
|
|||
def validate(machine)
|
||||
errors = _detected_errors
|
||||
errors << I18n.t("vagrant.config.vm.box_missing") if !box
|
||||
errors << I18n.t("vagrant.config.vm.box_not_found", :name => box) if \
|
||||
box && !box_url && !machine.box
|
||||
errors << I18n.t("vagrant.config.vm.hostname_invalid_characters") if \
|
||||
@hostname && @hostname !~ /^[a-z0-9][-.a-z0-9]+$/i
|
||||
|
||||
if @box_version
|
||||
@box_version.split(",").each do |v|
|
||||
begin
|
||||
Gem::Requirement.new(v.strip)
|
||||
rescue Gem::Requirement::BadRequirementError
|
||||
errors << I18n.t(
|
||||
"vagrant.config.vm.bad_version", version: v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if box_download_ca_cert
|
||||
path = Pathname.new(box_download_ca_cert).
|
||||
expand_path(machine.env.root_path)
|
||||
|
|
|
@ -246,6 +246,7 @@ module VagrantPlugins
|
|||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use CheckVirtualbox
|
||||
b.use ConfigValidate
|
||||
b.use BoxCheckOutdated
|
||||
b.use Call, IsRunning do |env, b2|
|
||||
# If the VM is running, then our work here is done, exit
|
||||
if env[:result]
|
||||
|
@ -302,7 +303,7 @@ module VagrantPlugins
|
|||
# works fine.
|
||||
b.use Call, Created do |env, b2|
|
||||
if !env[:result]
|
||||
b2.use HandleBoxUrl
|
||||
b2.use HandleBox
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -4,6 +4,59 @@ en:
|
|||
Machine booted and ready!
|
||||
boot_waiting: |-
|
||||
Waiting for machine to boot. This may take a few minutes...
|
||||
box_auto_adding: |-
|
||||
Box '%{name}' could not be found. Attempting to find and install...
|
||||
box_add_choose_provider: |-
|
||||
This box can work with multiple providers! The providers that it
|
||||
can work with are listed below. Please review the list and choose
|
||||
the provider you will be working with.
|
||||
|
||||
%{options}
|
||||
|
||||
Enter your choice:
|
||||
box_add_choose_provider_again: |-
|
||||
Invalid choice. Try again:
|
||||
box_add_with_version: |-
|
||||
Adding box '%{name}' (v%{version}) for provider: %{providers}
|
||||
box_added: |-
|
||||
Successfully added box '%{name}' (v%{version}) for '%{provider}'!
|
||||
box_downloading: |-
|
||||
Downloading: %{url}
|
||||
box_download_error: |-
|
||||
Error downloading: %{message}
|
||||
box_expanding_url: |-
|
||||
URL: %{url}
|
||||
box_loading_metadata: |-
|
||||
Loading metadata for box '%{name}'
|
||||
box_outdated: |-
|
||||
* '%{name}' is outdated! Current: %{current}. Latest: %{latest}
|
||||
box_outdated_checking_with_refresh: |-
|
||||
Checking if box '%{name}' is up to date...
|
||||
box_outdated_local: |-
|
||||
A newer version of the box '%{name}' is available and already
|
||||
installed, but your Vagrant machine is running against
|
||||
version '%{old}'. To update to version '%{new}',
|
||||
destroy and recreate your machine.
|
||||
box_outdated_metadata_error_single: |-
|
||||
Error loading box metadata while attempting to check for
|
||||
updates: %{message}
|
||||
box_outdated_single: |-
|
||||
A newer version of the box '%{name}' is available! You currently
|
||||
have version '%{current}'. The latest is version '%{latest}'. Run
|
||||
`vagrant box update` to update.
|
||||
box_outdated_metadata_error: |-
|
||||
* '%{name}': Error loading metadata: %{message}
|
||||
box_outdated_no_metadata: |-
|
||||
* '%{name}' wasn't added from a catalog, no version information
|
||||
box_updating: |-
|
||||
Updating '%{name}' with provider '%{provider}' from version
|
||||
'%{old}' to '%{new}'...
|
||||
box_update_checking: |-
|
||||
Checking for updates to '%{name}'
|
||||
box_up_to_date: |-
|
||||
* '%{name}' (v%{version}) is up to date
|
||||
box_up_to_date_single: |-
|
||||
Box '%{name}' (v%{version}) is running the latest version.
|
||||
cfengine_bootstrapping: |-
|
||||
Bootstrapping CFEngine with policy server: %{policy_server}...
|
||||
cfengine_bootstrapping_policy_hub: |-
|
||||
|
@ -211,6 +264,32 @@ en:
|
|||
Any errors that occurred are shown below.
|
||||
|
||||
%{message}
|
||||
box_add_no_matching_provider: |-
|
||||
The box you're attempting to add doesn't support the provider
|
||||
you requested. Please find an alternate box or use an alternate
|
||||
provider. Double-check your requested provider to verify you didn't
|
||||
simply misspell it.
|
||||
|
||||
Name: %{name}
|
||||
Address: %{url}
|
||||
Requested provider: %{requested}
|
||||
box_add_no_matching_version: |-
|
||||
The box you're attempting to add has no available version that
|
||||
matches the constraints you requested. Please double-check your
|
||||
settings.
|
||||
|
||||
Box: %{name}
|
||||
Address: %{url}
|
||||
Constraints: %{constraints}
|
||||
Available versions: %{versions}
|
||||
box_add_short_not_found: |-
|
||||
The box '%{name}' could not be found or
|
||||
could not be accessed in the remote catalog. Please
|
||||
double-check the name. The expanded URL and error message
|
||||
are shown below.
|
||||
|
||||
URL: %{url}
|
||||
Error: %{error}
|
||||
boot_bad_state: |-
|
||||
The guest machine entered an invalid state while waiting for it
|
||||
to boot. Valid states are '%{valid}'. The machine is in the
|
||||
|
@ -236,6 +315,33 @@ en:
|
|||
|
||||
If the box appears to be booting properly, you may want to increase
|
||||
the timeout ("config.vm.boot_timeout") value.
|
||||
box_add_exists: |-
|
||||
The box you're attempting to add already exists. Remove it before
|
||||
adding it again or add it with the `--force` flag.
|
||||
|
||||
Name: %{name}
|
||||
Provider: %{provider}
|
||||
Version: %{version}
|
||||
box_add_metadata_multi_url: |-
|
||||
Multiple URLs for a box can't be specified when adding
|
||||
versioned boxes. Please specify a single URL to the box
|
||||
metadata (JSON) information. The full list of URLs you
|
||||
specified is shown below:
|
||||
|
||||
%{urls}
|
||||
box_add_name_mismatch: |-
|
||||
The box you're adding has a name different from the name you
|
||||
requested. For boxes with metadata, you cannot override the name.
|
||||
If you're adding a box using `vagrant box add`, don't specify
|
||||
the `--name` parameter. If the box is being added via a Vagrantfile,
|
||||
change the `config.vm.box` value to match the name below.
|
||||
|
||||
Requested name: %{requested_name}
|
||||
Actual name: %{actual_name}
|
||||
box_add_name_required: |-
|
||||
A name is required when adding a box file directly. Please pass
|
||||
the `--name` parameter to `vagrant box add`. See
|
||||
`vagrant box add -h` for more help.
|
||||
box_checksum_invalid_type: |-
|
||||
The specified checksum type is not supported by Vagrant: %{type}.
|
||||
Vagrant supports the following checksum types:
|
||||
|
@ -266,16 +372,75 @@ en:
|
|||
box file format can be found at the URL below:
|
||||
|
||||
http://docs.vagrantup.com/v2/boxes/format.html
|
||||
box_not_found: Box '%{name}' with '%{provider}' provider could not be found.
|
||||
box_metadata_malformed: |-
|
||||
The metadata for the box was malformed. The exact error
|
||||
is shown below. Please contact the maintainer of the box so
|
||||
that this issue can be fixed.
|
||||
|
||||
%{error}
|
||||
box_not_found: |-
|
||||
The box '%{name}' does not exist. Please double check and
|
||||
try again. You can see the boxes that are installed with
|
||||
`vagrant box list`.
|
||||
box_not_found_with_provider: |-
|
||||
The box '%{name}' isn't installed for the provider '%{provider}'.
|
||||
Please double-check and try again. The installed providers for
|
||||
the box are shown below:
|
||||
|
||||
%{providers}
|
||||
box_outdated_no_box: |-
|
||||
The box '%{name}' isn't downloaded or added yet, so we can't
|
||||
check if it is outdated. Run a `vagrant up` or add the box
|
||||
with `vagrant box add` to download an appropriate version.
|
||||
box_provider_doesnt_match: |-
|
||||
The box you attempted to add doesn't match the provider you specified.
|
||||
|
||||
Provider expected: %{expected}
|
||||
Provider of box: %{actual}
|
||||
box_upgrade_required: |-
|
||||
The box '%{name}' is still stored on disk in the Vagrant 1.0.x
|
||||
format. This box must be upgraded in order to work properly with
|
||||
this version of Vagrant.
|
||||
box_remove_multi_provider: |-
|
||||
You requested to remove the box '%{name}'. This box has
|
||||
multiple providers. You must explicitly select a single
|
||||
provider to remove with `--provider`.
|
||||
|
||||
Available providers: %{providers}
|
||||
box_remove_multi_version: |-
|
||||
You requested to remove the box '%{name}' with provider
|
||||
'%{provider}'. This box has multiple versions. You must
|
||||
explicitly specify which version you want to remove with
|
||||
the `--box-version` flag.
|
||||
|
||||
Versions: %{versions}
|
||||
box_remove_not_found: |-
|
||||
The box you requested to be removed could not be found. No
|
||||
boxes named '%{name}' could be found.
|
||||
box_remove_provider_not_found: |-
|
||||
You requested to remove the box '%{name}' with provider
|
||||
'%{provider}'. The box '%{name}' exists but not with
|
||||
the provider specified. Please double-check and try again.
|
||||
|
||||
The providers for this are: %{providers}
|
||||
box_server_not_set: |-
|
||||
A URL to a Vagrant Cloud server is not set, so boxes cannot
|
||||
be added with a shorthand ("mitchellh/precise64") format.
|
||||
You may also be seeing this error if you meant to type in
|
||||
a path to a box file which doesn't exist locally on your
|
||||
system.
|
||||
|
||||
To set a URL to a Vagrant Cloud server, set the
|
||||
`VAGRANT_SERVER_URL` environmental variable. Or, if you
|
||||
meant to use a file path, make sure the path to the file
|
||||
is valid.
|
||||
box_update_multi_provider: |-
|
||||
You requested to update the box '%{name}'. This box has
|
||||
multiple providers. You must explicitly select a single
|
||||
provider to remove with `--provider`.
|
||||
|
||||
Available providers: %{providers}
|
||||
box_update_no_metadata: |-
|
||||
The box '%{name}' is not a versioned box. The box was added
|
||||
directly instead of from a box catalog. Vagrant can only
|
||||
check the versions of boxes that were added from a catalog
|
||||
such as from the public Vagrant Server.
|
||||
bundler_disabled: |-
|
||||
Vagrant's built-in bundler management mechanism is disabled because
|
||||
Vagrant is running in an external bundler environment. In these
|
||||
|
@ -432,11 +597,20 @@ en:
|
|||
as mounting shared folders and configuring networks. Please add
|
||||
the ability to detect this guest operating system to Vagrant
|
||||
by creating a plugin or reporting a bug.
|
||||
home_dir_later_version: |-
|
||||
It appears that a newer version of Vagrant was run on this machine
|
||||
at some point. The current version of Vagrant is unable to read
|
||||
the configuration structure of this newer version. Please upgrade to
|
||||
the latest version of Vagrant.
|
||||
home_dir_not_accessible: |-
|
||||
The home directory you specified is not accessible. The home
|
||||
directory that Vagrant uses must be both readable and writable.
|
||||
|
||||
You specified: %{home_path}
|
||||
home_dir_unknown_version: |-
|
||||
The Vagrant app data directory (%{path}) is in a
|
||||
structure Vagrant doesn't understand. This is a rare exception.
|
||||
Please report an issue or ask the mailing list for help.
|
||||
host_explicit_not_detected: |-
|
||||
The host implementation explicitly specified in your Vagrantfile
|
||||
("%{value}") could not be found. Please verify that the plugin is
|
||||
|
@ -859,6 +1033,8 @@ en:
|
|||
ssh:
|
||||
private_key_missing: "`private_key_path` file must exist: %{path}"
|
||||
vm:
|
||||
bad_version: |-
|
||||
Invalid box version constraints: %{version}
|
||||
box_download_ca_cert_not_found: |-
|
||||
"box_download_ca_cert" file not found: %{path}
|
||||
box_download_checksum_blank: |-
|
||||
|
@ -866,7 +1042,6 @@ en:
|
|||
box_download_checksum_notblank: |-
|
||||
Checksum specified but must also specify "box_download_checksum_type"
|
||||
box_missing: "A box must be specified."
|
||||
box_not_found: "The box '%{name}' could not be found."
|
||||
hostname_invalid_characters: |-
|
||||
The hostname set for the VM should only contain letters, numbers,
|
||||
hyphens or dots. It cannot start with a hyphen or dot.
|
||||
|
@ -916,17 +1091,6 @@ en:
|
|||
vm_not_created: "VM not created. Moving on..."
|
||||
vm_not_running: "VM is not currently running. Please, first bring it up with `vagrant up` then run this command."
|
||||
box:
|
||||
remove_must_specify_provider: |-
|
||||
Multiple providers were found for the box '%{name}'. Please specify
|
||||
the specific provider for the box you want to remove. The list of
|
||||
providers backing this box is:
|
||||
|
||||
'%{providers}'
|
||||
|
||||
To remove the box for a specific provider, run the following command,
|
||||
filling in PROVIDER with one of the providers above:
|
||||
|
||||
vagrant box remove '%{name}' PROVIDER
|
||||
no_installed_boxes: "There are no installed boxes! Use `vagrant box add` to add some."
|
||||
removing: |-
|
||||
Removing box '%{name}' with provider '%{provider}'...
|
||||
|
@ -1217,23 +1381,15 @@ en:
|
|||
output from attempting to unpackage (if any):
|
||||
|
||||
%{output}
|
||||
already_exists: |-
|
||||
The box you're attempting to add already exists:
|
||||
|
||||
Name: %{name}
|
||||
Provider: %{formats}
|
||||
add:
|
||||
adding: |-
|
||||
Extracting box...
|
||||
added: |-
|
||||
Successfully added box '%{name}' with provider '%{provider}'!
|
||||
checksumming: |-
|
||||
Calculating and comparing box checksum...
|
||||
destroy:
|
||||
destroying: "Deleting box '%{name}'..."
|
||||
download:
|
||||
cleaning: "Cleaning up downloaded box..."
|
||||
downloading: "Downloading box from URL: %{url}"
|
||||
download_failed: |-
|
||||
Download failed. Will try another box URL if there is one.
|
||||
interrupted: "Box download was interrupted. Exiting."
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
require File.expand_path("../../../../../base", __FILE__)
|
||||
|
||||
require Vagrant.source_root.join("plugins/commands/box/command/add")
|
||||
|
||||
describe VagrantPlugins::CommandBox::Command::Add do
|
||||
include_context "unit"
|
||||
|
||||
let(:argv) { [] }
|
||||
let(:iso_env) do
|
||||
# We have to create a Vagrantfile so there is a root path
|
||||
env = isolated_environment
|
||||
env.vagrantfile("")
|
||||
env.create_vagrant_env
|
||||
end
|
||||
|
||||
subject { described_class.new(argv, iso_env) }
|
||||
|
||||
let(:action_runner) { double("action_runner") }
|
||||
|
||||
before do
|
||||
iso_env.stub(action_runner: action_runner)
|
||||
end
|
||||
|
||||
context "with no arguments" do
|
||||
it "shows help" do
|
||||
expect { subject.execute }.
|
||||
to raise_error(Vagrant::Errors::CLIInvalidUsage)
|
||||
end
|
||||
end
|
||||
|
||||
context "with one argument" do
|
||||
let(:argv) { ["foo"] }
|
||||
|
||||
it "executes the runner with the proper actions" do
|
||||
action_runner.should_receive(:run).with do |action, **opts|
|
||||
expect(opts[:box_name]).to be_nil
|
||||
expect(opts[:box_url]).to eq("foo")
|
||||
true
|
||||
end
|
||||
|
||||
subject.execute
|
||||
end
|
||||
end
|
||||
|
||||
context "with two arguments" do
|
||||
let(:argv) { ["foo", "bar"] }
|
||||
|
||||
it "executes the runner with the proper actions" do
|
||||
action_runner.should_receive(:run).with do |action, **opts|
|
||||
expect(opts[:box_name]).to eq("foo")
|
||||
expect(opts[:box_url]).to eq("bar")
|
||||
true
|
||||
end
|
||||
|
||||
subject.execute
|
||||
end
|
||||
end
|
||||
|
||||
context "with more than two arguments" do
|
||||
let(:argv) { ["one", "two", "three"] }
|
||||
|
||||
it "shows help" do
|
||||
expect { subject.execute }.
|
||||
to raise_error(Vagrant::Errors::CLIInvalidUsage)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,66 @@
|
|||
require File.expand_path("../../../../../base", __FILE__)
|
||||
|
||||
require Vagrant.source_root.join("plugins/commands/box/command/remove")
|
||||
|
||||
describe VagrantPlugins::CommandBox::Command::Remove do
|
||||
include_context "unit"
|
||||
|
||||
let(:argv) { [] }
|
||||
let(:iso_env) do
|
||||
# We have to create a Vagrantfile so there is a root path
|
||||
env = isolated_environment
|
||||
env.vagrantfile("")
|
||||
env.create_vagrant_env
|
||||
end
|
||||
|
||||
subject { described_class.new(argv, iso_env) }
|
||||
|
||||
let(:action_runner) { double("action_runner") }
|
||||
|
||||
before do
|
||||
iso_env.stub(action_runner: action_runner)
|
||||
end
|
||||
|
||||
context "with no arguments" do
|
||||
it "shows help" do
|
||||
expect { subject.execute }.
|
||||
to raise_error(Vagrant::Errors::CLIInvalidUsage)
|
||||
end
|
||||
end
|
||||
|
||||
context "with one argument" do
|
||||
let(:argv) { ["foo"] }
|
||||
|
||||
it "invokes the action runner" do
|
||||
action_runner.should_receive(:run).with do |action, opts|
|
||||
expect(opts[:box_name]).to eq("foo")
|
||||
true
|
||||
end
|
||||
|
||||
subject.execute
|
||||
end
|
||||
end
|
||||
|
||||
context "with two arguments" do
|
||||
let(:argv) { ["foo", "bar"] }
|
||||
|
||||
it "uses the 2nd arg as a provider" do
|
||||
action_runner.should_receive(:run).with do |action, opts|
|
||||
expect(opts[:box_name]).to eq("foo")
|
||||
expect(opts[:box_provider]).to eq("bar")
|
||||
true
|
||||
end
|
||||
|
||||
subject.execute
|
||||
end
|
||||
end
|
||||
|
||||
context "with more than two arguments" do
|
||||
let(:argv) { ["one", "two", "three"] }
|
||||
|
||||
it "shows help" do
|
||||
expect { subject.execute }.
|
||||
to raise_error(Vagrant::Errors::CLIInvalidUsage)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,54 @@
|
|||
require File.expand_path("../../../../../base", __FILE__)
|
||||
|
||||
require Vagrant.source_root.join("plugins/commands/box/command/repackage")
|
||||
|
||||
describe VagrantPlugins::CommandBox::Command::Repackage do
|
||||
include_context "unit"
|
||||
|
||||
let(:argv) { [] }
|
||||
let(:iso_env) do
|
||||
# We have to create a Vagrantfile so there is a root path
|
||||
env = isolated_environment
|
||||
env.vagrantfile("")
|
||||
env.create_vagrant_env
|
||||
end
|
||||
|
||||
subject { described_class.new(argv, iso_env) }
|
||||
|
||||
let(:action_runner) { double("action_runner") }
|
||||
|
||||
before do
|
||||
iso_env.stub(action_runner: action_runner)
|
||||
end
|
||||
|
||||
context "with no arguments" do
|
||||
it "shows help" do
|
||||
expect { subject.execute }.
|
||||
to raise_error(Vagrant::Errors::CLIInvalidUsage)
|
||||
end
|
||||
end
|
||||
|
||||
context "with one argument" do
|
||||
let(:argv) { ["one"] }
|
||||
|
||||
it "shows help" do
|
||||
expect { subject.execute }.
|
||||
to raise_error(Vagrant::Errors::CLIInvalidUsage)
|
||||
end
|
||||
end
|
||||
|
||||
context "with two arguments" do
|
||||
it "repackages the box with the given provider" do
|
||||
pending
|
||||
end
|
||||
end
|
||||
|
||||
context "with more than two arguments" do
|
||||
let(:argv) { ["one", "two", "three"] }
|
||||
|
||||
it "shows help" do
|
||||
expect { subject.execute }.
|
||||
to raise_error(Vagrant::Errors::CLIInvalidUsage)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,217 @@
|
|||
require "pathname"
|
||||
require "tmpdir"
|
||||
|
||||
require File.expand_path("../../../../../base", __FILE__)
|
||||
|
||||
require Vagrant.source_root.join("plugins/commands/box/command/update")
|
||||
|
||||
describe VagrantPlugins::CommandBox::Command::Update do
|
||||
include_context "unit"
|
||||
|
||||
let(:argv) { [] }
|
||||
let(:iso_env) do
|
||||
# We have to create a Vagrantfile so there is a root path
|
||||
test_iso_env.vagrantfile("")
|
||||
test_iso_env.create_vagrant_env
|
||||
end
|
||||
let(:test_iso_env) { isolated_environment }
|
||||
|
||||
let(:action_runner) { double("action_runner") }
|
||||
let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) }
|
||||
|
||||
subject { described_class.new(argv, iso_env) }
|
||||
|
||||
before do
|
||||
iso_env.stub(action_runner: action_runner)
|
||||
end
|
||||
|
||||
describe "execute" do
|
||||
context "updating specific box" do
|
||||
let(:argv) { ["--box", "foo"] }
|
||||
|
||||
let(:metadata_url) { Pathname.new(Dir.mktmpdir).join("metadata.json") }
|
||||
|
||||
before do
|
||||
metadata_url.open("w") do |f|
|
||||
f.write("")
|
||||
end
|
||||
|
||||
test_iso_env.box3(
|
||||
"foo", "1.0", :virtualbox, metadata_url: metadata_url.to_s)
|
||||
end
|
||||
|
||||
it "doesn't update if they're up to date" do
|
||||
action_runner.should_receive(:run).never
|
||||
|
||||
subject.execute
|
||||
end
|
||||
|
||||
it "does update if there is an update" do
|
||||
metadata_url.open("w") do |f|
|
||||
f.write(<<-RAW)
|
||||
{
|
||||
"name": "foo",
|
||||
"versions": [
|
||||
{
|
||||
"version": "1.0"
|
||||
},
|
||||
{
|
||||
"version": "1.1",
|
||||
"providers": [
|
||||
{
|
||||
"name": "virtualbox",
|
||||
"url": "bar"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
RAW
|
||||
end
|
||||
|
||||
action_runner.should_receive(:run).with do |action, opts|
|
||||
expect(opts[:box_url]).to eq(metadata_url.to_s)
|
||||
expect(opts[:box_provider]).to eq("virtualbox")
|
||||
expect(opts[:box_version]).to eq("1.1")
|
||||
true
|
||||
end
|
||||
|
||||
subject.execute
|
||||
end
|
||||
|
||||
it "raises an error if there are multiple providers" do
|
||||
test_iso_env.box3("foo", "1.0", :vmware)
|
||||
|
||||
action_runner.should_receive(:run).never
|
||||
|
||||
expect { subject.execute }.
|
||||
to raise_error(Vagrant::Errors::BoxUpdateMultiProvider)
|
||||
end
|
||||
|
||||
context "with multiple providers and specifying the provider" do
|
||||
let(:argv) { ["--box", "foo", "--provider", "vmware"] }
|
||||
|
||||
it "updates the proper box" do
|
||||
metadata_url.open("w") do |f|
|
||||
f.write(<<-RAW)
|
||||
{
|
||||
"name": "foo",
|
||||
"versions": [
|
||||
{
|
||||
"version": "1.0"
|
||||
},
|
||||
{
|
||||
"version": "1.1",
|
||||
"providers": [
|
||||
{
|
||||
"name": "vmware",
|
||||
"url": "bar"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
RAW
|
||||
end
|
||||
|
||||
test_iso_env.box3("foo", "1.0", :vmware)
|
||||
|
||||
action_runner.should_receive(:run).with do |action, opts|
|
||||
expect(opts[:box_url]).to eq(metadata_url.to_s)
|
||||
expect(opts[:box_provider]).to eq("vmware")
|
||||
expect(opts[:box_version]).to eq("1.1")
|
||||
true
|
||||
end
|
||||
|
||||
subject.execute
|
||||
end
|
||||
|
||||
it "raises an error if that provider doesn't exist" do
|
||||
action_runner.should_receive(:run).never
|
||||
|
||||
expect { subject.execute }.
|
||||
to raise_error(Vagrant::Errors::BoxNotFoundWithProvider)
|
||||
end
|
||||
end
|
||||
|
||||
context "with a box that doesn't exist" do
|
||||
let(:argv) { ["--box", "nope"] }
|
||||
|
||||
it "raises an exception" do
|
||||
action_runner.should_receive(:run).never
|
||||
|
||||
expect { subject.execute }.
|
||||
to raise_error(Vagrant::Errors::BoxNotFound)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "updating environment machines" do
|
||||
before do
|
||||
subject.stub(:with_target_vms) { |&block| block.call machine }
|
||||
end
|
||||
|
||||
let(:box) do
|
||||
box_dir = test_iso_env.box3("foo", "1.0", :virtualbox)
|
||||
box = Vagrant::Box.new(
|
||||
"foo", :virtualbox, "1.0", box_dir, metadata_url: "foo")
|
||||
box.stub(has_update?: nil)
|
||||
box
|
||||
end
|
||||
|
||||
it "ignores machines without boxes" do
|
||||
action_runner.should_receive(:run).never
|
||||
|
||||
subject.execute
|
||||
end
|
||||
|
||||
it "doesn't update boxes if they're up-to-date" do
|
||||
machine.stub(box: box)
|
||||
box.should_receive(:has_update?).
|
||||
with(machine.config.vm.box_version).
|
||||
and_return(nil)
|
||||
|
||||
action_runner.should_receive(:run).never
|
||||
|
||||
subject.execute
|
||||
end
|
||||
|
||||
it "updates boxes if they have an update" do
|
||||
md = Vagrant::BoxMetadata.new(StringIO.new(<<-RAW))
|
||||
{
|
||||
"name": "foo",
|
||||
"versions": [
|
||||
{
|
||||
"version": "1.0"
|
||||
},
|
||||
{
|
||||
"version": "1.1",
|
||||
"providers": [
|
||||
{
|
||||
"name": "virtualbox",
|
||||
"url": "bar"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
RAW
|
||||
|
||||
machine.stub(box: box)
|
||||
box.should_receive(:has_update?).
|
||||
with(machine.config.vm.box_version).
|
||||
and_return([md, md.version("1.1"), md.version("1.1").provider("virtualbox")])
|
||||
|
||||
action_runner.should_receive(:run).with do |action, opts|
|
||||
expect(opts[:box_url]).to eq(box.metadata_url)
|
||||
expect(opts[:box_provider]).to eq("virtualbox")
|
||||
expect(opts[:box_version]).to eq("1.1")
|
||||
expect(opts[:ui]).to equal(machine.ui)
|
||||
true
|
||||
end
|
||||
|
||||
subject.execute
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,6 +5,29 @@ require Vagrant.source_root.join("plugins/kernel_v2/config/vm")
|
|||
describe VagrantPlugins::Kernel_V2::VMConfig do
|
||||
subject { described_class.new }
|
||||
|
||||
let(:machine) { double("machine") }
|
||||
|
||||
def assert_valid
|
||||
errors = subject.validate(machine)
|
||||
if !errors.values.all? { |v| v.empty? }
|
||||
raise "Errors: #{errors.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
env = double("env")
|
||||
env.stub(root_path: nil)
|
||||
machine.stub(env: env)
|
||||
machine.stub(provider_config: nil)
|
||||
|
||||
subject.box = "foo"
|
||||
end
|
||||
|
||||
it "is valid with test defaults" do
|
||||
subject.finalize!
|
||||
assert_valid
|
||||
end
|
||||
|
||||
describe "#base_mac" do
|
||||
it "defaults properly" do
|
||||
subject.finalize!
|
||||
|
@ -12,11 +35,58 @@ describe VagrantPlugins::Kernel_V2::VMConfig do
|
|||
end
|
||||
end
|
||||
|
||||
describe "#box_url" do
|
||||
it "defaults properly" do
|
||||
context "#box_check_update" do
|
||||
it "defaults to true" do
|
||||
subject.finalize!
|
||||
|
||||
expect(subject.box_check_update).to be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe "#box_url" do
|
||||
it "defaults to nil" do
|
||||
subject.finalize!
|
||||
|
||||
expect(subject.box_url).to be_nil
|
||||
end
|
||||
|
||||
it "turns into an array" do
|
||||
subject.box_url = "foo"
|
||||
subject.finalize!
|
||||
|
||||
expect(subject.box_url).to eq(
|
||||
["foo"])
|
||||
end
|
||||
|
||||
it "keeps in array" do
|
||||
subject.box_url = ["foo", "bar"]
|
||||
subject.finalize!
|
||||
|
||||
expect(subject.box_url).to eq(
|
||||
["foo", "bar"])
|
||||
end
|
||||
end
|
||||
|
||||
context "#box_version" do
|
||||
it "defaults to >= 0" do
|
||||
subject.finalize!
|
||||
|
||||
expect(subject.box_version).to eq(">= 0")
|
||||
end
|
||||
|
||||
it "errors if invalid version" do
|
||||
subject.box_version = "nope"
|
||||
subject.finalize!
|
||||
|
||||
expect { assert_valid }.to raise_error(RuntimeError)
|
||||
end
|
||||
|
||||
it "can have complex constraints" do
|
||||
subject.box_version = ">= 0, ~> 1.0"
|
||||
subject.finalize!
|
||||
|
||||
assert_valid
|
||||
end
|
||||
end
|
||||
|
||||
describe "#network(s)" do
|
||||
|
|
|
@ -89,6 +89,44 @@ module Unit
|
|||
box_dir
|
||||
end
|
||||
|
||||
# Creates a fake box to exist in this environment according
|
||||
# to the "gen-3" box format.
|
||||
#
|
||||
# @param [String] name
|
||||
# @param [String] version
|
||||
# @param [String] provider
|
||||
# @return [Pathname]
|
||||
def box3(name, version, provider, **opts)
|
||||
# Create the directory for the box
|
||||
box_dir = boxes_dir.join(name, version, provider.to_s)
|
||||
box_dir.mkpath
|
||||
|
||||
# Create the metadata.json for it
|
||||
box_metadata_file = box_dir.join("metadata.json")
|
||||
box_metadata_file.open("w") do |f|
|
||||
f.write(JSON.generate({
|
||||
:provider => provider.to_s
|
||||
}))
|
||||
end
|
||||
|
||||
# Create a Vagrantfile
|
||||
if opts[:vagrantfile]
|
||||
box_vagrantfile = box_dir.join("Vagrantfile")
|
||||
box_vagrantfile.open("w") do |f|
|
||||
f.write(opts[:vagrantfile])
|
||||
end
|
||||
end
|
||||
|
||||
# Create the metadata URL
|
||||
if opts[:metadata_url]
|
||||
boxes_dir.join(name, "metadata_url").open("w") do |f|
|
||||
f.write(opts[:metadata_url])
|
||||
end
|
||||
end
|
||||
|
||||
box_dir
|
||||
end
|
||||
|
||||
# This creates a "box" file that is a valid V1 box.
|
||||
#
|
||||
# @return [Pathname] Path to the newly created box.
|
||||
|
|
|
@ -0,0 +1,826 @@
|
|||
require "digest/sha1"
|
||||
require "pathname"
|
||||
require "tempfile"
|
||||
require "tmpdir"
|
||||
require "webrick"
|
||||
|
||||
require File.expand_path("../../../../base", __FILE__)
|
||||
|
||||
require "vagrant/util/file_checksum"
|
||||
|
||||
describe Vagrant::Action::Builtin::BoxAdd do
|
||||
include_context "unit"
|
||||
|
||||
let(:app) { lambda { |env| } }
|
||||
let(:env) { {
|
||||
box_collection: box_collection,
|
||||
tmp_path: Pathname.new(Dir.mktmpdir),
|
||||
ui: Vagrant::UI::Silent.new,
|
||||
} }
|
||||
|
||||
subject { described_class.new(app, env) }
|
||||
|
||||
let(:box_collection) { double("box_collection") }
|
||||
let(:iso_env) { isolated_environment }
|
||||
|
||||
let(:box) do
|
||||
box_dir = iso_env.box3("foo", "1.0", :virtualbox)
|
||||
Vagrant::Box.new("foo", :virtualbox, "1.0", box_dir)
|
||||
end
|
||||
|
||||
# Helper to quickly SHA1 checksum a path
|
||||
def checksum(path)
|
||||
FileChecksum.new(path, Digest::SHA1).checksum
|
||||
end
|
||||
|
||||
def with_web_server(path)
|
||||
tf = Tempfile.new("vagrant")
|
||||
tf.close
|
||||
|
||||
mime_types = WEBrick::HTTPUtils::DefaultMimeTypes
|
||||
mime_types.store "json", "application/json"
|
||||
|
||||
port = 3838
|
||||
server = WEBrick::HTTPServer.new(
|
||||
AccessLog: [],
|
||||
Logger: WEBrick::Log.new(tf.path, 7),
|
||||
Port: port,
|
||||
DocumentRoot: path.dirname.to_s,
|
||||
MimeTypes: mime_types)
|
||||
thr = Thread.new { server.start }
|
||||
yield port
|
||||
ensure
|
||||
server.shutdown rescue nil
|
||||
thr.join rescue nil
|
||||
end
|
||||
|
||||
before do
|
||||
box_collection.stub(find: nil)
|
||||
end
|
||||
|
||||
context "with box file directly" do
|
||||
it "adds it" do
|
||||
box_path = iso_env.box2_file(:virtualbox)
|
||||
|
||||
env[:box_name] = "foo"
|
||||
env[:box_url] = box_path.to_s
|
||||
|
||||
box_collection.should_receive(:add).with do |path, name, version, **opts|
|
||||
expect(checksum(path)).to eq(checksum(box_path))
|
||||
expect(name).to eq("foo")
|
||||
expect(version).to eq("0")
|
||||
expect(opts[:metadata_url]).to be_nil
|
||||
true
|
||||
end.and_return(box)
|
||||
|
||||
app.should_receive(:call).with(env)
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
|
||||
it "adds from multiple URLs" do
|
||||
box_path = iso_env.box2_file(:virtualbox)
|
||||
|
||||
env[:box_name] = "foo"
|
||||
env[:box_url] = [
|
||||
"/foo/bar/baz",
|
||||
box_path.to_s,
|
||||
]
|
||||
|
||||
box_collection.should_receive(:add).with do |path, name, version, **opts|
|
||||
expect(checksum(path)).to eq(checksum(box_path))
|
||||
expect(name).to eq("foo")
|
||||
expect(version).to eq("0")
|
||||
expect(opts[:metadata_url]).to be_nil
|
||||
true
|
||||
end.and_return(box)
|
||||
|
||||
app.should_receive(:call).with(env)
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
|
||||
it "adds from HTTP URL" do
|
||||
box_path = iso_env.box2_file(:virtualbox)
|
||||
with_web_server(box_path) do |port|
|
||||
env[:box_name] = "foo"
|
||||
env[:box_url] = "http://127.0.0.1:#{port}/#{box_path.basename}"
|
||||
|
||||
box_collection.should_receive(:add).with do |path, name, version, **opts|
|
||||
expect(checksum(path)).to eq(checksum(box_path))
|
||||
expect(name).to eq("foo")
|
||||
expect(version).to eq("0")
|
||||
expect(opts[:metadata_url]).to be_nil
|
||||
true
|
||||
end.and_return(box)
|
||||
|
||||
app.should_receive(:call).with(env)
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
it "raises an error if no name is given" do
|
||||
box_path = iso_env.box2_file(:virtualbox)
|
||||
|
||||
env[:box_url] = box_path.to_s
|
||||
|
||||
box_collection.should_receive(:add).never
|
||||
app.should_receive(:call).never
|
||||
|
||||
expect { subject.call(env) }.
|
||||
to raise_error(Vagrant::Errors::BoxAddNameRequired)
|
||||
end
|
||||
|
||||
it "raises an error if the box already exists" do
|
||||
box_path = iso_env.box2_file(:virtualbox)
|
||||
|
||||
env[:box_name] = "foo"
|
||||
env[:box_url] = box_path.to_s
|
||||
env[:box_provider] = "virtualbox"
|
||||
|
||||
box_collection.should_receive(:find).with(
|
||||
"foo", ["virtualbox"], "0").and_return(box)
|
||||
box_collection.should_receive(:add).never
|
||||
app.should_receive(:call).never
|
||||
|
||||
expect { subject.call(env) }.
|
||||
to raise_error(Vagrant::Errors::BoxAlreadyExists)
|
||||
end
|
||||
|
||||
it "force adds if exists and specified" do
|
||||
box_path = iso_env.box2_file(:virtualbox)
|
||||
|
||||
env[:box_force] = true
|
||||
env[:box_name] = "foo"
|
||||
env[:box_url] = box_path.to_s
|
||||
env[:box_provider] = "virtualbox"
|
||||
|
||||
box_collection.stub(find: box)
|
||||
box_collection.should_receive(:add).with do |path, name, version, **opts|
|
||||
expect(checksum(path)).to eq(checksum(box_path))
|
||||
expect(name).to eq("foo")
|
||||
expect(version).to eq("0")
|
||||
expect(opts[:metadata_url]).to be_nil
|
||||
true
|
||||
end.and_return(box)
|
||||
app.should_receive(:call).with(env).once
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
context "with box metadata" do
|
||||
it "adds from HTTP URL" do
|
||||
box_path = iso_env.box2_file(:virtualbox)
|
||||
tf = Tempfile.new(["vagrant", ".json"]).tap do |f|
|
||||
f.write(<<-RAW)
|
||||
{
|
||||
"name": "foo/bar",
|
||||
"versions": [
|
||||
{
|
||||
"version": "0.5"
|
||||
},
|
||||
{
|
||||
"version": "0.7",
|
||||
"providers": [
|
||||
{
|
||||
"name": "virtualbox",
|
||||
"url": "#{box_path}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
RAW
|
||||
f.close
|
||||
end
|
||||
|
||||
md_path = Pathname.new(tf.path)
|
||||
with_web_server(md_path) do |port|
|
||||
env[:box_url] = "http://127.0.0.1:#{port}/#{md_path.basename}"
|
||||
|
||||
box_collection.should_receive(:add).with do |path, name, version, **opts|
|
||||
expect(name).to eq("foo/bar")
|
||||
expect(version).to eq("0.7")
|
||||
expect(checksum(path)).to eq(checksum(box_path))
|
||||
expect(opts[:metadata_url]).to eq(env[:box_url])
|
||||
true
|
||||
end.and_return(box)
|
||||
|
||||
app.should_receive(:call).with(env)
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
it "adds from shorthand path" do
|
||||
box_path = iso_env.box2_file(:virtualbox)
|
||||
td = Pathname.new(Dir.mktmpdir)
|
||||
tf = td.join("mitchellh", "precise64.json")
|
||||
tf.dirname.mkpath
|
||||
tf.open("w") do |f|
|
||||
f.write(<<-RAW)
|
||||
{
|
||||
"name": "mitchellh/precise64",
|
||||
"versions": [
|
||||
{
|
||||
"version": "0.5"
|
||||
},
|
||||
{
|
||||
"version": "0.7",
|
||||
"providers": [
|
||||
{
|
||||
"name": "virtualbox",
|
||||
"url": "#{box_path}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
RAW
|
||||
end
|
||||
|
||||
with_web_server(tf.dirname) do |port|
|
||||
url = "http://127.0.0.1:#{port}"
|
||||
env[:box_url] = "mitchellh/precise64.json"
|
||||
|
||||
box_collection.should_receive(:add).with do |path, name, version, **opts|
|
||||
expect(name).to eq("mitchellh/precise64")
|
||||
expect(version).to eq("0.7")
|
||||
expect(checksum(path)).to eq(checksum(box_path))
|
||||
expect(opts[:metadata_url]).to eq(
|
||||
"#{url}/#{env[:box_url]}")
|
||||
true
|
||||
end.and_return(box)
|
||||
|
||||
app.should_receive(:call).with(env)
|
||||
|
||||
with_temp_env("VAGRANT_SERVER_URL" => url) do
|
||||
subject.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "raises an error if no Vagrant server is set" do
|
||||
tf = Tempfile.new("foo")
|
||||
tf.close
|
||||
|
||||
env[:box_url] = "mitchellh/precise64.json"
|
||||
|
||||
box_collection.should_receive(:add).never
|
||||
app.should_receive(:call).never
|
||||
|
||||
Vagrant.stub(server_url: nil)
|
||||
|
||||
expect { subject.call(env) }.
|
||||
to raise_error(Vagrant::Errors::BoxServerNotSet)
|
||||
end
|
||||
|
||||
it "raises an error if shorthand is invalid" do
|
||||
tf = Tempfile.new("foo")
|
||||
tf.close
|
||||
|
||||
with_web_server(Pathname.new(tf.path)) do |port|
|
||||
env[:box_url] = "mitchellh/precise64.json"
|
||||
|
||||
box_collection.should_receive(:add).never
|
||||
app.should_receive(:call).never
|
||||
|
||||
url = "http://127.0.0.1:#{port}"
|
||||
with_temp_env("VAGRANT_SERVER_URL" => url) do
|
||||
expect { subject.call(env) }.
|
||||
to raise_error(Vagrant::Errors::BoxAddShortNotFound)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "raises an error if multiple metadata URLs are given" do
|
||||
box_path = iso_env.box2_file(:virtualbox)
|
||||
tf = Tempfile.new("vagrant").tap do |f|
|
||||
f.write(<<-RAW)
|
||||
{
|
||||
"name": "foo/bar",
|
||||
"versions": [
|
||||
{
|
||||
"version": "0.5"
|
||||
},
|
||||
{
|
||||
"version": "0.7",
|
||||
"providers": [
|
||||
{
|
||||
"name": "virtualbox",
|
||||
"url": "#{box_path}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
RAW
|
||||
f.close
|
||||
end
|
||||
|
||||
env[:box_url] = [
|
||||
"/foo/bar/baz",
|
||||
tf.path,
|
||||
]
|
||||
box_collection.should_receive(:add).never
|
||||
app.should_receive(:call).never
|
||||
|
||||
expect { subject.call(env) }.
|
||||
to raise_error(Vagrant::Errors::BoxAddMetadataMultiURL)
|
||||
end
|
||||
|
||||
it "adds the latest version of a box with only one provider" do
|
||||
box_path = iso_env.box2_file(:virtualbox)
|
||||
tf = Tempfile.new("vagrant").tap do |f|
|
||||
f.write(<<-RAW)
|
||||
{
|
||||
"name": "foo/bar",
|
||||
"versions": [
|
||||
{
|
||||
"version": "0.5"
|
||||
},
|
||||
{
|
||||
"version": "0.7",
|
||||
"providers": [
|
||||
{
|
||||
"name": "virtualbox",
|
||||
"url": "#{box_path}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
RAW
|
||||
f.close
|
||||
end
|
||||
|
||||
env[:box_url] = tf.path
|
||||
box_collection.should_receive(:add).with do |path, name, version, **opts|
|
||||
expect(checksum(path)).to eq(checksum(box_path))
|
||||
expect(name).to eq("foo/bar")
|
||||
expect(version).to eq("0.7")
|
||||
expect(opts[:metadata_url]).to eq("file://#{tf.path}")
|
||||
true
|
||||
end.and_return(box)
|
||||
|
||||
app.should_receive(:call).with(env)
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
|
||||
it "adds the latest version of a box with the specified provider" do
|
||||
box_path = iso_env.box2_file(:vmware)
|
||||
tf = Tempfile.new("vagrant").tap do |f|
|
||||
f.write(<<-RAW)
|
||||
{
|
||||
"name": "foo/bar",
|
||||
"versions": [
|
||||
{
|
||||
"version": "0.5"
|
||||
},
|
||||
{
|
||||
"version": "0.7",
|
||||
"providers": [
|
||||
{
|
||||
"name": "virtualbox",
|
||||
"url": "#{iso_env.box2_file(:virtualbox)}"
|
||||
},
|
||||
{
|
||||
"name": "vmware",
|
||||
"url": "#{box_path}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
RAW
|
||||
f.close
|
||||
end
|
||||
|
||||
env[:box_url] = tf.path
|
||||
env[:box_provider] = "vmware"
|
||||
box_collection.should_receive(:add).with do |path, name, version, **opts|
|
||||
expect(checksum(path)).to eq(checksum(box_path))
|
||||
expect(name).to eq("foo/bar")
|
||||
expect(version).to eq("0.7")
|
||||
expect(opts[:metadata_url]).to eq("file://#{tf.path}")
|
||||
true
|
||||
end.and_return(box)
|
||||
|
||||
app.should_receive(:call).with(env)
|
||||
|
||||
subject.call(env)
|
||||
|
||||
expect(env[:box_added]).to equal(box)
|
||||
end
|
||||
|
||||
it "adds the latest version of a box with the specified provider, even if not latest" do
|
||||
box_path = iso_env.box2_file(:vmware)
|
||||
tf = Tempfile.new("vagrant").tap do |f|
|
||||
f.write(<<-RAW)
|
||||
{
|
||||
"name": "foo/bar",
|
||||
"versions": [
|
||||
{
|
||||
"version": "0.5"
|
||||
},
|
||||
{
|
||||
"version": "0.7",
|
||||
"providers": [
|
||||
{
|
||||
"name": "virtualbox",
|
||||
"url": "#{iso_env.box2_file(:virtualbox)}"
|
||||
},
|
||||
{
|
||||
"name": "vmware",
|
||||
"url": "#{box_path}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.5"
|
||||
}
|
||||
]
|
||||
}
|
||||
RAW
|
||||
f.close
|
||||
end
|
||||
|
||||
env[:box_url] = tf.path
|
||||
env[:box_provider] = "vmware"
|
||||
box_collection.should_receive(:add).with do |path, name, version, **opts|
|
||||
expect(checksum(path)).to eq(checksum(box_path))
|
||||
expect(name).to eq("foo/bar")
|
||||
expect(version).to eq("0.7")
|
||||
expect(opts[:metadata_url]).to eq("file://#{tf.path}")
|
||||
true
|
||||
end.and_return(box)
|
||||
|
||||
app.should_receive(:call).with(env)
|
||||
|
||||
subject.call(env)
|
||||
|
||||
expect(env[:box_added]).to equal(box)
|
||||
end
|
||||
|
||||
it "adds the constrained version of a box with the only provider" do
|
||||
box_path = iso_env.box2_file(:vmware)
|
||||
tf = Tempfile.new("vagrant").tap do |f|
|
||||
f.write(<<-RAW)
|
||||
{
|
||||
"name": "foo/bar",
|
||||
"versions": [
|
||||
{
|
||||
"version": "0.5",
|
||||
"providers": [
|
||||
{
|
||||
"name": "vmware",
|
||||
"url": "#{box_path}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{ "version": "1.1" }
|
||||
]
|
||||
}
|
||||
RAW
|
||||
f.close
|
||||
end
|
||||
|
||||
env[:box_url] = tf.path
|
||||
env[:box_version] = "~> 0.1"
|
||||
box_collection.should_receive(:add).with do |path, name, version, **opts|
|
||||
expect(checksum(path)).to eq(checksum(box_path))
|
||||
expect(name).to eq("foo/bar")
|
||||
expect(version).to eq("0.5")
|
||||
expect(opts[:metadata_url]).to eq("file://#{tf.path}")
|
||||
true
|
||||
end.and_return(box)
|
||||
|
||||
app.should_receive(:call).with(env)
|
||||
|
||||
subject.call(env)
|
||||
|
||||
expect(env[:box_added]).to equal(box)
|
||||
end
|
||||
|
||||
it "adds the constrained version of a box with the specified provider" do
|
||||
box_path = iso_env.box2_file(:vmware)
|
||||
tf = Tempfile.new("vagrant").tap do |f|
|
||||
f.write(<<-RAW)
|
||||
{
|
||||
"name": "foo/bar",
|
||||
"versions": [
|
||||
{
|
||||
"version": "0.5",
|
||||
"providers": [
|
||||
{
|
||||
"name": "vmware",
|
||||
"url": "#{box_path}"
|
||||
},
|
||||
{
|
||||
"name": "virtualbox",
|
||||
"url": "#{iso_env.box2_file(:virtualbox)}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{ "version": "1.1" }
|
||||
]
|
||||
}
|
||||
RAW
|
||||
f.close
|
||||
end
|
||||
|
||||
env[:box_url] = tf.path
|
||||
env[:box_provider] = "vmware"
|
||||
env[:box_version] = "~> 0.1"
|
||||
box_collection.should_receive(:add).with do |path, name, version, **opts|
|
||||
expect(checksum(path)).to eq(checksum(box_path))
|
||||
expect(name).to eq("foo/bar")
|
||||
expect(version).to eq("0.5")
|
||||
expect(opts[:metadata_url]).to eq("file://#{tf.path}")
|
||||
true
|
||||
end.and_return(box)
|
||||
|
||||
app.should_receive(:call).with(env)
|
||||
|
||||
subject.call(env)
|
||||
|
||||
expect(env[:box_added]).to equal(box)
|
||||
end
|
||||
|
||||
it "adds the latest version of a box with any specified provider" do
|
||||
box_path = iso_env.box2_file(:vmware)
|
||||
tf = Tempfile.new("vagrant").tap do |f|
|
||||
f.write(<<-RAW)
|
||||
{
|
||||
"name": "foo/bar",
|
||||
"versions": [
|
||||
{
|
||||
"version": "0.5",
|
||||
"providers": [
|
||||
{
|
||||
"name": "virtualbox",
|
||||
"url": "#{iso_env.box2_file(:virtualbox)}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "0.7",
|
||||
"providers": [
|
||||
{
|
||||
"name": "vmware",
|
||||
"url": "#{box_path}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
RAW
|
||||
f.close
|
||||
end
|
||||
|
||||
env[:box_url] = tf.path
|
||||
env[:box_provider] = ["virtualbox", "vmware"]
|
||||
box_collection.should_receive(:add).with do |path, name, version, **opts|
|
||||
expect(checksum(path)).to eq(checksum(box_path))
|
||||
expect(name).to eq("foo/bar")
|
||||
expect(version).to eq("0.7")
|
||||
expect(opts[:metadata_url]).to eq("file://#{tf.path}")
|
||||
true
|
||||
end.and_return(box)
|
||||
|
||||
app.should_receive(:call).with(env)
|
||||
|
||||
subject.call(env)
|
||||
|
||||
expect(env[:box_added]).to equal(box)
|
||||
end
|
||||
|
||||
it "asks the user what provider if multiple options" do
|
||||
box_path = iso_env.box2_file(:virtualbox)
|
||||
tf = Tempfile.new("vagrant").tap do |f|
|
||||
f.write(<<-RAW)
|
||||
{
|
||||
"name": "foo/bar",
|
||||
"versions": [
|
||||
{
|
||||
"version": "0.5"
|
||||
},
|
||||
{
|
||||
"version": "0.7",
|
||||
"providers": [
|
||||
{
|
||||
"name": "virtualbox",
|
||||
"url": "#{box_path}"
|
||||
},
|
||||
{
|
||||
"name": "vmware",
|
||||
"url": "#{iso_env.box2_file(:vmware)}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
RAW
|
||||
f.close
|
||||
end
|
||||
|
||||
env[:box_url] = tf.path
|
||||
|
||||
env[:ui].should_receive(:ask).and_return("1")
|
||||
|
||||
box_collection.should_receive(:add).with do |path, name, version, **opts|
|
||||
expect(checksum(path)).to eq(checksum(box_path))
|
||||
expect(name).to eq("foo/bar")
|
||||
expect(version).to eq("0.7")
|
||||
expect(opts[:metadata_url]).to eq("file://#{tf.path}")
|
||||
true
|
||||
end.and_return(box)
|
||||
|
||||
app.should_receive(:call).with(env)
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
|
||||
it "raises an exception if the name doesn't match a requested name" do
|
||||
box_path = iso_env.box2_file(:virtualbox)
|
||||
tf = Tempfile.new("vagrant").tap do |f|
|
||||
f.write(<<-RAW)
|
||||
{
|
||||
"name": "foo/bar",
|
||||
"versions": [
|
||||
{
|
||||
"version": "0.5"
|
||||
},
|
||||
{
|
||||
"version": "0.7",
|
||||
"providers": [
|
||||
{
|
||||
"name": "virtualbox",
|
||||
"url": "#{box_path}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
RAW
|
||||
f.close
|
||||
end
|
||||
|
||||
env[:box_name] = "foo"
|
||||
env[:box_url] = tf.path
|
||||
|
||||
box_collection.should_receive(:add).never
|
||||
app.should_receive(:call).never
|
||||
|
||||
expect { subject.call(env) }.
|
||||
to raise_error(Vagrant::Errors::BoxAddNameMismatch)
|
||||
end
|
||||
|
||||
it "raises an exception if no matching version" do
|
||||
box_path = iso_env.box2_file(:vmware)
|
||||
tf = Tempfile.new("vagrant").tap do |f|
|
||||
f.write(<<-RAW)
|
||||
{
|
||||
"name": "foo/bar",
|
||||
"versions": [
|
||||
{
|
||||
"version": "0.5",
|
||||
"providers": [
|
||||
{
|
||||
"name": "vmware",
|
||||
"url": "#{box_path}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{ "version": "1.1" }
|
||||
]
|
||||
}
|
||||
RAW
|
||||
f.close
|
||||
end
|
||||
|
||||
env[:box_url] = tf.path
|
||||
env[:box_version] = "~> 2.0"
|
||||
box_collection.should_receive(:add).never
|
||||
app.should_receive(:call).never
|
||||
|
||||
expect { subject.call(env) }.
|
||||
to raise_error(Vagrant::Errors::BoxAddNoMatchingVersion)
|
||||
end
|
||||
|
||||
it "raises an error if there is no matching provider" do
|
||||
tf = Tempfile.new("vagrant").tap do |f|
|
||||
f.write(<<-RAW)
|
||||
{
|
||||
"name": "foo/bar",
|
||||
"versions": [
|
||||
{
|
||||
"version": "0.5"
|
||||
},
|
||||
{
|
||||
"version": "0.7",
|
||||
"providers": [
|
||||
{
|
||||
"name": "virtualbox",
|
||||
"url": "#{iso_env.box2_file(:virtualbox)}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
RAW
|
||||
f.close
|
||||
end
|
||||
|
||||
env[:box_url] = tf.path
|
||||
env[:box_provider] = "vmware"
|
||||
box_collection.should_receive(:add).never
|
||||
app.should_receive(:call).never
|
||||
|
||||
expect { subject.call(env) }.
|
||||
to raise_error(Vagrant::Errors::BoxAddNoMatchingProvider)
|
||||
end
|
||||
|
||||
it "raises an error if a box already exists" do
|
||||
box_path = iso_env.box2_file(:virtualbox)
|
||||
tf = Tempfile.new("vagrant").tap do |f|
|
||||
f.write(<<-RAW)
|
||||
{
|
||||
"name": "foo/bar",
|
||||
"versions": [
|
||||
{
|
||||
"version": "0.5"
|
||||
},
|
||||
{
|
||||
"version": "0.7",
|
||||
"providers": [
|
||||
{
|
||||
"name": "virtualbox",
|
||||
"url": "#{box_path}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
RAW
|
||||
f.close
|
||||
end
|
||||
|
||||
env[:box_url] = tf.path
|
||||
box_collection.should_receive(:find).
|
||||
with("foo/bar", "virtualbox", "0.7").and_return(box)
|
||||
box_collection.should_receive(:add).never
|
||||
app.should_receive(:call).never
|
||||
|
||||
expect { subject.call(env) }.
|
||||
to raise_error(Vagrant::Errors::BoxAlreadyExists)
|
||||
end
|
||||
|
||||
it "force adds a box if specified" do
|
||||
box_path = iso_env.box2_file(:virtualbox)
|
||||
tf = Tempfile.new("vagrant").tap do |f|
|
||||
f.write(<<-RAW)
|
||||
{
|
||||
"name": "foo/bar",
|
||||
"versions": [
|
||||
{
|
||||
"version": "0.5"
|
||||
},
|
||||
{
|
||||
"version": "0.7",
|
||||
"providers": [
|
||||
{
|
||||
"name": "virtualbox",
|
||||
"url": "#{box_path}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
RAW
|
||||
f.close
|
||||
end
|
||||
|
||||
env[:box_force] = true
|
||||
env[:box_url] = tf.path
|
||||
box_collection.stub(find: box)
|
||||
box_collection.should_receive(:add).with do |path, name, version, **opts|
|
||||
expect(checksum(path)).to eq(checksum(box_path))
|
||||
expect(name).to eq("foo/bar")
|
||||
expect(version).to eq("0.7")
|
||||
expect(opts[:force]).to be_true
|
||||
expect(opts[:metadata_url]).to eq("file://#{tf.path}")
|
||||
true
|
||||
end.and_return(box)
|
||||
|
||||
app.should_receive(:call).with(env)
|
||||
|
||||
subject.call(env)
|
||||
|
||||
expect(env[:box_added]).to equal(box)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,146 @@
|
|||
require File.expand_path("../../../../base", __FILE__)
|
||||
|
||||
describe Vagrant::Action::Builtin::BoxCheckOutdated do
|
||||
include_context "unit"
|
||||
|
||||
let(:app) { lambda { |env| } }
|
||||
let(:env) { {
|
||||
box_collection: iso_vagrant_env.boxes,
|
||||
machine: machine,
|
||||
ui: Vagrant::UI::Silent.new,
|
||||
} }
|
||||
|
||||
subject { described_class.new(app, env) }
|
||||
|
||||
let(:iso_env) do
|
||||
# We have to create a Vagrantfile so there is a root path
|
||||
isolated_environment.tap do |env|
|
||||
env.vagrantfile("")
|
||||
end
|
||||
end
|
||||
|
||||
let(:iso_vagrant_env) { iso_env.create_vagrant_env }
|
||||
|
||||
let(:box) do
|
||||
box_dir = iso_env.box3("foo", "1.0", :virtualbox)
|
||||
Vagrant::Box.new("foo", :virtualbox, "1.0", box_dir).tap do |b|
|
||||
b.stub(has_update?: nil)
|
||||
end
|
||||
end
|
||||
|
||||
let(:machine) do
|
||||
m = iso_vagrant_env.machine(iso_vagrant_env.machine_names[0], :dummy)
|
||||
m.config.vm.box_check_update = true
|
||||
m
|
||||
end
|
||||
|
||||
before do
|
||||
machine.stub(box: box)
|
||||
end
|
||||
|
||||
context "disabling outdated checking" do
|
||||
it "doesn't check" do
|
||||
machine.config.vm.box_check_update = false
|
||||
|
||||
app.should_receive(:call).with(env).once
|
||||
|
||||
subject.call(env)
|
||||
|
||||
expect(env).to_not have_key(:box_outdated)
|
||||
end
|
||||
|
||||
it "checks if forced" do
|
||||
machine.config.vm.box_check_update = false
|
||||
env[:box_outdated_force] = true
|
||||
|
||||
app.should_receive(:call).with(env).once
|
||||
|
||||
subject.call(env)
|
||||
|
||||
expect(env).to have_key(:box_outdated)
|
||||
end
|
||||
end
|
||||
|
||||
context "no box" do
|
||||
it "raises an exception if the machine doesn't have a box yet" do
|
||||
machine.stub(box: nil)
|
||||
|
||||
app.should_receive(:call).never
|
||||
|
||||
expect { subject.call(env) }.
|
||||
to raise_error(Vagrant::Errors::BoxOutdatedNoBox)
|
||||
end
|
||||
end
|
||||
|
||||
context "with a non-versioned box" do
|
||||
it "does nothing" do
|
||||
box.stub(metadata_url: nil)
|
||||
box.stub(version: "0")
|
||||
|
||||
app.should_receive(:call).once
|
||||
box.should_receive(:has_update?).never
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
context "with a box" do
|
||||
it "sets env if no update" do
|
||||
box.should_receive(:has_update?).and_return(nil)
|
||||
|
||||
app.should_receive(:call).with(env).once
|
||||
|
||||
subject.call(env)
|
||||
|
||||
expect(env[:box_outdated]).to be_false
|
||||
end
|
||||
|
||||
it "sets env if there is an update" do
|
||||
md = Vagrant::BoxMetadata.new(StringIO.new(<<-RAW))
|
||||
{
|
||||
"name": "foo",
|
||||
"versions": [
|
||||
{
|
||||
"version": "1.0"
|
||||
},
|
||||
{
|
||||
"version": "1.1",
|
||||
"providers": [
|
||||
{
|
||||
"name": "virtualbox",
|
||||
"url": "bar"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
RAW
|
||||
|
||||
box.should_receive(:has_update?).with(machine.config.vm.box_version).
|
||||
and_return([md, md.version("1.1"), md.version("1.1").provider("virtualbox")])
|
||||
|
||||
app.should_receive(:call).with(env).once
|
||||
|
||||
subject.call(env)
|
||||
|
||||
expect(env[:box_outdated]).to be_true
|
||||
end
|
||||
|
||||
it "raises error if has_update? errors" do
|
||||
box.should_receive(:has_update?).and_raise(Vagrant::Errors::VagrantError)
|
||||
|
||||
app.should_receive(:call).never
|
||||
|
||||
expect { subject.call(env) }.to raise_error(Vagrant::Errors::VagrantError)
|
||||
end
|
||||
|
||||
it "doesn't raise an error if ignore errors is on" do
|
||||
env[:box_outdated_ignore_errors] = true
|
||||
|
||||
box.should_receive(:has_update?).and_raise(Vagrant::Errors::VagrantError)
|
||||
app.should_receive(:call).with(env).once
|
||||
|
||||
expect { subject.call(env) }.to_not raise_error
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,108 @@
|
|||
require File.expand_path("../../../../base", __FILE__)
|
||||
|
||||
describe Vagrant::Action::Builtin::BoxRemove do
|
||||
include_context "unit"
|
||||
|
||||
let(:app) { lambda { |env| } }
|
||||
let(:env) { {
|
||||
box_collection: box_collection,
|
||||
ui: Vagrant::UI::Silent.new,
|
||||
} }
|
||||
|
||||
subject { described_class.new(app, env) }
|
||||
|
||||
let(:box_collection) { double("box_collection") }
|
||||
let(:iso_env) { isolated_environment }
|
||||
|
||||
let(:box) do
|
||||
box_dir = iso_env.box3("foo", "1.0", :virtualbox)
|
||||
Vagrant::Box.new("foo", :virtualbox, "1.0", box_dir)
|
||||
end
|
||||
|
||||
it "deletes the box if it is the only option" do
|
||||
box_collection.stub(all: [["foo", "1.0", :virtualbox]])
|
||||
|
||||
env[:box_name] = "foo"
|
||||
|
||||
box_collection.should_receive(:find).with(
|
||||
"foo", :virtualbox, "1.0").and_return(box)
|
||||
box.should_receive(:destroy!).once
|
||||
app.should_receive(:call).with(env).once
|
||||
|
||||
subject.call(env)
|
||||
|
||||
expect(env[:box_removed]).to equal(box)
|
||||
end
|
||||
|
||||
it "deletes the box with the specified provider if given" do
|
||||
box_collection.stub(
|
||||
all: [
|
||||
["foo", "1.0", :virtualbox],
|
||||
["foo", "1.0", :vmware],
|
||||
])
|
||||
|
||||
env[:box_name] = "foo"
|
||||
env[:box_provider] = "virtualbox"
|
||||
|
||||
box_collection.should_receive(:find).with(
|
||||
"foo", :virtualbox, "1.0").and_return(box)
|
||||
box.should_receive(:destroy!).once
|
||||
app.should_receive(:call).with(env).once
|
||||
|
||||
subject.call(env)
|
||||
|
||||
expect(env[:box_removed]).to equal(box)
|
||||
end
|
||||
|
||||
it "errors if the box doesn't exist" do
|
||||
box_collection.stub(all: [])
|
||||
|
||||
app.should_receive(:call).never
|
||||
|
||||
expect { subject.call(env) }.
|
||||
to raise_error(Vagrant::Errors::BoxRemoveNotFound)
|
||||
end
|
||||
|
||||
it "errors if the specified provider doesn't exist" do
|
||||
env[:box_name] = "foo"
|
||||
env[:box_provider] = "bar"
|
||||
|
||||
box_collection.stub(all: [["foo", "1.0", :virtualbox]])
|
||||
|
||||
app.should_receive(:call).never
|
||||
|
||||
expect { subject.call(env) }.
|
||||
to raise_error(Vagrant::Errors::BoxRemoveProviderNotFound)
|
||||
end
|
||||
|
||||
it "errors if there are multiple providers" do
|
||||
env[:box_name] = "foo"
|
||||
|
||||
box_collection.stub(
|
||||
all: [
|
||||
["foo", "1.0", :virtualbox],
|
||||
["foo", "1.0", :vmware],
|
||||
])
|
||||
|
||||
app.should_receive(:call).never
|
||||
|
||||
expect { subject.call(env) }.
|
||||
to raise_error(Vagrant::Errors::BoxRemoveMultiProvider)
|
||||
end
|
||||
|
||||
it "errors if the specified provider has multiple versions" do
|
||||
env[:box_name] = "foo"
|
||||
env[:box_provider] = "virtualbox"
|
||||
|
||||
box_collection.stub(
|
||||
all: [
|
||||
["foo", "1.0", :virtualbox],
|
||||
["foo", "1.1", :virtualbox],
|
||||
])
|
||||
|
||||
app.should_receive(:call).never
|
||||
|
||||
expect { subject.call(env) }.
|
||||
to raise_error(Vagrant::Errors::BoxRemoveMultiVersion)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,109 @@
|
|||
require File.expand_path("../../../../base", __FILE__)
|
||||
|
||||
describe Vagrant::Action::Builtin::HandleBox do
|
||||
include_context "unit"
|
||||
|
||||
let(:app) { lambda { |env| } }
|
||||
let(:env) { {
|
||||
action_runner: action_runner,
|
||||
machine: machine,
|
||||
ui: Vagrant::UI::Silent.new,
|
||||
} }
|
||||
|
||||
subject { described_class.new(app, env) }
|
||||
|
||||
let(:iso_env) do
|
||||
# We have to create a Vagrantfile so there is a root path
|
||||
isolated_environment.tap do |env|
|
||||
env.vagrantfile("")
|
||||
end
|
||||
end
|
||||
|
||||
let(:iso_vagrant_env) { iso_env.create_vagrant_env }
|
||||
|
||||
let(:action_runner) { double("action_runner") }
|
||||
let(:box) do
|
||||
box_dir = iso_env.box3("foo", "1.0", :virtualbox)
|
||||
Vagrant::Box.new("foo", :virtualbox, "1.0", box_dir)
|
||||
end
|
||||
let(:machine) { iso_vagrant_env.machine(iso_vagrant_env.machine_names[0], :dummy) }
|
||||
|
||||
it "works if there is no box set" do
|
||||
machine.config.vm.box = nil
|
||||
machine.config.vm.box_url = nil
|
||||
|
||||
app.should_receive(:call).with(env)
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
|
||||
it "doesn't do anything if a box exists" do
|
||||
machine.stub(box: box)
|
||||
|
||||
action_runner.should_receive(:run).never
|
||||
app.should_receive(:call).with(env)
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
|
||||
context "with a box set and no box_url" do
|
||||
before do
|
||||
machine.stub(box: nil)
|
||||
|
||||
machine.config.vm.box = "foo"
|
||||
end
|
||||
|
||||
it "adds a box that doesn't exist" do
|
||||
action_runner.should_receive(:run).with do |action, opts|
|
||||
expect(opts[:box_name]).to eq(machine.config.vm.box)
|
||||
expect(opts[:box_url]).to eq(machine.config.vm.box)
|
||||
expect(opts[:box_provider]).to eq(:dummy)
|
||||
expect(opts[:box_version]).to eq(machine.config.vm.box_version)
|
||||
true
|
||||
end
|
||||
|
||||
app.should_receive(:call).with(env)
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
|
||||
it "adds a box using any format the provider allows" do
|
||||
machine.provider_options[:box_format] = [:foo, :bar]
|
||||
|
||||
action_runner.should_receive(:run).with do |action, opts|
|
||||
expect(opts[:box_name]).to eq(machine.config.vm.box)
|
||||
expect(opts[:box_url]).to eq(machine.config.vm.box)
|
||||
expect(opts[:box_provider]).to eq([:foo, :bar])
|
||||
expect(opts[:box_version]).to eq(machine.config.vm.box_version)
|
||||
true
|
||||
end
|
||||
|
||||
app.should_receive(:call).with(env)
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
context "with a box and box_url set" do
|
||||
before do
|
||||
machine.stub(box: nil)
|
||||
|
||||
machine.config.vm.box = "foo"
|
||||
machine.config.vm.box_url = "bar"
|
||||
end
|
||||
|
||||
it "adds a box that doesn't exist" do
|
||||
action_runner.should_receive(:run).with do |action, opts|
|
||||
expect(opts[:box_name]).to eq(machine.config.vm.box)
|
||||
expect(opts[:box_url]).to eq(machine.config.vm.box_url)
|
||||
expect(opts[:box_provider]).to eq(:dummy)
|
||||
expect(opts[:box_version]).to eq(machine.config.vm.box_version)
|
||||
true
|
||||
end
|
||||
|
||||
app.should_receive(:call).with(env)
|
||||
|
||||
subject.call(env)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -8,35 +8,141 @@ describe Vagrant::BoxCollection do
|
|||
|
||||
let(:box_class) { Vagrant::Box }
|
||||
let(:environment) { isolated_environment }
|
||||
let(:instance) { described_class.new(environment.boxes_dir) }
|
||||
|
||||
subject { described_class.new(environment.boxes_dir) }
|
||||
|
||||
it "should tell us the directory it is using" do
|
||||
instance.directory.should == environment.boxes_dir
|
||||
subject.directory.should == environment.boxes_dir
|
||||
end
|
||||
|
||||
describe "adding" do
|
||||
describe "#all" do
|
||||
it "should return an empty array when no boxes are there" do
|
||||
subject.all.should == []
|
||||
end
|
||||
|
||||
it "should return the boxes and their providers" do
|
||||
# Create some boxes
|
||||
environment.box3("foo", "1.0", :virtualbox)
|
||||
environment.box3("foo", "1.0", :vmware)
|
||||
environment.box3("bar", "0", :ec2)
|
||||
environment.box3("foo-VAGRANTSLASH-bar", "1.0", :virtualbox)
|
||||
|
||||
# Verify some output
|
||||
results = subject.all
|
||||
results.length.should == 4
|
||||
results.include?(["foo", "1.0", :virtualbox]).should be
|
||||
results.include?(["foo", "1.0", :vmware]).should be
|
||||
results.include?(["bar", "0", :ec2]).should be
|
||||
results.include?(["foo/bar", "1.0", :virtualbox]).should be
|
||||
end
|
||||
|
||||
it 'does not raise an exception when a file appears in the boxes dir' do
|
||||
Tempfile.new('a_file', environment.boxes_dir)
|
||||
expect { subject.all }.to_not raise_error
|
||||
end
|
||||
end
|
||||
|
||||
describe "#find" do
|
||||
it "returns nil if the box does not exist" do
|
||||
expect(subject.find("foo", :i_dont_exist, ">= 0")).to be_nil
|
||||
end
|
||||
|
||||
it "returns a box if the box does exist" do
|
||||
# Create the "box"
|
||||
environment.box3("foo", "0", :virtualbox)
|
||||
|
||||
# Actual test
|
||||
result = subject.find("foo", :virtualbox, ">= 0")
|
||||
expect(result).to_not be_nil
|
||||
expect(result).to be_kind_of(box_class)
|
||||
expect(result.name).to eq("foo")
|
||||
end
|
||||
|
||||
it "returns latest version matching constraint" do
|
||||
# Create the "box"
|
||||
environment.box3("foo", "1.0", :virtualbox)
|
||||
environment.box3("foo", "1.5", :virtualbox)
|
||||
|
||||
# Actual test
|
||||
result = subject.find("foo", :virtualbox, ">= 0")
|
||||
expect(result).to_not be_nil
|
||||
expect(result).to be_kind_of(box_class)
|
||||
expect(result.name).to eq("foo")
|
||||
expect(result.version).to eq("1.5")
|
||||
end
|
||||
|
||||
it "can satisfy complex constraints" do
|
||||
# Create the "box"
|
||||
environment.box3("foo", "0.1", :virtualbox)
|
||||
environment.box3("foo", "1.0", :virtualbox)
|
||||
environment.box3("foo", "2.1", :virtualbox)
|
||||
|
||||
# Actual test
|
||||
result = subject.find("foo", :virtualbox, ">= 0.9, < 1.5")
|
||||
expect(result).to_not be_nil
|
||||
expect(result).to be_kind_of(box_class)
|
||||
expect(result.name).to eq("foo")
|
||||
expect(result.version).to eq("1.0")
|
||||
end
|
||||
|
||||
it "returns nil if a box's constraints can't be satisfied" do
|
||||
# Create the "box"
|
||||
environment.box3("foo", "0.1", :virtualbox)
|
||||
environment.box3("foo", "1.0", :virtualbox)
|
||||
environment.box3("foo", "2.1", :virtualbox)
|
||||
|
||||
# Actual test
|
||||
result = subject.find("foo", :virtualbox, "> 1.0, < 1.5")
|
||||
expect(result).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "#add" 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
|
||||
box = subject.add(box_path, "foo", "1.0", providers: :virtualbox)
|
||||
expect(box).to be_kind_of(box_class)
|
||||
expect(box.name).to eq("foo")
|
||||
expect(box.provider).to eq(:virtualbox)
|
||||
|
||||
# Verify we can find it as well
|
||||
box = instance.find("foo", :virtualbox)
|
||||
box.should_not be_nil
|
||||
expect(subject.find("foo", :virtualbox, "1.0")).to_not be_nil
|
||||
end
|
||||
|
||||
it "should add a box with a name with '/' in it" do
|
||||
box_path = environment.box2_file(:virtualbox)
|
||||
|
||||
# Add the box
|
||||
box = subject.add(box_path, "foo/bar", "1.0")
|
||||
expect(box).to be_kind_of(box_class)
|
||||
expect(box.name).to eq("foo/bar")
|
||||
expect(box.provider).to eq(:virtualbox)
|
||||
|
||||
# Verify we can find it as well
|
||||
expect(subject.find("foo/bar", :virtualbox, "1.0")).to_not be_nil
|
||||
end
|
||||
|
||||
it "should add a box without specifying a provider" do
|
||||
box_path = environment.box2_file(:vmware)
|
||||
|
||||
# Add the box
|
||||
box = instance.add(box_path, "foo")
|
||||
box.should be_kind_of(box_class)
|
||||
box.name.should == "foo"
|
||||
box.provider.should == :vmware
|
||||
box = subject.add(box_path, "foo", "1.0")
|
||||
expect(box).to be_kind_of(box_class)
|
||||
expect(box.name).to eq("foo")
|
||||
expect(box.provider).to eq(:vmware)
|
||||
end
|
||||
|
||||
it "should store a metadata URL" do
|
||||
box_path = environment.box2_file(:virtualbox)
|
||||
|
||||
subject.add(
|
||||
box_path, "foo", "1.0",
|
||||
metadata_url: "bar")
|
||||
|
||||
box = subject.find("foo", :virtualbox, "1.0")
|
||||
expect(box.metadata_url).to eq("bar")
|
||||
end
|
||||
|
||||
it "should add a V1 box" do
|
||||
|
@ -44,35 +150,39 @@ describe Vagrant::BoxCollection do
|
|||
box_path = environment.box1_file
|
||||
|
||||
# Add the box
|
||||
box = instance.add(box_path, "foo")
|
||||
box.should be_kind_of(box_class)
|
||||
box.name.should == "foo"
|
||||
box.provider.should == :virtualbox
|
||||
box = subject.add(box_path, "foo", "1.0")
|
||||
expect(box).to be_kind_of(box_class)
|
||||
expect(box.name).to eq("foo")
|
||||
expect(box.provider).to eq(:virtualbox)
|
||||
end
|
||||
|
||||
it "should raise an exception if the box already exists" do
|
||||
prev_box_name = "foo"
|
||||
prev_box_provider = :virtualbox
|
||||
prev_box_version = "1.0"
|
||||
|
||||
# Create the box we're adding
|
||||
environment.box2(prev_box_name, prev_box_provider)
|
||||
environment.box3(prev_box_name, "1.0", 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)
|
||||
expect {
|
||||
subject.add(box_path, prev_box_name,
|
||||
prev_box_version, providers: prev_box_provider)
|
||||
}.to raise_error(Vagrant::Errors::BoxAlreadyExists)
|
||||
end
|
||||
|
||||
it "should replace the box if force is specified" do
|
||||
prev_box_name = "foo"
|
||||
prev_box_provider = :vmware
|
||||
prev_box_version = "1.0"
|
||||
|
||||
# Setup the environment with the box pre-added
|
||||
environment.box2(prev_box_name, prev_box_provider)
|
||||
environment.box3(prev_box_name, prev_box_version, prev_box_provider)
|
||||
|
||||
# Attempt to add the box with the same name
|
||||
box_path = environment.box2_file(prev_box_provider, metadata: { "replaced" => "yes" })
|
||||
box = instance.add(box_path, prev_box_name, nil, true)
|
||||
box = subject.add(box_path, prev_box_name, prev_box_version, force: true)
|
||||
box.metadata["replaced"].should == "yes"
|
||||
end
|
||||
|
||||
|
@ -82,25 +192,13 @@ describe Vagrant::BoxCollection do
|
|||
box_path = environment.box2_file(:vmware)
|
||||
|
||||
# Add it once, successfully
|
||||
expect { instance.add(box_path, box_name) }.to_not raise_error
|
||||
expect { subject.add(box_path, box_name, "1.0") }.to_not raise_error
|
||||
|
||||
# Add it again, and fail!
|
||||
expect { instance.add(box_path, box_name) }.
|
||||
expect { subject.add(box_path, box_name, "1.0") }.
|
||||
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) }.
|
||||
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
|
||||
|
@ -111,11 +209,11 @@ describe Vagrant::BoxCollection do
|
|||
|
||||
# Add the box but with an invalid provider, verify we get the proper
|
||||
# error.
|
||||
expect { instance.add(box_path, box_name, bad_provider) }.
|
||||
expect { subject.add(box_path, box_name, "1.0", providers: bad_provider) }.
|
||||
to raise_error(Vagrant::Errors::BoxProviderDoesntMatch)
|
||||
|
||||
# Verify the box doesn't exist
|
||||
instance.find(box_name, bad_provider).should be_nil
|
||||
expect(subject.find(box_name, bad_provider, "1.0")).to be_nil
|
||||
end
|
||||
|
||||
it "should raise an exception if you add an invalid box file" do
|
||||
|
@ -130,7 +228,7 @@ describe Vagrant::BoxCollection do
|
|||
f.write("\0"*CHECKSUM_LENGTH)
|
||||
f.close
|
||||
|
||||
expect { instance.add(f.path, "foo", :virtualbox) }.
|
||||
expect { subject.add(f.path, "foo", "1.0") }.
|
||||
to raise_error(Vagrant::Errors::BoxUnpackageFailure)
|
||||
ensure
|
||||
f.close
|
||||
|
@ -139,103 +237,36 @@ describe Vagrant::BoxCollection do
|
|||
end
|
||||
end
|
||||
|
||||
describe "listing all" do
|
||||
it "should return an empty array when no boxes are there" do
|
||||
instance.all.should == []
|
||||
describe "#upgrade_v1_1_v1_5" do
|
||||
let(:boxes_dir) { environment.boxes_dir }
|
||||
|
||||
before do
|
||||
# Create all the various box directories
|
||||
@foo_path = environment.box2("foo", "virtualbox")
|
||||
@vbox_path = environment.box2("precise64", "virtualbox")
|
||||
@vmware_path = environment.box2("precise64", "vmware")
|
||||
@v1_path = environment.box("v1box")
|
||||
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)
|
||||
it "upgrades the boxes" do
|
||||
subject.upgrade_v1_1_v1_5
|
||||
|
||||
# Verify some output
|
||||
results = instance.all
|
||||
results.length.should == 3
|
||||
results.include?(["foo", :virtualbox]).should be
|
||||
results.include?(["foo", :vmware]).should be
|
||||
results.include?(["bar", :ec2]).should be
|
||||
end
|
||||
# The old paths should not exist anymore
|
||||
expect(@foo_path).to_not exist
|
||||
expect(@vbox_path).to_not exist
|
||||
expect(@vmware_path).to_not exist
|
||||
expect(@v1_path.join("box.ovf")).to_not exist
|
||||
|
||||
it "should return V1 boxes as well" do
|
||||
# Create some boxes, including a V1 box
|
||||
environment.box1("bar")
|
||||
environment.box2("foo", :vmware)
|
||||
# New paths should exist
|
||||
foo_path = boxes_dir.join("foo", "0", "virtualbox")
|
||||
vbox_path = boxes_dir.join("precise64", "0", "virtualbox")
|
||||
vmware_path = boxes_dir.join("precise64", "0", "vmware")
|
||||
v1_path = boxes_dir.join("v1box", "0", "virtualbox")
|
||||
|
||||
# Verify some output
|
||||
results = instance.all.sort
|
||||
results.should == [["bar", :virtualbox, :v1], ["foo", :vmware]]
|
||||
end
|
||||
|
||||
it 'does not raise an exception when a file appears in the boxes dir' do
|
||||
Tempfile.new('a_file', environment.boxes_dir)
|
||||
expect { instance.all }.to_not raise_error
|
||||
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
|
||||
|
||||
it "should return nil if there is a V1 box but we're looking for another provider" do
|
||||
# Create a V1 box
|
||||
environment.box1("foo")
|
||||
|
||||
# Test
|
||||
instance.find("foo", :another_provider).should be_nil
|
||||
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
|
||||
expect(foo_path).to exist
|
||||
expect(vbox_path).to exist
|
||||
expect(vmware_path).to exist
|
||||
expect(v1_path).to exist
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
require File.expand_path("../../base", __FILE__)
|
||||
|
||||
require "vagrant/box_metadata"
|
||||
|
||||
describe Vagrant::BoxMetadata do
|
||||
include_context "unit"
|
||||
|
||||
let(:raw) do
|
||||
<<-RAW
|
||||
{
|
||||
"name": "foo",
|
||||
"description": "bar",
|
||||
"versions": [
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"providers": [
|
||||
{ "name": "virtualbox" },
|
||||
{ "name": "vmware" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.1.5",
|
||||
"providers": [
|
||||
{ "name": "virtualbox" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.1.0",
|
||||
"providers": [
|
||||
{ "name": "virtualbox" },
|
||||
{ "name": "vmware" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
RAW
|
||||
end
|
||||
|
||||
subject { described_class.new(raw) }
|
||||
|
||||
its(:name) { should eq("foo") }
|
||||
its(:description) { should eq("bar") }
|
||||
|
||||
context "with poorly formatted JSON" do
|
||||
let(:raw) {
|
||||
<<-RAW
|
||||
{ "name": "foo", }
|
||||
RAW
|
||||
}
|
||||
|
||||
it "raises an exception" do
|
||||
expect { subject }.
|
||||
to raise_error(Vagrant::Errors::BoxMetadataMalformed)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#version" do
|
||||
it "matches an exact version" do
|
||||
result = subject.version("1.0.0")
|
||||
expect(result).to_not be_nil
|
||||
expect(result).to be_kind_of(Vagrant::BoxMetadata::Version)
|
||||
expect(result.version).to eq("1.0.0")
|
||||
end
|
||||
|
||||
it "matches a constraint with latest matching version" do
|
||||
result = subject.version(">= 1.0")
|
||||
expect(result).to_not be_nil
|
||||
expect(result).to be_kind_of(Vagrant::BoxMetadata::Version)
|
||||
expect(result.version).to eq("1.1.5")
|
||||
end
|
||||
|
||||
it "matches complex constraints" do
|
||||
result = subject.version(">= 0.9, ~> 1.0.0")
|
||||
expect(result).to_not be_nil
|
||||
expect(result).to be_kind_of(Vagrant::BoxMetadata::Version)
|
||||
expect(result.version).to eq("1.0.0")
|
||||
end
|
||||
|
||||
it "matches the constraint that has the given provider" do
|
||||
result = subject.version(">= 0", provider: :vmware)
|
||||
expect(result).to_not be_nil
|
||||
expect(result).to be_kind_of(Vagrant::BoxMetadata::Version)
|
||||
expect(result.version).to eq("1.1.0")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#versions" do
|
||||
it "returns the versions it contained" do
|
||||
expect(subject.versions).to eq(
|
||||
["1.0.0", "1.1.0", "1.1.5"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe Vagrant::BoxMetadata::Version do
|
||||
let(:raw) { {} }
|
||||
|
||||
subject { described_class.new(raw) }
|
||||
|
||||
before do
|
||||
raw["providers"] = [
|
||||
{
|
||||
"name" => "virtualbox",
|
||||
},
|
||||
{
|
||||
"name" => "vmware",
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
describe "#version" do
|
||||
it "is the version in the raw data" do
|
||||
v = "1.0"
|
||||
raw["version"] = v
|
||||
expect(subject.version).to eq(v)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#provider" do
|
||||
it "returns nil if a provider isn't supported" do
|
||||
expect(subject.provider("foo")).to be_nil
|
||||
end
|
||||
|
||||
it "returns the provider specified" do
|
||||
result = subject.provider("virtualbox")
|
||||
expect(result).to_not be_nil
|
||||
expect(result).to be_kind_of(Vagrant::BoxMetadata::Provider)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#providers" do
|
||||
it "returns the providers available" do
|
||||
expect(subject.providers.sort).to eq(
|
||||
[:virtualbox, :vmware])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe Vagrant::BoxMetadata::Provider do
|
||||
let(:raw) { {} }
|
||||
|
||||
subject { described_class.new(raw) }
|
||||
|
||||
describe "#name" do
|
||||
it "is the name specified" do
|
||||
raw["name"] = "foo"
|
||||
expect(subject.name).to eq("foo")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#url" do
|
||||
it "is the URL specified" do
|
||||
raw["url"] = "bar"
|
||||
expect(subject.url).to eq("bar")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,6 +1,10 @@
|
|||
require File.expand_path("../../base", __FILE__)
|
||||
|
||||
require "pathname"
|
||||
require "stringio"
|
||||
require "tempfile"
|
||||
|
||||
require "vagrant/box_metadata"
|
||||
|
||||
describe Vagrant::Box do
|
||||
include_context "unit"
|
||||
|
@ -11,21 +15,22 @@ describe Vagrant::Box do
|
|||
|
||||
let(:name) { "foo" }
|
||||
let(:provider) { :virtualbox }
|
||||
let(:directory) { environment.box2("foo", :virtualbox) }
|
||||
let(:instance) { described_class.new(name, provider, directory) }
|
||||
let(:version) { "1.0" }
|
||||
let(:directory) { environment.box3("foo", "1.0", :virtualbox) }
|
||||
subject { described_class.new(name, provider, version, directory) }
|
||||
|
||||
subject { described_class.new(name, provider, directory) }
|
||||
its(:metadata_url) { should be_nil }
|
||||
|
||||
it "provides the name" do
|
||||
instance.name.should == name
|
||||
subject.name.should == name
|
||||
end
|
||||
|
||||
it "provides the provider" do
|
||||
instance.provider.should == provider
|
||||
subject.provider.should == provider
|
||||
end
|
||||
|
||||
it "provides the directory" do
|
||||
instance.directory.should == directory
|
||||
subject.directory.should == directory
|
||||
end
|
||||
|
||||
it "provides the metadata associated with a box" do
|
||||
|
@ -37,7 +42,17 @@ describe Vagrant::Box do
|
|||
end
|
||||
|
||||
# Verify the metadata
|
||||
instance.metadata.should == data
|
||||
subject.metadata.should == data
|
||||
end
|
||||
|
||||
context "with a metadata URL" do
|
||||
subject do
|
||||
described_class.new(
|
||||
name, provider, version, directory,
|
||||
metadata_url: "foo")
|
||||
end
|
||||
|
||||
its(:metadata_url) { should eq("foo") }
|
||||
end
|
||||
|
||||
context "with a corrupt metadata file" do
|
||||
|
@ -64,21 +79,157 @@ describe Vagrant::Box do
|
|||
end
|
||||
end
|
||||
|
||||
context "#has_update?" do
|
||||
subject do
|
||||
described_class.new(
|
||||
name, provider, version, directory,
|
||||
metadata_url: "foo")
|
||||
end
|
||||
|
||||
it "raises an exception if no metadata_url is set" do
|
||||
subject = described_class.new(
|
||||
name, provider, version, directory)
|
||||
|
||||
expect { subject.has_update?("> 0") }.
|
||||
to raise_error(Vagrant::Errors::BoxUpdateNoMetadata)
|
||||
end
|
||||
|
||||
it "returns nil if there is no update" do
|
||||
metadata = Vagrant::BoxMetadata.new(StringIO.new(<<-RAW))
|
||||
{
|
||||
"name": "foo",
|
||||
"versions": [
|
||||
{ "version": "1.0" }
|
||||
]
|
||||
}
|
||||
RAW
|
||||
|
||||
subject.stub(load_metadata: metadata)
|
||||
|
||||
expect(subject.has_update?).to be_nil
|
||||
end
|
||||
|
||||
it "returns the updated box info if there is an update available" do
|
||||
metadata = Vagrant::BoxMetadata.new(StringIO.new(<<-RAW))
|
||||
{
|
||||
"name": "foo",
|
||||
"versions": [
|
||||
{
|
||||
"version": "1.0"
|
||||
},
|
||||
{
|
||||
"version": "1.1",
|
||||
"providers": [
|
||||
{
|
||||
"name": "virtualbox",
|
||||
"url": "bar"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
RAW
|
||||
|
||||
subject.stub(load_metadata: metadata)
|
||||
|
||||
result = subject.has_update?
|
||||
expect(result).to_not be_nil
|
||||
|
||||
expect(result[0]).to be_kind_of(Vagrant::BoxMetadata)
|
||||
expect(result[1]).to be_kind_of(Vagrant::BoxMetadata::Version)
|
||||
expect(result[2]).to be_kind_of(Vagrant::BoxMetadata::Provider)
|
||||
|
||||
expect(result[0].name).to eq("foo")
|
||||
expect(result[1].version).to eq("1.1")
|
||||
expect(result[2].url).to eq("bar")
|
||||
end
|
||||
|
||||
it "returns the updated box info within constraints" do
|
||||
metadata = Vagrant::BoxMetadata.new(StringIO.new(<<-RAW))
|
||||
{
|
||||
"name": "foo",
|
||||
"versions": [
|
||||
{
|
||||
"version": "1.0"
|
||||
},
|
||||
{
|
||||
"version": "1.1",
|
||||
"providers": [
|
||||
{
|
||||
"name": "virtualbox",
|
||||
"url": "bar"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.4",
|
||||
"providers": [
|
||||
{
|
||||
"name": "virtualbox",
|
||||
"url": "bar"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
RAW
|
||||
|
||||
subject.stub(load_metadata: metadata)
|
||||
|
||||
result = subject.has_update?(">= 1.1, < 1.4")
|
||||
expect(result).to_not be_nil
|
||||
|
||||
expect(result[0]).to be_kind_of(Vagrant::BoxMetadata)
|
||||
expect(result[1]).to be_kind_of(Vagrant::BoxMetadata::Version)
|
||||
expect(result[2]).to be_kind_of(Vagrant::BoxMetadata::Provider)
|
||||
|
||||
expect(result[0].name).to eq("foo")
|
||||
expect(result[1].version).to eq("1.1")
|
||||
expect(result[2].url).to eq("bar")
|
||||
end
|
||||
end
|
||||
|
||||
context "#load_metadata" do
|
||||
let(:metadata_url) do
|
||||
Tempfile.new("vagrant").tap do |f|
|
||||
f.write(<<-RAW)
|
||||
{
|
||||
"name": "foo",
|
||||
"description": "bar"
|
||||
}
|
||||
RAW
|
||||
f.close
|
||||
end
|
||||
end
|
||||
|
||||
subject do
|
||||
described_class.new(
|
||||
name, provider, version, directory,
|
||||
metadata_url: metadata_url.path)
|
||||
end
|
||||
|
||||
it "loads the url and returns the data" do
|
||||
result = subject.load_metadata
|
||||
expect(result.name).to eq("foo")
|
||||
expect(result.description).to eq("bar")
|
||||
end
|
||||
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
|
||||
subject.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
|
||||
# Get the subject so that it is instantiated
|
||||
box = subject
|
||||
|
||||
# Delete the directory
|
||||
directory.rmtree
|
||||
|
@ -100,36 +251,51 @@ describe Vagrant::Box do
|
|||
|
||||
# Repackage our box to some temporary directory
|
||||
box_output_path = temporary_dir.join("package.box")
|
||||
instance.repackage(box_output_path).should be
|
||||
expect(subject.repackage(box_output_path)).to be_true
|
||||
|
||||
# Let's now add this box again under a different name, and then
|
||||
# verify that we get the proper result back.
|
||||
new_box = box_collection.add(box_output_path, "foo2")
|
||||
new_box = box_collection.add(box_output_path, "foo2", "1.0")
|
||||
new_box.directory.join("test_file").read.should == test_file_contents
|
||||
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)
|
||||
it "should be equal if the name, provider, version match" do
|
||||
a = described_class.new("a", :foo, "1.0", directory)
|
||||
b = described_class.new("a", :foo, "1.0", 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)
|
||||
it "should not be equal if name doesn't match" do
|
||||
a = described_class.new("a", :foo, "1.0", directory)
|
||||
b = described_class.new("b", :foo, "1.0", directory)
|
||||
|
||||
a.should_not == b
|
||||
expect(a).to_not eq(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)
|
||||
it "should not be equal if provider doesn't match" do
|
||||
a = described_class.new("a", :foo, "1.0", directory)
|
||||
b = described_class.new("a", :bar, "1.0", directory)
|
||||
|
||||
[c, a, b].sort.should == [a, b, c]
|
||||
expect(a).to_not eq(b)
|
||||
end
|
||||
|
||||
it "should not be equal if version doesn't match" do
|
||||
a = described_class.new("a", :foo, "1.0", directory)
|
||||
b = described_class.new("a", :foo, "1.1", directory)
|
||||
|
||||
expect(a).to_not eq(b)
|
||||
end
|
||||
|
||||
it "should sort them in order of name, version, provider" do
|
||||
a = described_class.new("a", :foo, "1.0", directory)
|
||||
b = described_class.new("a", :foo2, "1.0", directory)
|
||||
c = described_class.new("a", :foo2, "1.1", directory)
|
||||
d = described_class.new("b", :foo2, "1.0", directory)
|
||||
|
||||
[d, c, a, b].sort.should == [a, b, c, d]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,7 +13,7 @@ describe Vagrant::Environment do
|
|||
|
||||
let(:env) do
|
||||
isolated_environment.tap do |e|
|
||||
e.box2("base", :virtualbox)
|
||||
e.box3("base", "1.0", :virtualbox)
|
||||
e.vagrantfile <<-VF
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "base"
|
||||
|
@ -25,6 +25,100 @@ describe Vagrant::Environment do
|
|||
let(:instance) { env.create_vagrant_env }
|
||||
subject { instance }
|
||||
|
||||
describe "#home_path" do
|
||||
it "is set to the home path given" do
|
||||
Dir.mktmpdir do |dir|
|
||||
instance = described_class.new(:home_path => dir)
|
||||
instance.home_path.should == Pathname.new(dir)
|
||||
end
|
||||
end
|
||||
|
||||
it "is set to the environmental variable VAGRANT_HOME" do
|
||||
Dir.mktmpdir do |dir|
|
||||
instance = with_temp_env("VAGRANT_HOME" => dir) do
|
||||
described_class.new
|
||||
end
|
||||
|
||||
instance.home_path.should == Pathname.new(dir)
|
||||
end
|
||||
end
|
||||
|
||||
it "throws an exception if inaccessible" do
|
||||
expect {
|
||||
described_class.new(:home_path => "/")
|
||||
}.to raise_error(Vagrant::Errors::HomeDirectoryNotAccessible)
|
||||
end
|
||||
|
||||
context "with setup version file" do
|
||||
it "creates a setup version flie" do
|
||||
path = subject.home_path.join("setup_version")
|
||||
expect(path).to be_file
|
||||
expect(path.read).to eq(Vagrant::Environment::CURRENT_SETUP_VERSION)
|
||||
end
|
||||
|
||||
it "is okay if it has the current version" do
|
||||
Dir.mktmpdir do |dir|
|
||||
Pathname.new(dir).join("setup_version").open("w") do |f|
|
||||
f.write(Vagrant::Environment::CURRENT_SETUP_VERSION)
|
||||
end
|
||||
|
||||
instance = described_class.new(home_path: dir)
|
||||
path = instance.home_path.join("setup_version")
|
||||
expect(path).to be_file
|
||||
expect(path.read).to eq(Vagrant::Environment::CURRENT_SETUP_VERSION)
|
||||
end
|
||||
end
|
||||
|
||||
it "raises an exception if the version is newer than ours" do
|
||||
Dir.mktmpdir do |dir|
|
||||
Pathname.new(dir).join("setup_version").open("w") do |f|
|
||||
f.write("100.5")
|
||||
end
|
||||
|
||||
expect { described_class.new(home_path: dir) }.
|
||||
to raise_error(Vagrant::Errors::HomeDirectoryLaterVersion)
|
||||
end
|
||||
end
|
||||
|
||||
it "raises an exception if there is an unknown home directory version" do
|
||||
Dir.mktmpdir do |dir|
|
||||
Pathname.new(dir).join("setup_version").open("w") do |f|
|
||||
f.write("0.7")
|
||||
end
|
||||
|
||||
expect { described_class.new(home_path: dir) }.
|
||||
to raise_error(Vagrant::Errors::HomeDirectoryUnknownVersion)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "upgrading a v1.1 directory structure" do
|
||||
let(:env) { isolated_environment }
|
||||
|
||||
before do
|
||||
env.homedir.join("setup_version").open("w") do |f|
|
||||
f.write("1.1")
|
||||
end
|
||||
end
|
||||
|
||||
it "replaces the setup version with the new version" do
|
||||
expect(subject.home_path.join("setup_version").read).
|
||||
to eq(Vagrant::Environment::CURRENT_SETUP_VERSION)
|
||||
end
|
||||
|
||||
it "moves the boxes into the new directory structure" do
|
||||
# Kind of hacky but avoids two instantiations of BoxCollection
|
||||
Vagrant::Environment.any_instance.stub(boxes: double("boxes"))
|
||||
|
||||
collection = double("collection")
|
||||
Vagrant::BoxCollection.should_receive(:new).with(
|
||||
env.homedir.join("boxes"), anything).and_return(collection)
|
||||
collection.should_receive(:upgrade_v1_1_v1_5).once
|
||||
subject
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#host" do
|
||||
let(:plugin_hosts) { {} }
|
||||
let(:plugin_host_caps) { {} }
|
||||
|
@ -100,6 +194,334 @@ describe Vagrant::Environment do
|
|||
end
|
||||
end
|
||||
|
||||
describe "#machine" do
|
||||
# A helper to register a provider for use in tests.
|
||||
def register_provider(name, config_class=nil, options=nil)
|
||||
provider_cls = Class.new(Vagrant.plugin("2", :provider))
|
||||
|
||||
register_plugin("2") do |p|
|
||||
p.provider(name, options) { provider_cls }
|
||||
|
||||
if config_class
|
||||
p.config(name, :provider) { config_class }
|
||||
end
|
||||
end
|
||||
|
||||
provider_cls
|
||||
end
|
||||
|
||||
it "should return a machine object with the correct provider" do
|
||||
# Create a provider
|
||||
foo_provider = register_provider("foo")
|
||||
|
||||
# Create the configuration
|
||||
isolated_env = isolated_environment do |e|
|
||||
e.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "base"
|
||||
config.vm.define "foo"
|
||||
end
|
||||
VF
|
||||
|
||||
e.box3("base", "1.0", :foo)
|
||||
end
|
||||
|
||||
# Verify that we can get the machine
|
||||
env = isolated_env.create_vagrant_env
|
||||
machine = env.machine(:foo, :foo)
|
||||
machine.should be_kind_of(Vagrant::Machine)
|
||||
machine.name.should == :foo
|
||||
machine.provider.should be_kind_of(foo_provider)
|
||||
machine.provider_config.should be_nil
|
||||
end
|
||||
|
||||
it "should return a machine object with the machine configuration" do
|
||||
# Create a provider
|
||||
foo_config = Class.new(Vagrant.plugin("2", :config)) do
|
||||
attr_accessor :value
|
||||
end
|
||||
|
||||
foo_provider = register_provider("foo", foo_config)
|
||||
|
||||
# Create the configuration
|
||||
isolated_env = isolated_environment do |e|
|
||||
e.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "base"
|
||||
config.vm.define "foo"
|
||||
|
||||
config.vm.provider :foo do |fooconfig|
|
||||
fooconfig.value = 100
|
||||
end
|
||||
end
|
||||
VF
|
||||
|
||||
e.box3("base", "1.0", :foo)
|
||||
end
|
||||
|
||||
# Verify that we can get the machine
|
||||
env = isolated_env.create_vagrant_env
|
||||
machine = env.machine(:foo, :foo)
|
||||
machine.should be_kind_of(Vagrant::Machine)
|
||||
machine.name.should == :foo
|
||||
machine.provider.should be_kind_of(foo_provider)
|
||||
machine.provider_config.value.should == 100
|
||||
end
|
||||
|
||||
it "should cache the machine objects by name and provider" do
|
||||
# Create a provider
|
||||
foo_provider = register_provider("foo")
|
||||
bar_provider = register_provider("bar")
|
||||
|
||||
# Create the configuration
|
||||
isolated_env = isolated_environment do |e|
|
||||
e.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "base"
|
||||
config.vm.define "vm1"
|
||||
config.vm.define "vm2"
|
||||
end
|
||||
VF
|
||||
|
||||
e.box3("base", "1.0", :foo)
|
||||
e.box3("base", "1.0", :bar)
|
||||
end
|
||||
|
||||
env = isolated_env.create_vagrant_env
|
||||
vm1_foo = env.machine(:vm1, :foo)
|
||||
vm1_bar = env.machine(:vm1, :bar)
|
||||
vm2_foo = env.machine(:vm2, :foo)
|
||||
|
||||
vm1_foo.should eql(env.machine(:vm1, :foo))
|
||||
vm1_bar.should eql(env.machine(:vm1, :bar))
|
||||
vm1_foo.should_not eql(vm1_bar)
|
||||
vm2_foo.should eql(env.machine(:vm2, :foo))
|
||||
end
|
||||
|
||||
it "should load a machine without a box" do
|
||||
register_provider("foo")
|
||||
|
||||
environment = isolated_environment do |env|
|
||||
env.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "i-dont-exist"
|
||||
end
|
||||
VF
|
||||
end
|
||||
|
||||
env = environment.create_vagrant_env
|
||||
machine = env.machine(:default, :foo)
|
||||
machine.box.should be_nil
|
||||
end
|
||||
|
||||
it "should load the machine configuration" do
|
||||
register_provider("foo")
|
||||
|
||||
environment = isolated_environment do |env|
|
||||
env.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.ssh.port = 1
|
||||
config.vm.box = "base"
|
||||
|
||||
config.vm.define "vm1" do |inner|
|
||||
inner.ssh.port = 100
|
||||
end
|
||||
end
|
||||
VF
|
||||
|
||||
env.box3("base", "1.0", :foo)
|
||||
end
|
||||
|
||||
env = environment.create_vagrant_env
|
||||
machine = env.machine(:vm1, :foo)
|
||||
machine.config.ssh.port.should == 100
|
||||
machine.config.vm.box.should == "base"
|
||||
end
|
||||
|
||||
it "should load the box configuration for a box" do
|
||||
register_provider("foo")
|
||||
|
||||
environment = isolated_environment do |env|
|
||||
env.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "base"
|
||||
end
|
||||
VF
|
||||
|
||||
env.box3("base", "1.0", :foo, :vagrantfile => <<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.ssh.port = 100
|
||||
end
|
||||
VF
|
||||
end
|
||||
|
||||
env = environment.create_vagrant_env
|
||||
machine = env.machine(:default, :foo)
|
||||
machine.config.ssh.port.should == 100
|
||||
end
|
||||
|
||||
it "should load the box configuration for a box and custom Vagrantfile name" do
|
||||
register_provider("foo")
|
||||
|
||||
environment = isolated_environment do |env|
|
||||
env.file("some_other_name", <<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "base"
|
||||
end
|
||||
VF
|
||||
|
||||
env.box3("base", "1.0", :foo, :vagrantfile => <<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.ssh.port = 100
|
||||
end
|
||||
VF
|
||||
end
|
||||
|
||||
env = with_temp_env("VAGRANT_VAGRANTFILE" => "some_other_name") do
|
||||
environment.create_vagrant_env
|
||||
end
|
||||
|
||||
machine = env.machine(:default, :foo)
|
||||
machine.config.ssh.port.should == 100
|
||||
end
|
||||
|
||||
it "should load the box configuration for other formats for a box" do
|
||||
register_provider("foo", nil, box_format: "bar")
|
||||
|
||||
environment = isolated_environment do |env|
|
||||
env.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "base"
|
||||
end
|
||||
VF
|
||||
|
||||
env.box3("base", "1.0", :bar, :vagrantfile => <<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.ssh.port = 100
|
||||
end
|
||||
VF
|
||||
end
|
||||
|
||||
env = environment.create_vagrant_env
|
||||
machine = env.machine(:default, :foo)
|
||||
machine.config.ssh.port.should == 100
|
||||
end
|
||||
|
||||
it "prefer sooner formats when multiple box formats are available" do
|
||||
register_provider("foo", nil, box_format: ["fA", "fB"])
|
||||
|
||||
environment = isolated_environment do |env|
|
||||
env.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "base"
|
||||
end
|
||||
VF
|
||||
|
||||
env.box3("base", "1.0", :fA, :vagrantfile => <<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.ssh.port = 100
|
||||
end
|
||||
VF
|
||||
|
||||
env.box3("base", "1.0", :fB, :vagrantfile => <<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.ssh.port = 200
|
||||
end
|
||||
VF
|
||||
end
|
||||
|
||||
env = environment.create_vagrant_env
|
||||
machine = env.machine(:default, :foo)
|
||||
machine.config.ssh.port.should == 100
|
||||
end
|
||||
|
||||
it "should load the proper version of a box" do
|
||||
register_provider("foo")
|
||||
|
||||
environment = isolated_environment do |env|
|
||||
env.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "base"
|
||||
config.vm.box_version = "~> 1.2"
|
||||
end
|
||||
VF
|
||||
|
||||
env.box3("base", "1.0", :foo, :vagrantfile => <<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.ssh.port = 100
|
||||
end
|
||||
VF
|
||||
|
||||
env.box3("base", "1.5", :foo, :vagrantfile => <<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.ssh.port = 200
|
||||
end
|
||||
VF
|
||||
end
|
||||
|
||||
env = environment.create_vagrant_env
|
||||
machine = env.machine(:default, :foo)
|
||||
machine.config.ssh.port.should == 200
|
||||
end
|
||||
|
||||
it "should load the provider override if set" do
|
||||
register_provider("bar")
|
||||
register_provider("foo")
|
||||
|
||||
isolated_env = isolated_environment do |e|
|
||||
e.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "foo"
|
||||
|
||||
config.vm.provider :foo do |_, c|
|
||||
c.vm.box = "bar"
|
||||
end
|
||||
end
|
||||
VF
|
||||
end
|
||||
|
||||
env = isolated_env.create_vagrant_env
|
||||
foo_vm = env.machine(:default, :foo)
|
||||
bar_vm = env.machine(:default, :bar)
|
||||
foo_vm.config.vm.box.should == "bar"
|
||||
bar_vm.config.vm.box.should == "foo"
|
||||
end
|
||||
|
||||
it "should reload the cache if refresh is set" do
|
||||
# Create a provider
|
||||
foo_provider = register_provider("foo")
|
||||
|
||||
# Create the configuration
|
||||
isolated_env = isolated_environment do |e|
|
||||
e.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "base"
|
||||
end
|
||||
VF
|
||||
|
||||
e.box3("base", "1.0", :foo)
|
||||
end
|
||||
|
||||
env = isolated_env.create_vagrant_env
|
||||
vm1 = env.machine(:default, :foo)
|
||||
vm2 = env.machine(:default, :foo, true)
|
||||
vm3 = env.machine(:default, :foo)
|
||||
|
||||
vm1.should_not eql(vm2)
|
||||
vm2.should eql(vm3)
|
||||
end
|
||||
|
||||
it "should raise an error if the VM is not found" do
|
||||
expect { instance.machine("i-definitely-dont-exist", :virtualbox) }.
|
||||
to raise_error(Vagrant::Errors::MachineNotFound)
|
||||
end
|
||||
|
||||
it "should raise an error if the provider is not found" do
|
||||
expect { instance.machine(:default, :lol_no) }.
|
||||
to raise_error(Vagrant::Errors::ProviderNotFound)
|
||||
end
|
||||
end
|
||||
|
||||
describe "active machines" do
|
||||
it "should be empty if the machines folder doesn't exist" do
|
||||
folder = instance.local_data_path.join("machines")
|
||||
|
@ -211,60 +633,6 @@ describe Vagrant::Environment do
|
|||
end
|
||||
end
|
||||
|
||||
describe "home path" do
|
||||
it "is set to the home path given" do
|
||||
Dir.mktmpdir do |dir|
|
||||
instance = described_class.new(:home_path => dir)
|
||||
instance.home_path.should == Pathname.new(dir)
|
||||
end
|
||||
end
|
||||
|
||||
it "is set to the environmental variable VAGRANT_HOME" do
|
||||
Dir.mktmpdir do |dir|
|
||||
instance = with_temp_env("VAGRANT_HOME" => dir) do
|
||||
described_class.new
|
||||
end
|
||||
|
||||
instance.home_path.should == Pathname.new(dir)
|
||||
end
|
||||
end
|
||||
|
||||
context "default home path" do
|
||||
it "is set to '~/.vagrant.d' by default" do
|
||||
expected = Vagrant::Util::Platform.fs_real_path("~/.vagrant.d")
|
||||
described_class.new.home_path.should == expected
|
||||
end
|
||||
|
||||
it "is set to '~/.vagrant.d' if on Windows but no USERPROFILE" do
|
||||
Vagrant::Util::Platform.stub(:windows? => true)
|
||||
|
||||
expected = Vagrant::Util::Platform.fs_real_path("~/.vagrant.d")
|
||||
|
||||
with_temp_env("USERPROFILE" => nil) do
|
||||
described_class.new.home_path.should == expected
|
||||
end
|
||||
end
|
||||
|
||||
it "is set to '%USERPROFILE%/.vagrant.d' if on Windows and USERPROFILE is set" do
|
||||
Vagrant::Util::Platform.stub(:windows? => true)
|
||||
|
||||
Dir.mktmpdir do |dir|
|
||||
expected = Vagrant::Util::Platform.fs_real_path("#{dir}/.vagrant.d")
|
||||
|
||||
with_temp_env("USERPROFILE" => dir) do
|
||||
described_class.new.home_path.should == expected
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "throws an exception if inaccessible" do
|
||||
expect {
|
||||
described_class.new(:home_path => "/")
|
||||
}.to raise_error(Vagrant::Errors::HomeDirectoryNotAccessible)
|
||||
end
|
||||
end
|
||||
|
||||
describe "local data path" do
|
||||
it "is set to the proper default" do
|
||||
default = instance.root_path.join(described_class::DEFAULT_LOCAL_DATA)
|
||||
|
@ -429,7 +797,7 @@ Vagrant.configure("2") do |config|
|
|||
end
|
||||
VF
|
||||
|
||||
env.box2("base", :virtualbox)
|
||||
env.box3("base", "1.0", :virtualbox)
|
||||
end
|
||||
|
||||
env = environment.create_vagrant_env
|
||||
|
@ -446,7 +814,7 @@ Vagrant.configure("2") do |config|
|
|||
end
|
||||
VF
|
||||
|
||||
env.box2("base", :virtualbox)
|
||||
env.box3("base", "1.0", :virtualbox)
|
||||
end
|
||||
|
||||
env = environment.create_vagrant_env
|
||||
|
@ -519,305 +887,6 @@ VF
|
|||
end
|
||||
end
|
||||
|
||||
describe "getting a machine" do
|
||||
# A helper to register a provider for use in tests.
|
||||
def register_provider(name, config_class=nil, options=nil)
|
||||
provider_cls = Class.new(Vagrant.plugin("2", :provider))
|
||||
|
||||
register_plugin("2") do |p|
|
||||
p.provider(name, options) { provider_cls }
|
||||
|
||||
if config_class
|
||||
p.config(name, :provider) { config_class }
|
||||
end
|
||||
end
|
||||
|
||||
provider_cls
|
||||
end
|
||||
|
||||
it "should return a machine object with the correct provider" do
|
||||
# Create a provider
|
||||
foo_provider = register_provider("foo")
|
||||
|
||||
# Create the configuration
|
||||
isolated_env = isolated_environment do |e|
|
||||
e.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "base"
|
||||
config.vm.define "foo"
|
||||
end
|
||||
VF
|
||||
|
||||
e.box2("base", :foo)
|
||||
end
|
||||
|
||||
# Verify that we can get the machine
|
||||
env = isolated_env.create_vagrant_env
|
||||
machine = env.machine(:foo, :foo)
|
||||
machine.should be_kind_of(Vagrant::Machine)
|
||||
machine.name.should == :foo
|
||||
machine.provider.should be_kind_of(foo_provider)
|
||||
machine.provider_config.should be_nil
|
||||
end
|
||||
|
||||
it "should return a machine object with the machine configuration" do
|
||||
# Create a provider
|
||||
foo_config = Class.new(Vagrant.plugin("2", :config)) do
|
||||
attr_accessor :value
|
||||
end
|
||||
|
||||
foo_provider = register_provider("foo", foo_config)
|
||||
|
||||
# Create the configuration
|
||||
isolated_env = isolated_environment do |e|
|
||||
e.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "base"
|
||||
config.vm.define "foo"
|
||||
|
||||
config.vm.provider :foo do |fooconfig|
|
||||
fooconfig.value = 100
|
||||
end
|
||||
end
|
||||
VF
|
||||
|
||||
e.box2("base", :foo)
|
||||
end
|
||||
|
||||
# Verify that we can get the machine
|
||||
env = isolated_env.create_vagrant_env
|
||||
machine = env.machine(:foo, :foo)
|
||||
machine.should be_kind_of(Vagrant::Machine)
|
||||
machine.name.should == :foo
|
||||
machine.provider.should be_kind_of(foo_provider)
|
||||
machine.provider_config.value.should == 100
|
||||
end
|
||||
|
||||
it "should cache the machine objects by name and provider" do
|
||||
# Create a provider
|
||||
foo_provider = register_provider("foo")
|
||||
bar_provider = register_provider("bar")
|
||||
|
||||
# Create the configuration
|
||||
isolated_env = isolated_environment do |e|
|
||||
e.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "base"
|
||||
config.vm.define "vm1"
|
||||
config.vm.define "vm2"
|
||||
end
|
||||
VF
|
||||
|
||||
e.box2("base", :foo)
|
||||
e.box2("base", :bar)
|
||||
end
|
||||
|
||||
env = isolated_env.create_vagrant_env
|
||||
vm1_foo = env.machine(:vm1, :foo)
|
||||
vm1_bar = env.machine(:vm1, :bar)
|
||||
vm2_foo = env.machine(:vm2, :foo)
|
||||
|
||||
vm1_foo.should eql(env.machine(:vm1, :foo))
|
||||
vm1_bar.should eql(env.machine(:vm1, :bar))
|
||||
vm1_foo.should_not eql(vm1_bar)
|
||||
vm2_foo.should eql(env.machine(:vm2, :foo))
|
||||
end
|
||||
|
||||
it "should load a machine without a box" do
|
||||
register_provider("foo")
|
||||
|
||||
environment = isolated_environment do |env|
|
||||
env.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "i-dont-exist"
|
||||
end
|
||||
VF
|
||||
end
|
||||
|
||||
env = environment.create_vagrant_env
|
||||
machine = env.machine(:default, :foo)
|
||||
machine.box.should be_nil
|
||||
end
|
||||
|
||||
it "should load the machine configuration" do
|
||||
register_provider("foo")
|
||||
|
||||
environment = isolated_environment do |env|
|
||||
env.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.ssh.port = 1
|
||||
config.vm.box = "base"
|
||||
|
||||
config.vm.define "vm1" do |inner|
|
||||
inner.ssh.port = 100
|
||||
end
|
||||
end
|
||||
VF
|
||||
|
||||
env.box2("base", :foo)
|
||||
end
|
||||
|
||||
env = environment.create_vagrant_env
|
||||
machine = env.machine(:vm1, :foo)
|
||||
machine.config.ssh.port.should == 100
|
||||
machine.config.vm.box.should == "base"
|
||||
end
|
||||
|
||||
it "should load the box configuration for a V2 box" do
|
||||
register_provider("foo")
|
||||
|
||||
environment = isolated_environment do |env|
|
||||
env.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "base"
|
||||
end
|
||||
VF
|
||||
|
||||
env.box2("base", :foo, :vagrantfile => <<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.ssh.port = 100
|
||||
end
|
||||
VF
|
||||
end
|
||||
|
||||
env = environment.create_vagrant_env
|
||||
machine = env.machine(:default, :foo)
|
||||
machine.config.ssh.port.should == 100
|
||||
end
|
||||
|
||||
it "should load the box configuration for a V2 box and custom Vagrantfile name" do
|
||||
register_provider("foo")
|
||||
|
||||
environment = isolated_environment do |env|
|
||||
env.file("some_other_name", <<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "base"
|
||||
end
|
||||
VF
|
||||
|
||||
env.box2("base", :foo, :vagrantfile => <<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.ssh.port = 100
|
||||
end
|
||||
VF
|
||||
end
|
||||
|
||||
env = with_temp_env("VAGRANT_VAGRANTFILE" => "some_other_name") do
|
||||
environment.create_vagrant_env
|
||||
end
|
||||
|
||||
machine = env.machine(:default, :foo)
|
||||
machine.config.ssh.port.should == 100
|
||||
end
|
||||
|
||||
it "should load the box configuration for other formats for a V2 box" do
|
||||
register_provider("foo", nil, box_format: "bar")
|
||||
|
||||
environment = isolated_environment do |env|
|
||||
env.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "base"
|
||||
end
|
||||
VF
|
||||
|
||||
env.box2("base", :bar, :vagrantfile => <<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.ssh.port = 100
|
||||
end
|
||||
VF
|
||||
end
|
||||
|
||||
env = environment.create_vagrant_env
|
||||
machine = env.machine(:default, :foo)
|
||||
machine.config.ssh.port.should == 100
|
||||
end
|
||||
|
||||
it "prefer sooner formats when multiple box formats are available" do
|
||||
register_provider("foo", nil, box_format: ["fA", "fB"])
|
||||
|
||||
environment = isolated_environment do |env|
|
||||
env.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "base"
|
||||
end
|
||||
VF
|
||||
|
||||
env.box2("base", :fA, :vagrantfile => <<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.ssh.port = 100
|
||||
end
|
||||
VF
|
||||
|
||||
env.box2("base", :fB, :vagrantfile => <<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.ssh.port = 200
|
||||
end
|
||||
VF
|
||||
end
|
||||
|
||||
env = environment.create_vagrant_env
|
||||
machine = env.machine(:default, :foo)
|
||||
machine.config.ssh.port.should == 100
|
||||
end
|
||||
|
||||
it "should load the provider override if set" do
|
||||
register_provider("bar")
|
||||
register_provider("foo")
|
||||
|
||||
isolated_env = isolated_environment do |e|
|
||||
e.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "foo"
|
||||
|
||||
config.vm.provider :foo do |_, c|
|
||||
c.vm.box = "bar"
|
||||
end
|
||||
end
|
||||
VF
|
||||
end
|
||||
|
||||
env = isolated_env.create_vagrant_env
|
||||
foo_vm = env.machine(:default, :foo)
|
||||
bar_vm = env.machine(:default, :bar)
|
||||
foo_vm.config.vm.box.should == "bar"
|
||||
bar_vm.config.vm.box.should == "foo"
|
||||
end
|
||||
|
||||
it "should reload the cache if refresh is set" do
|
||||
# Create a provider
|
||||
foo_provider = register_provider("foo")
|
||||
|
||||
# Create the configuration
|
||||
isolated_env = isolated_environment do |e|
|
||||
e.vagrantfile(<<-VF)
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "base"
|
||||
end
|
||||
VF
|
||||
|
||||
e.box2("base", :foo)
|
||||
end
|
||||
|
||||
env = isolated_env.create_vagrant_env
|
||||
vm1 = env.machine(:default, :foo)
|
||||
vm2 = env.machine(:default, :foo, true)
|
||||
vm3 = env.machine(:default, :foo)
|
||||
|
||||
vm1.should_not eql(vm2)
|
||||
vm2.should eql(vm3)
|
||||
end
|
||||
|
||||
it "should raise an error if the VM is not found" do
|
||||
expect { instance.machine("i-definitely-dont-exist", :virtualbox) }.
|
||||
to raise_error(Vagrant::Errors::MachineNotFound)
|
||||
end
|
||||
|
||||
it "should raise an error if the provider is not found" do
|
||||
expect { instance.machine(:default, :lol_no) }.
|
||||
to raise_error(Vagrant::Errors::ProviderNotFound)
|
||||
end
|
||||
end
|
||||
|
||||
describe "getting machine names" do
|
||||
it "should return the default machine if no multi-VM is used" do
|
||||
# Create the config
|
||||
|
|
|
@ -22,6 +22,21 @@ describe Vagrant do
|
|||
end
|
||||
end
|
||||
|
||||
describe "#server_url" do
|
||||
it "defaults to the default value" do
|
||||
with_temp_env("VAGRANT_SERVER_URL" => nil) do
|
||||
expect(subject.server_url).to eq(
|
||||
Vagrant::DEFAULT_SERVER_URL)
|
||||
end
|
||||
end
|
||||
|
||||
it "is the VAGRANT_SERVER_URL value" do
|
||||
with_temp_env("VAGRANT_SERVER_URL" => "foo") do
|
||||
expect(subject.server_url).to eq("foo")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#user_data_path" do
|
||||
around do |example|
|
||||
env = {
|
||||
|
|
|
@ -65,7 +65,7 @@ describe Vagrant::UI::Colored do
|
|||
|
||||
describe "#detail" do
|
||||
it "colors output nothing by default" do
|
||||
subject.should_receive(:safe_puts).with("foo", anything)
|
||||
subject.should_receive(:safe_puts).with("\033[0mfoo\033[0m", anything)
|
||||
subject.detail("foo")
|
||||
end
|
||||
|
||||
|
@ -91,11 +91,16 @@ describe Vagrant::UI::Colored do
|
|||
end
|
||||
|
||||
describe "#output" do
|
||||
it "colors output nothing by default" do
|
||||
subject.should_receive(:safe_puts).with("foo", anything)
|
||||
it "colors output nothing by default, no bold" do
|
||||
subject.should_receive(:safe_puts).with("\033[0mfoo\033[0m", anything)
|
||||
subject.output("foo")
|
||||
end
|
||||
|
||||
it "bolds output without color if specified" do
|
||||
subject.should_receive(:safe_puts).with("\033[1mfoo\033[0m", anything)
|
||||
subject.output("foo", bold: true)
|
||||
end
|
||||
|
||||
it "colors output to color specified in global opts" do
|
||||
subject.opts[:color] = :red
|
||||
|
||||
|
@ -232,6 +237,13 @@ describe Vagrant::UI::Prefixed do
|
|||
|
||||
subject { described_class.new(ui, prefix) }
|
||||
|
||||
describe "#ask" do
|
||||
it "does not request bolding" do
|
||||
ui.should_receive(:ask).with(" #{prefix}: foo", bold: false)
|
||||
subject.ask("foo")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#detail" do
|
||||
it "prefixes with spaces and the message" do
|
||||
ui.should_receive(:safe_puts).with(" #{prefix}: foo", anything)
|
||||
|
|
|
@ -54,4 +54,22 @@ describe Vagrant::Util::Downloader do
|
|||
pending "tests for a UI"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#head" do
|
||||
let(:curl_options) {
|
||||
["--fail", "--location", "--max-redirs", "10", "--user-agent", described_class::USER_AGENT, source, {}]
|
||||
}
|
||||
|
||||
it "returns the output" do
|
||||
subprocess_result.stub(stdout: "foo")
|
||||
|
||||
options = curl_options.dup
|
||||
options.unshift("-I")
|
||||
|
||||
Vagrant::Util::Subprocess.should_receive(:execute).
|
||||
with("curl", *options).and_return(subprocess_result)
|
||||
|
||||
expect(subject.head).to eq("foo")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
|
||||
VAGRANTFILE_API_VERSION = "2"
|
||||
|
||||
$script = <<SCRIPT
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install curl
|
||||
curl -sSL https://get.rvm.io | bash -s stable
|
||||
. ~/.bashrc
|
||||
. ~/.bash_profile
|
||||
rvm install 2.0.0
|
||||
rvm --default use 2.0.0
|
||||
cd /vagrant
|
||||
bundle
|
||||
SCRIPT
|
||||
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
config.vm.box = "precise64"
|
||||
config.vm.network "private_network", ip: "33.33.33.10"
|
||||
config.vm.provision "shell", inline: $script, privileged: false
|
||||
end
|
|
@ -139,6 +139,7 @@
|
|||
<li<%= sidebar_current("boxes") %>><a href="/v2/boxes.html">Boxes</a></li>
|
||||
<% if sidebar_section == "boxes" %>
|
||||
<ul class="sub unstyled">
|
||||
<li<%= sidebar_current("boxes-versioning") %>><a href="/v2/boxes/versioning.html">Box Versioning</a></li>
|
||||
<li<%= sidebar_current("boxes-base") %>><a href="/v2/boxes/base.html">Creating a Base Box</a></li>
|
||||
<li<%= sidebar_current("boxes-format") %>><a href="/v2/boxes/format.html">Box File Format</a></li>
|
||||
</ul>
|
||||
|
|
|
@ -87,7 +87,7 @@ h6 {
|
|||
}
|
||||
|
||||
p, td {
|
||||
letter-spacing: 0.05em;
|
||||
letter-spacing: normal;
|
||||
line-height: 32px;
|
||||
|
||||
a {
|
||||
|
|
|
@ -5,74 +5,44 @@ sidebar_current: "boxes"
|
|||
|
||||
# Boxes
|
||||
|
||||
Boxes are the skeleton from which Vagrant machines are constructed. They are
|
||||
portable files which can be used by others on any platform that runs Vagrant
|
||||
to bring up a working environment.
|
||||
Boxes are the package format for Vagrant environments. A box can be used by
|
||||
anyone on any platform that Vagrant supports to bring up an identical
|
||||
working environment.
|
||||
|
||||
The `vagrant box` utility provides all the functionality for managing
|
||||
boxes. Boxes must currently be created manually.
|
||||
boxes. You can read the documentation on the [vagrant box](/v2/cli/box.html)
|
||||
command for more information.
|
||||
|
||||
Boxes are [provider-specific](/v2/providers/index.html), so you must obtain
|
||||
the proper box depending on what provider you're using.
|
||||
The easiest way to use a box is to add a box from the
|
||||
[publicly available catalog of Vagrant boxes](http://www.vagrantcloud.com).
|
||||
You can also add and share your own customized boxes on this website.
|
||||
|
||||
If you're interested in more details on the exact file format of
|
||||
boxes, there is a separate [page dedicated to that](/v2/boxes/format.html), since
|
||||
it is an advanced topic that general users don't need to know.
|
||||
Boxes also support versioning so that members of your team using Vagrant
|
||||
can update the underlying box easily, and the people who create boxes
|
||||
can push fixes and communicate these fixes efficiently.
|
||||
|
||||
## Adding Boxes
|
||||
You can learn all about boxes by reading this page as well as the
|
||||
sub-pages in the navigation to the left.
|
||||
|
||||
Adding boxes is straightforward:
|
||||
## Discovering Boxes
|
||||
|
||||
The easiest way to find boxes is to look on the
|
||||
[public Vagrant box catalog](http://www.vagrantcloud.com)
|
||||
for a box matching your use case. The catalog contains most major operating
|
||||
systems as bases, as well as specialized boxes to get you up and running
|
||||
quickly with LAMP stacks, Ruby, Python, etc.
|
||||
|
||||
The boxes on the public catalog work with many different
|
||||
[providers](/v2/providers/index.html). Whether you're using Vagrant with
|
||||
VirtualBox, VMware, AWS, etc. you should be able to find a box you need.
|
||||
|
||||
Adding a box from the catalog is very easy. Each box shows you instructions
|
||||
with how to add it, but they all follow the same format:
|
||||
|
||||
```
|
||||
$ vagrant box add name url
|
||||
$ vagrant box add USER/BOX
|
||||
...
|
||||
```
|
||||
|
||||
`name` is a logical name by which the box is referenced from the
|
||||
Vagrantfile. You can put anything you want here, but know that Vagrant
|
||||
matches the `config.vm.box` directive with this name in order to look up
|
||||
the box to use.
|
||||
|
||||
`url` is the location of the box. This can be a path to your local filesystem
|
||||
or an HTTP URL to the box remotely.
|
||||
|
||||
Vagrant will automatically determine the provider the box was built
|
||||
for by reading the "metadata.json" file within the box archive. You
|
||||
may also tell Vagrant what provider the box is for by specifying the
|
||||
`--provider` flag.
|
||||
|
||||
This is recommended as it adds an extra level of verification
|
||||
to the box you're downloading. Additionally, Vagrant can exit early with
|
||||
an error if a box with that name and provider already exists, whereas
|
||||
it must download the entire box before showing such an error in the other
|
||||
case.
|
||||
|
||||
Multiple boxes with the same name can exist as long as they are all
|
||||
for different providers. The example of listing boxes below shows this,
|
||||
where there are multiple precise64 boxes, backed by different providers.
|
||||
This lets a single `config.vm.box` configuration within a Vagrantfile
|
||||
properly reference boxes across providers.
|
||||
|
||||
## Listing Boxes
|
||||
|
||||
To view what boxes Vagrant has locally installed, use `vagrant box list`:
|
||||
|
||||
```
|
||||
$ vagrant box list
|
||||
precise64 (virtualbox)
|
||||
precise64 (vmware_fusion)
|
||||
```
|
||||
|
||||
Vagrant lists all boxes along with the providers the box is for in parentheses.
|
||||
|
||||
## Removing Boxes
|
||||
|
||||
Boxes can be removed just as easily as they are added:
|
||||
|
||||
```
|
||||
$ vagrant box remove precise64 virtualbox
|
||||
```
|
||||
|
||||
The two arguments are the logical name of the box and the provider of the
|
||||
box. The second argument (the provider) is optional. If you have only a single
|
||||
provider backing that box, it doesn't need to be specified. If you have multiple
|
||||
providers backing a box and it isn't specified, Vagrant will show an error.
|
||||
For example: `vagrant box add hashicorp/precise64`. You can also quickly
|
||||
initialize a Vagrant environment with `vagrant init hashicorp/precise64`.
|
||||
|
|
|
@ -180,6 +180,15 @@ Packaging the box into a `box` file is provider-specific. Please refer to
|
|||
the provider-specific documentation for creating a base box. Some
|
||||
provider-specific guides are linked to towards the top of this page.
|
||||
|
||||
## Distributing the Box
|
||||
|
||||
You can distribute the box file however you'd like. However, if you want
|
||||
to support versioning, putting multiple providers at a single URL, pushing
|
||||
updates, analytics, and more, we recommend you add the box to
|
||||
[Vagrant Cloud](http://www.vagrantcloud.com).
|
||||
|
||||
You can upload both public and private boxes to this service.
|
||||
|
||||
## Testing the Box
|
||||
|
||||
To test the box, pretend you're a new user of Vagrant and give it a shot:
|
||||
|
|
|
@ -6,21 +6,82 @@ sidebar_current: "boxes-format"
|
|||
# Box File Format
|
||||
|
||||
In the past, boxes were just [tar files](http://en.wikipedia.org/wiki/Tar_\(computing\))
|
||||
of VirtualBox exports. With Vagrant supporting multiple providers, box files
|
||||
are now tar files where the contents differ for each provider. They are
|
||||
still tar files, but they may now optionally be [gzipped](http://en.wikipedia.org/wiki/Gzip)
|
||||
as well.
|
||||
of VirtualBox exports. With Vagrant supporting multiple
|
||||
[providers](/v2/providers/index.html) and [versioning](/v2/boxes/versioning.html)
|
||||
now, box files are slightly more complicated.
|
||||
|
||||
Box files made for Vagrant 1.0.x and VirtualBox continue to work with
|
||||
Vagrant 1.1+ and the VirtualBox provider.
|
||||
Box files made for Vagrant 1.0.x (the VirtualBox export tar files) continue
|
||||
to work with Vagrant today. When Vagrant encounters one of these old boxes,
|
||||
it automatically updates it internally to the new format.
|
||||
|
||||
The only file required within a box is a "metadata.json" file. This is
|
||||
a [JSON](http://www.json.org/) file that has a top-level object that
|
||||
can contain any metadata about the box. Vagrant requires that a "provider"
|
||||
key exists in this metadata with the name of the provider the box is made
|
||||
for.
|
||||
Today, box files are split into two different components:
|
||||
|
||||
The "metadata.json" file for a VirtualBox box:
|
||||
* Box Metadata - This is a JSON document that specifies the name of the box,
|
||||
a description, available versions, available providers, and URLs to the
|
||||
actual box files (next component) for each provider and version. If this
|
||||
metadata doesn't exist, a box file can still be added directly, but it
|
||||
will not support versioning and updating.
|
||||
|
||||
* Box File - This is a compressed (tar, tar.gz, zip) file that is specific
|
||||
to a single provider and can contain anything. Vagrant core doesn't ever
|
||||
use the contents of this file. Instead, they are passed to the provider.
|
||||
Therefore, a VirtualBox box file has different contents from a VMware
|
||||
box file and so on.
|
||||
|
||||
Each component is covered in more detail below.
|
||||
|
||||
## Box Metadata
|
||||
|
||||
The metadata is an optional component for a box (but highly recommended)
|
||||
that enables [versioning](/v2/boxes/versioning.html), updating, multiple
|
||||
providers from a single file, and more.
|
||||
|
||||
<div class="alert alert-block alert-info">
|
||||
<strong>You don't need to manually make the metadata.</strong> If you
|
||||
have an account with <a href="#">Vagrant Cloud</a>, you
|
||||
can create boxes there, and Vagrant Cloud automatically creates
|
||||
the metadata for you. The format is still documented here.
|
||||
</div>
|
||||
|
||||
It is a JSON document, structured in the following way:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "hashicorp/precise64",
|
||||
"description": "This box contains Ubuntu 12.04 LTS 64-bit.",
|
||||
"versions": [{
|
||||
"version": "0.1.0",
|
||||
"providers": [{
|
||||
"name": "virtualbox",
|
||||
"url": "http://somewhere.s3.com/precise64_010_virtualbox.box"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
As you can see, the JSON document can describe multiple versions of a box,
|
||||
multiple providers, and can add/remove providers in different versions.
|
||||
|
||||
This JSON file can be passed directly to `vagrant box add` and Vagrant
|
||||
will install the proper version of the box. If multiple providers are
|
||||
available, Vagrant will ask what provider you want to use.
|
||||
|
||||
## Box File
|
||||
|
||||
The actual box file is the required portion for Vagrant. It is recommended
|
||||
you always use a metadata file alongside a box file, but direct box files
|
||||
are supported for legacy reasons in Vagrant.
|
||||
|
||||
Box files are compressed using tar, tar.gz, or zip. The contents of the
|
||||
archive can be anything, and is specific to each
|
||||
[provider](/v2/providers/index.html). Vagrant core itself only unpacks
|
||||
the boxes for use later.
|
||||
|
||||
Within the archive, Vagrant does expect a single file: "metadata.json".
|
||||
This is a JSON file that is completely unrelated to the above "box metadata"
|
||||
component. This file must contain at least the "provider" key with the
|
||||
provider the box is for. For example, if your box was for VirtualBox,
|
||||
the metadata.json would look like this:
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
---
|
||||
page_title: "Box Versioning"
|
||||
sidebar_current: "boxes-versioning"
|
||||
---
|
||||
|
||||
# Box Versioning
|
||||
|
||||
Since Vagrant 1.5, boxes support versioning. This allows the people who
|
||||
make boxes to push updates to the box, and the people who use the box
|
||||
have a simple workflow for checking for updates, updating their boxes,
|
||||
and seeing what has changed.
|
||||
|
||||
If you're just getting started with Vagrant, box versioning isn't too
|
||||
important, and we recommend learning about some other topics first. But
|
||||
if you're using Vagrant on a team or plan on creating your own boxes,
|
||||
versioning is very important. Luckily, having versioning built right in
|
||||
to Vagrant makes it easy to use and fit nicely into the Vagrant workflow.
|
||||
|
||||
This page will cover how to use versioned boxes. It does _not_ cover how
|
||||
to update your own custom boxes with versions. That is covered in
|
||||
[creating a base box](/v2/boxes/base.html).
|
||||
|
||||
## Viewing Versions and Updating
|
||||
|
||||
You can view the versions a box has by calling `vagrant box info`. This
|
||||
command will refresh the metadata associated with a box and list all
|
||||
available versions for this box. `vagrant box list` only shows _installed_
|
||||
versions of boxes.
|
||||
|
||||
You can check if the box you're using is outdated with `vagrant box outdated`.
|
||||
This can check if the box in your current Vagrant environment is outdated
|
||||
as well as any other box installed on the system.
|
||||
|
||||
Finally, you can update boxes with `vagrant box update`. This will download
|
||||
and install the new box. This _will not_ magically update running Vagrant
|
||||
environments. If a Vagrant environment is already running, you'll have to
|
||||
destroy and recreate it to acquire the new updates in the box. The update
|
||||
command just downloads these updates locally.
|
||||
|
||||
## Version Constraints
|
||||
|
||||
You can constrain a Vagrant environment to a specific version or versions
|
||||
of a box using the [Vagrantfile](/v2/vagrantfile/index.html) by specifying
|
||||
the `config.vm.box_version` option.
|
||||
|
||||
If this option is not specified, the latest version is always used. This is
|
||||
equivalent to specifying a constraint of ">= 0".
|
||||
|
||||
The box version configuration can be a specific version or a constraint of
|
||||
versions. Constraints can be any combination of the following:
|
||||
`= X`, `> X`, `< X`, `>= X`, `<= X`, `~> X`. You can combine multiple
|
||||
constraints by separating them with commas. All the constraints should be
|
||||
self explanatory except perhaps for `~>`, known as the "pessimistic constraint".
|
||||
Examples explain it best: `~> 1.0` is equivalent to `>= 1.0, < 2.0`. And
|
||||
`~> 1.1.5` is equivalent to `>= 1.1.5, < 1.2.0`.
|
||||
|
||||
You can choose to handle versions however you see fit. However, many boxes
|
||||
in the public catalog follow [semantic versioning](http://semver.org/).
|
||||
Basically, only the first number (the "major version") breaks backwards
|
||||
compatibility. In terms of Vagrant boxes, this means that any software that
|
||||
runs in version "1.1.5" of a box should work in "1.2" and "1.4.5" and so on,
|
||||
but "2.0" might introduce big changes that break your software. By following
|
||||
this convention, the best constraint is `~> 1.0` because you know it is safe
|
||||
no matter what version is in that range.
|
||||
|
||||
Of course, you're free to use versions however you'd like!
|
||||
|
||||
## Automatic Update Checking
|
||||
|
||||
Using the [Vagrantfile](/v2/vagrantfile/index.html), you can also configure
|
||||
Vagrant to automatically check for updates during any `vagrant up`. This is
|
||||
disabled by default, but can easily be enabled with
|
||||
`config.vm.box_check_update = true` in your Vagrantfile.
|
||||
|
||||
When this is enabled, Vagrant will check for updates on every `vagrant up`,
|
||||
not just when the machine is being created from scratch, but also when it
|
||||
is resuming, starting after being halted, etc.
|
||||
|
||||
If an update is found, Vagrant will output a warning to the user letting
|
||||
them know an update is available. That user can choose to ignore the warning
|
||||
for now, or can update the box by running `vagrant box update`.
|
||||
|
||||
Vagrant can not and does not automatically download the updated box and
|
||||
update the machine because boxes can be relatively large and updating the
|
||||
machine requires destroying it and recreating it, which can cause important
|
||||
data to be lost. Therefore, this process is manual to the extent that the
|
||||
user has to manually enter a command to do it.
|
||||
|
||||
## Pruning Old Versions
|
||||
|
||||
Vagrant does not automatically prune old versions because it doesn't know
|
||||
if they might be in use by other Vagrant environments. Because boxes can
|
||||
be large, you may want to actively prune them once in awhile using
|
||||
`vagrant box remove`. You can see all the boxes that are installed
|
||||
using `vagrant box list`.
|
|
@ -13,23 +13,28 @@ The main functionality of this command is exposed via even more subcommands:
|
|||
|
||||
* `add`
|
||||
* `list`
|
||||
* `outdated`
|
||||
* `remove`
|
||||
* `repackage`
|
||||
* `update`
|
||||
|
||||
# Box Add
|
||||
|
||||
**Command: `vagrant box add NAME URL`**
|
||||
**Command: `vagrant box add ADDRESS`**
|
||||
|
||||
This adds a box at the given URL to Vagrant and stores it under the
|
||||
logical name `NAME`.
|
||||
This adds a box with the given address to Vagrant. The address can be
|
||||
one of three things:
|
||||
|
||||
The URL may be a file path or an HTTP URL. For HTTP, basic authentication
|
||||
is supported and `http_proxy` environmental variables are respected. HTTPS
|
||||
is also supported.
|
||||
* A shorthand name from the
|
||||
[public catalog of available Vagrant images](http://www.vagrantcloud.com),
|
||||
such as "hashicorp/precise64".
|
||||
|
||||
The name argument of this command is a _logical name_, meaning you can
|
||||
effectively choose whatever you want. This is the name that Vagrant searches
|
||||
for to match with the `config.vm.box` setting in Vagrantfiles.
|
||||
* File path or HTTP URL to a box in a [catalog](http://www.vagrantcloud.com).
|
||||
For HTTP, basic authentication is supported and `http_proxy` environmental
|
||||
variables are respected. HTTPS is also supported.
|
||||
|
||||
* URL directly a box file. In this case, you must specify a `--name` flag
|
||||
(see below) and versioning/updates won't work.
|
||||
|
||||
If an error occurs during the download or the download is interrupted with
|
||||
a Ctrl-C, then Vagrant will attempt to resume the download the next time it
|
||||
|
@ -38,21 +43,17 @@ after the initial download.
|
|||
|
||||
## Options
|
||||
|
||||
* `--box-version VALUE` - The version of the box you want to add. By default,
|
||||
the latest version will be added. The value of this can be an exact version
|
||||
number such as "1.2.3" or it can be a set of version constraints. A version
|
||||
constraint looks like ">= 1.0, < 2.0".
|
||||
|
||||
* `--cacert CERTFILE` - The certificate for the CA used to verify the peer.
|
||||
This should be used if the remote end doesn't use a standard root CA.
|
||||
|
||||
* `--cert CERTFILE` - A client certificate to use when downloading the box, if
|
||||
necessary.
|
||||
|
||||
* `--checksum VALUE` - A checksum for the box that is downloaded. If specified,
|
||||
Vagrant will compare this checksum to what is actually downloaded and will
|
||||
error if the checksums do not match. This is highly recommended since
|
||||
box files are so large. If this is specified, `--checksum-type` must
|
||||
also be specified.
|
||||
|
||||
* `--checksum-type TYPE` - The type of checksum that `--checksum` is if it
|
||||
is specified. Supported values are currently "md5", "sha1", and "sha256".
|
||||
|
||||
* `--clean` - If given, Vagrant will remove any old temporary files from
|
||||
prior downloads of the same URL. This is useful if you don't want Vagrant
|
||||
to resume a download from a previous point, perhaps because the contents
|
||||
|
@ -68,23 +69,66 @@ after the initial download.
|
|||
adding is for the given provider. By default, Vagrant automatically
|
||||
detects the proper provider to use.
|
||||
|
||||
## Options for direct box files
|
||||
|
||||
The options below only apply if you're adding a box file directly (when
|
||||
you're not using a catalog).
|
||||
|
||||
* `--checksum VALUE` - A checksum for the box that is downloaded. If specified,
|
||||
Vagrant will compare this checksum to what is actually downloaded and will
|
||||
error if the checksums do not match. This is highly recommended since
|
||||
box files are so large. If this is specified, `--checksum-type` must
|
||||
also be specified. If you're downloading from a catalog, the checksum is
|
||||
included within the catalog entry.
|
||||
|
||||
* `--checksum-type TYPE` - The type of checksum that `--checksum` is if it
|
||||
is specified. Supported values are currently "md5", "sha1", and "sha256".
|
||||
|
||||
* `--name VALUE` - Logical name for the box. This is the value that you
|
||||
would put into `config.vm.box` in your Vagrantfile. When adding a box from
|
||||
a catalog, the name is included in the catalog entry and doesn't have
|
||||
to be specified.
|
||||
|
||||
# Box List
|
||||
|
||||
**Command: `vagrant box list`**
|
||||
|
||||
This command lists all the boxes that are installed into Vagrant.
|
||||
|
||||
# Box Outdated
|
||||
|
||||
**Command: `vagrant box outdated`**
|
||||
|
||||
This command tells you whether or not the box you're using in
|
||||
your current Vagrant environment is outdated. If the `--global` flag
|
||||
is present, every installed box will be checked for updates.
|
||||
|
||||
Checking for updates involves refreshing the metadata associated with
|
||||
a box. This generally requires an internet connection.
|
||||
|
||||
## Options
|
||||
|
||||
* `--box-info` - If given, Vagrant will display the URL from where the box
|
||||
has been downloaded and the date it was added
|
||||
* `--global` - Check for updates for all installed boxes, not just the
|
||||
boxes for the current Vagrant environment.
|
||||
|
||||
# Box Remove
|
||||
|
||||
**Command: `vagrant box remove NAME PROVIDER`**
|
||||
**Command: `vagrant box remove NAME`**
|
||||
|
||||
This command removes a box from Vagrant that matches the given name and
|
||||
provider.
|
||||
This command removes a box from Vagrant that matches the given name.
|
||||
|
||||
If a box has multiple providers, the exact provider must be specified
|
||||
with the `--provider` flag. If a box has multiple versions, you can select
|
||||
what versions to delete with the `--box-version` flag.
|
||||
|
||||
## Options
|
||||
|
||||
* `--box-version VALUE` - Version of version constraints of the boxes to
|
||||
remove. See documentation on this flag for `box add` for more details.
|
||||
|
||||
* `--provider VALUE` - The provider-specific box to remove with the given
|
||||
name. This is only required if a box is backed by multiple providers.
|
||||
If there is only a single provider, Vagrant will default to removing it.
|
||||
|
||||
# Box Repackage
|
||||
|
||||
|
@ -96,3 +140,28 @@ directory so you can redistribute it.
|
|||
When you add a box, Vagrant unpacks it and stores it internally. The
|
||||
original `*.box` file is not preserved. This command is useful for
|
||||
reclaiming a `*.box` file from an installed Vagrant box.
|
||||
|
||||
# Box Update
|
||||
|
||||
**Command: `vagrant box update`**
|
||||
|
||||
This command updates the box for the current Vagrant environment if there
|
||||
are updates available. The command can also update a specific box (outside
|
||||
of an active Vagrant environment), by specifying the `--box` flag.
|
||||
|
||||
Note that updating the box will not update an already-running Vagrant
|
||||
machine. To reflect the changes in the box, you'll have to destroy and
|
||||
bring back up the Vagrant machine.
|
||||
|
||||
If you just want to check if there are updates available, use the
|
||||
`vagrant box outdated` command.
|
||||
|
||||
## Options
|
||||
|
||||
* `--box VALUE` - Name of a specific box to update. If this flag is not
|
||||
specified, Vagrant will update the boxes for the active Vagrant
|
||||
environment.
|
||||
|
||||
* `--provider VALUE` - When `--box` is present, this controls what
|
||||
provider-specific box to update. This is not required unless the box has
|
||||
multiple providers. Without the `--box` flag, this has no effect.
|
||||
|
|
|
@ -18,8 +18,18 @@ for the machine to boot and be accessible. By default this is 300 seconds.
|
|||
<hr>
|
||||
|
||||
`config.vm.box` - This configures what [box](/v2/boxes.html) the
|
||||
machine will be brought up against. The value here should match one of
|
||||
the installed boxes on the system.
|
||||
machine will be brought up against. The value here should be the name
|
||||
of an installed box or a shorthand name of a box in
|
||||
[Vagrant Cloud](http://www.vagrantcloud.com).
|
||||
|
||||
<hr>
|
||||
|
||||
`config.vm.box_check_update` - If true, Vagrant will check for updates to
|
||||
the configured box on every `vagrant up`. If an update is found, Vagrant
|
||||
will tell the user. By default this is true. Updates will only be checked
|
||||
for boxes that properly support updates (boxes from
|
||||
[Vagrant Cloud](http://www.vagrantcloud.com)
|
||||
or some other versioned box).
|
||||
|
||||
<hr>
|
||||
|
||||
|
@ -53,8 +63,10 @@ URL, then SSL certs will be verified.
|
|||
<hr>
|
||||
|
||||
`config.vm.box_url` - The URL that the configured box can be found at.
|
||||
If the box is not installed on the system, it will be retrieved from this
|
||||
URL when `vagrant up` is run.
|
||||
If `config.vm.box` is a shorthand to a box in [Vagrant Cloud](http://www.vagrantcloud.com)
|
||||
then this value doesn't need to be specified. Otherwise, it should
|
||||
point to the proper place where the box can be found if it isn't
|
||||
installed.
|
||||
|
||||
This can also be an array of multiple URLs. The URLs will be tried in
|
||||
order. Note that any client certificates, insecure download settings, and
|
||||
|
@ -62,6 +74,14 @@ so on will apply to all URLs in this list.
|
|||
|
||||
<hr>
|
||||
|
||||
`config.vm.box_version` - The version of the box to use. This defaults to
|
||||
">= 0" (the latest version available). This can contain an arbitrary list
|
||||
of constraints, separated by commas, such as: `>= 1.0, < 1.5`. When constraints
|
||||
are given, Vagrant will use the latest available box satisfying these
|
||||
constraints.
|
||||
|
||||
<hr>
|
||||
|
||||
`config.vm.graceful_halt_timeout` - The time in seconds that Vagrant will
|
||||
wait for the machine to gracefully halt when `vagrant halt` is called.
|
||||
Defaults to 300 seconds.
|
||||
|
|
Loading…
Reference in New Issue