lib/vagrant-mutate/box_loader.rb in vagrant-mutate-0.3.2 vs lib/vagrant-mutate/box_loader.rb in vagrant-mutate-1.0.0
- old
+ new
@@ -1,213 +1,296 @@
require 'fileutils'
require 'json'
require 'uri'
-require "vagrant/util/subprocess"
-require "vagrant/util/downloader"
+require 'vagrant/util/subprocess'
+require 'vagrant/util/downloader'
+require 'vagrant/box'
+require 'vagrant/box_metadata'
module VagrantMutate
class BoxLoader
-
- def initialize( env )
+ def initialize(env)
@env = env
@logger = Log4r::Logger.new('vagrant::mutate')
@tmp_files = []
end
- def prepare_for_output(name, provider_name)
- @logger.info "Preparing #{name} for output as #{provider_name}"
- dir = create_output_dir(name, provider_name)
- box = create_box(provider_name, name, dir)
+ def prepare_for_output(name, provider_name, version)
+ @logger.info "Preparing #{name} for output as #{provider_name} with version #{version}."
+ safe_name = sanitize_name(name)
+ dir = create_output_dir(safe_name, provider_name, version)
+ box = create_box(provider_name, name, version, dir)
if box.supported_output
return box
else
- raise Errors::ProviderNotSupported, :provider => provider_name, :direction => 'output'
+ fail Errors::ProviderNotSupported, provider: provider_name, direction: 'output'
end
end
- def load(box_arg, provider_name)
+ def load(box_arg, provider_name, input_version)
if box_arg =~ /:\/\//
box = load_from_url(box_arg)
elsif File.file?(box_arg)
box = load_from_file(box_arg)
- elsif box_arg =~ /\//
- raise Errors::CloudNotSupported
else
- box = load_from_boxes_path(box_arg, provider_name)
+ box = load_from_boxes_path(box_arg, provider_name, input_version)
end
if box.supported_input
return box
else
- raise Errors::ProviderNotSupported, :provider => box.provider_name, :direction => 'input'
+ fail Errors::ProviderNotSupported, provider: box.provider_name, direction: 'input'
end
end
def load_from_url(url)
@logger.info "Loading box from url #{url}"
# test that we have a valid url
url = URI(url)
unless url.scheme and url.host and url.path
- raise Errors::URLError, :url => url
+ fail Errors::URLError, url: url
end
# extract the name of the box from the url
# if it ends in .box remove that extension
# if not just remove leading slash
name = nil
- if url.path =~ /(\w+).box$/
- name = $1
+ if url.path =~ /([-\w]+).box$/
+ name = Regexp.last_match[1]
else
name = url.path.sub(/^\//, '')
end
if name.empty?
- raise Errors::URLError, :url => url
+ fail Errors::URLError, url: url
end
+ # Extract the version of the box from the URL
+ if url.path =~ /\/([\d.]+)\//
+ version = Regexp.last_match[1]
+ @logger.info "Pulled version from URL (#{version})"
+ else
+ version = '0'
+ @logger.info "No version found in URL, assuming '0'"
+ end
+
# using same path as in vagrants box add action
download_path = File.join(@env.tmp_path, 'box' + Digest::SHA1.hexdigest(url.to_s))
@tmp_files << download_path
# if this fails it will raise an error and we'll quit
@env.ui.info "Downloading box #{name} from #{url}"
- downloader = Vagrant::Util::Downloader.new(url, download_path, { :ui => @env.ui })
+ downloader = Vagrant::Util::Downloader.new(url, download_path, ui: @env.ui)
downloader.download!
dir = unpack(download_path)
-
provider_name = determine_provider(dir)
- box = create_box(provider_name, name, dir)
+ create_box(provider_name, name, version, dir)
end
def load_from_file(file)
@logger.info "Loading box from file #{file}"
- name = File.basename( file, File.extname(file) )
+ name = File.basename(file, File.extname(file))
+
dir = unpack(file)
provider_name = determine_provider(dir)
+ version = determine_version(dir)
- box = create_box(provider_name, name, dir)
+ create_box(provider_name, name, version, dir)
end
- def load_from_boxes_path(name, provider_name)
- @logger.info "Loading box #{name} from vagrants box path"
+ def load_from_boxes_path(name, provider_name, input_version)
+ @logger.info "Loading box #{name} from vagrants box path using provider #{provider_name} and version #{input_version}."
+ safe_name = sanitize_name(name)
if provider_name
- dir = verify_input_dir(provider_name, name)
+ @logger.info "Checking directory for provider #{provider_name}."
+ if input_version
+ @logger.info 'Input version provided, using it.'
+ version = input_version
+ else
+ @logger.info 'No version provided, getting it.'
+ version = get_version(safe_name)
+ @logger.info "Version = #{version}"
+ end
+ dir = verify_input_dir(provider_name, safe_name, version)
else
- provider_name, dir = find_input_dir(name)
+ @logger.info 'Working out provider, version and directory...'
+ provider_name, version, dir = find_input_dir(safe_name)
end
- box = create_box(provider_name, name, dir)
+ @logger.info "Creating #{name} box using provider #{provider_name} with version #{version} in #{dir}."
+ create_box(provider_name, name, version, dir)
end
def cleanup
unless @tmp_files.empty?
- @env.ui.info "Cleaning up temporary files."
+ @env.ui.info 'Cleaning up temporary files.'
@tmp_files.each do |f|
@logger.info "Deleting #{f}"
FileUtils.remove_entry_secure(f)
end
end
end
private
- def create_box(provider_name, name, dir)
- @logger.info "Creating box #{name} with provider #{provider_name} in #{dir}"
- case provider_name
- when 'kvm'
- require_relative 'box/kvm'
- Box::Kvm.new(@env, name, dir)
- when 'libvirt'
- require_relative 'box/libvirt'
- Box::Libvirt.new(@env, name, dir)
- when 'virtualbox'
- require_relative 'box/virtualbox'
- Box::Virtualbox.new(@env, name, dir)
- else
- raise Errors::ProviderNotSupported, :provider => provider_name, :direction => 'input or output'
- end
+ def create_box(provider_name, name, version, dir)
+ @logger.info "Creating box #{name} with provider #{provider_name} and version #{version} in #{dir}"
+ case provider_name
+ when 'kvm'
+ require_relative 'box/kvm'
+ Box::Kvm.new(@env, name, version, dir)
+ when 'libvirt'
+ require_relative 'box/libvirt'
+ Box::Libvirt.new(@env, name, version, dir)
+ when 'virtualbox'
+ require_relative 'box/virtualbox'
+ Box::Virtualbox.new(@env, name, version, dir)
+ else
+ fail Errors::ProviderNotSupported, provider: provider_name, direction: 'input or output'
+ end
end
- def create_output_dir(name, provider_name)
+ def create_output_dir(name, provider_name, version)
# e.g. $HOME/.vagrant.d/boxes/fedora-19/0/libvirt
- # TODO support versioned boxes
- out_dir = File.join( @env.boxes_path, name, '0', provider_name )
+ @logger.info "Attempting to create output dir for #{name} with version #{version} and provider #{provider_name}."
+ out_dir = File.join(@env.boxes_path, name, version, provider_name)
+ @logger.info "Creating out_dir #{out_dir}."
begin
FileUtils.mkdir_p(out_dir)
rescue => e
- raise Errors::CreateBoxDirFailed, :error_message => e.message
+ raise Errors::CreateBoxDirFailed, error_message: e.message
end
@logger.info "Created output directory #{out_dir}"
- return out_dir
+ out_dir
end
def unpack(file)
- @env.ui.info "Extracting box file to a temporary directory."
+ @env.ui.info 'Extracting box file to a temporary directory.'
unless File.exist? file
- raise Errors::BoxNotFound, :box => file
+ fail Errors::BoxNotFound, box: file
end
tmp_dir = Dir.mktmpdir(nil, @env.tmp_path)
@tmp_files << tmp_dir
result = Vagrant::Util::Subprocess.execute(
- "bsdtar", "-v", "-x", "-m", "-C", tmp_dir.to_s, "-f", file)
+ 'bsdtar', '-v', '-x', '-m', '-C', tmp_dir.to_s, '-f', file)
if result.exit_code != 0
- raise Errors::ExtractBoxFailed, :error_message => result.stderr.to_s
+ fail Errors::ExtractBoxFailed, error_message: result.stderr.to_s
end
@logger.info "Unpacked box to #{tmp_dir}"
- return tmp_dir
+ tmp_dir
end
def determine_provider(dir)
metadata_file = File.join(dir, 'metadata.json')
if File.exist? metadata_file
begin
- metadata = JSON.load( File.new( metadata_file, 'r') )
+ metadata = JSON.load(File.new(metadata_file, 'r'))
rescue => e
- raise Errors::DetermineProviderFailed, :error_message => e.message
+ raise Errors::LoadMetadataFailed, error_message: e.message
end
@logger.info "Determined input provider is #{metadata['provider']}"
return metadata['provider']
else
- @logger.info "No metadata found, so assuming input provider is virtualbox"
+ @logger.info 'No metadata found, so assuming input provider is virtualbox'
return 'virtualbox'
end
end
- def verify_input_dir(provider_name, name)
- # TODO support versioned boxes
- input_dir = File.join( @env.boxes_path, name, '0', provider_name)
+ def determine_version(dir)
+ metadata_file = File.join(dir, 'metadata.json')
+ if File.exist? metadata_file
+ begin
+ metadata = JSON.load(File.new(metadata_file, 'r'))
+ rescue => e
+ raise Errors::LoadMetadataFailed, error_message: e.message
+ end
+ # Handle single or multiple versions
+ if metadata['versions'].nil?
+ @logger.info 'No versions provided by metadata, asuming version 0'
+ version = '0'
+ elsif metadata['versions'].length > 1
+ metadata['versions'].each do |metadata_version|
+ @logger.info 'Itterating available metadata versions for active version.'
+ next unless metadata_version['status'] == 'active'
+ version = metadata_version['version']
+ end
+ else
+ @logger.info 'Only one metadata version, grabbing version.'
+ version = metadata['versions'][0]['version']
+ end
+ @logger.info "Determined input version is #{version}"
+ return version
+ else
+ @logger.info 'No metadata found, so assuming version is 0'
+ return '0'
+ end
+ end
+
+ def verify_input_dir(provider_name, name, version)
+ input_dir = File.join(@env.boxes_path, name, version, provider_name)
if File.directory?(input_dir)
@logger.info "Found input directory #{input_dir}"
return input_dir
else
- raise Errors::BoxNotFound, :box => input_dir
+ fail Errors::BoxNotFound, box: input_dir
end
end
def find_input_dir(name)
- # TODO support versioned boxes
- box_parent_dir = File.join( @env.boxes_path, name, '0')
+ @logger.info "Looking for input dir for box #{name}."
+ version = get_version(name)
+ box_parent_dir = File.join(@env.boxes_path, name, version)
if Dir.exist?(box_parent_dir)
providers = Dir.entries(box_parent_dir).reject { |entry| entry =~ /^\./ }
@logger.info "Found potential providers #{providers}"
else
providers = []
end
case
when providers.length < 1
- raise Errors::BoxNotFound, :box => name
+ fail Errors::BoxNotFound, box: name
when providers.length > 1
- raise Errors::TooManyBoxesFound, :box => name
+ fail Errors::TooManyBoxesFound, box: name
else
provider_name = providers.first
- input_dir = File.join( box_parent_dir, provider_name)
- @logger.info "Found source for box #{name} from provider #{provider_name} at #{input_dir}"
- return provider_name, input_dir
+ input_dir = File.join(box_parent_dir, provider_name)
+ @logger.info "Found source for box #{name} from provider #{provider_name} with version #{version} at #{input_dir}"
+ return provider_name, version, input_dir
end
end
+ def get_version(name)
+ # Get a list of directories for this box
+ @logger.info "Getting versions for #{name}."
+
+ box_dir = File.join(@env.boxes_path, name, '*')
+ possible_versions = Dir.glob(box_dir).select { |f| File.directory? f }.map { |x| x.split('/').last }
+
+ @logger.info "Possible_versions = #{possible_versions.inspect}"
+
+ if possible_versions.length > 1
+ @logger.info 'Got multiple possible versions, selecting max value'
+ version = possible_versions.max
+ else
+ @logger.info 'Got a single version, so returning it'
+ version = possible_versions.first
+ end
+
+ @logger.info "Found version #{version}"
+ version
+ end
+
+ def sanitize_name(name)
+ if name =~ /\//
+ @logger.info 'Replacing / with -VAGRANTSLASH-.'
+ name = name.dup
+ name.gsub!('/', '-VAGRANTSLASH-')
+ @logger.info "New name = #{name}."
+ end
+ name
+ end
end
end