From f2509f5c65ad28140f1c4c2a82687a00b99ecd06 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 24 Jan 2014 14:14:05 -0800 Subject: [PATCH] commands/box: outdated command to find outdated boxes --- lib/vagrant/box.rb | 24 +++++++++- lib/vagrant/box_collection.rb | 2 + lib/vagrant/box_metadata.rb | 2 +- plugins/commands/box/command/outdated.rb | 59 ++++++++++++++++++++++++ plugins/commands/box/command/root.rb | 5 ++ templates/locales/en.yml | 8 ++++ test/unit/vagrant/box_test.rb | 27 +++++++++++ 7 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 plugins/commands/box/command/outdated.rb diff --git a/lib/vagrant/box.rb b/lib/vagrant/box.rb index 2b848da7c..c09d59424 100644 --- a/lib/vagrant/box.rb +++ b/lib/vagrant/box.rb @@ -1,8 +1,11 @@ require 'fileutils' +require "tempfile" require "json" require "log4r" +require "vagrant/box_metadata" +require "vagrant/util/downloader" require "vagrant/util/platform" require "vagrant/util/safe_chdir" require "vagrant/util/subprocess" @@ -82,6 +85,25 @@ module Vagrant return true end + # Loads the metadata URL and returns the latest metadata associated + # with this box. + # + # @return [BoxMetadata] + def load_metadata + tf = Tempfile.new("vagrant") + tf.close + + url = @metadata_url + if File.file?(url) || url !~ /^[a-z0-9]+:.*$/i + url = File.expand_path(url) + url = Util::Platform.cygwin_windows_path(url) + url = "file:#{url}" + end + + Util::Downloader.new(url, tf.path).download! + BoxMetadata.new(File.open(tf.path, "r")) + end + # This repackages this box and outputs it to the given path. # # @param [Pathname] path The full path (filename included) of where @@ -110,7 +132,7 @@ module Vagrant # Comparison is done by composing the name and provider "#{@name}-#{@version}-#{@provider}" <=> - "#{other.name}-#{other.version}-#{other.provider}" + "#{other.name}-#{other.version}-#{other.provider}" end end end diff --git a/lib/vagrant/box_collection.rb b/lib/vagrant/box_collection.rb index fc5e1eda6..62f942389 100644 --- a/lib/vagrant/box_collection.rb +++ b/lib/vagrant/box_collection.rb @@ -208,6 +208,8 @@ module Vagrant # Otherwise, traverse the subdirectories and see what versions # we have. child.children(true).each do |versiondir| + next if !versiondir.directory? + version = versiondir.basename.to_s versiondir.children(true).each do |provider| diff --git a/lib/vagrant/box_metadata.rb b/lib/vagrant/box_metadata.rb index 14ac62ee7..0366246f1 100644 --- a/lib/vagrant/box_metadata.rb +++ b/lib/vagrant/box_metadata.rb @@ -29,7 +29,7 @@ module Vagrant @name = @raw["name"] @description = @raw["description"] - @version_map = @raw["versions"].map do |v| + @version_map = (@raw["versions"] || []).map do |v| [Gem::Version.new(v["version"]), v] end @version_map = Hash[@version_map] diff --git a/plugins/commands/box/command/outdated.rb b/plugins/commands/box/command/outdated.rb new file mode 100644 index 000000000..818a478a9 --- /dev/null +++ b/plugins/commands/box/command/outdated.rb @@ -0,0 +1,59 @@ +require 'optparse' + +module VagrantPlugins + module CommandBox + module Command + class Outdated < Vagrant.plugin("2", :command) + def execute + OptionParser.new do |o| + o.banner = "Usage: vagrant box outdated" + end + + boxes = {} + @env.boxes.all.reverse.each do |name, version, provider| + next if boxes[name] + boxes[name] = @env.boxes.find(name, provider, version) + end + + boxes.values.each do |box| + if !box.metadata_url + @env.ui.output(I18n.t( + "vagrant.box_outdated_no_metadata", + name: box.name)) + next + end + + md = nil + begin + md = box.load_metadata + rescue Vagrant::Errors::DownloaderError => e + @env.ui.error(I18n.t( + "vagrant.box_outdated_metadata_error", + name: box.name, + message: e.extra_data[:message])) + next + end + + current = Gem::Version.new(box.version) + latest = Gem::Version.new(md.versions.last) + if latest <= current + @env.ui.success(I18n.t( + "vagrant.box_up_to_date", + name: box.name, + version: box.version)) + else + @env.ui.warn(I18n.t( + "vagrant.box_outdated", + name: box.name, + current: box.version, + latest: latest.to_s,)) + end + end + + # Success, exit status 0 + 0 + end + end + end + end +end diff --git a/plugins/commands/box/command/root.rb b/plugins/commands/box/command/root.rb index 4150fd659..96f599308 100644 --- a/plugins/commands/box/command/root.rb +++ b/plugins/commands/box/command/root.rb @@ -24,6 +24,11 @@ module VagrantPlugins List end + @subcommands.register(:outdated) do + require_relative "outdated" + Outdated + end + @subcommands.register(:remove) do require File.expand_path("../remove", __FILE__) Remove diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 90969d57a..43398e490 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -28,6 +28,14 @@ en: URL: %{url} box_loading_metadata: |- Loading metadata for box '%{name}' + box_outdated: |- + * '%{name}' is outdated! Current: %{current}. Latest: %{latest} + box_outdated_metadata_error: |- + * '%{name}': Error loading metadata: %{message} + box_outdated_no_metadata: |- + * '%{name}' wasn't added from a catalog, no version information + box_up_to_date: |- + * '%{name}' (v%{version}) is up to date cfengine_bootstrapping: |- Bootstrapping CFEngine with policy server: %{policy_server}... cfengine_bootstrapping_policy_hub: |- diff --git a/test/unit/vagrant/box_test.rb b/test/unit/vagrant/box_test.rb index 6832e3ca4..d682875be 100644 --- a/test/unit/vagrant/box_test.rb +++ b/test/unit/vagrant/box_test.rb @@ -1,6 +1,7 @@ require File.expand_path("../../base", __FILE__) require "pathname" +require "tempfile" describe Vagrant::Box do include_context "unit" @@ -75,6 +76,32 @@ describe Vagrant::Box do end end + context "#load_metadata" do + let(:metadata_url) do + Tempfile.new("vagrant").tap do |f| + f.write(<<-RAW) + { + "name": "foo", + "description": "bar" + } + RAW + f.close + end + end + + subject do + described_class.new( + name, provider, version, directory, + metadata_url: metadata_url.path) + end + + it "loads the url and returns the data" do + result = subject.load_metadata + expect(result.name).to eq("foo") + expect(result.description).to eq("bar") + end + end + describe "destroying" do it "should destroy an existing box" do # Verify that our "box" exists