vagrant-unbundled 1.9.8.1 → 2.0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +47 -0
  3. data/Gemfile.lock +1 -1
  4. data/lib/vagrant/errors.rb +4 -0
  5. data/lib/vagrant/util/guest_inspection.rb +1 -1
  6. data/lib/vagrant/util/platform.rb +67 -16
  7. data/lib/vagrant/util/ssh.rb +2 -1
  8. data/plugins/commands/login/client.rb +101 -11
  9. data/plugins/commands/login/command.rb +41 -16
  10. data/plugins/commands/login/errors.rb +7 -0
  11. data/plugins/commands/login/locales/en.yml +4 -1
  12. data/plugins/commands/ssh_config/command.rb +9 -0
  13. data/plugins/communicators/winrm/shell.rb +14 -1
  14. data/plugins/guests/alt/cap/change_host_name.rb +46 -0
  15. data/plugins/guests/alt/cap/configure_networks.rb +126 -0
  16. data/plugins/guests/alt/cap/flavor.rb +63 -0
  17. data/plugins/guests/alt/cap/network_scripts_dir.rb +11 -0
  18. data/plugins/guests/alt/cap/rsync.rb +13 -0
  19. data/plugins/guests/alt/guest.rb +9 -0
  20. data/plugins/guests/alt/plugin.rb +40 -0
  21. data/plugins/guests/darwin/cap/shell_expand_guest_path.rb +2 -1
  22. data/plugins/guests/freebsd/cap/shell_expand_guest_path.rb +2 -1
  23. data/plugins/guests/kali/guest.rb +0 -0
  24. data/plugins/guests/kali/plugin.rb +0 -0
  25. data/plugins/guests/linux/cap/shell_expand_guest_path.rb +2 -1
  26. data/plugins/guests/netbsd/cap/shell_expand_guest_path.rb +2 -1
  27. data/plugins/guests/openbsd/cap/shell_expand_guest_path.rb +2 -1
  28. data/plugins/hosts/alt/cap/nfs.rb +43 -0
  29. data/plugins/hosts/alt/host.rb +11 -0
  30. data/plugins/hosts/alt/plugin.rb +32 -0
  31. data/plugins/hosts/gentoo/cap/nfs.rb +6 -13
  32. data/plugins/hosts/linux/cap/nfs.rb +53 -8
  33. data/plugins/hosts/redhat/cap/nfs.rb +10 -2
  34. data/plugins/providers/virtualbox/driver/version_4_0.rb +20 -6
  35. data/plugins/providers/virtualbox/driver/version_4_1.rb +20 -6
  36. data/plugins/providers/virtualbox/driver/version_4_2.rb +20 -6
  37. data/plugins/providers/virtualbox/driver/version_4_3.rb +20 -6
  38. data/plugins/providers/virtualbox/driver/version_5_0.rb +115 -74
  39. data/plugins/provisioners/ansible/config/base.rb +42 -6
  40. data/plugins/provisioners/ansible/config/guest.rb +0 -3
  41. data/plugins/provisioners/ansible/config/host.rb +12 -3
  42. data/plugins/provisioners/ansible/constants.rb +14 -0
  43. data/plugins/provisioners/ansible/errors.rb +13 -4
  44. data/plugins/provisioners/ansible/provisioner/base.rb +115 -5
  45. data/plugins/provisioners/ansible/provisioner/guest.rb +35 -15
  46. data/plugins/provisioners/ansible/provisioner/host.rb +53 -10
  47. data/plugins/provisioners/file/provisioner.rb +18 -5
  48. data/plugins/provisioners/salt/config.rb +14 -0
  49. data/plugins/provisioners/salt/provisioner.rb +37 -6
  50. data/templates/guests/alt/network_dhcp.erb +7 -0
  51. data/templates/guests/alt/network_ipv4address.erb +3 -0
  52. data/templates/guests/alt/network_ipv4route.erb +5 -0
  53. data/templates/guests/alt/network_static.erb +7 -0
  54. data/templates/locales/en.yml +45 -7
  55. data/test/unit/plugins/commands/login/client_test.rb +141 -24
  56. data/test/unit/plugins/commands/ssh_config/command_test.rb +16 -1
  57. data/test/unit/plugins/communicators/winrm/shell_test.rb +30 -1
  58. data/test/unit/plugins/guests/alt/cap/change_host_name_test.rb +42 -0
  59. data/test/unit/plugins/guests/alt/cap/configure_networks_test.rb +213 -0
  60. data/test/unit/plugins/guests/alt/cap/flavor_test.rb +72 -0
  61. data/test/unit/plugins/guests/alt/cap/network_scripts_dir_test.rb +21 -0
  62. data/test/unit/plugins/guests/alt/cap/rsync_test.rb +29 -0
  63. data/test/unit/plugins/guests/darwin/cap/shell_expand_guest_path_test.rb +3 -2
  64. data/test/unit/plugins/guests/freebsd/cap/shell_expand_guest_path_test.rb +3 -2
  65. data/test/unit/plugins/guests/linux/cap/shell_expand_guest_path_test.rb +3 -2
  66. data/test/unit/plugins/guests/netbsd/cap/shell_expand_guest_path_test.rb +3 -2
  67. data/test/unit/plugins/guests/openbsd/cap/shell_expand_guest_path_test.rb +3 -2
  68. data/test/unit/plugins/hosts/linux/cap/nfs_test.rb +77 -2
  69. data/test/unit/plugins/provisioners/ansible/config/guest_test.rb +7 -3
  70. data/test/unit/plugins/provisioners/ansible/config/host_test.rb +18 -3
  71. data/test/unit/plugins/provisioners/ansible/config/shared.rb +57 -3
  72. data/test/unit/plugins/provisioners/ansible/provisioner_test.rb +296 -70
  73. data/test/unit/plugins/provisioners/file/provisioner_test.rb +34 -0
  74. data/test/unit/plugins/provisioners/salt/config_test.rb +36 -0
  75. data/test/unit/plugins/provisioners/salt/provisioner_test.rb +85 -0
  76. data/test/unit/vagrant/util/platform_test.rb +86 -0
  77. data/test/unit/vagrant/util/ssh_test.rb +7 -7
  78. data/version.txt +1 -1
  79. metadata +22 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cbfc0426952400e2cf50440f9e33da65b96de1bf
4
- data.tar.gz: 6b5376cf7c8971923fa2da01b9e5cb5597cf98b2
3
+ metadata.gz: ab94e193c0cacc1e2503a53fc9cf56ebbacc8ca6
4
+ data.tar.gz: e9dff8cd516a995f503ed4e482c30bf788fb864a
5
5
  SHA512:
6
- metadata.gz: 3ffe827d8f83715f2f085a458acd2b6052f34691922ba71468cc9cca2794e5217471493a2c4d02b8b0becb17d060e07637de7ecb1ce63674808fb19d1a557272
7
- data.tar.gz: b0033fcd286a3445ee1acb2344e34e41419fe8551dc5ceef615eabcad07f70478f392f51ef1a950500879f621b0ced555a1a8bd63844aca79ca7630ca61afd48
6
+ metadata.gz: 7513bbf956fd58b41f9581ed285f799c5f26735a85d04de738aeab51261f75314840b6380e5659d4ae7a1aacc52bcf9cb0a8967d1dd071f9a75b2538efcef3e8
7
+ data.tar.gz: dddc399868eab9071a5c3db492680ca0896d49f66ce0a44937a2ddc0ae4ec63ee8034fa8d6cebcf05dd0c74e3fc53946175dc14d864614c4d46478168e2c708c
data/CHANGELOG.md CHANGED
@@ -1,3 +1,50 @@
1
+ ## 2.0.0 (September 7, 2017)
2
+
3
+ IMPROVEMENTS:
4
+
5
+ - commands/login: Add support for two-factor authentication [GH-8935]
6
+ - commands/ssh-config: Properly display windows path if invoked from msys2 or
7
+ cygwin [GH-8915]
8
+ - guests/alt: Add support for ALT Linux [GH-8746]
9
+ - guests/kali: Fix file permissions on guest plugin ruby files [GH-8950]
10
+ - hosts/linux: Provide common systemd detection for services interaction, fix NFS
11
+ host interactions [GH-8938]
12
+ - providers/salt: Remove duplicate stdout, stderr output from salt [GH-8767]
13
+ - providers/salt: Introduce salt_call_args and salt_args option for salt provisioner
14
+ [GH-8927]
15
+ - providers/virtualbox: Improving resilience of some VirtualBox commands [GH-8951]
16
+ - provisioners/ansible(both): Add the compatibility_mode option, with auto-detection
17
+ enabled by default [GH-8913, GH-6570]
18
+ - provisioners/ansible: Add the version option to the host-based provisioner
19
+ [GH-8913, GH-8914]
20
+ - provisioners/ansible(both): Add the become and become_user options with deprecation
21
+ of sudo and sudo_user options [GH-8913, GH-6570]
22
+ - provisioners/ansible: Add the ask_become_pass option with deprecation of the
23
+ ask_sudo_pass option [GH-8913, GH-6570]
24
+
25
+ BUG FIXES:
26
+
27
+ - guests/shell_expand_guest_path : Properly expand guest paths that include relative
28
+ path alias [GH-8918]
29
+ - hosts/linux: Remove duplicate export folders before writing /etc/exports [GH-8945]
30
+ - provisioners/ansible(both): Add single quotes to the inventory host variables, only
31
+ when necessary [GH-8597]
32
+ - provisioners/ansible(both): Add the "all:vars" section to the inventory when defined
33
+ in `groups` option [GH-7730]
34
+ - provisioners/ansible_local: Extra variables are no longer truncated when a dollar ($)
35
+ character is present [GH-7735]
36
+ - provisioners/file: Align file provisioner functionality on all platforms [GH-8939]
37
+ - util/ssh: Properly quote key path for IdentityFile option to allow for spaces [GH-8924]
38
+
39
+ BREAKING CHANGES:
40
+
41
+ - Both Ansible provisioners are now capable of automatically setting the compatibility_mode that
42
+ best fits with the Ansible version in use. You may encounter some compatibility issues when
43
+ upgrading. If you were using Ansible 2.x and referring to the _ssh-prefixed variables present
44
+ in the generated inventory (e.g. `ansible_ssh_host`). In this case, you can fix your Vagrant
45
+ setup by setting compatibility_mode = "1.8", or by migrating to the new variable names (e.g.
46
+ ansible_host).
47
+
1
48
  ## 1.9.8 (August 23, 2017)
2
49
 
3
50
  IMPROVEMENTS:
data/Gemfile.lock CHANGED
@@ -11,7 +11,7 @@ GIT
11
11
  PATH
12
12
  remote: .
13
13
  specs:
14
- vagrant-unbundled (1.9.8.1)
14
+ vagrant-unbundled (2.0.0.1)
15
15
  childprocess (~> 0.6.0)
16
16
  erubis (~> 2.7.0)
17
17
  hashicorp-checkpoint (~> 0.1.1)
@@ -468,6 +468,10 @@ module Vagrant
468
468
  error_key(:nfs_bad_exports)
469
469
  end
470
470
 
471
+ class NFSDupePerms < VagrantError
472
+ error_key(:nfs_dupe_permissions)
473
+ end
474
+
471
475
  class NFSExportsFailed < VagrantError
472
476
  error_key(:nfs_exports_failed)
473
477
  end
@@ -8,7 +8,7 @@ module Vagrant
8
8
 
9
9
  ## systemd helpers
10
10
 
11
- # systemd is in used
11
+ # systemd is in use
12
12
  #
13
13
  # @return [Boolean]
14
14
  def systemd?(comm)
@@ -4,6 +4,7 @@ require "tmpdir"
4
4
 
5
5
  require "vagrant/util/subprocess"
6
6
  require "vagrant/util/powershell"
7
+ require "vagrant/util/which"
7
8
 
8
9
  module Vagrant
9
10
  module Util
@@ -19,6 +20,15 @@ module Vagrant
19
20
  @_cygwin
20
21
  end
21
22
 
23
+ def msys?
24
+ if !defined?(@_msys)
25
+ @_msys = ENV["VAGRANT_DETECTED_OS"].to_s.downcase.include?("msys") ||
26
+ platform.include?("msys") ||
27
+ ENV["OSTYPE"].to_s.downcase.include?("msys")
28
+ end
29
+ @_msys
30
+ end
31
+
22
32
  def wsl?
23
33
  if !defined?(@_wsl)
24
34
  @_wsl = false
@@ -93,25 +103,36 @@ module Vagrant
93
103
  # @param [String] path
94
104
  # @return [String]
95
105
  def cygwin_path(path)
96
- if cygwin?
97
- begin
98
- # First try the real cygpath
99
- process = Subprocess.execute("cygpath", "-u", "-a", path.to_s)
100
- return process.stdout.chomp
101
- rescue Errors::CommandUnavailableWindows
102
- end
106
+ begin
107
+ # We have to revert to the old env
108
+ # path here, otherwise it looks like
109
+ # msys2 ends up using the wrong cygpath
110
+ # binary and ends up with a `/cygdrive`
111
+ # when it doesn't exist in msys2
112
+ original_path_env = ENV['PATH']
113
+ ENV['PATH'] = ENV['VAGRANT_OLD_ENV_PATH']
114
+ cygpath = Vagrant::Util::Which.which("cygpath")
115
+ cygpath.gsub!("/", '\\')
116
+ process = Subprocess.execute(
117
+ cygpath, "-u", "-a", path.to_s)
118
+ return process.stdout.chomp
119
+ rescue Errors::CommandUnavailableWindows => e
120
+ # Sometimes cygpath isn't available (msys). Instead, do what we
121
+ # can with bash tricks.
122
+ process = Subprocess.execute(
123
+ "bash",
124
+ "--noprofile",
125
+ "--norc",
126
+ "-c", "cd #{Shellwords.escape(path)} && pwd")
127
+ return process.stdout.chomp
128
+ ensure
129
+ ENV['PATH'] = original_path_env
103
130
  end
104
-
105
- # Sometimes cygpath isn't available (msys). Instead, do what we
106
- # can with bash tricks.
107
- process = Subprocess.execute(
108
- "bash",
109
- "--noprofile",
110
- "--norc",
111
- "-c", "cd #{Shellwords.escape(path)} && pwd")
112
- return process.stdout.chomp
113
131
  end
114
132
 
133
+ # Identical to cygwin_path for now
134
+ alias_method :msys_path, :cygwin_path
135
+
115
136
  # This takes any path and converts it to a full-length Windows
116
137
  # path on Windows machines in Cygwin.
117
138
  #
@@ -264,6 +285,23 @@ module Vagrant
264
285
  end
265
286
  end
266
287
 
288
+ # Takes a windows path and formats it to the
289
+ # 'unix' style (i.e. `/cygdrive/c` or `/c/`)
290
+ #
291
+ # @param [Pathname, String] path Path to convert
292
+ # @param [Hash] hash of arguments
293
+ # @return [String]
294
+ def format_windows_path(path, *args)
295
+ path = cygwin_path(path) if cygwin?
296
+ path = msys_path(path) if msys?
297
+ path = wsl_to_windows_path(path) if wsl?
298
+ if windows? || wsl?
299
+ path = windows_unc_path(path) if !args.include?(:disable_unc)
300
+ end
301
+
302
+ path
303
+ end
304
+
267
305
  # Automatically convert a given path to a Windows path. Will only
268
306
  # be applied if running on a Windows host. If running on Windows
269
307
  # host within the WSL, the actual Windows path will be returned.
@@ -410,6 +448,19 @@ module Vagrant
410
448
  end
411
449
  end
412
450
 
451
+ # systemd is in use
452
+ def systemd?
453
+ if !defined?(@_systemd)
454
+ if !windows?
455
+ result = Vagrant::Util::Subprocess.execute("ps", "-o", "comm=", "1")
456
+ @_systemd = result.stdout.chomp == "systemd"
457
+ else
458
+ @_systemd = false
459
+ end
460
+ end
461
+ @_systemd
462
+ end
463
+
413
464
  # @private
414
465
  # Reset the cached values for platform. This is not considered a public
415
466
  # API and should only be used for testing.
@@ -139,7 +139,8 @@ module Vagrant
139
139
  # Use '-o' instead of '-i' because '-i' does not call
140
140
  # percent_expand in misc.c, but '-o' does. when passing the path,
141
141
  # replace '%' in the path with '%%' to escape the '%'
142
- command_options += ["-o", "IdentityFile=%s" % [path.to_s.gsub('%', '%%')]]
142
+ path = path.to_s.gsub('%', '%%')
143
+ command_options += ["-o", "IdentityFile=\"#{path}\""]
143
144
  end
144
145
  end
145
146
 
@@ -5,8 +5,15 @@ require "vagrant/util/presence"
5
5
  module VagrantPlugins
6
6
  module LoginCommand
7
7
  class Client
8
+ APP = "app".freeze
9
+
8
10
  include Vagrant::Util::Presence
9
11
 
12
+ attr_accessor :username_or_email
13
+ attr_accessor :password
14
+ attr_reader :two_factor_default_delivery_method
15
+ attr_reader :two_factor_delivery_methods
16
+
10
17
  # Initializes a login client with the given Vagrant::Environment.
11
18
  #
12
19
  # @param [Vagrant::Environment] env
@@ -35,29 +42,67 @@ module VagrantPlugins
35
42
  RestClient.get(url, content_type: :json)
36
43
  true
37
44
  end
45
+ rescue Errors::Unauthorized
46
+ false
38
47
  end
39
48
 
40
49
  # Login logs a user in and returns the token for that user. The token
41
50
  # is _not_ stored unless {#store_token} is called.
42
51
  #
43
- # @param [String] username_or_email
44
- # @param [String] password
45
52
  # @param [String] description
53
+ # @param [String] code
46
54
  # @return [String] token The access token, or nil if auth failed.
47
- def login(username_or_email, password, description: nil)
55
+ def login(description: nil, code: nil)
48
56
  @logger.info("Logging in '#{username_or_email}'")
49
57
 
50
- with_error_handling do
51
- url = "#{Vagrant.server_url}/api/v1/authenticate"
52
- request = {
58
+ response = post(
59
+ "/api/v1/authenticate", {
53
60
  user: {
54
61
  login: username_or_email,
55
62
  password: password
56
63
  },
57
64
  token: {
58
65
  description: description
66
+ },
67
+ two_factor: {
68
+ code: code
69
+ }
70
+ }
71
+ )
72
+
73
+ response["token"]
74
+ end
75
+
76
+ # Requests a 2FA code
77
+ # @param [String] delivery_method
78
+ def request_code(delivery_method)
79
+ @env.ui.warn("Requesting 2FA code via #{delivery_method.upcase}...")
80
+
81
+ response = post(
82
+ "/api/v1/two-factor/request-code", {
83
+ user: {
84
+ login: username_or_email,
85
+ password: password
86
+ },
87
+ two_factor: {
88
+ delivery_method: delivery_method.downcase
59
89
  }
60
90
  }
91
+ )
92
+
93
+ two_factor = response['two_factor']
94
+ obfuscated_destination = two_factor['obfuscated_destination']
95
+
96
+ @env.ui.success("2FA code sent to #{obfuscated_destination}.")
97
+ end
98
+
99
+ # Issues a post to a Vagrant Cloud path with the given payload.
100
+ # @param [String] path
101
+ # @param [Hash] payload
102
+ # @return [Hash] response data
103
+ def post(path, payload)
104
+ with_error_handling do
105
+ url = File.join(Vagrant.server_url, path)
61
106
 
62
107
  proxy = nil
63
108
  proxy ||= ENV["HTTPS_PROXY"] || ENV["https_proxy"]
@@ -67,7 +112,7 @@ module VagrantPlugins
67
112
  response = RestClient::Request.execute(
68
113
  method: :post,
69
114
  url: url,
70
- payload: JSON.dump(request),
115
+ payload: JSON.dump(payload),
71
116
  proxy: proxy,
72
117
  headers: {
73
118
  accept: :json,
@@ -76,8 +121,7 @@ module VagrantPlugins
76
121
  },
77
122
  )
78
123
 
79
- data = JSON.load(response.to_s)
80
- data["token"]
124
+ JSON.load(response.to_s)
81
125
  end
82
126
  end
83
127
 
@@ -138,14 +182,33 @@ EOH
138
182
  yield
139
183
  rescue RestClient::Unauthorized
140
184
  @logger.debug("Unauthorized!")
141
- false
185
+ raise Errors::Unauthorized
186
+ rescue RestClient::BadRequest => e
187
+ @logger.debug("Bad request:")
188
+ @logger.debug(e.message)
189
+ @logger.debug(e.backtrace.join("\n"))
190
+ parsed_response = JSON.parse(e.response)
191
+ errors = parsed_response["errors"].join("\n")
192
+ raise Errors::ServerError, errors: errors
142
193
  rescue RestClient::NotAcceptable => e
143
194
  @logger.debug("Got unacceptable response:")
144
195
  @logger.debug(e.message)
145
196
  @logger.debug(e.backtrace.join("\n"))
146
197
 
198
+ parsed_response = JSON.parse(e.response)
199
+
200
+ if two_factor = parsed_response['two_factor']
201
+ store_two_factor_information two_factor
202
+
203
+ if two_factor_default_delivery_method != APP
204
+ request_code two_factor_default_delivery_method
205
+ end
206
+
207
+ raise Errors::TwoFactorRequired
208
+ end
209
+
147
210
  begin
148
- errors = JSON.parse(e.response)["errors"].join("\n")
211
+ errors = parsed_response["errors"].join("\n")
149
212
  raise Errors::ServerError, errors: errors
150
213
  rescue JSON::ParserError; end
151
214
 
@@ -158,6 +221,33 @@ EOH
158
221
  def token_path
159
222
  @env.data_dir.join("vagrant_login_token")
160
223
  end
224
+
225
+ def store_two_factor_information(two_factor)
226
+ @two_factor_default_delivery_method =
227
+ two_factor['default_delivery_method']
228
+
229
+ @two_factor_delivery_methods =
230
+ two_factor['delivery_methods']
231
+
232
+ @env.ui.warn "2FA is enabled for your account."
233
+ if two_factor_default_delivery_method == APP
234
+ @env.ui.info "Enter the code from your authenticator."
235
+ else
236
+ @env.ui.info "Default method is " \
237
+ "'#{two_factor_default_delivery_method}'."
238
+ end
239
+
240
+ other_delivery_methods =
241
+ two_factor_delivery_methods - [APP]
242
+
243
+ if other_delivery_methods.any?
244
+ other_delivery_methods_sentence = other_delivery_methods
245
+ .map { |word| "'#{word}'" }
246
+ .join(' or ')
247
+ @env.ui.info "You can also type #{other_delivery_methods_sentence} " \
248
+ "to request a new code."
249
+ end
250
+ end
161
251
  end
162
252
  end
163
253
  end
@@ -17,6 +17,10 @@ module VagrantPlugins
17
17
  options[:check] = c
18
18
  end
19
19
 
20
+ o.on("-d", "--description DESCRIPTION", String, "Description for the Vagrant Cloud token") do |t|
21
+ options[:description] = t
22
+ end
23
+
20
24
  o.on("-k", "--logout", "Logs you out if you're logged in") do |k|
21
25
  options[:logout] = k
22
26
  end
@@ -24,6 +28,10 @@ module VagrantPlugins
24
28
  o.on("-t", "--token TOKEN", String, "Set the Vagrant Cloud token") do |t|
25
29
  options[:token] = t
26
30
  end
31
+
32
+ o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |t|
33
+ options[:login] = t
34
+ end
27
35
  end
28
36
 
29
37
  # Parse the options
@@ -31,6 +39,7 @@ module VagrantPlugins
31
39
  return if !argv
32
40
 
33
41
  @client = Client.new(@env)
42
+ @client.username_or_email = options[:login]
34
43
 
35
44
  # Determine what task we're actually taking based on flags
36
45
  if options[:check]
@@ -50,28 +59,44 @@ module VagrantPlugins
50
59
  end
51
60
 
52
61
  # Ask for the username
53
- login = nil
54
- password = nil
55
- description = nil
56
- while !login
57
- login = @env.ui.ask("Vagrant Cloud Username: ")
62
+ if @client.username_or_email
63
+ @env.ui.output("Vagrant Cloud username or email: #{@client.username_or_email}")
64
+ end
65
+ until @client.username_or_email
66
+ @client.username_or_email = @env.ui.ask("Vagrant Cloud username or email: ")
58
67
  end
59
68
 
60
- while !password
61
- password = @env.ui.ask("Password (will be hidden): ", echo: false)
69
+ until @client.password
70
+ @client.password = @env.ui.ask("Password (will be hidden): ", echo: false)
62
71
  end
63
72
 
64
- description_default = "Vagrant login from #{Socket.gethostname}"
65
- while !description
66
- description =
67
- @env.ui.ask("Token description (Defaults to #{description_default.inspect}): ")
73
+ description = options[:description]
74
+ if description
75
+ @env.ui.output("Token description: #{description}")
76
+ else
77
+ description_default = "Vagrant login from #{Socket.gethostname}"
78
+ until description
79
+ description =
80
+ @env.ui.ask("Token description (Defaults to #{description_default.inspect}): ")
81
+ end
82
+ description = description_default if description.empty?
68
83
  end
69
- description = description_default if description.empty?
70
84
 
71
- token = @client.login(login, password, description: description)
72
- if !token
73
- @env.ui.error(I18n.t("login_command.invalid_login"))
74
- return 1
85
+ code = nil
86
+
87
+ begin
88
+ token = @client.login(description: description, code: code)
89
+ rescue Errors::TwoFactorRequired
90
+ until code
91
+ code = @env.ui.ask("2FA code: ")
92
+
93
+ if @client.two_factor_delivery_methods.include?(code.downcase)
94
+ delivery_method, code = code, nil
95
+ @client.request_code delivery_method
96
+ end
97
+ end
98
+
99
+ retry
75
100
  end
76
101
 
77
102
  @client.store_token(token)