class Vagrant::Box
Represents a “box,” which is a package Vagrant
environment that is used as a base image when creating a new guest machine.
Constants
- BOX_UPDATE_CHECK_INTERVAL
Number of seconds to wait between checks for box updates
- REQUIRED_METADATA_FIELDS
The required fields in a boxes ‘metadata.json` file
Attributes
This is the directory on disk where this box exists.
@return [Pathname]
This is the metadata for the box. This is read from the “metadata.json” file that all boxes require.
@return [Hash]
This is the URL to the version info and other metadata for this box.
@return [String]
The box name. This is the logical name used when adding the box.
@return [String]
This is the provider that this box is built for.
@return [Symbol]
The version of this box.
@return [String]
Public Class Methods
This is used to initialize a box.
@param [String] name Logical name of the box. @param [Symbol] provider The provider that this box implements. @param [Pathname] directory The directory where this box exists on
disk.
@param [String] metadata_url
Metadata URL for box @param [Hook] hook A hook to apply to the box downloader, for example, for authentication
# File lib/vagrant/box.rb, line 68 def initialize(name, provider, version, directory, metadata_url: nil, hook: nil) @name = name @version = version @provider = provider @directory = directory @metadata_url = metadata_url @hook = hook metadata_file = directory.join("metadata.json") raise Errors::BoxMetadataFileNotFound, name: @name if !metadata_file.file? begin @metadata = JSON.parse(directory.join("metadata.json").read) validate_metadata_json(@metadata) rescue JSON::ParserError raise Errors::BoxMetadataCorrupted, name: @name end @logger = Log4r::Logger.new("vagrant::box") end
Public Instance Methods
Implemented for comparison with other boxes. Comparison is implemented by comparing names and providers.
# File lib/vagrant/box.rb, line 243 def <=>(other) return super if !other.is_a?(self.class) # Comparison is done by composing the name and provider "#{@name}-#{@version}-#{@provider}" <=> "#{other.name}-#{other.version}-#{other.provider}" end
Check if a box update check is allowed. Uses a file in the box data directory to track when the last auto update check was performed and returns true if the BOX_UPDATE_CHECK_INTERVAL
has passed.
@return [Boolean]
# File lib/vagrant/box.rb, line 207 def automatic_update_check_allowed? check_path = directory.join("box_update_check") if check_path.exist? last_check_span = Time.now.to_i - check_path.mtime.to_i if last_check_span < BOX_UPDATE_CHECK_INTERVAL @logger.info("box update check is under the interval threshold") return false end end FileUtils.touch(check_path) true end
This deletes the box. This is NOT undoable.
# File lib/vagrant/box.rb, line 102 def destroy! # Delete the directory to delete the box. FileUtils.rm_r(@directory) # Just return true always true rescue Errno::ENOENT # This means the directory didn't exist. Not a problem. return true end
Checks if the box has an update and returns the metadata, version, and provider. If the box doesn’t have an update that satisfies the constraints, it will return nil.
This will potentially make a network call if it has to load the metadata from the network.
@param [String] version Version constraints the update must
satisfy. If nil, the version constrain defaults to being a larger version than this box.
@return [Array]
# File lib/vagrant/box.rb, line 181 def has_update?(version=nil, download_options: {}) if !@metadata_url raise Errors::BoxUpdateNoMetadata, name: @name end if download_options.delete(:automatic_check) && !automatic_update_check_allowed? @logger.info("Skipping box update check") return end version += ", " if version version ||= "" version += "> #{@version}" md = self.load_metadata(download_options) newer = md.version(version, provider: @provider) return nil if !newer [md, newer, newer.provider(@provider)] end
Checks if this box is in use according to the given machine index and returns the entries that appear to be using the box.
The entries returned, if any, are not tested for validity with {MachineIndex::Entry#valid?}, so the caller should do that if the caller cares.
@param [MachineIndex] index @return [Array<MachineIndex::Entry>]
# File lib/vagrant/box.rb, line 122 def in_use?(index) results = [] index.each do |entry| box_data = entry.extra_data["box"] next if !box_data # If all the data matches, record it if box_data["name"] == self.name && box_data["provider"] == self.provider.to_s && box_data["version"] == self.version.to_s results << entry end end return nil if results.empty? results end
Loads the metadata URL and returns the latest metadata associated with this box.
@param [Hash] download_options Options to pass to the downloader. @return [BoxMetadata]
# File lib/vagrant/box.rb, line 145 def load_metadata(download_options={}) tf = Util::Tempfile.new("vagrant-load-metadata") tf.close url = @metadata_url if File.file?(url) || url !~ /^[a-z0-9]+:.*$/i url = File.expand_path(url) url = Util::Platform.cygwin_windows_path(url) url = "file:#{url}" end opts = { headers: ["Accept: application/json"] }.merge(download_options) d = Util::Downloader.new(url, tf.path, opts) if @hook @hook.call(:authenticate_box_downloader, downloader: d) end d.download! BoxMetadata.new(File.open(tf.path, "r"), url: url) rescue Errors::DownloaderError => e raise Errors::BoxMetadataDownloadError, message: e.extra_data[:message] ensure tf.unlink if tf end
This repackages this box and outputs it to the given path.
@param [Pathname] path The full path (filename included) of where
to output this box.
@return [Boolean] true if this succeeds.
# File lib/vagrant/box.rb, line 225 def repackage(path) @logger.debug("Repackaging box '#{@name}' to: #{path}") Util::SafeChdir.safe_chdir(@directory) do # Find all the files in our current directory and tar it up! files = Dir.glob(File.join(".", "**", "*")).select { |f| File.file?(f) } # Package! Util::Subprocess.execute("bsdtar", "-czf", path.to_s, *files) end @logger.info("Repackaged box '#{@name}' successfully: #{path}") true end
# File lib/vagrant/box.rb, line 89 def validate_metadata_json(metadata) metatdata_fields = metadata.keys REQUIRED_METADATA_FIELDS.each do |field| if !metatdata_fields.include?(field) raise Errors::BoxMetadataMissingRequiredFields, name: @name, required_field: field, all_fields: REQUIRED_METADATA_FIELDS.join(", ") end end end