core: BoxMetadata can read the JSON description
This commit is contained in:
parent
e8197c4e87
commit
8abcc6e5f2
|
@ -0,0 +1,124 @@
|
||||||
|
require "json"
|
||||||
|
|
||||||
|
module Vagrant
|
||||||
|
# BoxMetadata represents metadata about a box, including the name
|
||||||
|
# it should have, a description of it, the versions it has, and
|
||||||
|
# more.
|
||||||
|
class BoxMetadata
|
||||||
|
# The name that the box should be if it is added.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
attr_accessor :name
|
||||||
|
|
||||||
|
# The long-form human-readable description of a box.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
attr_accessor :description
|
||||||
|
|
||||||
|
# Loads the metadata associated with the box from the given
|
||||||
|
# IO.
|
||||||
|
#
|
||||||
|
# @param [IO] io An IO object to read the metadata from.
|
||||||
|
def initialize(io)
|
||||||
|
begin
|
||||||
|
@raw = JSON.load(io)
|
||||||
|
rescue JSON::ParserError => e
|
||||||
|
raise Errors::BoxMetadataMalformed,
|
||||||
|
error: e.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
@name = @raw["name"]
|
||||||
|
@description = @raw["description"]
|
||||||
|
@version_map = @raw["versions"].map do |v|
|
||||||
|
[Gem::Version.new(v["version"]), v]
|
||||||
|
end
|
||||||
|
@version_map = Hash[@version_map]
|
||||||
|
|
||||||
|
# TODO: check for corruption:
|
||||||
|
# - malformed version
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns data about a single version that is included in this
|
||||||
|
# metadata.
|
||||||
|
#
|
||||||
|
# @param [String] version The version to return, this can also
|
||||||
|
# be a constraint.
|
||||||
|
# @return [Version] The matching version or nil if a matching
|
||||||
|
# version was not found.
|
||||||
|
def version(version)
|
||||||
|
requirements = version.split(",").map do |v|
|
||||||
|
Gem::Requirement.new(v.strip)
|
||||||
|
end
|
||||||
|
|
||||||
|
@version_map.keys.sort.reverse.each do |v|
|
||||||
|
if requirements.all? { |r| r.satisfied_by?(v) }
|
||||||
|
return Version.new(@version_map[v])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns all the versions supported by this metadata. These
|
||||||
|
# versions are sorted so the last element of the list is the
|
||||||
|
# latest version.
|
||||||
|
#
|
||||||
|
# @return[Array<String>]
|
||||||
|
def versions
|
||||||
|
@version_map.keys.sort.map(&:to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Represents a single version within the metadata.
|
||||||
|
class Version
|
||||||
|
# The version that this Version object represents.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
attr_accessor :version
|
||||||
|
|
||||||
|
def initialize(raw=nil)
|
||||||
|
return if !raw
|
||||||
|
|
||||||
|
@version = raw["version"]
|
||||||
|
@provider_map = (raw["providers"] || []).map do |p|
|
||||||
|
[p["name"], p]
|
||||||
|
end
|
||||||
|
@provider_map = Hash[@provider_map]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns a [Provider] for the given name, or nil if it isn't
|
||||||
|
# supported by this version.
|
||||||
|
def provider(name)
|
||||||
|
p = @provider_map[name]
|
||||||
|
return nil if !p
|
||||||
|
Provider.new(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the providers that are available for this version
|
||||||
|
# of the box.
|
||||||
|
#
|
||||||
|
# @return [Provider]
|
||||||
|
def providers
|
||||||
|
@provider_map.keys
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Provider represents a single provider-specific box available
|
||||||
|
# for a version for a box.
|
||||||
|
class Provider
|
||||||
|
# The name of the provider.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
attr_accessor :name
|
||||||
|
|
||||||
|
# The URL of the box.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
attr_accessor :url
|
||||||
|
|
||||||
|
def initialize(raw)
|
||||||
|
@name = raw["name"]
|
||||||
|
@url = raw["url"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -144,6 +144,10 @@ module Vagrant
|
||||||
error_key(:box_metadata_file_not_found)
|
error_key(:box_metadata_file_not_found)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class BoxMetadataMalformed < VagrantError
|
||||||
|
error_key(:box_metadata_malformed)
|
||||||
|
end
|
||||||
|
|
||||||
class BoxNotFound < VagrantError
|
class BoxNotFound < VagrantError
|
||||||
error_key(:box_not_found)
|
error_key(:box_not_found)
|
||||||
end
|
end
|
||||||
|
|
|
@ -266,6 +266,12 @@ en:
|
||||||
box file format can be found at the URL below:
|
box file format can be found at the URL below:
|
||||||
|
|
||||||
http://docs.vagrantup.com/v2/boxes/format.html
|
http://docs.vagrantup.com/v2/boxes/format.html
|
||||||
|
box_metadata_malformed: |-
|
||||||
|
The metadata for the box was malformed. The exact error
|
||||||
|
is shown below. Please contact the maintainer of the box so
|
||||||
|
that this issue can be fixed.
|
||||||
|
|
||||||
|
%{error}
|
||||||
box_not_found: Box '%{name}' with '%{provider}' provider could not be found.
|
box_not_found: Box '%{name}' with '%{provider}' provider could not be found.
|
||||||
box_provider_doesnt_match: |-
|
box_provider_doesnt_match: |-
|
||||||
The box you attempted to add doesn't match the provider you specified.
|
The box you attempted to add doesn't match the provider you specified.
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
require File.expand_path("../../base", __FILE__)
|
||||||
|
|
||||||
|
require "vagrant/box_metadata"
|
||||||
|
|
||||||
|
describe Vagrant::BoxMetadata do
|
||||||
|
include_context "unit"
|
||||||
|
|
||||||
|
let(:raw) do
|
||||||
|
<<-RAW
|
||||||
|
{
|
||||||
|
"name": "foo",
|
||||||
|
"description": "bar",
|
||||||
|
"versions": [
|
||||||
|
{
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.1.5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.1.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
RAW
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { described_class.new(raw) }
|
||||||
|
|
||||||
|
its(:name) { should eq("foo") }
|
||||||
|
its(:description) { should eq("bar") }
|
||||||
|
|
||||||
|
context "with poorly formatted JSON" do
|
||||||
|
let(:raw) {
|
||||||
|
<<-RAW
|
||||||
|
{ "name": "foo", }
|
||||||
|
RAW
|
||||||
|
}
|
||||||
|
|
||||||
|
it "raises an exception" do
|
||||||
|
expect { subject }.
|
||||||
|
to raise_error(Vagrant::Errors::BoxMetadataMalformed)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#version" do
|
||||||
|
it "matches an exact version" do
|
||||||
|
result = subject.version("1.0.0")
|
||||||
|
expect(result).to_not be_nil
|
||||||
|
expect(result).to be_kind_of(Vagrant::BoxMetadata::Version)
|
||||||
|
expect(result.version).to eq("1.0.0")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "matches a constraint with latest matching version" do
|
||||||
|
result = subject.version(">= 1.0")
|
||||||
|
expect(result).to_not be_nil
|
||||||
|
expect(result).to be_kind_of(Vagrant::BoxMetadata::Version)
|
||||||
|
expect(result.version).to eq("1.1.5")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "matches complex constraints" do
|
||||||
|
result = subject.version(">= 0.9, ~> 1.0.0")
|
||||||
|
expect(result).to_not be_nil
|
||||||
|
expect(result).to be_kind_of(Vagrant::BoxMetadata::Version)
|
||||||
|
expect(result.version).to eq("1.0.0")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#versions" do
|
||||||
|
it "returns the versions it contained" do
|
||||||
|
expect(subject.versions).to eq(
|
||||||
|
["1.0.0", "1.1.0", "1.1.5"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe Vagrant::BoxMetadata::Version do
|
||||||
|
let(:raw) { {} }
|
||||||
|
|
||||||
|
subject { described_class.new(raw) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
raw["providers"] = [
|
||||||
|
{
|
||||||
|
"name" => "virtualbox",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" => "vmware",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#version" do
|
||||||
|
it "is the version in the raw data" do
|
||||||
|
v = "1.0"
|
||||||
|
raw["version"] = v
|
||||||
|
expect(subject.version).to eq(v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#provider" do
|
||||||
|
it "returns nil if a provider isn't supported" do
|
||||||
|
expect(subject.provider("foo")).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the provider specified" do
|
||||||
|
result = subject.provider("virtualbox")
|
||||||
|
expect(result).to_not be_nil
|
||||||
|
expect(result).to be_kind_of(Vagrant::BoxMetadata::Provider)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#providers" do
|
||||||
|
it "returns the providers available" do
|
||||||
|
expect(subject.providers.sort).to eq(
|
||||||
|
["virtualbox", "vmware"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe Vagrant::BoxMetadata::Provider do
|
||||||
|
let(:raw) { {} }
|
||||||
|
|
||||||
|
subject { described_class.new(raw) }
|
||||||
|
|
||||||
|
describe "#name" do
|
||||||
|
it "is the name specified" do
|
||||||
|
raw["name"] = "foo"
|
||||||
|
expect(subject.name).to eq("foo")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#url" do
|
||||||
|
it "is the URL specified" do
|
||||||
|
raw["url"] = "bar"
|
||||||
|
expect(subject.url).to eq("bar")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue