Replacing isReservedNetwork with Driver capability

Currently store makes use of a static isReservedNetwork check to decide
if a network needs to be stored in the distributed store or not. But it
is better if the check is not static, but be determined based on the
capability of the driver that backs the network.

Hence introducing a new capability mechanism to the driver which it can
express its capability during registration. Making use of first such
capability : Scope. This can be expanded in the future for more such cases.

Signed-off-by: Madhu Venugopal <madhu@docker.com>
This commit is contained in:
Madhu Venugopal 2015-06-06 10:21:51 -07:00
parent f88824fb8a
commit 9e8974cc64
13 changed files with 93 additions and 43 deletions

View File

@ -92,6 +92,12 @@ type NetworkController interface {
// When the function returns true, the walk will stop.
type NetworkWalker func(nw Network) bool
type driverData struct {
driver driverapi.Driver
capability driverapi.Capability
}
type driverTable map[string]*driverData
type networkTable map[types.UUID]*network
type endpointTable map[types.UUID]*endpoint
type sandboxTable map[string]*sandboxData
@ -175,21 +181,21 @@ func (c *controller) hostLeaveCallback(hosts []net.IP) {
func (c *controller) ConfigureNetworkDriver(networkType string, options map[string]interface{}) error {
c.Lock()
d, ok := c.drivers[networkType]
dd, ok := c.drivers[networkType]
c.Unlock()
if !ok {
return NetworkTypeError(networkType)
}
return d.Config(options)
return dd.driver.Config(options)
}
func (c *controller) RegisterDriver(networkType string, driver driverapi.Driver) error {
func (c *controller) RegisterDriver(networkType string, driver driverapi.Driver, capability driverapi.Capability) error {
c.Lock()
defer c.Unlock()
if _, ok := c.drivers[networkType]; ok {
return driverapi.ErrActiveRegistration(networkType)
}
c.drivers[networkType] = driver
c.drivers[networkType] = &driverData{driver, capability}
return nil
}
@ -238,18 +244,21 @@ func (c *controller) addNetwork(n *network) error {
c.Lock()
// Check if a driver for the specified network type is available
d, ok := c.drivers[n.networkType]
dd, ok := c.drivers[n.networkType]
c.Unlock()
if !ok {
var err error
d, err = c.loadDriver(n.networkType)
dd, err = c.loadDriver(n.networkType)
if err != nil {
return err
}
}
n.driver = d
n.Lock()
n.driver = dd.driver
d := n.driver
n.Unlock()
// Create the network
if err := d.CreateNetwork(n.id, n.generic); err != nil {
@ -317,7 +326,7 @@ func (c *controller) NetworkByID(id string) (Network, error) {
return nil, ErrNoSuchNetwork(id)
}
func (c *controller) loadDriver(networkType string) (driverapi.Driver, error) {
func (c *controller) loadDriver(networkType string) (*driverData, error) {
// Plugins pkg performs lazy loading of plugins that acts as remote drivers.
// As per the design, this Get call will result in remote driver discovery if there is a corresponding plugin available.
_, err := plugins.Get(networkType, driverapi.NetworkPluginEndpointType)
@ -329,11 +338,24 @@ func (c *controller) loadDriver(networkType string) (driverapi.Driver, error) {
}
c.Lock()
defer c.Unlock()
d, ok := c.drivers[networkType]
dd, ok := c.drivers[networkType]
if !ok {
return nil, ErrInvalidNetworkDriver(networkType)
}
return d, nil
return dd, nil
}
func (c *controller) isDriverGlobalScoped(networkType string) (bool, error) {
c.Lock()
dd, ok := c.drivers[networkType]
c.Unlock()
if !ok {
return false, types.NotFoundErrorf("driver not found for %s", networkType)
}
if dd.capability.Scope == driverapi.GlobalScope {
return true, nil
}
return false, nil
}
func (c *controller) GC() {

View File

@ -118,5 +118,20 @@ type JoinInfo interface {
// 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
RegisterDriver(name string, driver Driver, capability Capability) error
}
// Scope indicates the drivers scope capability
type Scope int
const (
// LocalScope represents the driver capable of providing networking services for containers in a single host
LocalScope Scope = iota
// GlobalScope represents the driver capable of providing networking services for containers across hosts
GlobalScope
)
// Capability represents the high level capabilities of the drivers which libnetwork can make use of
type Capability struct {
Scope Scope
}

View File

@ -110,7 +110,10 @@ func Init(dc driverapi.DriverCallback) error {
if out, err := exec.Command("modprobe", "-va", "bridge", "nf_nat", "br_netfilter").Output(); err != nil {
logrus.Warnf("Running modprobe bridge nf_nat failed with message: %s, error: %v", out, err)
}
return dc.RegisterDriver(networkType, newDriver())
c := driverapi.Capability{
Scope: driverapi.LocalScope,
}
return dc.RegisterDriver(networkType, newDriver(), c)
}
// Validate performs a static validation on the network configuration parameters.

View File

@ -16,7 +16,10 @@ type driver struct {
// Init registers a new instance of host driver
func Init(dc driverapi.DriverCallback) error {
return dc.RegisterDriver(networkType, &driver{})
c := driverapi.Capability{
Scope: driverapi.LocalScope,
}
return dc.RegisterDriver(networkType, &driver{}, c)
}
func (d *driver) Config(option map[string]interface{}) error {

View File

@ -16,7 +16,10 @@ type driver struct {
// Init registers a new instance of null driver
func Init(dc driverapi.DriverCallback) error {
return dc.RegisterDriver(networkType, &driver{})
c := driverapi.Capability{
Scope: driverapi.LocalScope,
}
return dc.RegisterDriver(networkType, &driver{}, c)
}
func (d *driver) Config(option map[string]interface{}) error {

View File

@ -23,7 +23,10 @@ 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) {
if err := dc.RegisterDriver(name, newDriver(name, client)); err != nil {
c := driverapi.Capability{
Scope: driverapi.GlobalScope,
}
if err := dc.RegisterDriver(name, newDriver(name, client), c); err != nil {
log.Errorf("error registering driver for %s due to %v", name, err)
}
})

View File

@ -13,7 +13,10 @@ type driver struct{}
// Init registers a new instance of null driver
func Init(dc driverapi.DriverCallback) error {
return dc.RegisterDriver(networkType, &driver{})
c := driverapi.Capability{
Scope: driverapi.LocalScope,
}
return dc.RegisterDriver(networkType, &driver{}, c)
}
func (d *driver) Config(option map[string]interface{}) error {

View File

@ -6,8 +6,6 @@ import (
"github.com/docker/libnetwork/drivers/remote"
)
type driverTable map[string]driverapi.Driver
func initDrivers(dc driverapi.DriverCallback) error {
for _, fn := range [](func(driverapi.DriverCallback) error){
null.Init,

View File

@ -8,8 +8,6 @@ import (
"github.com/docker/libnetwork/drivers/remote"
)
type driverTable map[string]driverapi.Driver
func initDrivers(dc driverapi.DriverCallback) error {
for _, fn := range [](func(driverapi.DriverCallback) error){
bridge.Init,

View File

@ -5,8 +5,6 @@ import (
"github.com/docker/libnetwork/drivers/windows"
)
type driverTable map[string]driverapi.Driver
func initDrivers(dc driverapi.DriverCallback) error {
for _, fn := range [](func(driverapi.DriverCallback) error){
windows.Init,

View File

@ -13,14 +13,14 @@ func TestDriverRegistration(t *testing.T) {
if err != nil {
t.Fatal(err)
}
err = c.(*controller).RegisterDriver(bridgeNetType, nil)
err = c.(*controller).RegisterDriver(bridgeNetType, nil, driverapi.Capability{})
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)
err = c.(*controller).RegisterDriver("test-dummy", nil, driverapi.Capability{})
if err != nil {
t.Fatalf("Test failed with an error %v", err)
}

View File

@ -2,7 +2,6 @@ package libnetwork
import (
"encoding/json"
"strings"
"sync"
log "github.com/Sirupsen/logrus"
@ -377,12 +376,9 @@ func (n *network) EndpointByID(id string) (Endpoint, error) {
return nil, ErrNoSuchEndpoint(id)
}
func isReservedNetwork(name string) bool {
reserved := []string{"bridge", "none", "host"}
for _, r := range reserved {
if strings.EqualFold(r, name) {
return true
}
}
return false
func (n *network) isGlobalScoped() (bool, error) {
n.Lock()
c := n.ctrlr
n.Unlock()
return c.isDriverGlobalScoped(n.networkType)
}

View File

@ -37,8 +37,9 @@ func (c *controller) newNetworkFromStore(n *network) error {
}
func (c *controller) updateNetworkToStore(n *network) error {
if isReservedNetwork(n.Name()) {
return nil
global, err := n.isGlobalScoped()
if err != nil || !global {
return err
}
c.Lock()
cs := c.store
@ -52,8 +53,9 @@ func (c *controller) updateNetworkToStore(n *network) error {
}
func (c *controller) deleteNetworkFromStore(n *network) error {
if isReservedNetwork(n.Name()) {
return nil
global, err := n.isGlobalScoped()
if err != nil || !global {
return err
}
c.Lock()
cs := c.store
@ -111,12 +113,13 @@ func (c *controller) newEndpointFromStore(key string, ep *endpoint) error {
func (c *controller) updateEndpointToStore(ep *endpoint) error {
ep.Lock()
n := ep.network
name := ep.name
if isReservedNetwork(ep.network.name) {
ep.Unlock()
return nil
}
ep.Unlock()
global, err := n.isGlobalScoped()
if err != nil || !global {
return err
}
c.Lock()
cs := c.store
c.Unlock()
@ -137,9 +140,14 @@ func (c *controller) getEndpointFromStore(eid types.UUID) (*endpoint, error) {
}
func (c *controller) deleteEndpointFromStore(ep *endpoint) error {
if isReservedNetwork(ep.network.Name()) {
return nil
ep.Lock()
n := ep.network
ep.Unlock()
global, err := n.isGlobalScoped()
if err != nil || !global {
return err
}
c.Lock()
cs := c.store
c.Unlock()