|
| 1 | +package plugin |
| 2 | + |
| 3 | +import ( |
| 4 | + "errors" |
| 5 | + "os/exec" |
| 6 | + |
| 7 | + plugin "github.com/hashicorp/go-plugin" |
| 8 | + "github.com/michelvocks/gaia/proto" |
| 9 | +) |
| 10 | + |
| 11 | +const ( |
| 12 | + pluginMapKey = "plugin" |
| 13 | +) |
| 14 | + |
| 15 | +var handshake = plugin.HandshakeConfig{ |
| 16 | + ProtocolVersion: 1, |
| 17 | + MagicCookieKey: "GAIA_PLUGIN", |
| 18 | + // This cookie should never be changed again |
| 19 | + MagicCookieValue: "FdXjW27mN6XuG2zDBP4LixXUwDAGCEkidxwqBGYpUhxiWHzctATYZvpz4ZJdALmh", |
| 20 | +} |
| 21 | + |
| 22 | +var pluginMap = map[string]plugin.Plugin{ |
| 23 | + pluginMapKey: &PluginGRPCImpl{}, |
| 24 | +} |
| 25 | + |
| 26 | +// Plugin represents a single plugin instance which uses gRPC |
| 27 | +// to connect to exactly one plugin. |
| 28 | +type Plugin struct { |
| 29 | + // Client instance used to open gRPC connections. |
| 30 | + client *plugin.Client |
| 31 | + |
| 32 | + // Interface to the connected plugin. |
| 33 | + pluginConn PluginGRPC |
| 34 | +} |
| 35 | + |
| 36 | +// NewPlugin creates a new instance of Plugin. |
| 37 | +// One Plugin instance represents one connection to a plugin. |
| 38 | +func NewPlugin(c *exec.Cmd) *Plugin { |
| 39 | + // Allocate |
| 40 | + p := &Plugin{} |
| 41 | + |
| 42 | + // Get new client |
| 43 | + p.client = plugin.NewClient(&plugin.ClientConfig{ |
| 44 | + HandshakeConfig: handshake, |
| 45 | + Plugins: pluginMap, |
| 46 | + Cmd: c, |
| 47 | + AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, |
| 48 | + }) |
| 49 | + |
| 50 | + return p |
| 51 | +} |
| 52 | + |
| 53 | +// Connect starts the plugin, initiates the gRPC connection and looks up the plugin. |
| 54 | +// It's up to the caller to call plugin.Close to shutdown the plugin |
| 55 | +// and close the gRPC connection. |
| 56 | +func (p *Plugin) Connect() error { |
| 57 | + // Connect via gRPC |
| 58 | + gRPCClient, err := p.client.Client() |
| 59 | + if err != nil { |
| 60 | + return err |
| 61 | + } |
| 62 | + |
| 63 | + // Request the plugin |
| 64 | + raw, err := gRPCClient.Dispense(pluginMapKey) |
| 65 | + if err != nil { |
| 66 | + return err |
| 67 | + } |
| 68 | + |
| 69 | + // Convert plugin to interface |
| 70 | + if pC, ok := raw.(PluginGRPC); ok { |
| 71 | + p.pluginConn = pC |
| 72 | + return nil |
| 73 | + } |
| 74 | + |
| 75 | + return errors.New("plugin is not compatible with Gaia plugin interface") |
| 76 | +} |
| 77 | + |
| 78 | +// Execute triggers the execution of one single job |
| 79 | +// for the given plugin. |
| 80 | +func (p *Plugin) Execute(j *proto.Job) error { |
| 81 | + _, err := p.pluginConn.ExecuteJob(j) |
| 82 | + return err |
| 83 | +} |
| 84 | + |
| 85 | +// Close shutdown the plugin and kills the gRPC connection. |
| 86 | +// Remember to call this when you call plugin.Connect. |
| 87 | +func (p *Plugin) Close() { |
| 88 | + // We start the kill command in a goroutine because kill |
| 89 | + // is blocking until the subprocess successfully exits. |
| 90 | + // The user should not notice the stopping process. |
| 91 | + go func() { |
| 92 | + p.client.Kill() |
| 93 | + }() |
| 94 | +} |
0 commit comments