diff --git a/internal/core/basis.go b/internal/core/basis.go index 3ee1651a5..4bdee0244 100644 --- a/internal/core/basis.go +++ b/internal/core/basis.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "sort" "strings" "sync" @@ -12,6 +13,7 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-multierror" goplugin "github.com/hashicorp/go-plugin" + "github.com/pkg/errors" "google.golang.org/protobuf/proto" "github.com/hashicorp/vagrant-plugin-sdk/component" @@ -404,6 +406,96 @@ func (b *Basis) DefaultPrivateKey() (path path.Path, err error) { return b.dir.DataDir().Join("insecure_private_key"), nil } +// DefaultProvider implements core.Basis +// This is a subset of the Project.DefaultProvider() algorithm, just the parts +// that make sense when you don't have a Vagrantfile +func (b *Basis) DefaultProvider() (string, error) { + logger := b.logger.Named("default-provider") + logger.Debug("Searching for default provider") + + defaultProvider := os.Getenv("VAGRANT_DEFAULT_PROVIDER") + if defaultProvider != "" { + logger.Debug("Using VAGRANT_DEFAULT_PROVIDER", "provider", defaultProvider) + return defaultProvider, nil + } + + usableProviders := []*core.NamedPlugin{} + pluginProviders, err := b.plugins.ListPlugins("provider") + if err != nil { + return "", err + } + for _, pp := range pluginProviders { + logger.Debug("considering plugin", "provider", pp.Name) + + plug, err := b.plugins.GetPlugin(pp.Name, pp.Type) + if err != nil { + return "", err + } + + plugOpts := plug.Options.(*component.ProviderOptions) + logger.Debug("got provider options", "options", fmt.Sprintf("%#v", plugOpts)) + + // Skip providers that can't be defaulted. + if !plugOpts.Defaultable { + logger.Debug("skipping non-defaultable provider", "provider", pp.Name) + continue + } + + // Skip the providers that aren't usable. + logger.Debug("Checking usable on provider", "provider", pp.Name) + pluginImpl := plug.Plugin.(core.Provider) + usable, err := pluginImpl.Usable() + if err != nil { + return "", err + } + if !usable { + logger.Debug("Skipping unusable provider", "provider", pp.Name) + continue + } + + // If we made it here we have a candidate usable provider + usableProviders = append(usableProviders, plug) + } + logger.Debug("Initial usable provider list", "usableProviders", usableProviders) + + // Sort by plugin priority, higher is first + sort.SliceStable(usableProviders, func(i, j int) bool { + iPriority := usableProviders[i].Options.(*component.ProviderOptions).Priority + jPriority := usableProviders[j].Options.(*component.ProviderOptions).Priority + return iPriority > jPriority + }) + logger.Debug("Priority sorted usable provider list", "usableProviders", usableProviders) + + 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 _, pp := range preferredProviders { + for _, up := range usableProviders { + if pp == up.Name { + logger.Debug("Using preffered provider found in usable list", + "provider", pp) + return pp, nil + } + } + } + + if len(usableProviders) > 0 { + logger.Debug("Using the first provider from the usable list", + "provider", usableProviders[0]) + return usableProviders[0].Name, nil + } + + return "", errors.New("No default provider.") +} + // Implements core.Basis // Returns all the registered plugins of the types specified func (b *Basis) Plugins(types ...string) (plugins []*core.NamedPlugin, err error) { @@ -801,6 +893,10 @@ func (b *Basis) TargetIndex() (core.TargetIndex, error) { return b.index, nil } +func (b *Basis) Vagrantfile() (core.Vagrantfile, error) { + return b.vagrantfile, nil +} + // Returns the list of all known components func (b *Basis) Components(ctx context.Context) ([]*Component, error) { return b.components(b.ctx) diff --git a/lib/vagrant/environment/remote.rb b/lib/vagrant/environment/remote.rb index 95c91af6c..3d6bee44e 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(opts) + client.default_provider(**opts) end # Gets a target (machine) by name 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 28a2e092b..3f66327c9 100644 --- a/lib/vagrant/protobufs/proto/vagrant_plugin_sdk/plugin_pb.rb +++ b/lib/vagrant/protobufs/proto/vagrant_plugin_sdk/plugin_pb.rb @@ -660,6 +660,9 @@ Google::Protobuf::DescriptorPool.generated_pool.build do add_message "hashicorp.vagrant.sdk.Basis.ResourceIdResponse" do optional :resource_id, :string, 1 end + add_message "hashicorp.vagrant.sdk.Basis.DefaultProviderResponse" do + optional :provider_name, :string, 1 + end add_message "hashicorp.vagrant.sdk.Target" do end add_message "hashicorp.vagrant.sdk.Target.ResourceIdResponse" do @@ -1164,6 +1167,7 @@ module Hashicorp Ref::Machine = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.Ref.Machine").msgclass Basis = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.Basis").msgclass Basis::ResourceIdResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.Basis.ResourceIdResponse").msgclass + Basis::DefaultProviderResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.Basis.DefaultProviderResponse").msgclass Target = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.Target").msgclass Target::ResourceIdResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.Target.ResourceIdResponse").msgclass Target::RecordResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.Target.RecordResponse").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 5e320af6d..79d0b4ee9 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 @@ -356,14 +356,16 @@ module Hashicorp self.unmarshal_class_method = :decode self.service_name = 'hashicorp.vagrant.sdk.BasisService' + rpc :Boxes, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::BoxCollection rpc :CWD, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::Path rpc :DataDir, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::DataDir::Basis rpc :DefaultPrivateKey, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::Path - rpc :UI, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::TerminalUI + rpc :DefaultProvider, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Basis::DefaultProviderResponse rpc :Host, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::Host - rpc :Boxes, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::BoxCollection - rpc :TargetIndex, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::TargetIndex rpc :ResourceId, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Basis::ResourceIdResponse + rpc :TargetIndex, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::TargetIndex + rpc :Vagrantfile, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::Vagrantfile + rpc :UI, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::TerminalUI rpc :Seed, ::Hashicorp::Vagrant::Sdk::Args::Seeds, ::Google::Protobuf::Empty rpc :Seeds, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::Seeds end diff --git a/plugins/commands/serve/client/basis.rb b/plugins/commands/serve/client/basis.rb index 221171397..05270f46e 100644 --- a/plugins/commands/serve/client/basis.rb +++ b/plugins/commands/serve/client/basis.rb @@ -30,6 +30,11 @@ module VagrantPlugins resp.path end + def default_provider(**opts) + resp = client.default_provider(Empty.new) + resp.provider_name.to_sym + end + def target_index TargetIndex.load( client.target_index(Empty.new), @@ -37,6 +42,10 @@ module VagrantPlugins ) end + def vagrantfile + client.vagrantfile(Empty.new).to_ruby + end + # @return [Terminal] def ui begin