110 lines
3.3 KiB
Ruby
110 lines
3.3 KiB
Ruby
# Copyright (c) HashiCorp, Inc.
|
|
# SPDX-License-Identifier: BUSL-1.1
|
|
|
|
require "uri"
|
|
|
|
require "log4r"
|
|
require "vagrant/util/busy"
|
|
require "vagrant/util/platform"
|
|
require "vagrant/util/subprocess"
|
|
require "vagrant/util/curl_helper"
|
|
|
|
module Vagrant
|
|
module Util
|
|
# This class uploads files using various protocols by subprocessing
|
|
# to cURL. cURL is a much more capable and complete download tool than
|
|
# a hand-rolled Ruby library, so we defer to its expertise.
|
|
class Uploader
|
|
|
|
# @param [String] destination Valid URL to upload file to
|
|
# @param [String] file Location of file to upload on disk
|
|
# @param [Hash] options
|
|
# @option options [Vagrant::UI] :ui UI interface for output
|
|
# @option options [String, Symbol] :method Request method for upload
|
|
def initialize(destination, file, options=nil)
|
|
options ||= {}
|
|
@logger = Log4r::Logger.new("vagrant::util::uploader")
|
|
@destination = destination.to_s
|
|
@file = file.to_s
|
|
@ui = options[:ui]
|
|
@request_method = options[:method]
|
|
|
|
if !@request_method
|
|
@request_method = "PUT"
|
|
end
|
|
@request_method = @request_method.to_s.upcase
|
|
end
|
|
|
|
def upload!
|
|
data_proc = Vagrant::Util::CurlHelper.capture_output_proc(@logger, @ui)
|
|
|
|
@logger.info("Uploader starting upload: ")
|
|
@logger.info(" -- Source: #{@file}")
|
|
@logger.info(" -- Destination: #{@destination}")
|
|
|
|
options = build_options
|
|
subprocess_options = {notify: :stderr}
|
|
|
|
begin
|
|
execute_curl(options, subprocess_options, &data_proc)
|
|
rescue Errors::UploaderError => e
|
|
raise
|
|
ensure
|
|
@ui.clear_line if @ui
|
|
end
|
|
end
|
|
|
|
protected
|
|
|
|
def build_options
|
|
options = [@destination, "--request", @request_method, "--upload-file", @file, "--fail"]
|
|
return options
|
|
end
|
|
|
|
def execute_curl(options, subprocess_options, &data_proc)
|
|
options = options.dup
|
|
options << subprocess_options
|
|
|
|
# Create the callback that is called if we are interrupted
|
|
interrupted = false
|
|
int_callback = Proc.new do
|
|
@logger.info("Uploader interrupted!")
|
|
interrupted = true
|
|
end
|
|
|
|
# Execute!
|
|
result = Busy.busy(int_callback) do
|
|
Subprocess.execute("curl", *options, &data_proc)
|
|
end
|
|
|
|
# If the upload was interrupted, then raise a specific error
|
|
raise Errors::UploaderInterrupted if interrupted
|
|
|
|
# If it didn't exit successfully, we need to parse the data and
|
|
# show an error message.
|
|
if result.exit_code != 0
|
|
@logger.warn("Uploader exit code: #{result.exit_code}")
|
|
check = result.stderr.match(/\n*curl:\s+\((?<code>\d+)\)\s*(?<error>.*)$/)
|
|
if !check
|
|
err_msg = result.stderr
|
|
else
|
|
err_msg = check[:error]
|
|
end
|
|
|
|
raise Errors::UploaderError,
|
|
exit_code: result.exit_code,
|
|
message: err_msg
|
|
end
|
|
|
|
if @ui
|
|
@ui.clear_line
|
|
# Windows doesn't clear properly for some reason, so we just
|
|
# output one more newline.
|
|
@ui.detail("") if Platform.windows?
|
|
end
|
|
result
|
|
end
|
|
end
|
|
end
|
|
end
|