core: BoxCheckOutdated is *much* simpler, unified logic, tests

This commit is contained in:
Mitchell Hashimoto 2014-01-24 17:52:52 -08:00
parent 1a5ad9f3d5
commit 61a1082d65
9 changed files with 284 additions and 225 deletions

View File

@ -20,11 +20,6 @@ module Vagrant
if !machine.config.vm.box_check_update
return @app.call(env)
end
if !env.has_key?(:box_outdated_refresh)
env[:box_outdated_refresh] = true
env[:box_outdated_ignore_errors] = true
end
end
if !machine.box
@ -33,22 +28,28 @@ module Vagrant
# message anyways.
raise Errors::BoxOutdatedNoBox, name: machine.config.vm.box
end
box = machine.box
constraints = machine.config.vm.box_version
if env[:box_outdated_refresh]
env[:ui].output(I18n.t(
"vagrant.box_outdated_checking_with_refresh",
name: machine.box.name))
begin
check_outdated_refresh(env)
rescue Errors::VagrantError => e
raise if !env[:box_outdated_ignore_errors]
env[:ui].detail(I18n.t(
"vagrant.box_outdated_metadata_error",
message: e.message))
end
else
@logger.info("Checking if box is outdated locally")
check_outdated_local(env)
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)
@ -71,38 +72,6 @@ module Vagrant
env[:box_outdated] = false
end
def check_outdated_refresh(env)
machine = env[:machine]
if !machine.box.metadata_url
# This box doesn't have a metadata URL, so we can't
# possibly check the version information.
raise Errors::BoxOutdatedNoMetadata, name: machine.box.name
end
md = machine.box.load_metadata
newer = md.version(
"> #{machine.box.version}", provider: machine.box.provider)
if !newer
if env[:box_outdated_success_ui]
env[:ui].success(I18n.t(
"vagrant.box_up_to_date_single",
name: machine.box.name,
version: machine.box.version))
end
env[:box_outdated] = false
return
end
env[:ui].warn(I18n.t(
"vagrant.box_outdated_single",
name: machine.box.name,
current: machine.box.version,
latest: newer.version))
env[:box_outdated] = true
end
end
end
end

View File

@ -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

View File

@ -104,6 +104,32 @@ module Vagrant
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

View File

@ -176,10 +176,6 @@ module Vagrant
error_key(:box_outdated_no_box)
end
class BoxOutdatedNoMetadata < VagrantError
error_key(:box_outdated_no_metadata)
end
class BoxProviderDoesntMatch < VagrantError
error_key(:box_provider_doesnt_match)
end
@ -204,6 +200,10 @@ module Vagrant
error_key(:untar_failure, "vagrant.actions.box.unpackage")
end
class BoxUpdateNoMetadata < VagrantError
error_key(:box_update_no_metadata)
end
class BoxVerificationFailed < VagrantError
error_key(:failed, "vagrant.actions.box.verify")
end

View File

@ -8,7 +8,13 @@ module VagrantPlugins
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant box outdated"
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|
@ -17,6 +23,7 @@ module VagrantPlugins
end
argv = parse_options(opts)
return if !argv
# If we're checking the boxes globally, then do that.
if options[:global]

View File

@ -0,0 +1,43 @@
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
end
argv = parse_options(opts)
return if !argv
with_target_vms(argv) do |machine|
@env.action_runner.run(Vagrant::Action.action_box_update, {
box_outdated_force: true,
box_outdated_refresh: true,
box_outdated_success_ui: true,
machine: machine,
})
end
end
end
end
end
end

View File

@ -37,7 +37,7 @@ en:
installed, but your Vagrant machine is running against
version '%{old}'. To update to version '%{new}',
destroy and recreate your machine.
box_outdated_metadata_error: |-
box_outdated_metadata_error_single: |-
Error loading box metadata while attempting to check for
updates: %{message}
box_outdated_single: |-
@ -373,11 +373,6 @@ en:
that this issue can be fixed.
%{error}
box_outdated_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.
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
@ -409,6 +404,11 @@ en:
the provider specified. Please double-check and try again.
The providers for this are: %{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

View File

@ -23,8 +23,11 @@ describe Vagrant::Action::Builtin::BoxCheckOutdated do
let(:box) do
box_dir = iso_env.box3("foo", "1.0", :virtualbox)
Vagrant::Box.new("foo", :virtualbox, "1.0", box_dir)
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
@ -69,185 +72,63 @@ describe Vagrant::Action::Builtin::BoxCheckOutdated do
end
end
context "without refreshing" do
before do
env[:box_outdated_refresh] = false
context "with a box" do
it "sets env if no update" do
box.should_receive(:has_update?).and_return(nil)
machine.stub(box: box)
end
it "isn't outdated if there are no newer boxes" do
iso_env.box3("foo", "0.5", :virtualbox)
app.should_receive(:call).with(env)
app.should_receive(:call).with(env).once
subject.call(env)
expect(env[:box_outdated]).to be_false
end
it "is outdated if there are newer boxes" do
iso_env.box3("foo", "1.5", :virtualbox)
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
app.should_receive(:call).with(env)
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
end
context "with refreshing" do
before do
env[:box_outdated_refresh] = true
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
context "no metadata URL" do
let(:box) do
box_dir = iso_env.box3("foo", "1.0", :virtualbox)
Vagrant::Box.new("foo", :virtualbox, "1.0", box_dir)
end
it "doesn't raise an error if ignore errors is on" do
env[:box_outdated_ignore_errors] = true
before do
machine.stub(box: box)
end
box.should_receive(:has_update?).and_raise(Vagrant::Errors::VagrantError)
app.should_receive(:call).with(env).once
it "raises an exception" do
app.should_receive(:call).never
expect { subject.call(env) }.
to raise_error(Vagrant::Errors::BoxOutdatedNoMetadata)
end
end
context "with metadata URL" do
let(:metadata_url) do
Tempfile.new("vagrant").tap do |f|
f.close
end
end
let(:box_dir) { iso_env.box3("foo", "1.0", :virtualbox) }
context "isn't outdated" do
before do
File.open(metadata_url.path, "w") do |f|
f.write(<<-RAW)
{
"name": "foo/bar",
"versions": [
{
"version": "1.0",
"providers": [
{
"name": "virtualbox",
"url": "#{iso_env.box2_file(:virtualbox)}"
}
]
}
]
}
RAW
end
box = Vagrant::Box.new(
"foo", :virtualbox, "1.0", box_dir,
metadata_url: metadata_url.path)
machine.stub(box: box)
end
it "marks it isn't outdated" do
app.should_receive(:call).with(env)
subject.call(env)
expect(env[:box_outdated]).to be_false
end
it "talks to the UI" do
env[:box_outdated_success_ui] = true
app.should_receive(:call).with(env)
env[:ui].should_receive(:success)
subject.call(env)
expect(env[:box_outdated]).to be_false
end
it "doesn't talk to UI if it is told" do
app.should_receive(:call).with(env)
env[:ui].should_receive(:success).never
subject.call(env)
expect(env[:box_outdated]).to be_false
end
end
it "is outdated if it is" do
File.open(metadata_url.path, "w") do |f|
f.write(<<-RAW)
{
"name": "foo/bar",
"versions": [
{
"version": "1.0"
},
{
"version": "1.5",
"providers": [
{
"name": "virtualbox",
"url": "#{iso_env.box2_file(:virtualbox)}"
}
]
}
]
}
RAW
end
box = Vagrant::Box.new(
"foo", :virtualbox, "1.0", box_dir, metadata_url: metadata_url.path)
machine.stub(box: box)
subject.call(env)
expect(env[:box_outdated]).to be_true
end
it "isn't outdated if the newer box is for another provider" do
File.open(metadata_url.path, "w") do |f|
f.write(<<-RAW)
{
"name": "foo/bar",
"versions": [
{
"version": "1.0"
},
{
"version": "1.5",
"providers": [
{
"name": "vmware",
"url": "#{iso_env.box2_file(:vmware)}"
}
]
}
]
}
RAW
end
box = Vagrant::Box.new(
"foo", :virtualbox, "1.0", box_dir, metadata_url: metadata_url.path)
machine.stub(box: box)
subject.call(env)
expect(env[:box_outdated]).to be_false
end
expect { subject.call(env) }.to_not raise_error
end
end
end

View File

@ -1,8 +1,11 @@
require File.expand_path("../../base", __FILE__)
require "pathname"
require "stringio"
require "tempfile"
require "vagrant/box_metadata"
describe Vagrant::Box do
include_context "unit"
@ -76,6 +79,116 @@ 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|