vagrant-unbundled 2.2.10.0 → 2.2.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -0
  3. data/Gemfile +1 -1
  4. data/README.md +4 -44
  5. data/RELEASE.md +1 -1
  6. data/contrib/zsh/_vagrant +3 -1
  7. data/contrib/zsh/generate_zsh_completion.rb +2 -3
  8. data/lib/vagrant.rb +0 -4
  9. data/lib/vagrant/action/builder.rb +6 -15
  10. data/lib/vagrant/action/builtin/box_add.rb +5 -1
  11. data/lib/vagrant/action/builtin/cloud_init_setup.rb +10 -15
  12. data/lib/vagrant/action/builtin/synced_folders.rb +8 -2
  13. data/lib/vagrant/action/runner.rb +1 -1
  14. data/lib/vagrant/box.rb +8 -2
  15. data/lib/vagrant/box_collection.rb +1 -1
  16. data/lib/vagrant/bundler.rb +43 -16
  17. data/lib/vagrant/machine.rb +8 -5
  18. data/lib/vagrant/machine_index.rb +1 -0
  19. data/lib/vagrant/plugin/v2/command.rb +2 -1
  20. data/lib/vagrant/shared_helpers.rb +8 -0
  21. data/lib/vagrant/util/downloader.rb +3 -2
  22. data/lib/vagrant/util/is_port_open.rb +1 -1
  23. data/lib/vagrant/util/mime.rb +92 -0
  24. data/lib/vagrant/util/platform.rb +2 -1
  25. data/lib/vagrant/util/template_renderer.rb +2 -2
  26. data/lib/vagrant/util/uploader.rb +7 -4
  27. data/plugins/commands/cap/command.rb +5 -1
  28. data/plugins/commands/cloud/auth/login.rb +20 -23
  29. data/plugins/commands/cloud/auth/logout.rb +2 -10
  30. data/plugins/commands/cloud/auth/middleware/add_downloader_authentication.rb +57 -0
  31. data/plugins/commands/cloud/auth/whoami.rb +18 -20
  32. data/plugins/commands/cloud/box/create.rb +33 -29
  33. data/plugins/commands/cloud/box/delete.rb +30 -24
  34. data/plugins/commands/cloud/box/show.rb +41 -31
  35. data/plugins/commands/cloud/box/update.rb +34 -26
  36. data/plugins/commands/cloud/client/client.rb +50 -81
  37. data/plugins/commands/cloud/list.rb +3 -4
  38. data/plugins/commands/cloud/locales/en.yml +9 -9
  39. data/plugins/commands/cloud/plugin.rb +10 -0
  40. data/plugins/commands/cloud/provider/create.rb +38 -28
  41. data/plugins/commands/cloud/provider/delete.rb +39 -29
  42. data/plugins/commands/cloud/provider/update.rb +37 -28
  43. data/plugins/commands/cloud/provider/upload.rb +44 -34
  44. data/plugins/commands/cloud/publish.rb +185 -108
  45. data/plugins/commands/cloud/search.rb +34 -21
  46. data/plugins/commands/cloud/util.rb +266 -162
  47. data/plugins/commands/cloud/version/create.rb +33 -28
  48. data/plugins/commands/cloud/version/delete.rb +35 -28
  49. data/plugins/commands/cloud/version/release.rb +35 -29
  50. data/plugins/commands/cloud/version/revoke.rb +36 -29
  51. data/plugins/commands/cloud/version/update.rb +29 -25
  52. data/plugins/commands/login/plugin.rb +0 -13
  53. data/plugins/guests/arch/cap/smb.rb +1 -1
  54. data/plugins/guests/darwin/cap/darwin_version.rb +40 -0
  55. data/plugins/guests/darwin/cap/mount_smb_shared_folder.rb +1 -1
  56. data/plugins/guests/darwin/cap/mount_vmware_shared_folder.rb +12 -2
  57. data/plugins/guests/darwin/plugin.rb +10 -0
  58. data/plugins/guests/debian/cap/change_host_name.rb +8 -7
  59. data/plugins/guests/linux/cap/mount_smb_shared_folder.rb +16 -41
  60. data/plugins/guests/linux/cap/mount_virtualbox_shared_folder.rb +6 -0
  61. data/plugins/guests/linux/cap/persist_mount_shared_folder.rb +18 -5
  62. data/plugins/guests/linux/cap/reboot.rb +10 -5
  63. data/plugins/guests/redhat/cap/change_host_name.rb +6 -2
  64. data/plugins/guests/suse/cap/change_host_name.rb +32 -11
  65. data/plugins/guests/windows/cap/reboot.rb +8 -4
  66. data/plugins/kernel_v2/config/cloud_init.rb +7 -0
  67. data/plugins/kernel_v2/config/disk.rb +1 -1
  68. data/plugins/kernel_v2/config/vm.rb +5 -4
  69. data/plugins/providers/hyperv/action.rb +1 -1
  70. data/plugins/providers/virtualbox/cap/mount_options.rb +1 -1
  71. data/plugins/providers/virtualbox/model/storage_controller_array.rb +4 -6
  72. data/plugins/providers/virtualbox/provider.rb +2 -1
  73. data/plugins/synced_folders/smb/cap/mount_options.rb +21 -1
  74. data/plugins/synced_folders/smb/plugin.rb +10 -0
  75. data/scripts/website_push_www.sh +1 -1
  76. data/vagrant.gemspec +5 -6
  77. data/version.txt +1 -1
  78. metadata +1202 -1595
  79. data/plugins/commands/login/client.rb +0 -253
  80. data/plugins/commands/login/command.rb +0 -137
  81. data/plugins/commands/login/errors.rb +0 -24
  82. data/plugins/commands/login/locales/en.yml +0 -49
  83. data/scripts/website_push_docs.sh +0 -40
@@ -231,15 +231,18 @@ module Vagrant
231
231
  # @param [Proc] callable
232
232
  # @param [Hash] extra_env Extra env for the action env.
233
233
  # @return [Hash] The resulting env
234
- def action_raw(name, callable, extra_env=nil)
234
+ def action_raw(name, callable, extra_env={})
235
+ if !extra_env.is_a?(Hash)
236
+ extra_env = {}
237
+ end
238
+
235
239
  # Run the action with the action runner on the environment
236
- env = {
240
+ env = {ui: @ui}.merge(extra_env).merge(
237
241
  raw_action_name: name,
238
242
  action_name: "machine_action_#{name}".to_sym,
239
243
  machine: self,
240
- machine_action: name,
241
- ui: @ui,
242
- }.merge(extra_env || {})
244
+ machine_action: name
245
+ )
243
246
  @env.action_runner.run(callable, env)
244
247
  end
245
248
 
@@ -263,6 +263,7 @@ module Vagrant
263
263
  #
264
264
  # @return [Hash]
265
265
  def find_by_prefix(prefix)
266
+ return if !prefix
266
267
  @machines.each do |uuid, data|
267
268
  return data.merge("id" => uuid) if uuid.start_with?(prefix)
268
269
  end
@@ -230,7 +230,8 @@ module Vagrant
230
230
  color_index = 0
231
231
 
232
232
  machines.each do |machine|
233
- if machine.state && machine.state.id != :not_created && !@env.machine_index.include?(machine.index_uuid)
233
+ if (machine.state && machine.state.id != :not_created &&
234
+ !machine.index_uuid.nil? && !@env.machine_index.include?(machine.index_uuid))
234
235
  machine.recover_machine(machine.state.id)
235
236
  end
236
237
 
@@ -125,6 +125,14 @@ module Vagrant
125
125
  Gem::Version.new(Vagrant::VERSION).prerelease?
126
126
  end
127
127
 
128
+ # This returns true/false if the Vagrant should allow prerelease
129
+ # versions when resolving plugin dependency constraints
130
+ #
131
+ # @return [Boolean]
132
+ def self.allow_prerelease_dependencies?
133
+ !!ENV["VAGRANT_ALLOW_PRERELEASE"]
134
+ end
135
+
128
136
  # This allows control over dependency resolution when installing
129
137
  # plugins into vagrant. When true, dependency libraries that Vagrant
130
138
  # core relies upon will be hard constraints.
@@ -29,8 +29,9 @@ module Vagrant
29
29
  "vagrantup.com".freeze
30
30
  ].freeze
31
31
 
32
- attr_reader :source
32
+ attr_accessor :source
33
33
  attr_reader :destination
34
+ attr_accessor :headers
34
35
 
35
36
  def initialize(source, destination, options=nil)
36
37
  options ||= {}
@@ -58,7 +59,7 @@ module Vagrant
58
59
  @ca_cert = options[:ca_cert]
59
60
  @ca_path = options[:ca_path]
60
61
  @continue = options[:continue]
61
- @headers = options[:headers]
62
+ @headers = Array(options[:headers])
62
63
  @insecure = options[:insecure]
63
64
  @ui = options[:ui]
64
65
  @client_cert = options[:client_cert]
@@ -17,7 +17,7 @@ module Vagrant
17
17
  Socket.tcp(host, port, connect_timeout: 0.1).close
18
18
  true
19
19
  rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, \
20
- Errno::ENETUNREACH, Errno::EACCES, Errno::ENOTCONN
20
+ Errno::ENETUNREACH, Errno::EACCES, Errno::ENOTCONN, Errno::EALREADY
21
21
  false
22
22
  end
23
23
  end
@@ -0,0 +1,92 @@
1
+ require 'mime/types'
2
+ require 'securerandom'
3
+
4
+ module Vagrant
5
+ module Util
6
+ module Mime
7
+ class Multipart
8
+
9
+ # @return [Array<String>] collection of content part of the multipart mime
10
+ attr_accessor :content
11
+
12
+ # @return [String] type of the content
13
+ attr_accessor :content_type
14
+
15
+ # @return [Hash] headers for the mime
16
+ attr_accessor :headers
17
+
18
+ # @param [String] (optional) mime content type
19
+ # @param [String] (optional) mime version
20
+ def initialize(content_type="multipart/mixed")
21
+ @content_id = "#{Time.now.to_i}@#{SecureRandom.alphanumeric(24)}.local"
22
+ @boundary = "Boundary_#{SecureRandom.alphanumeric(24)}"
23
+ @content_type = MIME::Types[content_type].first
24
+ @content = []
25
+ @headers = {
26
+ "Content-ID"=> "<#{@content_id}>",
27
+ "Content-Type"=> "#{content_type}; boundary=#{@boundary}",
28
+ }
29
+ end
30
+
31
+ # Add an entry to the multipart mime
32
+ #
33
+ # @param entry to add
34
+ def add(entry)
35
+ content << entry
36
+ end
37
+
38
+ # Output MimeEntity as a string
39
+ #
40
+ # @return [String] mime data
41
+ def to_s
42
+ output_string = ""
43
+ headers.each do |k, v|
44
+ output_string += "#{k}: #{v}\n"
45
+ end
46
+ output_string += "\n--#{@boundary}\n"
47
+ @content.each do |entry|
48
+ output_string += entry.to_s
49
+ output_string += "\n--#{@boundary}\n"
50
+ end
51
+ output_string
52
+ end
53
+ end
54
+
55
+ class Entity
56
+
57
+ # @return [String] entity content
58
+ attr_reader :content
59
+
60
+ # @return [String] type of the entity content
61
+ attr_reader :content_type
62
+
63
+ # @return [String] content disposition
64
+ attr_accessor :disposition
65
+
66
+ # @param [String] entity content
67
+ # @param [String] type of the entity content
68
+ def initialize(content, content_type)
69
+ if !MIME::Types.include?(content_type)
70
+ MIME::Types.add(MIME::Type.new(content_type))
71
+ end
72
+ @content = content
73
+ @content_type = MIME::Types[content_type].first
74
+ @content_id = "#{Time.now.to_i}@#{SecureRandom.alphanumeric(24)}.local"
75
+ end
76
+
77
+ # Output MimeEntity as a string
78
+ #
79
+ # @return [String] mime data
80
+ def to_s
81
+ output_string = "Content-ID: <#{@content_id}>\n"
82
+ output_string += "Content-Type: #{@content_type}\n"
83
+ if disposition
84
+ output_string += "Content-Disposition: #{@disposition}\n"
85
+ end
86
+ output_string += "\n#{content}"
87
+ output_string
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -554,6 +554,7 @@ module Vagrant
554
554
  # Get list of local mount paths that are DrvFs file systems
555
555
  #
556
556
  # @return [Array<String>]
557
+ # @todo(chrisroberts): Constantize types for check
557
558
  def wsl_drvfs_mounts
558
559
  if !defined?(@_wsl_drvfs_mounts)
559
560
  @_wsl_drvfs_mounts = []
@@ -561,7 +562,7 @@ module Vagrant
561
562
  result = Util::Subprocess.execute("mount")
562
563
  result.stdout.each_line do |line|
563
564
  info = line.match(MOUNT_PATTERN)
564
- if info && info[:type] == "drvfs"
565
+ if info && (info[:type] == "drvfs" || info[:type] == "9p")
565
566
  @_wsl_drvfs_mounts << info[:mount]
566
567
  end
567
568
  end
@@ -1,7 +1,7 @@
1
1
  require 'ostruct'
2
2
  require "pathname"
3
3
 
4
- require 'erubis'
4
+ require 'erubi'
5
5
 
6
6
  module Vagrant
7
7
  module Util
@@ -73,7 +73,7 @@ module Vagrant
73
73
  #
74
74
  # @return [String]
75
75
  def render_string
76
- Erubis::Eruby.new(template, trim: true).result(binding)
76
+ binding.eval(Erubi::Engine.new(template, trim: true).src)
77
77
  end
78
78
 
79
79
  # Returns the full path to the template, taking into account the gem directory
@@ -13,9 +13,11 @@ module Vagrant
13
13
  # a hand-rolled Ruby library, so we defer to its expertise.
14
14
  class Uploader
15
15
 
16
- # @param [String] destination - valid URL to upload file to
17
- # @param [String] file - location of file to upload on disk
18
- # @param [Hash] options
16
+ # @param [String] destination Valid URL to upload file to
17
+ # @param [String] file Location of file to upload on disk
18
+ # @param [Hash] options
19
+ # @option options [Vagrant::UI] :ui UI interface for output
20
+ # @option options [String, Symbol] :method Request method for upload
19
21
  def initialize(destination, file, options=nil)
20
22
  options ||= {}
21
23
  @logger = Log4r::Logger.new("vagrant::util::uploader")
@@ -27,6 +29,7 @@ module Vagrant
27
29
  if !@request_method
28
30
  @request_method = "PUT"
29
31
  end
32
+ @request_method = @request_method.to_s.upcase
30
33
  end
31
34
 
32
35
  def upload!
@@ -51,7 +54,7 @@ module Vagrant
51
54
  protected
52
55
 
53
56
  def build_options
54
- options = [@destination, "--request", @request_method, "--upload-file", @file]
57
+ options = [@destination, "--request", @request_method, "--upload-file", @file, "--fail"]
55
58
  return options
56
59
  end
57
60
 
@@ -27,6 +27,10 @@ module VagrantPlugins
27
27
  o.on("--check", "Only checks for a capability, does not execute") do |f|
28
28
  options[:check] = f
29
29
  end
30
+
31
+ o.on("-t", "--target=TARGET", "Target guest to run against (if applicable)") do |t|
32
+ options[:target] = t
33
+ end
30
34
  end
31
35
 
32
36
  # Parse the options
@@ -45,7 +49,7 @@ module VagrantPlugins
45
49
  if type == :host
46
50
  cap_host = @env.host
47
51
  else
48
- with_target_vms([]) do |vm|
52
+ with_target_vms(options[:target] || []) do |vm|
49
53
  cap_host = case type
50
54
  when :provider
51
55
  vm.provider
@@ -5,6 +5,8 @@ module VagrantPlugins
5
5
  module AuthCommand
6
6
  module Command
7
7
  class Login < Vagrant.plugin("2", :command)
8
+ include Util
9
+
8
10
  def execute
9
11
  options = {}
10
12
 
@@ -16,15 +18,10 @@ module VagrantPlugins
16
18
  o.on("-c", "--check", "Checks if currently logged in") do |c|
17
19
  options[:check] = c
18
20
  end
19
-
20
21
  o.on("-d", "--description DESCRIPTION", String, "Set description for the Vagrant Cloud token") do |d|
21
22
  options[:description] = d
22
23
  end
23
24
 
24
- o.on("-k", "--logout", "Logout from Vagrant Cloud") do |k|
25
- options[:logout] = k
26
- end
27
-
28
25
  o.on("-t", "--token TOKEN", String, "Set the Vagrant Cloud token") do |t|
29
26
  options[:token] = t
30
27
  end
@@ -37,26 +34,32 @@ module VagrantPlugins
37
34
  # Parse the options
38
35
  argv = parse_options(opts)
39
36
  return if !argv
37
+ if !argv.empty?
38
+ raise Vagrant::Errors::CLIInvalidUsage,
39
+ help: opts.help.chomp
40
+ end
40
41
 
41
- @client = Client.new(@env)
42
- @client.username_or_email = options[:login]
42
+ client = Client.new(@env)
43
+ client.username_or_email = options[:login]
43
44
 
44
45
  # Determine what task we're actually taking based on flags
45
46
  if options[:check]
46
- return execute_check
47
- elsif options[:logout]
48
- return execute_logout
47
+ return execute_check(client)
49
48
  elsif options[:token]
50
- return execute_token(options[:token])
49
+ return execute_token(client, options[:token])
51
50
  else
52
- @client = VagrantPlugins::CloudCommand::Util.client_login(@env, options)
51
+ if client.logged_in?
52
+ @env.ui.success(I18n.t("cloud_command.check_logged_in"))
53
+ else
54
+ client_login(@env, options.slice(:login, :description))
55
+ end
53
56
  end
54
57
 
55
58
  0
56
59
  end
57
60
 
58
- def execute_check
59
- if @client.logged_in?
61
+ def execute_check(client)
62
+ if client.logged_in?
60
63
  @env.ui.success(I18n.t("cloud_command.check_logged_in"))
61
64
  return 0
62
65
  else
@@ -65,17 +68,11 @@ module VagrantPlugins
65
68
  end
66
69
  end
67
70
 
68
- def execute_logout
69
- @client.clear_token
70
- @env.ui.success(I18n.t("cloud_command.logged_out"))
71
- return 0
72
- end
73
-
74
- def execute_token(token)
75
- @client.store_token(token)
71
+ def execute_token(client, token)
72
+ client.store_token(token)
76
73
  @env.ui.success(I18n.t("cloud_command.token_saved"))
77
74
 
78
- if @client.logged_in?
75
+ if client.logged_in?
79
76
  @env.ui.success(I18n.t("cloud_command.check_logged_in"))
80
77
  return 0
81
78
  else
@@ -9,15 +9,9 @@ module VagrantPlugins
9
9
  options = {}
10
10
 
11
11
  opts = OptionParser.new do |o|
12
- o.banner = "Usage: vagrant cloud auth logout [options]"
12
+ o.banner = "Usage: vagrant cloud auth logout"
13
13
  o.separator ""
14
14
  o.separator "Log out of Vagrant Cloud"
15
- o.separator ""
16
- o.separator "Options:"
17
- o.separator ""
18
- o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |l|
19
- options[:login] = l
20
- end
21
15
  end
22
16
 
23
17
  # Parse the options
@@ -28,9 +22,7 @@ module VagrantPlugins
28
22
  help: opts.help.chomp
29
23
  end
30
24
 
31
- # Initializes client and deletes token on disk
32
- @client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
33
-
25
+ @client = Client.new(@env)
34
26
  @client.clear_token
35
27
  @env.ui.success(I18n.t("cloud_command.logged_out"))
36
28
  return 0
@@ -0,0 +1,57 @@
1
+ require "cgi"
2
+ require "uri"
3
+
4
+ require "vagrant/util/credential_scrubber"
5
+ require_relative "./add_authentication"
6
+
7
+ require Vagrant.source_root.join("plugins/commands/cloud/client/client")
8
+
9
+ # Similar to AddAuthentication this middleware will add authentication for interacting
10
+ # with Vagrant cloud. It does this by adding Authentication headers to a
11
+ # Vagrant::Util::Downloader object.
12
+ module VagrantPlugins
13
+ module CloudCommand
14
+ class AddDownloaderAuthentication < AddAuthentication
15
+
16
+ @@logger = Log4r::Logger.new("vagrant::clout::add_download_authentication")
17
+
18
+ def call(env)
19
+ client = Client.new(env[:env])
20
+ token = client.token
21
+ Vagrant::Util::CredentialScrubber.sensitive(token)
22
+
23
+ begin
24
+ target_url = URI.parse(env[:downloader].source)
25
+ if target_url.host != TARGET_HOST && REPLACEMENT_HOSTS.include?(target_url.host)
26
+ target_url.host = TARGET_HOST
27
+ env[:downloader].source = target_url.to_s
28
+ end
29
+ rescue URI::Error
30
+ # if there is an error, use current target_url
31
+ end
32
+
33
+ server_uri = URI.parse(Vagrant.server_url.to_s)
34
+ if token && !server_uri.host.to_s.empty?
35
+ if target_url.host == server_uri.host
36
+ if server_uri.host != TARGET_HOST && !self.class.custom_host_notified?
37
+ env[:ui].warn(I18n.t("cloud_command.middleware.authentication.different_target",
38
+ custom_host: server_uri.host, known_host: TARGET_HOST) + "\n")
39
+ sleep CUSTOM_HOST_NOTIFY_WAIT
40
+ self.class.custom_host_notified!
41
+ end
42
+
43
+ if Array(env[:downloader].headers).any? { |h| h.include?("Authorization") }
44
+ @@logger.info("Not adding an authentication header, one already found")
45
+ else
46
+ env[:downloader].headers << "Authorization: Bearer #{token}"
47
+ end
48
+ end
49
+
50
+ env[:downloader]
51
+ end
52
+
53
+ @app.call(env)
54
+ end.freeze
55
+ end
56
+ end
57
+ end
@@ -5,19 +5,15 @@ module VagrantPlugins
5
5
  module AuthCommand
6
6
  module Command
7
7
  class Whoami < Vagrant.plugin("2", :command)
8
+ include Util
9
+
8
10
  def execute
9
11
  options = {}
10
12
 
11
13
  opts = OptionParser.new do |o|
12
- o.banner = "Usage: vagrant cloud auth whoami [options] [token]"
14
+ o.banner = "Usage: vagrant cloud auth whoami [token]"
13
15
  o.separator ""
14
16
  o.separator "Display currently logged in user"
15
- o.separator ""
16
- o.separator "Options:"
17
- o.separator ""
18
- o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |l|
19
- options[:login] = l
20
- end
21
17
  end
22
18
 
23
19
  # Parse the options
@@ -28,28 +24,30 @@ module VagrantPlugins
28
24
  help: opts.help.chomp
29
25
  end
30
26
 
31
- @client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:login])
32
-
33
27
  if argv.first
34
28
  token = argv.first
35
29
  else
36
- token = @client.token
30
+ client = Client.new(@env)
31
+ token = client.token
37
32
  end
38
33
 
39
- whoami(token, options[:username])
34
+ whoami(token)
40
35
  end
41
36
 
42
- def whoami(access_token, username)
43
- server_url = VagrantPlugins::CloudCommand::Util.api_server_url
44
- account = VagrantPlugins::CloudCommand::Util.account(username, access_token, server_url)
45
-
37
+ def whoami(access_token)
38
+ if access_token.to_s.empty?
39
+ @env.ui.error(I18n.t("cloud_command.check_not_logged_in"))
40
+ return 1
41
+ end
46
42
  begin
47
- success = account.validate_token
48
- user = success["user"]["username"]
49
- @env.ui.success("Currently logged in as #{user}")
43
+ account = VagrantCloud::Account.new(
44
+ custom_server: api_server_url,
45
+ access_token: access_token
46
+ )
47
+ @env.ui.success("Currently logged in as #{account.username}")
50
48
  return 0
51
- rescue VagrantCloud::ClientError => e
52
- @env.ui.error(I18n.t("cloud_command.errors.whoami.read_error", org: username))
49
+ rescue VagrantCloud::Error::ClientError => e
50
+ @env.ui.error(I18n.t("cloud_command.errors.whoami.read_error"))
53
51
  @env.ui.error(e)
54
52
  return 1
55
53
  end