2015-11-19 18:18:51 -08:00

159 lines
5.6 KiB
Ruby

require 'fileutils'
require "pathname"
require 'vagrant/util/safe_chdir'
require 'vagrant/util/subprocess'
module Vagrant
module Action
module General
# A general packaging (tar) middleware. Given the following options,
# it will do the right thing:
#
# * package.output - The filename of the outputted package.
# * package.include - An array of files to include in the package.
# * package.directory - The directory which contains the contents to
# compress into the package.
#
# This middleware always produces the final file in the current working
# directory (FileUtils.pwd)
class Package
include Util
def initialize(app, env)
@app = app
env["package.files"] ||= {}
env["package.output"] ||= "package.box"
end
def call(env)
@env = env
file_name = File.basename(@env["package.output"].to_s)
raise Errors::PackageOutputDirectory if File.directory?(tar_path)
raise Errors::PackageOutputExists, file_name:file_name if File.exist?(tar_path)
raise Errors::PackageRequiresDirectory if !env["package.directory"] ||
!File.directory?(env["package.directory"])
@app.call(env)
@env[:ui].info I18n.t("vagrant.actions.general.package.compressing", tar_path: tar_path)
copy_include_files
setup_private_key
compress
end
def recover(env)
@env = env
# There are certain exceptions that we don't delete the file for.
ignore_exc = [Errors::PackageOutputDirectory, Errors::PackageOutputExists]
ignore_exc.each do |exc|
return if env["vagrant.error"].is_a?(exc)
end
# Cleanup any packaged files if the packaging failed at some point.
File.delete(tar_path) if File.exist?(tar_path)
end
# This method copies the include files (passed in via command line)
# to the temporary directory so they are included in a sub-folder within
# the actual box
def copy_include_files
include_directory = Pathname.new(@env["package.directory"]).join("include")
@env["package.files"].each do |from, dest|
# We place the file in the include directory
to = include_directory.join(dest)
@env[:ui].info I18n.t("vagrant.actions.general.package.packaging", file: from)
FileUtils.mkdir_p(to.parent)
# Copy direcotry contents recursively.
if File.directory?(from)
FileUtils.cp_r(Dir.glob(from), to.parent, preserve: true)
else
FileUtils.cp(from, to, preserve: true)
end
end
rescue Errno::EEXIST => e
raise if !e.to_s.include?("symlink")
# The directory contains symlinks. Show a nicer error.
raise Errors::PackageIncludeSymlink
end
# Compress the exported file into a package
def compress
# Get the output path. We have to do this up here so that the
# pwd returns the proper thing.
output_path = tar_path.to_s
# Switch into that directory and package everything up
Util::SafeChdir.safe_chdir(@env["package.directory"]) do
# Find all the files in our current directory and tar it up!
files = Dir.glob(File.join(".", "*"))
# Package!
Util::Subprocess.execute("bsdtar", "-czf", output_path, *files)
end
end
# This will copy the generated private key into the box and use
# it for SSH by default. We have to do this because we now generate
# random keypairs on boot, so packaged boxes would stop working
# without this.
def setup_private_key
# If we don't have machine, we do nothing (weird)
return if !@env[:machine]
# If we don't have a data dir, we also do nothing (base package)
return if !@env[:machine].data_dir
# If we don't have a generated private key, we do nothing
path = @env[:machine].data_dir.join("private_key")
if !path.file?
# If we have a private key that was copied into this box,
# then we copy that. This is a bit of a heuristic and can be a
# security risk if the key is named the correct thing, but
# we'll take that risk for dev environments.
(@env[:machine].config.ssh.private_key_path || []).each do |p|
# If we have the correctly named key, copy it
if File.basename(p) == "vagrant_private_key"
path = Pathname.new(p)
break
end
end
end
# If we still have no matching key, do nothing
return if !path.file?
# Copy it into our box directory
dir = Pathname.new(@env["package.directory"])
new_path = dir.join("vagrant_private_key")
FileUtils.cp(path, new_path)
# Append it to the Vagrantfile (or create a Vagrantfile)
vf_path = dir.join("Vagrantfile")
mode = "w+"
mode = "a" if vf_path.file?
vf_path.open(mode) do |f|
f.binmode
f.puts
f.puts %Q[Vagrant.configure("2") do |config|]
f.puts %Q[ config.ssh.private_key_path = File.expand_path("../vagrant_private_key", __FILE__)]
f.puts %Q[end]
end
end
# Path to the final box output file
def tar_path
File.expand_path(@env["package.output"], FileUtils.pwd)
end
end
end
end
end