core: BoxAdd can add old boxes (happy path)
This commit is contained in:
parent
28a6beaa56
commit
2a08302145
|
@ -1,5 +1,7 @@
|
||||||
require "digest/sha1"
|
require "digest/sha1"
|
||||||
require "log4r"
|
require "log4r"
|
||||||
|
require "pathname"
|
||||||
|
require "uri"
|
||||||
|
|
||||||
require "vagrant/box_metadata"
|
require "vagrant/box_metadata"
|
||||||
require "vagrant/util/downloader"
|
require "vagrant/util/downloader"
|
||||||
|
@ -20,6 +22,47 @@ module Vagrant
|
||||||
def call(env)
|
def call(env)
|
||||||
@download_interrupted = false
|
@download_interrupted = false
|
||||||
|
|
||||||
|
url = env[:box_url]
|
||||||
|
if metadata_url?(url, env)
|
||||||
|
add_from_metadata(env)
|
||||||
|
else
|
||||||
|
add_direct(env)
|
||||||
|
end
|
||||||
|
|
||||||
|
@app.call(env)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Adds a box file directly (no metadata component, versioning,
|
||||||
|
# etc.)
|
||||||
|
def add_direct(env)
|
||||||
|
# TODO: what if we have no name
|
||||||
|
name = env[:box_name]
|
||||||
|
url = env[:box_url]
|
||||||
|
|
||||||
|
# Now we have a URL, we have to download this URL.
|
||||||
|
box = nil
|
||||||
|
begin
|
||||||
|
box_url = download(url, env)
|
||||||
|
|
||||||
|
# Add the box!
|
||||||
|
box = env[:box_collection].add(box_url, name, "0")
|
||||||
|
ensure
|
||||||
|
# Make sure we delete the temporary file after we add it,
|
||||||
|
# unless we were interrupted, in which case we keep it around
|
||||||
|
# so we can resume the download later.
|
||||||
|
if !@download_interrupted
|
||||||
|
@logger.debug("Deleting temporary box: #{box_url}")
|
||||||
|
box_url.delete
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
env[:ui].success(I18n.t(
|
||||||
|
"vagrant.box_added",
|
||||||
|
name: box.name, provider: box.provider))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Adds a box given that the URL is a metadata document.
|
||||||
|
def add_from_metadata(env)
|
||||||
provider = env[:box_provider]
|
provider = env[:box_provider]
|
||||||
provider = Array(provider) if provider
|
provider = Array(provider) if provider
|
||||||
url = env[:box_url]
|
url = env[:box_url]
|
||||||
|
@ -28,7 +71,6 @@ module Vagrant
|
||||||
metadata = nil
|
metadata = nil
|
||||||
if File.file?(url)
|
if File.file?(url)
|
||||||
# TODO: What if file isn't valid JSON
|
# TODO: What if file isn't valid JSON
|
||||||
# TODO: What if file is old-style box
|
|
||||||
# TODO: What if URL is in the "file:" format
|
# TODO: What if URL is in the "file:" format
|
||||||
File.open(url) do |f|
|
File.open(url) do |f|
|
||||||
metadata = BoxMetadata.new(f)
|
metadata = BoxMetadata.new(f)
|
||||||
|
@ -72,6 +114,9 @@ module Vagrant
|
||||||
version: metadata_version.version,
|
version: metadata_version.version,
|
||||||
provider: metadata_provider.name))
|
provider: metadata_provider.name))
|
||||||
|
|
||||||
|
# TODO(mitchellh): verify that the box we're adding
|
||||||
|
# doesn't already exist.
|
||||||
|
|
||||||
# Now we have a URL, we have to download this URL.
|
# Now we have a URL, we have to download this URL.
|
||||||
box = nil
|
box = nil
|
||||||
begin
|
begin
|
||||||
|
@ -93,72 +138,9 @@ module Vagrant
|
||||||
env[:ui].success(I18n.t(
|
env[:ui].success(I18n.t(
|
||||||
"vagrant.box_added",
|
"vagrant.box_added",
|
||||||
name: box.name, provider: box.provider))
|
name: box.name, provider: box.provider))
|
||||||
|
|
||||||
@app.call(env)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def download(url, env)
|
|
||||||
temp_path = env[:tmp_path].join("box" + Digest::SHA1.hexdigest(url))
|
|
||||||
@logger.info("Downloading box: #{url} => #{temp_path}")
|
|
||||||
|
|
||||||
if File.file?(url) || url !~ /^[a-z0-9]+:.*$/i
|
|
||||||
@logger.info("URL is a file or protocol not found and assuming file.")
|
|
||||||
file_path = File.expand_path(url)
|
|
||||||
file_path = Util::Platform.cygwin_windows_path(file_path)
|
|
||||||
url = "file:#{file_path}"
|
|
||||||
end
|
|
||||||
|
|
||||||
downloader_options = {}
|
|
||||||
downloader_options[:ca_cert] = env[:box_download_ca_cert]
|
|
||||||
downloader_options[:continue] = true
|
|
||||||
downloader_options[:insecure] = env[:box_download_insecure]
|
|
||||||
downloader_options[:ui] = env[:ui]
|
|
||||||
downloader_options[:client_cert] = env[:box_client_cert]
|
|
||||||
|
|
||||||
# If the temporary path exists, verify it is not too old. If its
|
|
||||||
# too old, delete it first because the data may have changed.
|
|
||||||
if temp_path.file?
|
|
||||||
delete = false
|
|
||||||
if env[:box_clean]
|
|
||||||
@logger.info("Cleaning existing temp box file.")
|
|
||||||
delete = true
|
|
||||||
elsif temp_path.mtime.to_i < (Time.now.to_i - 6 * 60 * 60)
|
|
||||||
@logger.info("Existing temp file is too old. Removing.")
|
|
||||||
delete = true
|
|
||||||
end
|
|
||||||
|
|
||||||
temp_path.unlink if delete
|
|
||||||
end
|
|
||||||
|
|
||||||
# Download the box to a temporary path. We store the temporary
|
|
||||||
# path as an instance variable so that the `#recover` method can
|
|
||||||
# access it.
|
|
||||||
env[:ui].info(I18n.t(
|
|
||||||
"vagrant.actions.box.download.downloading",
|
|
||||||
url: url))
|
|
||||||
if temp_path.file?
|
|
||||||
env[:ui].info(I18n.t("vagrant.actions.box.download.resuming"))
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
|
||||||
downloader = Util::Downloader.new(url, temp_path, downloader_options)
|
|
||||||
downloader.download!
|
|
||||||
rescue Errors::DownloaderInterrupted
|
|
||||||
# The downloader was interrupted, so just return, because that
|
|
||||||
# means we were interrupted as well.
|
|
||||||
@download_interrupted = true
|
|
||||||
env[:ui].info(I18n.t("vagrant.actions.box.download.interrupted"))
|
|
||||||
rescue Errors::DownloaderError
|
|
||||||
# The download failed for some reason, clean out the temp path
|
|
||||||
temp_path.unlink if temp_path.file?
|
|
||||||
raise
|
|
||||||
end
|
|
||||||
|
|
||||||
temp_path
|
|
||||||
end
|
|
||||||
=begin
|
=begin
|
||||||
@download_interrupted = false
|
|
||||||
|
|
||||||
box_name = env[:box_name]
|
box_name = env[:box_name]
|
||||||
box_formats = env[:box_provider]
|
box_formats = env[:box_provider]
|
||||||
if box_formats
|
if box_formats
|
||||||
|
@ -350,6 +332,103 @@ module Vagrant
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
=end
|
=end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
# Returns the download options for the download.
|
||||||
|
#
|
||||||
|
# @return [Hash]
|
||||||
|
def downloader(url, env)
|
||||||
|
temp_path = env[:tmp_path].join("box" + Digest::SHA1.hexdigest(url))
|
||||||
|
@logger.info("Downloading box: #{url} => #{temp_path}")
|
||||||
|
|
||||||
|
if File.file?(url) || url !~ /^[a-z0-9]+:.*$/i
|
||||||
|
@logger.info("URL is a file or protocol not found and assuming file.")
|
||||||
|
file_path = File.expand_path(url)
|
||||||
|
file_path = Util::Platform.cygwin_windows_path(file_path)
|
||||||
|
url = "file:#{file_path}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# If the temporary path exists, verify it is not too old. If its
|
||||||
|
# too old, delete it first because the data may have changed.
|
||||||
|
if temp_path.file?
|
||||||
|
delete = false
|
||||||
|
if env[:box_clean]
|
||||||
|
@logger.info("Cleaning existing temp box file.")
|
||||||
|
delete = true
|
||||||
|
elsif temp_path.mtime.to_i < (Time.now.to_i - 6 * 60 * 60)
|
||||||
|
@logger.info("Existing temp file is too old. Removing.")
|
||||||
|
delete = true
|
||||||
|
end
|
||||||
|
|
||||||
|
temp_path.unlink if delete
|
||||||
|
end
|
||||||
|
|
||||||
|
downloader_options = {}
|
||||||
|
downloader_options[:ca_cert] = env[:box_download_ca_cert]
|
||||||
|
downloader_options[:continue] = true
|
||||||
|
downloader_options[:insecure] = env[:box_download_insecure]
|
||||||
|
downloader_options[:ui] = env[:ui]
|
||||||
|
downloader_options[:client_cert] = env[:box_client_cert]
|
||||||
|
|
||||||
|
Util::Downloader.new(url, temp_path, downloader_options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def download(url, env)
|
||||||
|
d = downloader(url, env)
|
||||||
|
|
||||||
|
# Download the box to a temporary path. We store the temporary
|
||||||
|
# path as an instance variable so that the `#recover` method can
|
||||||
|
# access it.
|
||||||
|
env[:ui].info(I18n.t(
|
||||||
|
"vagrant.actions.box.download.downloading",
|
||||||
|
url: url))
|
||||||
|
if File.file?(d.destination)
|
||||||
|
env[:ui].info(I18n.t("vagrant.actions.box.download.resuming"))
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
d.download!
|
||||||
|
rescue Errors::DownloaderInterrupted
|
||||||
|
# The downloader was interrupted, so just return, because that
|
||||||
|
# means we were interrupted as well.
|
||||||
|
@download_interrupted = true
|
||||||
|
env[:ui].info(I18n.t("vagrant.actions.box.download.interrupted"))
|
||||||
|
rescue Errors::DownloaderError
|
||||||
|
# The download failed for some reason, clean out the temp path
|
||||||
|
File.unlink(d.destination) if File.file?(d.destination)
|
||||||
|
raise
|
||||||
|
end
|
||||||
|
|
||||||
|
Pathname.new(d.destination)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Tests whether the given URL points to a metadata file or a
|
||||||
|
# box file without completely downloading the file.
|
||||||
|
#
|
||||||
|
# @param [String] url
|
||||||
|
# @return [Boolean] true if metadata
|
||||||
|
def metadata_url?(url, env)
|
||||||
|
d = downloader(url, env)
|
||||||
|
|
||||||
|
# If we're downloading a file, cURL just returns no
|
||||||
|
# content-type (makes sense), so we just test if it is JSON
|
||||||
|
# by trying to parse JSON!
|
||||||
|
uri = URI.parse(d.source)
|
||||||
|
if uri.scheme == "file"
|
||||||
|
begin
|
||||||
|
File.open(uri.opaque, "r") do |f|
|
||||||
|
BoxMetadata.new(f)
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
rescue Errors::BoxMetadataMalformed
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: do the HEAD request
|
||||||
|
true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,6 +14,9 @@ module Vagrant
|
||||||
# are properly tracked.
|
# are properly tracked.
|
||||||
USER_AGENT = "Vagrant/#{VERSION}"
|
USER_AGENT = "Vagrant/#{VERSION}"
|
||||||
|
|
||||||
|
attr_reader :source
|
||||||
|
attr_reader :destination
|
||||||
|
|
||||||
def initialize(source, destination, options=nil)
|
def initialize(source, destination, options=nil)
|
||||||
@logger = Log4r::Logger.new("vagrant::util::downloader")
|
@logger = Log4r::Logger.new("vagrant::util::downloader")
|
||||||
@source = source.to_s
|
@source = source.to_s
|
||||||
|
|
|
@ -22,17 +22,37 @@ describe Vagrant::Action::Builtin::BoxAdd do
|
||||||
let(:box_collection) { double("box_collection") }
|
let(:box_collection) { double("box_collection") }
|
||||||
let(:iso_env) { isolated_environment }
|
let(:iso_env) { isolated_environment }
|
||||||
|
|
||||||
|
let(:box) do
|
||||||
|
box_dir = iso_env.box3("foo", "1.0", :virtualbox)
|
||||||
|
Vagrant::Box.new("foo", :virtualbox, "1.0", box_dir)
|
||||||
|
end
|
||||||
|
|
||||||
# Helper to quickly SHA1 checksum a path
|
# Helper to quickly SHA1 checksum a path
|
||||||
def checksum(path)
|
def checksum(path)
|
||||||
FileChecksum.new(path, Digest::SHA1).checksum
|
FileChecksum.new(path, Digest::SHA1).checksum
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with box metadata" do
|
context "with box file directly" do
|
||||||
let(:box) do
|
it "adds it" do
|
||||||
box_dir = iso_env.box3("foo", "1.0", :virtualbox)
|
box_path = iso_env.box2_file(:virtualbox)
|
||||||
Vagrant::Box.new("foo", :virtualbox, "1.0", box_dir)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
env[:box_name] = "foo"
|
||||||
|
env[:box_url] = box_path.to_s
|
||||||
|
|
||||||
|
box_collection.should_receive(:add).with do |path, name, version|
|
||||||
|
expect(checksum(path)).to eq(checksum(box_path))
|
||||||
|
expect(name).to eq("foo")
|
||||||
|
expect(version).to eq("0")
|
||||||
|
true
|
||||||
|
end.and_return(box)
|
||||||
|
|
||||||
|
app.should_receive(:call).with(env)
|
||||||
|
|
||||||
|
subject.call(env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with box metadata" do
|
||||||
it "adds the latest version of a box with only one provider" do
|
it "adds the latest version of a box with only one provider" do
|
||||||
box_path = iso_env.box2_file(:virtualbox)
|
box_path = iso_env.box2_file(:virtualbox)
|
||||||
tf = Tempfile.new("vagrant").tap do |f|
|
tf = Tempfile.new("vagrant").tap do |f|
|
||||||
|
|
Loading…
Reference in New Issue