plugins/commands/cloud/util.rb in vagrant-unbundled-2.2.10.0 vs plugins/commands/cloud/util.rb in vagrant-unbundled-2.2.14.0
- old
+ new
@@ -1,206 +1,310 @@
module VagrantPlugins
module CloudCommand
- class Util
- class << self
- # @param [String] username - Vagrant Cloud username
- # @param [String] access_token - Vagrant Cloud Token used to authenticate
- # @param [String] vagrant_cloud_server - Vagrant Cloud server to make API request
- # @return [VagrantCloud::Account]
- def account(username, access_token, vagrant_cloud_server)
- if !defined?(@_account)
- @_account = VagrantCloud::Account.new(username, access_token, vagrant_cloud_server)
- end
- @_account
+ module Util
+ # @return [String] Vagrant Cloud server URL
+ 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 api_server_url
- if Vagrant.server_url == Vagrant::DEFAULT_SERVER_URL
- return "#{Vagrant.server_url}/api/v1"
- else
- return Vagrant.server_url
- end
- end
+ # @param [Vagrant::Environment] env
+ # @param [Hash] options
+ # @option options [String] :login Username or email
+ # @option options [String] :description Description of login usage for token
+ # @option options [String] :code 2FA code for login
+ # @option options [Boolean] :quiet Do not prompt user
+ # @returns [VagrantPlugins::CloudCommand::Client, nil]
+ def client_login(env, options={})
+ return @_client if defined?(@_client)
+ @_client = Client.new(env)
+ return @_client if @_client.logged_in?
- # @param [Vagrant::Environment] env
- # @param [Hash] options
- # @returns [VagrantPlugins::CloudCommand::Client]
- def client_login(env, options)
- if !defined?(@_client)
- @_client = Client.new(env)
- return @_client if @_client.logged_in?
+ # If directed to be quiet, do not continue and
+ # just return nil
+ return if options[:quiet]
- # Let the user know what is going on.
- env.ui.output(I18n.t("cloud_command.command_header") + "\n")
+ # 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
+ # 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
- options = {} if !options
- # Ask for the username
- if options[:login]
- @_client.username_or_email = options[:login]
- env.ui.output("Vagrant Cloud username or email: #{@_client.username_or_email}")
- else
- @_client.username_or_email = env.ui.ask("Vagrant Cloud username or email: ")
- end
+ options = {} if !options
+ # Ask for the username
+ if options[:login]
+ @_client.username_or_email = options[:login]
+ env.ui.output("Vagrant Cloud username or email: #{@_client.username_or_email}")
+ else
+ @_client.username_or_email = env.ui.ask("Vagrant Cloud username or email: ")
+ end
- @_client.password = env.ui.ask("Password (will be hidden): ", echo: false)
+ @_client.password = env.ui.ask("Password (will be hidden): ", echo: false)
- description_default = "Vagrant login from #{Socket.gethostname}"
- if !options[:description]
- description = env.ui.ask("Token description (Defaults to #{description_default.inspect}): ")
- else
- description = options[:description]
- env.ui.output("Token description: #{description}")
- end
+ description_default = "Vagrant login from #{Socket.gethostname}"
+ if !options[:description]
+ description = env.ui.ask("Token description (Defaults to #{description_default.inspect}): ")
+ else
+ description = options[:description]
+ env.ui.output("Token description: #{description}")
+ end
- description = description_default if description.empty?
+ description = description_default if description.empty?
- code = nil
+ code = nil
- begin
- token = @_client.login(description: description, code: code)
- rescue Errors::TwoFactorRequired
- until code
- code = env.ui.ask("2FA code: ")
+ 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
+ if @_client.two_factor_delivery_methods.include?(code.downcase)
+ delivery_method, code = code, nil
+ @_client.request_code delivery_method
end
-
- @_client.store_token(token)
- Vagrant::Util::CredentialScrubber.sensitive(token)
- env.ui.success(I18n.t("cloud_command.logged_in"))
- @_client
end
- @_client
+
+ retry
end
- # ===================================================
- # Modified from https://stackoverflow.com/a/28685559
- # for printing arrays of hashes in formatted tables
- # ===================================================
+ @_client.store_token(token)
+ Vagrant::Util::CredentialScrubber.sensitive(token)
+ env.ui.success(I18n.t("cloud_command.logged_in"))
+ @_client
+ end
- # @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
- }}
+ # Print search results from Vagrant Cloud to the console
+ #
+ # @param [Array<VagrantCloud::Box>] search_results Box search results from Vagrant Cloud
+ # @param [Boolean] short Print short summary
+ # @param [Boolean] json Print output in JSON format
+ # @param [Vagrant::Environment] env Current Vagrant environment
+ # @return [nil]
+ def format_search_results(search_results, short, json, env)
+ result = search_results.map do |b|
+ {
+ name: b.tag,
+ version: b.current_version.version,
+ downloads: format_downloads(b.downloads.to_s),
+ providers: b.current_version.providers.map(&:name).join(", ")
+ }
+ end
- write_header(env, columns)
- write_divider(env, columns)
- results.each { |h| write_line(env, columns, h,to_rjust_keys) }
- write_divider(env, columns)
+ 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
+ nil
+ end
- def write_header(env, columns)
- env.ui.info "| #{ columns.map { |_,g| g[:label].ljust(g[:width]) }.join(' | ') } |"
+ # Output box details result from Vagrant Cloud
+ #
+ # @param [VagrantCloud::Box, VagrantCloud::Box::Version] box Box or box version to display
+ # @param [Vagrant::Environment] env Current Vagrant environment
+ # @return [nil]
+ def format_box_results(box, env)
+ if box.is_a?(VagrantCloud::Box)
+ info = box_info(box)
+ elsif box.is_a?(VagrantCloud::Box::Provider)
+ info = version_info(box.version)
+ else
+ info = version_info(box)
end
- def write_divider(env, columns)
- env.ui.info "+-#{ columns.map { |_,g| "-"*g[:width] }.join("-+-") }-+"
+ width = info.keys.map(&:size).max
+ info.each do |k, v|
+ whitespace = width - k.size
+ env.ui.info "#{k}: #{"".ljust(whitespace)} #{v}"
end
+ nil
+ 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} |"
+ # Load box and yield
+ #
+ # @param [VagrantCloud::Account] account Vagrant Cloud account
+ # @param [String] org Organization name
+ # @param [String] box Box name
+ # @yieldparam [VagrantCloud::Box] box Requested Vagrant Cloud box
+ # @yieldreturn [Integer]
+ # @return [Integer]
+ def with_box(account:, org:, box:)
+ org = account.organization(name: org)
+ b = org.boxes.detect { |b| b.name == box }
+ if !b
+ @env.ui.error(I18n.t("cloud_command.box.not_found",
+ org: org.username, box_name: box))
+ return 1
end
+ yield b
+ end
- # ===================================================
- # ===================================================
+ # Load box version and yield
+ #
+ # @param [VagrantCloud::Account] account Vagrant Cloud account
+ # @param [String] org Organization name
+ # @param [String] box Box name
+ # @param [String] version Box version
+ # @yieldparam [VagrantCloud::Box::Version] version Requested Vagrant Cloud box version
+ # @yieldreturn [Integer]
+ # @return [Integer]
+ def with_version(account:, org:, box:, version:)
+ with_box(account: account, org: org, box: box) do |b|
+ v = b.versions.detect { |v| v.version == version }
+ if !v
+ @env.ui.error(I18n.t("cloud_command.version.not_found",
+ box_name: box, org: org, version: version))
+ return 1
+ end
+ yield v
+ end
+ 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(", ") + "..."
+ # Load box version and yield
+ #
+ # @param [VagrantCloud::Account] account Vagrant Cloud account
+ # @param [String] org Organization name
+ # @param [String] box Box name
+ # @param [String] version Box version
+ # @param [String] provider Box version provider name
+ # @yieldparam [VagrantCloud::Box::Provider] provider Requested Vagrant Cloud box version provider
+ # @yieldreturn [Integer]
+ # @return [Integer]
+ def with_provider(account:, org:, box:, version:, provider:)
+ with_version(account: account, org: org, box: box, version: version) do |v|
+ p = v.providers.detect { |p| p.name == provider }
+ if !p
+ @env.ui.error(I18n.t("cloud_command.provider.not_found",
+ org: org, box_name: box, version: version, provider_name: provider))
+ return 1
end
+ yield p
+ end
+ end
+ protected
- 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}"
+ # Extract box information for display
+ #
+ # @param [VagrantCloud::Box] box Box for extracting information
+ # @return [Hash<String,String>]
+ def box_info(box)
+ Hash.new.tap do |i|
+ i["Box"] = box.tag
+ i["Description"] = box.description
+ i["Private"] = box.private ? "yes" : "no"
+ i["Created"] = box.created_at
+ i["Updated"] = box.updated_at
+ if !box.current_version.nil?
+ i["Current Version"] = box.current_version.version
+ else
+ i["Current Version"] = "N/A"
end
+ i["Versions"] = box.versions.slice(0, 5).map(&:version).join(", ")
+ if box.versions.size > 5
+ i["Versions"] += " ..."
+ end
+ i["Downloads"] = format_downloads(box.downloads)
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
+ # Extract version information for display
+ #
+ # @param [VagrantCloud::Box::Version] version Box version for extracting information
+ # @return [Hash<String,String>]
+ def version_info(version)
+ Hash.new.tap do |i|
+ i["Box"] = version.box.tag
+ i["Version"] = version.version
+ i["Description"] = version.description
+ i["Status"] = version.status
+ i["Providers"] = version.providers.map(&:name).sort.join(", ")
+ i["Created"] = version.created_at
+ i["Updated"] = version.updated_at
end
+ end
+ # Print table results from search request
+ #
+ # @param [Vagrant::Environment] env Current Vagrant environment
+ # @param [Hash] column_labels A hash of key/value pairs for table labels (i.e. {col1: "COL1"})
+ # @param [Array] results An array of hashes representing search resuls
+ # @param [Array] to_jrust_keys - List of columns keys to right justify (left justify is defualt)
+ # @return [nil]
+ # @note Modified from https://stackoverflow.com/a/28685559
+ def print_search_table(env, column_labels, results, to_rjust_keys)
+ columns = column_labels.each_with_object({}) do |(col,label),h|
+ h[col] = {
+ label: label,
+ width: [results.map { |g| g[col].size }.max, label.size].max
+ }
+ 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
+ write_header(env, columns)
+ write_divider(env, columns)
+ results.each { |h| write_line(env, columns, h, to_rjust_keys) }
+ write_divider(env, columns)
+ end
- if short
- result.map {|b| env.ui.info(b[:name])}
- elsif json
- env.ui.info(result.to_json)
+ # Write the header for a table
+ #
+ # @param [Vagrant::Environment] env Current Vagrant environment
+ # @param [Array<Hash>] columns List of columns in Hash format with `:label` and `:width` keys
+ # @return [nil]
+ def write_header(env, columns)
+ env.ui.info "| #{ columns.map { |_,g| g[:label].ljust(g[:width]) }.join(' | ') } |"
+ nil
+ end
+
+ # Write a row divider for a table
+ #
+ # @param [Vagrant::Environment] env Current Vagrant environment
+ # @param [Array<Hash>] columns List of columns in Hash format with `:label` and `:width` keys
+ # @return [nil]
+ def write_divider(env, columns)
+ env.ui.info "+-#{ columns.map { |_,g| "-"*g[:width] }.join("-+-") }-+"
+ nil
+ end
+
+ # Write a line of content for a table
+ #
+ # @param [Vagrant::Environment] env Current Vagrant environment
+ # @param [Array<Hash>] columns List of columns in Hash format with `:label` and `:width` keys
+ # @param [Hash] h Values to print in row
+ # @param [Array<String>] to_rjust_keys List of columns to right justify
+ # @return [nil]
+ 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
- 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])
+ h[k].ljust(columns[k][:width])
end
- end
+ }.join(" | ")
+ env.ui.info "| #{str} |"
+ nil
+ end
+
+ # Converts a string of numbers into a formatted number
+ #
+ # 1234 -> 1,234
+ #
+ # @param [String] number Numer to format
+ def format_downloads(number)
+ number.to_s.chars.reverse.each_slice(3).map(&:join).join(",").reverse
end
end
end
end