237 lines
7.8 KiB
Ruby
237 lines
7.8 KiB
Ruby
require 'optparse'
|
|
require "rubygems/package"
|
|
|
|
module VagrantPlugins
|
|
module CommandUpload
|
|
class Command < Vagrant.plugin("2", :command)
|
|
|
|
VALID_COMPRESS_TYPES = [:tgz, :zip].freeze
|
|
|
|
def self.synopsis
|
|
"upload to machine via communicator"
|
|
end
|
|
|
|
def execute
|
|
options = {}
|
|
|
|
opts = OptionParser.new do |o|
|
|
o.banner = "Usage: vagrant upload [options] <source> [destination] [name|id]"
|
|
o.separator ""
|
|
o.separator "Options:"
|
|
o.separator ""
|
|
|
|
o.on("-t", "--temporary", "Upload source to temporary directory") do |t|
|
|
options[:temporary] = t
|
|
end
|
|
|
|
o.on("-c", "--compress", "Use gzip compression for upload") do |c|
|
|
options[:compress] = c
|
|
end
|
|
|
|
o.on("-C", "--compression-type=TYPE", "Type of compression to use (#{VALID_COMPRESS_TYPES.join(", ")})") do |c|
|
|
options[:compression_type] = c.to_sym
|
|
options[:compress] = true
|
|
end
|
|
end
|
|
|
|
argv = parse_options(opts)
|
|
return if !argv
|
|
|
|
case argv.size
|
|
when 3
|
|
source, destination, guest = argv
|
|
when 2, 1
|
|
source = argv[0]
|
|
if @env.active_machines.map(&:first).map(&:to_s).include?(argv[1])
|
|
guest = argv[1]
|
|
else
|
|
destination = argv[1]
|
|
end
|
|
else
|
|
raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp
|
|
end
|
|
|
|
# NOTE: We do this to handle paths on Windows like: "..\space dir\"
|
|
# because the final separater acts to escape the quote and ends up
|
|
# in the source value.
|
|
source = source.sub(/["']$/, "")
|
|
destination ||= File.basename(source)
|
|
|
|
if File.file?(source)
|
|
type = :file
|
|
elsif File.directory?(source)
|
|
type = :directory
|
|
else
|
|
raise Vagrant::Errors::UploadSourceMissing,
|
|
source: source
|
|
end
|
|
|
|
with_target_vms(guest, single_target: true) do |machine|
|
|
if options[:temporary]
|
|
if !machine.guest.capability?(:create_tmp_path)
|
|
raise Vagrant::Errors::UploadMissingTempCapability
|
|
end
|
|
extension = File.extname(source) if type == :file
|
|
destination = machine.guest.capability(:create_tmp_path, type: type, extension: extension)
|
|
end
|
|
|
|
if options[:compress]
|
|
compression_setup!(machine, options)
|
|
@env.ui.info(I18n.t("vagrant.commands.upload.compress",
|
|
source: source,
|
|
type: options[:compression_type]
|
|
))
|
|
destination_decompressed = destination
|
|
destination = machine.guest.capability(:create_tmp_path, type: :file, extension: ".#{options[:compression_type]}")
|
|
source_display = source
|
|
source = options[:compression_type] == :zip ? compress_source_zip(source) : compress_source_tgz(source)
|
|
end
|
|
|
|
@env.ui.info(I18n.t("vagrant.commands.upload.start",
|
|
source: source,
|
|
destination: destination
|
|
))
|
|
|
|
# If the source is a directory, attach a `/.` to the end so we
|
|
# upload the contents to the destination instead of within a
|
|
# folder at the destination
|
|
if File.directory?(source) && !source.end_with?(".")
|
|
upload_source = File.join(source, ".")
|
|
else
|
|
upload_source = source
|
|
end
|
|
|
|
machine.communicate.upload(upload_source, destination)
|
|
|
|
if options[:compress]
|
|
@env.ui.info(I18n.t("vagrant.commands.upload.decompress",
|
|
destination: destination_decompressed,
|
|
type: options[:compression_type]
|
|
))
|
|
machine.guest.capability(options[:decompression_method], destination, destination_decompressed, type: type)
|
|
destination = destination_decompressed
|
|
FileUtils.rm(source)
|
|
source = source_display
|
|
end
|
|
end
|
|
|
|
@env.ui.info(I18n.t("vagrant.commands.upload.complete",
|
|
source: source,
|
|
destination: destination
|
|
))
|
|
|
|
# Success, exit status 0
|
|
0
|
|
end
|
|
|
|
# Setup compression options and validate host and guest have capability
|
|
# to handle compression
|
|
#
|
|
# @param [Vagrant::Machine] machine Vagrant guest machine
|
|
# @param [Hash] options Command options
|
|
def compression_setup!(machine, options)
|
|
if !options[:compression_type]
|
|
if machine.guest.capability_host_chain.first[0] == :windows
|
|
options[:compression_type] = :zip
|
|
else
|
|
options[:compression_type] = :tgz
|
|
end
|
|
end
|
|
if !VALID_COMPRESS_TYPES.include?(options[:compression_type])
|
|
raise Vagrant::Errors::UploadInvalidCompressionType,
|
|
type: options[:compression_type],
|
|
valid_types: VALID_COMPRESS_TYPES.join(", ")
|
|
end
|
|
options[:decompression_method] = "decompress_#{options[:compression_type]}".to_sym
|
|
if !machine.guest.capability?(options[:decompression_method])
|
|
raise Vagrant::Errors::UploadMissingExtractCapability,
|
|
type: options[:compression_type]
|
|
end
|
|
end
|
|
|
|
# Compress path using zip into temporary file
|
|
#
|
|
# @param [String] path Path to compress
|
|
# @return [String] path to compressed file
|
|
def compress_source_zip(path)
|
|
require "zip"
|
|
zipfile = Tempfile.create(["vagrant", ".zip"])
|
|
zipfile.close
|
|
if File.file?(path)
|
|
source_items = [path]
|
|
else
|
|
source_items = Dir.glob(File.join(path, "**", "**", "*"))
|
|
end
|
|
c_dir = nil
|
|
Zip::File.open(zipfile.path, Zip::File::CREATE) do |zip|
|
|
source_items.each do |source_item|
|
|
next if File.directory?(source_item)
|
|
trim_item = source_item.sub(path, "").sub(%r{^[/\\]}, "")
|
|
dirname = File.dirname(trim_item)
|
|
zip.mkdir dirname if c_dir != dirname
|
|
c_dir = dirname
|
|
zip.get_output_stream(trim_item) do |f|
|
|
source_file = File.open(source_item, "rb")
|
|
while data = source_file.read(2048)
|
|
f.write(data)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
zipfile.path
|
|
end
|
|
|
|
# Compress path using tar and gzip into temporary file
|
|
#
|
|
# @param [String] path Path to compress
|
|
# @return [String] path to compressed file
|
|
def compress_source_tgz(path)
|
|
tarfile = Tempfile.create(["vagrant", ".tar"])
|
|
tarfile.close
|
|
tarfile = File.open(tarfile.path, "wb+")
|
|
tgzfile = Tempfile.create(["vagrant", ".tgz"])
|
|
tgzfile.close
|
|
tgzfile = File.open(tgzfile.path, "wb")
|
|
tar = Gem::Package::TarWriter.new(tarfile)
|
|
tgz = Zlib::GzipWriter.new(tgzfile)
|
|
if File.file?(path)
|
|
tar.add_file(File.basename(path), File.stat(path).mode) do |io|
|
|
File.open(path, "rb") do |file|
|
|
while bytes = file.read(4096)
|
|
io.write(bytes)
|
|
end
|
|
end
|
|
end
|
|
else
|
|
Dir.glob(File.join(path, "**/**/*")).each do |item|
|
|
rel_path = item.sub(path, "")
|
|
item_mode = File.stat(item).mode
|
|
|
|
if File.directory?(item)
|
|
tar.mkdir(rel_path, item_mode)
|
|
else
|
|
tar.add_file(rel_path, item_mode) do |io|
|
|
File.open(item, "rb") do |file|
|
|
while bytes = file.read(4096)
|
|
io.write(bytes)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
tar.close
|
|
tarfile.rewind
|
|
while bytes = tarfile.read(4096)
|
|
tgz.write bytes
|
|
end
|
|
tgz.close
|
|
tgzfile.close
|
|
tarfile.close
|
|
File.delete(tarfile.path)
|
|
tgzfile.path
|
|
end
|
|
end
|
|
end
|
|
end
|