Track downloaded base boxes URLs into a state file and allow hooking into base boxes removal #2327

Merged
merged 13 commits into from Nov 25, 2013
+108 −21
Split
View
@@ -8,7 +8,8 @@ module Action
# Builtin contains middleware classes that are shipped with Vagrant-core
# and are thus available to all plugins as a "standard library" of sorts.
module Builtin
- autoload :BoxAdd, "vagrant/action/builtin/box_add"
+ autoload :BoxAdd, "vagrant/action/builtin/box_add"
+ autoload :BoxRemove, "vagrant/action/builtin/box_remove"
autoload :Call, "vagrant/action/builtin/call"
autoload :Confirm, "vagrant/action/builtin/confirm"
autoload :ConfigValidate, "vagrant/action/builtin/config_validate"
@@ -41,5 +42,14 @@ def self.action_box_add
b.use Builtin::BoxAdd
end
end
+
+ # This is the action that will remove a box given a name (and optionally
+ # a provider). This middleware sequence is built-in to Vagrant. Plugins
+ # can hook into this like any other middleware sequence.
+ def self.action_box_remove
+ Builder.new.tap do |b|
+ b.use Builtin::BoxRemove
+ end
+ end
end
end
@@ -57,9 +57,9 @@ def call(env)
# Add the box
env[:ui].info I18n.t("vagrant.actions.box.add.adding", :name => env[:box_name])
- added_box = nil
+ box_added = nil
begin
- added_box = env[:box_collection].add(
+ box_added = env[:box_collection].add(
@temp_path, env[:box_name], box_formats, env[:box_force])
rescue Vagrant::Errors::BoxUpgradeRequired
# Upgrade the box
@@ -75,7 +75,13 @@ def call(env)
# Success, we added a box!
env[:ui].success(
- I18n.t("vagrant.actions.box.add.added", name: added_box.name, provider: added_box.provider))
+ I18n.t("vagrant.actions.box.add.added", name: box_added.name, provider: box_added.provider))
+
+ # Persists URL used on download and the time it was added
+ write_extra_info(box_added, url)
+
+ # Passes on the newly added box to the rest of the middleware chain
+ env[:box_added] = box_added
# Carry on!
@app.call(env)
@@ -86,6 +92,13 @@ def recover(env)
File.unlink(@temp_path)
end
end
+
+ def write_extra_info(box_added, url)
+ info = {'url' => url, 'downloaded_at' => Time.now.utc}
+ box_added.directory.join('info.json').open("w+") do |f|
+ f.write(JSON.dump(info))
+ end
+ end
end
end
end
@@ -0,0 +1,38 @@
+require "log4r"
+
+module Vagrant
+ module Action
+ module Builtin
+ # This middleware will remove a box for a given provider.
+ class BoxRemove
+ def initialize(app, env)
+ @app = app
+ @logger = Log4r::Logger.new("vagrant::action::builtin::box_remove")
+ end
+
+ def call(env)
+ box_name = env[:box_name]
+ box_provider = env[:box_provider].to_sym
+
+ box = nil
+ begin
+ box = env[:box_collection].find(box_name, box_provider)
+ rescue Vagrant::Errors::BoxUpgradeRequired
+ @env.boxes.upgrade(box_name)
+ retry
+ end
+
+ raise Vagrant::Errors::BoxNotFound, :name => box_name, :provider => box_provider if !box
+ env[:ui].info(I18n.t("vagrant.commands.box.removing",
+ :name => box_name,
+ :provider => box_provider))
+ box.destroy!
+
+ # Passes on the removed box to the rest of the middleware chain
+ env[:box_removed] = box
+ @app.call(env)
+ end
+ end
+ end
+ end
+end
@@ -39,7 +39,7 @@ def execute
:box_provider => provider,
:box_url => argv[1],
:box_force => options[:force],
- :box_download_insecure => options[:insecure],
+ :box_download_insecure => options[:insecure]
})
# Success, exit status 0
@@ -9,6 +9,11 @@ def execute
opts = OptionParser.new do |opts|
opts.banner = "Usage: vagrant box list"
+ opts.separator ""
+
+ opts.on("-i", "--box-info", "Displays additional information about the boxes.") do |i|
+ options[:info] = i
+ end
end
# Parse the options
@@ -20,20 +25,50 @@ def execute
return @env.ui.warn(I18n.t("vagrant.commands.box.no_installed_boxes"), :prefix => false)
end
+ list_boxes(boxes, options[:info])
+
+ # Success, exit status 0
+ 0
+ end
+
+ private
+
+ def list_boxes(boxes, extra_info)
# Find the longest box name
longest_box = boxes.max_by { |x| x[0].length }
longest_box_length = longest_box[0].length
+ # Find the longest provider name
+ longest_provider = boxes.max_by { |x| x[1].length }
+ longest_provider_length = longest_provider[1].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, provider, _v1|
- @env.ui.info("#{name.ljust(longest_box_length)} (#{provider})", :prefix => false)
+ extra = ''
+ if extra_info
+ extra << format_extra_info(name.to_s, provider.to_s)
+ end
+
+ name = name.ljust(longest_box_length)
+ provider = "(#{provider})".ljust(longest_provider_length + 2) # 2 -> parenthesis
+ box_info = "#{name} #{provider}#{extra}"
+
+ @env.ui.info(box_info, :prefix => false)
end
+ end
- # Success, exit status 0
- 0
+ def format_extra_info(name, provider)
+ info_json = @env.boxes.find(name, provider).directory.join('info.json')
+ if info_json.file?
+ info = JSON.parse(info_json.read)
+ return "\n `- URL: #{info['url']}" +
+ "\n `- Date: #{info['downloaded_at']}"
+ else
+ return ''
+ end
end
end
end
@@ -34,19 +34,10 @@ def execute
argv[1] = providers[0] || ""
end
- b = nil
- begin
- b = @env.boxes.find(argv[0], argv[1].to_sym)
- rescue Vagrant::Errors::BoxUpgradeRequired
- @env.boxes.upgrade(argv[0])
- retry
- end
-
- raise Vagrant::Errors::BoxNotFound, :name => argv[0], :provider => argv[1].to_sym if !b
- @env.ui.info(I18n.t("vagrant.commands.box.removing",
- :name => argv[0],
- :provider => argv[1]))
- b.destroy!
+ @env.action_runner.run(Vagrant::Action.action_box_remove, {
+ :box_name => argv[0],
+ :box_provider => argv[1]
+ })
# Success, exit status 0
0