From 8dbe72a5a07ac3b53787d375b4d0ca38d5a728b5 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Fri, 3 Jun 2022 16:26:24 -0500 Subject: [PATCH] Use Component Options to implement ProviderOptions * Populates ComponentOptions into plugin structs * Maps options for legacy Provider Plugins into PluginOptions * Demos use of PluginOptions in a stub provider * Honors plugin priority and defaultable settings --- builtin/myplugin/main.go | 9 +- builtin/myplugin/provider.go | 75 --------------- builtin/myplugin/provider/happy.go | 96 +++++++++++++++++++ internal/core/project.go | 42 +++++--- internal/plugin/factory.go | 15 +++ internal/plugin/manager.go | 13 ++- internal/plugin/plugin.go | 16 ++-- .../proto/ruby_vagrant/ruby-server.pb.go | 44 ++++++--- .../proto/ruby_vagrant/ruby-server.proto | 3 + .../proto/ruby_vagrant/ruby-server_pb.rb | 1 + .../proto/vagrant_plugin_sdk/plugin_pb.rb | 11 +++ .../vagrant_plugin_sdk/plugin_services_pb.rb | 1 + .../serve/service/internal_service.rb | 41 +++++++- 13 files changed, 248 insertions(+), 119 deletions(-) delete mode 100644 builtin/myplugin/provider.go create mode 100644 builtin/myplugin/provider/happy.go diff --git a/builtin/myplugin/main.go b/builtin/myplugin/main.go index a20c91851..918f601a4 100644 --- a/builtin/myplugin/main.go +++ b/builtin/myplugin/main.go @@ -2,9 +2,11 @@ package myplugin import ( sdk "github.com/hashicorp/vagrant-plugin-sdk" + "github.com/hashicorp/vagrant-plugin-sdk/component" "github.com/hashicorp/vagrant/builtin/myplugin/command" - communincator "github.com/hashicorp/vagrant/builtin/myplugin/communicator" + "github.com/hashicorp/vagrant/builtin/myplugin/communicator" "github.com/hashicorp/vagrant/builtin/myplugin/host" + "github.com/hashicorp/vagrant/builtin/myplugin/provider" "github.com/hashicorp/vagrant/builtin/myplugin/push" ) @@ -19,9 +21,12 @@ var CommandOptions = []sdk.Option{ // &Provider{}, &command.Command{}, &host.AlwaysTrueHost{}, - &communincator.DummyCommunicator{}, + &communicator.DummyCommunicator{}, &push.Encouragement{}, ), + sdk.WithComponent(&provider.Happy{}, &component.ProviderOptions{ + Priority: 100, + }), sdk.WithMappers(StructToCommunincatorOptions), sdk.WithName("myplugin"), } diff --git a/builtin/myplugin/provider.go b/builtin/myplugin/provider.go deleted file mode 100644 index 1ea2fcfae..000000000 --- a/builtin/myplugin/provider.go +++ /dev/null @@ -1,75 +0,0 @@ -package myplugin - -// import ( -// "context" - -// "github.com/hashicorp/vagrant-plugin-sdk/component" -// "github.com/hashicorp/vagrant-plugin-sdk/docs" -// "github.com/hashicorp/vagrant-plugin-sdk/multistep" -// pb "github.com/hashicorp/vagrant/builtin/myplugin/proto" -// ) - -// type ProviderConfig struct { -// } - -// // Provider is the Provider implementation for myplugin. -// type Provider struct { -// config ProviderConfig -// } - -// // Config implements Configurable -// func (p *Provider) Config() (interface{}, error) { -// return &p.config, nil -// } - -// func (b *Provider) Documentation() (*docs.Documentation, error) { -// doc, err := docs.New(docs.FromConfig(&ProviderConfig{})) -// if err != nil { -// return nil, err -// } -// return doc, nil -// } - -// // UsableFunc implements component.Provider -// func (p *Provider) UsableFunc() interface{} { -// return p.Usable -// } - -// // InstalledFunc implements component.Provider -// func (p *Provider) InstalledFunc() interface{} { -// return p.Installed -// } - -// // InitFunc implements component.Provider -// func (p *Provider) InitFunc() interface{} { -// return p.Init -// } - -// // ActionUpFunc implements component.Provider -// func (p *Provider) ActionUpFunc() interface{} { -// return p.ActionUp -// } - -// // TODO -// func (p *Provider) Usable() (bool, error) { -// return true, nil -// } - -// func (p *Provider) Installed(context.Context) (bool, error) { -// return true, nil -// } - -// // TODO -// func (p *Provider) Init() (bool, error) { -// return true, nil -// } - -// // TODO: Take an implementation of core.Machine as an input -// func (c *Provider) ActionUp(ctx context.Context, statebag *multistep.BasicStateBag) (*pb.UpResult, error) { -// return &pb.UpResult{}, nil -// } - -// var ( -// _ component.Provider = (*Provider)(nil) -// _ component.Configurable = (*Provider)(nil) -// ) diff --git a/builtin/myplugin/provider/happy.go b/builtin/myplugin/provider/happy.go new file mode 100644 index 000000000..52e1806cf --- /dev/null +++ b/builtin/myplugin/provider/happy.go @@ -0,0 +1,96 @@ +package provider + +import ( + "context" + + "github.com/hashicorp/vagrant-plugin-sdk/component" + "github.com/hashicorp/vagrant-plugin-sdk/core" +) + +// Happy is a provider that is just happy to be backing your vagrant VMs. +type Happy struct{} + +func (p *Happy) Action(name string, args ...interface{}) error { + return nil +} + +// ActionFunc implements component.Provider +func (h *Happy) ActionFunc(actionName string) interface{} { + return h.Action +} + +func (h *Happy) Capability(name string, args ...interface{}) (interface{}, error) { + return nil, nil +} + +// CapabilityFunc implements component.Provider +func (h *Happy) CapabilityFunc(name string) interface{} { + return h.Capability +} + +func (h *Happy) HasCapability(n *component.NamedCapability) bool { + return false +} + +// HasCapabilityFunc implements component.Provicer +func (h *Happy) HasCapabilityFunc() interface{} { + return h.HasCapability +} + +func (h *Happy) MachineIdChanged() error { + return nil +} + +// MachineIdChangedFunc implements component.Provicer +func (h *Happy) MachineIdChangedFunc() interface{} { + return h.MachineIdChanged +} + +func (h *Happy) Installed(context.Context) (bool, error) { + return true, nil +} + +// InstalledFunc implements component.Provider +func (h *Happy) InstalledFunc() interface{} { + return h.Installed +} + +func (h *Happy) Init() (bool, error) { + return true, nil +} + +// InitFunc implements component.Provider +func (h *Happy) InitFunc() interface{} { + return h.Init +} + +func (h *Happy) SshInfo() (*core.SshInfo, error) { + return nil, nil +} + +// SshInfoFunc implements component.Provider +func (h *Happy) SshInfoFunc() interface{} { + return h.SshInfo +} + +func (h *Happy) State() (*core.MachineState, error) { + return nil, nil +} + +// StateFunc implements component.Provider +func (h *Happy) StateFunc() interface{} { + return h.State +} + +func (h *Happy) Usable() (bool, error) { + return false, nil +} + +// UsableFunc implements component.Provider +func (h *Happy) UsableFunc() interface{} { + return h.Usable +} + +var ( + _ component.Provider = (*Happy)(nil) +) diff --git a/internal/core/project.go b/internal/core/project.go index bdd94031f..31443f311 100644 --- a/internal/core/project.go +++ b/internal/core/project.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "os" + "sort" "strings" "sync" @@ -140,26 +141,33 @@ func (p *Project) DefaultProvider(opts *core.DefaultProviderOptions) (string, er } } - usableProviders := []string{} + usableProviders := []*core.NamedPlugin{} pluginProviders, err := p.basis.plugins.ListPlugins("provider") if err != nil { return "", err } for _, pp := range pluginProviders { + logger.Debug("considering plugin", "provider", pp.Name) + // Skip excluded providers if opts.IsExcluded(pp.Name) { + logger.Debug("skipping excluded provider", "provider", pp.Name) continue } - // TODO: how to check for defaultable? + plug, err := p.basis.plugins.GetPlugin(pp.Name, pp.Type) + if err != nil { + return "", err + } + + plugOpts := plug.Options.(*component.ProviderOptions) + if !plugOpts.Defaultable { + logger.Debug("skipping non-defaultable provider", "provider", pp.Name) + } // 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 { @@ -171,19 +179,25 @@ func (p *Project) DefaultProvider(opts *core.DefaultProviderOptions) (string, er } // If we made it here we have a candidate usable provider - usableProviders = append(usableProviders, pp.Name) + usableProviders = append(usableProviders, plug) } logger.Debug("Initial usable provider list", "usableProviders", usableProviders) - // TODO: how to get and sort by provider priority? + // 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) // 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 { + if u.Name == defaultProvider { logger.Debug("Using default provider as it was found in usable list", "provider", u) - return u, nil + return u.Name, nil } } @@ -203,7 +217,7 @@ func (p *Project) DefaultProvider(opts *core.DefaultProviderOptions) (string, er for _, cp := range configProviders { for _, up := range usableProviders { - if cp == up { + if cp == up.Name { for _, pp := range preferredProviders { if cp == pp { logger.Debug("Using preferred provider detected in configuration and usable", @@ -221,7 +235,7 @@ func (p *Project) DefaultProvider(opts *core.DefaultProviderOptions) (string, er // be chosen on Mac this way. It must be both configured and usable. for _, cp := range configProviders { for _, up := range usableProviders { - if cp == up { + if cp == up.Name { logger.Debug("Using provider detected in configuration and usable", "provider", cp) return cp, nil @@ -233,7 +247,7 @@ func (p *Project) DefaultProvider(opts *core.DefaultProviderOptions) (string, er // first plugin that reports it is usable. for _, pp := range preferredProviders { for _, up := range usableProviders { - if pp == up { + if pp == up.Name { logger.Debug("Using preffered provider found in usable list", "provider", pp) return pp, nil @@ -250,7 +264,7 @@ func (p *Project) DefaultProvider(opts *core.DefaultProviderOptions) (string, er if len(usableProviders) > 0 { logger.Debug("Using the first provider from the usable list", "provider", usableProviders[0]) - return usableProviders[0], nil + return usableProviders[0].Name, nil } return "", errors.New("No default provider.") diff --git a/internal/plugin/factory.go b/internal/plugin/factory.go index ffdc288f6..686aa80d2 100644 --- a/internal/plugin/factory.go +++ b/internal/plugin/factory.go @@ -98,12 +98,18 @@ func Factory( "mappers", mappers, ) + log.Info("plugin components and options", + "components", info.ComponentTypes(), + "options", info.ComponentOptions(), + ) + p = &Plugin{ Builtin: false, Client: rpcClient, Location: cmd.Path, Name: info.Name(), Types: info.ComponentTypes(), + Options: info.ComponentOptions(), Mappers: mappers, cleaner: cleanup.New(), logger: nlog.Named(info.Name()), @@ -136,14 +142,20 @@ func RubyFactory( rubyClient plugin.ClientProtocol, name string, typ component.Type, + optsProto interface{}, ) PluginRegistration { return func(log hclog.Logger) (*Plugin, error) { + options, err := component.UnmarshalOptionsProto(typ, optsProto) + if err != nil { + return nil, err + } return &Plugin{ Builtin: false, Client: rubyClient, Location: "ruby-runtime", Name: name, Types: []component.Type{typ}, + Options: map[component.Type]interface{}{typ: options}, cleaner: cleanup.New(), logger: log.ResetNamed( fmt.Sprintf("vagrant.legacy-plugin.%s.%s", strings.ToLower(typ.String()), name), @@ -164,6 +176,9 @@ type Instance struct { // Component is the dispensed component Component interface{} + // Options for component type, see PluginInfo.ComponentOptions + Options interface{} + // Mappers is the list of mappers that this plugin is providing. Mappers []*argmapper.Func diff --git a/internal/plugin/manager.go b/internal/plugin/manager.go index 4f4b4154d..7a12ce9a4 100644 --- a/internal/plugin/manager.go +++ b/internal/plugin/manager.go @@ -149,7 +149,7 @@ func (m *Manager) LoadLegacyPlugins( "type", p.Type, ) - if err = m.register(RubyFactory(r, p.Name, component.Type(p.Type))); err != nil { + if err = m.register(RubyFactory(r, p.Name, component.Type(p.Type), p.Options)); err != nil { return } } @@ -388,7 +388,9 @@ func (m *Manager) Close() (err error) { return } -// Implements core.PluginManager +// Implements core.PluginManager. Note this returns a slice of core.NamedPlugin +// with only the Type and Name fields populated. To get a NamedPlugin with +// instance of the plugin you need to call GetPlugin. func (m *Manager) ListPlugins(typeNames ...string) ([]*core.NamedPlugin, error) { result := []*core.NamedPlugin{} for _, n := range typeNames { @@ -426,9 +428,10 @@ func (m *Manager) GetPlugin(name, typ string) (*core.NamedPlugin, error) { return nil, err } v := &core.NamedPlugin{ - Name: name, - Type: t.String(), - Plugin: c.Component, + Name: name, + Type: t.String(), + Plugin: c.Component, + Options: c.Options, } m.cache.Register(cid, v) diff --git a/internal/plugin/plugin.go b/internal/plugin/plugin.go index 080c838b6..f39e15190 100644 --- a/internal/plugin/plugin.go +++ b/internal/plugin/plugin.go @@ -37,13 +37,14 @@ var ( ) type Plugin struct { - Builtin bool // Flags if this plugin is a builtin plugin - Cache cacher.Cache // Cache for plugins to utilize in mappers - Client plugin.ClientProtocol // Client connection to plugin - Location string // Location of the plugin (generally path to binary) - Mappers []*argmapper.Func // Plugin specific mappers - Name string // Name of the plugin - Types []component.Type // Component types supported by this plugin + Builtin bool // Flags if this plugin is a builtin plugin + Cache cacher.Cache // Cache for plugins to utilize in mappers + Client plugin.ClientProtocol // Client connection to plugin + Location string // Location of the plugin (generally path to binary) + Mappers []*argmapper.Func // Plugin specific mappers + Name string // Name of the plugin + Types []component.Type // Component types supported by this plugin + Options map[component.Type]interface{} // Options for supported components cleaner cleanup.Cleanup // Cleanup tasks to perform on closing logger hclog.Logger @@ -166,6 +167,7 @@ func (p *Plugin) InstanceOf( Mappers: p.Mappers, Name: p.Name, Type: c, + Options: p.Options[c], } // Be sure the instance is close when the plugin is closed diff --git a/internal/server/proto/ruby_vagrant/ruby-server.pb.go b/internal/server/proto/ruby_vagrant/ruby-server.pb.go index e13a0ea7f..6c53aae6b 100644 --- a/internal/server/proto/ruby_vagrant/ruby-server.pb.go +++ b/internal/server/proto/ruby_vagrant/ruby-server.pb.go @@ -11,7 +11,7 @@ import ( _ "google.golang.org/genproto/googleapis/rpc/errdetails" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - _ "google.golang.org/protobuf/types/known/anypb" + anypb "google.golang.org/protobuf/types/known/anypb" emptypb "google.golang.org/protobuf/types/known/emptypb" reflect "reflect" sync "sync" @@ -168,6 +168,8 @@ type Plugin struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // type of the plugin Type Plugin_Type `protobuf:"varint,2,opt,name=type,proto3,enum=hashicorp.vagrant.Plugin_Type" json:"type,omitempty"` + // options for the plugin + Options *anypb.Any `protobuf:"bytes,3,opt,name=options,proto3" json:"options,omitempty"` } func (x *Plugin) Reset() { @@ -216,6 +218,13 @@ func (x *Plugin) GetType() Plugin_Type { return Plugin_UNKNOWN } +func (x *Plugin) GetOptions() *anypb.Any { + if x != nil { + return x.Options + } + return nil +} + type ParseVagrantfileRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -329,12 +338,15 @@ var file_proto_ruby_vagrant_ruby_server_proto_rawDesc = []byte{ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x07, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x76, 0x61, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x2e, 0x50, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x52, 0x07, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x22, 0xb0, 0x02, 0x0a, 0x06, 0x50, + 0x6e, 0x52, 0x07, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x22, 0xe0, 0x02, 0x0a, 0x06, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x32, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x76, 0x61, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x2e, 0x50, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xdd, 0x01, + 0x69, 0x6e, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2e, 0x0a, + 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xdd, 0x01, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x4d, 0x4d, 0x41, 0x4e, 0x44, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x4f, 0x4d, 0x4d, 0x55, 0x4e, 0x49, 0x43, 0x41, 0x54, 0x4f, 0x52, @@ -398,22 +410,24 @@ var file_proto_ruby_vagrant_ruby_server_proto_goTypes = []interface{}{ (*Plugin)(nil), // 2: hashicorp.vagrant.Plugin (*ParseVagrantfileRequest)(nil), // 3: hashicorp.vagrant.ParseVagrantfileRequest (*ParseVagrantfileResponse)(nil), // 4: hashicorp.vagrant.ParseVagrantfileResponse - (*vagrant_plugin_sdk.Vagrantfile_Vagrantfile)(nil), // 5: hashicorp.vagrant.sdk.Vagrantfile.Vagrantfile - (*emptypb.Empty)(nil), // 6: google.protobuf.Empty + (*anypb.Any)(nil), // 5: google.protobuf.Any + (*vagrant_plugin_sdk.Vagrantfile_Vagrantfile)(nil), // 6: hashicorp.vagrant.sdk.Vagrantfile.Vagrantfile + (*emptypb.Empty)(nil), // 7: google.protobuf.Empty } var file_proto_ruby_vagrant_ruby_server_proto_depIdxs = []int32{ 2, // 0: hashicorp.vagrant.GetPluginsResponse.plugins:type_name -> hashicorp.vagrant.Plugin 0, // 1: hashicorp.vagrant.Plugin.type:type_name -> hashicorp.vagrant.Plugin.Type - 5, // 2: hashicorp.vagrant.ParseVagrantfileResponse.vagrantfile:type_name -> hashicorp.vagrant.sdk.Vagrantfile.Vagrantfile - 6, // 3: hashicorp.vagrant.RubyVagrant.GetPlugins:input_type -> google.protobuf.Empty - 3, // 4: hashicorp.vagrant.RubyVagrant.ParseVagrantfile:input_type -> hashicorp.vagrant.ParseVagrantfileRequest - 1, // 5: hashicorp.vagrant.RubyVagrant.GetPlugins:output_type -> hashicorp.vagrant.GetPluginsResponse - 4, // 6: hashicorp.vagrant.RubyVagrant.ParseVagrantfile:output_type -> hashicorp.vagrant.ParseVagrantfileResponse - 5, // [5:7] is the sub-list for method output_type - 3, // [3:5] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name + 5, // 2: hashicorp.vagrant.Plugin.options:type_name -> google.protobuf.Any + 6, // 3: hashicorp.vagrant.ParseVagrantfileResponse.vagrantfile:type_name -> hashicorp.vagrant.sdk.Vagrantfile.Vagrantfile + 7, // 4: hashicorp.vagrant.RubyVagrant.GetPlugins:input_type -> google.protobuf.Empty + 3, // 5: hashicorp.vagrant.RubyVagrant.ParseVagrantfile:input_type -> hashicorp.vagrant.ParseVagrantfileRequest + 1, // 6: hashicorp.vagrant.RubyVagrant.GetPlugins:output_type -> hashicorp.vagrant.GetPluginsResponse + 4, // 7: hashicorp.vagrant.RubyVagrant.ParseVagrantfile:output_type -> hashicorp.vagrant.ParseVagrantfileResponse + 6, // [6:8] is the sub-list for method output_type + 4, // [4:6] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name } func init() { file_proto_ruby_vagrant_ruby_server_proto_init() } diff --git a/internal/server/proto/ruby_vagrant/ruby-server.proto b/internal/server/proto/ruby_vagrant/ruby-server.proto index ff716f985..8bccdc0d5 100644 --- a/internal/server/proto/ruby_vagrant/ruby-server.proto +++ b/internal/server/proto/ruby_vagrant/ruby-server.proto @@ -32,6 +32,9 @@ message Plugin { // type of the plugin Type type = 2; + // options for the plugin + google.protobuf.Any options = 3; + // Supported plugin types, the values here MUST match the enum values // in the Go sdk/component package exactly. A test in internal/server // validates this. diff --git a/lib/vagrant/protobufs/proto/ruby_vagrant/ruby-server_pb.rb b/lib/vagrant/protobufs/proto/ruby_vagrant/ruby-server_pb.rb index e36c32cbe..d4ab8a19f 100644 --- a/lib/vagrant/protobufs/proto/ruby_vagrant/ruby-server_pb.rb +++ b/lib/vagrant/protobufs/proto/ruby_vagrant/ruby-server_pb.rb @@ -15,6 +15,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do add_message "hashicorp.vagrant.Plugin" do optional :name, :string, 1 optional :type, :enum, 2, "hashicorp.vagrant.Plugin.Type" + optional :options, :message, 3, "google.protobuf.Any" end add_enum "hashicorp.vagrant.Plugin.Type" do value :UNKNOWN, 0 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 7e4b20fbb..42cdd50bb 100644 --- a/lib/vagrant/protobufs/proto/vagrant_plugin_sdk/plugin_pb.rb +++ b/lib/vagrant/protobufs/proto/vagrant_plugin_sdk/plugin_pb.rb @@ -440,6 +440,15 @@ Google::Protobuf::DescriptorPool.generated_pool.build do add_message "hashicorp.vagrant.sdk.PluginInfo.Name" do optional :name, :string, 1 end + add_message "hashicorp.vagrant.sdk.PluginInfo.ComponentOptionsMap" do + map :options, :uint32, :message, 1, "google.protobuf.Any" + end + add_message "hashicorp.vagrant.sdk.PluginInfo.ProviderOptions" do + optional :priority, :int32, 1 + optional :parallel, :bool, 2 + optional :box_optional, :bool, 3 + optional :defaultable, :bool, 4 + end add_message "hashicorp.vagrant.sdk.PluginManager" do end add_message "hashicorp.vagrant.sdk.PluginManager.PluginsRequest" do @@ -1056,6 +1065,8 @@ module Hashicorp PluginInfo = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.PluginInfo").msgclass PluginInfo::ComponentList = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.PluginInfo.ComponentList").msgclass PluginInfo::Name = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.PluginInfo.Name").msgclass + PluginInfo::ComponentOptionsMap = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.PluginInfo.ComponentOptionsMap").msgclass + PluginInfo::ProviderOptions = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.PluginInfo.ProviderOptions").msgclass PluginManager = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.PluginManager").msgclass PluginManager::PluginsRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.PluginManager.PluginsRequest").msgclass PluginManager::PluginsResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("hashicorp.vagrant.sdk.PluginManager.PluginsResponse").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 2f3b01357..a466065fd 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 @@ -82,6 +82,7 @@ module Hashicorp self.service_name = 'hashicorp.vagrant.sdk.PluginInfoService' rpc :ComponentTypes, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::PluginInfo::ComponentList + rpc :ComponentOptions, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::PluginInfo::ComponentOptionsMap rpc :Name, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::PluginInfo::Name end diff --git a/plugins/commands/serve/service/internal_service.rb b/plugins/commands/serve/service/internal_service.rb index a8c5c947b..562e7084a 100644 --- a/plugins/commands/serve/service/internal_service.rb +++ b/plugins/commands/serve/service/internal_service.rb @@ -22,7 +22,13 @@ module VagrantPlugins [:pushes, :PUSH], [:synced_folders, :SYNCEDFOLDER]].map do |method, const| plugin_manager.send(method).map do |k, v| - Hashicorp::Vagrant::Plugin.new(name: k, type: Hashicorp::Vagrant::Plugin::Type.const_get(const)) + Hashicorp::Vagrant::Plugin.new( + name: k, + type: Hashicorp::Vagrant::Plugin::Type.const_get(const), + options: Google::Protobuf::Any.pack( + _convert_options_to_proto(const, v) + ) + ) end end.flatten Hashicorp::Vagrant::GetPluginsResponse.new( @@ -79,6 +85,39 @@ module VagrantPlugins vagrantfile: vagrantfile ) end + + def _convert_options_to_proto(type, class_or_tuple_with_class_and_options) + case type + when :COMMAND + # _, command_options = class_or_tuple_with_class_and_options + return Google::Protobuf::Empty.new + when :COMMUNICATOR + # No options for communicators + return Google::Protobuf::Empty.new + when :GUEST + # No options for guests + return Google::Protobuf::Empty.new + when :HOST + # _, parent = class_or_tuple_with_class_and_options + return Google::Protobuf::Empty.new + when :PROVIDER + _, popts = class_or_tuple_with_class_and_options + return SDK::PluginInfo::ProviderOptions.new( + priority: popts[:priority], + parallel: !!popts[:parallel], + box_optional: !!popts[:box_optional], + defaultable: !!popts[:defaultable], + ) + when :PROVISIONER + return Google::Protobuf::Empty.new + when :PUSH + return Google::Protobuf::Empty.new + when :SYNCEDFOLDER + return Google::Protobuf::Empty.new + else + raise "Cannot convert options for unknown component type: #{type}" + end + end end end end