139 lines
3.8 KiB
Go
139 lines
3.8 KiB
Go
package plugin
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"os/exec"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/go-argmapper"
|
|
"github.com/hashicorp/go-hclog"
|
|
"github.com/hashicorp/go-plugin"
|
|
|
|
"github.com/hashicorp/vagrant-plugin-sdk/component"
|
|
"github.com/hashicorp/vagrant-plugin-sdk/internal-shared/pluginclient"
|
|
"github.com/hashicorp/vagrant/internal/serverclient"
|
|
)
|
|
|
|
// exePath contains the value of os.Executable. We cache the value because
|
|
// we use it a lot and subsequent calls perform syscalls.
|
|
var exePath string
|
|
|
|
func init() {
|
|
var err error
|
|
exePath, err = os.Executable()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// Factory returns the factory function for a plugin that is already
|
|
// represented by an *exec.Cmd. This returns an *Instance and NOT the component
|
|
// interface value directly. This instance lets you more carefully manage the
|
|
// lifecycle of the plugin as well as get additional information about the
|
|
// plugin.
|
|
func Factory(cmd *exec.Cmd, typ component.Type) interface{} {
|
|
return func(log hclog.Logger) (interface{}, error) {
|
|
// We have to copy the command because go-plugin will set some
|
|
// fields on it.
|
|
cmdCopy := *cmd
|
|
|
|
config := pluginclient.ClientConfig(log)
|
|
config.Cmd = &cmdCopy
|
|
config.Logger = log
|
|
|
|
// Log that we're going to launch this
|
|
log.Info("launching plugin", "type", typ, "path", cmd.Path, "args", cmd.Args)
|
|
|
|
// Connect to the plugin
|
|
client := plugin.NewClient(config)
|
|
rpcClient, err := client.Client()
|
|
if err != nil {
|
|
log.Error("error creating plugin client", "err", err)
|
|
client.Kill()
|
|
return nil, err
|
|
}
|
|
|
|
// Request the plugin. We don't request the mapper type because
|
|
// we handle that below.
|
|
var raw interface{}
|
|
if typ != component.MapperType {
|
|
raw, err = rpcClient.Dispense(strings.ToLower(typ.String()))
|
|
if err != nil {
|
|
log.Error("error requesting plugin", "type", typ, "err", err)
|
|
client.Kill()
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Request the mappers
|
|
mappers, err := pluginclient.Mappers(client)
|
|
if err != nil {
|
|
log.Error("error requesting plugin mappers", "err", err)
|
|
client.Kill()
|
|
return nil, err
|
|
}
|
|
|
|
log.Debug("plugin successfully launched and connected")
|
|
return &Instance{
|
|
Component: raw,
|
|
Mappers: mappers,
|
|
Close: func() { client.Kill() },
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
// BuiltinFactory creates a factory for a built-in plugin type.
|
|
func BuiltinFactory(name string, typ component.Type) interface{} {
|
|
cmd := exec.Command(exePath, "plugin-run", name)
|
|
|
|
// For non-windows systems, we attach stdout/stderr as extra fds
|
|
// so that we can get direct access to the TTY if possible for output.
|
|
if runtime.GOOS != "windows" {
|
|
cmd.ExtraFiles = []*os.File{os.Stdout, os.Stderr}
|
|
}
|
|
|
|
return Factory(cmd, typ)
|
|
}
|
|
|
|
type PluginMetadata interface {
|
|
SetRequestMetadata(k, v string)
|
|
}
|
|
|
|
func BuiltinRubyFactory(rubyClient *serverclient.RubyVagrantClient, name string, typ component.Type) interface{} {
|
|
return func(log hclog.Logger) (interface{}, error) {
|
|
raw, err := rubyClient.Dispense(strings.ToLower(typ.String()))
|
|
if err != nil {
|
|
log.Error("error requesting the ruby plugin", "type", typ, "err", err)
|
|
return nil, err
|
|
}
|
|
|
|
setter, ok := raw.(PluginMetadata)
|
|
if !ok {
|
|
return nil, errors.New("ruby runtime plugin does not support name setting")
|
|
}
|
|
setter.SetRequestMetadata("plugin_name", name)
|
|
|
|
return &Instance{
|
|
Component: raw,
|
|
Mappers: nil,
|
|
Close: func() {},
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
// Instance is the result generated by the factory. This lets us pack
|
|
// a bit more information into plugin-launched components.
|
|
type Instance struct {
|
|
// Component is the dispensed component
|
|
Component interface{}
|
|
|
|
// Mappers is the list of mappers that this plugin is providing.
|
|
Mappers []*argmapper.Func
|
|
|
|
// Closer is a function that should be called to clean up resources
|
|
// associated with this plugin.
|
|
Close func()
|
|
}
|