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>
This commit is contained in:
Madhu Venugopal 2015-05-06 16:57:38 -07:00
parent 0d45fb66b3
commit 904a313396
13 changed files with 184 additions and 20 deletions

View file

@ -49,6 +49,7 @@ import (
"sync"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/libnetwork/driverapi"
"github.com/docker/libnetwork/sandbox"
"github.com/docker/libnetwork/types"
)
@ -98,22 +99,39 @@ type controller struct {
// New creates a new instance of network controller.
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 {
c.Lock()
d, ok := c.drivers[networkType]
c.Unlock()
if !ok {
return NetworkTypeError(networkType)
}
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
// are network specific and modeled in a generic way.
func (c *controller) NewNetwork(networkType, name string, options ...NetworkOption) (Network, error) {
// Check if a driver for the specified network type is available
c.Lock()
d, ok := c.drivers[networkType]
c.Unlock()
if !ok {
return nil, ErrInvalidNetworkDriver
}

View file

@ -5,7 +5,7 @@ Remote driver is a special built-in driver. This driver in itself doesn't provid
## 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.

View file

@ -2,6 +2,7 @@ package driverapi
import (
"errors"
"fmt"
"github.com/docker/libnetwork/sandbox"
"github.com/docker/libnetwork/types"
@ -14,6 +15,8 @@ var (
ErrNoNetwork = errors.New("No network exists")
// ErrNoEndpoint is returned if no endpoint with the specified id 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.
@ -58,3 +61,17 @@ type Driver interface {
type JoinInfo struct {
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
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
}

View file

@ -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")
}
}

View file

@ -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)
}
}