File: util.rb

package info (click to toggle)
vagrant 2.2.3%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 8,072 kB
  • sloc: ruby: 80,731; sh: 369; makefile: 9; lisp: 1
file content (206 lines) | stat: -rw-r--r-- 7,638 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
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
        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
        # @returns [VagrantPlugins::CloudCommand::Client]
        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

            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)

            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?

            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)
            Vagrant::Util::CredentialScrubber.sensitive(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