vagrant/lib/vagrant/box_metadata.rb

144 lines
3.7 KiB
Ruby

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
@raw ||= {}
@name = @raw["name"]
@description = @raw["description"]
@version_map = (@raw["versions"] || []).map do |v|
begin
[Gem::Version.new(v["version"]), v]
rescue ArgumentError
raise Errors::BoxMetadataMalformedVersion,
version: v["version"].to_s
end
end
@version_map = Hash[@version_map]
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, **opts)
requirements = version.split(",").map do |v|
Gem::Requirement.new(v.strip)
end
providers = nil
providers = Array(opts[:provider]).map(&:to_sym) if opts[:provider]
@version_map.keys.sort.reverse.each do |v|
next if !requirements.all? { |r| r.satisfied_by?(v) }
version = Version.new(@version_map[v])
next if (providers & version.providers).empty? if providers
return version
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"].to_sym, 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.to_sym]
return nil if !p
Provider.new(p)
end
# Returns the providers that are available for this version
# of the box.
#
# @return [Array<Symbol>]
def providers
@provider_map.keys.map(&:to_sym)
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
# The checksum value for this box, if any.
#
# @return [String]
attr_accessor :checksum
# The type of checksum (if any) associated with this provider.
#
# @return [String]
attr_accessor :checksum_type
def initialize(raw)
@name = raw["name"]
@url = raw["url"]
@checksum = raw["checksum"]
@checksum_type = raw["checksum_type"]
end
end
end
end