From 1e5c432e1029601a664454388ae366ef69618d62 Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Mon, 25 Jun 2018 12:33:13 -0700 Subject: MM-10702 Moving plugins to use hashicorp go-plugin. (#8978) * Moving plugins to use hashicorp go-plugin. * Tweaks from feedback. --- plugin/supervisor.go | 102 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 94 insertions(+), 8 deletions(-) (limited to 'plugin/supervisor.go') diff --git a/plugin/supervisor.go b/plugin/supervisor.go index f20df7040..0471f7861 100644 --- a/plugin/supervisor.go +++ b/plugin/supervisor.go @@ -1,13 +1,99 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. +// See LICENSE.txt for license information. package plugin -// Supervisor provides the interface for an object that controls the execution of a plugin. This -// type is only relevant to the server, and isn't used by the plugins themselves. -type Supervisor interface { - Start(API) error - Wait() error - Stop() error - Hooks() Hooks +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "time" + + "github.com/hashicorp/go-plugin" + "github.com/mattermost/mattermost-server/mlog" + "github.com/mattermost/mattermost-server/model" +) + +type Supervisor struct { + pluginId string + client *plugin.Client + hooks Hooks + implemented [TotalHooksId]bool +} + +func NewSupervisor(pluginInfo *model.BundleInfo, parentLogger *mlog.Logger, apiImpl API) (*Supervisor, error) { + supervisor := Supervisor{} + + wrappedLogger := pluginInfo.WrapLogger(parentLogger) + + hclogAdaptedLogger := &HclogAdapter{ + wrappedLogger: wrappedLogger, + extrasKey: "wrapped_extras", + } + + pluginMap := map[string]plugin.Plugin{ + "hooks": &HooksPlugin{ + log: wrappedLogger, + apiImpl: apiImpl, + }, + } + + executable := filepath.Clean(filepath.Join(".", pluginInfo.Manifest.Backend.Executable)) + if strings.HasPrefix(executable, "..") { + return nil, fmt.Errorf("invalid backend executable") + } + executable = filepath.Join(pluginInfo.Path, executable) + + supervisor.client = plugin.NewClient(&plugin.ClientConfig{ + HandshakeConfig: Handshake, + Plugins: pluginMap, + Cmd: exec.Command(executable), + SyncStdout: os.Stdout, + SyncStderr: os.Stdout, + Logger: hclogAdaptedLogger, + StartTimeout: time.Second * 3, + }) + + rpcClient, err := supervisor.client.Client() + if err != nil { + return nil, err + } + + raw, err := rpcClient.Dispense("hooks") + if err != nil { + return nil, err + } + + supervisor.hooks = raw.(Hooks) + + if impl, err := supervisor.hooks.Implemented(); err != nil { + return nil, err + } else { + for _, hookName := range impl { + if hookId, ok := HookNameToId[hookName]; ok { + supervisor.implemented[hookId] = true + } + } + } + + err = supervisor.Hooks().OnActivate() + if err != nil { + return nil, err + } + + return &supervisor, nil +} + +func (sup *Supervisor) Shutdown() { + sup.client.Kill() +} + +func (sup *Supervisor) Hooks() Hooks { + return sup.hooks +} + +func (sup *Supervisor) Implements(hookId int) bool { + return sup.implemented[hookId] } -- cgit v1.2.3-1-g7c22