mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Create driver registry package
Currently driver management logic is tightly coupled with libnetwork package and that makes it very difficult to modularize it and use it separately. This PR modularizes the driver management logic by creating a driver registry package. Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
This commit is contained in:
parent
77c66f968b
commit
f539be8a63
7 changed files with 568 additions and 324 deletions
|
@ -58,6 +58,7 @@ import (
|
|||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/drvregistry"
|
||||
"github.com/docker/libnetwork/hostdiscovery"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
|
@ -119,26 +120,11 @@ type NetworkWalker func(nw Network) bool
|
|||
// When the function returns true, the walk will stop.
|
||||
type SandboxWalker func(sb Sandbox) bool
|
||||
|
||||
type driverData struct {
|
||||
driver driverapi.Driver
|
||||
capability driverapi.Capability
|
||||
}
|
||||
|
||||
type ipamData struct {
|
||||
driver ipamapi.Ipam
|
||||
capability *ipamapi.Capability
|
||||
// default address spaces are provided by ipam driver at registration time
|
||||
defaultLocalAddressSpace, defaultGlobalAddressSpace string
|
||||
}
|
||||
|
||||
type driverTable map[string]*driverData
|
||||
type ipamTable map[string]*ipamData
|
||||
type sandboxTable map[string]*sandbox
|
||||
|
||||
type controller struct {
|
||||
id string
|
||||
drivers driverTable
|
||||
ipamDrivers ipamTable
|
||||
drvRegistry *drvregistry.DrvRegistry
|
||||
sandboxes sandboxTable
|
||||
cfg *config.Config
|
||||
stores []datastore.DataStore
|
||||
|
@ -153,21 +139,44 @@ type controller struct {
|
|||
sync.Mutex
|
||||
}
|
||||
|
||||
type initializer struct {
|
||||
fn drvregistry.InitFunc
|
||||
ntype string
|
||||
}
|
||||
|
||||
// New creates a new instance of network controller.
|
||||
func New(cfgOptions ...config.Option) (NetworkController, error) {
|
||||
c := &controller{
|
||||
id: stringid.GenerateRandomID(),
|
||||
cfg: config.ParseConfigOptions(cfgOptions...),
|
||||
sandboxes: sandboxTable{},
|
||||
drivers: driverTable{},
|
||||
ipamDrivers: ipamTable{},
|
||||
svcDb: make(map[string]svcInfo),
|
||||
id: stringid.GenerateRandomID(),
|
||||
cfg: config.ParseConfigOptions(cfgOptions...),
|
||||
sandboxes: sandboxTable{},
|
||||
svcDb: make(map[string]svcInfo),
|
||||
}
|
||||
|
||||
if err := c.initStores(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
drvRegistry, err := drvregistry.New(c.getStore(datastore.LocalScope), c.getStore(datastore.GlobalScope), c.RegisterDriver, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, i := range getInitializers() {
|
||||
var dcfg map[string]interface{}
|
||||
|
||||
// External plugins don't need config passed through daemon. They can
|
||||
// bootstrap themselves
|
||||
if i.ntype != "remote" {
|
||||
dcfg = c.makeDriverConfig(i.ntype)
|
||||
}
|
||||
|
||||
if err := drvRegistry.AddDriver(i.ntype, i.fn, dcfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
c.drvRegistry = drvRegistry
|
||||
|
||||
if c.cfg != nil && c.cfg.Cluster.Watcher != nil {
|
||||
if err := c.initDiscovery(c.cfg.Cluster.Watcher); err != nil {
|
||||
// Failing to initialize discovery is a bad situation to be in.
|
||||
|
@ -176,15 +185,6 @@ func New(cfgOptions ...config.Option) (NetworkController, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if err := initDrivers(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := initIpams(c, c.getStore(datastore.LocalScope),
|
||||
c.getStore(datastore.GlobalScope)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.sandboxCleanup()
|
||||
c.cleanupLocalEndpoints()
|
||||
c.networkCleanup()
|
||||
|
@ -196,6 +196,43 @@ func New(cfgOptions ...config.Option) (NetworkController, error) {
|
|||
return c, nil
|
||||
}
|
||||
|
||||
func (c *controller) makeDriverConfig(ntype string) map[string]interface{} {
|
||||
if c.cfg == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
config := make(map[string]interface{})
|
||||
|
||||
for _, label := range c.cfg.Daemon.Labels {
|
||||
if !strings.HasPrefix(netlabel.Key(label), netlabel.DriverPrefix+"."+ntype) {
|
||||
continue
|
||||
}
|
||||
|
||||
config[netlabel.Key(label)] = netlabel.Value(label)
|
||||
}
|
||||
|
||||
drvCfg, ok := c.cfg.Daemon.DriverCfg[ntype]
|
||||
if ok {
|
||||
for k, v := range drvCfg.(map[string]interface{}) {
|
||||
config[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range c.cfg.Scopes {
|
||||
if !v.IsValid() {
|
||||
continue
|
||||
}
|
||||
config[netlabel.MakeKVClient(k)] = discoverapi.DatastoreConfigData{
|
||||
Scope: k,
|
||||
Provider: v.Client.Provider,
|
||||
Address: v.Client.Address,
|
||||
Config: v.Client.Config,
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
var procReloadConfig = make(chan (bool), 1)
|
||||
|
||||
func (c *controller) ReloadConfiguration(cfgOptions ...config.Option) error {
|
||||
|
@ -255,19 +292,21 @@ func (c *controller) ReloadConfiguration(cfgOptions ...config.Option) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
for nm, id := range c.getIpamDrivers() {
|
||||
err := id.driver.DiscoverNew(discoverapi.DatastoreConfig, *dsConfig)
|
||||
c.drvRegistry.WalkIPAMs(func(name string, driver ipamapi.Ipam, cap *ipamapi.Capability) bool {
|
||||
err := driver.DiscoverNew(discoverapi.DatastoreConfig, *dsConfig)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to set datastore in driver %s: %v", nm, err)
|
||||
log.Errorf("Failed to set datastore in driver %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
for nm, id := range c.getNetDrivers() {
|
||||
err := id.driver.DiscoverNew(discoverapi.DatastoreConfig, *dsConfig)
|
||||
c.drvRegistry.WalkDrivers(func(name string, driver driverapi.Driver, capability driverapi.Capability) bool {
|
||||
err := driver.DiscoverNew(discoverapi.DatastoreConfig, *dsConfig)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to set datastore in driver %s: %v", nm, err)
|
||||
log.Errorf("Failed to set datastore in driver %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -333,34 +372,30 @@ func (c *controller) hostLeaveCallback(nodes []net.IP) {
|
|||
}
|
||||
|
||||
func (c *controller) processNodeDiscovery(nodes []net.IP, add bool) {
|
||||
c.Lock()
|
||||
drivers := []*driverData{}
|
||||
for _, d := range c.drivers {
|
||||
drivers = append(drivers, d)
|
||||
}
|
||||
c.Unlock()
|
||||
|
||||
for _, d := range drivers {
|
||||
c.pushNodeDiscovery(d, nodes, add)
|
||||
}
|
||||
c.drvRegistry.WalkDrivers(func(name string, driver driverapi.Driver, capability driverapi.Capability) bool {
|
||||
c.pushNodeDiscovery(driver, capability, nodes, add)
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func (c *controller) pushNodeDiscovery(d *driverData, nodes []net.IP, add bool) {
|
||||
func (c *controller) pushNodeDiscovery(d driverapi.Driver, cap driverapi.Capability, nodes []net.IP, add bool) {
|
||||
var self net.IP
|
||||
if c.cfg != nil {
|
||||
addr := strings.Split(c.cfg.Cluster.Address, ":")
|
||||
self = net.ParseIP(addr[0])
|
||||
}
|
||||
if d == nil || d.capability.DataScope != datastore.GlobalScope || nodes == nil {
|
||||
|
||||
if d == nil || cap.DataScope != datastore.GlobalScope || nodes == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, node := range nodes {
|
||||
nodeData := discoverapi.NodeDiscoveryData{Address: node.String(), Self: node.Equal(self)}
|
||||
var err error
|
||||
if add {
|
||||
err = d.driver.DiscoverNew(discoverapi.NodeDiscovery, nodeData)
|
||||
err = d.DiscoverNew(discoverapi.NodeDiscovery, nodeData)
|
||||
} else {
|
||||
err = d.driver.DiscoverDelete(discoverapi.NodeDiscovery, nodeData)
|
||||
err = d.DiscoverDelete(discoverapi.NodeDiscovery, nodeData)
|
||||
}
|
||||
if err != nil {
|
||||
log.Debugf("discovery notification error : %v", err)
|
||||
|
@ -378,59 +413,17 @@ func (c *controller) Config() config.Config {
|
|||
}
|
||||
|
||||
func (c *controller) RegisterDriver(networkType string, driver driverapi.Driver, capability driverapi.Capability) error {
|
||||
if !config.IsValidName(networkType) {
|
||||
return ErrInvalidName(networkType)
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
if _, ok := c.drivers[networkType]; ok {
|
||||
c.Unlock()
|
||||
return driverapi.ErrActiveRegistration(networkType)
|
||||
}
|
||||
dData := &driverData{driver, capability}
|
||||
c.drivers[networkType] = dData
|
||||
hd := c.discovery
|
||||
c.Unlock()
|
||||
|
||||
if hd != nil {
|
||||
c.pushNodeDiscovery(dData, hd.Fetch(), true)
|
||||
c.pushNodeDiscovery(driver, capability, hd.Fetch(), true)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) registerIpamDriver(name string, driver ipamapi.Ipam, caps *ipamapi.Capability) error {
|
||||
if !config.IsValidName(name) {
|
||||
return ErrInvalidName(name)
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
_, ok := c.ipamDrivers[name]
|
||||
c.Unlock()
|
||||
if ok {
|
||||
return types.ForbiddenErrorf("ipam driver %q already registered", name)
|
||||
}
|
||||
locAS, glbAS, err := driver.GetDefaultAddressSpaces()
|
||||
if err != nil {
|
||||
return types.InternalErrorf("ipam driver %q failed to return default address spaces: %v", name, err)
|
||||
}
|
||||
c.Lock()
|
||||
c.ipamDrivers[name] = &ipamData{driver: driver, defaultLocalAddressSpace: locAS, defaultGlobalAddressSpace: glbAS, capability: caps}
|
||||
c.Unlock()
|
||||
|
||||
log.Debugf("Registering ipam driver: %q", name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) RegisterIpamDriver(name string, driver ipamapi.Ipam) error {
|
||||
return c.registerIpamDriver(name, driver, &ipamapi.Capability{})
|
||||
}
|
||||
|
||||
func (c *controller) RegisterIpamDriverWithCapabilities(name string, driver ipamapi.Ipam, caps *ipamapi.Capability) error {
|
||||
return c.registerIpamDriver(name, driver, caps)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
@ -745,78 +738,47 @@ func SandboxKeyWalker(out *Sandbox, key string) SandboxWalker {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *controller) loadDriver(networkType string) (*driverData, error) {
|
||||
func (c *controller) loadDriver(networkType string) 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)
|
||||
if err != nil {
|
||||
if err == plugins.ErrNotFound {
|
||||
return nil, types.NotFoundErrorf(err.Error())
|
||||
return types.NotFoundErrorf(err.Error())
|
||||
}
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
dd, ok := c.drivers[networkType]
|
||||
if !ok {
|
||||
return nil, ErrInvalidNetworkDriver(networkType)
|
||||
}
|
||||
return dd, nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) loadIpamDriver(name string) (*ipamData, error) {
|
||||
func (c *controller) loadIPAMDriver(name string) error {
|
||||
if _, err := plugins.Get(name, ipamapi.PluginEndpointType); err != nil {
|
||||
if err == plugins.ErrNotFound {
|
||||
return nil, types.NotFoundErrorf(err.Error())
|
||||
return types.NotFoundErrorf(err.Error())
|
||||
}
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
c.Lock()
|
||||
id, ok := c.ipamDrivers[name]
|
||||
c.Unlock()
|
||||
if !ok {
|
||||
return nil, types.BadRequestErrorf("invalid ipam driver: %q", name)
|
||||
}
|
||||
return id, nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) getIPAM(name string) (id *ipamData, err error) {
|
||||
var ok bool
|
||||
c.Lock()
|
||||
id, ok = c.ipamDrivers[name]
|
||||
c.Unlock()
|
||||
if !ok {
|
||||
id, err = c.loadIpamDriver(name)
|
||||
}
|
||||
return id, err
|
||||
}
|
||||
func (c *controller) getIPAMDriver(name string) (ipamapi.Ipam, *ipamapi.Capability, error) {
|
||||
id, cap := c.drvRegistry.IPAM(name)
|
||||
if id == nil {
|
||||
// Might be a plugin name. Try loading it
|
||||
if err := c.loadIPAMDriver(name); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
func (c *controller) getIpamDriver(name string) (ipamapi.Ipam, error) {
|
||||
id, err := c.getIPAM(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// Now that we resolved the plugin, try again looking up the registry
|
||||
id, cap = c.drvRegistry.IPAM(name)
|
||||
if id == nil {
|
||||
return nil, nil, types.BadRequestErrorf("invalid ipam driver: %q", name)
|
||||
}
|
||||
}
|
||||
return id.driver, nil
|
||||
}
|
||||
|
||||
func (c *controller) getIpamDrivers() ipamTable {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
table := ipamTable{}
|
||||
for i, d := range c.ipamDrivers {
|
||||
table[i] = d
|
||||
}
|
||||
return table
|
||||
}
|
||||
|
||||
func (c *controller) getNetDrivers() driverTable {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
table := driverTable{}
|
||||
for i, d := range c.drivers {
|
||||
table[i] = d
|
||||
}
|
||||
return table
|
||||
return id, cap, nil
|
||||
}
|
||||
|
||||
func (c *controller) Stop() {
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
package libnetwork
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
|
||||
builtinIpam "github.com/docker/libnetwork/ipams/builtin"
|
||||
nullIpam "github.com/docker/libnetwork/ipams/null"
|
||||
remoteIpam "github.com/docker/libnetwork/ipams/remote"
|
||||
)
|
||||
|
||||
type initializer struct {
|
||||
fn func(driverapi.DriverCallback, map[string]interface{}) error
|
||||
ntype string
|
||||
}
|
||||
|
||||
func initDrivers(c *controller) error {
|
||||
for _, i := range getInitializers() {
|
||||
if err := i.fn(c, makeDriverConfig(c, i.ntype)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeDriverConfig(c *controller, ntype string) map[string]interface{} {
|
||||
if c.cfg == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
config := make(map[string]interface{})
|
||||
|
||||
for _, label := range c.cfg.Daemon.Labels {
|
||||
if !strings.HasPrefix(netlabel.Key(label), netlabel.DriverPrefix+"."+ntype) {
|
||||
continue
|
||||
}
|
||||
|
||||
config[netlabel.Key(label)] = netlabel.Value(label)
|
||||
}
|
||||
|
||||
drvCfg, ok := c.cfg.Daemon.DriverCfg[ntype]
|
||||
if ok {
|
||||
for k, v := range drvCfg.(map[string]interface{}) {
|
||||
config[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// We don't send datastore configs to external plugins
|
||||
if ntype == "remote" {
|
||||
return config
|
||||
}
|
||||
|
||||
for k, v := range c.cfg.Scopes {
|
||||
if !v.IsValid() {
|
||||
continue
|
||||
}
|
||||
config[netlabel.MakeKVClient(k)] = discoverapi.DatastoreConfigData{
|
||||
Scope: k,
|
||||
Provider: v.Client.Provider,
|
||||
Address: v.Client.Address,
|
||||
Config: v.Client.Config,
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func initIpams(ic ipamapi.Callback, lDs, gDs interface{}) error {
|
||||
for _, fn := range [](func(ipamapi.Callback, interface{}, interface{}) error){
|
||||
builtinIpam.Init,
|
||||
remoteIpam.Init,
|
||||
nullIpam.Init,
|
||||
} {
|
||||
if err := fn(ic, lDs, gDs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
241
libnetwork/drvregistry/drvregistry.go
Normal file
241
libnetwork/drvregistry/drvregistry.go
Normal file
|
@ -0,0 +1,241 @@
|
|||
package drvregistry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/types"
|
||||
|
||||
builtinIpam "github.com/docker/libnetwork/ipams/builtin"
|
||||
nullIpam "github.com/docker/libnetwork/ipams/null"
|
||||
remoteIpam "github.com/docker/libnetwork/ipams/remote"
|
||||
)
|
||||
|
||||
type driverData struct {
|
||||
driver driverapi.Driver
|
||||
capability driverapi.Capability
|
||||
}
|
||||
|
||||
type ipamData struct {
|
||||
driver ipamapi.Ipam
|
||||
capability *ipamapi.Capability
|
||||
// default address spaces are provided by ipam driver at registration time
|
||||
defaultLocalAddressSpace, defaultGlobalAddressSpace string
|
||||
}
|
||||
|
||||
type driverTable map[string]*driverData
|
||||
type ipamTable map[string]*ipamData
|
||||
|
||||
// DrvRegistry holds the registry of all network drivers and IPAM drivers that it knows about.
|
||||
type DrvRegistry struct {
|
||||
sync.Mutex
|
||||
drivers driverTable
|
||||
ipamDrivers ipamTable
|
||||
dfn DriverNotifyFunc
|
||||
ifn IPAMNotifyFunc
|
||||
}
|
||||
|
||||
// Functors definition
|
||||
|
||||
// InitFunc defines the driver initialization function signature.
|
||||
type InitFunc func(driverapi.DriverCallback, map[string]interface{}) error
|
||||
|
||||
// IPAMWalkFunc defines the IPAM driver table walker function signature.
|
||||
type IPAMWalkFunc func(name string, driver ipamapi.Ipam, cap *ipamapi.Capability) bool
|
||||
|
||||
// DriverWalkFunc defines the network driver table walker function signature.
|
||||
type DriverWalkFunc func(name string, driver driverapi.Driver, capability driverapi.Capability) bool
|
||||
|
||||
// IPAMNotifyFunc defines the notify function signature when a new IPAM driver gets registered.
|
||||
type IPAMNotifyFunc func(name string, driver ipamapi.Ipam, cap *ipamapi.Capability) error
|
||||
|
||||
// DriverNotifyFunc defines the notify function signature when a new network driver gets registered.
|
||||
type DriverNotifyFunc func(name string, driver driverapi.Driver, capability driverapi.Capability) error
|
||||
|
||||
// New retruns a new driver registry handle.
|
||||
func New(lDs, gDs interface{}, dfn DriverNotifyFunc, ifn IPAMNotifyFunc) (*DrvRegistry, error) {
|
||||
r := &DrvRegistry{
|
||||
drivers: make(driverTable),
|
||||
ipamDrivers: make(ipamTable),
|
||||
dfn: dfn,
|
||||
ifn: ifn,
|
||||
}
|
||||
|
||||
if err := r.initIPAMs(lDs, gDs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// AddDriver adds a network driver to the registry.
|
||||
func (r *DrvRegistry) AddDriver(ntype string, fn InitFunc, config map[string]interface{}) error {
|
||||
return fn(r, config)
|
||||
}
|
||||
|
||||
// WalkIPAMs walks the IPAM drivers registered in the registry and invokes the passed walk function and each one of them.
|
||||
func (r *DrvRegistry) WalkIPAMs(ifn IPAMWalkFunc) {
|
||||
type ipamVal struct {
|
||||
name string
|
||||
data *ipamData
|
||||
}
|
||||
|
||||
r.Lock()
|
||||
ivl := make([]ipamVal, 0, len(r.ipamDrivers))
|
||||
for k, v := range r.ipamDrivers {
|
||||
ivl = append(ivl, ipamVal{name: k, data: v})
|
||||
}
|
||||
r.Unlock()
|
||||
|
||||
for _, iv := range ivl {
|
||||
if ifn(iv.name, iv.data.driver, iv.data.capability) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WalkDrivers walks the network drivers registered in the registry and invokes the passed walk function and each one of them.
|
||||
func (r *DrvRegistry) WalkDrivers(dfn DriverWalkFunc) {
|
||||
type driverVal struct {
|
||||
name string
|
||||
data *driverData
|
||||
}
|
||||
|
||||
r.Lock()
|
||||
dvl := make([]driverVal, 0, len(r.drivers))
|
||||
for k, v := range r.drivers {
|
||||
dvl = append(dvl, driverVal{name: k, data: v})
|
||||
}
|
||||
r.Unlock()
|
||||
|
||||
for _, dv := range dvl {
|
||||
if dfn(dv.name, dv.data.driver, dv.data.capability) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Driver returns the actual network driver instance and its capability which registered with the passed name.
|
||||
func (r *DrvRegistry) Driver(name string) (driverapi.Driver, *driverapi.Capability) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
d, ok := r.drivers[name]
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return d.driver, &d.capability
|
||||
}
|
||||
|
||||
// IPAM returns the actual IPAM driver instance and its capability which registered with the passed name.
|
||||
func (r *DrvRegistry) IPAM(name string) (ipamapi.Ipam, *ipamapi.Capability) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
i, ok := r.ipamDrivers[name]
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return i.driver, i.capability
|
||||
}
|
||||
|
||||
// IPAMDefaultAddressSpaces returns the default address space strings for the passed IPAM driver name.
|
||||
func (r *DrvRegistry) IPAMDefaultAddressSpaces(name string) (string, string, error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
i, ok := r.ipamDrivers[name]
|
||||
if !ok {
|
||||
return "", "", fmt.Errorf("ipam %s not found", name)
|
||||
}
|
||||
|
||||
return i.defaultLocalAddressSpace, i.defaultGlobalAddressSpace, nil
|
||||
}
|
||||
|
||||
func (r *DrvRegistry) initIPAMs(lDs, gDs interface{}) error {
|
||||
for _, fn := range [](func(ipamapi.Callback, interface{}, interface{}) error){
|
||||
builtinIpam.Init,
|
||||
remoteIpam.Init,
|
||||
nullIpam.Init,
|
||||
} {
|
||||
if err := fn(r, lDs, gDs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterDriver registers the network driver when it gets discovered.
|
||||
func (r *DrvRegistry) RegisterDriver(ntype string, driver driverapi.Driver, capability driverapi.Capability) error {
|
||||
if strings.TrimSpace(ntype) == "" {
|
||||
return fmt.Errorf("network type string cannot be empty")
|
||||
}
|
||||
|
||||
r.Lock()
|
||||
_, ok := r.drivers[ntype]
|
||||
r.Unlock()
|
||||
|
||||
if ok {
|
||||
return driverapi.ErrActiveRegistration(ntype)
|
||||
}
|
||||
|
||||
if r.dfn != nil {
|
||||
if err := r.dfn(ntype, driver, capability); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
dData := &driverData{driver, capability}
|
||||
|
||||
r.Lock()
|
||||
r.drivers[ntype] = dData
|
||||
r.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DrvRegistry) registerIpamDriver(name string, driver ipamapi.Ipam, caps *ipamapi.Capability) error {
|
||||
if strings.TrimSpace(name) == "" {
|
||||
return fmt.Errorf("ipam driver name string cannot be empty")
|
||||
}
|
||||
|
||||
r.Lock()
|
||||
_, ok := r.ipamDrivers[name]
|
||||
r.Unlock()
|
||||
if ok {
|
||||
return types.ForbiddenErrorf("ipam driver %q already registered", name)
|
||||
}
|
||||
|
||||
locAS, glbAS, err := driver.GetDefaultAddressSpaces()
|
||||
if err != nil {
|
||||
return types.InternalErrorf("ipam driver %q failed to return default address spaces: %v", name, err)
|
||||
}
|
||||
|
||||
if r.ifn != nil {
|
||||
if err := r.ifn(name, driver, caps); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
r.Lock()
|
||||
r.ipamDrivers[name] = &ipamData{driver: driver, defaultLocalAddressSpace: locAS, defaultGlobalAddressSpace: glbAS, capability: caps}
|
||||
r.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterIpamDriver registers the IPAM driver discovered with default capabilities.
|
||||
func (r *DrvRegistry) RegisterIpamDriver(name string, driver ipamapi.Ipam) error {
|
||||
return r.registerIpamDriver(name, driver, &ipamapi.Capability{})
|
||||
}
|
||||
|
||||
// RegisterIpamDriverWithCapabilities registers the IPAM driver discovered with specified capabilities.
|
||||
func (r *DrvRegistry) RegisterIpamDriverWithCapabilities(name string, driver ipamapi.Ipam, caps *ipamapi.Capability) error {
|
||||
return r.registerIpamDriver(name, driver, caps)
|
||||
}
|
161
libnetwork/drvregistry/drvregistry_test.go
Normal file
161
libnetwork/drvregistry/drvregistry_test.go
Normal file
|
@ -0,0 +1,161 @@
|
|||
package drvregistry
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var runningInContainer = flag.Bool("incontainer", false,
|
||||
"Indicates if the test is running in a container")
|
||||
|
||||
const mockDriverName = "mock-driver"
|
||||
|
||||
type mockDriver struct{}
|
||||
|
||||
var md = mockDriver{}
|
||||
|
||||
func mockDriverInit(reg driverapi.DriverCallback, opt map[string]interface{}) error {
|
||||
return reg.RegisterDriver(mockDriverName, &md, driverapi.Capability{DataScope: datastore.LocalScope})
|
||||
}
|
||||
|
||||
func (m *mockDriver) CreateNetwork(nid string, options map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockDriver) DeleteNetwork(nid string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockDriver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, options map[string]interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockDriver) DeleteEndpoint(nid, eid string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockDriver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *mockDriver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockDriver) Leave(nid, eid string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockDriver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockDriver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockDriver) Type() string {
|
||||
return mockDriverName
|
||||
}
|
||||
|
||||
func (m *mockDriver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockDriver) RevokeExternalConnectivity(nid, eid string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getNew(t *testing.T) *DrvRegistry {
|
||||
reg, err := New(nil, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return reg
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
getNew(t)
|
||||
}
|
||||
|
||||
func TestAddDriver(t *testing.T) {
|
||||
reg := getNew(t)
|
||||
|
||||
err := reg.AddDriver(mockDriverName, mockDriverInit, nil)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestAddDuplicateDriver(t *testing.T) {
|
||||
reg := getNew(t)
|
||||
|
||||
err := reg.AddDriver(mockDriverName, mockDriverInit, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Try adding the same driver
|
||||
err = reg.AddDriver(mockDriverName, mockDriverInit, nil)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestIPAMDefaultAddressSpaces(t *testing.T) {
|
||||
reg := getNew(t)
|
||||
|
||||
as1, as2, err := reg.IPAMDefaultAddressSpaces("default")
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, as1, "")
|
||||
assert.NotEqual(t, as2, "")
|
||||
}
|
||||
|
||||
func TestDriver(t *testing.T) {
|
||||
reg := getNew(t)
|
||||
|
||||
err := reg.AddDriver(mockDriverName, mockDriverInit, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
d, cap := reg.Driver(mockDriverName)
|
||||
assert.NotEqual(t, d, nil)
|
||||
assert.NotEqual(t, cap, nil)
|
||||
}
|
||||
|
||||
func TestIPAM(t *testing.T) {
|
||||
reg := getNew(t)
|
||||
|
||||
i, cap := reg.IPAM("default")
|
||||
assert.NotEqual(t, i, nil)
|
||||
assert.NotEqual(t, cap, nil)
|
||||
}
|
||||
|
||||
func TestWalkIPAMs(t *testing.T) {
|
||||
reg := getNew(t)
|
||||
|
||||
ipams := make([]string, 0, 2)
|
||||
reg.WalkIPAMs(func(name string, driver ipamapi.Ipam, cap *ipamapi.Capability) bool {
|
||||
ipams = append(ipams, name)
|
||||
return false
|
||||
})
|
||||
|
||||
sort.Strings(ipams)
|
||||
assert.Equal(t, ipams, []string{"default", "null"})
|
||||
}
|
||||
|
||||
func TestWalkDrivers(t *testing.T) {
|
||||
reg := getNew(t)
|
||||
|
||||
err := reg.AddDriver(mockDriverName, mockDriverInit, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var driverName string
|
||||
reg.WalkDrivers(func(name string, driver driverapi.Driver, capability driverapi.Capability) bool {
|
||||
driverName = name
|
||||
return false
|
||||
})
|
||||
|
||||
assert.Equal(t, driverName, mockDriverName)
|
||||
}
|
|
@ -977,7 +977,7 @@ func (ep *endpoint) releaseAddress() {
|
|||
|
||||
log.Debugf("Releasing addresses for endpoint %s's interface on network %s", ep.Name(), n.Name())
|
||||
|
||||
ipam, err := n.getController().getIpamDriver(n.ipamType)
|
||||
ipam, _, err := n.getController().getIPAMDriver(n.ipamType)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to retrieve ipam driver to release interface address on delete of endpoint %s (%s): %v", ep.Name(), ep.ID(), err)
|
||||
return
|
||||
|
|
|
@ -15,50 +15,6 @@ import (
|
|||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
func TestDriverRegistration(t *testing.T) {
|
||||
bridgeNetType := "bridge"
|
||||
c, err := New()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer c.Stop()
|
||||
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, driverapi.Capability{})
|
||||
if err != nil {
|
||||
t.Fatalf("Test failed with an error %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIpamDriverRegistration(t *testing.T) {
|
||||
c, err := New()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer c.Stop()
|
||||
|
||||
err = c.(*controller).RegisterIpamDriver("", nil)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected failure, but succeeded")
|
||||
}
|
||||
if _, ok := err.(types.BadRequestError); !ok {
|
||||
t.Fatalf("Failed for unexpected reason: %v", err)
|
||||
}
|
||||
|
||||
err = c.(*controller).RegisterIpamDriver(ipamapi.DefaultIPAM, nil)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected failure, but succeeded")
|
||||
}
|
||||
if _, ok := err.(types.ForbiddenError); !ok {
|
||||
t.Fatalf("Failed for unexpected reason: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNetworkMarshalling(t *testing.T) {
|
||||
n := &network{
|
||||
name: "Miao",
|
||||
|
@ -375,8 +331,10 @@ func TestIpamReleaseOnNetDriverFailures(t *testing.T) {
|
|||
defer c.Stop()
|
||||
|
||||
cc := c.(*controller)
|
||||
bd := badDriver{failNetworkCreation: true}
|
||||
cc.drivers[badDriverName] = &driverData{driver: &bd, capability: driverapi.Capability{DataScope: datastore.LocalScope}}
|
||||
|
||||
if err := cc.drvRegistry.AddDriver(badDriverName, badDriverInit, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Test whether ipam state release is invoked on network create failure from net driver
|
||||
// by checking whether subsequent network creation requesting same gateway IP succeeds
|
||||
|
@ -429,6 +387,12 @@ type badDriver struct {
|
|||
failNetworkCreation bool
|
||||
}
|
||||
|
||||
var bd = badDriver{failNetworkCreation: true}
|
||||
|
||||
func badDriverInit(reg driverapi.DriverCallback, opt map[string]interface{}) error {
|
||||
return reg.RegisterDriver(badDriverName, &bd, driverapi.Capability{DataScope: datastore.LocalScope})
|
||||
}
|
||||
|
||||
func (b *badDriver) CreateNetwork(nid string, options map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
if b.failNetworkCreation {
|
||||
return fmt.Errorf("I will not create any network")
|
||||
|
|
|
@ -620,49 +620,52 @@ func (n *network) processOptions(options ...NetworkOption) {
|
|||
}
|
||||
}
|
||||
|
||||
func (n *network) driverScope() string {
|
||||
func (n *network) resolveDriver(name string, load bool) (driverapi.Driver, *driverapi.Capability, error) {
|
||||
c := n.getController()
|
||||
|
||||
c.Lock()
|
||||
// Check if a driver for the specified network type is available
|
||||
dd, ok := c.drivers[n.networkType]
|
||||
c.Unlock()
|
||||
d, cap := c.drvRegistry.Driver(name)
|
||||
if d == nil {
|
||||
if load {
|
||||
var err error
|
||||
err = c.loadDriver(name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if !ok {
|
||||
var err error
|
||||
dd, err = c.loadDriver(n.networkType)
|
||||
if err != nil {
|
||||
// If driver could not be resolved simply return an empty string
|
||||
return ""
|
||||
d, cap = c.drvRegistry.Driver(name)
|
||||
if d == nil {
|
||||
return nil, nil, fmt.Errorf("could not resolve driver %s in registry", name)
|
||||
}
|
||||
} else {
|
||||
// don't fail if driver loading is not required
|
||||
return nil, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
return dd.capability.DataScope
|
||||
return d, cap, nil
|
||||
}
|
||||
|
||||
func (n *network) driverScope() string {
|
||||
_, cap, err := n.resolveDriver(n.networkType, true)
|
||||
if err != nil {
|
||||
// If driver could not be resolved simply return an empty string
|
||||
return ""
|
||||
}
|
||||
|
||||
return cap.DataScope
|
||||
}
|
||||
|
||||
func (n *network) driver(load bool) (driverapi.Driver, error) {
|
||||
c := n.getController()
|
||||
|
||||
c.Lock()
|
||||
// Check if a driver for the specified network type is available
|
||||
dd, ok := c.drivers[n.networkType]
|
||||
c.Unlock()
|
||||
|
||||
if !ok && load {
|
||||
var err error
|
||||
dd, err = c.loadDriver(n.networkType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if !ok {
|
||||
// don't fail if driver loading is not required
|
||||
return nil, nil
|
||||
d, cap, err := n.resolveDriver(n.networkType, load)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n.Lock()
|
||||
n.scope = dd.capability.DataScope
|
||||
n.scope = cap.DataScope
|
||||
n.Unlock()
|
||||
return dd.driver, nil
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (n *network) Delete() error {
|
||||
|
@ -786,12 +789,12 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
|
|||
}
|
||||
}
|
||||
|
||||
ipam, err := n.getController().getIPAM(n.ipamType)
|
||||
ipam, cap, err := n.getController().getIPAMDriver(n.ipamType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ipam.capability.RequiresMACAddress {
|
||||
if cap.RequiresMACAddress {
|
||||
if ep.iface.mac == nil {
|
||||
ep.iface.mac = netutils.GenerateRandomMAC()
|
||||
}
|
||||
|
@ -801,7 +804,7 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
|
|||
ep.ipamOptions[netlabel.MacAddress] = ep.iface.mac.String()
|
||||
}
|
||||
|
||||
if err = ep.assignAddress(ipam.driver, true, n.enableIPv6 && !n.postIPv6); err != nil {
|
||||
if err = ep.assignAddress(ipam, true, n.enableIPv6 && !n.postIPv6); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
|
@ -821,7 +824,7 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
|
|||
}
|
||||
}()
|
||||
|
||||
if err = ep.assignAddress(ipam.driver, false, n.enableIPv6 && n.postIPv6); err != nil {
|
||||
if err = ep.assignAddress(ipam, false, n.enableIPv6 && n.postIPv6); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -1065,7 +1068,7 @@ func (n *network) ipamAllocate() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
ipam, err := n.getController().getIpamDriver(n.ipamType)
|
||||
ipam, _, err := n.getController().getIPAMDriver(n.ipamType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1189,7 +1192,7 @@ func (n *network) ipamRelease() {
|
|||
if n.Type() == "host" || n.Type() == "null" {
|
||||
return
|
||||
}
|
||||
ipam, err := n.getController().getIpamDriver(n.ipamType)
|
||||
ipam, _, err := n.getController().getIPAMDriver(n.ipamType)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to retrieve ipam driver to release address pool(s) on delete of network %s (%s): %v", n.Name(), n.ID(), err)
|
||||
return
|
||||
|
@ -1279,17 +1282,14 @@ func (n *network) getIPData(ipVer int) []driverapi.IPAMData {
|
|||
}
|
||||
|
||||
func (n *network) deriveAddressSpace() (string, error) {
|
||||
c := n.getController()
|
||||
c.Lock()
|
||||
ipd, ok := c.ipamDrivers[n.ipamType]
|
||||
c.Unlock()
|
||||
if !ok {
|
||||
return "", types.NotFoundErrorf("could not find ipam driver %s to get default address space", n.ipamType)
|
||||
local, global, err := n.getController().drvRegistry.IPAMDefaultAddressSpaces(n.ipamType)
|
||||
if err != nil {
|
||||
return "", types.NotFoundErrorf("failed to get default address space: %v", err)
|
||||
}
|
||||
if n.DataScope() == datastore.GlobalScope {
|
||||
return ipd.defaultGlobalAddressSpace, nil
|
||||
return global, nil
|
||||
}
|
||||
return ipd.defaultLocalAddressSpace, nil
|
||||
return local, nil
|
||||
}
|
||||
|
||||
func (n *network) Info() NetworkInfo {
|
||||
|
|
Loading…
Add table
Reference in a new issue