This repository has been archived by the owner on Jan 21, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 262
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Version information to plugin APIs (#318)
Signed-off-by: Bill Farner <bill@docker.com>
- Loading branch information
Showing
22 changed files
with
294 additions
and
21 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
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
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,70 @@ | ||
package client | ||
|
||
import ( | ||
"fmt" | ||
"github.com/docker/infrakit/pkg/rpc/plugin" | ||
"github.com/docker/infrakit/pkg/spi" | ||
"sync" | ||
) | ||
|
||
type handshakingClient struct { | ||
client Client | ||
iface spi.InterfaceSpec | ||
|
||
// handshakeResult handles the tri-state outcome of handshake state: | ||
// - handshake has not yet completed (nil) | ||
// - handshake completed successfully (non-nil result, nil error) | ||
// - handshake failed (non-nil result, non-nil error) | ||
handshakeResult *handshakeResult | ||
|
||
// lock guards handshakeResult | ||
lock *sync.Mutex | ||
} | ||
|
||
type handshakeResult struct { | ||
err error | ||
} | ||
|
||
func (c *handshakingClient) handshake() error { | ||
c.lock.Lock() | ||
defer c.lock.Unlock() | ||
|
||
if c.handshakeResult == nil { | ||
req := plugin.ImplementsRequest{} | ||
resp := plugin.ImplementsResponse{} | ||
|
||
if err := c.client.Call("Plugin.Implements", req, &resp); err != nil { | ||
return err | ||
} | ||
|
||
err := fmt.Errorf("Plugin does not support interface %v", c.iface) | ||
if resp.APIs != nil { | ||
for _, iface := range resp.APIs { | ||
if iface.Name == c.iface.Name { | ||
if iface.Version == c.iface.Version { | ||
err = nil | ||
break | ||
} else { | ||
err = fmt.Errorf( | ||
"Plugin supports %s interface version %s, client requires %s", | ||
iface.Name, | ||
iface.Version, | ||
c.iface.Version) | ||
} | ||
} | ||
} | ||
} | ||
|
||
c.handshakeResult = &handshakeResult{err: err} | ||
} | ||
|
||
return c.handshakeResult.err | ||
} | ||
|
||
func (c *handshakingClient) Call(method string, arg interface{}, result interface{}) error { | ||
if err := c.handshake(); err != nil { | ||
return err | ||
} | ||
|
||
return c.client.Call(method, arg, result) | ||
} |
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,85 @@ | ||
package client | ||
|
||
import ( | ||
"github.com/docker/infrakit/pkg/rpc/server" | ||
"github.com/docker/infrakit/pkg/spi" | ||
"github.com/stretchr/testify/require" | ||
"io/ioutil" | ||
"net/http" | ||
"path/filepath" | ||
"testing" | ||
) | ||
|
||
var apiSpec = spi.InterfaceSpec{ | ||
Name: "TestPlugin", | ||
Version: "0.1.0", | ||
} | ||
|
||
func startPluginServer(t *testing.T) (server.Stoppable, string) { | ||
dir, err := ioutil.TempDir("", "infrakit_handshake_test") | ||
require.NoError(t, err) | ||
|
||
name := "instance" | ||
socket := filepath.Join(dir, name) | ||
|
||
testServer, err := server.StartPluginAtPath(socket, &TestPlugin{spec: apiSpec}) | ||
require.NoError(t, err) | ||
return testServer, socket | ||
} | ||
|
||
func TestHandshakeSuccess(t *testing.T) { | ||
testServer, socket := startPluginServer(t) | ||
defer testServer.Stop() | ||
|
||
client := rpcClient{client: New(socket, apiSpec)} | ||
require.NoError(t, client.DoSomething()) | ||
} | ||
|
||
func TestHandshakeFailVersion(t *testing.T) { | ||
testServer, socket := startPluginServer(t) | ||
defer testServer.Stop() | ||
|
||
client := rpcClient{client: New(socket, spi.InterfaceSpec{Name: "TestPlugin", Version: "0.2.0"})} | ||
err := client.DoSomething() | ||
require.Error(t, err) | ||
require.Equal(t, "Plugin supports TestPlugin interface version 0.1.0, client requires 0.2.0", err.Error()) | ||
} | ||
|
||
func TestHandshakeFailWrongAPI(t *testing.T) { | ||
testServer, socket := startPluginServer(t) | ||
defer testServer.Stop() | ||
|
||
client := rpcClient{client: New(socket, spi.InterfaceSpec{Name: "OtherPlugin", Version: "0.1.0"})} | ||
err := client.DoSomething() | ||
require.Error(t, err) | ||
require.Equal(t, "Plugin does not support interface {OtherPlugin 0.1.0}", err.Error()) | ||
} | ||
|
||
type rpcClient struct { | ||
client Client | ||
} | ||
|
||
func (c rpcClient) DoSomething() error { | ||
req := EmptyMessage{} | ||
resp := EmptyMessage{} | ||
return c.client.Call("TestPlugin.DoSomething", req, &resp) | ||
} | ||
|
||
// TestPlugin is an RPC service for this unit test. | ||
type TestPlugin struct { | ||
spec spi.InterfaceSpec | ||
} | ||
|
||
// ImplementedInterface returns the interface implemented by this RPC service. | ||
func (p *TestPlugin) ImplementedInterface() spi.InterfaceSpec { | ||
return p.spec | ||
} | ||
|
||
// EmptyMessage is an empty test message. | ||
type EmptyMessage struct { | ||
} | ||
|
||
// DoSomething is an empty test RPC. | ||
func (p *TestPlugin) DoSomething(_ *http.Request, req *EmptyMessage, resp *EmptyMessage) error { | ||
return 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,8 @@ | ||
package client | ||
|
||
// Client allows execution of RPCs. | ||
type Client interface { | ||
|
||
// Call invokes an RPC method with an argument and a pointer to a result that will hold the return value. | ||
Call(method string, arg interface{}, result interface{}) error | ||
} |
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
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
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,17 @@ | ||
package plugin | ||
|
||
import ( | ||
"github.com/docker/infrakit/pkg/spi" | ||
"net/http" | ||
) | ||
|
||
// Plugin is the service for API metadata. | ||
type Plugin struct { | ||
Spec spi.InterfaceSpec | ||
} | ||
|
||
// Implements responds to a request for the supported plugin interfaces. | ||
func (p Plugin) Implements(_ *http.Request, req *ImplementsRequest, resp *ImplementsResponse) error { | ||
resp.APIs = []spi.InterfaceSpec{p.Spec} | ||
return nil | ||
} |
Oops, something went wrong.