Add architecture support for boxes
Introduce support for handling box architecture. Adds a new `box_architecture` setting that defaults to `:auto` which will perform automatic detection of the host system, but can be overridden with a custom value. Can also be set to `nil` which will result in it fetching the box flagged with the default architecture within the metadata. Box collection has been modified to allow existing boxes already downloaded and unpacked to still function as expected when architecture information is not available.
This commit is contained in:
parent
5513173758
commit
51adb12547
@ -165,6 +165,7 @@ module Vagrant
|
||||
env,
|
||||
checksum: env[:box_checksum],
|
||||
checksum_type: env[:box_checksum_type],
|
||||
architecture: env[:architecture]
|
||||
)
|
||||
end
|
||||
|
||||
@ -179,6 +180,9 @@ module Vagrant
|
||||
# a Atlas server URL.
|
||||
def add_from_metadata(url, env, expanded)
|
||||
original_url = env[:box_url]
|
||||
architecture = env[:box_architecture]
|
||||
display_architecture = architecture == :auto ?
|
||||
Util::Platform.architecture : architecture
|
||||
provider = env[:box_provider]
|
||||
provider = Array(provider) if provider
|
||||
version = env[:box_version]
|
||||
@ -228,12 +232,17 @@ module Vagrant
|
||||
end
|
||||
|
||||
metadata_version = metadata.version(
|
||||
version || ">= 0", provider: provider)
|
||||
version || ">= 0",
|
||||
provider: provider,
|
||||
architecture: architecture,
|
||||
)
|
||||
if !metadata_version
|
||||
if provider && !metadata.version(">= 0", provider: provider)
|
||||
if provider && !metadata.version(">= 0", provider: provider, architecture: architecture)
|
||||
raise Errors::BoxAddNoMatchingProvider,
|
||||
name: metadata.name,
|
||||
requested: provider,
|
||||
requested: [provider,
|
||||
display_architecture ? "(#{display_architecture})" : nil
|
||||
].compact.join(" "),
|
||||
url: display_url
|
||||
else
|
||||
raise Errors::BoxAddNoMatchingVersion,
|
||||
@ -249,16 +258,16 @@ module Vagrant
|
||||
# If a provider was specified, make sure we get that specific
|
||||
# version.
|
||||
provider.each do |p|
|
||||
metadata_provider = metadata_version.provider(p)
|
||||
metadata_provider = metadata_version.provider(p, architecture)
|
||||
break if metadata_provider
|
||||
end
|
||||
elsif metadata_version.providers.length == 1
|
||||
elsif metadata_version.providers(architecture).length == 1
|
||||
# If we have only one provider in the metadata, just use that
|
||||
# provider.
|
||||
metadata_provider = metadata_version.provider(
|
||||
metadata_version.providers.first)
|
||||
metadata_version.providers.first, architecture)
|
||||
else
|
||||
providers = metadata_version.providers.sort
|
||||
providers = metadata_version.providers(architecture).sort
|
||||
|
||||
choice = 0
|
||||
options = providers.map do |p|
|
||||
@ -279,7 +288,7 @@ module Vagrant
|
||||
end
|
||||
|
||||
metadata_provider = metadata_version.provider(
|
||||
providers[choice-1])
|
||||
providers[choice-1], architecture)
|
||||
end
|
||||
|
||||
provider_url = metadata_provider.url
|
||||
@ -302,6 +311,7 @@ module Vagrant
|
||||
env,
|
||||
checksum: metadata_provider.checksum,
|
||||
checksum_type: metadata_provider.checksum_type,
|
||||
architecture: architecture,
|
||||
)
|
||||
end
|
||||
|
||||
@ -317,16 +327,21 @@ module Vagrant
|
||||
# @param [Hash] env
|
||||
# @return [Box]
|
||||
def box_add(urls, name, version, provider, md_url, env, **opts)
|
||||
display_architecture = opts[:architecture] == :auto ?
|
||||
Util::Platform.architecture : opts[:architecture]
|
||||
env[:ui].output(I18n.t(
|
||||
"vagrant.box_add_with_version",
|
||||
name: name,
|
||||
version: version,
|
||||
providers: Array(provider).join(", ")))
|
||||
providers: [
|
||||
provider,
|
||||
display_architecture ? "(#{display_architecture})" : nil
|
||||
].compact.join(" ")))
|
||||
|
||||
# Verify the box we're adding doesn't already exist
|
||||
if provider && !env[:box_force]
|
||||
box = env[:box_collection].find(
|
||||
name, provider, version)
|
||||
name, provider, version, opts[:architecture])
|
||||
if box
|
||||
raise Errors::BoxAlreadyExists,
|
||||
name: name,
|
||||
@ -377,7 +392,9 @@ module Vagrant
|
||||
box_url, name, version,
|
||||
force: env[:box_force],
|
||||
metadata_url: md_url,
|
||||
providers: provider)
|
||||
providers: provider,
|
||||
architecture: env[:box_architecture]
|
||||
)
|
||||
ensure
|
||||
# Make sure we delete the temporary file after we add it,
|
||||
# unless we were interrupted, in which case we keep it around
|
||||
@ -396,7 +413,10 @@ module Vagrant
|
||||
"vagrant.box_added",
|
||||
name: box.name,
|
||||
version: box.version,
|
||||
provider: box.provider))
|
||||
provider: [
|
||||
provider,
|
||||
display_architecture ? "(#{display_architecture})" : nil
|
||||
].compact.join(" ")))
|
||||
|
||||
# Store the added box in the env for future middleware
|
||||
env[:box_added] = box
|
||||
|
||||
@ -15,112 +15,162 @@ module Vagrant
|
||||
|
||||
def call(env)
|
||||
box_name = env[:box_name]
|
||||
box_provider = env[:box_provider]
|
||||
box_provider = box_provider.to_sym if box_provider
|
||||
box_architecture = env[:box_architecture] if env[:box_architecture]
|
||||
box_provider = env[:box_provider].to_sym if env[:box_provider]
|
||||
box_version = env[:box_version]
|
||||
box_remove_all_versions = env[:box_remove_all_versions]
|
||||
box_remove_all_providers = env[:box_remove_all_providers]
|
||||
box_remove_all_architectures = env[:box_remove_all_architectures]
|
||||
|
||||
boxes = {}
|
||||
env[:box_collection].all.each do |n, v, p|
|
||||
boxes[n] ||= {}
|
||||
boxes[n][p] ||= []
|
||||
boxes[n][p] << v
|
||||
box_info = Util::HashWithIndifferentAccess.new
|
||||
env[:box_collection].all.each do |name, version, provider, architecture|
|
||||
next if name != box_name
|
||||
box_info[version] ||= Util::HashWithIndifferentAccess.new
|
||||
box_info[version][provider] ||= []
|
||||
box_info[version][provider] << architecture
|
||||
end
|
||||
|
||||
all_box = boxes[box_name]
|
||||
if !all_box
|
||||
# If there's no box info, then the box doesn't exist here
|
||||
if box_info.empty?
|
||||
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
|
||||
elsif not box_remove_all_versions
|
||||
# There are multiple versions, we can't choose.
|
||||
# Filtering only matters if not removing all versions
|
||||
if !box_remove_all_versions
|
||||
# If no version was provided, and not removing all versions,
|
||||
# only allow one version to proceed
|
||||
if !box_version && box_info.size > 1
|
||||
raise Errors::BoxRemoveMultiVersion,
|
||||
name: box_name,
|
||||
provider: box_provider.to_s,
|
||||
versions: all_versions.sort.map { |k| " * #{k}" }.join("\n")
|
||||
versions: box_info.keys.sort.map { |k| " * #{k}" }.join("\n")
|
||||
end
|
||||
|
||||
# If a version was provided, make sure it exists
|
||||
if box_version
|
||||
if !box_info.keys.include?(box_version)
|
||||
raise Errors::BoxRemoveVersionNotFound,
|
||||
name: box_name,
|
||||
version: box_version,
|
||||
versions: box_info.keys.sort.map { |k| " * #{k}" }.join("\n")
|
||||
else
|
||||
box_info.delete_if { |k, _| k != box_version }
|
||||
end
|
||||
end
|
||||
|
||||
# Only a single version remains
|
||||
box_version = box_info.keys.first
|
||||
|
||||
# Further filtering only matters if not removing all providers
|
||||
if !box_remove_all_providers
|
||||
# If no provider was given, check if there are more
|
||||
# than a single provider for the version
|
||||
if !box_provider && box_info.values.first.size > 1
|
||||
raise Errors::BoxRemoveMultiProvider,
|
||||
name: box_name,
|
||||
version: box_version,
|
||||
providers: box_info.values.first.keys.map(&:to_s).sort.join(", ")
|
||||
end
|
||||
|
||||
# If a provider was given, check the version has it
|
||||
if box_provider
|
||||
if !box_info.values.first.key?(box_provider)
|
||||
raise Errors::BoxRemoveProviderNotFound,
|
||||
name: box_name,
|
||||
version: box_version,
|
||||
provider: box_provider.to_s,
|
||||
providers: box_info.values.first.keys.map(&:to_s).sort.join(", ")
|
||||
else
|
||||
box_info.values.first.delete_if { |k, _| k.to_s != box_provider.to_s }
|
||||
end
|
||||
end
|
||||
|
||||
# Only a single provider remains
|
||||
box_provider = box_info.values.first.keys.first
|
||||
|
||||
# Further filtering only matters if not removing all architectures
|
||||
if !box_remove_all_architectures
|
||||
# If no architecture was given, check if there are more
|
||||
# than a single architecture for the provider in version
|
||||
if !box_architecture && box_info.values.first.values.first.size > 1
|
||||
raise Errors::BoxRemoveMultiArchitecture,
|
||||
name: box_name,
|
||||
version: box_version,
|
||||
provider: box_provider.to_s,
|
||||
architectures: box_info.values.first.values.first.sort.join(", ")
|
||||
end
|
||||
|
||||
# If architecture was given, check the provider for the version has it
|
||||
if box_architecture
|
||||
if !box_info.values.first.values.first.include?(box_architecture)
|
||||
raise Errors::BoxRemoveArchitectureNotFound,
|
||||
name: box_name,
|
||||
version: box_version,
|
||||
provider: box_provider.to_s,
|
||||
architecture: box_architecture,
|
||||
architectures: box_info.values.first.values.first.sort.join(", ")
|
||||
else
|
||||
box_info.values.first.values.first.delete_if { |v| v != box_architecture }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
elsif !all_versions.include?(box_version)
|
||||
raise Errors::BoxRemoveVersionNotFound,
|
||||
name: box_name,
|
||||
provider: box_provider.to_s,
|
||||
version: box_version,
|
||||
versions: all_versions.sort.map { |k| " * #{k}" }.join("\n")
|
||||
end
|
||||
|
||||
versions_to_remove = [box_version]
|
||||
versions_to_remove = all_versions if box_remove_all_versions
|
||||
box_info.each do |version, provider_info|
|
||||
provider_info.each do |provider, architecture_info|
|
||||
provider = provider.to_sym
|
||||
architecture_info.each do |architecture|
|
||||
box = env[:box_collection].find(
|
||||
box_name, provider, version, architecture
|
||||
)
|
||||
|
||||
versions_to_remove.sort.each do |version_to_remove|
|
||||
box = env[:box_collection].find(
|
||||
box_name, box_provider, box_version)
|
||||
# Verify that this box is not in use by an active machine,
|
||||
# otherwise warn the user.
|
||||
users = box.in_use?(env[:machine_index]) || []
|
||||
users = users.find_all { |u| u.valid?(env[:home_path]) }
|
||||
if !users.empty?
|
||||
# Build up the output to show the user.
|
||||
users = users.map do |entry|
|
||||
"#{entry.name} (ID: #{entry.id})"
|
||||
end.join("\n")
|
||||
|
||||
# Verify that this box is not in use by an active machine,
|
||||
# otherwise warn the user.
|
||||
users = box.in_use?(env[:machine_index]) || []
|
||||
users = users.find_all { |u| u.valid?(env[:home_path]) }
|
||||
if !users.empty?
|
||||
# Build up the output to show the user.
|
||||
users = users.map do |entry|
|
||||
"#{entry.name} (ID: #{entry.id})"
|
||||
end.join("\n")
|
||||
force_key = :force_confirm_box_remove
|
||||
message = I18n.t(
|
||||
"vagrant.commands.box.remove_in_use_query",
|
||||
name: box.name,
|
||||
architecture: box.architecture,
|
||||
provider: box.provider,
|
||||
version: box.version,
|
||||
users: users) + " "
|
||||
|
||||
force_key = :force_confirm_box_remove
|
||||
message = I18n.t(
|
||||
"vagrant.commands.box.remove_in_use_query",
|
||||
name: box.name,
|
||||
provider: box.provider,
|
||||
version: box.version,
|
||||
users: users) + " "
|
||||
# Ask the user if we should do this
|
||||
stack = Builder.new.tap do |b|
|
||||
b.use Confirm, message, force_key
|
||||
end
|
||||
|
||||
# Ask the user if we should do this
|
||||
stack = Builder.new.tap do |b|
|
||||
b.use Confirm, message, force_key
|
||||
end
|
||||
# Keep used boxes, even if "force" is applied
|
||||
keep_used_boxes = env[:keep_used_boxes]
|
||||
|
||||
# Keep used boxes, even if "force" is applied
|
||||
keep_used_boxes = env[:keep_used_boxes]
|
||||
result = env[:action_runner].run(stack, env)
|
||||
if !result[:result] || keep_used_boxes
|
||||
# They said "no", so continue with the next box
|
||||
next
|
||||
end
|
||||
end
|
||||
|
||||
result = env[:action_runner].run(stack, env)
|
||||
if !result[:result] || keep_used_boxes
|
||||
# They said "no", so continue with the next box
|
||||
next
|
||||
env[:ui].info(I18n.t("vagrant.commands.box.removing",
|
||||
name: box.name,
|
||||
architecture: box.architecture,
|
||||
provider: box.provider,
|
||||
version: box.version))
|
||||
|
||||
box.destroy!
|
||||
env[:box_collection].clean(box.name)
|
||||
|
||||
# Passes on the removed box to the rest of the middleware chain
|
||||
env[:box_removed] = box
|
||||
end
|
||||
end
|
||||
|
||||
env[:ui].info(I18n.t("vagrant.commands.box.removing",
|
||||
name: box.name,
|
||||
provider: box.provider,
|
||||
version: box.version))
|
||||
box.destroy!
|
||||
env[:box_collection].clean(box.name)
|
||||
|
||||
# Passes on the removed box to the rest of the middleware chain
|
||||
env[:box_removed] = box
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
|
||||
@ -87,6 +87,7 @@ module Vagrant
|
||||
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_architecture: machine.config.vm.box_architecture,
|
||||
box_server_url: machine.config.vm.box_server_url,
|
||||
box_provider: box_formats,
|
||||
box_version: machine.config.vm.box_version,
|
||||
|
||||
@ -37,6 +37,11 @@ module Vagrant
|
||||
# @return [Symbol]
|
||||
attr_reader :provider
|
||||
|
||||
# This is the architecture that this box is build for.
|
||||
#
|
||||
# @return [String]
|
||||
attr_reader :architecture
|
||||
|
||||
# The version of this box.
|
||||
#
|
||||
# @return [String]
|
||||
@ -65,13 +70,15 @@ module Vagrant
|
||||
# @param [Symbol] provider The provider that this box implements.
|
||||
# @param [Pathname] directory The directory where this box exists on
|
||||
# disk.
|
||||
# @param [String] architecture Architecture the box was built for
|
||||
# @param [String] metadata_url Metadata URL for box
|
||||
# @param [Hook] hook A hook to apply to the box downloader, for example, for authentication
|
||||
def initialize(name, provider, version, directory, metadata_url: nil, hook: nil)
|
||||
def initialize(name, provider, version, directory, architecture: nil, metadata_url: nil, hook: nil)
|
||||
@name = name
|
||||
@version = version
|
||||
@provider = provider
|
||||
@directory = directory
|
||||
@architecture = architecture
|
||||
@metadata_url = metadata_url
|
||||
@hook = hook
|
||||
|
||||
@ -130,6 +137,7 @@ module Vagrant
|
||||
# If all the data matches, record it
|
||||
if box_data["name"] == self.name &&
|
||||
box_data["provider"] == self.provider.to_s &&
|
||||
box_data["architecture"] == self.architecture &&
|
||||
box_data["version"] == self.version.to_s
|
||||
results << entry
|
||||
end
|
||||
@ -194,10 +202,10 @@ module Vagrant
|
||||
version ||= ""
|
||||
version += "> #{@version}"
|
||||
md = self.load_metadata(download_options)
|
||||
newer = md.version(version, provider: @provider)
|
||||
newer = md.version(version, provider: @provider, architecture: @architecture)
|
||||
return nil if !newer
|
||||
|
||||
[md, newer, newer.provider(@provider)]
|
||||
[md, newer, newer.provider(@provider, @architecture)]
|
||||
end
|
||||
|
||||
# Check if a box update check is allowed. Uses a file
|
||||
@ -241,13 +249,13 @@ module Vagrant
|
||||
end
|
||||
|
||||
# Implemented for comparison with other boxes. Comparison is
|
||||
# implemented by comparing names and providers.
|
||||
# implemented by comparing names, providers, and architectures.
|
||||
def <=>(other)
|
||||
return super if !other.is_a?(self.class)
|
||||
|
||||
# Comparison is done by composing the name and provider
|
||||
"#{@name}-#{@version}-#{@provider}" <=>
|
||||
"#{other.name}-#{other.version}-#{other.provider}"
|
||||
"#{@name}-#{@version}-#{@provider}-#{@architecture}" <=>
|
||||
"#{other.name}-#{other.version}-#{other.provider}-#{other.architecture}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -28,7 +28,7 @@ module Vagrant
|
||||
# expects in order to easily manage and modify boxes. The folder structure
|
||||
# is the following:
|
||||
#
|
||||
# COLLECTION_ROOT/BOX_NAME/PROVIDER/metadata.json
|
||||
# COLLECTION_ROOT/BOX_NAME/PROVIDER/[ARCHITECTURE]/metadata.json
|
||||
#
|
||||
# Where:
|
||||
#
|
||||
@ -38,6 +38,8 @@ module Vagrant
|
||||
# the user of Vagrant.
|
||||
# * PROVIDER - The provider that the box was built for (VirtualBox,
|
||||
# VMware, etc.).
|
||||
# * ARCHITECTURE - Optional. The architecture that the box was built
|
||||
# for (amd64, arm64, 386, etc.).
|
||||
# * metadata.json - A simple JSON file that at the bare minimum
|
||||
# contains a "provider" key that matches the provider for the
|
||||
# box. This metadata JSON, however, can contain anything.
|
||||
@ -80,14 +82,15 @@ module Vagrant
|
||||
# @param [Boolean] force If true, any existing box with the same name
|
||||
# and provider will be replaced.
|
||||
def add(path, name, version, **opts)
|
||||
architecture = opts[:architecture]
|
||||
providers = opts[:providers]
|
||||
providers = Array(providers) if providers
|
||||
provider = nil
|
||||
|
||||
# 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)
|
||||
check_box_exists = lambda do |box_formats, box_architecture|
|
||||
box = find(name, box_formats, version, box_architecture)
|
||||
next if !box
|
||||
|
||||
if !opts[:force]
|
||||
@ -108,12 +111,12 @@ module Vagrant
|
||||
|
||||
with_collection_lock do
|
||||
log_provider = providers ? providers.join(", ") : "any provider"
|
||||
@logger.debug("Adding box: #{name} (#{log_provider}) from #{path}")
|
||||
@logger.debug("Adding box: #{name} (#{log_provider} - #{architecture.inspect}) 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(providers) if providers
|
||||
check_box_exists.call(providers, architecture) 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)
|
||||
@ -154,7 +157,7 @@ module Vagrant
|
||||
end
|
||||
else
|
||||
# Verify the box doesn't already exist
|
||||
check_box_exists.call([box_provider])
|
||||
check_box_exists.call([box_provider], architecture)
|
||||
end
|
||||
|
||||
# We weren't given a provider, so store this one.
|
||||
@ -167,7 +170,16 @@ module Vagrant
|
||||
@logger.debug("Box directory: #{box_dir}")
|
||||
|
||||
# This is the final directory we'll move it to
|
||||
final_dir = box_dir.join(provider.to_s)
|
||||
provider_dir = box_dir.join(provider.to_s)
|
||||
final_dir = provider_dir
|
||||
@logger.debug("Provider directory: #{provider_dir}")
|
||||
# If architecture is set, unpack into architecture specific directory
|
||||
if architecture
|
||||
arch = architecture
|
||||
arch = Util::Platform.architecture if architecture == :auto
|
||||
final_dir = provider_dir.join(arch)
|
||||
end
|
||||
|
||||
if final_dir.exist?
|
||||
@logger.debug("Removing existing provider directory...")
|
||||
final_dir.rmtree
|
||||
@ -209,13 +221,13 @@ module Vagrant
|
||||
end
|
||||
|
||||
# Return the box
|
||||
find(name, provider, version)
|
||||
find(name, provider, version, architecture)
|
||||
end
|
||||
|
||||
# This returns an array of all the boxes on the system, given by
|
||||
# their name and their provider.
|
||||
#
|
||||
# @return [Array] Array of `[name, version, provider]` of the boxes
|
||||
# @return [Array] Array of `[name, version, provider, architecture]` of the boxes
|
||||
# installed on this system.
|
||||
def all
|
||||
results = []
|
||||
@ -252,7 +264,16 @@ module Vagrant
|
||||
if provider.directory? && provider.join("metadata.json").file?
|
||||
provider_name = provider.basename.to_s.to_sym
|
||||
@logger.debug("Box: #{box_name} (#{provider_name}, #{version})")
|
||||
results << [box_name, version, provider_name]
|
||||
results << [box_name, version, provider_name, nil]
|
||||
elsif provider.directory?
|
||||
provider.children(true).each do |architecture|
|
||||
provider_name = provider.basename.to_s.to_sym
|
||||
if architecture.directory? && architecture.join("metadata.json").file?
|
||||
architecture_name = architecture.basename.to_s.to_sym
|
||||
@logger.debug("Box: #{box_name} (#{provider_name} (#{architecture_name}), #{version})")
|
||||
results << [box_name, version, provider_name, architecture_name]
|
||||
end
|
||||
end
|
||||
else
|
||||
@logger.debug("Invalid box #{box_name}, ignoring: #{provider}")
|
||||
end
|
||||
@ -262,7 +283,7 @@ module Vagrant
|
||||
end
|
||||
# Sort the list to group like providers and properly ordered versions
|
||||
results.sort_by! do |box_result|
|
||||
[box_result[0], box_result[2], Gem::Version.new(box_result[1])]
|
||||
[box_result[0], box_result[2], Gem::Version.new(box_result[1]), box_result[3]]
|
||||
end
|
||||
results
|
||||
end
|
||||
@ -274,8 +295,10 @@ module Vagrant
|
||||
# @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, version)
|
||||
def find(name, providers, version, box_architecture=:auto)
|
||||
providers = Array(providers)
|
||||
architecture = box_architecture
|
||||
architecture = Util::Platform.architecture if architecture == :auto
|
||||
|
||||
# Build up the requirements we have
|
||||
requirements = version.to_s.split(",").map do |v|
|
||||
@ -321,6 +344,19 @@ module Vagrant
|
||||
providers.each do |provider|
|
||||
provider_dir = versiondir.join(provider.to_s)
|
||||
next if !provider_dir.directory?
|
||||
# If architecture is defined then the box should be within
|
||||
# a subdirectory. However, if box_architecture value is :auto
|
||||
# and the box provider directory exists but the architecture
|
||||
# directory does not, we will use the box provider directory. This
|
||||
# allows Vagrant to work correctly with boxes which were added
|
||||
# prior to the addition of architecture support
|
||||
final_dir = provider_dir
|
||||
if architecture
|
||||
arch_dir = provider_dir.join(architecture.to_s)
|
||||
next if !arch_dir.directory? && box_architecture != :auto
|
||||
end
|
||||
final_dir = arch_dir if arch_dir && arch_dir.directory?
|
||||
|
||||
@logger.info("Box found: #{name} (#{provider})")
|
||||
|
||||
metadata_url = nil
|
||||
@ -334,8 +370,8 @@ module Vagrant
|
||||
end
|
||||
|
||||
return Box.new(
|
||||
name, provider, version_dir_map[v.to_s], provider_dir,
|
||||
metadata_url: metadata_url, hook: @hook
|
||||
name, provider, version_dir_map[v.to_s], final_dir,
|
||||
architecture: architecture, metadata_url: metadata_url, hook: @hook
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@ -38,7 +38,7 @@ module Vagrant
|
||||
@description = @raw["description"]
|
||||
@version_map = (@raw["versions"] || []).map do |v|
|
||||
begin
|
||||
[Gem::Version.new(v["version"]), v]
|
||||
[Gem::Version.new(v["version"]), Version.new(v)]
|
||||
rescue ArgumentError
|
||||
raise Errors::BoxMetadataMalformedVersion,
|
||||
version: v["version"].to_s
|
||||
@ -61,11 +61,27 @@ module Vagrant
|
||||
|
||||
providers = nil
|
||||
providers = Array(opts[:provider]).map(&:to_sym) if opts[:provider]
|
||||
# NOTE: The :auto value is not expanded here since no architecture
|
||||
# value comparisons are being done within this method
|
||||
architecture = opts.fetch(:architecture, :auto)
|
||||
|
||||
@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
|
||||
version = @version_map[v]
|
||||
valid_providers = version.providers
|
||||
|
||||
# If filtering by provider(s), apply filter
|
||||
valid_providers &= providers if providers
|
||||
|
||||
# Skip if no valid providers are found
|
||||
next if valid_providers.empty?
|
||||
|
||||
# Skip if no valid provider includes support
|
||||
# the desired architecture
|
||||
next if architecture && valid_providers.none? { |p|
|
||||
version.provider(p, architecture)
|
||||
}
|
||||
|
||||
return version
|
||||
end
|
||||
|
||||
@ -79,20 +95,25 @@ module Vagrant
|
||||
#
|
||||
# @return[Array<String>]
|
||||
def versions(**opts)
|
||||
provider = nil
|
||||
architecture = opts[:architecture]
|
||||
provider = opts[:provider].to_sym if opts[:provider]
|
||||
|
||||
if provider
|
||||
@version_map.select do |version, raw|
|
||||
if raw["providers"]
|
||||
raw["providers"].detect do |p|
|
||||
p["name"].to_sym == provider
|
||||
end
|
||||
end
|
||||
end.keys.sort.map(&:to_s)
|
||||
else
|
||||
@version_map.keys.sort.map(&:to_s)
|
||||
# Return full version list if no filters provided
|
||||
if provider.nil? && architecture.nil?
|
||||
return @version_map.keys.sort.map(&:to_s)
|
||||
end
|
||||
|
||||
# If a specific provider is not provided, filter
|
||||
# only on architecture
|
||||
if provider.nil?
|
||||
return @version_map.select { |_, version|
|
||||
!version.providers(architecture).empty?
|
||||
}.keys.sort.map(&:to_s)
|
||||
end
|
||||
|
||||
@version_map.select { |_, version|
|
||||
version.provider(provider, architecture)
|
||||
}.keys.sort.map(&:to_s)
|
||||
end
|
||||
|
||||
# Represents a single version within the metadata.
|
||||
@ -106,26 +127,81 @@ module Vagrant
|
||||
return if !raw
|
||||
|
||||
@version = raw["version"]
|
||||
@provider_map = (raw["providers"] || []).map do |p|
|
||||
[p["name"].to_sym, p]
|
||||
@providers = raw.fetch("providers", []).map do |data|
|
||||
Provider.new(data)
|
||||
end
|
||||
@provider_map = Hash[@provider_map]
|
||||
@provider_map = @providers.group_by(&:name)
|
||||
@provider_map = Util::HashWithIndifferentAccess.new(@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)
|
||||
def provider(name, architecture=nil)
|
||||
name = name.to_sym
|
||||
arch_name = architecture
|
||||
arch_name = Util::Platform.architecture if arch_name == :auto
|
||||
arch_name = arch_name.to_s if arch_name
|
||||
|
||||
# If the provider doesn't exist in the map, return immediately
|
||||
return if !@provider_map.key?(name)
|
||||
|
||||
# If the arch_name value is set, filter based
|
||||
# on architecture and return match if found. If
|
||||
# no match is found and architecture wasn't automatically
|
||||
# detected, return nil as an explicit match is
|
||||
# being requested
|
||||
if arch_name
|
||||
match = @provider_map[name].detect do |p|
|
||||
p.architecture == arch_name
|
||||
end
|
||||
|
||||
return match if match || architecture != :auto
|
||||
end
|
||||
|
||||
# If the passed architecture value was :auto and no explicit
|
||||
# match for the architecture was found, check for a provider
|
||||
# that is flagged as the default architecture, and has an
|
||||
# architecture value of "unknown"
|
||||
#
|
||||
# NOTE: This preserves expected behavior with legacy boxes
|
||||
if architecture == :auto
|
||||
match = @provider_map[name].detect do |p|
|
||||
p.architecture == "unknown" &&
|
||||
p.default_architecture
|
||||
end
|
||||
|
||||
return match if match
|
||||
end
|
||||
|
||||
# If the architecture value is set to nil, then just return
|
||||
# whatever is defined as the default architecture
|
||||
if architecture.nil?
|
||||
match = @provider_map[name].detect(&:default_architecture)
|
||||
|
||||
return match if match
|
||||
end
|
||||
|
||||
# The metadata consumed may not include architecture information,
|
||||
# in which case the match would just be the single provider
|
||||
# defined within the provider map for the name
|
||||
if @provider_map[name].size == 1 && !@provider_map[name].first.architecture_support?
|
||||
return @provider_map[name].first
|
||||
end
|
||||
|
||||
# Otherwise, there is no match
|
||||
nil
|
||||
end
|
||||
|
||||
# Returns the providers that are available for this version
|
||||
# of the box.
|
||||
#
|
||||
# @return [Array<Symbol>]
|
||||
def providers
|
||||
@provider_map.keys.map(&:to_sym)
|
||||
def providers(architecture=nil)
|
||||
return @provider_map.keys.map(&:to_sym) if architecture.nil?
|
||||
|
||||
@provider_map.keys.find_all { |k|
|
||||
provider(k, architecture)
|
||||
}.map(&:to_sym)
|
||||
end
|
||||
end
|
||||
|
||||
@ -152,11 +228,27 @@ module Vagrant
|
||||
# @return [String]
|
||||
attr_accessor :checksum_type
|
||||
|
||||
# The architecture of the box
|
||||
#
|
||||
# @return [String]
|
||||
attr_accessor :architecture
|
||||
|
||||
# Marked as the default architecture
|
||||
#
|
||||
# @return [Boolean, NilClass]
|
||||
attr_accessor :default_architecture
|
||||
|
||||
def initialize(raw, **_)
|
||||
@name = raw["name"]
|
||||
@url = raw["url"]
|
||||
@checksum = raw["checksum"]
|
||||
@checksum_type = raw["checksum_type"]
|
||||
@architecture = raw["architecture"]
|
||||
@default_architecture = raw["default_architecture"]
|
||||
end
|
||||
|
||||
def architecture_support?
|
||||
!@default_architecture.nil?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -199,6 +199,10 @@ module Vagrant
|
||||
error_key(:box_not_found_with_provider)
|
||||
end
|
||||
|
||||
class BoxNotFoundWithProviderArchitecture < VagrantError
|
||||
error_key(:box_not_found_with_provider_architecture)
|
||||
end
|
||||
|
||||
class BoxNotFoundWithProviderAndVersion < VagrantError
|
||||
error_key(:box_not_found_with_provider_and_version)
|
||||
end
|
||||
@ -211,6 +215,10 @@ module Vagrant
|
||||
error_key(:box_remove_not_found)
|
||||
end
|
||||
|
||||
class BoxRemoveArchitectureNotFound < VagrantError
|
||||
error_key(:box_remove_architecture_not_found)
|
||||
end
|
||||
|
||||
class BoxRemoveProviderNotFound < VagrantError
|
||||
error_key(:box_remove_provider_not_found)
|
||||
end
|
||||
@ -219,6 +227,10 @@ module Vagrant
|
||||
error_key(:box_remove_version_not_found)
|
||||
end
|
||||
|
||||
class BoxRemoveMultiArchitecture < VagrantError
|
||||
error_key(:box_remove_multi_architecture)
|
||||
end
|
||||
|
||||
class BoxRemoveMultiProvider < VagrantError
|
||||
error_key(:box_remove_multi_provider)
|
||||
end
|
||||
@ -239,6 +251,10 @@ module Vagrant
|
||||
error_key(:box_update_multi_provider)
|
||||
end
|
||||
|
||||
class BoxUpdateMultiArchitecture < VagrantError
|
||||
error_key(:box_update_multi_architecture)
|
||||
end
|
||||
|
||||
class BoxUpdateNoMetadata < VagrantError
|
||||
error_key(:box_update_no_metadata)
|
||||
end
|
||||
|
||||
@ -326,6 +326,7 @@ module Vagrant
|
||||
entry.local_data_path = @env.local_data_path
|
||||
entry.name = @name.to_s
|
||||
entry.provider = @provider_name.to_s
|
||||
entry.architecture = @architecture
|
||||
entry.state = "preparing"
|
||||
entry.vagrantfile_path = @env.root_path
|
||||
entry.vagrantfile_name = @env.vagrantfile_name
|
||||
@ -334,6 +335,7 @@ module Vagrant
|
||||
entry.extra_data["box"] = {
|
||||
"name" => @box.name,
|
||||
"provider" => @box.provider.to_s,
|
||||
"architecture" => @box.architecture,
|
||||
"version" => @box.version.to_s,
|
||||
}
|
||||
end
|
||||
@ -587,6 +589,7 @@ module Vagrant
|
||||
entry.extra_data["box"] = {
|
||||
"name" => @box.name,
|
||||
"provider" => @box.provider.to_s,
|
||||
"architecture" => @box.architecture,
|
||||
"version" => @box.version.to_s,
|
||||
}
|
||||
end
|
||||
|
||||
@ -30,6 +30,7 @@ module Vagrant
|
||||
# "uuid": {
|
||||
# "name": "foo",
|
||||
# "provider": "vmware_fusion",
|
||||
# "architecture": "amd64",
|
||||
# "data_path": "/path/to/data/dir",
|
||||
# "vagrantfile_path": "/path/to/Vagrantfile",
|
||||
# "state": "running",
|
||||
@ -381,6 +382,11 @@ module Vagrant
|
||||
# @return [String]
|
||||
attr_accessor :provider
|
||||
|
||||
# The name of the architecture.
|
||||
#
|
||||
# @return [String]
|
||||
attr_accessor :architecture
|
||||
|
||||
# The last known state of this machine.
|
||||
#
|
||||
# @return [String]
|
||||
@ -427,6 +433,7 @@ module Vagrant
|
||||
@local_data_path = raw["local_data_path"]
|
||||
@name = raw["name"]
|
||||
@provider = raw["provider"]
|
||||
@architecture = raw["architecture"]
|
||||
@state = raw["state"]
|
||||
@full_state = raw["full_state"]
|
||||
@vagrantfile_name = raw["vagrantfile_name"]
|
||||
@ -510,6 +517,7 @@ module Vagrant
|
||||
"local_data_path" => @local_data_path.to_s,
|
||||
"name" => @name,
|
||||
"provider" => @provider,
|
||||
"architecture" => @architecture,
|
||||
"state" => @state,
|
||||
"vagrantfile_name" => @vagrantfile_name,
|
||||
"vagrantfile_path" => @vagrantfile_path.to_s,
|
||||
|
||||
@ -46,7 +46,7 @@ module Vagrant
|
||||
autoload :Numeric, 'vagrant/util/numeric'
|
||||
autoload :Platform, 'vagrant/util/platform'
|
||||
autoload :Powershell, 'vagrant/util/powershell'
|
||||
autoload :Presence, 'vagrant/util/presence'
|
||||
autoload :Presence, 'vagrant/util/presence'
|
||||
autoload :Retryable, 'vagrant/util/retryable'
|
||||
autoload :SafeChdir, 'vagrant/util/safe_chdir'
|
||||
autoload :SafeEnv, 'vagrant/util/safe_env'
|
||||
|
||||
@ -17,6 +17,29 @@ module Vagrant
|
||||
class Platform
|
||||
class << self
|
||||
|
||||
# Detect architecture of host system
|
||||
#
|
||||
# @return [String]
|
||||
def architecture
|
||||
if !defined?(@_host_architecture)
|
||||
if ENV["VAGRANT_HOST_ARCHITECTURE"].to_s != ""
|
||||
return @_host_architecture = ENV["VAGRANT_HOST_ARCHITECTURE"]
|
||||
end
|
||||
|
||||
@_host_architecture = case RbConfig::CONFIG["target_cpu"]
|
||||
when "x86_64"
|
||||
"amd64"
|
||||
when "i386"
|
||||
"386"
|
||||
when "arm64", "aarch64"
|
||||
"arm64"
|
||||
else
|
||||
RbConfig::CONFIG["target_cpu"]
|
||||
end
|
||||
end
|
||||
@_host_architecture
|
||||
end
|
||||
|
||||
def logger
|
||||
if !defined?(@_logger)
|
||||
@_logger = Log4r::Logger.new("vagrant::util::platform")
|
||||
|
||||
@ -202,7 +202,7 @@ module Vagrant
|
||||
|
||||
# Load the box Vagrantfile, if there is one
|
||||
if !config.vm.box.to_s.empty? && boxes
|
||||
box = boxes.find(config.vm.box, box_formats, config.vm.box_version)
|
||||
box = boxes.find(config.vm.box, box_formats, config.vm.box_version, config.vm.box_architecture)
|
||||
if box
|
||||
box_vagrantfile = find_vagrantfile(box.directory)
|
||||
if box_vagrantfile && !config.vm.ignore_box_vagrantfile
|
||||
|
||||
@ -12,7 +12,9 @@ module VagrantPlugins
|
||||
include DownloadMixins
|
||||
|
||||
def execute
|
||||
options = {}
|
||||
options = {
|
||||
architecture: :auto,
|
||||
}
|
||||
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant box add [options] <name, url, or path>"
|
||||
@ -34,6 +36,10 @@ module VagrantPlugins
|
||||
options[:location_trusted] = l
|
||||
end
|
||||
|
||||
o.on("-a", "--architecture ARCH", String, "Architecture the box should satisfy") do |a|
|
||||
options[:architecture] = a
|
||||
end
|
||||
|
||||
o.on("--provider PROVIDER", String, "Provider the box should satisfy") do |p|
|
||||
options[:provider] = p
|
||||
end
|
||||
@ -82,6 +88,7 @@ module VagrantPlugins
|
||||
box_url: url,
|
||||
box_name: options[:name],
|
||||
box_provider: options[:provider],
|
||||
box_architecture: options[:architecture],
|
||||
box_version: options[:version],
|
||||
box_checksum_type: options[:checksum_type],
|
||||
box_checksum: options[:checksum],
|
||||
|
||||
@ -44,26 +44,33 @@ module VagrantPlugins
|
||||
longest_box = boxes.max_by { |x| x[0].length }
|
||||
longest_box_length = longest_box[0].length
|
||||
|
||||
# Go through each box and output the information about it. We
|
||||
# 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, version, provider|
|
||||
@env.ui.info("#{name.ljust(longest_box_length)} (#{provider}, #{version})")
|
||||
# Group boxes by name and version and start iterating
|
||||
boxes.group_by { |b| [b[0], b[1]] }.each do |box_info, box_data|
|
||||
name, version = box_info
|
||||
# Now group by provider so we can collect common architectures
|
||||
box_data.group_by { |b| b[2] }.each do |provider, data|
|
||||
architectures = data.map { |d| d.last }.compact.sort.uniq
|
||||
meta_info = [provider, version]
|
||||
if !architectures.empty?
|
||||
meta_info << "(#{architectures.join(", ")})"
|
||||
end
|
||||
@env.ui.info("#{name.ljust(longest_box_length)} (#{meta_info.join(", ")})")
|
||||
data.each do |arch_info|
|
||||
@env.ui.machine("box-name", name)
|
||||
@env.ui.machine("box-provider", provider)
|
||||
@env.ui.machine("box-version", version)
|
||||
@env.ui.machine("box-architecture", arch_info.last || "n/a")
|
||||
info_file = @env.boxes.find(name, provider, version, arch_info.last).
|
||||
directory.join("info.json")
|
||||
if info_file.file?
|
||||
info = JSON.parse(info_file.read)
|
||||
info.each do |k, v|
|
||||
@env.ui.machine("box-info", k, v)
|
||||
|
||||
@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, version).
|
||||
directory.join("info.json")
|
||||
if info_file.file?
|
||||
info = JSON.parse(info_file.read)
|
||||
info.each do |k, v|
|
||||
@env.ui.machine("box-info", k, v)
|
||||
|
||||
if extra_info
|
||||
@env.ui.info(" - #{k}: #{v}", prefix: false)
|
||||
if extra_info
|
||||
@env.ui.info(" - #{k}: #{v}", prefix: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -21,6 +21,9 @@ module VagrantPlugins
|
||||
options[:force] = f
|
||||
end
|
||||
|
||||
o.on("-a", "--architecture ARCH", String, "The specific architecture for the box to remove") do |a|
|
||||
options[:architecture] = a
|
||||
end
|
||||
o.on("--provider PROVIDER", String,
|
||||
"The specific provider type for the box to remove") do |p|
|
||||
options[:provider] = p
|
||||
@ -34,6 +37,14 @@ module VagrantPlugins
|
||||
o.on("--all", "Remove all available versions of the box") do |a|
|
||||
options[:all] = a
|
||||
end
|
||||
|
||||
o.on("--all-providers", "Remove all providers within a version of the box") do |a|
|
||||
options[:all_providers] = a
|
||||
end
|
||||
|
||||
o.on("--all-architectures", "Remove all architectures within a provider a version of the box") do |a|
|
||||
options[:all_architectures] = a
|
||||
end
|
||||
end
|
||||
|
||||
# Parse the options
|
||||
@ -54,10 +65,13 @@ module VagrantPlugins
|
||||
|
||||
@env.action_runner.run(Vagrant::Action.action_box_remove, {
|
||||
box_name: argv[0],
|
||||
box_architecture: options[:architecture],
|
||||
box_provider: options[:provider],
|
||||
box_version: options[:version],
|
||||
force_confirm_box_remove: options[:force],
|
||||
box_remove_all_versions: options[:all],
|
||||
box_remove_all_providers: options[:all_providers],
|
||||
box_remove_all_architectures: options[:all_architectures]
|
||||
})
|
||||
|
||||
# Success, exit status 0
|
||||
|
||||
@ -32,6 +32,10 @@ module VagrantPlugins
|
||||
options[:box] = b
|
||||
end
|
||||
|
||||
o.on("--architecture ARCHITECTURE", String, "Update box with specific architecture") do |a|
|
||||
options[:architecture] = a
|
||||
end
|
||||
|
||||
o.on("--provider PROVIDER", String, "Update box with specific provider") do |p|
|
||||
options[:provider] = p.to_sym
|
||||
end
|
||||
@ -47,7 +51,7 @@ module VagrantPlugins
|
||||
return if !argv
|
||||
|
||||
if options[:box]
|
||||
update_specific(options[:box], options[:provider], download_options, options[:force])
|
||||
update_specific(options[:box], options[:provider], options[:architecture], download_options, options[:force])
|
||||
else
|
||||
update_vms(argv, options[:provider], download_options, options[:force])
|
||||
end
|
||||
@ -55,41 +59,62 @@ module VagrantPlugins
|
||||
0
|
||||
end
|
||||
|
||||
def update_specific(name, provider, download_options, force)
|
||||
boxes = {}
|
||||
@env.boxes.all.each do |n, v, p|
|
||||
boxes[n] ||= {}
|
||||
boxes[n][p] ||= []
|
||||
boxes[n][p] << v
|
||||
def update_specific(name, provider, architecture, download_options, force)
|
||||
box_info = Vagrant::Util::HashWithIndifferentAccess.new
|
||||
@env.boxes.all.each do |box_name, box_version, box_provider, box_architecture|
|
||||
next if name != box_name
|
||||
box_info[box_provider] ||= Vagrant::Util::HashWithIndifferentAccess.new
|
||||
box_info[box_provider][box_version] ||= []
|
||||
box_info[box_provider][box_version].push(box_architecture.to_s).uniq!
|
||||
end
|
||||
|
||||
if !boxes[name]
|
||||
if box_info.empty?
|
||||
raise Vagrant::Errors::BoxNotFound, name: name.to_s
|
||||
end
|
||||
|
||||
if !provider
|
||||
if boxes[name].length > 1
|
||||
if box_info.size > 1
|
||||
raise Vagrant::Errors::BoxUpdateMultiProvider,
|
||||
name: name.to_s,
|
||||
providers: boxes[name].keys.map(&:to_s).sort.join(", ")
|
||||
providers: box_info.keys.map(&:to_s).sort.join(", ")
|
||||
end
|
||||
|
||||
provider = boxes[name].keys.first
|
||||
elsif !boxes[name][provider]
|
||||
provider = box_info.keys.first
|
||||
elsif !box_info[provider]
|
||||
raise Vagrant::Errors::BoxNotFoundWithProvider,
|
||||
name: name.to_s,
|
||||
provider: provider.to_s,
|
||||
providers: boxes[name].keys.map(&:to_s).sort.join(", ")
|
||||
providers: box_info.keys.map(&:to_s).sort.join(", ")
|
||||
end
|
||||
|
||||
to_update = [
|
||||
[name, provider, boxes[name][provider].sort_by{|n| Gem::Version.new(n)}.last],
|
||||
]
|
||||
version = box_info[provider].keys.sort_by{ |v| Gem::Version.new(v) }.last
|
||||
architecture_list = box_info[provider][version]
|
||||
|
||||
to_update.each do |n, p, v|
|
||||
box = @env.boxes.find(n, p, v)
|
||||
box_update(box, "> #{v}", @env.ui, download_options, force)
|
||||
if !architecture
|
||||
if architecture_list.size > 1
|
||||
raise Vagrant::Errors::BoxUpdateMultiArchitecture,
|
||||
name: name.to_s,
|
||||
provider: provider.to_s,
|
||||
version: version.to_s,
|
||||
architectures: architecture_list.sort.join(", ")
|
||||
end
|
||||
|
||||
architecture = architecture_list.first
|
||||
elsif !architecture_list.include?(architecture)
|
||||
raise Vagrant::Errors::BoxNotFoundWithProviderArchitecture,
|
||||
name: name.to_s,
|
||||
provider: provider.to_s,
|
||||
version: version.to_s,
|
||||
architecture: architecture,
|
||||
architectures: architecture_list.sort.join(", ")
|
||||
end
|
||||
|
||||
# Architecture gets cast to a string when collecting information
|
||||
# above. Convert it back to a nil if it's empty
|
||||
architecture = nil if architecture.to_s.empty?
|
||||
|
||||
box = @env.boxes.find(name, provider, version, architecture)
|
||||
box_update(box, "> #{version}", @env.ui, download_options, force)
|
||||
end
|
||||
|
||||
def update_vms(argv, provider, download_options, force)
|
||||
@ -146,6 +171,7 @@ module VagrantPlugins
|
||||
ui.detail("Latest installed version: #{box.version}")
|
||||
ui.detail("Version constraints: #{version}")
|
||||
ui.detail("Provider: #{box.provider}")
|
||||
ui.detail("Architecture: #{box.architecture.inspect}") if box.architecture
|
||||
|
||||
update = box.has_update?(version, download_options: download_options)
|
||||
if !update
|
||||
@ -165,6 +191,7 @@ module VagrantPlugins
|
||||
box_url: box.metadata_url,
|
||||
box_provider: update[2].name,
|
||||
box_version: update[1].version,
|
||||
box_architecture: update[2].architecture,
|
||||
ui: ui,
|
||||
box_force: force,
|
||||
box_download_client_cert: download_options[:client_cert],
|
||||
|
||||
@ -11,7 +11,12 @@ module VagrantPlugins
|
||||
include Util
|
||||
|
||||
def execute
|
||||
options = {quiet: true, versions: []}
|
||||
options = {
|
||||
architectures: [],
|
||||
providers: [],
|
||||
quiet: true,
|
||||
versions: [],
|
||||
}
|
||||
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant cloud box show [options] organization/box-name"
|
||||
@ -21,8 +26,14 @@ module VagrantPlugins
|
||||
o.separator "Options:"
|
||||
o.separator ""
|
||||
|
||||
o.on("--architectures ARCH", String, "Filter results by architecture support (can be defined multiple times)") do |a|
|
||||
options[:architectures].push(a).uniq!
|
||||
end
|
||||
o.on("--versions VERSION", String, "Display box information for a specific version (can be defined multiple times)") do |v|
|
||||
options[:versions] << v
|
||||
options[:versions].push(v).uniq!
|
||||
end
|
||||
o.on("--providers PROVIDER", String, "Filter results by provider support (can be defined multiple times)") do |pv|
|
||||
options[:providers].push(pv).uniq!
|
||||
end
|
||||
o.on("--[no-]auth", "Authenticate with Vagrant Cloud if required before searching") do |l|
|
||||
options[:quiet] = !l
|
||||
@ -40,7 +51,7 @@ module VagrantPlugins
|
||||
@client = client_login(@env, options.slice(:quiet))
|
||||
org, box_name = argv.first.split('/', 2)
|
||||
|
||||
show_box(org, box_name, @client&.token, options.slice(:versions))
|
||||
show_box(org, box_name, @client&.token, options.slice(:architectures, :providers, :versions))
|
||||
end
|
||||
|
||||
# Display the requested box to the user
|
||||
@ -57,26 +68,61 @@ module VagrantPlugins
|
||||
access_token: access_token
|
||||
)
|
||||
with_box(account: account, org: org, box: box_name) do |box|
|
||||
if box && !Array(options[:versions]).empty?
|
||||
box = box.versions.find_all{ |v| options[:versions].include?(v.version) }
|
||||
else
|
||||
box = [box]
|
||||
end
|
||||
list = [box]
|
||||
|
||||
if !box.empty?
|
||||
box.each do |b|
|
||||
format_box_results(b, @env)
|
||||
# If specific version(s) provided, filter out the version
|
||||
list = list.first.versions.find_all { |v|
|
||||
options[:versions].include?(v.version)
|
||||
} if !Array(options[:versions]).empty?
|
||||
|
||||
# If specific provider(s) provided, filter out the provider(s)
|
||||
list = list.find_all { |item|
|
||||
if item.is_a?(VagrantCloud::Box)
|
||||
item.versions.any? { |v|
|
||||
v.providers.any? { |p|
|
||||
options[:providers].include?(p.name)
|
||||
}
|
||||
}
|
||||
else
|
||||
item.providers.any? { |p|
|
||||
options[:providers].include?(p.name)
|
||||
}
|
||||
end
|
||||
} if !Array(options[:providers]).empty?
|
||||
|
||||
list = list.find_all { |item|
|
||||
if item.is_a?(VagrantCloud::Box)
|
||||
item.versions.any? { |v|
|
||||
v.providers.any? { |p|
|
||||
options[:architectures].include?(p.architecture)
|
||||
}
|
||||
}
|
||||
else
|
||||
item.providers.any? { |p|
|
||||
options[:architectures].include?(p.architecture)
|
||||
}
|
||||
end
|
||||
} if !Array(options[:architectures]).empty?
|
||||
|
||||
if !list.empty?
|
||||
list.each do |b|
|
||||
format_box_results(b, @env, options.slice(:providers, :architectures))
|
||||
@env.ui.output("")
|
||||
end
|
||||
0
|
||||
else
|
||||
@env.ui.warn(I18n.t("cloud_command.box.show_filter_empty",
|
||||
version: options[:version], org: org, box_name: box_name))
|
||||
org: org,
|
||||
box_name: box_name,
|
||||
architectures: Array(options[:architectures]).empty? ? "N/A" : Array(options[:architectures]).join(", "),
|
||||
providers: Array(options[:providers]).empty? ? "N/A" : Array(options[:providers]).join(", "),
|
||||
versions: Array(options[:versions]).empty? ? "N/A" : Array(options[:versions]).join(", ")
|
||||
))
|
||||
1
|
||||
end
|
||||
end
|
||||
rescue VagrantCloud::Error => e
|
||||
@env.ui.error(I18n.t("cloud_command.errors.box.show_fail", org: org,box_name:box_name))
|
||||
@env.ui.error(I18n.t("cloud_command.errors.box.show_fail", org: org, box_name:box_name))
|
||||
@env.ui.error(e.message)
|
||||
1
|
||||
end
|
||||
|
||||
@ -168,6 +168,11 @@ EOH
|
||||
|
||||
def with_error_handling(&block)
|
||||
yield
|
||||
rescue VagrantCloud::Error::ClientError => e
|
||||
@logger.debug("vagrantcloud request error:")
|
||||
@logger.debug(e.message)
|
||||
@logger.debug(e.backtrace.join("\n"))
|
||||
raise Errors::Unexpected, error: e.message
|
||||
rescue Excon::Error::Unauthorized
|
||||
@logger.debug("Unauthorized!")
|
||||
raise Errors::Unauthorized
|
||||
|
||||
@ -39,13 +39,25 @@ en:
|
||||
Box Description: %{description}
|
||||
box_short_desc: |-
|
||||
Box Short Description: %{short_description}
|
||||
checksum_type: |-
|
||||
Checksum Type: %{checksum_type}
|
||||
checksum_value: |-
|
||||
Checksum Value: %{checksum_value}
|
||||
architecture: |-
|
||||
Box Architecture: %{architecture}
|
||||
default_architecture: |-
|
||||
Default Architecture: true
|
||||
version_desc: |-
|
||||
Version Description: %{version_description}
|
||||
continue: |-
|
||||
Do you wish to continue? [y/N]
|
||||
box:
|
||||
show_filter_empty: |-
|
||||
No version matched %{version} for %{org}/%{box_name}
|
||||
No matches found for %{org}/%{box_name}
|
||||
Filters applied:
|
||||
Architectures: %{architectures}
|
||||
Providers: %{providers}
|
||||
Versions: %{versions}
|
||||
create_success: |-
|
||||
Created box %{org}/%{box_name}
|
||||
delete_success: |-
|
||||
@ -68,12 +80,19 @@ en:
|
||||
Uploading box file for '%{org}/%{box_name}' (v%{version}) for provider: '%{provider}'
|
||||
upload_success: |-
|
||||
Uploaded provider %{provider} on %{org}/%{box_name} for version %{version}
|
||||
delete_multiple_architectures: |-
|
||||
Multiple architectures detected for %{provider} on %{org}/%{box_name}:
|
||||
|
||||
delete_architectures_prompt: |-
|
||||
Please enter the architecture name to delete:
|
||||
delete_warn: |-
|
||||
This will completely remove provider %{provider} on version %{version} from %{box} on Vagrant Cloud. This cannot be undone.
|
||||
This will completely remove provider %{provider} with architecture %{architecture}
|
||||
on version %{version} from %{box} on Vagrant Cloud. This cannot be undone.
|
||||
create_success: |-
|
||||
Created provider %{provider} on %{org}/%{box_name} for version %{version}
|
||||
Created provider %{provider} with %{architecture} architecture on %{org}/%{box_name} for version %{version}
|
||||
delete_success: |-
|
||||
Deleted provider %{provider} on %{org}/%{box_name} for version %{version}
|
||||
Deleted provider %{provider} with %{architecture} architecture on %{org}/%{box_name}
|
||||
for version %{version}
|
||||
update_success: |-
|
||||
Updated provider %{provider} on %{org}/%{box_name} for version %{version}
|
||||
not_found: |-
|
||||
@ -122,13 +141,13 @@ en:
|
||||
Failed to locate account information
|
||||
provider:
|
||||
create_fail: |-
|
||||
Failed to create provider %{provider} on box %{org}/%{box_name} for version %{version}
|
||||
Failed to create '%{architecture}' variant of %{provider} provider for version %{version} of the %{org}/%{box_name} box
|
||||
update_fail: |-
|
||||
Failed to update provider %{provider} on box %{org}/%{box_name} for version %{version}
|
||||
Failed to update '%{architecture}' variant of %{provider} provider for version %{version} of the %{org}/%{box_name} box
|
||||
delete_fail: |-
|
||||
Failed to delete provider %{provider} on box %{org}/%{box_name} for version %{version}
|
||||
Failed to delete '%{architecture}' variant of %{provider} provider for version %{version} of the %{org}/%{box_name} box
|
||||
upload_fail: |-
|
||||
Failed to upload provider %{provider} on box %{org}/%{box_name} for version %{version}
|
||||
Failed to upload '%{architecture}' variant of %{provider} provider for version %{version} of the %{org}/%{box_name} box
|
||||
version:
|
||||
create_fail: |-
|
||||
Failed to create version %{version} on box %{org}/%{box_name}
|
||||
|
||||
@ -11,7 +11,9 @@ module VagrantPlugins
|
||||
include Util
|
||||
|
||||
def execute
|
||||
options = {}
|
||||
options = {
|
||||
architecture: Vagrant::Util::Platform.architecture,
|
||||
}
|
||||
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant cloud provider create [options] organization/box-name provider-name version [url]"
|
||||
@ -21,12 +23,18 @@ module VagrantPlugins
|
||||
o.separator "Options:"
|
||||
o.separator ""
|
||||
|
||||
o.on("-a", "--architecture ARCH", String, "Architecture of guest box (defaults to current host architecture)") do |a|
|
||||
options[:architecture] = a
|
||||
end
|
||||
o.on("-c", "--checksum CHECKSUM_VALUE", String, "Checksum of the box for this provider. --checksum-type option is required.") do |c|
|
||||
options[:checksum] = c
|
||||
end
|
||||
o.on("-C", "--checksum-type TYPE", String, "Type of checksum used (md5, sha1, sha256, sha384, sha512). --checksum option is required.") do |c|
|
||||
options[:checksum_type] = c
|
||||
end
|
||||
o.on("--[no-]default-architecture", "Mark as default architecture for specific provider") do |d|
|
||||
options[:default_architecture] = d
|
||||
end
|
||||
end
|
||||
|
||||
# Parse the options
|
||||
@ -56,8 +64,10 @@ module VagrantPlugins
|
||||
# @param [String] url Provider asset URL
|
||||
# @param [String] access_token User Vagrant Cloud access token
|
||||
# @param [Hash] options
|
||||
# @option options [String] :architecture Architecture of guest box
|
||||
# @option options [String] :checksum Checksum of the box asset
|
||||
# @option options [String] :checksum_type Type of the checksum
|
||||
# @option options [Boolean] :default_architecture Default architecture for named provider
|
||||
# @return [Integer]
|
||||
def create_provider(org, box, version, provider, url, access_token, options={})
|
||||
if !url
|
||||
@ -68,21 +78,23 @@ module VagrantPlugins
|
||||
access_token: access_token
|
||||
)
|
||||
with_version(account: account, org: org, box: box, version: version) do |version|
|
||||
provider = version.add_provider(provider)
|
||||
provider = version.add_provider(provider, options[:architecture])
|
||||
provider.checksum = options[:checksum] if options.key?(:checksum)
|
||||
provider.checksum_type = options[:checksum_type] if options.key?(:checksum_type)
|
||||
provider.architecture = options[:architecture] if options.key?(:architecture)
|
||||
provider.default_architecture = options[:default_architecture] if options.key?(:default_architecture)
|
||||
provider.url = url if url
|
||||
|
||||
provider.save
|
||||
|
||||
@env.ui.success(I18n.t("cloud_command.provider.create_success",
|
||||
provider: provider.name, org: org, box_name: box, version: version.version))
|
||||
architecture: options[:architecture], provider: provider.name, org: org, box_name: box, version: version.version))
|
||||
format_box_results(provider, @env)
|
||||
0
|
||||
end
|
||||
rescue VagrantCloud::Error => e
|
||||
@env.ui.error(I18n.t("cloud_command.errors.provider.create_fail",
|
||||
provider: provider, org: org, box_name: box, version: version))
|
||||
architecture: options[:architecture], provider: provider.name, org: org, box_name: box, version: version))
|
||||
@env.ui.error(e.message)
|
||||
1
|
||||
end
|
||||
|
||||
@ -14,7 +14,7 @@ module VagrantPlugins
|
||||
options = {}
|
||||
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant cloud provider delete [options] organization/box-name provider-name version"
|
||||
o.banner = "Usage: vagrant cloud provider delete [options] organization/box-name provider-name version [architecture]"
|
||||
o.separator ""
|
||||
o.separator "Deletes a provider entry on Vagrant Cloud"
|
||||
o.separator ""
|
||||
@ -28,7 +28,7 @@ module VagrantPlugins
|
||||
# Parse the options
|
||||
argv = parse_options(opts)
|
||||
return if !argv
|
||||
if argv.count != 3
|
||||
if argv.count < 3 || argv.count > 4
|
||||
raise Vagrant::Errors::CLIInvalidUsage,
|
||||
help: opts.help.chomp
|
||||
end
|
||||
@ -36,18 +36,47 @@ module VagrantPlugins
|
||||
org, box_name = argv.first.split('/', 2)
|
||||
provider_name = argv[1]
|
||||
version = argv[2]
|
||||
architecture = argv[3]
|
||||
|
||||
@client = client_login(@env)
|
||||
account = VagrantCloud::Account.new(
|
||||
custom_server: api_server_url,
|
||||
access_token: @client.token
|
||||
)
|
||||
|
||||
if architecture.nil?
|
||||
architecture = select_provider_architecture(account, org, box_name, version, provider_name)
|
||||
end
|
||||
|
||||
@env.ui.warn(I18n.t("cloud_command.provider.delete_warn",
|
||||
provider: provider_name, version:version, box: argv.first))
|
||||
architecture: architecture, provider: provider_name, version: version, box: argv.first))
|
||||
|
||||
if !options[:force]
|
||||
cont = @env.ui.ask(I18n.t("cloud_command.continue"))
|
||||
return 1 if cont.strip.downcase != "y"
|
||||
end
|
||||
|
||||
@client = client_login(@env)
|
||||
delete_provider(org, box_name, version, provider_name, architecture, account, options)
|
||||
end
|
||||
|
||||
delete_provider(org, box_name, version, provider_name, @client.token, options)
|
||||
def select_provider_architecture(account, org, box, version, provider)
|
||||
with_version(account: account, org: org, box: box, version: version) do |box_version|
|
||||
list = box_version.providers.map(&:architecture)
|
||||
return list.first if list.size == 1
|
||||
|
||||
@env.ui.info(I18n.t("cloud_command.provider.delete_multiple_architectures",
|
||||
org: org, box_name: box, provider: provider))
|
||||
list.each do |provider_name|
|
||||
@env.ui.info(" * #{provider_name}")
|
||||
end
|
||||
selected = nil
|
||||
while selected.nil?
|
||||
user_input = @env.ui.ask(I18n.t("cloud_command.provider.delete_architectures_prompt") + " ")
|
||||
selected = user_input if list.include?(user_input)
|
||||
end
|
||||
|
||||
return selected
|
||||
end
|
||||
end
|
||||
|
||||
# Delete a provider for the box version
|
||||
@ -56,23 +85,20 @@ module VagrantPlugins
|
||||
# @param [String] box Box name
|
||||
# @param [String] version Box version
|
||||
# @param [String] provider Provider name
|
||||
# @param [String] access_token User Vagrant Cloud access token
|
||||
# @param [String] architecture Architecture of guest
|
||||
# @param [VagrantCloud::Account] account VagrantCloud account
|
||||
# @param [Hash] options Currently unused
|
||||
# @return [Integer]
|
||||
def delete_provider(org, box, version, provider, access_token, options={})
|
||||
account = VagrantCloud::Account.new(
|
||||
custom_server: api_server_url,
|
||||
access_token: access_token
|
||||
)
|
||||
with_provider(account: account, org: org, box: box, version: version, provider: provider) do |p|
|
||||
def delete_provider(org, box, version, provider, architecture, account, options={})
|
||||
with_provider(account: account, org: org, box: box, version: version, provider: provider, architecture: architecture) do |p|
|
||||
p.delete
|
||||
@env.ui.error(I18n.t("cloud_command.provider.delete_success",
|
||||
provider: provider, org: org, box_name: box, version: version))
|
||||
architecture: architecture, provider: provider, org: org, box_name: box, version: version))
|
||||
0
|
||||
end
|
||||
rescue VagrantCloud::Error => e
|
||||
@env.ui.error(I18n.t("cloud_command.errors.provider.delete_fail",
|
||||
provider: provider, org: org, box_name: box, version: version))
|
||||
architecture: architecture, provider: provider, org: org, box_name: box, version: version))
|
||||
@env.ui.error(e)
|
||||
1
|
||||
end
|
||||
|
||||
@ -14,25 +14,31 @@ module VagrantPlugins
|
||||
options = {}
|
||||
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant cloud provider update [options] organization/box-name provider-name version [url]"
|
||||
o.banner = "Usage: vagrant cloud provider update [options] organization/box-name provider-name version architecture [url]"
|
||||
o.separator ""
|
||||
o.separator "Updates a provider entry on Vagrant Cloud"
|
||||
o.separator ""
|
||||
o.separator "Options:"
|
||||
o.separator ""
|
||||
|
||||
o.on("-a", "--architecture ARCH", String, "Update architecture value of guest box") do |a|
|
||||
options[:architecture] = a
|
||||
end
|
||||
o.on("-c", "--checksum CHECKSUM_VALUE", String, "Checksum of the box for this provider. --checksum-type option is required.") do |c|
|
||||
options[:checksum] = c
|
||||
end
|
||||
o.on("-C", "--checksum-type TYPE", String, "Type of checksum used (md5, sha1, sha256, sha384, sha512). --checksum option is required.") do |c|
|
||||
options[:checksum_type] = c
|
||||
end
|
||||
o.on("--[no-]default-architecture", "Mark as default architecture for specific provider") do |d|
|
||||
options[:default_architecture] = d
|
||||
end
|
||||
end
|
||||
|
||||
# Parse the options
|
||||
argv = parse_options(opts)
|
||||
return if !argv
|
||||
if argv.count < 3 || argv.count > 4
|
||||
if argv.count < 4 || argv.count > 5
|
||||
raise Vagrant::Errors::CLIInvalidUsage,
|
||||
help: opts.help.chomp
|
||||
end
|
||||
@ -42,9 +48,10 @@ module VagrantPlugins
|
||||
org, box_name = argv.first.split('/', 2)
|
||||
provider_name = argv[1]
|
||||
version = argv[2]
|
||||
url = argv[3]
|
||||
architecture = argv[3]
|
||||
url = argv[4]
|
||||
|
||||
update_provider(org, box_name, version, provider_name, url, @client.token, options)
|
||||
update_provider(org, box_name, version, provider_name, architecture, url, @client.token, options)
|
||||
end
|
||||
|
||||
# Update a provider for the box version
|
||||
@ -53,12 +60,13 @@ module VagrantPlugins
|
||||
# @param [String] box Box name
|
||||
# @param [String] version Box version
|
||||
# @param [String] provider Provider name
|
||||
# @param [String] architecture Architecture of guest
|
||||
# @param [String] access_token User Vagrant Cloud access token
|
||||
# @param [Hash] options
|
||||
# @option options [String] :checksum Checksum of the box asset
|
||||
# @option options [String] :checksum_type Type of the checksum
|
||||
# @return [Integer]
|
||||
def update_provider(org, box, version, provider, url, access_token, options)
|
||||
def update_provider(org, box, version, provider, architecture, url, access_token, options)
|
||||
if !url
|
||||
@env.ui.warn(I18n.t("cloud_command.upload.no_url"))
|
||||
end
|
||||
@ -67,21 +75,23 @@ module VagrantPlugins
|
||||
access_token: access_token
|
||||
)
|
||||
|
||||
with_provider(account: account, org: org, box: box, version: version, provider: provider) do |p|
|
||||
with_provider(account: account, org: org, box: box, version: version, provider: provider, architecture: architecture) do |p|
|
||||
p.checksum = options[:checksum] if options.key?(:checksum)
|
||||
p.checksum_type = options[:checksum_type] if options.key?(:checksum_type)
|
||||
p.architecture = options[:architecture] if options.key?(:architecture)
|
||||
p.default_architecture = options[:default_architecture] if options.key?(:default_architecture)
|
||||
p.url = url if !url.nil?
|
||||
p.save
|
||||
|
||||
@env.ui.success(I18n.t("cloud_command.provider.update_success",
|
||||
provider: provider, org: org, box_name: box, version: version))
|
||||
architecture: architecture, provider: provider, org: org, box_name: box, version: version))
|
||||
|
||||
format_box_results(p, @env)
|
||||
0
|
||||
end
|
||||
rescue VagrantCloud::Error => e
|
||||
@env.ui.error(I18n.t("cloud_command.errors.provider.update_fail",
|
||||
provider: provider, org: org, box_name: box, version: version))
|
||||
architecture: architecture, provider: provider, org: org, box_name: box, version: version))
|
||||
@env.ui.error(e.message)
|
||||
1
|
||||
end
|
||||
|
||||
@ -11,7 +11,10 @@ module VagrantPlugins
|
||||
include Util
|
||||
|
||||
def execute
|
||||
options = {direct_upload: true}
|
||||
options = {
|
||||
architecture: Vagrant::Util::Platform.architecture,
|
||||
direct_upload: true,
|
||||
}
|
||||
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant cloud publish [options] organization/box-name version provider-name [provider-file]"
|
||||
@ -21,8 +24,8 @@ module VagrantPlugins
|
||||
o.separator "Options:"
|
||||
o.separator ""
|
||||
|
||||
o.on("--box-version VERSION", String, "Version of box to create") do |v|
|
||||
options[:box_version] = v
|
||||
o.on("-a", "--architecture ARCH", String, "Architecture of guest box (defaults to current host architecture)") do |a|
|
||||
options[:architecture] = a
|
||||
end
|
||||
o.on("--url URL", String, "Remote URL to download this provider (cannot be used with provider-file)") do |u|
|
||||
options[:url] = u
|
||||
@ -54,6 +57,9 @@ module VagrantPlugins
|
||||
o.on("--[no-]direct-upload", "Upload asset directly to backend storage") do |d|
|
||||
options[:direct_upload] = d
|
||||
end
|
||||
o.on("--[no-]default-architecture", "Mark as default architecture for specific provider") do |d|
|
||||
options[:default_architecture] = d
|
||||
end
|
||||
end
|
||||
|
||||
# Parse the options
|
||||
@ -63,7 +69,7 @@ module VagrantPlugins
|
||||
if argv.length < 3 || # missing required arguments
|
||||
argv.length > 4 || # too many arguments
|
||||
(argv.length < 4 && !options.key?(:url)) || # file argument required if url is not provided
|
||||
(argv.length > 3 && options.key?(:url)) # cannot provider url and file argument
|
||||
(argv.length > 3 && options.key?(:url)) # cannot provide url and file argument
|
||||
raise Vagrant::Errors::CLIInvalidUsage,
|
||||
help: opts.help.chomp
|
||||
end
|
||||
@ -78,7 +84,8 @@ module VagrantPlugins
|
||||
|
||||
@client = client_login(@env)
|
||||
params = options.slice(:private, :release, :url, :short_description,
|
||||
:description, :version_description, :checksum, :checksum_type)
|
||||
:description, :version_description, :checksum, :checksum_type,
|
||||
:architecture, :default_architecture)
|
||||
|
||||
# Display output to user describing action to be taken
|
||||
display_preamble(org, box_name, version, provider_name, params)
|
||||
@ -91,12 +98,17 @@ module VagrantPlugins
|
||||
# Load up all the models we'll need to publish the asset
|
||||
box = load_box(org, box_name, @client.token)
|
||||
box_v = load_box_version(box, version)
|
||||
box_p = load_version_provider(box_v, provider_name)
|
||||
box_p = load_version_provider(box_v, provider_name, params[:architecture])
|
||||
|
||||
# Update all the data
|
||||
set_box_info(box, params.slice(:private, :short_description, :description))
|
||||
set_version_info(box_v, params.slice(:version_description))
|
||||
set_provider_info(box_p, params.slice(:checksum, :checksum_type, :url))
|
||||
set_provider_info(box_p, params.slice(
|
||||
:architecture,
|
||||
:checksum,
|
||||
:checksum_type,
|
||||
:default_architecture,
|
||||
:url))
|
||||
|
||||
# Save any updated state
|
||||
@env.ui.warn(I18n.t("cloud_command.publish.box_save"))
|
||||
@ -114,7 +126,7 @@ module VagrantPlugins
|
||||
|
||||
# And we're done!
|
||||
@env.ui.success(I18n.t("cloud_command.publish.complete", org: org, box_name: box_name))
|
||||
format_box_results(box, @env)
|
||||
format_box_results(box_p, @env)
|
||||
0
|
||||
rescue VagrantCloud::Error => err
|
||||
@env.ui.error(I18n.t("cloud_command.errors.publish.fail", org: org, box_name: box_name))
|
||||
@ -188,14 +200,18 @@ module VagrantPlugins
|
||||
#
|
||||
# @param [VagrantCloud::Box::Provider] provider Vagrant Cloud box version provider
|
||||
# @param [Hash] options
|
||||
# @option options [String] architecture Guest architecture of box
|
||||
# @option options [String] :url Remote URL for self hosted
|
||||
# @option options [String] :checksum_type Type of checksum value provided
|
||||
# @option options [String] :checksum Checksum of the box asset
|
||||
# @option options [Boolean] :default_architecture Default architecture for named provider
|
||||
# @return [VagrantCloud::Box::Provider]
|
||||
def set_provider_info(provider, options={})
|
||||
provider.url = options[:url] if options.key?(:url)
|
||||
provider.checksum_type = options[:checksum_type] if options.key?(:checksum_type)
|
||||
provider.checksum = options[:checksum] if options.key?(:checksum)
|
||||
provider.architecture = options[:architecture] if options.key?(:architecture)
|
||||
provider.default_architecture = options[:default_architecture] if options.key?(:default_architecture)
|
||||
provider
|
||||
end
|
||||
|
||||
@ -204,10 +220,13 @@ module VagrantPlugins
|
||||
# @param [VagrantCloud::Box::Version] version The version of the Vagrant Cloud box
|
||||
# @param [String] provider_name Name of the provider
|
||||
# @return [VagrantCloud::Box::Provider]
|
||||
def load_version_provider(version, provider_name)
|
||||
provider = version.providers.detect { |pv| pv.name == provider_name }
|
||||
def load_version_provider(version, provider_name, architecture)
|
||||
provider = version.providers.detect { |pv|
|
||||
pv.name == provider_name &&
|
||||
pv.architecture == architecture
|
||||
}
|
||||
return provider if provider
|
||||
version.add_provider(provider_name)
|
||||
version.add_provider(provider_name, architecture)
|
||||
end
|
||||
|
||||
# Load the requested box version
|
||||
@ -244,9 +263,11 @@ module VagrantPlugins
|
||||
# @param [String] version Version of the box to publish
|
||||
# @param [String] provider_name Name of the provider being published
|
||||
# @param [Hash] options
|
||||
# @option options [String] :architecture Name of architecture of provider being published#
|
||||
# @option options [Boolean] :private Box is private
|
||||
# @option options [Boolean] :release Box should be released
|
||||
# @option options [String] :url Remote URL for self-hosted boxes
|
||||
# @option options [Boolean] :default_architecture Architecture is default for provider name
|
||||
# @option options [String] :description Description of the box
|
||||
# @option options [String] :short_description Short description of the box
|
||||
# @option options [String] :version_description Description of the box version
|
||||
@ -257,6 +278,9 @@ module VagrantPlugins
|
||||
box_name: box_name, version: version, provider_name: provider_name))
|
||||
@env.ui.info(I18n.t("cloud_command.publish.confirm.private")) if options[:private]
|
||||
@env.ui.info(I18n.t("cloud_command.publish.confirm.release")) if options[:release]
|
||||
@env.ui.info(I18n.t("cloud_command.publish.confirm.architecture",
|
||||
architecture: options[:architecture]))
|
||||
@env.ui.info(I18n.t("cloud_command.publish.confirm.default_architecture")) if options[:default_architecture]
|
||||
@env.ui.info(I18n.t("cloud_command.publish.confirm.box_url",
|
||||
url: options[:url])) if options[:url]
|
||||
@env.ui.info(I18n.t("cloud_command.publish.confirm.box_description",
|
||||
|
||||
@ -21,6 +21,9 @@ module VagrantPlugins
|
||||
o.separator "Options:"
|
||||
o.separator ""
|
||||
|
||||
o.on("-a", "--architecture ARCH", "Filter search results to a single architecture. Defaults to all.") do |a|
|
||||
options[:architecture] = a
|
||||
end
|
||||
o.on("-j", "--json", "Formats results in JSON") do |j|
|
||||
options[:json] = j
|
||||
end
|
||||
@ -78,7 +81,7 @@ module VagrantPlugins
|
||||
custom_server: api_server_url,
|
||||
access_token: access_token
|
||||
)
|
||||
params = {query: query}.merge(options.slice(:provider, :sort, :order, :limit, :page))
|
||||
params = {query: query}.merge(options.slice(:architecture, :provider, :sort, :order, :limit, :page))
|
||||
result = account.searcher.search(**params)
|
||||
|
||||
if result.boxes.empty?
|
||||
|
||||
@ -102,7 +102,8 @@ module VagrantPlugins
|
||||
name: b.tag,
|
||||
version: b.current_version.version,
|
||||
downloads: format_downloads(b.downloads.to_s),
|
||||
providers: b.current_version.providers.map(&:name).join(", ")
|
||||
providers: b.current_version.providers.map(&:name).uniq.join(", "),
|
||||
architectures: b.current_version.providers.map(&:architecture).join(", ")
|
||||
}
|
||||
end
|
||||
|
||||
@ -126,19 +127,26 @@ module VagrantPlugins
|
||||
# @param [VagrantCloud::Box, VagrantCloud::Box::Version] box Box or box version to display
|
||||
# @param [Vagrant::Environment] env Current Vagrant environment
|
||||
# @return [nil]
|
||||
def format_box_results(box, env)
|
||||
def format_box_results(box, env, options={})
|
||||
if box.is_a?(VagrantCloud::Box)
|
||||
info = box_info(box)
|
||||
elsif box.is_a?(VagrantCloud::Box::Provider)
|
||||
info = version_info(box.version)
|
||||
else
|
||||
info = box_info(box, options)
|
||||
elsif box.is_a?(VagrantCloud::Box::Version)
|
||||
info = version_info(box)
|
||||
else
|
||||
info = provider_info(box)
|
||||
end
|
||||
|
||||
width = info.keys.map(&:size).max
|
||||
info.each do |k, v|
|
||||
whitespace = width - k.size
|
||||
env.ui.info "#{k}: #{"".ljust(whitespace)} #{v}"
|
||||
v.to_s.split("\n").each_with_index do |line, idx|
|
||||
whitespace = width - k.size + line.to_s.size
|
||||
if idx == 0
|
||||
env.ui.info "#{k}: #{line.rjust(whitespace)}"
|
||||
else
|
||||
whitespace += k.size + 2
|
||||
env.ui.info line.rjust(whitespace)
|
||||
end
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
@ -193,9 +201,12 @@ module VagrantPlugins
|
||||
# @yieldparam [VagrantCloud::Box::Provider] provider Requested Vagrant Cloud box version provider
|
||||
# @yieldreturn [Integer]
|
||||
# @return [Integer]
|
||||
def with_provider(account:, org:, box:, version:, provider:)
|
||||
def with_provider(account:, org:, box:, version:, provider:, architecture:)
|
||||
with_version(account: account, org: org, box: box, version: version) do |v|
|
||||
p = v.providers.detect { |p| p.name == provider }
|
||||
p = v.providers.detect { |p|
|
||||
p.name == provider &&
|
||||
p.architecture == architecture
|
||||
}
|
||||
if !p
|
||||
@env.ui.error(I18n.t("cloud_command.provider.not_found",
|
||||
org: org, box_name: box, version: version, provider_name: provider))
|
||||
@ -211,19 +222,42 @@ module VagrantPlugins
|
||||
#
|
||||
# @param [VagrantCloud::Box] box Box for extracting information
|
||||
# @return [Hash<String,String>]
|
||||
def box_info(box)
|
||||
def box_info(box, options={})
|
||||
current_version = box.current_version
|
||||
if current_version
|
||||
current_version = nil if !Array(options[:providers]).empty? &&
|
||||
current_version.providers.none? { |p| options[:providers].include?(p.name) }
|
||||
current_version = nil if !Array(options[:architectures]).empty? &&
|
||||
current_version.providers.none? { |p| options[:architectures].include?(p.architecture) }
|
||||
end
|
||||
versions = box.versions
|
||||
# Apply provider filter if defined
|
||||
versions = versions.find_all { |v|
|
||||
v.providers.any? { |p|
|
||||
options[:providers].include?(p.name)
|
||||
}
|
||||
} if !Array(options[:providers]).empty?
|
||||
# Apply architecture filter if defined
|
||||
versions = versions.find_all { |v|
|
||||
v.providers.any? { |p|
|
||||
options[:architectures].include?(p.architecture)
|
||||
}
|
||||
} if !Array(options[:architectures]).empty?
|
||||
|
||||
raise "no matches" if current_version.nil? && versions.empty?
|
||||
|
||||
Hash.new.tap do |i|
|
||||
i["Box"] = box.tag
|
||||
i["Description"] = box.description
|
||||
i["Private"] = box.private ? "yes" : "no"
|
||||
i["Created"] = box.created_at
|
||||
i["Updated"] = box.updated_at
|
||||
if !box.current_version.nil?
|
||||
if !current_version.nil?
|
||||
i["Current Version"] = box.current_version.version
|
||||
else
|
||||
i["Current Version"] = "N/A"
|
||||
end
|
||||
i["Versions"] = box.versions.slice(0, 5).map(&:version).join(", ")
|
||||
i["Versions"] = versions.slice(0, 5).map(&:version).join(", ")
|
||||
if box.versions.size > 5
|
||||
i["Versions"] += " ..."
|
||||
end
|
||||
@ -236,17 +270,35 @@ module VagrantPlugins
|
||||
# @param [VagrantCloud::Box::Version] version Box version for extracting information
|
||||
# @return [Hash<String,String>]
|
||||
def version_info(version)
|
||||
provider_arches = version.providers.group_by(&:name).map { |provider_name, info|
|
||||
"#{provider_name} (#{info.map(&:architecture).sort.join(", ")})"
|
||||
}.sort.join("\n")
|
||||
Hash.new.tap do |i|
|
||||
i["Box"] = version.box.tag
|
||||
i["Version"] = version.version
|
||||
i["Description"] = version.description
|
||||
i["Status"] = version.status
|
||||
i["Providers"] = version.providers.map(&:name).sort.join(", ")
|
||||
i["Providers"] = provider_arches
|
||||
i["Created"] = version.created_at
|
||||
i["Updated"] = version.updated_at
|
||||
end
|
||||
end
|
||||
|
||||
# Extract provider information for display
|
||||
#
|
||||
# @param [VagrantCloud::Box::Provider] provider Box provider for extracting information
|
||||
# @return [Hash<String,String>]
|
||||
def provider_info(provider)
|
||||
{
|
||||
"Box" => provider.version.box.tag,
|
||||
"Private" => provider.version.box.private ? "yes" : "no",
|
||||
"Version" => provider.version.version,
|
||||
"Provider" => provider.name,
|
||||
"Architecture" => provider.architecture,
|
||||
"Default Architecture" => provider.default_architecture ? "yes" : "no",
|
||||
}
|
||||
end
|
||||
|
||||
# Print table results from search request
|
||||
#
|
||||
# @param [Vagrant::Environment] env Current Vagrant environment
|
||||
|
||||
@ -32,6 +32,7 @@ module VagrantPlugins
|
||||
attr_accessor :base_address
|
||||
attr_accessor :boot_timeout
|
||||
attr_accessor :box
|
||||
attr_accessor :box_architecture
|
||||
attr_accessor :ignore_box_vagrantfile
|
||||
attr_accessor :box_check_update
|
||||
attr_accessor :box_url
|
||||
@ -69,6 +70,7 @@ module VagrantPlugins
|
||||
@base_address = UNSET_VALUE
|
||||
@boot_timeout = UNSET_VALUE
|
||||
@box = UNSET_VALUE
|
||||
@box_architecture = UNSET_VALUE
|
||||
@ignore_box_vagrantfile = UNSET_VALUE
|
||||
@box_check_update = UNSET_VALUE
|
||||
@box_download_ca_cert = UNSET_VALUE
|
||||
@ -509,6 +511,11 @@ module VagrantPlugins
|
||||
@base_address = nil if @base_address == UNSET_VALUE
|
||||
@boot_timeout = 300 if @boot_timeout == UNSET_VALUE
|
||||
@box = nil if @box == UNSET_VALUE
|
||||
@box_architecture = :auto if @box_architecture == UNSET_VALUE
|
||||
# If box architecture value was set, force to string
|
||||
if @box_architecture && @box_architecture != :auto
|
||||
@box_architecture = @box_architecture.to_s
|
||||
end
|
||||
@ignore_box_vagrantfile = false if @ignore_box_vagrantfile == UNSET_VALUE
|
||||
|
||||
if @box_check_update == UNSET_VALUE
|
||||
|
||||
@ -659,6 +659,13 @@ en:
|
||||
the box are shown below:
|
||||
|
||||
%{providers}
|
||||
box_not_found_with_provider_architecture: |-
|
||||
The box '%{name}' for the provider '%{provider}' isn't installed
|
||||
for the architecture '%{architecture}'. Please double-check and
|
||||
try again. The installed architectures for the box are shown
|
||||
below:
|
||||
|
||||
%{architectures}
|
||||
box_not_found_with_provider_and_version: |-
|
||||
The box '%{name}' (v%{version}) with provider '%{provider}'
|
||||
could not be found. Please double check and try again. You
|
||||
@ -669,35 +676,51 @@ en:
|
||||
Provider expected: %{expected}
|
||||
Provider of box: %{actual}
|
||||
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`.
|
||||
You requested to remove the box '%{name}' version '%{version}'. This
|
||||
box has multiple providers. You must explicitly select a single provider to
|
||||
remove with `--provider` or specify the `--all-providers` flag to remove
|
||||
all providers.
|
||||
|
||||
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 or specify the `--all` flag to remove all
|
||||
versions. The available versions for this box are:
|
||||
You requested to remove the box '%{name}'. This box has multiple
|
||||
versions. You must explicitly specify which version you want to
|
||||
remove with the `--box-version` flag or specify the `--all` flag
|
||||
to remove all versions. The available versions for this box are:
|
||||
|
||||
%{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.
|
||||
You requested to remove the box '%{name}' version '%{version}'
|
||||
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_remove_version_not_found: |-
|
||||
You requested to remove the box '%{name}' version '%{version}' with
|
||||
provider '%{provider}', but that specific version of the box is
|
||||
not installed. Please double-check and try again. The available versions
|
||||
for this box are:
|
||||
You requested to remove the box '%{name}' version '%{version}',
|
||||
but that specific version of the box is not installed. Please
|
||||
double-check and try again. The available versions for this box
|
||||
are:
|
||||
|
||||
%{versions}
|
||||
box_remove_multi_architecture: |-
|
||||
You requested to remove the box '%{name}' version '%{version}'
|
||||
with provider '%{provider}'. This box has multiple architectures.
|
||||
You must explicitly select a single architecture to remove with
|
||||
`--architecture` or specify the `--all-architectures` flag to
|
||||
remove all architectures. The available architectures for this
|
||||
box are:
|
||||
|
||||
%{architectures}
|
||||
box_remove_architecture_not_found: |-
|
||||
You requested to remove the box '%{name}' version '%{version}'
|
||||
with provider '%{provider}' and architecture '%{architecture}' but
|
||||
that specific architecture is not installed. Please double-check
|
||||
and try again. The available architectures are:
|
||||
|
||||
%{architectures}
|
||||
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
|
||||
@ -710,9 +733,15 @@ en:
|
||||
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`.
|
||||
provider to update with `--provider`.
|
||||
|
||||
Available providers: %{providers}
|
||||
box_update_multi_architecture: |-
|
||||
You requested to update the box '%{name}' (v%{version}) with provider
|
||||
'%{provider}'. This box has multiple architectures. You must explicitly
|
||||
select a single architecture to update with `--architecture`.
|
||||
|
||||
Available architectures: %{architectures}
|
||||
box_update_no_box: |-
|
||||
Box '%{name}' not installed, can't check for updates.
|
||||
box_update_no_metadata: |-
|
||||
@ -2188,10 +2217,10 @@ en:
|
||||
box:
|
||||
no_installed_boxes: "There are no installed boxes! Use `vagrant box add` to add some."
|
||||
remove_in_use_query: |-
|
||||
Box '%{name}' (v%{version}) with provider '%{provider}' appears
|
||||
to still be in use by at least one Vagrant environment. Removing
|
||||
the box could corrupt the environment. We recommend destroying
|
||||
these environments first:
|
||||
Box '%{name}' (v%{version}) with provider '%{provider}' and
|
||||
architectures '%{architecture}' appears to still be in use by
|
||||
at least one Vagrant environment. Removing the box could corrupt
|
||||
the environment. We recommend destroying these environments first:
|
||||
|
||||
%{users}
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ Gem::Specification.new do |s|
|
||||
s.add_dependency "rexml", "~> 3.2"
|
||||
s.add_dependency "rgl", "~> 0.5.10"
|
||||
s.add_dependency "rubyzip", "~> 2.3.2"
|
||||
s.add_dependency "vagrant_cloud", "~> 3.0.5"
|
||||
s.add_dependency "vagrant_cloud", "> 3.0.5", "< 3.1"
|
||||
s.add_dependency "wdm", "~> 0.1.1"
|
||||
s.add_dependency "winrm", ">= 2.3.6", "< 3.0"
|
||||
s.add_dependency "winrm-elevated", ">= 1.2.3", "< 2.0"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user