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:
parent
0d45fb66b3
commit
904a313396
13 changed files with 184 additions and 20 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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{}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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{}
|
||||
}
|
||||
|
||||
|
|
|
@ -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{}
|
||||
}
|
||||
|
||||
|
|
75
libnetwork/drivers/remote/driver.go
Normal file
75
libnetwork/drivers/remote/driver.go
Normal 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
|
||||
}
|
29
libnetwork/drivers/remote/driver_test.go
Normal file
29
libnetwork/drivers/remote/driver_test.go
Normal 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")
|
||||
}
|
||||
}
|
23
libnetwork/libnetwork_internal_test.go
Normal file
23
libnetwork/libnetwork_internal_test.go
Normal 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)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue