core: BoxAdd now works with HTTP URLs

This commit is contained in:
Mitchell Hashimoto 2014-01-23 16:43:03 -08:00
parent 03b22ab9a1
commit 09e8666296
3 changed files with 164 additions and 46 deletions

View File

@ -57,12 +57,14 @@ module Vagrant
version = env[:box_version]
metadata = nil
if File.file?(url)
# TODO: What if file isn't valid JSON
# TODO: What if URL is in the "file:" format
File.open(url) do |f|
begin
metadata_path = download(url, env)
File.open(metadata_path) do |f|
metadata = BoxMetadata.new(f)
end
ensure
metadata_path.delete if metadata_path.file?
end
metadata_version = metadata.version(
@ -316,7 +318,10 @@ module Vagrant
end
# TODO: do the HEAD request
true
output = d.head
match = output.scan(/^Content-Type: (.+?)$/).last
return false if !match
match.last.chomp == "application/json"
end
end
end

View File

@ -38,30 +38,8 @@ module Vagrant
# If this method returns without an exception, the download
# succeeded. An exception will be raised if the download failed.
def download!
# Build the list of parameters to execute with cURL
options = [
"--fail",
"--location",
"--max-redirs", "10",
"--user-agent", USER_AGENT,
"--output", @destination,
]
options += ["--cacert", @ca_cert] if @ca_cert
options += ["--continue-at", "-"] if @continue
options << "--insecure" if @insecure
options << "--cert" << @client_cert if @client_cert
options << @source
# Specify some options for the subprocess
subprocess_options = {}
# If we're in Vagrant, then we use the packaged CA bundle
if Vagrant.in_installer?
subprocess_options[:env] ||= {}
subprocess_options[:env]["CURL_CA_BUNDLE"] =
File.expand_path("cacert.pem", ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"])
end
options, subprocess_options = self.options
options += ["--output", @destination]
# This variable can contain the proc that'll be sent to
# the subprocess execute.
@ -118,7 +96,42 @@ module Vagrant
end
end
# Add the subprocess options onto the options we'll execute with
@logger.info("Downloader starting download: ")
@logger.info(" -- Source: #{@source}")
@logger.info(" -- Destination: #{@destination}")
begin
execute_curl(options, subprocess_options, &data_proc)
ensure
# If we're outputting to the UI, clear the output to
# avoid lingering progress meters.
if @ui
@ui.clear_line
# Windows doesn't clear properly for some reason, so we just
# output one more newline.
@ui.info("") if Platform.windows?
end
end
# Everything succeeded
true
end
# Does a HEAD request of the URL and returns the output.
def head
options, subprocess_options = self.options
options.unshift("-I")
@logger.info("HEAD: #{@source}")
result = execute_curl(options, subprocess_options)
result.stdout
end
protected
def execute_curl(options, subprocess_options, &data_proc)
options = options.dup
options << subprocess_options
# Create the callback that is called if we are interrupted
@ -128,10 +141,6 @@ module Vagrant
interrupted = true
end
@logger.info("Downloader starting download: ")
@logger.info(" -- Source: #{@source}")
@logger.info(" -- Destination: #{@destination}")
# Execute!
result = Busy.busy(int_callback) do
Subprocess.execute("curl", *options, &data_proc)
@ -140,16 +149,6 @@ module Vagrant
# If the download was interrupted, then raise a specific error
raise Errors::DownloaderInterrupted if interrupted
# If we're outputting to the UI, clear the output to
# avoid lingering progress meters.
if @ui
@ui.clear_line
# Windows doesn't clear properly for some reason, so we just
# output one more newline.
@ui.info("") if Platform.windows?
end
# If it didn't exit successfully, we need to parse the data and
# show an error message.
if result.exit_code != 0
@ -159,8 +158,38 @@ module Vagrant
raise Errors::DownloaderError, :message => parts[1].chomp
end
# Everything succeeded
true
result
end
# Returns the varoius cURL and subprocess options.
#
# @return [Array<Array, Hash>]
def options
# Build the list of parameters to execute with cURL
options = [
"--fail",
"--location",
"--max-redirs", "10",
"--user-agent", USER_AGENT,
]
options += ["--cacert", @ca_cert] if @ca_cert
options += ["--continue-at", "-"] if @continue
options << "--insecure" if @insecure
options << "--cert" << @client_cert if @client_cert
options << @source
# Specify some options for the subprocess
subprocess_options = {}
# If we're in Vagrant, then we use the packaged CA bundle
if Vagrant.in_installer?
subprocess_options[:env] ||= {}
subprocess_options[:env]["CURL_CA_BUNDLE"] =
File.expand_path("cacert.pem", ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"])
end
return [options, subprocess_options]
end
end
end

View File

@ -2,6 +2,7 @@ require "digest/sha1"
require "pathname"
require "tempfile"
require "tmpdir"
require "webrick"
require File.expand_path("../../../../base", __FILE__)
@ -32,6 +33,27 @@ describe Vagrant::Action::Builtin::BoxAdd do
FileChecksum.new(path, Digest::SHA1).checksum
end
def with_web_server(path)
tf = Tempfile.new("vagrant")
tf.close
mime_types = WEBrick::HTTPUtils::DefaultMimeTypes
mime_types.store "json", "application/json"
port = 3838
server = WEBrick::HTTPServer.new(
AccessLog: [],
Logger: WEBrick::Log.new(tf.path, 7),
Port: port,
DocumentRoot: path.dirname.to_s,
MimeTypes: mime_types)
thr = Thread.new { server.start }
yield port
ensure
server.shutdown rescue nil
thr.join rescue nil
end
before do
box_collection.stub(find: nil)
end
@ -55,6 +77,25 @@ describe Vagrant::Action::Builtin::BoxAdd do
subject.call(env)
end
it "adds from HTTP URL" do
box_path = iso_env.box2_file(:virtualbox)
with_web_server(box_path) do |port|
env[:box_name] = "foo"
env[:box_url] = "http://127.0.0.1:#{port}/#{box_path.basename}"
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
it "raises an error if the box already exists" do
box_path = iso_env.box2_file(:virtualbox)
@ -93,6 +134,49 @@ describe Vagrant::Action::Builtin::BoxAdd do
end
context "with box metadata" do
it "adds from HTTP URL" do
box_path = iso_env.box2_file(:virtualbox)
tf = Tempfile.new(["vagrant", ".json"]).tap do |f|
f.write(<<-RAW)
{
"name": "foo/bar",
"versions": [
{
"version": "0.5"
},
{
"version": "0.7",
"providers": [
{
"name": "virtualbox",
"url": "#{box_path}"
}
]
}
]
}
RAW
f.close
end
md_path = Pathname.new(tf.path)
with_web_server(md_path) do |port|
env[:box_url] = "http://127.0.0.1:#{port}/#{md_path.basename}"
box_collection.should_receive(:add).with do |path, name, version|
expect(name).to eq("foo/bar")
expect(version).to eq("0.7")
expect(checksum(path)).to eq(checksum(box_path))
true
end.and_return(box)
app.should_receive(:call).with(env)
subject.call(env)
end
end
it "adds the latest version of a box with only one provider" do
box_path = iso_env.box2_file(:virtualbox)
tf = Tempfile.new("vagrant").tap do |f|