-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: added plugin support with go-plugin
- initially implemented Output plugin - has a sample plugin in `examples/plugin`
- Loading branch information
Showing
6 changed files
with
213 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package cmd | ||
|
||
import ( | ||
"github.com/salsadigitalauorg/shipshape/pkg/plugin" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var pluginCmd = &cobra.Command{ | ||
Use: "plugin", | ||
Short: "Manage shipshape plugins", | ||
Run: func(cmd *cobra.Command, args []string) { | ||
plugin.Run() | ||
}, | ||
} | ||
|
||
func init() { | ||
rootCmd.AddCommand(pluginCmd) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"os" | ||
|
||
"github.com/hashicorp/go-hclog" | ||
"github.com/hashicorp/go-plugin" | ||
"github.com/salsadigitalauorg/shipshape/pkg/output" | ||
shipshape_plugin "github.com/salsadigitalauorg/shipshape/pkg/plugin" | ||
"github.com/salsadigitalauorg/shipshape/pkg/result" | ||
) | ||
|
||
type SampleOutput struct { | ||
logger hclog.Logger | ||
} | ||
|
||
func (o *SampleOutput) Output(rl *result.ResultList) ([]byte, error) { | ||
o.logger.Debug("message from SampleOutput.Output") | ||
b := bytes.Buffer{} | ||
fmt.Fprintln(&b, "ResultList:", *rl) | ||
return b.Bytes(), nil | ||
} | ||
|
||
func main() { | ||
logger := hclog.New(&hclog.LoggerOptions{ | ||
Level: hclog.Trace, | ||
Output: os.Stderr, | ||
JSONFormat: true, | ||
}) | ||
|
||
outputter := &SampleOutput{ | ||
logger: logger, | ||
} | ||
// pluginMap is the map of plugins we can dispense. | ||
var pluginMap = map[string]plugin.Plugin{ | ||
"outputter": &output.OutputterPlugin{Impl: outputter}, | ||
} | ||
|
||
logger.Debug("message from plugin", "foo", "bar") | ||
|
||
plugin.Serve(&plugin.ServeConfig{ | ||
HandshakeConfig: shipshape_plugin.Handshake, | ||
Plugins: pluginMap, | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package output | ||
|
||
import ( | ||
"net/rpc" | ||
|
||
"github.com/hashicorp/go-plugin" | ||
"github.com/salsadigitalauorg/shipshape/pkg/result" | ||
) | ||
|
||
// OutputterRPC is the client side implementation of the interface which is used | ||
// by the host to send data to the plugin and receive the result. | ||
type OutputterRPC struct{ client *rpc.Client } | ||
|
||
func (or *OutputterRPC) Output(rl *result.ResultList) ([]byte, error) { | ||
var resp []byte | ||
err := or.client.Call("Plugin.Output", rl, &resp) | ||
return resp, err | ||
} | ||
|
||
// OutputterRPCServer is the server representation of our interface which is | ||
// used by the plugin to receive data from the host and return the result. | ||
type OutputterRPCServer struct { | ||
Impl Outputter | ||
} | ||
|
||
func (os *OutputterRPCServer) Output(rl *result.ResultList, resp *[]byte) error { | ||
b, err := os.Impl.Output(rl) | ||
*resp = b | ||
return err | ||
} | ||
|
||
// OutputterPlugin is what exposes the Outputter interface as a plugin. | ||
type OutputterPlugin struct { | ||
Impl Outputter | ||
} | ||
|
||
func (p *OutputterPlugin) Server(*plugin.MuxBroker) (interface{}, error) { | ||
return &OutputterRPCServer{Impl: p.Impl}, nil | ||
} | ||
|
||
func (OutputterPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { | ||
return &OutputterRPC{client: c}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package plugin | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"os" | ||
"os/exec" | ||
|
||
"github.com/hashicorp/go-hclog" | ||
"github.com/hashicorp/go-plugin" | ||
"github.com/salsadigitalauorg/shipshape/pkg/output" | ||
"github.com/salsadigitalauorg/shipshape/pkg/result" | ||
) | ||
|
||
var Handshake = plugin.HandshakeConfig{ | ||
ProtocolVersion: 1, | ||
MagicCookieKey: "SHIPSHAPE_PLUGIN", | ||
MagicCookieValue: "28d1923e-f22f-41b7-9699-cdbf4a8e2faa", | ||
} | ||
|
||
// pluginMap is the map of plugins we can dispense. | ||
var pluginMap = map[string]plugin.Plugin{ | ||
"outputter": &output.OutputterPlugin{}, | ||
} | ||
|
||
func Run() { | ||
// Create an hclog.Logger | ||
logger := hclog.New(&hclog.LoggerOptions{ | ||
Name: "plugin", | ||
Output: os.Stdout, | ||
Level: hclog.Debug, | ||
}) | ||
|
||
// We're a host! Start by launching the plugin process. | ||
client := plugin.NewClient(&plugin.ClientConfig{ | ||
HandshakeConfig: Handshake, | ||
Plugins: pluginMap, | ||
Cmd: exec.Command("./build/plugin-sample"), | ||
Logger: logger, | ||
}) | ||
defer client.Kill() | ||
|
||
// Connect via RPC | ||
rpcClient, err := client.Client() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// Request the plugin | ||
raw, err := rpcClient.Dispense("outputter") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
outputter := raw.(output.Outputter) | ||
b, err := outputter.Output(&result.ResultList{ | ||
Policies: map[string][]string{"policy1": {"rule1", "rule2"}}, | ||
Results: []result.Result{{Name: "result1", Passes: []string{"rule1"}}}, | ||
}) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
fmt.Println("output from plugin:", string(b)) | ||
} |