Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split the plugin-example into two binaries. #19

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions examples/basic/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Plugin Example
--------------

Compile this driver via:

go build .

Compile the plugin itself via:

cd plugin/ && go build .

You can then launch the plugin sample via:

./basic

18 changes: 1 addition & 17 deletions examples/basic/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,12 @@ import (
"io/ioutil"
"log"
"net/rpc"
"os"
"os/exec"

"github.com/hashicorp/go-plugin"
)

func main() {
// Normal a plugin will be a separate binary. We put both in one
// here so that it is easy to build and use this example.
if len(os.Args) >= 2 && os.Args[1] != "" {
mainPlugin()
return
}

// We don't want to see the plugin logs.
log.SetOutput(ioutil.Discard)
Expand All @@ -26,7 +19,7 @@ func main() {
client := plugin.NewClient(&plugin.ClientConfig{
HandshakeConfig: handshakeConfig,
Plugins: pluginMap,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is a more general usage question. Do we really need these configs, especially pluginMap here? That means it needs to know what plugins are available at compile time right? Kinda destroy the whole purpose of having plugins

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the same. is this mandatory?

Cmd: exec.Command(os.Args[0], "plugin"),
Cmd: exec.Command("plugin/plugin"),
})
defer client.Kill()

Expand All @@ -48,15 +41,6 @@ func main() {
fmt.Println(greeter.Greet())
}

func mainPlugin() {
// We're a plugin! Serve the plugin. We set the handshake config
// so that the host and our plugin can verify they can talk to each other.
// Then we set the plugin map to say what plugins we're serving.
plugin.Serve(&plugin.ServeConfig{
HandshakeConfig: handshakeConfig,
Plugins: pluginMap,
})
}

// handshakeConfigs are used to just do a basic handshake between
// a plugin and host. If the handshake fails, a user friendly error is shown.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we supposed to remove the plugin implementations below?

Expand Down
88 changes: 88 additions & 0 deletions examples/basic/plugin/plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package main

import (
"net/rpc"
"github.com/hashicorp/go-plugin"
)

func main() {
// We're a plugin! Serve the plugin. We set the handshake config
// so that the host and our plugin can verify they can talk to each other.
// Then we set the plugin map to say what plugins we're serving.
plugin.Serve(&plugin.ServeConfig{
HandshakeConfig: handshakeConfig,
Plugins: pluginMap,
})
}

// handshakeConfigs are used to just do a basic handshake between
// a plugin and host. If the handshake fails, a user friendly error is shown.
// This prevents users from executing bad plugins or executing a plugin
// directory. It is a UX feature, not a security feature.
var handshakeConfig = plugin.HandshakeConfig{
ProtocolVersion: 1,
MagicCookieKey: "BASIC_PLUGIN",
MagicCookieValue: "hello",
}

// pluginMap is the map of plugins we can dispense.
var pluginMap = map[string]plugin.Plugin{
"greeter": new(GreeterPlugin),
}

// Greeter is the interface that we're exposing as a plugin.
type Greeter interface {
Greet() string
}

// Here is a real implementation of Greeter
type GreeterHello struct{}

func (GreeterHello) Greet() string { return "Hello!" }

// Here is an implementation that talks over RPC
type GreeterRPC struct{ client *rpc.Client }

func (g *GreeterRPC) Greet() string {
var resp string
err := g.client.Call("Plugin.Greet", new(interface{}), &resp)
if err != nil {
// You usually want your interfaces to return errors. If they don't,
// there isn't much other choice here.
panic(err)
}

return resp
}

// Here is the RPC server that GreeterRPC talks to, conforming to
// the requirements of net/rpc
type GreeterRPCServer struct {
// This is the real implementation
Impl Greeter
}

func (s *GreeterRPCServer) Greet(args interface{}, resp *string) error {
*resp = s.Impl.Greet()
return nil
}

// This is the implementation of plugin.Plugin so we can serve/consume this
//
// This has two methods: Server must return an RPC server for this plugin
// type. We construct a GreeterRPCServer for this.
//
// Client must return an implementation of our interface that communicates
// over an RPC client. We return GreeterRPC for this.
//
// Ignore MuxBroker. That is used to create more multiplexed streams on our
// plugin connection and is a more advanced use case.
type GreeterPlugin struct{}

func (GreeterPlugin) Server(*plugin.MuxBroker) (interface{}, error) {
return &GreeterRPCServer{Impl: new(GreeterHello)}, nil
}

func (GreeterPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
return &GreeterRPC{client: c}, nil
}