diff --git a/libnetwork/controller.go b/libnetwork/controller.go index 275994c24b..51c5a16a01 100644 --- a/libnetwork/controller.go +++ b/libnetwork/controller.go @@ -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 } diff --git a/libnetwork/docs/remote.md b/libnetwork/docs/remote.md index 8f7b174a84..96e91720a9 100644 --- a/libnetwork/docs/remote.md +++ b/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 -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. diff --git a/libnetwork/driverapi/driverapi.go b/libnetwork/driverapi/driverapi.go index 5f5637b647..a6b7bfc5a2 100644 --- a/libnetwork/driverapi/driverapi.go +++ b/libnetwork/driverapi/driverapi.go @@ -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 +} diff --git a/libnetwork/drivers.go b/libnetwork/drivers.go index da1da77daf..febc2988f2 100644 --- a/libnetwork/drivers.go +++ b/libnetwork/drivers.go @@ -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 } diff --git a/libnetwork/drivers/bridge/bridge.go b/libnetwork/drivers/bridge/bridge.go index c3cf1e1817..353fd7ffcc 100644 --- a/libnetwork/drivers/bridge/bridge.go +++ b/libnetwork/drivers/bridge/bridge.go @@ -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{} } diff --git a/libnetwork/drivers/bridge/bridge_test.go b/libnetwork/drivers/bridge/bridge_test.go index 04f9a19ea1..2e3ecebfd3 100644 --- a/libnetwork/drivers/bridge/bridge_test.go +++ b/libnetwork/drivers/bridge/bridge_test.go @@ -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() diff --git a/libnetwork/drivers/bridge/network_test.go b/libnetwork/drivers/bridge/network_test.go index 73135b705b..8f0b3a6f34 100644 --- a/libnetwork/drivers/bridge/network_test.go +++ b/libnetwork/drivers/bridge/network_test.go @@ -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, diff --git a/libnetwork/drivers/bridge/port_mapping_test.go b/libnetwork/drivers/bridge/port_mapping_test.go index 23c7b0536a..93c5788364 100644 --- a/libnetwork/drivers/bridge/port_mapping_test.go +++ b/libnetwork/drivers/bridge/port_mapping_test.go @@ -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)} diff --git a/libnetwork/drivers/host/host.go b/libnetwork/drivers/host/host.go index c218fbdd10..cd39ad0977 100644 --- a/libnetwork/drivers/host/host.go +++ b/libnetwork/drivers/host/host.go @@ -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{} } diff --git a/libnetwork/drivers/null/null.go b/libnetwork/drivers/null/null.go index 0f9cb2d76d..e2ba62b5d9 100644 --- a/libnetwork/drivers/null/null.go +++ b/libnetwork/drivers/null/null.go @@ -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{} } diff --git a/libnetwork/drivers/remote/driver.go b/libnetwork/drivers/remote/driver.go new file mode 100644 index 0000000000..ae1de3ded0 --- /dev/null +++ b/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 +} diff --git a/libnetwork/drivers/remote/driver_test.go b/libnetwork/drivers/remote/driver_test.go new file mode 100644 index 0000000000..42c7992fb2 --- /dev/null +++ b/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") + } +} diff --git a/libnetwork/libnetwork_internal_test.go b/libnetwork/libnetwork_internal_test.go new file mode 100644 index 0000000000..2eeda80bb5 --- /dev/null +++ b/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) + } +}