Browse Source

Remote Driver Registration

This commits brings in a functionality for remote drivers to register
with LibNetwork. The Built-In remote driver is responsible for the
actual "remote" plugin to be made available.

Having such a mechanism makes libnetwork core not dependent on any
external plugin mechanism and also the Libnetwork NB apis are free of
Driver interface.

Signed-off-by: Madhu Venugopal <madhu@docker.com>
Madhu Venugopal 10 years ago
parent
commit
904a313396

+ 19 - 1
libnetwork/controller.go

@@ -49,6 +49,7 @@ import (
 	"sync"
 	"sync"
 
 
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
+	"github.com/docker/libnetwork/driverapi"
 	"github.com/docker/libnetwork/sandbox"
 	"github.com/docker/libnetwork/sandbox"
 	"github.com/docker/libnetwork/types"
 	"github.com/docker/libnetwork/types"
 )
 )
@@ -98,22 +99,39 @@ type controller struct {
 
 
 // New creates a new instance of network controller.
 // New creates a new instance of network controller.
 func New() NetworkController {
 func New() NetworkController {
-	return &controller{networkTable{}, enumerateDrivers(), sandboxTable{}, sync.Mutex{}}
+	c := &controller{networks: networkTable{}, sandboxes: sandboxTable{}}
+	c.drivers = enumerateDrivers(c)
+	return c
+
 }
 }
 
 
 func (c *controller) ConfigureNetworkDriver(networkType string, options map[string]interface{}) error {
 func (c *controller) ConfigureNetworkDriver(networkType string, options map[string]interface{}) error {
+	c.Lock()
 	d, ok := c.drivers[networkType]
 	d, ok := c.drivers[networkType]
+	c.Unlock()
 	if !ok {
 	if !ok {
 		return NetworkTypeError(networkType)
 		return NetworkTypeError(networkType)
 	}
 	}
 	return d.Config(options)
 	return d.Config(options)
 }
 }
 
 
+func (c *controller) RegisterDriver(networkType string, driver driverapi.Driver) error {
+	c.Lock()
+	defer c.Unlock()
+	if _, ok := c.drivers[networkType]; ok {
+		return driverapi.ErrActiveRegistration(networkType)
+	}
+	c.drivers[networkType] = driver
+	return nil
+}
+
 // NewNetwork creates a new network of the specified network type. The options
 // NewNetwork creates a new network of the specified network type. The options
 // are network specific and modeled in a generic way.
 // are network specific and modeled in a generic way.
 func (c *controller) NewNetwork(networkType, name string, options ...NetworkOption) (Network, error) {
 func (c *controller) NewNetwork(networkType, name string, options ...NetworkOption) (Network, error) {
 	// Check if a driver for the specified network type is available
 	// Check if a driver for the specified network type is available
+	c.Lock()
 	d, ok := c.drivers[networkType]
 	d, ok := c.drivers[networkType]
+	c.Unlock()
 	if !ok {
 	if !ok {
 		return nil, ErrInvalidNetworkDriver
 		return nil, ErrInvalidNetworkDriver
 	}
 	}

+ 1 - 1
libnetwork/docs/remote.md

@@ -5,7 +5,7 @@ Remote driver is a special built-in driver. This driver in itself doesn't provid
 
 
 ## LibNetwork Integration
 ## LibNetwork Integration
 
 
-When LibNetwork creates an instance of the Built-in `Remote` Driver via the `New()` function, it provides a `DriverCallback` which implements the `RegisterDynamicDriver()` to let the Built-in Remote Driver to register any of the `Dynamic` Drivers/Plugins with LibNetwork's `NetworkController`
+When LibNetwork creates an instance of the Built-in `Remote` Driver via the `New()` function, it provides a `DriverCallback` which implements the `RegisterDriver()` to let the Built-in Remote Driver to register any of the `Dynamic` Drivers/Plugins with LibNetwork's `NetworkController`
 
 
 Refer to [Remote Driver Test](https://github.com/docker/libnetwork/blob/drivers/remote/driver_test.go) which provides an example of how the Built-In Remote driver can register any Dynamic driver with LibNetwork.
 Refer to [Remote Driver Test](https://github.com/docker/libnetwork/blob/drivers/remote/driver_test.go) which provides an example of how the Built-In Remote driver can register any Dynamic driver with LibNetwork.
 
 

+ 17 - 0
libnetwork/driverapi/driverapi.go

@@ -2,6 +2,7 @@ package driverapi
 
 
 import (
 import (
 	"errors"
 	"errors"
+	"fmt"
 
 
 	"github.com/docker/libnetwork/sandbox"
 	"github.com/docker/libnetwork/sandbox"
 	"github.com/docker/libnetwork/types"
 	"github.com/docker/libnetwork/types"
@@ -14,6 +15,8 @@ var (
 	ErrNoNetwork = errors.New("No network exists")
 	ErrNoNetwork = errors.New("No network exists")
 	// ErrNoEndpoint is returned if no endpoint with the specified id exists
 	// ErrNoEndpoint is returned if no endpoint with the specified id exists
 	ErrNoEndpoint = errors.New("No endpoint exists")
 	ErrNoEndpoint = errors.New("No endpoint exists")
+	// ErrNotImplemented is returned when a Driver has not implemented an API yet
+	ErrNotImplemented = errors.New("The API is not implemneted yet")
 )
 )
 
 
 // Driver is an interface that every plugin driver needs to implement.
 // Driver is an interface that every plugin driver needs to implement.
@@ -58,3 +61,17 @@ type Driver interface {
 type JoinInfo struct {
 type JoinInfo struct {
 	HostsPath string
 	HostsPath string
 }
 }
+
+// ErrActiveRegistration represents an error when a driver is registered to a networkType that is previously registered
+type ErrActiveRegistration string
+
+// Error interface for ErrActiveRegistration
+func (ar ErrActiveRegistration) Error() string {
+	return fmt.Sprintf("Driver already registered for type %q", string(ar))
+}
+
+// DriverCallback provides a Callback interface for Drivers into LibNetwork
+type DriverCallback interface {
+	// RegisterDriver provides a way for Remote drivers to dynamically register new NetworkType and associate with a driver instance
+	RegisterDriver(name string, driver Driver) error
+}

+ 5 - 3
libnetwork/drivers.go

@@ -5,19 +5,21 @@ import (
 	"github.com/docker/libnetwork/drivers/bridge"
 	"github.com/docker/libnetwork/drivers/bridge"
 	"github.com/docker/libnetwork/drivers/host"
 	"github.com/docker/libnetwork/drivers/host"
 	"github.com/docker/libnetwork/drivers/null"
 	"github.com/docker/libnetwork/drivers/null"
+	"github.com/docker/libnetwork/drivers/remote"
 )
 )
 
 
 type driverTable map[string]driverapi.Driver
 type driverTable map[string]driverapi.Driver
 
 
-func enumerateDrivers() driverTable {
+func enumerateDrivers(dc driverapi.DriverCallback) driverTable {
 	drivers := make(driverTable)
 	drivers := make(driverTable)
 
 
-	for _, fn := range [](func() (string, driverapi.Driver)){
+	for _, fn := range [](func(driverapi.DriverCallback) (string, driverapi.Driver)){
 		bridge.New,
 		bridge.New,
 		host.New,
 		host.New,
 		null.New,
 		null.New,
+		remote.New,
 	} {
 	} {
-		name, driver := fn()
+		name, driver := fn(dc)
 		drivers[name] = driver
 		drivers[name] = driver
 	}
 	}
 
 

+ 1 - 1
libnetwork/drivers/bridge/bridge.go

@@ -93,7 +93,7 @@ func init() {
 }
 }
 
 
 // New provides a new instance of bridge driver
 // New provides a new instance of bridge driver
-func New() (string, driverapi.Driver) {
+func New(dc driverapi.DriverCallback) (string, driverapi.Driver) {
 	return networkType, &driver{}
 	return networkType, &driver{}
 }
 }
 
 

+ 7 - 7
libnetwork/drivers/bridge/bridge_test.go

@@ -15,7 +15,7 @@ import (
 
 
 func TestCreateFullOptions(t *testing.T) {
 func TestCreateFullOptions(t *testing.T) {
 	defer netutils.SetupTestNetNS(t)()
 	defer netutils.SetupTestNetNS(t)()
-	_, d := New()
+	_, d := New(nil)
 
 
 	config := &Configuration{
 	config := &Configuration{
 		EnableIPForwarding: true,
 		EnableIPForwarding: true,
@@ -46,7 +46,7 @@ func TestCreateFullOptions(t *testing.T) {
 
 
 func TestCreate(t *testing.T) {
 func TestCreate(t *testing.T) {
 	defer netutils.SetupTestNetNS(t)()
 	defer netutils.SetupTestNetNS(t)()
-	_, d := New()
+	_, d := New(nil)
 
 
 	config := &NetworkConfiguration{BridgeName: DefaultBridgeName}
 	config := &NetworkConfiguration{BridgeName: DefaultBridgeName}
 	genericOption := make(map[string]interface{})
 	genericOption := make(map[string]interface{})
@@ -59,7 +59,7 @@ func TestCreate(t *testing.T) {
 
 
 func TestCreateFail(t *testing.T) {
 func TestCreateFail(t *testing.T) {
 	defer netutils.SetupTestNetNS(t)()
 	defer netutils.SetupTestNetNS(t)()
-	_, d := New()
+	_, d := New(nil)
 
 
 	config := &NetworkConfiguration{BridgeName: "dummy0"}
 	config := &NetworkConfiguration{BridgeName: "dummy0"}
 	genericOption := make(map[string]interface{})
 	genericOption := make(map[string]interface{})
@@ -73,7 +73,7 @@ func TestCreateFail(t *testing.T) {
 func TestQueryEndpointInfo(t *testing.T) {
 func TestQueryEndpointInfo(t *testing.T) {
 	defer netutils.SetupTestNetNS(t)()
 	defer netutils.SetupTestNetNS(t)()
 
 
-	_, d := New()
+	_, d := New(nil)
 
 
 	config := &NetworkConfiguration{
 	config := &NetworkConfiguration{
 		BridgeName:     DefaultBridgeName,
 		BridgeName:     DefaultBridgeName,
@@ -130,7 +130,7 @@ func TestQueryEndpointInfo(t *testing.T) {
 func TestCreateLinkWithOptions(t *testing.T) {
 func TestCreateLinkWithOptions(t *testing.T) {
 	defer netutils.SetupTestNetNS(t)()
 	defer netutils.SetupTestNetNS(t)()
 
 
-	_, d := New()
+	_, d := New(nil)
 
 
 	config := &NetworkConfiguration{BridgeName: DefaultBridgeName}
 	config := &NetworkConfiguration{BridgeName: DefaultBridgeName}
 	netOptions := make(map[string]interface{})
 	netOptions := make(map[string]interface{})
@@ -180,7 +180,7 @@ func getPortMapping() []netutils.PortBinding {
 func TestLinkContainers(t *testing.T) {
 func TestLinkContainers(t *testing.T) {
 	defer netutils.SetupTestNetNS(t)()
 	defer netutils.SetupTestNetNS(t)()
 
 
-	_, d := New()
+	_, d := New(nil)
 
 
 	config := &NetworkConfiguration{
 	config := &NetworkConfiguration{
 		BridgeName:     DefaultBridgeName,
 		BridgeName:     DefaultBridgeName,
@@ -385,7 +385,7 @@ func TestValidateConfig(t *testing.T) {
 
 
 func TestSetDefaultGw(t *testing.T) {
 func TestSetDefaultGw(t *testing.T) {
 	defer netutils.SetupTestNetNS(t)()
 	defer netutils.SetupTestNetNS(t)()
-	_, d := New()
+	_, d := New(nil)
 
 
 	_, subnetv6, _ := net.ParseCIDR("2001:db8:ea9:9abc:b0c4::/80")
 	_, subnetv6, _ := net.ParseCIDR("2001:db8:ea9:9abc:b0c4::/80")
 	gw4 := bridgeNetworks[0].IP.To4()
 	gw4 := bridgeNetworks[0].IP.To4()

+ 4 - 4
libnetwork/drivers/bridge/network_test.go

@@ -11,7 +11,7 @@ import (
 
 
 func TestLinkCreate(t *testing.T) {
 func TestLinkCreate(t *testing.T) {
 	defer netutils.SetupTestNetNS(t)()
 	defer netutils.SetupTestNetNS(t)()
-	_, d := New()
+	_, d := New(nil)
 	dr := d.(*driver)
 	dr := d.(*driver)
 
 
 	mtu := 1490
 	mtu := 1490
@@ -97,7 +97,7 @@ func TestLinkCreate(t *testing.T) {
 
 
 func TestLinkCreateTwo(t *testing.T) {
 func TestLinkCreateTwo(t *testing.T) {
 	defer netutils.SetupTestNetNS(t)()
 	defer netutils.SetupTestNetNS(t)()
-	_, d := New()
+	_, d := New(nil)
 
 
 	config := &NetworkConfiguration{
 	config := &NetworkConfiguration{
 		BridgeName: DefaultBridgeName,
 		BridgeName: DefaultBridgeName,
@@ -127,7 +127,7 @@ func TestLinkCreateTwo(t *testing.T) {
 
 
 func TestLinkCreateNoEnableIPv6(t *testing.T) {
 func TestLinkCreateNoEnableIPv6(t *testing.T) {
 	defer netutils.SetupTestNetNS(t)()
 	defer netutils.SetupTestNetNS(t)()
-	_, d := New()
+	_, d := New(nil)
 
 
 	config := &NetworkConfiguration{
 	config := &NetworkConfiguration{
 		BridgeName: DefaultBridgeName}
 		BridgeName: DefaultBridgeName}
@@ -156,7 +156,7 @@ func TestLinkCreateNoEnableIPv6(t *testing.T) {
 
 
 func TestLinkDelete(t *testing.T) {
 func TestLinkDelete(t *testing.T) {
 	defer netutils.SetupTestNetNS(t)()
 	defer netutils.SetupTestNetNS(t)()
-	_, d := New()
+	_, d := New(nil)
 
 
 	config := &NetworkConfiguration{
 	config := &NetworkConfiguration{
 		BridgeName: DefaultBridgeName,
 		BridgeName: DefaultBridgeName,

+ 1 - 1
libnetwork/drivers/bridge/port_mapping_test.go

@@ -18,7 +18,7 @@ func TestMain(m *testing.M) {
 
 
 func TestPortMappingConfig(t *testing.T) {
 func TestPortMappingConfig(t *testing.T) {
 	defer netutils.SetupTestNetNS(t)()
 	defer netutils.SetupTestNetNS(t)()
-	_, d := New()
+	_, d := New(nil)
 
 
 	binding1 := netutils.PortBinding{Proto: netutils.UDP, Port: uint16(400), HostPort: uint16(54000)}
 	binding1 := netutils.PortBinding{Proto: netutils.UDP, Port: uint16(400), HostPort: uint16(54000)}
 	binding2 := netutils.PortBinding{Proto: netutils.TCP, Port: uint16(500), HostPort: uint16(65000)}
 	binding2 := netutils.PortBinding{Proto: netutils.TCP, Port: uint16(500), HostPort: uint16(65000)}

+ 1 - 1
libnetwork/drivers/host/host.go

@@ -11,7 +11,7 @@ const networkType = "host"
 type driver struct{}
 type driver struct{}
 
 
 // New provides a new instance of host driver
 // New provides a new instance of host driver
-func New() (string, driverapi.Driver) {
+func New(dc driverapi.DriverCallback) (string, driverapi.Driver) {
 	return networkType, &driver{}
 	return networkType, &driver{}
 }
 }
 
 

+ 1 - 1
libnetwork/drivers/null/null.go

@@ -11,7 +11,7 @@ const networkType = "null"
 type driver struct{}
 type driver struct{}
 
 
 // New provides a new instance of null driver
 // New provides a new instance of null driver
-func New() (string, driverapi.Driver) {
+func New(dc driverapi.DriverCallback) (string, driverapi.Driver) {
 	return networkType, &driver{}
 	return networkType, &driver{}
 }
 }
 
 

+ 75 - 0
libnetwork/drivers/remote/driver.go

@@ -0,0 +1,75 @@
+package remote
+
+import (
+	"errors"
+
+	"github.com/docker/libnetwork/driverapi"
+	"github.com/docker/libnetwork/sandbox"
+	"github.com/docker/libnetwork/types"
+)
+
+var errNoCallback = errors.New("No Callback handler registered with Driver")
+
+const networkType = "remote"
+
+type driver struct {
+	callback driverapi.DriverCallback
+}
+
+// New instance of remote driver returned to LibNetwork
+func New(dc driverapi.DriverCallback) (string, driverapi.Driver) {
+	d := &driver{}
+	d.callback = dc
+	return networkType, d
+}
+
+// Internal Convenience method to register a remote driver.
+// The implementation of this method will change based on the dynamic driver registration design
+func (d *driver) registerRemoteDriver(networkType string) (driverapi.Driver, error) {
+	newDriver := &driver{}
+	if d.callback == nil {
+		return nil, errNoCallback
+	}
+	if err := d.callback.RegisterDriver(networkType, newDriver); err != nil {
+		return nil, err
+	}
+	return newDriver, nil
+}
+
+func (d *driver) Config(option map[string]interface{}) error {
+	return driverapi.ErrNotImplemented
+}
+
+func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) error {
+	return driverapi.ErrNotImplemented
+}
+
+func (d *driver) DeleteNetwork(nid types.UUID) error {
+	return driverapi.ErrNotImplemented
+}
+
+func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interface{}) (*sandbox.Info, error) {
+	return nil, driverapi.ErrNotImplemented
+}
+
+func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
+	return driverapi.ErrNotImplemented
+}
+
+func (d *driver) EndpointInfo(nid, eid types.UUID) (map[string]interface{}, error) {
+	return nil, driverapi.ErrNotImplemented
+}
+
+// Join method is invoked when a Sandbox is attached to an endpoint.
+func (d *driver) Join(nid, eid types.UUID, sboxKey string, options map[string]interface{}) (*driverapi.JoinInfo, error) {
+	return nil, driverapi.ErrNotImplemented
+}
+
+// Leave method is invoked when a Sandbox detaches from an endpoint.
+func (d *driver) Leave(nid, eid types.UUID, options map[string]interface{}) error {
+	return driverapi.ErrNotImplemented
+}
+
+func (d *driver) Type() string {
+	return networkType
+}

+ 29 - 0
libnetwork/drivers/remote/driver_test.go

@@ -0,0 +1,29 @@
+package remote
+
+import (
+	"testing"
+
+	"github.com/docker/libnetwork/driverapi"
+)
+
+type testCallbackStruct struct {
+	networkType string
+}
+
+func (t *testCallbackStruct) RegisterDriver(networkType string, driver driverapi.Driver) error {
+	t.networkType = networkType
+	return nil
+}
+
+func TestCallback(t *testing.T) {
+	tc := &testCallbackStruct{}
+	_, d := New(tc)
+	expected := "test-dummy"
+	_, err := d.(*driver).registerRemoteDriver(expected)
+	if err != nil {
+		t.Fatalf("Remote Driver callback registration failed with Error : %v", err)
+	}
+	if tc.networkType != expected {
+		t.Fatal("Remote Driver Callback Registration failed")
+	}
+}

+ 23 - 0
libnetwork/libnetwork_internal_test.go

@@ -0,0 +1,23 @@
+package libnetwork
+
+import (
+	"testing"
+
+	"github.com/docker/libnetwork/driverapi"
+)
+
+func TestDriverRegistration(t *testing.T) {
+	bridgeNetType := "bridge"
+	c := New()
+	err := c.(*controller).RegisterDriver(bridgeNetType, nil)
+	if err == nil {
+		t.Fatalf("Expecting the RegisterDriver to fail for %s", bridgeNetType)
+	}
+	if _, ok := err.(driverapi.ErrActiveRegistration); !ok {
+		t.Fatalf("Failed for unexpected reason: %v", err)
+	}
+	err = c.(*controller).RegisterDriver("test-dummy", nil)
+	if err != nil {
+		t.Fatalf("Test failed with an error %v", err)
+	}
+}