274 lines
12 KiB
Ruby
274 lines
12 KiB
Ruby
# Copyright (c) HashiCorp, Inc.
|
|
# SPDX-License-Identifier: BUSL-1.1
|
|
|
|
require 'optparse'
|
|
require "vagrant/util/uploader"
|
|
|
|
module VagrantPlugins
|
|
module CloudCommand
|
|
module Command
|
|
class Publish < Vagrant.plugin("2", :command)
|
|
include Util
|
|
|
|
def execute
|
|
options = {direct_upload: true}
|
|
|
|
opts = OptionParser.new do |o|
|
|
o.banner = "Usage: vagrant cloud publish [options] organization/box-name version provider-name [provider-file]"
|
|
o.separator ""
|
|
o.separator "Create and release a new Vagrant Box on Vagrant Cloud"
|
|
o.separator ""
|
|
o.separator "Options:"
|
|
o.separator ""
|
|
|
|
o.on("--box-version VERSION", String, "Version of box to create") do |v|
|
|
options[:box_version] = v
|
|
end
|
|
o.on("--url URL", String, "Remote URL to download this provider (cannot be used with provider-file)") do |u|
|
|
options[:url] = u
|
|
end
|
|
o.on("-d", "--description DESCRIPTION", String, "Full description of box") do |d|
|
|
options[:description] = d
|
|
end
|
|
o.on("--version-description DESCRIPTION", String, "Description of the version to create") do |v|
|
|
options[:version_description] = v
|
|
end
|
|
o.on("-f", "--[no-]force", "Disables confirmation to create or update box") do |f|
|
|
options[:force] = f
|
|
end
|
|
o.on("-p", "--[no-]private", "Makes box private") do |p|
|
|
options[:private] = p
|
|
end
|
|
o.on("-r", "--[no-]release", "Releases box") do |p|
|
|
options[:release] = p
|
|
end
|
|
o.on("-s", "--short-description DESCRIPTION", String, "Short description of the box") do |s|
|
|
options[:short_description] = s
|
|
end
|
|
o.on("-c", "--checksum CHECKSUM_VALUE", String, "Checksum of the box for this provider. --checksum-type option is required.") do |c|
|
|
options[:checksum] = c
|
|
end
|
|
o.on("-C", "--checksum-type TYPE", String, "Type of checksum used (md5, sha1, sha256, sha384, sha512). --checksum option is required.") do |c|
|
|
options[:checksum_type] = c
|
|
end
|
|
o.on("--[no-]direct-upload", "Upload asset directly to backend storage") do |d|
|
|
options[:direct_upload] = d
|
|
end
|
|
end
|
|
|
|
# Parse the options
|
|
argv = parse_options(opts)
|
|
return if !argv
|
|
|
|
if argv.length < 3 || # missing required arguments
|
|
argv.length > 4 || # too many arguments
|
|
(argv.length < 4 && !options.key?(:url)) || # file argument required if url is not provided
|
|
(argv.length > 3 && options.key?(:url)) # cannot provider url and file argument
|
|
raise Vagrant::Errors::CLIInvalidUsage,
|
|
help: opts.help.chomp
|
|
end
|
|
|
|
org, box_name = argv.first.split('/', 2)
|
|
_, version, provider_name, box_file = argv
|
|
|
|
if box_file && !File.file?(box_file)
|
|
raise Vagrant::Errors::BoxFileNotExist,
|
|
file: box_file
|
|
end
|
|
|
|
@client = client_login(@env)
|
|
params = options.slice(:private, :release, :url, :short_description,
|
|
:description, :version_description, :checksum, :checksum_type)
|
|
|
|
# Display output to user describing action to be taken
|
|
display_preamble(org, box_name, version, provider_name, params)
|
|
|
|
if !options[:force]
|
|
cont = @env.ui.ask(I18n.t("cloud_command.continue"))
|
|
return 1 if cont.strip.downcase != "y"
|
|
end
|
|
|
|
# Load up all the models we'll need to publish the asset
|
|
box = load_box(org, box_name, @client.token)
|
|
box_v = load_box_version(box, version)
|
|
box_p = load_version_provider(box_v, provider_name)
|
|
|
|
# Update all the data
|
|
set_box_info(box, params.slice(:private, :short_description, :description))
|
|
set_version_info(box_v, params.slice(:version_description))
|
|
set_provider_info(box_p, params.slice(:checksum, :checksum_type, :url))
|
|
|
|
# Save any updated state
|
|
@env.ui.warn(I18n.t("cloud_command.publish.box_save"))
|
|
box.save
|
|
|
|
# If we have a box file asset, upload it
|
|
if box_file
|
|
upload_box_file(box_p, box_file, options.slice(:direct_upload))
|
|
end
|
|
|
|
# If configured to release the box, release it
|
|
if options[:release] && !box_v.released?
|
|
release_version(box_v)
|
|
end
|
|
|
|
# And we're done!
|
|
@env.ui.success(I18n.t("cloud_command.publish.complete", org: org, box_name: box_name))
|
|
format_box_results(box, @env)
|
|
0
|
|
rescue VagrantCloud::Error => err
|
|
@env.ui.error(I18n.t("cloud_command.errors.publish.fail", org: org, box_name: box_name))
|
|
@env.ui.error(err.message)
|
|
1
|
|
end
|
|
|
|
# Upload the file for the given box provider
|
|
#
|
|
# @param [VagrantCloud::Box::Provider] provider Vagrant Cloud box version provider
|
|
# @param [String] box_file Path to local asset for upload
|
|
# @param [Hash] options
|
|
# @option options [Boolean] :direct_upload Upload directly to backend storage
|
|
# @return [nil]
|
|
def upload_box_file(provider, box_file, options={})
|
|
box_file = File.absolute_path(box_file)
|
|
@env.ui.info(I18n.t("cloud_command.publish.upload_provider", file: box_file))
|
|
# Include size check on file and disable direct if over 5G
|
|
if options[:direct_upload]
|
|
fsize = File.stat(box_file).size
|
|
if fsize > (5 * Vagrant::Util::Numeric::GIGABYTE)
|
|
box_size = Vagrant::Util::Numeric.bytes_to_string(fsize)
|
|
@env.ui.warn(I18n.t("cloud_command.provider.direct_disable", size: box_size))
|
|
options[:direct_upload] = false
|
|
end
|
|
end
|
|
|
|
provider.upload(direct: options[:direct_upload]) do |upload_url|
|
|
Vagrant::Util::Uploader.new(upload_url, box_file, ui: @env.ui, method: :put).upload!
|
|
end
|
|
nil
|
|
end
|
|
|
|
# Release the box version
|
|
#
|
|
# @param [VagrantCloud::Box::Version] version Vagrant Cloud box version
|
|
# @return [nil]
|
|
def release_version(version)
|
|
@env.ui.info(I18n.t("cloud_command.publish.release"))
|
|
version.release
|
|
nil
|
|
end
|
|
|
|
# Set any box related attributes that were provided
|
|
#
|
|
# @param [VagrantCloud::Box] box Vagrant Cloud box
|
|
# @param [Hash] options
|
|
# @option options [Boolean] :private Box access is private
|
|
# @option options [String] :short_description Short description of box
|
|
# @option options [String] :description Full description of box
|
|
# @return [VagrantCloud::Box]
|
|
def set_box_info(box, options={})
|
|
box.private = options[:private] if options.key?(:private)
|
|
box.short_description = options[:short_description] if options.key?(:short_description)
|
|
box.description = options[:description] if options.key?(:description)
|
|
box
|
|
end
|
|
|
|
# Set any version related attributes that were provided
|
|
#
|
|
# @param [VagrantCloud::Box::Version] version Vagrant Cloud box version
|
|
# @param [Hash] options
|
|
# @option options [String] :version_description Description for this version
|
|
# @return [VagrantCloud::Box::Version]
|
|
def set_version_info(version, options={})
|
|
version.description = options[:version_description] if options.key?(:version_description)
|
|
version
|
|
end
|
|
|
|
# Set any provider related attributes that were provided
|
|
#
|
|
# @param [VagrantCloud::Box::Provider] provider Vagrant Cloud box version provider
|
|
# @param [Hash] options
|
|
# @option options [String] :url Remote URL for self hosted
|
|
# @option options [String] :checksum_type Type of checksum value provided
|
|
# @option options [String] :checksum Checksum of the box asset
|
|
# @return [VagrantCloud::Box::Provider]
|
|
def set_provider_info(provider, options={})
|
|
provider.url = options[:url] if options.key?(:url)
|
|
provider.checksum_type = options[:checksum_type] if options.key?(:checksum_type)
|
|
provider.checksum = options[:checksum] if options.key?(:checksum)
|
|
provider
|
|
end
|
|
|
|
# Load the requested version provider
|
|
#
|
|
# @param [VagrantCloud::Box::Version] version The version of the Vagrant Cloud box
|
|
# @param [String] provider_name Name of the provider
|
|
# @return [VagrantCloud::Box::Provider]
|
|
def load_version_provider(version, provider_name)
|
|
provider = version.providers.detect { |pv| pv.name == provider_name }
|
|
return provider if provider
|
|
version.add_provider(provider_name)
|
|
end
|
|
|
|
# Load the requested box version
|
|
#
|
|
# @param [VagrantCloud::Box] box The Vagrant Cloud box
|
|
# @param [String] version Version of the box
|
|
# @return [VagrantCloud::Box::Version]
|
|
def load_box_version(box, version)
|
|
v = box.versions.detect { |v| v.version == version }
|
|
return v if v
|
|
box.add_version(version)
|
|
end
|
|
|
|
# Load the requested box
|
|
#
|
|
# @param [String] org Organization name for box
|
|
# @param [String] box_name Name of the box
|
|
# @param [String] access_token User access token
|
|
# @return [VagrantCloud::Box]
|
|
def load_box(org, box_name, access_token)
|
|
account = VagrantCloud::Account.new(
|
|
custom_server: api_server_url,
|
|
access_token: access_token
|
|
)
|
|
box = account.organization(name: org).boxes.detect { |b| b.name == box_name }
|
|
return box if box
|
|
account.organization(name: org).add_box(box_name)
|
|
end
|
|
|
|
# Display publishing information to user before starting process
|
|
#
|
|
# @param [String] org Organization name
|
|
# @param [String] box_name Name of the box to publish
|
|
# @param [String] version Version of the box to publish
|
|
# @param [String] provider_name Name of the provider being published
|
|
# @param [Hash] options
|
|
# @option options [Boolean] :private Box is private
|
|
# @option options [Boolean] :release Box should be released
|
|
# @option options [String] :url Remote URL for self-hosted boxes
|
|
# @option options [String] :description Description of the box
|
|
# @option options [String] :short_description Short description of the box
|
|
# @option options [String] :version_description Description of the box version
|
|
# @return [nil]
|
|
def display_preamble(org, box_name, version, provider_name, options={})
|
|
@env.ui.warn(I18n.t("cloud_command.publish.confirm.warn"))
|
|
@env.ui.info(I18n.t("cloud_command.publish.confirm.box", org: org,
|
|
box_name: box_name, version: version, provider_name: provider_name))
|
|
@env.ui.info(I18n.t("cloud_command.publish.confirm.private")) if options[:private]
|
|
@env.ui.info(I18n.t("cloud_command.publish.confirm.release")) if options[:release]
|
|
@env.ui.info(I18n.t("cloud_command.publish.confirm.box_url",
|
|
url: options[:url])) if options[:url]
|
|
@env.ui.info(I18n.t("cloud_command.publish.confirm.box_description",
|
|
description: options[:description])) if options[:description]
|
|
@env.ui.info(I18n.t("cloud_command.publish.confirm.box_short_desc",
|
|
short_description: options[:short_description])) if options[:short_description]
|
|
@env.ui.info(I18n.t("cloud_command.publish.confirm.version_desc",
|
|
version_description: options[:version_description])) if options[:version_description]
|
|
nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|