Blob Blame History Raw
From 5003bb6e150dd7f812969acfd45aac3b92ce5cae Mon Sep 17 00:00:00 2001
From: Chris Roberts <croberts@hashicorp.com>
Date: Mon, 10 Aug 2020 11:11:30 -0700
Subject: [PATCH 1/3] Updates to address all Ruby deprecations and warnings

This includes updates for resolving all warnings provided by Ruby
for deprecations and/or removed methods. It also enables support
for Ruby 2.7 in the specification constraint as all 2.7 related
warnings are resolved with this changeset.
---
 lib/vagrant/action/builder.rb                 |  62 ++++++++---
 lib/vagrant/action/builtin/box_add.rb         |  11 +-
 .../action/builtin/mixin_synced_folders.rb    |   2 +-
 lib/vagrant/action/hook.rb                    |  83 +++++++++-----
 lib/vagrant/action/warden.rb                  |  30 +++++-
 lib/vagrant/box.rb                            |   9 +-
 lib/vagrant/bundler.rb                        |   7 +-
 lib/vagrant/errors.rb                         |   2 +-
 lib/vagrant/machine_index.rb                  |   2 +-
 lib/vagrant/ui.rb                             |   2 +-
 lib/vagrant/util/downloader.rb                |   5 +-
 plugins/communicators/winssh/communicator.rb  |   2 +-
 plugins/kernel_v2/config/vm.rb                |   2 +-
 .../plugins/commands/box/command/add_test.rb  |   4 +-
 .../commands/box/command/outdated_test.rb     |   2 +-
 .../communicators/ssh/communicator_test.rb    |   2 +-
 .../windows/cap/change_host_name_test.rb      |   4 +-
 .../providers/docker/driver_compose_test.rb   |   4 +-
 .../virtualbox/action/network_test.rb         |  14 +--
 .../chef/config/chef_apply_test.rb            |   2 +-
 test/unit/vagrant/action/builder_test.rb      |  26 ++---
 .../vagrant/action/builtin/box_add_test.rb    |  46 ++++----
 test/unit/vagrant/action/hook_test.rb         | 102 ++++++++++++------
 test/unit/vagrant/plugin/manager_test.rb      |   4 +-
 test/unit/vagrant/ui_test.rb                  |  14 +--
 vagrant.gemspec                               |   2 +-
 26 files changed, 286 insertions(+), 159 deletions(-)

diff --git a/lib/vagrant/action/builder.rb b/lib/vagrant/action/builder.rb
index 074d512b12..7e25d185b8 100644
--- a/lib/vagrant/action/builder.rb
+++ b/lib/vagrant/action/builder.rb
@@ -17,6 +17,11 @@ module Action
     #     Vagrant::Action.run(app)
     #
     class Builder
+      # Container for Action arguments
+      MiddlewareArguments = Struct.new(:parameters, :block, :keywords, keyword_init: true)
+      # Item within the stack
+      StackItem = Struct.new(:middleware, :arguments, keyword_init: true)
+
       # This is the stack of middlewares added. This should NOT be used
       # directly.
       #
@@ -28,8 +33,8 @@ class Builder
       # see {#use} instead.
       #
       # @return [Builder]
-      def self.build(middleware, *args, &block)
-        new.use(middleware, *args, &block)
+      def self.build(middleware, *args, **keywords, &block)
+        new.use(middleware, *args, **keywords, &block)
       end
 
       def initialize
@@ -58,12 +63,21 @@ def flatten
       # of the middleware.
       #
       # @param [Class] middleware The middleware class
-      def use(middleware, *args, &block)
+      def use(middleware, *args, **keywords, &block)
+        item = StackItem.new(
+          middleware: middleware,
+          arguments: MiddlewareArguments.new(
+            parameters: args,
+            keywords: keywords,
+            block: block
+          )
+        )
+
         if middleware.kind_of?(Builder)
           # Merge in the other builder's stack into our own
           self.stack.concat(middleware.stack)
         else
-          self.stack << [middleware, args, block]
+          self.stack << item
         end
 
         self
@@ -71,8 +85,22 @@ def use(middleware, *args, &block)
 
       # Inserts a middleware at the given index or directly before the
       # given middleware object.
-      def insert(index, middleware, *args, &block)
-        index = self.index(index) unless index.is_a?(Integer)
+      def insert(idx_or_item, middleware, *args, **keywords, &block)
+        item = StackItem.new(
+          middleware: middleware,
+          arguments: MiddlewareArguments.new(
+            parameters: args,
+            keywords: keywords,
+            block: block
+          )
+        )
+
+        if idx_or_item.is_a?(Integer)
+          index = idx_or_item
+        else
+          index = self.index(idx_or_item)
+        end
+
         raise "no such middleware to insert before: #{index.inspect}" unless index
 
         if middleware.kind_of?(Builder)
@@ -80,27 +108,32 @@ def insert(index, middleware, *args, &block)
             stack.insert(index, stack_item)
           end
         else
-          stack.insert(index, [middleware, args, block])
+          stack.insert(index, item)
         end
       end
 
       alias_method :insert_before, :insert
 
       # Inserts a middleware after the given index or middleware object.
-      def insert_after(index, middleware, *args, &block)
-        index = self.index(index) unless index.is_a?(Integer)
+      def insert_after(idx_or_item, middleware, *args, **keywords, &block)
+        if idx_or_item.is_a?(Integer)
+          index = idx_or_item
+        else
+          index = self.index(idx_or_item)
+        end
+
         raise "no such middleware to insert after: #{index.inspect}" unless index
         insert(index + 1, middleware, *args, &block)
       end
 
       # Replaces the given middlware object or index with the new
       # middleware.
-      def replace(index, middleware, *args, &block)
+      def replace(index, middleware, *args, **keywords, &block)
         if index.is_a?(Integer)
           delete(index)
-          insert(index, middleware, *args, &block)
+          insert(index, middleware, *args, **keywords, &block)
         else
-          insert_before(index, middleware, *args, &block)
+          insert_before(index, middleware, *args, **keywords, &block)
           delete(index)
         end
       end
@@ -123,8 +156,9 @@ def call(env)
       def index(object)
         stack.each_with_index do |item, i|
           return i if item == object
-          return i if item[0] == object
-          return i if item[0].respond_to?(:name) && item[0].name == object
+          return i if item.middleware == object
+          return i if item.middleware.respond_to?(:name) &&
+            item.middleware.name == object
         end
 
         nil
diff --git a/lib/vagrant/action/builtin/box_add.rb b/lib/vagrant/action/builtin/box_add.rb
index dac1fefa52..a635d77cec 100644
--- a/lib/vagrant/action/builtin/box_add.rb
+++ b/lib/vagrant/action/builtin/box_add.rb
@@ -25,6 +25,7 @@ class BoxAdd
         def initialize(app, env)
           @app    = app
           @logger = Log4r::Logger.new("vagrant::action::builtin::box_add")
+          @parser = URI::RFC2396_Parser.new
         end
 
         def call(env)
@@ -44,7 +45,7 @@ def call(env)
             u = u.gsub("\\", "/")
             if Util::Platform.windows? && u =~ /^[a-z]:/i
               # On Windows, we need to be careful about drive letters
-              u = "file:///#{URI.escape(u)}"
+              u = "file:///#{@parser.escape(u)}"
             end
 
             if u =~ /^[a-z0-9]+:.*$/i && !u.start_with?("file://")
@@ -53,9 +54,9 @@ def call(env)
             end
 
             # Expand the path and try to use that, if possible
-            p = File.expand_path(URI.unescape(u.gsub(/^file:\/\//, "")))
+            p = File.expand_path(@parser.unescape(u.gsub(/^file:\/\//, "")))
             p = Util::Platform.cygwin_windows_path(p)
-            next "file://#{URI.escape(p.gsub("\\", "/"))}" if File.file?(p)
+            next "file://#{@parser.escape(p.gsub("\\", "/"))}" if File.file?(p)
 
             u
           end
@@ -434,7 +435,7 @@ def downloader(url, env, **opts)
           downloader_options[:ui] = env[:ui] if opts[:ui]
           downloader_options[:location_trusted] = env[:box_download_location_trusted]
           downloader_options[:box_extra_download_options] = env[:box_extra_download_options]
-          
+
           Util::Downloader.new(url, temp_path, downloader_options)
         end
 
@@ -495,7 +496,7 @@ def metadata_url?(url, env)
             url ||= uri.opaque
             #7570 Strip leading slash left in front of drive letter by uri.path
             Util::Platform.windows? && url.gsub!(/^\/([a-zA-Z]:)/, '\1')
-            url = URI.unescape(url)
+            url = @parser.unescape(url)
 
             begin
               File.open(url, "r") do |f|
diff --git a/lib/vagrant/action/builtin/mixin_synced_folders.rb b/lib/vagrant/action/builtin/mixin_synced_folders.rb
index c2d8aabb5a..1209f5d690 100644
--- a/lib/vagrant/action/builtin/mixin_synced_folders.rb
+++ b/lib/vagrant/action/builtin/mixin_synced_folders.rb
@@ -72,7 +72,7 @@ def plugins
         #
         # @param [Machine] machine The machine that the folders belong to
         # @param [Hash] folders The result from a {#synced_folders} call.
-        def save_synced_folders(machine, folders, **opts)
+        def save_synced_folders(machine, folders, opts={})
           if opts[:merge]
             existing = cached_synced_folders(machine)
             if existing
diff --git a/lib/vagrant/action/hook.rb b/lib/vagrant/action/hook.rb
index 3ab3080859..7270e6a33f 100644
--- a/lib/vagrant/action/hook.rb
+++ b/lib/vagrant/action/hook.rb
@@ -7,23 +7,23 @@ class Hook
       # This is a hash of the middleware to prepend to a certain
       # other middleware.
       #
-      # @return [Hash<Class, Array<Class>>]
+      # @return [Hash<Class, Array<Builder::StackItem>>]
       attr_reader :before_hooks
 
       # This is a hash of the middleware to append to a certain other
       # middleware.
       #
-      # @return [Hash<Class, Array<Class>>]
+      # @return [Hash<Class, Array<Builder::StackItem>>]
       attr_reader :after_hooks
 
       # This is a list of the hooks to just prepend to the beginning
       #
-      # @return [Array<Class>]
+      # @return [Array<Builder::StackItem>]
       attr_reader :prepend_hooks
 
       # This is a list of the hooks to just append to the end
       #
-      # @return [Array<Class>]
+      # @return [Array<Builder::StackItem>]
       attr_reader :append_hooks
 
       def initialize
@@ -37,16 +37,32 @@ def initialize
       #
       # @param [Class] existing The existing middleware.
       # @param [Class] new The new middleware.
-      def before(existing, new, *args, &block)
-        @before_hooks[existing] << [new, args, block]
+      def before(existing, new, *args, **keywords, &block)
+        item = Builder::StackItem.new(
+          middleware: new,
+          arguments: Builder::MiddlewareArguments.new(
+            parameters: args,
+            keywords: keywords,
+            block: block
+          )
+        )
+        @before_hooks[existing] << item
       end
 
       # Add a middleware after an existing middleware.
       #
       # @param [Class] existing The existing middleware.
       # @param [Class] new The new middleware.
-      def after(existing, new, *args, &block)
-        @after_hooks[existing] << [new, args, block]
+      def after(existing, new, *args, **keywords, &block)
+        item = Builder::StackItem.new(
+          middleware: new,
+          arguments: Builder::MiddlewareArguments.new(
+            parameters: args,
+            keywords: keywords,
+            block: block
+          )
+        )
+        @after_hooks[existing] << item
       end
 
       # Append a middleware to the end of the stack. Note that if the
@@ -54,15 +70,31 @@ def after(existing, new, *args, &block)
       # be run.
       #
       # @param [Class] new The middleware to append.
-      def append(new, *args, &block)
-        @append_hooks << [new, args, block]
+      def append(new, *args, **keywords, &block)
+        item = Builder::StackItem.new(
+          middleware: new,
+          arguments: Builder::MiddlewareArguments.new(
+            parameters: args,
+            keywords: keywords,
+            block: block
+          )
+        )
+        @append_hooks << item
       end
 
       # Prepend a middleware to the beginning of the stack.
       #
       # @param [Class] new The new middleware to prepend.
-      def prepend(new, *args, &block)
-        @prepend_hooks << [new, args, block]
+      def prepend(new, *args, **keywords, &block)
+        item = Builder::StackItem.new(
+          middleware: new,
+          arguments: Builder::MiddlewareArguments.new(
+            parameters: args,
+            keywords: keywords,
+            block: block
+          )
+        )
+        @prepend_hooks << item
       end
 
       # @return [Boolean]
@@ -77,27 +109,28 @@ def empty?
       # called directly.
       #
       # @param [Builder] builder
-      def apply(builder, options=nil)
-        options ||= {}
-
+      def apply(builder, options={})
         if !options[:no_prepend_or_append]
           # Prepends first
-          @prepend_hooks.each do |klass, args, block|
+          @prepend_hooks.each do |item|
             if options[:root]
               idx = builder.index(options[:root])
             else
               idx = 0
             end
-            builder.insert(idx, klass, *args, &block)
+            builder.insert(idx, item.middleware, *item.arguments.parameters,
+              **item.arguments.keywords, &item.arguments.block)
           end
 
           # Appends
-          @append_hooks.each do |klass, args, block|
+          @append_hooks.each do |item|
             if options[:root]
               idx = builder.index(options[:root])
-              builder.insert(idx + 1, klass, *args, &block)
+              builder.insert(idx + 1, item.middleware, *item.arguments.parameters,
+                **item.arguments.keywords, &item.arguments.block)
             else
-              builder.use(klass, *args, &block)
+              builder.use(item.middleware, *item.arguments.parameters,
+                **item.arguments.keywords, &item.arguments.block)
             end
           end
         end
@@ -106,8 +139,9 @@ def apply(builder, options=nil)
         @before_hooks.each do |key, list|
           next if !builder.index(key)
 
-          list.each do |klass, args, block|
-            builder.insert_before(key, klass, *args, &block)
+          list.each do |item|
+            builder.insert_before(key, item.middleware, *item.arguments.parameters,
+              **item.arguments.keywords, &item.arguments.block)
           end
         end
 
@@ -115,8 +149,9 @@ def apply(builder, options=nil)
         @after_hooks.each do |key, list|
           next if !builder.index(key)
 
-          list.each do |klass, args, block|
-            builder.insert_after(key, klass, *args, &block)
+          list.each do |item|
+            builder.insert_after(key, item.middleware, *item.arguments.parameters,
+              **item.arguments.keywords, &item.arguments.block)
           end
         end
       end
diff --git a/lib/vagrant/action/warden.rb b/lib/vagrant/action/warden.rb
index 828e317fcc..931ce87fc8 100644
--- a/lib/vagrant/action/warden.rb
+++ b/lib/vagrant/action/warden.rb
@@ -92,14 +92,34 @@ def recover(env)
       # A somewhat confusing function which simply initializes each
       # middleware properly to call the next middleware in the sequence.
       def finalize_action(action, env)
-        klass, args, block = action
+        if action.is_a?(Builder::StackItem)
+          klass = action.middleware
+          args = action.arguments.parameters
+          keywords = action.arguments.keywords
+          block = action.arguments.block
+        else
+          klass = action
+          args = []
+          keywords = {}
+        end
 
-        # Default the arguments to an empty array. Otherwise in Ruby 1.8
-        # a `nil` args will actually pass `nil` into the class.
-        args ||= []
+        args = nil if args.empty?
+        keywords = nil if keywords.empty?
 
         if klass.is_a?(Class)
-          klass.new(self, env, *args, &block)
+          # NOTE: We need to detect if we are passing args and/or
+          #       keywords and do it explicitly. Earlier versions
+          #       are not as lax about splatting keywords when the
+          #       target method is not expecting them.
+          if args && keywords
+            klass.new(self, env, *args, **keywords, &block)
+          elsif args
+            klass.new(self, env, *args, &block)
+          elsif keywords
+            klass.new(self, env, **keywords, &block)
+          else
+            klass.new(self, env, &block)
+          end
         elsif klass.respond_to?(:call)
           # Make it a lambda which calls the item then forwards
           # up the chain
diff --git a/lib/vagrant/box.rb b/lib/vagrant/box.rb
index 2f12775f5e..0ee5d29f39 100644
--- a/lib/vagrant/box.rb
+++ b/lib/vagrant/box.rb
@@ -57,12 +57,13 @@ class Box
     # @param [Symbol] provider The provider that this box implements.
     # @param [Pathname] directory The directory where this box exists on
     #   disk.
-    def initialize(name, provider, version, directory, **opts)
+    # @param [String] metadata_url Metadata URL for box
+    def initialize(name, provider, version, directory, metadata_url: nil)
       @name      = name
       @version   = version
       @provider  = provider
       @directory = directory
-      @metadata_url = opts[:metadata_url]
+      @metadata_url = metadata_url
 
       metadata_file = directory.join("metadata.json")
       raise Errors::BoxMetadataFileNotFound, name: @name if !metadata_file.file?
@@ -120,7 +121,7 @@ def in_use?(index)
     #
     # @param [Hash] download_options Options to pass to the downloader.
     # @return [BoxMetadata]
-    def load_metadata(**download_options)
+    def load_metadata(download_options={})
       tf = Tempfile.new("vagrant-load-metadata")
       tf.close
 
@@ -132,7 +133,7 @@ def load_metadata(**download_options)
       end
 
       opts = { headers: ["Accept: application/json"] }.merge(download_options)
-      Util::Downloader.new(url, tf.path, **opts).download!
+      Util::Downloader.new(url, tf.path, opts).download!
       BoxMetadata.new(File.open(tf.path, "r"))
     rescue Errors::DownloaderError => e
       raise Errors::BoxMetadataDownloadError,
diff --git a/lib/vagrant/bundler.rb b/lib/vagrant/bundler.rb
index f4d2a02396..336ac1e057 100644
--- a/lib/vagrant/bundler.rb
+++ b/lib/vagrant/bundler.rb
@@ -632,7 +632,12 @@ def vagrant_internal_specs
       self_spec.runtime_dependencies.each { |d| gem d.name, *d.requirement.as_list }
       # discover all the gems we have available
       list = {}
-      directories = [Gem::Specification.default_specifications_dir]
+      if Gem.respond_to?(:default_specifications_dir)
+        spec_dir = Gem.default_specifications_dir
+      else
+        spec_dir = Gem::Specification.default_specifications_dir
+      end
+      directories = [spec_dir]
       Gem::Specification.find_all{true}.each do |spec|
         list[spec.full_name] = spec
       end
diff --git a/lib/vagrant/errors.rb b/lib/vagrant/errors.rb
index e41c0f72eb..782615bc45 100644
--- a/lib/vagrant/errors.rb
+++ b/lib/vagrant/errors.rb
@@ -100,7 +100,7 @@ def status_code; 1; end
 
       def translate_error(opts)
         return nil if !opts[:_key]
-        I18n.t("#{opts[:_namespace]}.#{opts[:_key]}", opts)
+        I18n.t("#{opts[:_namespace]}.#{opts[:_key]}", **opts)
       end
     end
 
diff --git a/lib/vagrant/machine_index.rb b/lib/vagrant/machine_index.rb
index eec0062923..802f6dfa73 100644
--- a/lib/vagrant/machine_index.rb
+++ b/lib/vagrant/machine_index.rb
@@ -452,7 +452,7 @@ def valid?(home_path)
       # Creates a {Vagrant::Environment} for this entry.
       #
       # @return [Vagrant::Environment]
-      def vagrant_env(home_path, **opts)
+      def vagrant_env(home_path, opts={})
         Vagrant::Util::SilenceWarnings.silence! do
           Environment.new({
             cwd: @vagrantfile_path,
diff --git a/lib/vagrant/ui.rb b/lib/vagrant/ui.rb
index a521fd105a..b1a80a49f5 100644
--- a/lib/vagrant/ui.rb
+++ b/lib/vagrant/ui.rb
@@ -227,7 +227,7 @@ def clear_line
 
       # This method handles actually outputting a message of a given type
       # to the console.
-      def say(type, message, **opts)
+      def say(type, message, opts={})
         defaults = { new_line: true, prefix: true }
         opts     = defaults.merge(@opts).merge(opts)
 
diff --git a/lib/vagrant/util/downloader.rb b/lib/vagrant/util/downloader.rb
index 26c0d7067e..bf966ecafe 100644
--- a/lib/vagrant/util/downloader.rb
+++ b/lib/vagrant/util/downloader.rb
@@ -1,3 +1,4 @@
+require "cgi"
 require "uri"
 
 require "log4r"
@@ -42,8 +43,8 @@ def initialize(source, destination, options=nil)
         begin
           url = URI.parse(@source)
           if url.scheme && url.scheme.start_with?("http") && url.user
-            auth = "#{URI.unescape(url.user)}"
-            auth += ":#{URI.unescape(url.password)}" if url.password
+            auth = "#{CGI.unescape(url.user)}"
+            auth += ":#{CGI.unescape(url.password)}" if url.password
             url.user = nil
             url.password = nil
             options[:auth] ||= auth
diff --git a/plugins/communicators/winssh/communicator.rb b/plugins/communicators/winssh/communicator.rb
index 1239049d21..3ea012f618 100644
--- a/plugins/communicators/winssh/communicator.rb
+++ b/plugins/communicators/winssh/communicator.rb
@@ -217,7 +217,7 @@ def sftp_connect
       # The WinSSH communicator connection provides isolated modification
       # to the generated connection instances. This modification forces
       # all provided commands to run within powershell
-      def connect(*args)
+      def connect(**opts)
         connection = nil
         super { |c| connection = c }
 
diff --git a/plugins/kernel_v2/config/vm.rb b/plugins/kernel_v2/config/vm.rb
index acc6df6a6b..f6fb07c000 100644
--- a/plugins/kernel_v2/config/vm.rb
+++ b/plugins/kernel_v2/config/vm.rb
@@ -380,7 +380,7 @@ def provision(name, **options, &block)
         prov.preserve_order = !!options.delete(:preserve_order) if \
           options.key?(:preserve_order)
         prov.run = options.delete(:run) if options.key?(:run)
-        prov.add_config(options, &block)
+        prov.add_config(**options, &block)
         nil
       end
 
@@ -435,7 +435,7 @@ def disk(type, **options, &block)
         disk_config.set_options(options)
 
         # Add provider config
-        disk_config.add_provider_config(provider_options, &block)
+        disk_config.add_provider_config(**provider_options, &block)
 
         if !Vagrant::Util::Experimental.feature_enabled?("disks")
           @logger.warn("Disk config defined, but experimental feature is not enabled. To use this feature, enable it with the experimental flag `disks`. Disk will not be added to internal config, and will be ignored.")
diff --git a/test/unit/plugins/commands/box/command/add_test.rb b/test/unit/plugins/commands/box/command/add_test.rb
index 77cf7d1acd..ea3c5f3317 100644
--- a/test/unit/plugins/commands/box/command/add_test.rb
+++ b/test/unit/plugins/commands/box/command/add_test.rb
@@ -32,7 +32,7 @@
     let(:argv) { ["foo"] }
 
     it "executes the runner with the proper actions" do
-      expect(action_runner).to receive(:run).with(any_args) { |action, **opts|
+      expect(action_runner).to receive(:run).with(any_args) { |action, opts|
         expect(opts[:box_name]).to be_nil
         expect(opts[:box_url]).to eq("foo")
         true
@@ -46,7 +46,7 @@
     let(:argv) { ["foo", "bar"] }
 
     it "executes the runner with the proper actions" do
-      expect(action_runner).to receive(:run).with(any_args) { |action, **opts|
+      expect(action_runner).to receive(:run).with(any_args) { |action, opts|
         expect(opts[:box_name]).to eq("foo")
         expect(opts[:box_url]).to eq("bar")
         true
diff --git a/test/unit/plugins/commands/box/command/outdated_test.rb b/test/unit/plugins/commands/box/command/outdated_test.rb
index c395171aaa..6b52614cdc 100644
--- a/test/unit/plugins/commands/box/command/outdated_test.rb
+++ b/test/unit/plugins/commands/box/command/outdated_test.rb
@@ -24,7 +24,7 @@
     let(:argv) { ["--force"] }
 
     it "passes along the force update option" do
-      expect(action_runner).to receive(:run).with(any_args) { |action, **opts|
+      expect(action_runner).to receive(:run).with(any_args) { |action, opts|
         expect(opts[:box_outdated_force]).to be_truthy
         true
       }
diff --git a/test/unit/plugins/communicators/ssh/communicator_test.rb b/test/unit/plugins/communicators/ssh/communicator_test.rb
index 3686fa6a04..f04ddf673b 100644
--- a/test/unit/plugins/communicators/ssh/communicator_test.rb
+++ b/test/unit/plugins/communicators/ssh/communicator_test.rb
@@ -120,7 +120,7 @@
       context "when printing message to the user" do
         before do
           allow(machine).to receive(:ssh_info).
-            and_return(host: '10.1.2.3', port: 22).ordered
+            and_return(host: '10.1.2.3', port: 22)
           allow(communicator).to receive(:connect)
           allow(communicator).to receive(:ready?).and_return(true)
         end
diff --git a/test/unit/plugins/guests/windows/cap/change_host_name_test.rb b/test/unit/plugins/guests/windows/cap/change_host_name_test.rb
index a75412ae22..07ef98461d 100644
--- a/test/unit/plugins/guests/windows/cap/change_host_name_test.rb
+++ b/test/unit/plugins/guests/windows/cap/change_host_name_test.rb
@@ -6,7 +6,8 @@
   let(:described_class) do
     VagrantPlugins::GuestWindows::Plugin.components.guest_capabilities[:windows].get(:change_host_name)
   end
-  let(:machine) { double("machine") }
+  let(:machine) { double("machine", guest: guest) }
+  let(:guest) { double("guest") }
   let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) }
 
   before do
@@ -34,7 +35,6 @@
         'if (!([System.Net.Dns]::GetHostName() -eq \'newhostname\')) { exit 0 } exit 1',
         exit_code: 0)
       communicator.stub_command(rename_script, exit_code: 0)
-      allow(machine).to receive(:guest)
       allow(machine.guest).to receive(:capability)
       allow(machine.guest).to receive(:capability?)
       described_class.change_host_name_and_wait(machine, 'newhostname', 0)
diff --git a/test/unit/plugins/providers/docker/driver_compose_test.rb b/test/unit/plugins/providers/docker/driver_compose_test.rb
index 9606de5ddb..d8b3006cab 100644
--- a/test/unit/plugins/providers/docker/driver_compose_test.rb
+++ b/test/unit/plugins/providers/docker/driver_compose_test.rb
@@ -32,7 +32,7 @@
   end
   let(:data_directory){ double("data-directory", join: composition_path) }
   let(:local_data_path){ double("local-data-path") }
-  let(:compose_execute_up){ ["docker-compose", "-f", "docker-compose.yml", "-p", "cwd", "up", "--remove-orphans", "-d", {}] }
+  let(:compose_execute_up){ ["docker-compose", "-f", "docker-compose.yml", "-p", "cwd", "up", "--remove-orphans", "-d", any_args] }
 
 
   subject{ described_class.new(machine) }
@@ -277,7 +277,7 @@
       before { allow(subject).to receive(:created?).and_return(true) }
 
       it 'removes the container' do
-        expect(subject).to receive(:execute).with("docker-compose", "-f", "docker-compose.yml", "-p", "cwd", "rm", "-f", "docker_1", {})
+        expect(subject).to receive(:execute).with("docker-compose", "-f", "docker-compose.yml", "-p", "cwd", "rm", "-f", "docker_1", any_args)
         subject.rm(cid)
       end
     end
diff --git a/test/unit/plugins/providers/virtualbox/action/network_test.rb b/test/unit/plugins/providers/virtualbox/action/network_test.rb
index 34ed17d94e..69b30b3390 100644
--- a/test/unit/plugins/providers/virtualbox/action/network_test.rb
+++ b/test/unit/plugins/providers/virtualbox/action/network_test.rb
@@ -44,7 +44,7 @@
 
   it "creates a host-only interface with an IPv6 address <prefix>:1" do
     guest = double("guest")
-    machine.config.vm.network 'private_network', { type: :static, ip: 'dead:beef::100' }
+    machine.config.vm.network 'private_network', type: :static, ip: 'dead:beef::100'
     #allow(driver).to receive(:read_bridged_interfaces) { [] }
     allow(driver).to receive(:read_host_only_interfaces) { [] }
     #allow(driver).to receive(:read_dhcp_servers) { [] }
@@ -71,19 +71,11 @@
   end
 
   it "raises the appropriate error when provided with an invalid IP address" do
-    guest = double("guest")
-    machine.config.vm.network 'private_network', { ip: '192.168.33.06' }
+    machine.config.vm.network 'private_network', ip: '192.168.33.06'
 
     expect{ subject.call(env) }.to raise_error(Vagrant::Errors::NetworkAddressInvalid)
   end
 
-  it "raises no invalid network error when provided with a valid IP address" do
-    guest = double("guest")
-    machine.config.vm.network 'private_network', { ip: '192.168.33.6' }
-
-    expect{ subject.call(env) }.not_to raise_error(Vagrant::Errors::NetworkAddressInvalid)
-  end
-
   context "with a dhcp private network" do
     let(:bridgedifs)  { [] }
     let(:hostonlyifs) { [] }
@@ -92,7 +84,7 @@
     let(:network_args) {{ type: :dhcp }}
 
     before do
-      machine.config.vm.network 'private_network', network_args
+      machine.config.vm.network 'private_network', **network_args
       allow(driver).to receive(:read_bridged_interfaces) { bridgedifs }
       allow(driver).to receive(:read_host_only_interfaces) { hostonlyifs }
       allow(driver).to receive(:read_dhcp_servers) { dhcpservers }
diff --git a/test/unit/plugins/provisioners/chef/config/chef_apply_test.rb b/test/unit/plugins/provisioners/chef/config/chef_apply_test.rb
index 135c042138..5bcee520c1 100644
--- a/test/unit/plugins/provisioners/chef/config/chef_apply_test.rb
+++ b/test/unit/plugins/provisioners/chef/config/chef_apply_test.rb
@@ -10,7 +10,7 @@
   let(:machine) { double("machine") }
 
   def chef_error(key, options = {})
-    I18n.t("vagrant.provisioners.chef.#{key}", options)
+    I18n.t("vagrant.provisioners.chef.#{key}", **options)
   end
 
   describe "#recipe" do
diff --git a/test/unit/vagrant/action/builder_test.rb b/test/unit/vagrant/action/builder_test.rb
index cf87f375e3..efb4194573 100644
--- a/test/unit/vagrant/action/builder_test.rb
+++ b/test/unit/vagrant/action/builder_test.rb
@@ -549,12 +549,12 @@ def call(env)
 
             it "should add trigger action to start of stack" do
               subject.apply_dynamic_updates(env)
-              expect(subject.stack[0].first).to eq(Vagrant::Action::Builtin::Trigger)
+              expect(subject.stack[0].middleware).to eq(Vagrant::Action::Builtin::Trigger)
             end
 
             it "should have timing and type arguments" do
               subject.apply_dynamic_updates(env)
-              args = subject.stack[0][1]
+              args = subject.stack[0].arguments.parameters
               expect(args).to include(type)
               expect(args).to include(timing)
               expect(args).to include(action.to_s)
@@ -566,12 +566,12 @@ def call(env)
 
             it "should add trigger action to middle of stack" do
               subject.apply_dynamic_updates(env)
-              expect(subject.stack[1].first).to eq(Vagrant::Action::Builtin::Trigger)
+              expect(subject.stack[1].middleware).to eq(Vagrant::Action::Builtin::Trigger)
             end
 
             it "should have timing and type arguments" do
               subject.apply_dynamic_updates(env)
-              args = subject.stack[1][1]
+              args = subject.stack[1].arguments.parameters
               expect(args).to include(type)
               expect(args).to include(timing)
               expect(args).to include(action.to_s)
@@ -587,12 +587,12 @@ def call(env)
 
             it "should add trigger action to start of stack" do
               subject.apply_dynamic_updates(env)
-              expect(subject.stack[0].first).to eq(Vagrant::Action::Builtin::Trigger)
+              expect(subject.stack[0].middleware).to eq(Vagrant::Action::Builtin::Trigger)
             end
 
             it "should have timing and type arguments" do
               subject.apply_dynamic_updates(env)
-              args = subject.stack[0][1]
+              args = subject.stack[0].arguments.parameters
               expect(args).to include(type)
               expect(args).to include(timing)
               expect(args).to include(action.to_s)
@@ -609,7 +609,7 @@ def call(env)
 
             it "should have timing and type arguments" do
               subject.apply_dynamic_updates(env)
-              args = subject.stack[1][1]
+              args = subject.stack[1].arguments.parameters
               expect(args).to include(type)
               expect(args).to include(timing)
               expect(args).to include(action.to_s)
@@ -688,7 +688,7 @@ def call(env)
 
         it "should include arguments to the trigger action" do
           subject.apply_action_name(env)
-          args = subject.stack[0][1]
+          args = subject.stack[0].arguments.parameters
           expect(args).to include(raw_action_name)
           expect(args).to include(timing)
           expect(args).to include(:action)
@@ -705,9 +705,9 @@ def call(env)
 
         it "should include arguments to the trigger action" do
           subject.apply_action_name(env)
-          builder = subject.stack.first[1]&.first
+          builder = subject.stack.first.arguments.parameters.first
           expect(builder).not_to be_nil
-          args = builder.stack.first[1]
+          args = builder.stack.first.arguments.parameters
           expect(args).to include(raw_action_name)
           expect(args).to include(timing)
           expect(args).to include(:action)
@@ -728,12 +728,12 @@ def call(env)
 
         it "should add a trigger action to the start of the stack" do
           subject.apply_action_name(env)
-          expect(subject.stack[0].first).to eq(Vagrant::Action::Builtin::Trigger)
+          expect(subject.stack[0].middleware).to eq(Vagrant::Action::Builtin::Trigger)
         end
 
         it "should include arguments to the trigger action" do
           subject.apply_action_name(env)
-          args = subject.stack[0][1]
+          args = subject.stack[0].arguments.parameters
           expect(args).to include(action_name)
           expect(args).to include(timing)
           expect(args).to include(:hook)
@@ -750,7 +750,7 @@ def call(env)
 
         it "should include arguments to the trigger action" do
           subject.apply_action_name(env)
-          args = subject.stack.last[1]
+          args = subject.stack.last.arguments.parameters
           expect(args).to include(action_name)
           expect(args).to include(timing)
           expect(args).to include(:hook)
diff --git a/test/unit/vagrant/action/builtin/box_add_test.rb b/test/unit/vagrant/action/builtin/box_add_test.rb
index 91bbc27a25..48406c2cf8 100644
--- a/test/unit/vagrant/action/builtin/box_add_test.rb
+++ b/test/unit/vagrant/action/builtin/box_add_test.rb
@@ -91,7 +91,7 @@ def with_web_server(path, **opts)
       env[:box_name] = "foo"
       env[:box_url] = box_path.to_s
 
-      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
         expect(checksum(path)).to eq(checksum(box_path))
         expect(name).to eq("foo")
         expect(version).to eq("0")
@@ -113,7 +113,7 @@ def with_web_server(path, **opts)
         box_path.to_s,
       ]
 
-      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
         expect(checksum(path)).to eq(checksum(box_path))
         expect(name).to eq("foo")
         expect(version).to eq("0")
@@ -132,7 +132,7 @@ def with_web_server(path, **opts)
         env[:box_name] = "foo"
         env[:box_url] = "http://127.0.0.1:#{port}/#{box_path.basename}"
 
-        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
           expect(checksum(path)).to eq(checksum(box_path))
           expect(name).to eq("foo")
           expect(version).to eq("0")
@@ -152,7 +152,7 @@ def with_web_server(path, **opts)
         env[:box_name] = "foo"
         env[:box_url] = "ftp://127.0.0.1:#{port}/#{box_path.basename}"
 
-        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
           expect(checksum(path)).to eq(checksum(box_path))
           expect(name).to eq("foo")
           expect(version).to eq("0")
@@ -218,7 +218,7 @@ def with_web_server(path, **opts)
         env[:box_checksum_type] = ""
 
 
-        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
           expect(checksum(path)).to eq(checksum(box_path))
           expect(name).to eq("foo")
           expect(version).to eq("0")
@@ -291,7 +291,7 @@ def with_web_server(path, **opts)
       env[:box_provider] = "virtualbox"
 
       allow(box_collection).to receive(:find).and_return(box)
-      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
         expect(checksum(path)).to eq(checksum(box_path))
         expect(name).to eq("foo")
         expect(version).to eq("0")
@@ -311,7 +311,7 @@ def with_web_server(path, **opts)
         box_url_name = "http://127.0.0.1:#{port}/#{box_path.basename}"
         env[:box_name] = box_url_name
 
-        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
           expect(name).to eq(box_url_name)
           expect(version).to eq("0")
           expect(opts[:metadata_url]).to be_nil
@@ -333,7 +333,7 @@ def with_web_server(path, **opts)
         box_url_name = "box name with spaces"
         env[:box_name] = box_url_name
 
-        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
           expect(name).to eq(box_url_name)
           expect(version).to eq("0")
           expect(opts[:metadata_url]).to be_nil
@@ -356,7 +356,7 @@ def with_web_server(path, **opts)
           env[:box_name] = "foo"
           env[:box_url] = "http://#{username}:#{password}@127.0.0.1:#{port}/#{box_path.basename}"
 
-          expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+          expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
             expect(checksum(path)).to eq(checksum(box_path))
             expect(name).to eq("foo")
             expect(version).to eq("0")
@@ -404,7 +404,7 @@ def with_web_server(path, **opts)
       with_web_server(md_path) do |port|
         env[:box_url] = "http://127.0.0.1:#{port}/#{md_path.basename}"
 
-        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
           expect(name).to eq("foo/bar")
           expect(version).to eq("0.7")
           expect(checksum(path)).to eq(checksum(box_path))
@@ -449,7 +449,7 @@ def with_web_server(path, **opts)
       with_web_server(md_path, **opts) do |port|
         env[:box_url] = "http://127.0.0.1:#{port}/#{md_path.basename}"
 
-        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
           expect(name).to eq("foo/bar")
           expect(version).to eq("0.7")
           expect(checksum(path)).to eq(checksum(box_path))
@@ -494,7 +494,7 @@ def with_web_server(path, **opts)
         url = "http://127.0.0.1:#{port}"
         env[:box_url] = "mitchellh/precise64.json"
 
-        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
           expect(name).to eq("mitchellh/precise64")
           expect(version).to eq("0.7")
           expect(checksum(path)).to eq(checksum(box_path))
@@ -545,7 +545,7 @@ def with_web_server(path, **opts)
         env[:box_url] = "mitchellh/precise64.json"
         env[:box_server_url] = url
 
-        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
           expect(name).to eq("mitchellh/precise64")
           expect(version).to eq("0.7")
           expect(checksum(path)).to eq(checksum(box_path))
@@ -606,7 +606,7 @@ def with_web_server(path, **opts)
           end
         end
 
-        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
           expect(name).to eq("foo/bar")
           expect(version).to eq("0.7")
           expect(checksum(path)).to eq(checksum(box_path))
@@ -651,7 +651,7 @@ def with_web_server(path, **opts)
       with_web_server(md_path) do |port|
         env[:box_url] = "http://127.0.0.1:#{port}/#{md_path.basename}"
 
-        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+        expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
           expect(name).to eq("foo/bar")
           expect(version).to eq("0.7")
           expect(checksum(path)).to eq(checksum(box_path))
@@ -795,7 +795,7 @@ def with_web_server(path, **opts)
       end
 
       env[:box_url] = tf.path
-      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
         expect(checksum(path)).to eq(checksum(box_path))
         expect(name).to eq("foo/bar")
         expect(version).to eq("0.7")
@@ -839,7 +839,7 @@ def with_web_server(path, **opts)
 
       env[:box_url] = tf.path
       env[:box_provider] = "vmware"
-      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
         expect(checksum(path)).to eq(checksum(box_path))
         expect(name).to eq("foo/bar")
         expect(version).to eq("0.7")
@@ -888,7 +888,7 @@ def with_web_server(path, **opts)
 
       env[:box_url] = tf.path
       env[:box_provider] = "vmware"
-      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
         expect(checksum(path)).to eq(checksum(box_path))
         expect(name).to eq("foo/bar")
         expect(version).to eq("0.7")
@@ -928,7 +928,7 @@ def with_web_server(path, **opts)
 
       env[:box_url] = tf.path
       env[:box_version] = "~> 0.1"
-      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
         expect(checksum(path)).to eq(checksum(box_path))
         expect(name).to eq("foo/bar")
         expect(version).to eq("0.5")
@@ -973,7 +973,7 @@ def with_web_server(path, **opts)
       env[:box_url] = tf.path
       env[:box_provider] = "vmware"
       env[:box_version] = "~> 0.1"
-      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
         expect(checksum(path)).to eq(checksum(box_path))
         expect(name).to eq("foo/bar")
         expect(version).to eq("0.5")
@@ -1021,7 +1021,7 @@ def with_web_server(path, **opts)
 
       env[:box_url] = tf.path
       env[:box_provider] = ["virtualbox", "vmware"]
-      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
         expect(checksum(path)).to eq(checksum(box_path))
         expect(name).to eq("foo/bar")
         expect(version).to eq("0.7")
@@ -1069,7 +1069,7 @@ def with_web_server(path, **opts)
 
       expect(env[:ui]).to receive(:ask).and_return("1")
 
-      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
         expect(checksum(path)).to eq(checksum(box_path))
         expect(name).to eq("foo/bar")
         expect(version).to eq("0.7")
@@ -1245,7 +1245,7 @@ def with_web_server(path, **opts)
       env[:box_force] = true
       env[:box_url] = tf.path
       allow(box_collection).to receive(:find).and_return(box)
-      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts|
+      expect(box_collection).to receive(:add).with(any_args) { |path, name, version, opts|
         expect(checksum(path)).to eq(checksum(box_path))
         expect(name).to eq("foo/bar")
         expect(version).to eq("0.7")
diff --git a/test/unit/vagrant/action/hook_test.rb b/test/unit/vagrant/action/hook_test.rb
index 9c6054b099..c4a3ba9dd8 100644
--- a/test/unit/vagrant/action/hook_test.rb
+++ b/test/unit/vagrant/action/hook_test.rb
@@ -36,11 +36,20 @@
       subject.before(existing, 2)
       subject.before(existing, 3, :arg, &block)
 
-      expect(subject.before_hooks[existing]).to eq([
-        [1, [], nil],
-        [2, [], nil],
-        [3, [:arg], block]
-      ])
+      hooks = subject.before_hooks[existing]
+      expect(hooks.size).to eq(3)
+      expect(hooks[0].middleware).to eq(1)
+      expect(hooks[0].arguments.parameters).to eq([])
+      expect(hooks[0].arguments.keywords).to eq({})
+      expect(hooks[0].arguments.block).to be_nil
+      expect(hooks[1].middleware).to eq(2)
+      expect(hooks[1].arguments.parameters).to eq([])
+      expect(hooks[1].arguments.keywords).to eq({})
+      expect(hooks[1].arguments.block).to be_nil
+      expect(hooks[2].middleware).to eq(3)
+      expect(hooks[2].arguments.parameters).to eq([:arg])
+      expect(hooks[2].arguments.keywords).to eq({})
+      expect(hooks[2].arguments.block).to eq(block)
     end
   end
 
@@ -54,11 +63,20 @@
       subject.after(existing, 2)
       subject.after(existing, 3, :arg, &block)
 
-      expect(subject.after_hooks[existing]).to eq([
-        [1, [], nil],
-        [2, [], nil],
-        [3, [:arg], block]
-      ])
+      hooks = subject.after_hooks[existing]
+      expect(hooks.size).to eq(3)
+      expect(hooks[0].middleware).to eq(1)
+      expect(hooks[0].arguments.parameters).to eq([])
+      expect(hooks[0].arguments.keywords).to eq({})
+      expect(hooks[0].arguments.block).to be_nil
+      expect(hooks[1].middleware).to eq(2)
+      expect(hooks[1].arguments.parameters).to eq([])
+      expect(hooks[1].arguments.keywords).to eq({})
+      expect(hooks[1].arguments.block).to be_nil
+      expect(hooks[2].middleware).to eq(3)
+      expect(hooks[2].arguments.parameters).to eq([:arg])
+      expect(hooks[2].arguments.keywords).to eq({})
+      expect(hooks[2].arguments.block).to eq(block)
     end
   end
 
@@ -70,11 +88,20 @@
       subject.append(2)
       subject.append(3, :arg, &block)
 
-      expect(subject.append_hooks).to eq([
-        [1, [], nil],
-        [2, [], nil],
-        [3, [:arg], block]
-      ])
+      hooks = subject.append_hooks
+      expect(hooks.size).to eq(3)
+      expect(hooks[0].middleware).to eq(1)
+      expect(hooks[0].arguments.parameters).to eq([])
+      expect(hooks[0].arguments.keywords).to eq({})
+      expect(hooks[0].arguments.block).to be_nil
+      expect(hooks[1].middleware).to eq(2)
+      expect(hooks[1].arguments.parameters).to eq([])
+      expect(hooks[1].arguments.keywords).to eq({})
+      expect(hooks[1].arguments.block).to be_nil
+      expect(hooks[2].middleware).to eq(3)
+      expect(hooks[2].arguments.parameters).to eq([:arg])
+      expect(hooks[2].arguments.keywords).to eq({})
+      expect(hooks[2].arguments.block).to eq(block)
     end
   end
 
@@ -86,11 +113,20 @@
       subject.prepend(2)
       subject.prepend(3, :arg, &block)
 
-      expect(subject.prepend_hooks).to eq([
-        [1, [], nil],
-        [2, [], nil],
-        [3, [:arg], block]
-      ])
+      hooks = subject.prepend_hooks
+      expect(hooks.size).to eq(3)
+      expect(hooks[0].middleware).to eq(1)
+      expect(hooks[0].arguments.parameters).to eq([])
+      expect(hooks[0].arguments.keywords).to eq({})
+      expect(hooks[0].arguments.block).to be_nil
+      expect(hooks[1].middleware).to eq(2)
+      expect(hooks[1].arguments.parameters).to eq([])
+      expect(hooks[1].arguments.keywords).to eq({})
+      expect(hooks[1].arguments.block).to be_nil
+      expect(hooks[2].middleware).to eq(3)
+      expect(hooks[2].arguments.parameters).to eq([:arg])
+      expect(hooks[2].arguments.keywords).to eq({})
+      expect(hooks[2].arguments.block).to eq(block)
     end
   end
 
@@ -105,12 +141,15 @@
 
       subject.apply(builder)
 
-      expect(builder.stack).to eq([
-        ["1", [2], nil],
-        ["2", [], nil],
-        ["8", [], nil],
-        ["9", [], nil]
-      ])
+      stack = builder.stack
+      expect(stack[0].middleware).to eq("1")
+      expect(stack[0].arguments.parameters).to eq([2])
+      expect(stack[1].middleware).to eq("2")
+      expect(stack[1].arguments.parameters).to eq([])
+      expect(stack[2].middleware).to eq("8")
+      expect(stack[2].arguments.parameters).to eq([])
+      expect(stack[3].middleware).to eq("9")
+      expect(stack[3].arguments.parameters).to eq([])
     end
 
     it "should not prepend or append if disabled" do
@@ -124,12 +163,11 @@
 
       subject.apply(builder, no_prepend_or_append: true)
 
-      expect(builder.stack).to eq([
-        ["3", [], nil],
-        ["4", [], nil],
-        ["7", [], nil],
-        ["8", [], nil]
-      ])
+      stack = builder.stack
+      expect(stack[0].middleware).to eq("3")
+      expect(stack[1].middleware).to eq("4")
+      expect(stack[2].middleware).to eq("7")
+      expect(stack[3].middleware).to eq("8")
     end
   end
 end
diff --git a/test/unit/vagrant/plugin/manager_test.rb b/test/unit/vagrant/plugin/manager_test.rb
index dcb3dbf5e8..08ac2c3f83 100644
--- a/test/unit/vagrant/plugin/manager_test.rb
+++ b/test/unit/vagrant/plugin/manager_test.rb
@@ -53,7 +53,7 @@
     before do
       allow(Vagrant::Plugin::StateFile).to receive(:new).and_return(state_file)
       allow(bundler).to receive(:environment_path=)
-      allow(local_data_path).to receive(:join).and_return(local_data_path)
+      allow(local_data_path).to receive(:join).and_return(local_data_path) if local_data_path
       allow(subject).to receive(:bundler_init)
     end
 
@@ -118,7 +118,7 @@
     end
 
     it "should init the bundler instance with plugins" do
-      expect(bundler).to receive(:init!).with(plugins, anything)
+      expect(bundler).to receive(:init!).with(plugins, any_args)
       subject.bundler_init(plugins)
     end
 
diff --git a/test/unit/vagrant/ui_test.rb b/test/unit/vagrant/ui_test.rb
index 4d1ded16e9..e484b81543 100644
--- a/test/unit/vagrant/ui_test.rb
+++ b/test/unit/vagrant/ui_test.rb
@@ -14,7 +14,7 @@
     end
 
     it "outputs using `puts` by default" do
-      expect(subject).to receive(:safe_puts).with(any_args) { |message, **opts|
+      expect(subject).to receive(:safe_puts).with(any_args) { |message, opts|
         expect(opts[:printer]).to eq(:puts)
         true
       }
@@ -23,7 +23,7 @@
     end
 
     it "outputs using `print` if new_line is false" do
-      expect(subject).to receive(:safe_puts).with(any_args) { |message, **opts|
+      expect(subject).to receive(:safe_puts).with(any_args) { |message, opts|
         expect(opts[:printer]).to eq(:print)
         true
       }
@@ -32,7 +32,7 @@
     end
 
     it "outputs using `print` if new_line is false" do
-      expect(subject).to receive(:safe_puts).with(any_args) { |message, **opts|
+      expect(subject).to receive(:safe_puts).with(any_args) { |message, opts|
         expect(opts[:printer]).to eq(:print)
         true
       }
@@ -44,7 +44,7 @@
       stdout = StringIO.new
       subject.stdout = stdout
 
-      expect(subject).to receive(:safe_puts).with(any_args) { |message, **opts|
+      expect(subject).to receive(:safe_puts).with(any_args) { |message, opts|
         expect(opts[:io]).to be(stdout)
         true
       }
@@ -60,7 +60,7 @@
       stderr = StringIO.new
       subject.stderr = stderr
 
-      expect(subject).to receive(:safe_puts).with(any_args) { |message, **opts|
+      expect(subject).to receive(:safe_puts).with(any_args) { |message, opts|
         expect(opts[:io]).to be(stderr)
         true
       }
@@ -81,7 +81,7 @@
 
   context "#detail" do
     it "outputs details" do
-      expect(subject).to receive(:safe_puts).with(any_args) { |message, **opts|
+      expect(subject).to receive(:safe_puts).with(any_args) { |message, opts|
         expect(message).to eq("foo")
         true
       }
@@ -104,7 +104,7 @@
     before{ Vagrant::Util::CredentialScrubber.sensitive(password) }
 
     it "should remove sensitive information from the output" do
-      expect(subject).to receive(:safe_puts).with(any_args) do |message, **opts|
+      expect(subject).to receive(:safe_puts).with(any_args) do |message, opts|
         expect(message).not_to include(password)
       end
       subject.detail(output)
diff --git a/vagrant.gemspec b/vagrant.gemspec
index 8aaf9cc7d9..9287a99a3e 100644
--- a/vagrant.gemspec
+++ b/vagrant.gemspec
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
   s.summary       = "Build and distribute virtualized development environments."
   s.description   = "Vagrant is a tool for building and distributing virtualized development environments."
 
-  s.required_ruby_version     = "~> 2.4", "< 2.7"
+  s.required_ruby_version     = "~> 2.4", "< 2.8"
   s.required_rubygems_version = ">= 1.3.6"
 
   s.add_dependency "bcrypt_pbkdf", "~> 1.0.0"

From 203ebf59adf161826a63ecede0e6318a39f13a1a Mon Sep 17 00:00:00 2001
From: Chris Roberts <croberts@hashicorp.com>
Date: Mon, 10 Aug 2020 11:17:24 -0700
Subject: [PATCH 2/3] Update minimum Ruby constriant to 2.5

---
 .github/workflows/testing.yml | 2 +-
 vagrant.gemspec               | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml
index 95591be6a5..68d284de78 100644
--- a/.github/workflows/testing.yml
+++ b/.github/workflows/testing.yml
@@ -22,7 +22,7 @@ jobs:
     runs-on: ubuntu-18.04
     strategy:
       matrix:
-        ruby: [ '2.4.x', '2.5.x', '2.6.x' ]
+        ruby: [ '2.5.x', '2.6.x', '2.7.x' ]
     name: Vagrant unit tests on Ruby ${{ matrix.ruby }}
     steps:
       - name: Code Checkout
diff --git a/vagrant.gemspec b/vagrant.gemspec
index 9287a99a3e..f088f3adf0 100644
--- a/vagrant.gemspec
+++ b/vagrant.gemspec
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
   s.summary       = "Build and distribute virtualized development environments."
   s.description   = "Vagrant is a tool for building and distributing virtualized development environments."
 
-  s.required_ruby_version     = "~> 2.4", "< 2.8"
+  s.required_ruby_version     = "~> 2.5", "< 2.8"
   s.required_rubygems_version = ">= 1.3.6"
 
   s.add_dependency "bcrypt_pbkdf", "~> 1.0.0"

From e7e956ca1244410558f74ab6c5e52838cf2106d8 Mon Sep 17 00:00:00 2001
From: sophia <scastellarin95@gmail.com>
Date: Mon, 10 Aug 2020 16:32:54 -0500
Subject: [PATCH 3/3] Fix a few deprecation warnings for ruby 2.7

---
 plugins/kernel_v2/config/vm.rb                       | 2 +-
 plugins/providers/virtualbox/action/forward_ports.rb | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/plugins/kernel_v2/config/vm.rb b/plugins/kernel_v2/config/vm.rb
index f6fb07c000..14dc2de808 100644
--- a/plugins/kernel_v2/config/vm.rb
+++ b/plugins/kernel_v2/config/vm.rb
@@ -370,7 +370,7 @@ def provision(name, **options, &block)
 
           if Vagrant::Util::Experimental.feature_enabled?("dependency_provisioners")
             opts = {before: before, after: after}
-            prov = VagrantConfigProvisioner.new(name, type.to_sym, opts)
+            prov = VagrantConfigProvisioner.new(name, type.to_sym, **opts)
           else
             prov = VagrantConfigProvisioner.new(name, type.to_sym)
           end
diff --git a/plugins/providers/virtualbox/action/forward_ports.rb b/plugins/providers/virtualbox/action/forward_ports.rb
index aac6639186..a468ae61bc 100644
--- a/plugins/providers/virtualbox/action/forward_ports.rb
+++ b/plugins/providers/virtualbox/action/forward_ports.rb
@@ -48,7 +48,7 @@ def forward_ports
             # bridged networking don't require port-forwarding and establishing
             # forwarded ports on these attachment types has uncertain behaviour.
             @env[:ui].detail(I18n.t("vagrant.actions.vm.forward_ports.forwarding_entry",
-                                    message_attributes))
+                                    **message_attributes))
 
             # Verify we have the network interface to attach to
             if !interfaces[fp.adapter]
@@ -62,7 +62,7 @@ def forward_ports
             # so verify that that is the case.
             if interfaces[fp.adapter][:type] != :nat
               @env[:ui].detail(I18n.t("vagrant.actions.vm.forward_ports.non_nat",
-                                    message_attributes))
+                                    **message_attributes))
               next
             end