From 102e100108bb54228f2d4c3f415cd69ab739d86b Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Tue, 3 May 2022 12:34:50 -0500 Subject: [PATCH 01/16] client/target_index: Make yard comments conformant --- plugins/commands/serve/client/target_index.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/commands/serve/client/target_index.rb b/plugins/commands/serve/client/target_index.rb index b80d7c50f..4e12e9f04 100644 --- a/plugins/commands/serve/client/target_index.rb +++ b/plugins/commands/serve/client/target_index.rb @@ -2,7 +2,7 @@ module VagrantPlugins module CommandServe class Client class TargetIndex < Client - # @param [string] + # @param [String] # @return [Boolean] true if delete is successful def delete(ident) logger.debug("deleting machine with id #{ident} from index") @@ -14,8 +14,8 @@ module VagrantPlugins true end - # @param [string] - # @return [MachineIndex::Entry] + # @param [String] + # @return [Vagrant::MachineIndex::Entry] def get(ident) logger.debug("getting machine with id #{ident} from index") begin @@ -31,7 +31,7 @@ module VagrantPlugins end end - # @param [string] + # @param [String] # @return [Boolean] def include?(ident) logger.debug("checking for machine with id #{ident} in index") @@ -42,8 +42,8 @@ module VagrantPlugins ).exists end - # @param [MachineIndex::Entry] - # @return [MachineIndex::Entry] + # @param [Vagrant::MachineIndex::Entry] + # @return [Vagrant::MachineIndex::Entry] def set(entry) logger.debug("setting machine #{entry} in index") if entry.id.to_s.empty? @@ -62,7 +62,7 @@ module VagrantPlugins end # Get all targets - # @return [Array] + # @return [Array] def all logger.debug("getting all machines") client.all(Empty.new).targets.map do |t_ref| From 16cd66525792e843b5728c2080f0aeb300f8d4f3 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Mon, 16 May 2022 16:55:19 -0500 Subject: [PATCH 02/16] gitignore: delve, solargraph, rubocop --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index 5f3e03c97..d2bead200 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,10 @@ cover.out # nektos/act secrets file .secrets + +# delve debug binary +__debug_bin + +# solargraph (ruby lsp) & rubocop +.solargraph.yml +.rubocop.yml From 9f9b3855b9dd0bdcbb884583e0b3646b225b6c79 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Mon, 16 May 2022 16:55:58 -0500 Subject: [PATCH 03/16] Remove unused GetDefaultProvider impl --- internal/client/project.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/internal/client/project.go b/internal/client/project.go index 706ce778b..092bab733 100644 --- a/internal/client/project.go +++ b/internal/client/project.go @@ -159,20 +159,6 @@ func (p *Project) LoadTarget(n string) (*Target, error) { }, nil } -// TODO: Determine default provider by implementing algorithm from -// https://www.vagrantup.com/docs/providers/basic_usage#default-provider -// -// Currently blocked on being able to parse Vagrantfile -func (p *Project) GetDefaultProvider(exclude []string, forceDefault bool, checkUsable bool) (provider string, err error) { - defaultProvider := os.Getenv("VAGRANT_DEFAULT_PROVIDER") - if defaultProvider != "" && forceDefault { - return defaultProvider, nil - } - - // HACK: This should throw an error if no default provider is found - return "virtualbox", nil -} - func (p *Project) UI() terminal.UI { return p.ui } From f221614187622e53cd6c0c9c9322117c086caf78 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Mon, 16 May 2022 16:57:14 -0500 Subject: [PATCH 04/16] Fix duplicate guess_provider call No reason to call it twice when it's already being stored in a local variable that's unmodified. Also document the params for this method to help lay the groundwork for porting. --- lib/vagrant/environment.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb index d9609fa3d..de99cbf3b 100644 --- a/lib/vagrant/environment.rb +++ b/lib/vagrant/environment.rb @@ -304,6 +304,12 @@ module Vagrant # This returns the provider name for the default provider for this # environment. # + # @param check_usable [Boolean] (true) whether to filter for `.usable?` providers + # @param exclude [Array] ([]) list of provider names to exclude from + # consideration + # @param force_default [Boolean] (true) whether to prefer the value of + # VAGRANT_DEFAULT_PROVIDER over other strategies if it is set + # @param machine [Symbol] (nil) a machine name to scope this lookup # @return [Symbol] Name of the default provider. def default_provider(**opts) opts[:exclude] = Set.new(opts[:exclude]) if opts[:exclude] @@ -974,7 +980,7 @@ module Vagrant provider = guess_provider vagrantfile.machine_names.each do |mname| ldp = @local_data_path.join("machines/#{mname}/#{provider}") if @local_data_path - plugins << vagrantfile.machine_config(mname, guess_provider, boxes, ldp, false)[:config] + plugins << vagrantfile.machine_config(mname, provider, boxes, ldp, false)[:config] end result = plugins.reverse.inject(Vagrant::Util::HashWithIndifferentAccess.new) do |memo, val| Vagrant::Util::DeepMerge.deep_merge(memo, val.vagrant.plugins) From 06350a7afc6e7359331be34932d3fbb219224908 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Mon, 16 May 2022 17:03:37 -0500 Subject: [PATCH 05/16] Port default provider selection - Pulls in the SDK changes to Project.DefaultProvider and Project.Target - Implements the hefty default provider method - Un-hard-codes provider from Target, and sets it when a Target is looked up from a Project --- builtin/otherplugin/command.go | 2 +- internal/core/project.go | 171 +++++++++++++++++- internal/core/project_test.go | 6 +- internal/core/target.go | 18 +- lib/vagrant/environment/remote.rb | 6 +- .../proto/vagrant_plugin_sdk/plugin_pb.rb | 8 + .../vagrant_plugin_sdk/plugin_services_pb.rb | 2 +- lib/vagrant/vagrantfile.rb | 1 - plugins/commands/serve/client/project.rb | 29 ++- plugins/commands/serve/client/target.rb | 2 +- plugins/commands/serve/mappers/mapper.rb | 10 +- 11 files changed, 224 insertions(+), 31 deletions(-) diff --git a/builtin/otherplugin/command.go b/builtin/otherplugin/command.go index d8e95cb28..011d44585 100644 --- a/builtin/otherplugin/command.go +++ b/builtin/otherplugin/command.go @@ -132,7 +132,7 @@ func (c *Command) ExecuteInfo(trm terminal.UI, p plugincore.Project) int32 { ptrm.Output("YAY! This is project specific output!") } - t, err := p.Target("one") + t, err := p.Target("one", "") if err != nil { trm.Output("Failed to load `one' target -- " + err.Error()) return 1 diff --git a/internal/core/project.go b/internal/core/project.go index 6cc55ff83..2cea8a78b 100644 --- a/internal/core/project.go +++ b/internal/core/project.go @@ -3,6 +3,8 @@ package core import ( "context" "errors" + "fmt" + "os" "strings" "sync" @@ -99,10 +101,159 @@ func (p *Project) DefaultPrivateKey() (path path.Path, err error) { } // DefaultProvider implements core.Project -func (p *Project) DefaultProvider() (name string, err error) { - // TODO: This needs to implement the default provider algorithm - // from https://www.vagrantup.com/docs/providers/basic_usage.html#default-provider - return "virtualbox", nil +func (p *Project) DefaultProvider(opts *core.DefaultProviderOptions) (string, error) { + logger := p.logger.Named("default-provider") + logger.Debug("Searching for default provider", "options", fmt.Sprintf("%#v", opts)) + // Algorithm ported from Vagrant::Environment#default_provider; structure + // and comments mirrored from there. + + // Implement the algorithm from + // https://www.vagrantup.com/docs/providers/basic_usage.html#default-provider + // with additional steps 2.5 and 3.5 from + // https://bugzilla.redhat.com/show_bug.cgi?id=1444492 + // to allow system-configured provider priorities. + // + // 1. The --provider flag on a vagrant up is chosen above all else, if it is + // present. + // + // (Step 1 is done by the caller; this method is only called if --provider + // wasn't given.) + // + // 2. If the VAGRANT_DEFAULT_PROVIDER environmental variable is set, it + // takes next priority and will be the provider chosen. + defaultProvider := os.Getenv("VAGRANT_DEFAULT_PROVIDER") + if defaultProvider != "" && opts.ForceDefault { + logger.Debug("Using forced default provider", "provider", defaultProvider) + return defaultProvider, nil + } + + // Get the list of providers in our configuration, in order + configProviders := []string{} + for _, m := range p.project.GetConfiguration().GetMachineConfigs() { + // If a MachineName is provided - we're only looking at providers + // scoped to that machine name + if opts.MachineName != "" && opts.MachineName != m.GetName() { + continue + } + for _, p := range m.GetConfigVm().GetProviders() { + configProviders = append(configProviders, p.GetType()) + } + } + + usableProviders := []string{} + pluginProviders, err := p.basis.plugins.ListPlugins("provider") + if err != nil { + return "", err + } + for _, pp := range pluginProviders { + // Skip excluded providers + if opts.IsExcluded(pp.Name) { + continue + } + + // TODO: how to check for defaultable? + + // Skip the providers that aren't usable. + if opts.CheckUsable { + logger.Debug("Checking usable on provider", "provider", pp.Name) + plug, err := p.basis.plugins.GetPlugin(pp.Name, pp.Type) + if err != nil { + return "", err + } + pluginImpl := plug.Plugin.(core.Provider) + usable, err := pluginImpl.Usable() + if err != nil { + return "", err + } + if !usable { + continue + } + } + + // If we made it here we have a candidate usable provider + usableProviders = append(usableProviders, pp.Name) + } + logger.Debug("Initial usable provider list", "usableProviders", usableProviders) + + // TODO: how to get and sort by provider priority? + + // If we're not forcing the default, but it's usable and hasn't been + // otherwise excluded, return it now. + for _, u := range usableProviders { + if u == defaultProvider { + logger.Debug("Using default provider as it was found in usable list", + "provider", u) + return u, nil + } + } + + // 2.5. Vagrant will go through all of the config.vm.provider calls in the + // Vagrantfile and try each in order. It will choose the first + // provider that is usable and listed in VAGRANT_PREFERRED_PROVIDERS. + preferredProviders := strings.Split(os.Getenv("VAGRANT_PREFERRED_PROVIDERS"), ",") + k := 0 + for _, pp := range preferredProviders { + spp := strings.TrimSpace(pp) // .map { s.strip } + if spp != "" { // .select { !s.empty? } + preferredProviders[k] = spp + k++ + } + } + preferredProviders = preferredProviders[:k] + + for _, cp := range configProviders { + for _, up := range usableProviders { + if cp == up { + for _, pp := range preferredProviders { + if cp == pp { + logger.Debug("Using preferred provider detected in configuration and usable", + "provider", pp) + return pp, nil + } + } + } + } + } + + // 3. Vagrant will go through all of the config.vm.provider calls in the + // Vagrantfile and try each in order. It will choose the first provider + // that is usable. For example, if you configure Hyper-V, it will never + // be chosen on Mac this way. It must be both configured and usable. + for _, cp := range configProviders { + for _, up := range usableProviders { + if cp == up { + logger.Debug("Using provider detected in configuration and usable", + "provider", cp) + return cp, nil + } + } + } + + // 3.5. Vagrant will go through VAGRANT_PREFERRED_PROVIDERS and find the + // first plugin that reports it is usable. + for _, pp := range preferredProviders { + for _, up := range usableProviders { + if pp == up { + logger.Debug("Using preffered provider found in usable list", + "provider", pp) + return pp, nil + } + } + } + + // 4. Vagrant will go through all installed provider plugins (including the + // ones that come with Vagrant), and find the first plugin that reports + // it is usable. There is a priority system here: systems that are known + // better have a higher priority than systems that are worse. For + // example, if you have the VMware provider installed, it will always + // take priority over VirtualBox. + if len(usableProviders) > 0 { + logger.Debug("Using the first provider from the usable list", + "provider", usableProviders[0]) + return usableProviders[0], nil + } + + return "", errors.New("No default provider.") } // Home implements core.Project @@ -138,13 +289,22 @@ func (p *Project) RootPath() (path path.Path, err error) { } // Target implements core.Project -func (p *Project) Target(nameOrId string) (core.Target, error) { +func (p *Project) Target(nameOrId string, provider string) (core.Target, error) { if t, ok := p.targets[nameOrId]; ok { return t, nil } // Check for name or id for _, t := range p.targets { if t.target.Name == nameOrId { + // TODO: Because we don't have provider selection fully ported + // over, there are cases where a target is loaded without a + // provider being set on it. For now we're just handling that here + // on lookup, but once we know for sure that any Target that exists + // already knows what its provider is, this should be able to be + // removed. + if t.target.Provider == "" && provider != "" { + t.target.Provider = provider + } return t, nil } if t.target.ResourceId == nameOrId { @@ -160,6 +320,7 @@ func (p *Project) Target(nameOrId string) (core.Target, error) { ResourceId: nameOrId, }, ), + WithProvider(provider), ) } diff --git a/internal/core/project_test.go b/internal/core/project_test.go index 25425a552..ddc444674 100644 --- a/internal/core/project_test.go +++ b/internal/core/project_test.go @@ -45,17 +45,17 @@ func TestProjectGetTarget(t *testing.T) { } // Get by id - one, err := tp.Target("id-one") + one, err := tp.Target("id-one", "") require.NoError(t, err) require.Equal(t, targetOne, one) // Get by name - two, err := tp.Target("target-two") + two, err := tp.Target("target-two", "") require.NoError(t, err) require.Equal(t, targetTwo, two) // Get target that doesn't exist - noexist, err := tp.Target("ohnooooo") + noexist, err := tp.Target("ohnooooo", "") require.Error(t, err) require.Nil(t, noexist) } diff --git a/internal/core/target.go b/internal/core/target.go index e0db5d089..74d3cd511 100644 --- a/internal/core/target.go +++ b/internal/core/target.go @@ -2,6 +2,7 @@ package core import ( "context" + "errors" "fmt" "os" "path/filepath" @@ -98,6 +99,9 @@ func (t *Target) Provider() (p core.Provider, err error) { if err != nil { return nil, err } + if providerName == "" { + return nil, errors.New("cannot fetch provider for target when provider name is blank") + } provider, err := t.project.basis.component( t.ctx, component.ProviderType, providerName) @@ -112,8 +116,7 @@ func (t *Target) Provider() (p core.Provider, err error) { // ProviderName implements core.Target // TODO: Use actual value once provider is set on the go side func (t *Target) ProviderName() (string, error) { - // return t.target.Provider, nil - return "virtualbox", nil + return t.target.Provider, nil } // Communicate implements core.Target @@ -410,7 +413,7 @@ type TargetOption func(*Target) error func WithTargetName(name string) TargetOption { return func(t *Target) (err error) { - if ex, _ := t.project.Target(name); ex != nil { + if ex, _ := t.project.Target(name, ""); ex != nil { if et, ok := ex.(*Target); ok { t.target = et.target } @@ -484,4 +487,13 @@ func WithTargetRef(r *vagrant_plugin_sdk.Ref_Target) TargetOption { } } +func WithProvider(provider string) TargetOption { + return func(t *Target) (err error) { + if provider != "" { + t.target.Provider = provider + } + return nil + } +} + var _ core.Target = (*Target)(nil) diff --git a/lib/vagrant/environment/remote.rb b/lib/vagrant/environment/remote.rb index 25e625caf..2f02d3705 100644 --- a/lib/vagrant/environment/remote.rb +++ b/lib/vagrant/environment/remote.rb @@ -102,7 +102,7 @@ module Vagrant end def default_provider(**opts) - client.respond_to?(:default_provider) && client.default_provider.to_sym + client.respond_to?(:default_provider) && client.default_provider(opts) end # Gets a target (machine) by name @@ -126,8 +126,8 @@ module Vagrant # @param [String] machine name # return [Vagrant::Machine] - def machine(name, *_, **_) - client.machine(name) + def machine(name, provider, **_) + client.machine(name, provider) end def machine_names diff --git a/lib/vagrant/protobufs/proto/vagrant_plugin_sdk/plugin_pb.rb b/lib/vagrant/protobufs/proto/vagrant_plugin_sdk/plugin_pb.rb index 3ccac7622..27f2a6bde 100644 --- a/lib/vagrant/protobufs/proto/vagrant_plugin_sdk/plugin_pb.rb +++ b/lib/vagrant/protobufs/proto/vagrant_plugin_sdk/plugin_pb.rb @@ -668,6 +668,12 @@ Google::Protobuf::DescriptorPool.generated_pool.build do add_message "hashicorp.vagrant.sdk.Project.CwdResponse" do optional :path, :string, 1 end + add_message "hashicorp.vagrant.sdk.Project.DefaultProviderRequest" do + optional :check_usable, :bool, 1 + repeated :exclude, :string, 2 + optional :force_default, :bool, 3 + optional :machine_name, :string, 4 + end add_message "hashicorp.vagrant.sdk.Project.DefaultProviderResponse" do optional :provider_name, :string, 1 end @@ -685,6 +691,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do end add_message "hashicorp.vagrant.sdk.Project.TargetRequest" do optional :name, :string, 1 + optional :provider, :string, 2 end add_message "hashicorp.vagrant.sdk.Project.TargetNamesResponse" do repeated :names, :string, 1 @@ -1114,6 +1121,7 @@ module Hashicorp Project::ActiveTargetsResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.Project.ActiveTargetsResponse").msgclass Project::ConfigResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.Project.ConfigResponse").msgclass Project::CwdResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.Project.CwdResponse").msgclass + Project::DefaultProviderRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.Project.DefaultProviderRequest").msgclass Project::DefaultProviderResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.Project.DefaultProviderResponse").msgclass Project::HomeResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.Project.HomeResponse").msgclass Project::LocalDataResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.Project.LocalDataResponse").msgclass diff --git a/lib/vagrant/protobufs/proto/vagrant_plugin_sdk/plugin_services_pb.rb b/lib/vagrant/protobufs/proto/vagrant_plugin_sdk/plugin_services_pb.rb index 634f7fc5b..39ccc159b 100644 --- a/lib/vagrant/protobufs/proto/vagrant_plugin_sdk/plugin_services_pb.rb +++ b/lib/vagrant/protobufs/proto/vagrant_plugin_sdk/plugin_services_pb.rb @@ -472,7 +472,7 @@ module Hashicorp rpc :CWD, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::Path rpc :DataDir, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::DataDir::Project rpc :DefaultPrivateKey, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::Path - rpc :DefaultProvider, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Project::DefaultProviderResponse + rpc :DefaultProvider, ::Hashicorp::Vagrant::Sdk::Project::DefaultProviderRequest, ::Hashicorp::Vagrant::Sdk::Project::DefaultProviderResponse rpc :Home, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::Path rpc :Host, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::Host rpc :LocalData, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::Path diff --git a/lib/vagrant/vagrantfile.rb b/lib/vagrant/vagrantfile.rb index 21dfa49ec..1a4d5006b 100644 --- a/lib/vagrant/vagrantfile.rb +++ b/lib/vagrant/vagrantfile.rb @@ -81,7 +81,6 @@ module Vagrant return Machine.new(name, provider, provider_cls, provider_config, provider_options, config, data_path, box, env, self) end - # Returns the configuration for a single machine. # # When loading a box Vagrantfile, it will be prepended to the diff --git a/plugins/commands/serve/client/project.rb b/plugins/commands/serve/client/project.rb index 8b4bd227e..a559c75f8 100644 --- a/plugins/commands/serve/client/project.rb +++ b/plugins/commands/serve/client/project.rb @@ -54,10 +54,15 @@ module VagrantPlugins resp.path end - # return [String] - def default_provider - resp = client.default_provider(Empty.new) - resp.provider_name + def default_provider(opts) + req = ::Hashicorp::Vagrant::Sdk::Project::DefaultProviderRequest.new( + exclude: opts.fetch(:exclude, []), + force_default: opts.fetch(:force_default, true), + check_usable: opts.fetch(:check_usable, true), + machine_name: opts[:machine], + ) + resp = client.default_provider(req) + resp.provider_name.to_sym end # return [String] @@ -79,8 +84,11 @@ module VagrantPlugins end # return [Vagrant::Machine] - def machine(name) - t = client.target(SDK::Project::TargetRequest.new(name: name)) + def machine(name, provider) + t = client.target(SDK::Project::TargetRequest.new( + name: name, + provider: provider, + )) machine = mapper.map(t, to: Vagrant::Machine) return machine end @@ -124,9 +132,14 @@ module VagrantPlugins # Returns a machine client for the given name # return [VagrantPlugins::CommandServe::Client::Target::Machine] - def target(name) + def target(name, provider) target = Target.load( - client.target(SDK::Project::TargetRequest.new(name: name)), + client.target( + SDK::Project::TargetRequest.new( + name: name, + provider: provider, + ) + ), broker: broker ) target.to_machine diff --git a/plugins/commands/serve/client/target.rb b/plugins/commands/serve/client/target.rb index e11c5723c..08ae6dd4e 100644 --- a/plugins/commands/serve/client/target.rb +++ b/plugins/commands/serve/client/target.rb @@ -13,7 +13,7 @@ module VagrantPlugins :DESTROYED, ].freeze - # @return [SDK::Ref::Target] proto reference for this target + # @return [Hashicorp::Vagrant::Sdk::Ref::Target] proto reference for this target def ref SDK::Ref::Target.new(resource_id: resource_id) end diff --git a/plugins/commands/serve/mappers/mapper.rb b/plugins/commands/serve/mappers/mapper.rb index 6e00c3934..85b9548bb 100644 --- a/plugins/commands/serve/mappers/mapper.rb +++ b/plugins/commands/serve/mappers/mapper.rb @@ -20,14 +20,14 @@ module VagrantPlugins attr_reader :name # @return [Class] type of the argument attr_reader :type - # @return [Callable] callable that can validate argument + # @return [#call] callable that can validate argument attr_reader :validator # Create a new input # # @param type [Class] Type of the input - # @param validator [Callable] Callable to validate argument (optional) - # @yield Callable to validate argument (optional) + # @param validator [#call] Callable to validate argument (optional) + # @yield #call to validate argument (optional) def initialize(type:, validator: nil, &block) if !type.is_a?(Class) && !type.is_a?(Module) raise ArgumentError, @@ -107,14 +107,14 @@ module VagrantPlugins attr_reader :inputs # @return [Class, nil] type of output attr_reader :output - # @return [Callable] callable to perform mapping + # @return [#call] callable to perform mapping attr_reader :func # Create a new mapper instance # # @param inputs [Array] List of inputs for mapper # @param output [Class] Type of output value - # @param func [Callable] Callable to perform mapping + # @param func [#call] Callable to perform mapping def initialize(inputs:, output:, func:) Array(inputs).each do |i| if !i.is_a?(Input) From 52ed0866445fd8f458f7dabcc43642cc976f92f8 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Mon, 16 May 2022 17:11:53 -0500 Subject: [PATCH 06/16] Change TargetIndex to only search by uuid Note this reverses a change made in https://github.com/hashicorp/vagrant-ruby/pull/180 to attempt to address issues losing track of machines. Further testing is in order to verify we haven't re-broken that, but after discussion we agreed this is the correct behavior for the index. --- internal/core/target_index.go | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/internal/core/target_index.go b/internal/core/target_index.go index 54c192373..bbbe37011 100644 --- a/internal/core/target_index.go +++ b/internal/core/target_index.go @@ -47,15 +47,7 @@ func (t *TargetIndex) Get(uuid string) (entry core.Target, err error) { }, }) if err != nil { - // Search name if not found by uuid - result, err = t.client.FindTarget(t.ctx, &vagrant_server.FindTargetRequest{ - Target: &vagrant_server.Target{ - Name: uuid, - }, - }) - if err != nil { - return - } + return } return t.loadTarget(&vagrant_plugin_sdk.Ref_Target{ ResourceId: result.Target.ResourceId, From e6051323219e687f91003ccdb4c313b49174ff78 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Mon, 16 May 2022 16:29:11 -0500 Subject: [PATCH 07/16] Pass along provider to environment.target Mirrors change in https://github.com/hashicorp/vagrant-plugin-sdk/pull/157 --- lib/vagrant/environment/remote.rb | 5 +++-- lib/vagrant/machine/remote.rb | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/vagrant/environment/remote.rb b/lib/vagrant/environment/remote.rb index 2f02d3705..20f4c8062 100644 --- a/lib/vagrant/environment/remote.rb +++ b/lib/vagrant/environment/remote.rb @@ -108,9 +108,10 @@ module Vagrant # Gets a target (machine) by name # # @param [String] machine name + # @param [String] provider name # return [VagrantPlugins::CommandServe::Client::Machine] - def get_target(name) - client.target(name) + def get_target(name, provider) + client.target(name, provider) end # Returns the host object associated with this environment. diff --git a/lib/vagrant/machine/remote.rb b/lib/vagrant/machine/remote.rb index 7cf9cd27b..6c9d56f5d 100644 --- a/lib/vagrant/machine/remote.rb +++ b/lib/vagrant/machine/remote.rb @@ -30,7 +30,7 @@ module Vagrant @logger = Log4r::Logger.new("vagrant::machine") if !env.nil? && client.nil? @env = env - @client = env.get_target(name) + @client = env.get_target(name, provider_name) else @client = client @env = client.environment From 02a06bca45c315dd04d27385b4ed96f768f62696 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Mon, 16 May 2022 16:50:19 -0500 Subject: [PATCH 08/16] Fixes for provider capability invocations It looks like I might have been the first to hit provider cabability invocation in testing these changes, and so I found these few missing methods on the client. They're just copied over from the other capability hosts. Calling capabilities on a provider also revealed that the wrong Machine type was being pulled out of the funcspec args, so we had to correct that too in order to get the capability calls working. --- lib/vagrant/plugin/remote/provider.rb | 19 +++++++++++++++++++ .../serve/service/provider_service.rb | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/vagrant/plugin/remote/provider.rb b/lib/vagrant/plugin/remote/provider.rb index d889e6aa8..6eb8c7f85 100644 --- a/lib/vagrant/plugin/remote/provider.rb +++ b/lib/vagrant/plugin/remote/provider.rb @@ -31,6 +31,25 @@ module Vagrant client.action(@machine.to_proto, name) end + # Executes the capability with the given name, optionally passing more + # arguments onwards to the capability. If the capability returns a value, + # it will be returned. + # + # @param [Symbol] cap_name Name of the capability + def capability(cap_name, *args) + @logger.debug("running remote provider capability #{cap_name} with args #{args}") + client.capability(cap_name, *args) + end + + # Tests whether the given capability is possible. + # + # @param [Symbol] cap_name Capability name + # @return [Boolean] + def capability?(cap_name) + @logger.debug("checking for remote provider capability #{cap_name}") + client.has_capability?(cap_name) + end + def machine_id_changed client.machine_id_changed(@machine.to_proto) end diff --git a/plugins/commands/serve/service/provider_service.rb b/plugins/commands/serve/service/provider_service.rb index d65661afc..c05d8c34f 100644 --- a/plugins/commands/serve/service/provider_service.rb +++ b/plugins/commands/serve/service/provider_service.rb @@ -9,7 +9,7 @@ module VagrantPlugins super caps = Vagrant.plugin("2").local_manager.provider_capabilities default_args = { - Client::Target::Machine => SDK::Args::Target::Machine + Vagrant::Machine => SDK::Args::Target::Machine } initialize_capability_platform!(caps, default_args) end From 8801e030b71f16d5cbef0ec0e0e67230f486d393 Mon Sep 17 00:00:00 2001 From: sophia Date: Mon, 14 Feb 2022 17:08:32 -0600 Subject: [PATCH 09/16] Get default synced folder type (cherry picked from commit cd6baafb9238a58e992519e0576565d1a57bf8f5) (but modified to work) --- internal/core/machine.go | 50 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/internal/core/machine.go b/internal/core/machine.go index b00bc0331..b521c69df 100644 --- a/internal/core/machine.go +++ b/internal/core/machine.go @@ -231,6 +231,49 @@ func StringToPathFunc() mapstructure.DecodeHookFunc { } } +func (m *Machine) defaultSyncedFolderType() (folderType *string, err error) { + // Get all available synced folder plugins + syncedFolders, err := m.project.basis.typeComponents(m.ctx, component.SyncedFolderType) + if err != nil { + return + } + + // Get all plugin components + syncedFoldersComponents := make([]*Component, 0, len(syncedFolders)) + for _, value := range syncedFolders { + syncedFoldersComponents = append(syncedFoldersComponents, value) + } + + // Sort by plugin priority. Higher is first + // TODO: reintroduce priority + // sort.SliceStable(syncedFoldersComponents, func(i, j int) bool { + // return syncedFoldersComponents[i].Info.Priority > syncedFoldersComponents[j].Info.Priority + // }) + + // Remove unallowed types + config := m.target.Configuration + machineConfig := config.ConfigVm + if machineConfig.AllowedSyncedFolderTypes != nil { + // TODO + } + + for _, component := range syncedFoldersComponents { + syncedFolder := component.Value.(core.SyncedFolder) + usable, err := syncedFolder.Usable(m) + if err != nil { + m.logger.Error("synced folder error on usable check", + "plugin", component.Info.Name, + "type", "SyncedFolder", + "error", err) + continue + } + if usable { + return &component.Info.Name, nil + } + } + return nil, fmt.Errorf("failed to detect guest plugin for current platform") +} + // SyncedFolders implements core.Machine func (m *Machine) SyncedFolders() (folders []*core.MachineSyncedFolder, err error) { config := m.target.Configuration @@ -239,10 +282,9 @@ func (m *Machine) SyncedFolders() (folders []*core.MachineSyncedFolder, err erro folders = []*core.MachineSyncedFolder{} for _, folder := range syncedFolders { - if folder.Type == nil { - // TODO: get default synced folder type - defaultType := "virtualbox" - folder.Type = &defaultType + folder.Type, err = m.defaultSyncedFolderType() + if err != nil { + return nil, err } lookup := "syncedfolder_" + *(folder.Type) v := m.cache.Get(lookup) From 49aa226613b57cfdab2b638155cf653eded48f35 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Mon, 16 May 2022 16:52:56 -0500 Subject: [PATCH 10/16] Temporary workaround to let Docker provider work Comment has the details, but this should hopefully be short lived --- plugins/kernel_v2/config/vm.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/kernel_v2/config/vm.rb b/plugins/kernel_v2/config/vm.rb index b5eb61035..2693d6ab4 100644 --- a/plugins/kernel_v2/config/vm.rb +++ b/plugins/kernel_v2/config/vm.rb @@ -755,7 +755,12 @@ module VagrantPlugins @allow_fstab_modification = true if @allow_fstab_modification == UNSET_VALUE end - if !box && !clone && !machine.provider_options[:box_optional] + # HACK(phinze): We cannot honor box_optional in gogo yet so we are + # temporarily hacking in a workaround which explicitly looks for docker + # instead of the option itself. Once plugin metadata is implemented + # this conditional should be reverted to the commented line below + # if !box && !clone && !machine.provider_options[:box_optional] + if !box && !clone && machine.provider_name != :docker errors << I18n.t("vagrant.config.vm.box_missing") end From 823b6d366c3039c2a834d93462656f1b473627cb Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Mon, 16 May 2022 18:36:53 -0500 Subject: [PATCH 11/16] More conservative nil checking on WithProvider to prevent panics --- internal/core/target.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/core/target.go b/internal/core/target.go index 74d3cd511..2467019f1 100644 --- a/internal/core/target.go +++ b/internal/core/target.go @@ -489,7 +489,7 @@ func WithTargetRef(r *vagrant_plugin_sdk.Ref_Target) TargetOption { func WithProvider(provider string) TargetOption { return func(t *Target) (err error) { - if provider != "" { + if t != nil && t.target != nil && provider != "" { t.target.Provider = provider } return nil From 88822f5a964c7b04eedbb68fc02da23e8431d75b Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Tue, 17 May 2022 16:00:38 -0500 Subject: [PATCH 12/16] Override provider for all non-active targets Instead of only targets with empty providers. This helps address a problem that @soapy1 found in review where machines that failed to come up would get stuck with the wrong provider. --- internal/core/project.go | 8 ++++++-- internal/core/target.go | 1 - 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/internal/core/project.go b/internal/core/project.go index 2cea8a78b..bdd94031f 100644 --- a/internal/core/project.go +++ b/internal/core/project.go @@ -68,7 +68,7 @@ func (p *Project) ActiveTargets() (activeTargets []core.Target, err error) { if err != nil { return nil, err } - if st == core.CREATED { + if st.IsActive() { activeTargets = append(activeTargets, t) } } @@ -302,7 +302,11 @@ func (p *Project) Target(nameOrId string, provider string) (core.Target, error) // on lookup, but once we know for sure that any Target that exists // already knows what its provider is, this should be able to be // removed. - if t.target.Provider == "" && provider != "" { + st, err := t.State() + if err != nil { + return nil, err + } + if provider != "" && !st.IsActive() { t.target.Provider = provider } return t, nil diff --git a/internal/core/target.go b/internal/core/target.go index 2467019f1..5e3a86b5d 100644 --- a/internal/core/target.go +++ b/internal/core/target.go @@ -114,7 +114,6 @@ func (t *Target) Provider() (p core.Provider, err error) { } // ProviderName implements core.Target -// TODO: Use actual value once provider is set on the go side func (t *Target) ProviderName() (string, error) { return t.target.Provider, nil } From 25fcf6136442e65048989410cfc13fa2dc46e418 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Wed, 25 May 2022 12:31:31 -0500 Subject: [PATCH 13/16] Sort synced folder plugins by priority and honor allowed setting It turns out that synced folder plugins aren't returned in a consistent order, which was causing all kinds of mayhem. We can tone down that mayhem by implementing a shim of priority sorting the plugins. This shim can be removed once we have proper priority registration in the SDK. --- internal/core/machine.go | 59 +++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/internal/core/machine.go b/internal/core/machine.go index b521c69df..6792a022e 100644 --- a/internal/core/machine.go +++ b/internal/core/machine.go @@ -231,7 +231,24 @@ func StringToPathFunc() mapstructure.DecodeHookFunc { } } +// TEMP: until we have plugin priority being sent along at registration, we are +// manually mirroring the plugin priorities from legacy vagrant +func syncedFolderPriority(name string) int { + switch name { + case "nfs": + return 5 + case "rsync": + return 5 + case "smb": + return 7 + default: // covers virtualbox, docker, vmware + return 10 + } +} + func (m *Machine) defaultSyncedFolderType() (folderType *string, err error) { + logger := m.logger.Named("default-synced-folder-type") + // Get all available synced folder plugins syncedFolders, err := m.project.basis.typeComponents(m.ctx, component.SyncedFolderType) if err != nil { @@ -239,38 +256,60 @@ func (m *Machine) defaultSyncedFolderType() (folderType *string, err error) { } // Get all plugin components - syncedFoldersComponents := make([]*Component, 0, len(syncedFolders)) + components := make([]*Component, 0, len(syncedFolders)) for _, value := range syncedFolders { - syncedFoldersComponents = append(syncedFoldersComponents, value) + components = append(components, value) } // Sort by plugin priority. Higher is first - // TODO: reintroduce priority - // sort.SliceStable(syncedFoldersComponents, func(i, j int) bool { - // return syncedFoldersComponents[i].Info.Priority > syncedFoldersComponents[j].Info.Priority - // }) + sort.SliceStable(components, func(i, j int) bool { + return syncedFolderPriority(components[i].Info.Name) > syncedFolderPriority(components[j].Info.Name) + }) + + names := make([]string, 0, len(components)) + for _, c := range components { + names = append(names, c.Info.Name) + } + logger.Debug("Sorted synced folder plugins", "names", names) // Remove unallowed types config := m.target.Configuration machineConfig := config.ConfigVm - if machineConfig.AllowedSyncedFolderTypes != nil { - // TODO + if len(machineConfig.AllowedSyncedFolderTypes) > 0 { + allowed := make(map[string]struct{}) + for _, a := range machineConfig.AllowedSyncedFolderTypes { + allowed[a] = struct{}{} + } + k := 0 + for _, c := range components { + if _, ok := allowed[c.Info.Name]; ok { + components[k] = c + k++ + } else { + logger.Debug("removing disallowed plugin", "type", c.Info.Name) + } + } + components = components[:k] } - for _, component := range syncedFoldersComponents { + for _, component := range components { syncedFolder := component.Value.(core.SyncedFolder) usable, err := syncedFolder.Usable(m) if err != nil { - m.logger.Error("synced folder error on usable check", + logger.Error("synced folder error on usable check", "plugin", component.Info.Name, "type", "SyncedFolder", "error", err) continue } if usable { + logger.Info("returning default", "name", component.Info.Name) return &component.Info.Name, nil + } else { + logger.Debug("skipping unusable plugin", "name", component.Info.Name) } } + return nil, fmt.Errorf("failed to detect guest plugin for current platform") } From 41933e9c11ccb122bef82f52eb4e9948d45f3d83 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Wed, 25 May 2022 15:04:28 -0500 Subject: [PATCH 14/16] Bump SDK --- go.mod | 2 +- go.sum | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 255054b66..9598400a9 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl/v2 v2.7.1-0.20201023000745-3de61ecba298 github.com/hashicorp/nomad/api v0.0.0-20200814140818-42de70466a9d - github.com/hashicorp/vagrant-plugin-sdk v0.0.0-20220524143830-80e9a67614d0 + github.com/hashicorp/vagrant-plugin-sdk v0.0.0-20220525200400-f1459dc0d92b github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce // indirect github.com/imdario/mergo v0.3.11 github.com/improbable-eng/grpc-web v0.13.0 diff --git a/go.sum b/go.sum index e24d580e6..b298c4e00 100644 --- a/go.sum +++ b/go.sum @@ -355,10 +355,8 @@ github.com/hashicorp/hcl/v2 v2.7.1-0.20201023000745-3de61ecba298 h1:pLsdnvAlWuZ9 github.com/hashicorp/hcl/v2 v2.7.1-0.20201023000745-3de61ecba298/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY= github.com/hashicorp/nomad/api v0.0.0-20200814140818-42de70466a9d h1:afuZ/KNbxwUgjEzq2NXO2bRKZgsIJQgFxgIRGETF0/A= github.com/hashicorp/nomad/api v0.0.0-20200814140818-42de70466a9d/go.mod h1:DCi2k47yuUDzf2qWAK8E1RVmWgz/lc0jZQeEnICTxmY= -github.com/hashicorp/vagrant-plugin-sdk v0.0.0-20220513172229-62750f6e1ce7 h1:4d/nNs0gKLHkm9Ra/bdQTvxWSz0ctvwaOUFrZD/4pN4= -github.com/hashicorp/vagrant-plugin-sdk v0.0.0-20220513172229-62750f6e1ce7/go.mod h1:KWfWOiotOWKiAqdroXVc7GUFnuOzlzhnRkGTV9Js7/s= -github.com/hashicorp/vagrant-plugin-sdk v0.0.0-20220524143830-80e9a67614d0 h1:oHSg3GwN9Wk4Fa94Ozx9et+hUesiPe4bdjyp0uIv5Qo= -github.com/hashicorp/vagrant-plugin-sdk v0.0.0-20220524143830-80e9a67614d0/go.mod h1:KWfWOiotOWKiAqdroXVc7GUFnuOzlzhnRkGTV9Js7/s= +github.com/hashicorp/vagrant-plugin-sdk v0.0.0-20220525200400-f1459dc0d92b h1:GidqljOXwQSIFn+nk4DOpa0kpR/1x/yS/yCNnA7sUFE= +github.com/hashicorp/vagrant-plugin-sdk v0.0.0-20220525200400-f1459dc0d92b/go.mod h1:KWfWOiotOWKiAqdroXVc7GUFnuOzlzhnRkGTV9Js7/s= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce h1:7UnVY3T/ZnHUrfviiAgIUjg2PXxsQfs5bphsG8F7Keo= github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= From 4d79c951774a022c5153599e697d814eeb85fad2 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Wed, 25 May 2022 17:56:40 -0500 Subject: [PATCH 15/16] Update tests and address one thing caught by them Namely, the specified synced folder type should override the default type --- internal/core/machine.go | 8 +++++--- internal/core/machine_test.go | 4 +++- internal/core/target_index_test.go | 11 ----------- internal/core/testing_basis.go | 1 + internal/core/testing_target.go | 7 +++++++ 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/internal/core/machine.go b/internal/core/machine.go index 6792a022e..8b3728655 100644 --- a/internal/core/machine.go +++ b/internal/core/machine.go @@ -321,9 +321,11 @@ func (m *Machine) SyncedFolders() (folders []*core.MachineSyncedFolder, err erro folders = []*core.MachineSyncedFolder{} for _, folder := range syncedFolders { - folder.Type, err = m.defaultSyncedFolderType() - if err != nil { - return nil, err + if folder.GetType() == "" { + folder.Type, err = m.defaultSyncedFolderType() + if err != nil { + return nil, err + } } lookup := "syncedfolder_" + *(folder.Type) v := m.cache.Get(lookup) diff --git a/internal/core/machine_test.go b/internal/core/machine_test.go index 7fcef998a..a9aa54c8c 100644 --- a/internal/core/machine_test.go +++ b/internal/core/machine_test.go @@ -101,6 +101,7 @@ func TestMachineGetNonExistentBox(t *testing.T) { WithTestTargetConfig(&vagrant_plugin_sdk.Vagrantfile_MachineConfig{ ConfigVm: &vagrant_plugin_sdk.Vagrantfile_ConfigVM{Box: "somebox"}, }), + WithTestTargetProvider("testprovider"), ) box, err := tm.Box() @@ -110,7 +111,7 @@ func TestMachineGetNonExistentBox(t *testing.T) { require.Equal(t, name, "somebox") provider, err := box.Provider() require.NoError(t, err) - require.NotEmpty(t, provider) + require.Equal(t, provider, "testprovider") metaurl, err := box.MetadataURL() require.NoError(t, err) require.Empty(t, metaurl) @@ -283,6 +284,7 @@ func syncedFolderPlugin(t *testing.T, name string) *plugin.Plugin { plugin.WithPluginTypes(component.SyncedFolderType), ) } + func TestMachineSyncedFolders(t *testing.T) { mySyncedFolder := syncedFolderPlugin(t, "mysyncedfolder") myOtherSyncedFolder := syncedFolderPlugin(t, "myothersyncedfolder") diff --git a/internal/core/target_index_test.go b/internal/core/target_index_test.go index 18545c180..5b90c78b7 100644 --- a/internal/core/target_index_test.go +++ b/internal/core/target_index_test.go @@ -44,12 +44,6 @@ func TestTargetIndexGet(t *testing.T) { // Add targets projectTargets(t, tp, 3) - // Get by target name - target, err = ti.Get("target-1") - require.NoError(t, err) - rid, _ := target.ResourceId() - require.Equal(t, rid, "id-1") - // Get by target id target, err = ti.Get("uuid-1") require.NoError(t, err) @@ -72,11 +66,6 @@ func TestTargetIndexIncludes(t *testing.T) { // Add targets projectTargets(t, tp, 3) - // Includes by target name - exists, err = ti.Includes("target-1") - require.NoError(t, err) - require.True(t, exists) - // Includes by target id exists, err = ti.Includes("uuid-1") require.NoError(t, err) diff --git a/internal/core/testing_basis.go b/internal/core/testing_basis.go index a65f657e0..49250fb9d 100644 --- a/internal/core/testing_basis.go +++ b/internal/core/testing_basis.go @@ -72,6 +72,7 @@ func BuildTestSyncedFolderPlugin(parent string) *TestSyncedFolderPlugin { p.On("Seed", mock.AnythingOfType("*core.Seeds")).Return(nil) p.On("Seeds").Return(core.NewSeeds(), nil) p.On("Parent").Return(parent, nil) + p.On("Usable", mock.AnythingOfType("*core.Machine")).Return(true, nil) return p } diff --git a/internal/core/testing_target.go b/internal/core/testing_target.go index e37319027..91b1b324e 100644 --- a/internal/core/testing_target.go +++ b/internal/core/testing_target.go @@ -101,3 +101,10 @@ func WithTestTargetConfig(config *vagrant_plugin_sdk.Vagrantfile_MachineConfig) return } } + +func WithTestTargetProvider(provider string) TestMachineOption { + return func(m *Machine) (err error) { + m.target.Provider = provider + return + } +} From 6f9ea74e0e954a979bf16ef74a8d8a417cdce798 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Thu, 26 May 2022 11:16:45 -0500 Subject: [PATCH 16/16] Mark box_optional test as temporarily pending --- test/unit/plugins/kernel_v2/config/vm_test.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/unit/plugins/kernel_v2/config/vm_test.rb b/test/unit/plugins/kernel_v2/config/vm_test.rb index d35cf3a9a..e12331bfc 100644 --- a/test/unit/plugins/kernel_v2/config/vm_test.rb +++ b/test/unit/plugins/kernel_v2/config/vm_test.rb @@ -100,6 +100,8 @@ describe VagrantPlugins::Kernel_V2::VMConfig do end it "is not required if the provider says so" do + # TODO: reenable this test once provider options are implemented + pending "removal of temporary workaround" machine.provider_options[:box_optional] = true subject.box = nil subject.finalize!