Seth Vargo dab4537682
core/boxes: Do not update the same box twice
Vagrant's environment (which includes the known list of boxes and
versions) is established at the start of the Vagrant run. This means
that box downloads which occur during the run are not contained in the
set until the next run. This causes duplicate box downloads to raise an
error in multi-machine Vagrantfiles.

This commit fixes that issue by pre-processing the machines by provider
and version, creating a unique set of boxes to update.

Fixes GH-6042
2016-05-31 23:08:51 -04:00

162 lines
5.4 KiB
Ruby

require 'optparse'
require_relative 'download_mixins'
module VagrantPlugins
module CommandBox
module Command
class Update < Vagrant.plugin("2", :command)
include DownloadMixins
def execute
options = {}
download_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 BOX", String, "Update a specific box") do |b|
options[:box] = b
end
o.on("--provider PROVIDER", String, "Update box with specific provider") do |p|
options[:provider] = p.to_sym
end
build_download_options(o, download_options)
end
argv = parse_options(opts)
return if !argv
if options[:box]
update_specific(options[:box], options[:provider], download_options)
else
update_vms(argv, options[:provider], download_options)
end
0
end
def update_specific(name, provider, download_options)
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].sort.last],
]
to_update.each do |n, p, v|
box = @env.boxes.find(n, p, v)
box_update(box, "> #{v}", @env.ui, download_options)
end
end
def update_vms(argv, provider, download_options)
machines = {}
with_target_vms(argv, provider: provider) do |machine|
if !machine.config.vm.box
machine.ui.output(I18n.t(
"vagrant.errors.box_update_no_name"))
next
end
if !machine.box
machine.ui.output(I18n.t(
"vagrant.errors.box_update_no_box",
name: machine.config.vm.box))
next
end
name = machine.box.name
provider = machine.box.provider
version = machine.config.vm.box_version || machine.box.version
machines["#{name}_#{provider}_#{version}"] = machine
end
machines.each do |_, machine|
box = machine.box
version = machine.config.vm.box_version
# Get download options from machine configuration if not specified
# on the command line.
download_options[:ca_cert] ||= machine.config.vm.box_download_ca_cert
download_options[:ca_path] ||= machine.config.vm.box_download_ca_path
download_options[:client_cert] ||= machine.config.vm.box_download_client_cert
if download_options[:insecure].nil?
download_options[:insecure] = machine.config.vm.box_download_insecure
end
box_update(box, version, machine.ui, download_options)
end
end
def box_update(box, version, ui, download_options)
ui.output(I18n.t("vagrant.box_update_checking", name: box.name))
ui.detail("Latest installed version: #{box.version}")
ui.detail("Version constraints: #{version}")
ui.detail("Provider: #{box.provider}")
update = box.has_update?(version, download_options: download_options)
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,
box_client_cert: download_options[:client_cert],
box_download_ca_cert: download_options[:ca_cert],
box_download_ca_path: download_options[:ca_path],
box_download_insecure: download_options[:insecure]
})
end
end
end
end
end