1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Add negotiation process for driver scope

Add one capability negotiation interaction after plugin handshake, use
this to determine plugin's capability instead of default "global" scope.

Signed-off-by: Zhang Wei <zhangwei555@huawei.com>
This commit is contained in:
Zhang Wei 2015-09-13 20:00:05 +08:00
parent 17f56bac82
commit 304bfd6261
4 changed files with 149 additions and 12 deletions

View file

@ -49,6 +49,16 @@ When loaded, a remote driver process receives an HTTP POST on the URL `/Plugin.A
Other entries in the list value are allowed; `"NetworkDriver"` indicates that the plugin should be registered with LibNetwork as a driver.
### Set capability
After Handshake, the remote driver will receive another POST message to the URL `/NetworkDriver.GetCapabilities` with no payload. The driver's response should have the form:
{
"Scope": "local"
}
Value of "Scope" should be either "local" or "global" which indicates the capability of remote driver, values beyond these will fail driver's registration and return an error to the caller.
### Create network
When the proxy is asked to create a network, the remote process shall receive a POST to the URL `/NetworkDriver.CreateNetwork` of the form

View file

@ -16,6 +16,12 @@ func (r *Response) GetError() string {
return r.Err
}
// GetCapabilityResponse is the response of GetCapability request
type GetCapabilityResponse struct {
Response
Scope string
}
// CreateNetworkRequest requests a new network.
type CreateNetworkRequest struct {
// A network ID that remote plugins are expected to store for future

View file

@ -28,16 +28,40 @@ func newDriver(name string, client *plugins.Client) driverapi.Driver {
// plugin is activated.
func Init(dc driverapi.DriverCallback) error {
plugins.Handle(driverapi.NetworkPluginEndpointType, func(name string, client *plugins.Client) {
c := driverapi.Capability{
Scope: driverapi.GlobalScope,
// negotiate driver capability with client
d := newDriver(name, client)
c, err := d.(*driver).getCapabilities()
if err != nil {
log.Errorf("error getting capability for %s due to %v", name, err)
return
}
if err := dc.RegisterDriver(name, newDriver(name, client), c); err != nil {
if err = dc.RegisterDriver(name, d, *c); err != nil {
log.Errorf("error registering driver for %s due to %v", name, err)
}
})
return nil
}
// Get capability from client
func (d *driver) getCapabilities() (*driverapi.Capability, error) {
var capResp api.GetCapabilityResponse
if err := d.call("GetCapabilities", nil, &capResp); err != nil {
return nil, err
}
c := &driverapi.Capability{}
switch capResp.Scope {
case "global":
c.Scope = driverapi.GlobalScope
case "local":
c.Scope = driverapi.LocalScope
default:
return nil, fmt.Errorf("invalid capability: expecting 'local' or 'global', got %s", capResp.Scope)
}
return c, nil
}
// Config is not implemented for remote drivers, since it is assumed
// to be supplied to the remote process out-of-band (e.g., as command
// line arguments).

View file

@ -153,6 +153,91 @@ func (test *testEndpoint) AddStaticRoute(destination *net.IPNet, routeType int,
return nil
}
func TestGetEmptyCapabilities(t *testing.T) {
var plugin = "test-net-driver-empty-cap"
mux := http.NewServeMux()
defer setupPlugin(t, plugin, mux)()
handle(t, mux, "GetCapabilities", func(msg map[string]interface{}) interface{} {
return map[string]interface{}{}
})
p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType)
if err != nil {
t.Fatal(err)
}
d := newDriver(plugin, p.Client)
if d.Type() != plugin {
t.Fatal("Driver type does not match that given")
}
_, err = d.(*driver).getCapabilities()
if err == nil {
t.Fatal("There should be error reported when get empty capability")
}
}
func TestGetExtraCapabilities(t *testing.T) {
var plugin = "test-net-driver-extra-cap"
mux := http.NewServeMux()
defer setupPlugin(t, plugin, mux)()
handle(t, mux, "GetCapabilities", func(msg map[string]interface{}) interface{} {
return map[string]interface{}{
"Scope": "local",
"foo": "bar",
}
})
p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType)
if err != nil {
t.Fatal(err)
}
d := newDriver(plugin, p.Client)
if d.Type() != plugin {
t.Fatal("Driver type does not match that given")
}
c, err := d.(*driver).getCapabilities()
if err != nil {
t.Fatal(err)
} else if c.Scope != driverapi.LocalScope {
t.Fatalf("get capability '%s', expecting 'local'", c.Scope)
}
}
func TestGetInvalidCapabilities(t *testing.T) {
var plugin = "test-net-driver-invalid-cap"
mux := http.NewServeMux()
defer setupPlugin(t, plugin, mux)()
handle(t, mux, "GetCapabilities", func(msg map[string]interface{}) interface{} {
return map[string]interface{}{
"Scope": "fake",
}
})
p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType)
if err != nil {
t.Fatal(err)
}
d := newDriver(plugin, p.Client)
if d.Type() != plugin {
t.Fatal("Driver type does not match that given")
}
_, err = d.(*driver).getCapabilities()
if err == nil {
t.Fatal("There should be error reported when get invalid capability")
}
}
func TestRemoteDriver(t *testing.T) {
var plugin = "test-net-driver"
@ -177,6 +262,11 @@ func TestRemoteDriver(t *testing.T) {
var networkID string
handle(t, mux, "GetCapabilities", func(msg map[string]interface{}) interface{} {
return map[string]interface{}{
"Scope": "global",
}
})
handle(t, mux, "CreateNetwork", func(msg map[string]interface{}) interface{} {
nid := msg["NetworkID"]
var ok bool
@ -245,38 +335,45 @@ func TestRemoteDriver(t *testing.T) {
t.Fatal(err)
}
driver := newDriver(plugin, p.Client)
if driver.Type() != plugin {
d := newDriver(plugin, p.Client)
if d.Type() != plugin {
t.Fatal("Driver type does not match that given")
}
c, err := d.(*driver).getCapabilities()
if err != nil {
t.Fatal(err)
} else if c.Scope != driverapi.GlobalScope {
t.Fatalf("get capability '%s', expecting 'global'", c.Scope)
}
netID := "dummy-network"
err = driver.CreateNetwork(netID, map[string]interface{}{})
err = d.CreateNetwork(netID, map[string]interface{}{})
if err != nil {
t.Fatal(err)
}
endID := "dummy-endpoint"
err = driver.CreateEndpoint(netID, endID, ep, map[string]interface{}{})
err = d.CreateEndpoint(netID, endID, ep, map[string]interface{}{})
if err != nil {
t.Fatal(err)
}
joinOpts := map[string]interface{}{"foo": "fooValue"}
err = driver.Join(netID, endID, "sandbox-key", ep, joinOpts)
err = d.Join(netID, endID, "sandbox-key", ep, joinOpts)
if err != nil {
t.Fatal(err)
}
if _, err = driver.EndpointOperInfo(netID, endID); err != nil {
if _, err = d.EndpointOperInfo(netID, endID); err != nil {
t.Fatal(err)
}
if err = driver.Leave(netID, endID); err != nil {
if err = d.Leave(netID, endID); err != nil {
t.Fatal(err)
}
if err = driver.DeleteEndpoint(netID, endID); err != nil {
if err = d.DeleteEndpoint(netID, endID); err != nil {
t.Fatal(err)
}
if err = driver.DeleteNetwork(netID); err != nil {
if err = d.DeleteNetwork(netID); err != nil {
t.Fatal(err)
}
}