170 lines
3.8 KiB
Go
170 lines
3.8 KiB
Go
package plugin
|
|
|
|
import (
|
|
"fmt"
|
|
"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"
|
|
)
|
|
|
|
// 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)
|
|
}
|
|
}
|
|
|
|
func Factory(
|
|
cmd *exec.Cmd, // Plugin command to run
|
|
) PluginRegistration {
|
|
return func(log hclog.Logger) (p *Plugin, err error) {
|
|
// We have to copy the command because go-plugin will set some
|
|
// fields on it.
|
|
cmdCopy := *cmd
|
|
|
|
log = log.Named("factory")
|
|
|
|
nlog := log.ResetNamed("vagrant.plugin")
|
|
config := pluginclient.ClientConfig(nlog)
|
|
config.Cmd = &cmdCopy
|
|
config.Logger = nlog
|
|
|
|
// Log that we're going to launch this
|
|
log.Info("launching plugin",
|
|
"path", cmd.Path,
|
|
"args", cmd.Args)
|
|
|
|
// Connect to the plugin
|
|
client := plugin.NewClient(config)
|
|
|
|
// If we encounter any errors during setup, automatically
|
|
// kill the client
|
|
defer func() {
|
|
if err != nil {
|
|
client.Kill()
|
|
}
|
|
}()
|
|
|
|
rpcClient, err := client.Client()
|
|
if err != nil {
|
|
log.Error("error creating plugin client",
|
|
"error", err)
|
|
|
|
return
|
|
}
|
|
|
|
raw, err := rpcClient.Dispense("plugininfo")
|
|
if err != nil {
|
|
log.Error("error requesting plugin information interface",
|
|
"plugin", cmd.Path,
|
|
"error", err)
|
|
|
|
return
|
|
}
|
|
|
|
info, ok := raw.(component.PluginInfo)
|
|
if !ok {
|
|
log.Error("cannot load plugin info component",
|
|
"plugin", cmd.Path)
|
|
|
|
return nil, fmt.Errorf("failed to load plugin information interface")
|
|
}
|
|
|
|
p = &Plugin{
|
|
Builtin: false,
|
|
Client: rpcClient,
|
|
Location: cmd.Path,
|
|
Name: info.Name(),
|
|
Types: info.ComponentTypes(),
|
|
components: map[component.Type]*Instance{},
|
|
logger: nlog.Named(info.Name()),
|
|
src: client,
|
|
}
|
|
|
|
// Close the rpcClient when plugin is closed
|
|
p.Closer(func() error {
|
|
return rpcClient.Close()
|
|
})
|
|
|
|
return
|
|
}
|
|
}
|
|
|
|
// Request the mappers
|
|
// mappers, err := pluginclient.Mappers(client)
|
|
// if err != nil {
|
|
// log.Error("error requesting plugin mappers",
|
|
// "error", err)
|
|
// client.Kill()
|
|
// return nil, err
|
|
// }
|
|
|
|
// BuiltinFactory creates a factory for a built-in plugin type.
|
|
func BuiltinFactory(name string) PluginRegistration {
|
|
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)
|
|
}
|
|
|
|
func RubyFactory(
|
|
rubyClient plugin.ClientProtocol,
|
|
name string,
|
|
typ component.Type,
|
|
) PluginRegistration {
|
|
return func(log hclog.Logger) (*Plugin, error) {
|
|
return &Plugin{
|
|
Builtin: false,
|
|
Client: rubyClient,
|
|
Location: "ruby-runtime",
|
|
Name: name,
|
|
Types: []component.Type{typ},
|
|
components: map[component.Type]*Instance{},
|
|
logger: log.ResetNamed("vagrant.legacy." + strings.ToLower(typ.String())),
|
|
}, 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
|
|
|
|
// The GRPCBroker attached to this plugin
|
|
Broker *plugin.GRPCBroker
|
|
|
|
// Closer is a function that should be called to clean up resources
|
|
// associated with this plugin.
|
|
Close func()
|
|
}
|
|
|
|
type PluginMetadata interface {
|
|
SetRequestMetadata(k, v string)
|
|
}
|
|
|
|
type hasGRPCBroker interface {
|
|
GRPCBroker() *plugin.GRPCBroker
|
|
}
|