Introduce `cloud` command

This commit adds a new command to Vagrant called `cloud`. It handles any
and all interactions with the external service Vagrant Cloud.
This commit is contained in:
Brian Cain 2018-06-25 12:56:54 -07:00
parent dd3d6c7cf2
commit e70b871660
No known key found for this signature in database
GPG Key ID: 9FC4639B2E4510A0
57 changed files with 4369 additions and 0 deletions

View File

@ -61,6 +61,13 @@ if ENV["VAGRANT_LOG"] && ENV["VAGRANT_LOG"] != ""
end end
Log4r::Outputter.stderr.formatter = Vagrant::Util::LoggingFormatter.new(base_formatter) Log4r::Outputter.stderr.formatter = Vagrant::Util::LoggingFormatter.new(base_formatter)
logger = nil logger = nil
# Cloud gem uses RestClient to make HTTP requests, so
# log them if debug is enabled
if level == 1
# TODO: Need to ensure token is marked sensitive, if possible here
ENV["RESTCLIENT_LOG"] = "stdout"
end
end end
end end

View File

@ -0,0 +1,91 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module AuthCommand
module Command
class Login < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud auth login [options]"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-c", "--check", "Only checks if you're logged in") do |c|
options[:check] = c
end
o.on("-d", "--description DESCRIPTION", String, "Description for the Vagrant Cloud token") do |d|
options[:description] = d
end
o.on("-k", "--logout", "Logs you out if you're logged in") do |k|
options[:logout] = k
end
o.on("-t", "--token TOKEN", String, "Set the Vagrant Cloud token") do |t|
options[:token] = t
end
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |l|
options[:login] = l
end
end
# TODO: Should be an alias for the existing login command
# Parse the options
argv = parse_options(opts)
return if !argv
@client = Client.new(@env)
@client.username_or_email = options[:login]
# Determine what task we're actually taking based on flags
if options[:check]
return execute_check
elsif options[:logout]
return execute_logout
elsif options[:token]
return execute_token(options[:token])
else
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options)
end
0
end
def execute_check
if @client.logged_in?
@env.ui.success(I18n.t("cloud_command.check_logged_in"))
return 0
else
@env.ui.error(I18n.t("cloud_command.check_not_logged_in"))
return 1
end
end
def execute_logout
@client.clear_token
@env.ui.success(I18n.t("cloud_command.logged_out"))
return 0
end
def execute_token(token)
@client.store_token(token)
@env.ui.success(I18n.t("cloud_command.token_saved"))
if @client.logged_in?
@env.ui.success(I18n.t("cloud_command.check_logged_in"))
return 0
else
@env.ui.error(I18n.t("cloud_command.invalid_token"))
return 1
end
end
end
end
end
end
end

View File

@ -0,0 +1,42 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module AuthCommand
module Command
class Logout < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud auth logout [options]"
o.separator ""
o.separator "Logs you out if you're logged in locally."
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |l|
options[:login] = l
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.length > 1
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
# Initializes client and deletes token on disk
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
@client.clear_token
@env.ui.success(I18n.t("cloud_command.logged_out"))
return 0
end
end
end
end
end
end

View File

@ -0,0 +1,20 @@
require "vagrant"
module VagrantPlugins
module CloudCommand
module AuthCommand
class Plugin < Vagrant.plugin("2")
name "vagrant cloud auth"
description <<-DESC
Authorization commands for Vagrant Cloud
DESC
command(:auth) do
require_relative "root"
Command::Root
end
end
end
end
end

View File

@ -0,0 +1,73 @@
module VagrantPlugins
module CloudCommand
module AuthCommand
module Command
class Root < Vagrant.plugin("2", :command)
def self.synopsis
"Manages everything authorization related to Vagrant Cloud"
end
def initialize(argv, env)
super
@main_args, @sub_command, @sub_args = split_main_and_subcommand(argv)
@subcommands = Vagrant::Registry.new
@subcommands.register(:login) do
require File.expand_path("../login", __FILE__)
Command::Login
end
@subcommands.register(:logout) do
require File.expand_path("../logout", __FILE__)
Command::Logout
end
@subcommands.register(:whoami) do
require File.expand_path("../whoami", __FILE__)
Command::Whoami
end
end
def execute
if @main_args.include?("-h") || @main_args.include?("--help")
# Print the help for all the box commands.
return help
end
# If we reached this far then we must have a subcommand. If not,
# then we also just print the help and exit.
command_class = @subcommands.get(@sub_command.to_sym) if @sub_command
return help if !command_class || !@sub_command
@logger.debug("Invoking command class: #{command_class} #{@sub_args.inspect}")
# Initialize and execute the command class
command_class.new(@sub_args, @env).execute
end
# Prints the help out for this command
def help
opts = OptionParser.new do |opts|
opts.banner = "Usage: vagrant cloud auth <subcommand> [<args>]"
opts.separator ""
opts.separator "Helper commands for authorization with Vagrant Cloud"
opts.separator ""
opts.separator "Available subcommands:"
# Add the available subcommands as separators in order to print them
# out as well.
keys = []
@subcommands.each { |key, value| keys << key.to_s }
keys.sort.each do |key|
opts.separator " #{key}"
end
opts.separator ""
opts.separator "For help on any individual subcommand run `vagrant cloud auth <subcommand> -h`"
end
@env.ui.info(opts.help, prefix: false)
end
end
end
end
end
end

View File

@ -0,0 +1,62 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module AuthCommand
module Command
class Whoami < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud auth whoami [options] [token]"
o.separator ""
o.separator "Determine who you are logged in as"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |l|
options[:login] = l
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.size > 1
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
if argv.first
token = argv.first
else
token = @client.token
end
whoami(token, options[:username])
end
def whoami(access_token, username)
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account?(username, access_token, server_url)
begin
success = account.validate_token
user = success["user"]["username"]
@env.ui.success("Currently logged in as #{user}")
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.whoami.read_error", org: username))
@env.ui.error(e)
return 1
end
return 1
end
end
end
end
end
end

View File

@ -0,0 +1,74 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module BoxCommand
module Command
class Create < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud box create [options] organization/box-name"
o.separator ""
o.separator "Creates an empty box entry on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-d", "--description DESCRIPTION", String, "Longer description of the box") do |d|
options[:description] = d
end
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |t|
options[:username] = u
end
o.on("-s", "--short-description DESCRIPTION", String, "Short description of the box") do |s|
options[:short] = s
end
o.on("-p", "--private", "Makes box private") do |p|
options[:private] = p
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 2
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
box = argv.first.split('/')
org = box[0]
box_name = box[1]
create_box(org, box_name, options, @client.token)
end
# @param [String] - org
# @param [String] - box_name
# @param [Hash] - options
def create_box(org, box_name, options, access_token)
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account?(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, options[:short], options[:description], access_token)
begin
success = box.create
@env.ui.success(I18n.t("cloud_command.box.create_success", org: org, box_name: box_name))
VagrantPlugins::CloudCommand::Util.format_box_results(success.compact, @env)
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.box.create_fail", org: org, box_name: box_name))
@env.ui.error(e)
return 1
end
return 1
end
end
end
end
end
end

View File

@ -0,0 +1,65 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module BoxCommand
module Command
class Delete < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud box delete [options] organization/box-name"
o.separator ""
o.separator "Deletes box entry on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |t|
options[:username] = u
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 2
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@env.ui.warn(I18n.t("cloud_command.box.delete_warn", box: argv.first))
continue = @env.ui.ask(I18n.t("cloud_command.continue"))
return 1 if continue.downcase != "y"
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
box = argv.first.split('/')
org = box[0]
box_name = box[1]
delete_box(org, box_name, options[:username], @client.token)
end
def delete_box(org, box_name, username, access_token)
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account?(username, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
begin
success = box.delete(org, box_name)
@env.ui.success(I18n.t("cloud_command.box.delete_success", org: org, box_name: box_name))
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.box.delete_fail", org: org, box_name: box_name))
@env.ui.error(e)
return 1
end
return 1
end
end
end
end
end
end

View File

@ -0,0 +1,19 @@
require "vagrant"
module VagrantPlugins
module CloudCommand
module BoxCommand
class Plugin < Vagrant.plugin("2")
name "vagrant cloud box"
description <<-DESC
Box CRUD commands for Vagrant Cloud
DESC
command(:box) do
require_relative "root"
Command::Root
end
end
end
end
end

View File

@ -0,0 +1,77 @@
module VagrantPlugins
module CloudCommand
module BoxCommand
module Command
class Root < Vagrant.plugin("2", :command)
def self.synopsis
"Commands to manage boxes on Vagrant Cloud"
end
def initialize(argv, env)
super
@main_args, @sub_command, @sub_args = split_main_and_subcommand(argv)
@subcommands = Vagrant::Registry.new
@subcommands.register(:create) do
require File.expand_path("../create", __FILE__)
Command::Create
end
@subcommands.register(:delete) do
require File.expand_path("../delete", __FILE__)
Command::Delete
end
@subcommands.register(:show) do
require File.expand_path("../show", __FILE__)
Command::Show
end
@subcommands.register(:update) do
require File.expand_path("../update", __FILE__)
Command::Update
end
end
def execute
if @main_args.include?("-h") || @main_args.include?("--help")
# Print the help for all the box commands.
return help
end
# If we reached this far then we must have a subcommand. If not,
# then we also just print the help and exit.
command_class = @subcommands.get(@sub_command.to_sym) if @sub_command
return help if !command_class || !@sub_command
@logger.debug("Invoking command class: #{command_class} #{@sub_args.inspect}")
# Initialize and execute the command class
command_class.new(@sub_args, @env).execute
end
# Prints the help out for this command
def help
opts = OptionParser.new do |opts|
opts.banner = "Usage: vagrant cloud box <subcommand> [<args>]"
opts.separator ""
opts.separator "Commands to manage boxes on Vagrant Cloud"
opts.separator ""
opts.separator "Available subcommands:"
# Add the available subcommands as separators in order to print them
# out as well.
keys = []
@subcommands.each { |key, value| keys << key.to_s }
keys.sort.each do |key|
opts.separator " #{key}"
end
opts.separator ""
opts.separator "For help on any individual subcommand run `vagrant cloud box <subcommand> -h`"
end
@env.ui.info(opts.help, prefix: false)
end
end
end
end
end
end

View File

@ -0,0 +1,73 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module BoxCommand
module Command
class Show < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud box show [options] organization/box-name"
o.separator ""
o.separator "Displays a boxes attributes on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |u|
options[:username] = u
end
o.on("--versions VERSION", String, "Display box information for a specific version") do |v|
options[:version] = v
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 2
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
box = argv.first.split('/')
show_box(box[0], box[1], options, @client.token)
end
def show_box(org, box_name, options, access_token)
username = options[:username]
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account?(username, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
begin
success = box.read(org, box_name)
if options[:version]
# show *this* version only
results = success["versions"].select{ |v| v if v["version"] == options[:version] }.first
if !results
@env.ui.warn(I18n.t("cloud_command.box.show_filter_empty", version: options[:version], org: org,box_name:box_name))
return 0
end
else
results = success
end
VagrantPlugins::CloudCommand::Util.format_box_results(results.compact, @env)
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.box.show_fail", org: org,box_name:box_name))
@env.ui.error(e)
return 1
end
end
end
end
end
end
end

View File

@ -0,0 +1,71 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module BoxCommand
module Command
class Update < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud box update [options] organization/box-name"
o.separator ""
o.separator "Updates a box entry on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-d", "--description DESCRIPTION", "Longer desscription of the box") do |d|
options[:description] = d
end
o.on("-u", "--username", "The username of the organization that will own the box") do |u|
options[:username] = u
end
o.on("-s", "--short-description DESCRIPTION", "Short description of the box") do |s|
options[:short_description] = s
end
o.on("-p", "--private", "Makes box private") do |p|
options[:private] = p
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 2 || options.length == 0
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
box = argv.first.split('/')
update_box(box[0], box[1], options, @client.token)
end
def update_box(org, box_name, options, access_token)
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account?(options[:username], access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
options[:organization] = org
options[:name] = box_name
begin
success = box.update(options)
@env.ui.success(I18n.t("cloud_command.box.update_success", org: org, box_name: box_name))
VagrantPlugins::CloudCommand::Util.format_box_results(success.compact, @env)
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.box.update_fail", org: org, box_name: box_name))
@env.ui.error(e)
return 1
end
return 1
end
end
end
end
end
end

View File

@ -0,0 +1,258 @@
require "rest_client"
require "vagrant_cloud"
require "vagrant/util/downloader"
require "vagrant/util/presence"
require Vagrant.source_root.join("plugins/commands/cloud/errors")
module VagrantPlugins
module CloudCommand
class Client
######################################################################
# Class that deals with managing users 'local' token for Vagrant Cloud
######################################################################
APP = "app".freeze
include Vagrant::Util::Presence
attr_accessor :username_or_email
attr_accessor :password
attr_reader :two_factor_default_delivery_method
attr_reader :two_factor_delivery_methods
# Initializes a login client with the given Vagrant::Environment.
#
# @param [Vagrant::Environment] env
def initialize(env)
@logger = Log4r::Logger.new("vagrant::cloud::client")
@env = env
end
# Removes the token, effectively logging the user out.
def clear_token
@logger.info("Clearing token")
token_path.delete if token_path.file?
end
# Checks if the user is logged in by verifying their authentication
# token.
#
# @return [Boolean]
def logged_in?
token = self.token
return false if !token
with_error_handling do
url = "#{Vagrant.server_url}/api/v1/authenticate" +
"?access_token=#{token}"
RestClient.get(url, content_type: :json)
true
end
rescue Errors::Unauthorized
false
end
# Login logs a user in and returns the token for that user. The token
# is _not_ stored unless {#store_token} is called.
#
# @param [String] description
# @param [String] code
# @return [String] token The access token, or nil if auth failed.
def login(description: nil, code: nil)
@logger.info("Logging in '#{username_or_email}'")
response = post(
"/api/v1/authenticate", {
user: {
login: username_or_email,
password: password
},
token: {
description: description
},
two_factor: {
code: code
}
}
)
response["token"]
end
# Requests a 2FA code
# @param [String] delivery_method
def request_code(delivery_method)
@env.ui.warn("Requesting 2FA code via #{delivery_method.upcase}...")
response = post(
"/api/v1/two-factor/request-code", {
user: {
login: username_or_email,
password: password
},
two_factor: {
delivery_method: delivery_method.downcase
}
}
)
two_factor = response['two_factor']
obfuscated_destination = two_factor['obfuscated_destination']
@env.ui.success("2FA code sent to #{obfuscated_destination}.")
end
# Issues a post to a Vagrant Cloud path with the given payload.
# @param [String] path
# @param [Hash] payload
# @return [Hash] response data
def post(path, payload)
with_error_handling do
url = File.join(Vagrant.server_url, path)
proxy = nil
proxy ||= ENV["HTTPS_PROXY"] || ENV["https_proxy"]
proxy ||= ENV["HTTP_PROXY"] || ENV["http_proxy"]
RestClient.proxy = proxy
response = RestClient::Request.execute(
method: :post,
url: url,
payload: JSON.dump(payload),
proxy: proxy,
headers: {
accept: :json,
content_type: :json,
user_agent: Vagrant::Util::Downloader::USER_AGENT,
},
)
JSON.load(response.to_s)
end
end
# Stores the given token locally, removing any previous tokens.
#
# @param [String] token
def store_token(token)
@logger.info("Storing token in #{token_path}")
token_path.open("w") do |f|
f.write(token)
end
nil
end
# Reads the access token if there is one. This will first read the
# `VAGRANT_CLOUD_TOKEN` environment variable and then fallback to the stored
# access token on disk.
#
# @return [String]
def token
if present?(ENV["VAGRANT_CLOUD_TOKEN"]) && token_path.exist?
@env.ui.warn <<-EOH.strip
Vagrant detected both the VAGRANT_CLOUD_TOKEN environment variable and a Vagrant login
token are present on this system. The VAGRANT_CLOUD_TOKEN environment variable takes
precedence over the locally stored token. To remove this error, either unset
the VAGRANT_CLOUD_TOKEN environment variable or remove the login token stored on disk:
~/.vagrant.d/data/vagrant_login_token
EOH
end
if present?(ENV["VAGRANT_CLOUD_TOKEN"])
@logger.debug("Using authentication token from environment variable")
return ENV["VAGRANT_CLOUD_TOKEN"]
end
if token_path.exist?
@logger.debug("Using authentication token from disk at #{token_path}")
return token_path.read.strip
end
if present?(ENV["ATLAS_TOKEN"])
@logger.warn("ATLAS_TOKEN detected within environment. Using ATLAS_TOKEN in place of VAGRANT_CLOUD_TOKEN.")
return ENV["ATLAS_TOKEN"]
end
@logger.debug("No authentication token in environment or #{token_path}")
nil
end
protected
def with_error_handling(&block)
yield
rescue RestClient::Unauthorized
@logger.debug("Unauthorized!")
raise Errors::Unauthorized
rescue RestClient::BadRequest => e
@logger.debug("Bad request:")
@logger.debug(e.message)
@logger.debug(e.backtrace.join("\n"))
parsed_response = JSON.parse(e.response)
errors = parsed_response["errors"].join("\n")
raise Errors::ServerError, errors: errors
rescue RestClient::NotAcceptable => e
@logger.debug("Got unacceptable response:")
@logger.debug(e.message)
@logger.debug(e.backtrace.join("\n"))
parsed_response = JSON.parse(e.response)
if two_factor = parsed_response['two_factor']
store_two_factor_information two_factor
if two_factor_default_delivery_method != APP
request_code two_factor_default_delivery_method
end
raise Errors::TwoFactorRequired
end
begin
errors = parsed_response["errors"].join("\n")
raise Errors::ServerError, errors: errors
rescue JSON::ParserError; end
raise "An unexpected error occurred: #{e.inspect}"
rescue SocketError
@logger.info("Socket error")
raise Errors::ServerUnreachable, url: Vagrant.server_url.to_s
end
def token_path
@env.data_dir.join("vagrant_login_token")
end
def store_two_factor_information(two_factor)
@two_factor_default_delivery_method =
two_factor['default_delivery_method']
@two_factor_delivery_methods =
two_factor['delivery_methods']
@env.ui.warn "2FA is enabled for your account."
if two_factor_default_delivery_method == APP
@env.ui.info "Enter the code from your authenticator."
else
@env.ui.info "Default method is " \
"'#{two_factor_default_delivery_method}'."
end
other_delivery_methods =
two_factor_delivery_methods - [APP]
if other_delivery_methods.any?
other_delivery_methods_sentence = other_delivery_methods
.map { |word| "'#{word}'" }
.join(' or ')
@env.ui.info "You can also type #{other_delivery_methods_sentence} " \
"to request a new code."
end
end
end
end
end

View File

@ -0,0 +1,24 @@
module VagrantPlugins
module CloudCommand
module Errors
class Error < Vagrant::Errors::VagrantError
error_namespace("cloud_command.errors")
end
class ServerError < Error
error_key(:server_error)
end
class ServerUnreachable < Error
error_key(:server_unreachable)
end
class Unauthorized < Error
error_key(:unauthorized)
end
class TwoFactorRequired < Error
end
end
end
end

View File

@ -0,0 +1,52 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module Command
class List < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud list [options] organization"
o.separator ""
o.separator "Search for boxes managed by a specific user"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-j", "--json", "Formats results in JSON") do |j|
options[:check] = j
end
o.on("-l", "--limit", Integer, "Max number of search results (default is 25)") do |l|
options[:check] = l
end
o.on("-p", "--provider", "Comma separated list of providers to filter search on. Defaults to all.") do |p|
options[:check] = p
end
o.on("-s", "--sort-by", "Column to sort list (created, downloads, updated)") do |s|
options[:check] = s
end
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |t|
options[:username] = u
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.length > 2
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
# TODO: This endpoint is not implemented yet
0
end
end
end
end
end

View File

@ -0,0 +1,138 @@
en:
cloud_command:
publish:
box_create:
Creating a box entry...
version_create:
Creating a version entry...
provider_create:
Creating a provider entry...
upload_provider:
Uploading provider with file %{file}
release:
Releasing box...
complete:
Complete! Published %{org}/%{box_name}
continue: |-
Do you wish to continue? [y/N]
box:
show_filter_empty: |-
No version matched %{version} for %{org}/%{box_name}
create_success: |-
Created box %{org}/%{box_name}
delete_success: |-
Deleted box %{org}/%{box_name}
delete_warn: |-
This will completely remove %{box} from Vagrant Cloud. This cannot be undone.
update_success: |-
Updated box %{org}/%{box_name}
search:
no_results: |-
No results found for %{query}
upload:
no_url: |-
No URL was provided to upload the provider
You will need to run the `vagrant cloud provider upload` command to provide a box
provider:
upload: |-
Uploading provider %{provider_file} ...
upload_success: |-
Uploaded provider %{provider} on %{org}/%{box_name} for version %{version}
delete_warn: |-
This will completely remove provider %{provider} on version %{version} from %{box} on Vagrant Cloud. This cannot be undone.
create_success: |-
Created provider %{provider} on %{org}/%{box_name} for version %{version}
delete_success: |-
Deleted provider %{provider} on %{org}/%{box_name} for version %{version}
update_success: |-
Updated provider %{provider} on %{org}/%{box_name} for version %{version}
version:
create_success: |-
Created version %{version} on %{org}/%{box_name} for version %{version}
delete_success: |-
Deleted version %{version} on %{org}/%{box_name}
release_success: |-
Released version %{version} on %{org}/%{box_name}
revoke_success: |-
Revoked version %{version} on %{org}/%{box_name}
update_success: |-
Updated version %{version} on %{org}/%{box_name}
revoke_warn: |-
This will revoke version %{version} from %{box} from Vagrant Cloud. This cannot be undone.
release_warn: |-
This will release version %{version} from %{box} to Vagrant Cloud and be available to download.
delete_warn: |-
This will completely remove version %{version} from %{box} from Vagrant Cloud. This cannot be undone.
errors:
search:
fail: |-
Could not complete search request
publish:
fail: |-
Failed to create box %{org}/%{box_name}
box:
create_fail: |-
Failed to create box %{org}/%{box_name}
delete_fail: |-
Failed to delete box %{org}/%{box_name}
show_fail: |-
Could not get information about box %{org}/%{box_name}
update_fail: |-
Failed to update box %{org}/%{box_name}
whoami:
read_error: |-
Failed to read organization %{org}
provider:
create_fail: |-
Failed to create provider %{provider} on box %{org}/%{box_name} for version %{version}
update_fail: |-
Failed to update provider %{provider} on box %{org}/%{box_name} for version %{version}
delete_fail: |-
Failed to delete provider %{provider} on box %{org}/%{box_name} for version %{version}
upload_fail: |-
Failed to upload provider %{provider} on box %{org}/%{box_name} for version %{version}
version:
create_fail: |-
Failed to create version %{version} on box %{org}/%{box_name}
delete_fail: |-
Failed to delete version %{version} on box %{org}/%{box_name}
release_fail: |-
Failed to release version %{version} on box %{org}/%{box_name}
revoke_fail: |-
Failed to revoke version %{version} on box %{org}/%{box_name}
update_fail: |-
Failed to update version %{version} on box %{org}/%{box_name}
server_error: |-
The Vagrant Cloud server responded with a not-OK response:
%{errors}
server_unreachable: |-
The Vagrant Cloud server is not currently accepting connections. Please check
your network connection and try again later.
unauthorized: |-
Invalid username or password. Please try again.
check_logged_in: |-
You are already logged in.
check_not_logged_in: |-
You are not currently logged in. Please run `vagrant login` and provide
your login information to authenticate.
command_header: |-
In a moment we will ask for your username and password to HashiCorp's
Vagrant Cloud. After authenticating, we will store an access token locally on
disk. Your login details will be transmitted over a secure connection, and
are never stored on disk locally.
If you do not have an Vagrant Cloud account, sign up at
https://www.vagrantcloud.com
invalid_login: |-
Invalid username or password. Please try again.
invalid_token: |-
Invalid token. Please try again.
logged_in: |-
You are now logged in.
logged_out: |-
You are logged out.
token_saved: |-
The token was successfully saved.

View File

@ -0,0 +1,30 @@
require "vagrant"
require 'vagrant_cloud'
require Vagrant.source_root.join("plugins/commands/cloud/util")
require Vagrant.source_root.join("plugins/commands/cloud/client/client")
module VagrantPlugins
module CloudCommand
class Plugin < Vagrant.plugin("2")
name "vagrant-cloud"
description <<-DESC
Provides the cloud command and internal API access to Vagrant Cloud.
DESC
command(:cloud) do
require_relative "root"
init!
Command::Root
end
protected
def self.init!
return if defined?(@_init)
I18n.load_path << File.expand_path("../locales/en.yml", __FILE__)
I18n.reload!
@_init = true
end
end
end
end

View File

@ -0,0 +1,72 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module ProviderCommand
module Command
class Create < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud provider create [options] organization/box-name provider-name version [url]"
o.separator ""
o.separator "Creates a provider entry on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |t|
options[:username] = u
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 4
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
box = argv.first.split('/')
org = box[0]
box_name = box[1]
provider_name = argv[1]
version = argv[2]
url = argv[3]
upload_provider(org, box_name, provider_name, version, url, @client.token, options)
end
def upload_provider(org, box_name, provider_name, version, url, access_token, options)
if !url
@env.ui.warn(I18n.t("cloud_command.upload.no_url"))
end
org = options[:username] if options[:username]
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account?(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
cloud_version = VagrantCloud::Version.new(box, version, nil, nil, access_token)
provider = VagrantCloud::Provider.new(cloud_version, provider_name, nil, url, org, box_name, access_token)
begin
success = provider.create_provider
@env.ui.success(I18n.t("cloud_command.provider.create_success", provider:provider_name, org: org, box_name: box_name, version: version))
VagrantPlugins::CloudCommand::Util.format_box_results(success.compact, @env)
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.provider.create_fail", provider:provider_name, org: org, box_name: box_name, version: version))
@env.ui.error(e)
return 1
end
end
end
end
end
end
end

View File

@ -0,0 +1,70 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module ProviderCommand
module Command
class Delete < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud provider delete [options] organization/box-name provider-name version"
o.separator ""
o.separator "Deletes a provider entry on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |t|
options[:username] = u
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 3
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
box = argv.first.split('/')
org = box[0]
box_name = box[1]
provider_name = argv[1]
version = argv[2]
@env.ui.warn(I18n.t("cloud_command.provider.delete_warn", provider: provider_name, version:version, box: argv.first))
continue = @env.ui.ask(I18n.t("cloud_command.continue"))
return 1 if continue.downcase != "y"
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
delete_provider(org, box_name, provider_name, version, @client.token, options)
end
def delete_provider(org, box_name, provider_name, version, access_token, options)
org = options[:username] if options[:username]
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account?(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
cloud_version = VagrantCloud::Version.new(box, version, nil, nil, access_token)
provider = VagrantCloud::Provider.new(cloud_version, provider_name, nil, nil, nil, nil, access_token)
begin
success = provider.delete
@env.ui.error(I18n.t("cloud_command.provider.delete_success", provider: provider_name, org: org, box_name: box_name, version: version))
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.provider.delete_fail", provider: provider_name, org: org, box_name: box_name, version: version))
@env.ui.error(e)
return 1
end
end
end
end
end
end
end

View File

@ -0,0 +1,19 @@
require "vagrant"
module VagrantPlugins
module CloudCommand
module ProviderCommand
class Plugin < Vagrant.plugin("2")
name "vagrant cloud box"
description <<-DESC
Provider CRUD commands for Vagrant Cloud
DESC
command(:provider) do
require_relative "root"
Command::Root
end
end
end
end
end

View File

@ -0,0 +1,77 @@
module VagrantPlugins
module CloudCommand
module ProviderCommand
module Command
class Root < Vagrant.plugin("2", :command)
def self.synopsis
"Provider commands"
end
def initialize(argv, env)
super
@main_args, @sub_command, @sub_args = split_main_and_subcommand(argv)
@subcommands = Vagrant::Registry.new
@subcommands.register(:create) do
require File.expand_path("../create", __FILE__)
Command::Create
end
@subcommands.register(:delete) do
require File.expand_path("../delete", __FILE__)
Command::Delete
end
@subcommands.register(:update) do
require File.expand_path("../update", __FILE__)
Command::Update
end
@subcommands.register(:upload) do
require File.expand_path("../upload", __FILE__)
Command::Upload
end
end
def execute
if @main_args.include?("-h") || @main_args.include?("--help")
# Print the help for all the provider commands.
return help
end
# If we reached this far then we must have a subcommand. If not,
# then we also just print the help and exit.
command_class = @subcommands.get(@sub_command.to_sym) if @sub_command
return help if !command_class || !@sub_command
@logger.debug("Invoking command class: #{command_class} #{@sub_args.inspect}")
# Initialize and execute the command class
command_class.new(@sub_args, @env).execute
end
# Prints the help out for this command
def help
opts = OptionParser.new do |opts|
opts.banner = "Usage: vagrant cloud provider <subcommand> [<args>]"
opts.separator ""
opts.separator "For various provider actions with Vagrant Cloud"
opts.separator ""
opts.separator "Available subcommands:"
# Add the available subcommands as separators in order to print them
# out as well.
keys = []
@subcommands.each { |key, value| keys << key.to_s }
keys.sort.each do |key|
opts.separator " #{key}"
end
opts.separator ""
opts.separator "For help on any individual subcommand run `vagrant cloud provider <subcommand> -h`"
end
@env.ui.info(opts.help, prefix: false)
end
end
end
end
end
end

View File

@ -0,0 +1,72 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module ProviderCommand
module Command
class Update < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud provider update [options] organization/box-name provider-name version url"
o.separator ""
o.separator "Updates a provider entry on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |t|
options[:username] = u
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 4
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
box = argv.first.split('/')
org = box[0]
box_name = box[1]
provider_name = argv[1]
version = argv[2]
url = argv[3]
update_provider(org, box_name, provider_name, version, url, @client.token, options)
end
def update_provider(org, box_name, provider_name, version, url, access_token, options)
if !url
@env.ui.warn(I18n.t("cloud_command.upload.no_url"))
end
org = options[:username] if options[:username]
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account?(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
cloud_version = VagrantCloud::Version.new(box, version, nil, nil, access_token)
provider = VagrantCloud::Provider.new(cloud_version, provider_name, nil, url, org, box_name, access_token)
begin
success = provider.update
@env.ui.success(I18n.t("cloud_command.provider.update_success", provider:provider_name, org: org, box_name: box_name, version: version))
VagrantPlugins::CloudCommand::Util.format_box_results(success.compact, @env)
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.provider.update_fail", provider:provider_name, org: org, box_name: box_name, version: version))
@env.ui.error(e)
return 1
end
end
end
end
end
end
end

View File

@ -0,0 +1,68 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module ProviderCommand
module Command
class Upload < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud provider upload [options] organization/box-name provider-name version box-file"
o.separator ""
o.separator "Uploads a box file to Vagrant Cloud for a specific provider"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |t|
options[:username] = u
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 4
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
box = argv.first.split('/')
org = box[0]
box_name = box[1]
provider_name = argv[1]
version = argv[2]
file = argv[3]
upload_provider(org, box_name, provider_name, version, file, @client.token, options)
end
def upload_provider(org, box_name, provider_name, version, file, access_token, options)
org = options[:username] if options[:username]
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account?(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
cloud_version = VagrantCloud::Version.new(box, version, nil, nil, access_token)
provider = VagrantCloud::Provider.new(cloud_version, provider_name, nil, nil, org, box_name, access_token)
begin
@env.ui.info(I18n.t("cloud_command.provider.upload", provider_file: file))
success = provider.upload_file(file)
@env.ui.success(I18n.t("cloud_command.provider.upload_success", provider: provider_name, org: org, box_name: box_name, version: version))
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.provider.upload_fail", provider: provider_name, org: org, box_name: box_name, version: version))
@env.ui.error(e)
return 1
end
end
end
end
end
end
end

View File

@ -0,0 +1,119 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module Command
class Publish < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud publish [options] organization/box-name version provider-name [provider-file]"
o.separator ""
o.separator "A Start-To-Finish command for creating and releasing 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", String, "Valid remote URL to download this provider") do |u|
options[:url] = u
end
o.on("-d", "--description DESCRIPTION", String, "Longer 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", "--force", "Disables confirmation to create or update box") do |f|
options[:force] = f
end
o.on("-p", "--private", "Makes box private") do |p|
options[:private] = p
end
o.on("-r", "--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("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |t|
options[:username] = u
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 4 || argv.length < 4
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
box = argv.first.split('/')
org = box[0]
box_name = box[1]
version = argv[1]
provider_name = argv[2]
box_file = argv[3]
publish_box(org, box_name, version, provider_name, box_file, options, @client.token)
end
def publish_box(org, box_name, version, provider_name, box_file, options, access_token)
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
@env.ui.warn("You are about to create a box on Vagrant Cloud with the following options:\n")
box_opts = " #{org}/#{box_name} (#{version}) for #{provider_name}\n"
box_opts << " Private: true\n" if options[:private]
box_opts << " Automatic Release: true\n" if options[:release]
box_opts << " Remote Box file: true\n" if options[:url]
box_opts << " Box Description: #{options[:description]}\n" if options[:description]
box_opts << " Box Short Description: #{options[:short_description]}\n" if options[:short_description]
box_opts << " Version Description: #{options[:version_description]}\n" if options[:version_description]
@env.ui.info(box_opts)
if !options[:force]
continue = @env.ui.ask(I18n.t("cloud_command.continue"))
return 1 if continue.downcase != "y"
end
account = VagrantPlugins::CloudCommand::Util.account?(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, options[:short_description], options[:description], access_token)
cloud_version = VagrantCloud::Version.new(box, version, nil, options[:version_description], access_token)
provider = VagrantCloud::Provider.new(cloud_version, provider_name, nil, options[:url], org, box_name, access_token)
begin
@env.ui.info(I18n.t("cloud_command.publish.box_create"))
box.create
@env.ui.info(I18n.t("cloud_command.publish.version_create"))
cloud_version.create_version
@env.ui.info(I18n.t("cloud_command.publish.provider_create"))
provider.create_provider
if !options[:url]
@env.ui.info(I18n.t("cloud_command.publish.upload_provider", file: box_file))
provider.upload_file(box_file)
end
if options[:release]
@env.ui.info(I18n.t("cloud_command.publish.release"))
cloud_version.release
end
@env.ui.success(I18n.t("cloud_command.publish.complete", org: org, box_name: box_name))
success = box.read(org, box_name)
VagrantPlugins::CloudCommand::Util.format_box_results(success.compact, @env)
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.publish.fail", org: org, box_name: box_name))
@env.ui.error(e)
return 1
end
return 1
end
end
end
end
end

View File

@ -0,0 +1,104 @@
module VagrantPlugins
module CloudCommand
module Command
class Root < Vagrant.plugin("2", :command)
def self.synopsis
"manages everything related to Vagrant Cloud"
end
def initialize(argv, env)
super
@main_args, @sub_command, @sub_args = split_main_and_subcommand(argv)
@subcommands = Vagrant::Registry.new
@subcommand_helptext = {}
@subcommands.register(:auth) do
require File.expand_path("../auth/root", __FILE__)
AuthCommand::Command::Root
end
@subcommand_helptext[:auth] = "For various authorization operations on Vagrant Cloud"
@subcommands.register(:box) do
require File.expand_path("../box/root", __FILE__)
BoxCommand::Command::Root
end
@subcommand_helptext[:box] = "For managing a Vagrant box entry on Vagrant Cloud"
# TODO: Uncomment this when API endpoint exists
#@subcommands.register(:list) do
# require File.expand_path("../list", __FILE__)
# List
#end
#@subcommand_helptext[:list] = "Displays a list of Vagrant boxes that the current user manages"
@subcommands.register(:search) do
require File.expand_path("../search", __FILE__)
Search
end
@subcommand_helptext[:search] = "Search Vagrant Cloud for available boxes"
@subcommands.register(:provider) do
require File.expand_path("../provider/root", __FILE__)
ProviderCommand::Command::Root
end
@subcommand_helptext[:provider] = "For managing a Vagrant box's provider options"
@subcommands.register(:publish) do
require File.expand_path("../publish", __FILE__)
Publish
end
@subcommand_helptext[:publish] = "A start-to-finish solution for creating or updating a new box on Vagrant Cloud"
@subcommands.register(:version) do
require File.expand_path("../version/root", __FILE__)
VersionCommand::Command::Root
end
@subcommand_helptext[:version] = "For managing a Vagrant box's versions"
end
def execute
if @main_args.include?("-h") || @main_args.include?("--help")
# Print the help for all the box commands.
return help
end
# If we reached this far then we must have a subcommand. If not,
# then we also just print the help and exit.
command_class = @subcommands.get(@sub_command.to_sym) if @sub_command
return help if !command_class || !@sub_command
@logger.debug("Invoking command class: #{command_class} #{@sub_args.inspect}")
# Initialize and execute the command class
command_class.new(@sub_args, @env).execute
end
# Prints the help out for this command
def help
opts = OptionParser.new do |opts|
opts.banner = "Usage: vagrant cloud <subcommand> [<args>]"
opts.separator ""
opts.separator "The cloud command can be used for taking actions against"
opts.separator "Vagrant Cloud like searching or uploading a Vagrant Box"
opts.separator ""
opts.separator "Available subcommands:"
# Add the available subcommands as separators in order to print them
# out as well.
keys = []
@subcommands.each { |key, value| keys << key.to_s }
keys.sort.each do |key|
opts.separator " #{key.ljust(15)} #{@subcommand_helptext[key.to_sym]}"
end
opts.separator ""
opts.separator "For help on any individual subcommand run `vagrant cloud <subcommand> -h`"
end
@env.ui.info(opts.help, prefix: false)
end
end
end
end
end

View File

@ -0,0 +1,82 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module Command
class Search < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud search [options] query"
o.separator ""
o.separator "Search for a box on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-j", "--json", "Formats results in JSON") do |j|
options[:json] = j
end
o.on("-p", "--page PAGE", Integer, "The page to display Default: 1") do |j|
options[:page] = j
end
o.on("-s", "--short", "Shows a simple list of box names") do |s|
options[:short] = s
end
o.on("-o", "--order ORDER", String, "Order to display results ('desc' or 'asc') Default: 'desc'") do |o|
options[:order] = o
end
o.on("-l", "--limit LIMIT", Integer, "Max number of search results Default: 25") do |l|
options[:limit] = l
end
o.on("-p", "--provider PROVIDER", String, "Filter search results to a single provider. Defaults to all.") do |p|
options[:provider] = p
end
o.on("--sort-by SORT", "Field to sort results on (created, downloads, updated) Default: downloads") do |s|
options[:sort] = s
end
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |t|
options[:username] = u
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.length > 2
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
query = argv.first
options[:limit] = 25 if !options[:limit]
search(query, options, @client.token)
end
def search(query, options, access_token)
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
search = VagrantCloud::Search.new(access_token, server_url)
begin
search_results = search.search(query, options[:provider], options[:sort], options[:order], options[:limit], options[:page])
if !search_results["boxes"].empty?
VagrantPlugins::CloudCommand::Util.format_search_results(search_results["boxes"], options[:short], options[:json], @env)
else
@env.ui.warn(I18n.t("cloud_command.search.no_results", query: query))
end
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.search.fail"))
@env.ui.error(e)
return 1
end
return 1
end
end
end
end
end

View File

@ -0,0 +1,199 @@
module VagrantPlugins
module CloudCommand
class Util
class << self
def account?(username, access_token, vagrant_cloud_server)
if !defined?(@_account)
@_account = VagrantCloud::Account.new(username, access_token, vagrant_cloud_server)
end
@_account
end
def api_server_url
if Vagrant.server_url == Vagrant::DEFAULT_SERVER_URL
return "#{Vagrant.server_url}/api/v1"
else
return Vagrant.server_url
end
end
def client_login(env, options)
if !defined?(@_client)
@_client = Client.new(env)
return @_client if @_client.logged_in?
# Let the user know what is going on.
env.ui.output(I18n.t("cloud_command.command_header") + "\n")
# If it is a private cloud installation, show that
if Vagrant.server_url != Vagrant::DEFAULT_SERVER_URL
env.ui.output("Vagrant Cloud URL: #{Vagrant.server_url}")
end
# Ask for the username
if @_client.username_or_email
env.ui.output("Vagrant Cloud username or email: #{@_client.username_or_email}")
end
until @_client.username_or_email
@_client.username_or_email = env.ui.ask("Vagrant Cloud username or email: ")
end
until @_client.password
@_client.password = env.ui.ask("Password (will be hidden): ", echo: false)
end
if options
description = options[:description]
end
description_default = "Vagrant login from #{Socket.gethostname}"
until description
description =
env.ui.ask("Token description (Defaults to #{description_default.inspect}): ")
end
description = description_default if description.empty?
code = nil
begin
token = @_client.login(description: description, code: code)
rescue Errors::TwoFactorRequired
until code
code = env.ui.ask("2FA code: ")
if @_client.two_factor_delivery_methods.include?(code.downcase)
delivery_method, code = code, nil
@_client.request_code delivery_method
end
end
retry
end
@_client.store_token(token)
env.ui.success(I18n.t("cloud_command.logged_in"))
@_client
end
@_client
end
# ===================================================
# Modified from https://stackoverflow.com/a/28685559
# for printing arrays of hashes in formatted tables
# ===================================================
# @param [Vagrant::Environment] - env
# @param [Hash] - column_labels - A hash of key values for table labels (i.e. {:col1=>"COL1", :col2=>"COL2"})
# @param [Array] - results - An array of hashes
# @param [Array] - to_jrust_keys - An array of column keys that should be right justified (default is left justified for all columns)
def print_search_table(env, column_labels, results, to_rjust_keys)
columns = column_labels.each_with_object({}) { |(col,label),h|
h[col] = { label: label,
width: [results.map { |g| g[col].size }.max, label.size].max
}}
write_header(env, columns)
write_divider(env, columns)
results.each { |h| write_line(env, columns, h,to_rjust_keys) }
write_divider(env, columns)
end
def write_header(env, columns)
env.ui.info "| #{ columns.map { |_,g| g[:label].ljust(g[:width]) }.join(' | ') } |"
end
def write_divider(env, columns)
env.ui.info "+-#{ columns.map { |_,g| "-"*g[:width] }.join("-+-") }-+"
end
def write_line(env, columns,h,to_rjust_keys)
str = h.keys.map { |k|
if to_rjust_keys.include?(k)
h[k].rjust(columns[k][:width])
else
h[k].ljust(columns[k][:width])
end
}.join(" | ")
env.ui.info "| #{str} |"
end
# ===================================================
# ===================================================
# Takes a "mostly" flat key=>value hash from Vagrant Cloud
# and prints its results in a list
#
# @param [Hash] - results - A response hash from vagrant cloud
# @param [Vagrant::Environment] - env
def format_box_results(results, env)
# TODO: remove other description fields? Maybe leave "short"?
results.delete("description_html")
if results["current_version"]
versions = results.delete("versions")
results["providers"] = results["current_version"]["providers"]
results["old_versions"] = versions.map{ |v| v["version"] }[1..5].join(", ") + "..."
end
width = results.keys.map{|k| k.size}.max
results.each do |k,v|
if k == "versions"
v = v.map{ |ver| ver["version"] }.join(", ")
elsif k == "current_version"
v = v["version"]
elsif k == "providers"
v = v.map{ |p| p["name"] }.join(", ")
elsif k == "downloads"
v = format_downloads(v.to_s)
end
whitespace = width-k.size
env.ui.info "#{k}:" + "".ljust(whitespace) + " #{v}"
end
end
# Converts a string of numbers into a formatted number
#
# 1234 -> 1,234
#
# @param [String] - download_string
def format_downloads(download_string)
return download_string.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
end
# @param [Array] search_results - Box search results from Vagrant Cloud
# @param [String,nil] short - determines if short version will be printed
# @param [String,nil] json - determines if json version will be printed
# @param [Vagrant::Environment] - env
def format_search_results(search_results, short, json, env)
result = []
search_results.each do |b|
box = {}
box = {
name: b["tag"],
version: b["current_version"]["version"],
downloads: format_downloads(b["downloads"].to_s),
providers: b["current_version"]["providers"].map{ |p| p["name"] }.join(",")
}
result << box
end
if short
result.map {|b| env.ui.info(b[:name])}
elsif json
env.ui.info(result.to_json)
else
column_labels = {}
columns = result.first.keys
columns.each do |c|
column_labels[c] = c.to_s.upcase
end
print_search_table(env, column_labels, result, [:downloads])
end
end
end
end
end
end

View File

@ -0,0 +1,68 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module VersionCommand
module Command
class Create < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud version create [options] organization/box-name version"
o.separator ""
o.separator "Creates a version entry on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-d", "--description DESCRIPTION", String, "A description for this version") do |d|
options[:description] = d
end
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |t|
options[:username] = u
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 2
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
box = argv.first.split('/')
org = box[0]
box_name = box[1]
version = argv[1]
create_version(org, box_name, version, @client.token, options)
end
def create_version(org, box_name, box_version, access_token, options)
org = options[:username] if options[:username]
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account?(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
version = VagrantCloud::Version.new(box, box_version, nil, options[:description], access_token)
begin
success = version.create_version
@env.ui.success(I18n.t("cloud_command.version.create_success", version: box_version, org: org, box_name: box_name))
VagrantPlugins::CloudCommand::Util.format_box_results(success.compact, @env)
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.version.create_fail", version: box_version, org: org, box_name: box_name))
@env.ui.error(e)
return 1
end
return 1
end
end
end
end
end
end

View File

@ -0,0 +1,68 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module VersionCommand
module Command
class Delete < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud version delete [options] organization/box-name version"
o.separator ""
o.separator "Deletes a version entry on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |t|
options[:username] = u
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 2
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
box = argv.first.split('/')
org = box[0]
box_name = box[1]
version = argv[1]
@env.ui.warn(I18n.t("cloud_command.version.delete_warn", version: version, box: argv.first))
continue = @env.ui.ask(I18n.t("cloud_command.continue"))
return 1 if continue.downcase != "y"
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
delete_version(org, box_name, version, options, @client.token)
end
def delete_version(org, box_name, box_version, options, access_token)
org = options[:username] if options[:username]
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account?(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
version = VagrantCloud::Version.new(box, box_version, nil, nil, access_token)
begin
success = version.delete
@env.ui.success(I18n.t("cloud_command.version.delete_success", version: box_version, org: org, box_name: box_name))
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.version.delete_fail", version: box_version, org: org, box_name: box_name))
@env.ui.error(e)
return 1
end
return 1
end
end
end
end
end
end

View File

@ -0,0 +1,19 @@
require "vagrant"
module VagrantPlugins
module CloudCommand
module VersionCommand
class Plugin < Vagrant.plugin("2")
name "vagrant cloud version"
description <<-DESC
Version CRUD commands for Vagrant Cloud
DESC
command(:version) do
require_relative "root"
Command::Root
end
end
end
end
end

View File

@ -0,0 +1,68 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module VersionCommand
module Command
class Release < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud version release [options] organization/box-name version"
o.separator ""
o.separator "Releases a version entry on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |t|
options[:username] = u
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 2
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@env.ui.warn(I18n.t("cloud_command.version.release_warn", version: argv[1], box: argv.first))
continue = @env.ui.ask(I18n.t("cloud_command.continue"))
return 1 if continue.downcase != "y"
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
box = argv.first.split('/')
org = box[0]
box_name = box[1]
version = argv[1]
release_version(org, box_name, version, @client.token, options)
end
def release_version(org, box_name, version, access_token, options)
org = options[:username] if options[:username]
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account?(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
version = VagrantCloud::Version.new(box, version, nil, nil, access_token)
begin
success = version.release
@env.ui.success(I18n.t("cloud_command.version.release_success", version: version, org: org, box_name: box_name))
VagrantPlugins::CloudCommand::Util.format_box_results(success.compact, @env)
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.version.release_fail", version: version, org: org, box_name: box_name))
@env.ui.error(e)
return 1
end
return 1
end
end
end
end
end
end

View File

@ -0,0 +1,68 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module VersionCommand
module Command
class Revoke < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud version revoke [options] organization/box-name version"
o.separator ""
o.separator "Revokes a version entry on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |t|
options[:username] = u
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 2
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@env.ui.warn(I18n.t("cloud_command.version.revoke_warn", version: argv[1], box: argv.first))
continue = @env.ui.ask(I18n.t("cloud_command.continue"))
return 1 if continue.downcase != "y"
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
box = argv.first.split('/')
org = box[0]
box_name = box[1]
version = argv[1]
revoke_version(org, box_name, version, @client.token, options)
end
def revoke_version(org, box_name, box_version, access_token, options)
org = options[:username] if options[:username]
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account?(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
version = VagrantCloud::Version.new(box, box_version, nil, nil, access_token)
begin
success = version.revoke
@env.ui.success(I18n.t("cloud_command.version.revoke_success", version: box_version, org: org, box_name: box_name))
VagrantPlugins::CloudCommand::Util.format_box_results(success.compact, @env)
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.version.revoke_fail", version: box_version, org: org, box_name: box_name))
@env.ui.error(e)
return 1
end
return 1
end
end
end
end
end
end

View File

@ -0,0 +1,81 @@
module VagrantPlugins
module CloudCommand
module VersionCommand
module Command
class Root < Vagrant.plugin("2", :command)
def self.synopsis
"Version commands"
end
def initialize(argv, env)
super
@main_args, @sub_command, @sub_args = split_main_and_subcommand(argv)
@subcommands = Vagrant::Registry.new
@subcommands.register(:create) do
require File.expand_path("../create", __FILE__)
Command::Create
end
@subcommands.register(:delete) do
require File.expand_path("../delete", __FILE__)
Command::Delete
end
@subcommands.register(:revoke) do
require File.expand_path("../revoke", __FILE__)
Command::Revoke
end
@subcommands.register(:release) do
require File.expand_path("../release", __FILE__)
Command::Release
end
@subcommands.register(:update) do
require File.expand_path("../update", __FILE__)
Command::Update
end
end
def execute
if @main_args.include?("-h") || @main_args.include?("--help")
# Print the help for all the version commands.
return help
end
# If we reached this far then we must have a subcommand. If not,
# then we also just print the help and exit.
command_class = @subcommands.get(@sub_command.to_sym) if @sub_command
return help if !command_class || !@sub_command
@logger.debug("Invoking command class: #{command_class} #{@sub_args.inspect}")
# Initialize and execute the command class
command_class.new(@sub_args, @env).execute
end
# Prints the help out for this command
def help
opts = OptionParser.new do |opts|
opts.banner = "Usage: vagrant cloud version <subcommand> [<args>]"
opts.separator ""
opts.separator "For taking various actions against a Vagrant boxes version attribute on Vagrant Cloud"
opts.separator ""
opts.separator "Available subcommands:"
# Add the available subcommands as separators in order to print them
# out as well.
keys = []
@subcommands.each { |key, value| keys << key.to_s }
keys.sort.each do |key|
opts.separator " #{key}"
end
opts.separator ""
opts.separator "For help on any individual subcommand run `vagrant cloud version <subcommand> -h`"
end
@env.ui.info(opts.help, prefix: false)
end
end
end
end
end
end

View File

@ -0,0 +1,68 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module VersionCommand
module Command
class Update < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud version update [options] organization/box-name version"
o.separator ""
o.separator "Updates a version entry on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-d", "--description DESCRIPTION", "A description for this version") do |d|
options[:description] = d
end
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |t|
options[:username] = u
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 2
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
box = argv.first.split('/')
org = box[0]
box_name = box[1]
version = argv[1]
update_version(org, box_name, version, @client.token, options)
end
def update_version(org, box_name, box_version, access_token, options)
org = options[:username] if options[:username]
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account?(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
version = VagrantCloud::Version.new(box, box_version, nil, options[:description], access_token)
begin
success = version.update
@env.ui.success(I18n.t("cloud_command.version.update_success", version: box_version, org: org, box_name: box_name))
VagrantPlugins::CloudCommand::Util.format_box_results(success.compact, @env)
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.version.update_fail", version: box_version, org: org, box_name: box_name))
@env.ui.error(e)
return 1
end
return 1
end
end
end
end
end
end

View File

@ -0,0 +1,103 @@
require File.expand_path("../../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/auth/login")
describe VagrantPlugins::CloudCommand::AuthCommand::Command::Login do
include_context "unit"
let(:argv) { [] }
let(:env) { isolated_environment.create_vagrant_env }
let(:token_path) { env.data_dir.join("vagrant_login_token") }
let(:stdout) { StringIO.new }
let(:stderr) { StringIO.new }
subject { described_class.new(argv, env) }
before do
stub_env("ATLAS_TOKEN" => "")
end
let(:action_runner) { double("action_runner") }
before do
allow(env).to receive(:action_runner).and_return(action_runner)
end
describe "#execute" do
context "with no args" do
let(:argv) { [] }
end
context "with --check" do
let(:argv) { ["--check"] }
context "when there is a token" do
before do
stub_request(:get, %r{^#{Vagrant.server_url}/api/v1/authenticate})
.to_return(status: 200)
end
before do
File.open(token_path, "w+") { |f| f.write("abcd1234") }
end
it "returns 0" do
expect(subject.execute).to eq(0)
end
end
context "when there is no token" do
it "returns 1" do
expect(subject.execute).to eq(1)
end
end
end
context "with --logout" do
let(:argv) { ["--logout"] }
it "returns 0" do
expect(subject.execute).to eq(0)
end
it "clears the token" do
subject.execute
expect(File.exist?(token_path)).to be(false)
end
end
context "with --token" do
let(:argv) { ["--token", "efgh5678"] }
context "when the token is valid" do
before do
stub_request(:get, %r{^#{Vagrant.server_url}/api/v1/authenticate})
.to_return(status: 200)
end
it "sets the token" do
subject.execute
token = File.read(token_path).strip
expect(token).to eq("efgh5678")
end
it "returns 0" do
expect(subject.execute).to eq(0)
end
end
context "when the token is invalid" do
before do
stub_request(:get, %r{^#{Vagrant.server_url}/api/v1/authenticate})
.to_return(status: 401)
end
it "returns 1" do
expect(subject.execute).to eq(1)
end
end
end
end
end

View File

@ -0,0 +1,43 @@
require File.expand_path("../../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/auth/logout")
describe VagrantPlugins::CloudCommand::AuthCommand::Command::Logout do
include_context "unit"
let(:argv) { [] }
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
env = isolated_environment
env.vagrantfile("")
env.create_vagrant_env
end
subject { described_class.new(argv, iso_env) }
let(:action_runner) { double("action_runner") }
let(:client) { double("client", token: "1234token1234") }
before do
allow(iso_env).to receive(:action_runner).and_return(action_runner)
allow(VagrantPlugins::CloudCommand::Util).to receive(:client_login).
and_return(client)
end
context "with any arguments" do
let (:argv) { ["stuff", "things"] }
it "shows the help" do
expect { subject.execute }.
to raise_error(Vagrant::Errors::CLIInvalidUsage)
end
end
context "with no arguments" do
it "logs you out" do
expect(client).to receive(:clear_token)
expect(subject.execute).to eq(0)
end
end
end

View File

@ -0,0 +1,54 @@
require File.expand_path("../../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/auth/whoami")
describe VagrantPlugins::CloudCommand::AuthCommand::Command::Whoami do
include_context "unit"
let(:argv) { [] }
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
env = isolated_environment
env.vagrantfile("")
env.create_vagrant_env
end
subject { described_class.new(argv, iso_env) }
let(:action_runner) { double("action_runner") }
let(:client) { double("client", token: "1234token1234") }
let(:account) { double("account") }
before do
allow(iso_env).to receive(:action_runner).and_return(action_runner)
allow(VagrantPlugins::CloudCommand::Util).to receive(:client_login).
and_return(client)
allow(VagrantPlugins::CloudCommand::Util).to receive(:account?).
and_return(account)
end
context "with too many arguments" do
let(:argv) { ["token", "token", "token"] }
it "shows help" do
expect { subject.execute }.
to raise_error(Vagrant::Errors::CLIInvalidUsage)
end
end
context "with username" do
let(:argv) { ["token"] }
let(:org_hash) { {"user"=>{"username"=>"mario"}, "boxes"=>[{"name"=>"box"}]} }
it "gets information about a user" do
expect(account).to receive(:validate_token).and_return(org_hash)
expect(subject.execute).to eq(0)
end
it "returns 1 if encountering an error making request" do
allow(account).to receive(:validate_token).
and_raise(VagrantCloud::ClientError.new("Fail Message", "Message"))
expect(subject.execute).to eq(1)
end
end
end

View File

@ -0,0 +1,61 @@
require File.expand_path("../../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/box/create")
describe VagrantPlugins::CloudCommand::BoxCommand::Command::Create do
include_context "unit"
let(:argv) { [] }
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
env = isolated_environment
env.vagrantfile("")
env.create_vagrant_env
end
subject { described_class.new(argv, iso_env) }
let(:action_runner) { double("action_runner") }
let(:client) { double("client", token: "1234token1234") }
let(:box) { double("box") }
before do
allow(iso_env).to receive(:action_runner).and_return(action_runner)
allow(VagrantPlugins::CloudCommand::Util).to receive(:client_login).
and_return(client)
allow(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results).
and_return(true)
end
context "with no arguments" do
it "shows help" do
expect { subject.execute }.
to raise_error(Vagrant::Errors::CLIInvalidUsage)
end
end
context "with arguments" do
let (:argv) { ["vagrant/box-name", "-s", "short", "-d", "long"] }
it "creates a box" do
allow(VagrantCloud::Box).to receive(:new)
.with(anything, "box-name", nil, "short", "long", client.token)
.and_return(box)
expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results)
expect(box).to receive(:create).and_return({})
expect(subject.execute).to eq(0)
end
it "displays an error if encoutering a problem with the request" do
allow(VagrantCloud::Box).to receive(:new)
.with(anything, "box-name", nil, "short", "long", client.token)
.and_return(box)
allow(box).to receive(:create).
and_raise(VagrantCloud::ClientError.new("Fail Message", "Message"))
expect(subject.execute).to eq(1)
end
end
end

View File

@ -0,0 +1,62 @@
require File.expand_path("../../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/box/delete")
describe VagrantPlugins::CloudCommand::BoxCommand::Command::Delete do
include_context "unit"
let(:argv) { [] }
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
env = isolated_environment
env.vagrantfile("")
env.create_vagrant_env
end
subject { described_class.new(argv, iso_env) }
let(:action_runner) { double("action_runner") }
let(:client) { double("client", token: "1234token1234") }
let(:box) { double("box") }
before do
allow(iso_env).to receive(:action_runner).and_return(action_runner)
allow(VagrantPlugins::CloudCommand::Util).to receive(:client_login).
and_return(client)
allow(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results).
and_return(true)
allow(iso_env.ui).to receive(:ask).
and_return("y")
end
context "with no arguments" do
it "shows help" do
expect { subject.execute }.
to raise_error(Vagrant::Errors::CLIInvalidUsage)
end
end
context "with arguments" do
let (:argv) { ["vagrant/box-name"] }
it "creates a box" do
allow(VagrantCloud::Box).to receive(:new)
.with(anything, "box-name", nil, nil, nil, client.token)
.and_return(box)
expect(box).to receive(:delete).and_return({})
expect(subject.execute).to eq(0)
end
it "displays an error if encoutering a problem with the request" do
allow(VagrantCloud::Box).to receive(:new)
.with(anything, "box-name", nil, nil, nil, client.token)
.and_return(box)
allow(box).to receive(:delete).
and_raise(VagrantCloud::ClientError.new("Fail Message", "Message"))
expect(subject.execute).to eq(1)
end
end
end

View File

@ -0,0 +1,63 @@
require File.expand_path("../../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/box/show")
describe VagrantPlugins::CloudCommand::BoxCommand::Command::Show do
include_context "unit"
let(:argv) { [] }
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
env = isolated_environment
env.vagrantfile("")
env.create_vagrant_env
end
subject { described_class.new(argv, iso_env) }
let(:action_runner) { double("action_runner") }
let(:client) { double("client", token: "1234token1234") }
let(:box) { double("box") }
before do
allow(iso_env).to receive(:action_runner).and_return(action_runner)
allow(VagrantPlugins::CloudCommand::Util).to receive(:client_login).
and_return(client)
allow(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results).
and_return(true)
allow(iso_env.ui).to receive(:ask).
and_return("y")
end
context "with no arguments" do
it "shows help" do
expect { subject.execute }.
to raise_error(Vagrant::Errors::CLIInvalidUsage)
end
end
context "with arguments" do
let (:argv) { ["vagrant/box-name"] }
it "creates a box" do
allow(VagrantCloud::Box).to receive(:new)
.with(anything, "box-name", nil, nil, nil, client.token)
.and_return(box)
expect(box).to receive(:read).and_return({})
expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results)
expect(subject.execute).to eq(0)
end
it "displays an error if encoutering a problem with the request" do
allow(VagrantCloud::Box).to receive(:new)
.with(anything, "box-name", nil, nil, nil, client.token)
.and_return(box)
allow(box).to receive(:read).
and_raise(VagrantCloud::ClientError.new("Fail Message", "Message"))
expect(subject.execute).to eq(1)
end
end
end

View File

@ -0,0 +1,64 @@
require File.expand_path("../../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/box/update")
describe VagrantPlugins::CloudCommand::BoxCommand::Command::Update do
include_context "unit"
let(:argv) { [] }
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
env = isolated_environment
env.vagrantfile("")
env.create_vagrant_env
end
subject { described_class.new(argv, iso_env) }
let(:action_runner) { double("action_runner") }
let(:client) { double("client", token: "1234token1234") }
let(:box) { double("box") }
before do
allow(iso_env).to receive(:action_runner).and_return(action_runner)
allow(VagrantPlugins::CloudCommand::Util).to receive(:client_login).
and_return(client)
allow(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results).
and_return(true)
end
context "with no arguments" do
it "shows help" do
expect { subject.execute }.
to raise_error(Vagrant::Errors::CLIInvalidUsage)
end
end
context "with arguments" do
let (:argv) { ["vagrant/box-name", "-d", "update", "-s", "short"] }
it "creates a box" do
allow(VagrantCloud::Box).to receive(:new)
.with(anything, "box-name", nil, nil, nil, client.token)
.and_return(box)
expect(box).to receive(:update).
with(organization: "vagrant", name: "box-name", description: "update", short_description: "short").
and_return({})
expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results)
expect(subject.execute).to eq(0)
end
it "displays an error if encoutering a problem with the request" do
allow(VagrantCloud::Box).to receive(:new)
.with(anything, "box-name", nil, nil, nil, client.token)
.and_return(box)
allow(box).to receive(:update).
with(organization: "vagrant", name: "box-name", description: "update", short_description: "short").
and_raise(VagrantCloud::ClientError.new("Fail Message", "Message"))
expect(subject.execute).to eq(1)
end
end
end

View File

@ -0,0 +1,261 @@
require File.expand_path("../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/client/client")
describe VagrantPlugins::CloudCommand::Client do
include_context "unit"
let(:env) { isolated_environment.create_vagrant_env }
subject(:client) { described_class.new(env) }
before(:all) do
I18n.load_path << Vagrant.source_root.join("plugins/commands/cloud/locales/en.yml")
I18n.reload!
end
before do
stub_env("ATLAS_TOKEN" => nil)
subject.clear_token
end
describe "#logged_in?" do
let(:url) { "#{Vagrant.server_url}/api/v1/authenticate?access_token=#{token}" }
let(:headers) { { "Content-Type" => "application/json" } }
before { allow(subject).to receive(:token).and_return(token) }
context "when there is no token" do
let(:token) { nil }
it "returns false" do
expect(subject.logged_in?).to be(false)
end
end
context "when there is a token" do
let(:token) { "ABCD1234" }
it "returns true if the endpoint returns a 200" do
stub_request(:get, url)
.with(headers: headers)
.to_return(body: JSON.pretty_generate("token" => token))
expect(subject.logged_in?).to be(true)
end
it "raises an error if the endpoint returns a non-200" do
stub_request(:get, url)
.with(headers: headers)
.to_return(body: JSON.pretty_generate("bad" => true), status: 401)
expect(subject.logged_in?).to be(false)
end
it "raises an exception if the server cannot be found" do
stub_request(:get, url)
.to_raise(SocketError)
expect { subject.logged_in? }
.to raise_error(VagrantPlugins::CloudCommand::Errors::ServerUnreachable)
end
end
end
describe "#login" do
let(:request) {
{
user: {
login: login,
password: password,
},
token: {
description: description,
},
two_factor: {
code: nil
}
}
}
let(:login) { "foo" }
let(:password) { "bar" }
let(:description) { "Token description" }
let(:headers) {
{
"Accept" => "application/json",
"Content-Type" => "application/json",
}
}
let(:response) {
{
token: "baz"
}
}
it "returns the access token after successful login" do
stub_request(:post, "#{Vagrant.server_url}/api/v1/authenticate").
with(body: JSON.dump(request), headers: headers).
to_return(status: 200, body: JSON.dump(response))
client.username_or_email = login
client.password = password
expect(client.login(description: "Token description")).to eq("baz")
end
context "when 2fa is required" do
let(:response) {
{
two_factor: {
default_delivery_method: default_delivery_method,
delivery_methods: delivery_methods
}
}
}
let(:default_delivery_method) { "app" }
let(:delivery_methods) { ["app"] }
before do
stub_request(:post, "#{Vagrant.server_url}/api/v1/authenticate").
to_return(status: 406, body: JSON.dump(response))
end
it "raises a two-factor required error" do
expect {
client.login
}.to raise_error(VagrantPlugins::CloudCommand::Errors::TwoFactorRequired)
end
context "when the default delivery method is not app" do
let(:default_delivery_method) { "sms" }
let(:delivery_methods) { ["app", "sms"] }
it "requests a code and then raises a two-factor required error" do
expect(client)
.to receive(:request_code)
.with(default_delivery_method)
expect {
client.login
}.to raise_error(VagrantPlugins::CloudCommand::Errors::TwoFactorRequired)
end
end
end
context "on bad login" do
before do
stub_request(:post, "#{Vagrant.server_url}/api/v1/authenticate").
to_return(status: 401, body: "")
end
it "raises an error" do
expect {
client.login
}.to raise_error(VagrantPlugins::CloudCommand::Errors::Unauthorized)
end
end
context "if it can't reach the server" do
before do
stub_request(:post, "#{Vagrant.server_url}/api/v1/authenticate").
to_raise(SocketError)
end
it "raises an exception" do
expect {
subject.login
}.to raise_error(VagrantPlugins::CloudCommand::Errors::ServerUnreachable)
end
end
end
describe "#request_code" do
let(:request) {
{
user: {
login: login,
password: password,
},
two_factor: {
delivery_method: delivery_method
}
}
}
let(:login) { "foo" }
let(:password) { "bar" }
let(:delivery_method) { "sms" }
let(:headers) {
{
"Accept" => "application/json",
"Content-Type" => "application/json"
}
}
let(:response) {
{
two_factor: {
obfuscated_destination: "SMS number ending in 1234"
}
}
}
it "displays that the code was sent" do
expect(env.ui)
.to receive(:success)
.with("2FA code sent to SMS number ending in 1234.")
stub_request(:post, "#{Vagrant.server_url}/api/v1/two-factor/request-code").
with(body: JSON.dump(request), headers: headers).
to_return(status: 201, body: JSON.dump(response))
client.username_or_email = login
client.password = password
client.request_code delivery_method
end
end
describe "#token" do
it "reads ATLAS_TOKEN" do
stub_env("ATLAS_TOKEN" => "ABCD1234")
expect(subject.token).to eq("ABCD1234")
end
it "reads the stored file" do
subject.store_token("EFGH5678")
expect(subject.token).to eq("EFGH5678")
end
it "prefers the environment variable" do
stub_env("VAGRANT_CLOUD_TOKEN" => "ABCD1234")
subject.store_token("EFGH5678")
expect(subject.token).to eq("ABCD1234")
end
it "prints a warning if the envvar and stored file are both present" do
stub_env("VAGRANT_CLOUD_TOKEN" => "ABCD1234")
subject.store_token("EFGH5678")
expect(env.ui).to receive(:warn).with(/detected both/)
subject.token
end
it "returns nil if there's no token set" do
expect(subject.token).to be(nil)
end
end
describe "#store_token, #clear_token" do
it "stores the token and can re-access it" do
subject.store_token("foo")
expect(subject.token).to eq("foo")
expect(described_class.new(env).token).to eq("foo")
end
it "deletes the token" do
subject.store_token("foo")
subject.clear_token
expect(subject.token).to be_nil
end
end
end

View File

@ -0,0 +1,24 @@
require File.expand_path("../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/list")
describe VagrantPlugins::CloudCommand::Command::List do
include_context "unit"
let(:argv) { [] }
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
env = isolated_environment
env.vagrantfile("")
env.create_vagrant_env
end
subject { described_class.new(argv, iso_env) }
let(:action_runner) { double("action_runner") }
before do
allow(iso_env).to receive(:action_runner).and_return(action_runner)
end
end

View File

@ -0,0 +1,85 @@
require File.expand_path("../../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/provider/create")
describe VagrantPlugins::CloudCommand::ProviderCommand::Command::Create do
include_context "unit"
let(:argv) { [] }
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
env = isolated_environment
env.vagrantfile("")
env.create_vagrant_env
end
subject { described_class.new(argv, iso_env) }
let(:action_runner) { double("action_runner") }
let(:client) { double("client", token: "1234token1234") }
let(:box) { double("box") }
let(:version) { double("version") }
let(:provider) { double("provider") }
before do
allow(iso_env).to receive(:action_runner).and_return(action_runner)
allow(VagrantPlugins::CloudCommand::Util).to receive(:client_login).
and_return(client)
allow(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results).
and_return(true)
allow(VagrantCloud::Box).to receive(:new)
.with(anything, "box-name", nil, nil, nil, client.token)
.and_return(box)
allow(VagrantCloud::Version).to receive(:new)
.with(box, "1.0.0", nil, nil, client.token)
.and_return(version)
end
context "with no arguments" do
it "shows help" do
expect { subject.execute }.
to raise_error(Vagrant::Errors::CLIInvalidUsage)
end
end
context "with arguments" do
let (:argv) { ["vagrant/box-name", "virtualbox", "1.0.0"] }
it "creates a provider" do
allow(VagrantCloud::Provider).to receive(:new).
with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token).
and_return(provider)
expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results)
expect(iso_env.ui).to receive(:warn)
expect(provider).to receive(:create_provider).and_return({})
expect(subject.execute).to eq(0)
end
it "displays an error if encoutering a problem with the request" do
allow(VagrantCloud::Provider).to receive(:new).
with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token).
and_return(provider)
allow(provider).to receive(:create_provider).
and_raise(VagrantCloud::ClientError.new("Fail Message", "Message"))
expect(subject.execute).to eq(1)
end
end
context "with arguments and a remote url" do
let (:argv) { ["vagrant/box-name", "virtualbox", "1.0.0", "https://box.com/box"] }
it "creates a provider" do
allow(VagrantCloud::Provider).to receive(:new).
with(version, "virtualbox", nil, "https://box.com/box", "vagrant", "box-name", client.token).
and_return(provider)
expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results)
expect(iso_env.ui).not_to receive(:warn)
expect(provider).to receive(:create_provider).and_return({})
expect(subject.execute).to eq(0)
end
end
end

View File

@ -0,0 +1,70 @@
require File.expand_path("../../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/provider/delete")
describe VagrantPlugins::CloudCommand::ProviderCommand::Command::Delete do
include_context "unit"
let(:argv) { [] }
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
env = isolated_environment
env.vagrantfile("")
env.create_vagrant_env
end
subject { described_class.new(argv, iso_env) }
let(:action_runner) { double("action_runner") }
let(:client) { double("client", token: "1234token1234") }
let(:box) { double("box") }
let(:version) { double("version") }
let(:provider) { double("provider") }
before do
allow(iso_env).to receive(:action_runner).and_return(action_runner)
allow(VagrantPlugins::CloudCommand::Util).to receive(:client_login).
and_return(client)
allow(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results).
and_return(true)
allow(VagrantCloud::Box).to receive(:new)
.with(anything, "box-name", nil, nil, nil, client.token)
.and_return(box)
allow(VagrantCloud::Version).to receive(:new)
.with(box, "1.0.0", nil, nil, client.token)
.and_return(version)
allow(iso_env.ui).to receive(:ask).
and_return("y")
end
context "with no arguments" do
it "shows help" do
expect { subject.execute }.
to raise_error(Vagrant::Errors::CLIInvalidUsage)
end
end
context "with arguments" do
let (:argv) { ["vagrant/box-name", "virtualbox", "1.0.0"] }
it "deletes a provider" do
allow(VagrantCloud::Provider).to receive(:new).
with(version, "virtualbox", nil, nil, nil, nil, client.token).
and_return(provider)
expect(provider).to receive(:delete).and_return({})
expect(subject.execute).to eq(0)
end
it "displays an error if encoutering a problem with the request" do
allow(VagrantCloud::Provider).to receive(:new).
with(version, "virtualbox", nil, nil, nil, nil, client.token).
and_return(provider)
allow(provider).to receive(:delete).
and_raise(VagrantCloud::ClientError.new("Fail Message", "Message"))
expect(subject.execute).to eq(1)
end
end
end

View File

@ -0,0 +1,85 @@
require File.expand_path("../../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/provider/update")
describe VagrantPlugins::CloudCommand::ProviderCommand::Command::Update do
include_context "unit"
let(:argv) { [] }
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
env = isolated_environment
env.vagrantfile("")
env.create_vagrant_env
end
subject { described_class.new(argv, iso_env) }
let(:action_runner) { double("action_runner") }
let(:client) { double("client", token: "1234token1234") }
let(:box) { double("box", create: true, read: {}) }
let(:version) { double("version", create_version: true, release: true) }
let(:provider) { double("provider", create_provider: true, upload_file: true) }
before do
allow(iso_env).to receive(:action_runner).and_return(action_runner)
allow(VagrantPlugins::CloudCommand::Util).to receive(:client_login).
and_return(client)
allow(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results).
and_return(true)
allow(VagrantCloud::Box).to receive(:new)
.with(anything, "box-name", nil, nil, nil, client.token)
.and_return(box)
allow(VagrantCloud::Version).to receive(:new)
.with(box, "1.0.0", nil, nil, client.token)
.and_return(version)
end
context "with no arguments" do
it "shows help" do
expect { subject.execute }.
to raise_error(Vagrant::Errors::CLIInvalidUsage)
end
end
context "with arguments" do
let (:argv) { ["vagrant/box-name", "virtualbox", "1.0.0"] }
it "updates a provider" do
allow(VagrantCloud::Provider).to receive(:new).
with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token).
and_return(provider)
expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results)
expect(iso_env.ui).to receive(:warn)
expect(provider).to receive(:update).and_return({})
expect(subject.execute).to eq(0)
end
it "displays an error if encoutering a problem with the request" do
allow(VagrantCloud::Provider).to receive(:new).
with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token).
and_return(provider)
allow(provider).to receive(:update).
and_raise(VagrantCloud::ClientError.new("Fail Message", "Message"))
expect(subject.execute).to eq(1)
end
end
context "with arguments and a remote url" do
let (:argv) { ["vagrant/box-name", "virtualbox", "1.0.0", "https://box.com/box"] }
it "creates a provider" do
allow(VagrantCloud::Provider).to receive(:new).
with(version, "virtualbox", nil, "https://box.com/box", "vagrant", "box-name", client.token).
and_return(provider)
expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results)
expect(iso_env.ui).not_to receive(:warn)
expect(provider).to receive(:update).and_return({})
expect(subject.execute).to eq(0)
end
end
end

View File

@ -0,0 +1,70 @@
require File.expand_path("../../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/provider/upload")
describe VagrantPlugins::CloudCommand::ProviderCommand::Command::Upload do
include_context "unit"
let(:argv) { [] }
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
env = isolated_environment
env.vagrantfile("")
env.create_vagrant_env
end
subject { described_class.new(argv, iso_env) }
let(:action_runner) { double("action_runner") }
let(:client) { double("client", token: "1234token1234") }
let(:box) { double("box") }
let(:version) { double("version") }
let(:provider) { double("provider") }
before do
allow(iso_env).to receive(:action_runner).and_return(action_runner)
allow(VagrantPlugins::CloudCommand::Util).to receive(:client_login).
and_return(client)
allow(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results).
and_return(true)
allow(VagrantCloud::Box).to receive(:new)
.with(anything, "box-name", nil, nil, nil, client.token)
.and_return(box)
allow(VagrantCloud::Version).to receive(:new)
.with(box, "1.0.0", nil, nil, client.token)
.and_return(version)
end
context "with no arguments" do
it "shows help" do
expect { subject.execute }.
to raise_error(Vagrant::Errors::CLIInvalidUsage)
end
end
context "with arguments" do
let (:argv) { ["vagrant/box-name", "virtualbox", "1.0.0", "path/to/box.box"] }
it "uploads a provider" do
allow(VagrantCloud::Provider).to receive(:new).
with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token).
and_return(provider)
expect(provider).to receive(:upload_file).
with("path/to/box.box").
and_return({})
expect(subject.execute).to eq(0)
end
it "displays an error if encoutering a problem with the request" do
allow(VagrantCloud::Provider).to receive(:new).
with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token).
and_return(provider)
allow(provider).to receive(:upload_file).
and_raise(VagrantCloud::ClientError.new("Fail Message", "Message"))
expect(subject.execute).to eq(1)
end
end
end

View File

@ -0,0 +1,80 @@
require File.expand_path("../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/publish")
describe VagrantPlugins::CloudCommand::Command::Publish do
include_context "unit"
let(:argv) { [] }
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
env = isolated_environment
env.vagrantfile("")
env.create_vagrant_env
end
let(:client) { double("client", token: "1234token1234") }
subject { described_class.new(argv, iso_env) }
let(:action_runner) { double("action_runner") }
let(:box) { double("box", create: true, read: {}) }
let(:version) { double("version", create_version: true, release: true) }
let(:provider) { double("provider", create_provider: true, upload_file: true) }
before do
allow(iso_env).to receive(:action_runner).and_return(action_runner)
allow(VagrantPlugins::CloudCommand::Util).to receive(:client_login).
and_return(client)
allow(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results).
and_return(true)
allow(iso_env.ui).to receive(:ask).
and_return("y")
allow(VagrantCloud::Box).to receive(:new).and_return(box)
allow(VagrantCloud::Version).to receive(:new).and_return(version)
allow(VagrantCloud::Provider).to receive(:new).and_return(provider)
end
context "with no arguments" do
it "shows help" do
expect { subject.execute }.
to raise_error(Vagrant::Errors::CLIInvalidUsage)
end
end
context "with arguments" do
let(:argv) { ["vagrant/box", "1.0.0", "virtualbox", "path/to/the/virtualbox.box"] }
it "publishes a box given options" do
expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results)
expect(subject.execute).to eq(0)
end
it "catches a ClientError if something goes wrong" do
allow(box).to receive(:create).
and_raise(VagrantCloud::ClientError.new("Fail Message", "Message"))
expect(subject.execute).to eq(1)
end
end
context "with arguments and releasing a box" do
let(:argv) { ["vagrant/box", "1.0.0", "virtualbox", "path/to/the/virtualbox.box", "--release"] }
it "releases the box" do
expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results)
expect(version).to receive(:release)
expect(subject.execute).to eq(0)
end
end
context "with arguments and a remote url" do
let(:argv) { ["vagrant/box", "1.0.0", "virtualbox", "--url", "https://www.boxes.com/path/to/the/virtualbox.box"] }
it "does not upload a file" do
expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results)
expect(subject.execute).to eq(0)
expect(provider).not_to receive(:upload_file)
end
end
end

View File

@ -0,0 +1,77 @@
require File.expand_path("../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/search")
describe VagrantPlugins::CloudCommand::Command::Search do
include_context "unit"
let(:argv) { [] }
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
env = isolated_environment
env.vagrantfile("")
env.create_vagrant_env
end
let(:client) { double("client", token: "1234token1234") }
subject { described_class.new(argv, iso_env) }
let(:action_runner) { double("action_runner") }
before do
allow(iso_env).to receive(:action_runner).and_return(action_runner)
allow(VagrantPlugins::CloudCommand::Util).to receive(:client_login).
and_return(client)
allow(VagrantPlugins::CloudCommand::Util).to receive(:format_search_results).
and_return(true)
end
context "with no arguments" do
let (:search) { double("search", search: {"boxes"=>["all of them"]}) }
it "makes a request to search all boxes and formats them" do
allow(VagrantCloud::Search).to receive(:new).
and_return(search)
expect(VagrantPlugins::CloudCommand::Util).to receive(:format_search_results)
expect(subject.execute).to eq(0)
end
end
context "with no arguments and an error occurs making requests" do
let (:search) { double("search") }
it "catches a ClientError if something goes wrong" do
allow(VagrantCloud::Search).to receive(:new).
and_return(search)
allow(search).to receive(:search).
and_raise(VagrantCloud::ClientError.new("Fail Message", "Message"))
expect(subject.execute).to eq(1)
end
end
context "with no arguments and no results" do
let (:search) { double("search", search: {"boxes"=>[]}) }
it "makes a request to search all boxes and formats them" do
allow(VagrantCloud::Search).to receive(:new).
and_return(search)
expect(VagrantPlugins::CloudCommand::Util).not_to receive(:format_search_results)
subject.execute
end
end
context "with arguments" do
let (:search) { double("search", search: {"boxes"=>["all of them"]}) }
let (:argv) { ["ubuntu", "--page", "1", "--order", "desc", "--limit", "100", "--provider", "provider", "--sort", "downloads"] }
it "sends the options to make a request with" do
allow(VagrantCloud::Search).to receive(:new).
and_return(search)
expect(search).to receive(:search).
with("ubuntu", "provider", "downloads", "desc", 100, 1)
subject.execute
end
end
end

View File

@ -0,0 +1,65 @@
require File.expand_path("../../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/version/create")
describe VagrantPlugins::CloudCommand::VersionCommand::Command::Create do
include_context "unit"
let(:argv) { [] }
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
env = isolated_environment
env.vagrantfile("")
env.create_vagrant_env
end
subject { described_class.new(argv, iso_env) }
let(:action_runner) { double("action_runner") }
let(:client) { double("client", token: "1234token1234") }
let(:box) { double("box") }
let(:version) { double("version") }
before do
allow(iso_env).to receive(:action_runner).and_return(action_runner)
allow(VagrantPlugins::CloudCommand::Util).to receive(:client_login).
and_return(client)
allow(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results).
and_return(true)
allow(VagrantCloud::Box).to receive(:new)
.with(anything, "box-name", nil, nil, nil, client.token)
.and_return(box)
end
context "with no arguments" do
it "shows help" do
expect { subject.execute }.
to raise_error(Vagrant::Errors::CLIInvalidUsage)
end
end
context "with arguments" do
let (:argv) { ["vagrant/box-name", "1.0.0", "-d", "description"] }
it "creates a version" do
allow(VagrantCloud::Version).to receive(:new).
with(box, "1.0.0", nil, "description", client.token).
and_return(version)
expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results)
expect(version).to receive(:create_version).and_return({})
expect(subject.execute).to eq(0)
end
it "displays an error if encoutering a problem with the request" do
allow(VagrantCloud::Version).to receive(:new).
with(box, "1.0.0", nil, "description", client.token).
and_return(version)
allow(version).to receive(:create_version).
and_raise(VagrantCloud::ClientError.new("Fail Message", "Message"))
expect(subject.execute).to eq(1)
end
end
end

View File

@ -0,0 +1,66 @@
require File.expand_path("../../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/version/delete")
describe VagrantPlugins::CloudCommand::VersionCommand::Command::Delete do
include_context "unit"
let(:argv) { [] }
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
env = isolated_environment
env.vagrantfile("")
env.create_vagrant_env
end
subject { described_class.new(argv, iso_env) }
let(:action_runner) { double("action_runner") }
let(:client) { double("client", token: "1234token1234") }
let(:box) { double("box") }
let(:version) { double("version") }
before do
allow(iso_env).to receive(:action_runner).and_return(action_runner)
allow(VagrantPlugins::CloudCommand::Util).to receive(:client_login).
and_return(client)
allow(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results).
and_return(true)
allow(VagrantCloud::Box).to receive(:new)
.with(anything, "box-name", nil, nil, nil, client.token)
.and_return(box)
allow(iso_env.ui).to receive(:ask).
and_return("y")
end
context "with no arguments" do
it "shows help" do
expect { subject.execute }.
to raise_error(Vagrant::Errors::CLIInvalidUsage)
end
end
context "with arguments" do
let (:argv) { ["vagrant/box-name", "1.0.0"] }
it "deletes a version" do
allow(VagrantCloud::Version).to receive(:new).
with(box, "1.0.0", nil, nil, client.token).
and_return(version)
expect(version).to receive(:delete).and_return({})
expect(subject.execute).to eq(0)
end
it "displays an error if encoutering a problem with the request" do
allow(VagrantCloud::Version).to receive(:new).
with(box, "1.0.0", nil, nil, client.token).
and_return(version)
allow(version).to receive(:delete).
and_raise(VagrantCloud::ClientError.new("Fail Message", "Message"))
expect(subject.execute).to eq(1)
end
end
end

View File

@ -0,0 +1,66 @@
require File.expand_path("../../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/version/release")
describe VagrantPlugins::CloudCommand::VersionCommand::Command::Release do
include_context "unit"
let(:argv) { [] }
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
env = isolated_environment
env.vagrantfile("")
env.create_vagrant_env
end
subject { described_class.new(argv, iso_env) }
let(:action_runner) { double("action_runner") }
let(:client) { double("client", token: "1234token1234") }
let(:box) { double("box") }
let(:version) { double("version") }
before do
allow(iso_env).to receive(:action_runner).and_return(action_runner)
allow(VagrantPlugins::CloudCommand::Util).to receive(:client_login).
and_return(client)
allow(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results).
and_return(true)
allow(VagrantCloud::Box).to receive(:new)
.with(anything, "box-name", nil, nil, nil, client.token)
.and_return(box)
allow(iso_env.ui).to receive(:ask).
and_return("y")
end
context "with no arguments" do
it "shows help" do
expect { subject.execute }.
to raise_error(Vagrant::Errors::CLIInvalidUsage)
end
end
context "with arguments" do
let (:argv) { ["vagrant/box-name", "1.0.0"] }
it "releases a version" do
allow(VagrantCloud::Version).to receive(:new).
with(box, "1.0.0", nil, nil, client.token).
and_return(version)
expect(version).to receive(:release).and_return({})
expect(subject.execute).to eq(0)
end
it "displays an error if encoutering a problem with the request" do
allow(VagrantCloud::Version).to receive(:new).
with(box, "1.0.0", nil, nil, client.token).
and_return(version)
allow(version).to receive(:release).
and_raise(VagrantCloud::ClientError.new("Fail Message", "Message"))
expect(subject.execute).to eq(1)
end
end
end

View File

@ -0,0 +1,66 @@
require File.expand_path("../../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/version/revoke")
describe VagrantPlugins::CloudCommand::VersionCommand::Command::Revoke do
include_context "unit"
let(:argv) { [] }
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
env = isolated_environment
env.vagrantfile("")
env.create_vagrant_env
end
subject { described_class.new(argv, iso_env) }
let(:action_runner) { double("action_runner") }
let(:client) { double("client", token: "1234token1234") }
let(:box) { double("box") }
let(:version) { double("version") }
before do
allow(iso_env).to receive(:action_runner).and_return(action_runner)
allow(VagrantPlugins::CloudCommand::Util).to receive(:client_login).
and_return(client)
allow(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results).
and_return(true)
allow(VagrantCloud::Box).to receive(:new)
.with(anything, "box-name", nil, nil, nil, client.token)
.and_return(box)
allow(iso_env.ui).to receive(:ask).
and_return("y")
end
context "with no arguments" do
it "shows help" do
expect { subject.execute }.
to raise_error(Vagrant::Errors::CLIInvalidUsage)
end
end
context "with arguments" do
let (:argv) { ["vagrant/box-name", "1.0.0"] }
it "revokes a version" do
allow(VagrantCloud::Version).to receive(:new).
with(box, "1.0.0", nil, nil, client.token).
and_return(version)
expect(version).to receive(:revoke).and_return({})
expect(subject.execute).to eq(0)
end
it "displays an error if encoutering a problem with the request" do
allow(VagrantCloud::Version).to receive(:new).
with(box, "1.0.0", nil, nil, client.token).
and_return(version)
expect(version).to receive(:revoke).
and_raise(VagrantCloud::ClientError.new("Fail Message", "Message"))
expect(subject.execute).to eq(1)
end
end
end

View File

@ -0,0 +1,65 @@
require File.expand_path("../../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/commands/cloud/version/update")
describe VagrantPlugins::CloudCommand::VersionCommand::Command::Update do
include_context "unit"
let(:argv) { [] }
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
env = isolated_environment
env.vagrantfile("")
env.create_vagrant_env
end
subject { described_class.new(argv, iso_env) }
let(:action_runner) { double("action_runner") }
let(:client) { double("client", token: "1234token1234") }
let(:box) { double("box") }
let(:version) { double("version") }
before do
allow(iso_env).to receive(:action_runner).and_return(action_runner)
allow(VagrantPlugins::CloudCommand::Util).to receive(:client_login).
and_return(client)
allow(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results).
and_return(true)
allow(VagrantCloud::Box).to receive(:new)
.with(anything, "box-name", nil, nil, nil, client.token)
.and_return(box)
end
context "with no arguments" do
it "shows help" do
expect { subject.execute }.
to raise_error(Vagrant::Errors::CLIInvalidUsage)
end
end
context "with arguments" do
let (:argv) { ["vagrant/box-name", "1.0.0", "-d", "description"] }
it "updates a version" do
allow(VagrantCloud::Version).to receive(:new).
with(box, "1.0.0", nil, "description", client.token).
and_return(version)
expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results)
expect(version).to receive(:update).and_return({})
expect(subject.execute).to eq(0)
end
it "displays an error if encoutering a problem with the request" do
allow(VagrantCloud::Version).to receive(:new).
with(box, "1.0.0", nil, "description", client.token).
and_return(version)
allow(version).to receive(:update).
and_raise(VagrantCloud::ClientError.new("Fail Message", "Message"))
expect(subject.execute).to eq(1)
end
end
end

View File

@ -32,6 +32,7 @@ Gem::Specification.new do |s|
s.add_dependency "winrm", "~> 2.1" s.add_dependency "winrm", "~> 2.1"
s.add_dependency "winrm-fs", "~> 1.0" s.add_dependency "winrm-fs", "~> 1.0"
s.add_dependency "winrm-elevated", "~> 1.1" s.add_dependency "winrm-elevated", "~> 1.1"
s.add_dependency "vagrant_cloud", "~> 2.0.0"
# NOTE: The ruby_dep gem is an implicit dependency from the listen gem. Later versions # NOTE: The ruby_dep gem is an implicit dependency from the listen gem. Later versions
# of the ruby_dep gem impose an aggressive constraint on the required ruby version (>= 2.2.5). # of the ruby_dep gem impose an aggressive constraint on the required ruby version (>= 2.2.5).

View File

@ -0,0 +1,339 @@
---
layout: "docs"
page_title: "vagrant cloud - Command-Line Interface"
sidebar_current: "cli-cloud"
description: |-
The "vagrant cloud" command can be used for taking actions against
Vagrant Cloud like searching or uploading a Vagrant Box
---
# Cloud
**Command: `vagrant cloud`**
This is the command used to manage anything related to [Vagrant Cloud](https://vagrantcloud.com)
The main functionality of this command is exposed via even more subcommands:
* [`auth`](#cloud-auth)
* [`box`](#cloud-box)
* [`provider`](#cloud-provider)
* [`publish`](#cloud-publish)
* [`search`](#cloud-search)
* [`version`](#cloud-version)
# Cloud Auth
**Command: `vagrant cloud auth`**
Information about this subcommand goes here
* [`login`](#cloud-auth-login)
* [`logout`](#cloud-auth-logout)
* [`who`](#cloud-auth-who)
## Cloud Auth Login
**Command: `vagrant cloud auth login`**
The login command is used to authenticate with the
[HashiCorp's Vagrant Cloud](/docs/vagrant-cloud) server. Logging is only
necessary if you are accessing protected boxes or using
[Vagrant Share](/docs/share/).
**Logging in is not a requirement to use Vagrant.** The vast majority
of Vagrant does _not_ require a login. Only certain features such as protected
boxes or [Vagrant Share](/docs/share/) require a login.
The reference of available command-line flags to this command
is available below.
### Options
* `--check` - This will check if you are logged in. In addition to outputting
whether you are logged in or not, the command will have exit status 0 if you are
logged in, and exit status 1 if you are not.
* `--logout` - This will log you out if you are logged in. If you are already
logged out, this command will do nothing. It is not an error to call this
command if you are already logged out.
* `--token` - This will set the Vagrant Cloud login token manually to the provided
string. It is assumed this token is a valid Vagrant Cloud access token.
### Examples
Securely authenticate to Vagrant Cloud using a username and password:
```text
$ vagrant cloud auth login
# ...
Vagrant Cloud username:
Vagrant Cloud password:
```
Check if the current user is authenticated:
```text
$ vagrant cloud auth login --check
You are already logged in.
```
Securely authenticate with Vagrant Cloud using a token:
```text
$ vagrant cloud auth login --token ABCD1234
The token was successfully saved.
```
## Cloud Auth Logout
**Command: `vagrant cloud auth logout`**
This will log you out if you are logged in. If you are already
logged out, this command will do nothing. It is not an error to call this
command if you are already logged out.
## Cloud Auth Whomi
**Command: `vagrant cloud auth whoami [TOKEN]`**
This command will validate your Vagrant Cloud token and will print the user who
it belongs to. If a token is passed in, it will attempt to validate it instead
of the token stored stored on disk.
# Cloud Box
**Command: `vagrant cloud box`**
The `cloud box` command is used to manage CRUD operations for all `box` entities on
Vagrant Cloud.
* [`create`](#cloud-box-create)
* [`delete`](#cloud-box-delete)
* [`show`](#cloud-box-show)
* [`update`](#cloud-box-update)
## Cloud Box Create
**Command: `vagrant cloud box create ORGANIZATION/BOX-NAME`**
The box create command is used to create a new box entry on Vagrant Cloud.
### Options
* `--description DESCRIPTION` - A longer description of the box. Can be
formatted with Markdown.
* `--short-description DESCRIPTION` - A short summary of the box.
* `--private` - Will make the new box private (Public by default)
## Cloud Box Delete
**Command: `vagrant cloud box delete ORGANIZATION/BOX-NAME`**
The box delete command will _permanently_ delete the given box entry on Vagrant Cloud. Before
making the request, it will ask if you are sure you want to delete the box.
## Cloud Box Show
**Command: `vagrant cloud box show ORGANIZATION/BOX-NAME`**
The box show command will display information about the latest version for the given Vagrant box.
## Cloud Box Update
**Command: `vagrant cloud box update ORGANIZATION/BOX-NAME`**
The box update command will update an already created box on Vagrant Cloud with the given options.
### Options
* `--description DESCRIPTION` - A longer description of the box. Can be
formatted with Markdown.
* `--short-description DESCRIPTION` - A short summary of the box.
* `--private` - Will make the new box private (Public by default)
# Cloud Provider
**Command: `vagrant cloud provider`**
The `cloud provider` command is used to manage CRUD operations for all `provider` entities on
Vagrant Cloud.
* [`create`](#cloud-provider-create)
* [`delete`](#cloud-provider-delete)
* [`update`](#cloud-provider-update)
* [`upload`](#cloud-provider-upload)
## Cloud Provider Create
**Command: `vagrant cloud provider create ORGANIZATION/BOX-NAME PROVIDER-NAME VERSION [URL]`**
The provider create command is used to create a new provider entry on Vagrant Cloud.
The `url` argument is expected to be a valid remote URL that Vagrant Cloud can use
to download the provider. If no `url` is specified, the provider entry can be updated
later with a url or the [upload](#cloud-provider-upload) command can be used to
upload a Vagrant [box file](/docs/boxes.html).
## Cloud Provider Delete
**Command: `vagrant cloud provider delete ORGANIZATION/BOX-NAME PROVIDER-NAME VERSION`**
The provider delete command is used to delete a provider entry on Vagrant Cloud.
Before making the request, it will ask if you are sure you want to delete the provider.
## Cloud Provider Update
**Command: `vagrant cloud provider update ORGANIZATION/BOX-NAME PROVIDER-NAME VERSION [URL]`**
The provider update command will update an already created provider for a box on
Vagrant Cloud with the given options.
## Cloud Provider Upload
**Command: `vagrant cloud provider upload ORGANIZATION/BOX-NAME PROVIDER-NAME VERSION BOX-FILE`**
The provider upload command will upload a Vagrant [box file](/docs/boxes.html) to Vagrant Cloud for
the specified version and provider.
# Cloud Publish
**Command: `vagrant cloud publish ORGANIZATION/BOX-NAME VERSION PROVIDER-NAME [PROVIDER-FILE]`**
The publish command is a start-to-finish solution for creating and updating a
Vagrant box on Vagrant Cloud. Instead of having to create each attribute of a Vagrant
box with separate commands, the publish command instead askes you to provide all
the information you need up front to create or update a new box.
## Options
* `--box-version VERSION` - Version to create for the box
* `--description DESCRIPTION` - A longer description of the box. Can be
formatted with Markdown.
* `--force` - Disables confirmation when creating or updating a box.
* `--short-description DESCRIPTION` - A short summary of the box.
* `--private` - Will make the new box private (Public by default)
* `--release` - Automatically releases the box after creation (Unreleased by default)
* `--url` - Valid remote URL to download the box file
* `--version-description DESCRIPTION` - Description of the version that will be created.
## Examples
Creating a new box on Vagrant Cloud
```text
$ vagrant cloud publish briancain/supertest 1.0.0 virtualbox boxes/my/virtualbox.box -d "A really cool box to download and use" --version-description "A cool version" --release --short-description "Donwload me!"
You are about to create a box on Vagrant Cloud with the following options:
briancain/supertest (1.0.0) for virtualbox
Automatic Release: true
Box Description: A really cool box to download and use
Box Short Description: Download me!
Version Description: A cool version
Do you wish to continue? [y/N] y
Creating a box entry...
Creating a version entry...
Creating a provider entry...
Uploading provider with file boxes/my/virtualbox.box
Releasing box...
Complete! Published briancain/supertest
tag: briancain/supertest
username: briancain
name: supertest
private: false
downloads: 0
created_at: 2018-07-25T17:53:04.340Z
updated_at: 2018-07-25T18:01:10.665Z
short_description: Download me!
description_markdown: A reall cool box to download and use
current_version: 1.0.0
providers: virtualbox
```
# Cloud Search
**Command: `vagrant cloud search QUERY`**
The cloud search command will take a query and search Vagrant Cloud for any matching
Vagrant boxes. Various filters can be applied to the results.
## Options
* `--json` - Format search results in JSON.
* `--page PAGE` - The page to display. Defaults to the first page of results.
* `--short` - Shows a simple list of box names for the results.
* `--order ORDER` - Order to display results. Can either be `desc` or `asc`.
Defaults to `desc`.
* `--limit LIMIT` - Max number of search results to display. Defaults to 25.
* `--provider PROVIDER` - Filter search results to a single provider.
* `--sort-by SORT` - The field to sort results on. Can be `created`, `downloads`
, or `updated`. Defaults to `downloads`.
## Examples
If you are looking for a HashiCorp box:
```text
vagrant cloud search hashicorp --limit 5
| NAME | VERSION | DOWNLOADS | PROVIDERS |
+-------------------------+---------+-----------+---------------------------------+
| hashicorp/precise64 | 1.1.0 | 6,675,725 | virtualbox,vmware_fusion,hyperv |
| hashicorp/precise32 | 1.0.0 | 2,261,377 | virtualbox |
| hashicorp/boot2docker | 1.7.8 | 59,284 | vmware_desktop,virtualbox |
| hashicorp/connect-vm | 0.1.0 | 6,912 | vmware_desktop,virtualbox |
| hashicorp/vagrant-share | 0.1.0 | 3,488 | vmware_desktop,virtualbox |
+-------------------------+---------+-----------+---------------------------------+
```
# Cloud Version
**Command: `vagrant cloud version`**
* [`create`](#cloud-version-create)
* [`delete`](#cloud-version-delete)
* [`release`](#cloud-version-release)
* [`revoke`](#cloud-version-revoke)
* [`update`](#cloud-version-update)
## Cloud Version Create
**Command: `vagrant cloud version create ORGANIZATION/BOX-NAME VERSION`**
The cloud create command creates a version entry for a box on Vagrant Cloud.
### Options
* `--description DESCRIPTION` - Description of the version that will be created.
## Cloud Version Delete
**Command: `vagrant cloud version delete ORGANIZATION/BOX-NAME VERSION`**
The cloud delete command deletes a version entry for a box on Vagrant Cloud.
Before making the request, it will ask if you are sure you want to delete the version.
## Cloud Version Release
**Command: `vagrant cloud version release ORGANIZATION/BOX-NAME VERSION`**
The cloud release command releases a version entry for a box on Vagrant Cloud
if it already exists. Before making the request, it will ask if you are sure you
want to release the version.
## Cloud Version Revoke
**Command: `vagrant cloud version revoke ORGANIZATION/BOX-NAME VERSION`**
The cloud revoke command revokes a version entry for a box on Vagrant Cloud
if it already exists. Before making the request, it will ask if you are sure you
want to revoke the version.
## Cloud Version Update
**Command: `vagrant cloud version update ORGANIZATION/BOX-NAME VERSION`**
### Options
* `--description DESCRIPTION` - Description of the version that will be created.

View File

@ -20,6 +20,7 @@
<a href="/docs/cli/">Commands (CLI)</a> <a href="/docs/cli/">Commands (CLI)</a>
<ul class="nav"> <ul class="nav">
<li<%= sidebar_current("cli-box") %>><a href="/docs/cli/box.html">box</a></li> <li<%= sidebar_current("cli-box") %>><a href="/docs/cli/box.html">box</a></li>
<li<%= sidebar_current("cli-cloud") %>><a href="/docs/cli/cloud.html">cloud</a></li>
<li<%= sidebar_current("cli-connect") %>><a href="/docs/cli/connect.html">connect</a></li> <li<%= sidebar_current("cli-connect") %>><a href="/docs/cli/connect.html">connect</a></li>
<li<%= sidebar_current("cli-destroy") %>><a href="/docs/cli/destroy.html">destroy</a></li> <li<%= sidebar_current("cli-destroy") %>><a href="/docs/cli/destroy.html">destroy</a></li>
<li<%= sidebar_current("cli-globalstatus") %>><a href="/docs/cli/global-status.html">global-status</a></li> <li<%= sidebar_current("cli-globalstatus") %>><a href="/docs/cli/global-status.html">global-status</a></li>