Browse Source

Merge pull request #44875 from corhere/libnet/local-scope-only

libnetwork: clean up vestigial datastore-related code
Cory Snider 2 years ago
parent
commit
b54af02b51
60 changed files with 1156 additions and 2103 deletions
  1. 1 1
      daemon/network.go
  2. 0 15
      libnetwork/bitmap/sequence.go
  3. 0 130
      libnetwork/bitmap/sequence_test.go
  4. 0 40
      libnetwork/bitseq/sequence.go
  5. 1 31
      libnetwork/bitseq/sequence_test.go
  6. 1 6
      libnetwork/cmd/ovrouter/ovrouter.go
  7. 6 18
      libnetwork/config/config.go
  8. 26 105
      libnetwork/controller.go
  9. 22 52
      libnetwork/datastore/datastore.go
  10. 7 4
      libnetwork/datastore/datastore_test.go
  11. 0 2
      libnetwork/discoverapi/discoverapi.go
  12. 6 2
      libnetwork/driverapi/driverapi.go
  13. 3 3
      libnetwork/drivers/bridge/bridge.go
  14. 9 2
      libnetwork/drivers/bridge/brmanager/brmanager.go
  15. 8 2
      libnetwork/drivers/host/host.go
  16. 3 3
      libnetwork/drivers/ipvlan/ipvlan.go
  17. 4 9
      libnetwork/drivers/ipvlan/ipvlan_test.go
  18. 9 2
      libnetwork/drivers/ipvlan/ivmanager/ivmanager.go
  19. 3 3
      libnetwork/drivers/macvlan/macvlan.go
  20. 4 9
      libnetwork/drivers/macvlan/macvlan_test.go
  21. 9 2
      libnetwork/drivers/macvlan/mvmanager/mvmanager.go
  22. 3 3
      libnetwork/drivers/null/null.go
  23. 3 16
      libnetwork/drivers/overlay/overlay.go
  24. 4 4
      libnetwork/drivers/overlay/overlay_test.go
  25. 9 2
      libnetwork/drivers/overlay/ovmanager/ovmanager.go
  26. 10 2
      libnetwork/drivers/remote/driver.go
  27. 3 3
      libnetwork/drivers/windows/overlay/overlay_windows.go
  28. 2 2
      libnetwork/drivers/windows/windows.go
  29. 1 3
      libnetwork/drivers_freebsd.go
  30. 8 8
      libnetwork/drivers_ipam.go
  31. 6 8
      libnetwork/drivers_linux.go
  32. 2 4
      libnetwork/drivers_windows.go
  33. 28 185
      libnetwork/drvregistry/drvregistry.go
  34. 12 68
      libnetwork/drvregistry/drvregistry_test.go
  35. 84 0
      libnetwork/drvregistry/ipams.go
  36. 51 0
      libnetwork/drvregistry/ipams_test.go
  37. 93 0
      libnetwork/drvregistry/networks.go
  38. 51 0
      libnetwork/drvregistry/networks_test.go
  39. 1 1
      libnetwork/endpoint.go
  40. 2 2
      libnetwork/endpoint_cnt.go
  41. 2 5
      libnetwork/error.go
  42. 21 195
      libnetwork/ipam/allocator.go
  43. 473 649
      libnetwork/ipam/allocator_test.go
  44. 3 5
      libnetwork/ipam/parallel_test.go
  45. 0 125
      libnetwork/ipam/store.go
  46. 6 198
      libnetwork/ipam/structures.go
  47. 12 8
      libnetwork/ipamapi/contract.go
  48. 3 22
      libnetwork/ipams/builtin/builtin.go
  49. 21 2
      libnetwork/ipams/builtin/builtin_unix.go
  50. 19 5
      libnetwork/ipams/builtin/builtin_windows.go
  51. 9 2
      libnetwork/ipams/null/null.go
  52. 8 2
      libnetwork/ipams/remote/remote.go
  53. 3 16
      libnetwork/ipams/windowsipam/windowsipam.go
  54. 2 2
      libnetwork/libnetwork_internal_test.go
  55. 1 1
      libnetwork/libnetwork_test.go
  56. 10 6
      libnetwork/network.go
  57. 1 1
      libnetwork/sandbox_store.go
  58. 64 104
      libnetwork/store.go
  59. 2 2
      libnetwork/store_linux_test.go
  60. 1 1
      libnetwork/store_test.go

+ 1 - 1
daemon/network.go

@@ -360,7 +360,7 @@ func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string
 
 	n, err := c.NewNetwork(driver, create.Name, id, nwOptions...)
 	if err != nil {
-		if _, ok := err.(libnetwork.ErrDataStoreNotInitialized); ok {
+		if errors.Is(err, libnetwork.ErrDataStoreNotInitialized) {
 			//nolint: revive
 			return nil, errors.New("This node is not a swarm manager. Use \"docker swarm init\" or \"docker swarm join\" to connect this node to swarm and try again.")
 		}

+ 0 - 15
libnetwork/bitmap/sequence.go

@@ -231,21 +231,6 @@ func (h *Bitmap) IsSet(ordinal uint64) bool {
 	return err != nil
 }
 
-// CheckConsistency checks if the bit sequence is in an inconsistent state and attempts to fix it.
-// It looks for a corruption signature that may happen in docker 1.9.0 and 1.9.1.
-func (h *Bitmap) CheckConsistency() bool {
-	corrupted := false
-	for p, c := h.head, h.head.next; c != nil; c = c.next {
-		if c.count == 0 {
-			corrupted = true
-			p.next = c.next
-			continue // keep same p
-		}
-		p = c
-	}
-	return corrupted
-}
-
 // set/reset the bit
 func (h *Bitmap) set(ordinal, start, end uint64, any bool, release bool, serial bool) (uint64, error) {
 	var (

+ 0 - 130
libnetwork/bitmap/sequence_test.go

@@ -963,136 +963,6 @@ func TestAllocateRandomDeallocateSerialize(t *testing.T) {
 	}
 }
 
-func TestIsCorrupted(t *testing.T) {
-	// Negative test
-	hnd := New(1024)
-
-	if hnd.CheckConsistency() {
-		t.Fatalf("Unexpected corrupted for %s", hnd)
-	}
-
-	hnd.Set(0)
-	if hnd.CheckConsistency() {
-		t.Fatalf("Unexpected corrupted for %s", hnd)
-	}
-
-	hnd.Set(1023)
-	if hnd.CheckConsistency() {
-		t.Fatalf("Unexpected corrupted for %s", hnd)
-	}
-
-	// Try real corrupted ipam handles found in the local store files reported by three docker users,
-	// plus a generic ipam handle from docker 1.9.1. This last will fail as well, because of how the
-	// last node in the sequence is expressed (This is true for IPAM handle only, because of the broadcast
-	// address reservation: last bit). This will allow an application using bitseq that runs a consistency
-	// check to detect and replace the 1.9.0/1 old vulnerable handle with the new one.
-	input := []*Bitmap{
-		{
-			bits:       65536,
-			unselected: 65412,
-			head: &sequence{
-				block: 0xffffffff,
-				count: 3,
-				next: &sequence{
-					block: 0xffffffbf,
-					count: 0,
-					next: &sequence{
-						block: 0xfe98816e,
-						count: 1,
-						next: &sequence{
-							block: 0xffffffff,
-							count: 0,
-							next: &sequence{
-								block: 0xe3bc0000,
-								count: 1,
-								next: &sequence{
-									block: 0x0,
-									count: 2042,
-									next: &sequence{
-										block: 0x1, count: 1,
-										next: &sequence{
-											block: 0x0, count: 0,
-										},
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-		},
-		{
-			bits:       65536,
-			unselected: 65319,
-			head: &sequence{
-				block: 0xffffffff,
-				count: 7,
-				next: &sequence{
-					block: 0xffffff7f,
-					count: 0,
-					next: &sequence{
-						block: 0xffffffff,
-						count: 0,
-						next: &sequence{
-							block: 0x2000000,
-							count: 1,
-							next: &sequence{
-								block: 0x0,
-								count: 2039,
-								next: &sequence{
-									block: 0x1,
-									count: 1,
-									next: &sequence{
-										block: 0x0,
-										count: 0,
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-		},
-		{
-			bits:       65536,
-			unselected: 65456,
-			head: &sequence{
-				block: 0xffffffff, count: 2,
-				next: &sequence{
-					block: 0xfffbffff, count: 0,
-					next: &sequence{
-						block: 0xffd07000, count: 1,
-						next: &sequence{
-							block: 0x0, count: 333,
-							next: &sequence{
-								block: 0x40000000, count: 1,
-								next: &sequence{
-									block: 0x0, count: 1710,
-									next: &sequence{
-										block: 0x1, count: 1,
-										next: &sequence{
-											block: 0x0, count: 0,
-										},
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-		},
-	}
-
-	for idx, hnd := range input {
-		if !hnd.CheckConsistency() {
-			t.Fatalf("Expected corrupted for (%d): %s", idx, hnd)
-		}
-		if hnd.CheckConsistency() {
-			t.Fatalf("Sequence still marked corrupted (%d): %s", idx, hnd)
-		}
-	}
-}
-
 func testSetRollover(t *testing.T, serial bool) {
 	numBlocks := uint32(8)
 	numBits := int(numBlocks * blockLen)

+ 0 - 40
libnetwork/bitseq/sequence.go

@@ -11,7 +11,6 @@ import (
 	"github.com/docker/docker/libnetwork/bitmap"
 	"github.com/docker/docker/libnetwork/datastore"
 	"github.com/docker/docker/libnetwork/types"
-	"github.com/sirupsen/logrus"
 )
 
 var (
@@ -101,45 +100,6 @@ func (h *Handle) IsSet(ordinal uint64) bool {
 	return h.bm.IsSet(ordinal)
 }
 
-// CheckConsistency checks if the bit sequence is in an inconsistent state and attempts to fix it.
-// It looks for a corruption signature that may happen in docker 1.9.0 and 1.9.1.
-func (h *Handle) CheckConsistency() error {
-	for {
-		h.mu.Lock()
-		store := h.store
-		h.mu.Unlock()
-
-		if store != nil {
-			if err := store.GetObject(datastore.Key(h.Key()...), h); err != nil && err != datastore.ErrKeyNotFound {
-				return err
-			}
-		}
-
-		h.mu.Lock()
-		nh := h.getCopy()
-		h.mu.Unlock()
-
-		if !nh.bm.CheckConsistency() {
-			return nil
-		}
-
-		if err := nh.writeToStore(); err != nil {
-			if _, ok := err.(types.RetryError); !ok {
-				return fmt.Errorf("internal failure while fixing inconsistent bitsequence: %v", err)
-			}
-			continue
-		}
-
-		logrus.Infof("Fixed inconsistent bit sequence in datastore:\n%s\n%s", h, nh)
-
-		h.mu.Lock()
-		h.bm = nh.bm
-		h.mu.Unlock()
-
-		return nil
-	}
-}
-
 // set/reset the bit
 func (h *Handle) apply(op func(*bitmap.Bitmap) (uint64, error)) (uint64, error) {
 	for {

+ 1 - 31
libnetwork/bitseq/sequence_test.go

@@ -29,7 +29,7 @@ func randomLocalStore() (datastore.DataStore, error) {
 	if err := tmp.Close(); err != nil {
 		return nil, fmt.Errorf("Error closing temp file: %v", err)
 	}
-	return datastore.NewDataStore(datastore.LocalScope, &datastore.ScopeCfg{
+	return datastore.NewDataStore(datastore.ScopeCfg{
 		Client: datastore.ScopeClientCfg{
 			Provider: "boltdb",
 			Address:  filepath.Join(defaultPrefix, filepath.Base(tmp.Name())),
@@ -181,36 +181,6 @@ func TestRetrieveFromStore(t *testing.T) {
 	}
 }
 
-func TestIsCorrupted(t *testing.T) {
-	ds, err := randomLocalStore()
-	if err != nil {
-		t.Fatal(err)
-	}
-	// Negative test
-	hnd, err := NewHandle("bitseq-test/data/", ds, "test_corrupted", 1024)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if err := hnd.CheckConsistency(); err != nil {
-		t.Fatal(err)
-	}
-
-	_ = hnd.Set(0)
-	if err := hnd.CheckConsistency(); err != nil {
-		t.Fatal(err)
-	}
-
-	_ = hnd.Set(1023)
-	if err := hnd.CheckConsistency(); err != nil {
-		t.Fatal(err)
-	}
-
-	if err := hnd.CheckConsistency(); err != nil {
-		t.Fatal(err)
-	}
-}
-
 func testSetRollover(t *testing.T, serial bool) {
 	ds, err := randomLocalStore()
 	if err != nil {

+ 1 - 6
libnetwork/cmd/ovrouter/ovrouter.go

@@ -13,7 +13,6 @@ import (
 	"github.com/docker/docker/libnetwork/drivers/overlay"
 	"github.com/docker/docker/libnetwork/netlabel"
 	"github.com/docker/docker/libnetwork/types"
-	"github.com/docker/docker/pkg/plugingetter"
 	"github.com/docker/docker/pkg/reexec"
 	"github.com/vishvananda/netlink"
 )
@@ -28,10 +27,6 @@ type endpoint struct {
 	name string
 }
 
-func (r *router) GetPluginGetter() plugingetter.PluginGetter {
-	return nil
-}
-
 func (r *router) RegisterDriver(name string, driver driverapi.Driver, c driverapi.Capability) error {
 	r.d = driver
 	return nil
@@ -126,7 +121,7 @@ func main() {
 	}
 
 	r := &router{}
-	if err := overlay.Init(r, opt); err != nil {
+	if err := overlay.Register(r, opt); err != nil {
 		fmt.Printf("Failed to initialize overlay driver: %v\n", err)
 		os.Exit(1)
 	}

+ 6 - 18
libnetwork/config/config.go

@@ -29,7 +29,7 @@ type Config struct {
 	ClusterProvider        cluster.Provider
 	NetworkControlPlaneMTU int
 	DefaultAddressPool     []*ipamutils.NetworkToSplit
-	Scopes                 map[string]*datastore.ScopeCfg
+	Scope                  datastore.ScopeCfg
 	ActiveSandboxes        map[string]interface{}
 	PluginGetter           plugingetter.PluginGetter
 }
@@ -38,7 +38,6 @@ type Config struct {
 func New(opts ...Option) *Config {
 	cfg := &Config{
 		DriverCfg: make(map[string]interface{}),
-		Scopes:    make(map[string]*datastore.ScopeCfg),
 	}
 
 	for _, opt := range opts {
@@ -48,10 +47,8 @@ func New(opts ...Option) *Config {
 	}
 
 	// load default scope configs which don't have explicit user specified configs.
-	for k, v := range datastore.DefaultScopes(cfg.DataDir) {
-		if _, ok := cfg.Scopes[k]; !ok {
-			cfg.Scopes[k] = v
-		}
+	if cfg.Scope == (datastore.ScopeCfg{}) {
+		cfg.Scope = datastore.DefaultScope(cfg.DataDir)
 	}
 	return cfg
 }
@@ -147,10 +144,7 @@ func IsValidName(name string) bool {
 func OptionLocalKVProvider(provider string) Option {
 	return func(c *Config) {
 		logrus.Debugf("Option OptionLocalKVProvider: %s", provider)
-		if _, ok := c.Scopes[datastore.LocalScope]; !ok {
-			c.Scopes[datastore.LocalScope] = &datastore.ScopeCfg{}
-		}
-		c.Scopes[datastore.LocalScope].Client.Provider = strings.TrimSpace(provider)
+		c.Scope.Client.Provider = strings.TrimSpace(provider)
 	}
 }
 
@@ -158,10 +152,7 @@ func OptionLocalKVProvider(provider string) Option {
 func OptionLocalKVProviderURL(url string) Option {
 	return func(c *Config) {
 		logrus.Debugf("Option OptionLocalKVProviderURL: %s", url)
-		if _, ok := c.Scopes[datastore.LocalScope]; !ok {
-			c.Scopes[datastore.LocalScope] = &datastore.ScopeCfg{}
-		}
-		c.Scopes[datastore.LocalScope].Client.Address = strings.TrimSpace(url)
+		c.Scope.Client.Address = strings.TrimSpace(url)
 	}
 }
 
@@ -169,10 +160,7 @@ func OptionLocalKVProviderURL(url string) Option {
 func OptionLocalKVProviderConfig(config *store.Config) Option {
 	return func(c *Config) {
 		logrus.Debugf("Option OptionLocalKVProviderConfig: %v", config)
-		if _, ok := c.Scopes[datastore.LocalScope]; !ok {
-			c.Scopes[datastore.LocalScope] = &datastore.ScopeCfg{}
-		}
-		c.Scopes[datastore.LocalScope].Client.Config = config
+		c.Scope.Client.Config = config
 	}
 }
 

+ 26 - 105
libnetwork/controller.go

@@ -58,6 +58,7 @@ import (
 	"github.com/docker/docker/libnetwork/diagnostic"
 	"github.com/docker/docker/libnetwork/discoverapi"
 	"github.com/docker/docker/libnetwork/driverapi"
+	remotedriver "github.com/docker/docker/libnetwork/drivers/remote"
 	"github.com/docker/docker/libnetwork/drvregistry"
 	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/libnetwork/netlabel"
@@ -85,10 +86,11 @@ type sandboxTable map[string]*Sandbox
 // Controller manages networks.
 type Controller struct {
 	id               string
-	drvRegistry      *drvregistry.DrvRegistry
+	drvRegistry      drvregistry.Networks
+	ipamRegistry     drvregistry.IPAMs
 	sandboxes        sandboxTable
 	cfg              *config.Config
-	stores           []datastore.DataStore
+	store            datastore.DataStore
 	extKeyListener   net.Listener
 	watchCh          chan *Endpoint
 	unWatchCh        chan *Endpoint
@@ -108,7 +110,7 @@ type Controller struct {
 }
 
 type initializer struct {
-	fn    drvregistry.InitFunc
+	fn    func(driverapi.Registerer, map[string]interface{}) error
 	ntype string
 }
 
@@ -130,31 +132,24 @@ func New(cfgOptions ...config.Option) (*Controller, error) {
 		return nil, err
 	}
 
-	drvRegistry, err := drvregistry.New(c.getStore(datastore.LocalScope), c.getStore(datastore.GlobalScope), c.RegisterDriver, nil, c.cfg.PluginGetter)
-	if err != nil {
+	c.drvRegistry.Notify = c.RegisterDriver
+
+	// External plugins don't need config passed through daemon. They can
+	// bootstrap themselves.
+	if err := remotedriver.Register(&c.drvRegistry, c.cfg.PluginGetter); 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 {
+		if err := i.fn(&c.drvRegistry, c.makeDriverConfig(i.ntype)); err != nil {
 			return nil, err
 		}
 	}
 
-	if err = initIPAMDrivers(drvRegistry, nil, c.getStore(datastore.GlobalScope), c.cfg.DefaultAddressPool); err != nil {
+	if err := initIPAMDrivers(&c.ipamRegistry, c.cfg.PluginGetter, c.cfg.DefaultAddressPool); err != nil {
 		return nil, err
 	}
 
-	c.drvRegistry = drvRegistry
-
 	c.WalkNetworks(populateSpecial)
 
 	// Reserve pools first before doing cleanup. Otherwise the
@@ -353,94 +348,20 @@ func (c *Controller) makeDriverConfig(ntype string) map[string]interface{} {
 		}
 	}
 
-	for k, v := range c.cfg.Scopes {
-		if !v.IsValid() {
-			continue
-		}
-		cfg[netlabel.MakeKVClient(k)] = discoverapi.DatastoreConfigData{
-			Scope:    k,
-			Provider: v.Client.Provider,
-			Address:  v.Client.Address,
-			Config:   v.Client.Config,
+	if c.cfg.Scope.IsValid() {
+		// FIXME: every driver instance constructs a new DataStore
+		// instance against the same database. Yikes!
+		cfg[netlabel.LocalKVClient] = discoverapi.DatastoreConfigData{
+			Scope:    datastore.LocalScope,
+			Provider: c.cfg.Scope.Client.Provider,
+			Address:  c.cfg.Scope.Client.Address,
+			Config:   c.cfg.Scope.Client.Config,
 		}
 	}
 
 	return cfg
 }
 
-var procReloadConfig = make(chan (bool), 1)
-
-// ReloadConfiguration updates the controller configuration.
-func (c *Controller) ReloadConfiguration(cfgOptions ...config.Option) error {
-	procReloadConfig <- true
-	defer func() { <-procReloadConfig }()
-
-	// For now we accept the configuration reload only as a mean to provide a global store config after boot.
-	// Refuse the configuration if it alters an existing datastore client configuration.
-	update := false
-	cfg := config.New(cfgOptions...)
-
-	for s := range c.cfg.Scopes {
-		if _, ok := cfg.Scopes[s]; !ok {
-			return types.ForbiddenErrorf("cannot accept new configuration because it removes an existing datastore client")
-		}
-	}
-	for s, nSCfg := range cfg.Scopes {
-		if eSCfg, ok := c.cfg.Scopes[s]; ok {
-			if eSCfg.Client.Provider != nSCfg.Client.Provider ||
-				eSCfg.Client.Address != nSCfg.Client.Address {
-				return types.ForbiddenErrorf("cannot accept new configuration because it modifies an existing datastore client")
-			}
-		} else {
-			if err := c.initScopedStore(s, nSCfg); err != nil {
-				return err
-			}
-			update = true
-		}
-	}
-	if !update {
-		return nil
-	}
-
-	c.mu.Lock()
-	c.cfg = cfg
-	c.mu.Unlock()
-
-	var dsConfig *discoverapi.DatastoreConfigData
-	for scope, sCfg := range cfg.Scopes {
-		if scope == datastore.LocalScope || !sCfg.IsValid() {
-			continue
-		}
-		dsConfig = &discoverapi.DatastoreConfigData{
-			Scope:    scope,
-			Provider: sCfg.Client.Provider,
-			Address:  sCfg.Client.Address,
-			Config:   sCfg.Client.Config,
-		}
-		break
-	}
-	if dsConfig == nil {
-		return nil
-	}
-
-	c.drvRegistry.WalkIPAMs(func(name string, driver ipamapi.Ipam, cap *ipamapi.Capability) bool {
-		err := driver.DiscoverNew(discoverapi.DatastoreConfig, *dsConfig)
-		if err != nil {
-			logrus.Errorf("Failed to set datastore in driver %s: %v", name, err)
-		}
-		return false
-	})
-
-	c.drvRegistry.WalkDrivers(func(name string, driver driverapi.Driver, capability driverapi.Capability) bool {
-		err := driver.DiscoverNew(discoverapi.DatastoreConfig, *dsConfig)
-		if err != nil {
-			logrus.Errorf("Failed to set datastore in driver %s: %v", name, err)
-		}
-		return false
-	})
-	return nil
-}
-
 // ID returns the controller's unique identity.
 func (c *Controller) ID() string {
 	return c.id
@@ -461,7 +382,7 @@ func (c *Controller) BuiltinDrivers() []string {
 // BuiltinIPAMDrivers returns the list of builtin ipam drivers.
 func (c *Controller) BuiltinIPAMDrivers() []string {
 	drivers := []string{}
-	c.drvRegistry.WalkIPAMs(func(name string, driver ipamapi.Ipam, cap *ipamapi.Capability) bool {
+	c.ipamRegistry.WalkIPAMs(func(name string, driver ipamapi.Ipam, cap *ipamapi.Capability) bool {
 		if driver.IsBuiltIn() {
 			drivers = append(drivers, name)
 		}
@@ -535,7 +456,7 @@ func (c *Controller) isDistributedControl() bool {
 }
 
 func (c *Controller) GetPluginGetter() plugingetter.PluginGetter {
-	return c.drvRegistry.GetPluginGetter()
+	return c.cfg.PluginGetter
 }
 
 func (c *Controller) RegisterDriver(networkType string, driver driverapi.Driver, capability driverapi.Capability) error {
@@ -550,7 +471,7 @@ const overlayDSROptionString = "dsr"
 // are network specific and modeled in a generic way.
 func (c *Controller) NewNetwork(networkType, name string, id string, options ...NetworkOption) (Network, error) {
 	var (
-		caps           *driverapi.Capability
+		caps           driverapi.Capability
 		err            error
 		t              *network
 		skipCfgEpCount bool
@@ -770,7 +691,7 @@ var joinCluster NetworkWalker = func(nw Network) bool {
 }
 
 func (c *Controller) reservePools() {
-	networks, err := c.getNetworksForScope(datastore.LocalScope)
+	networks, err := c.getNetworks()
 	if err != nil {
 		logrus.Warnf("Could not retrieve networks from local store during ipam allocation for existing networks: %v", err)
 		return
@@ -1175,7 +1096,7 @@ func (c *Controller) loadIPAMDriver(name string) error {
 }
 
 func (c *Controller) getIPAMDriver(name string) (ipamapi.Ipam, *ipamapi.Capability, error) {
-	id, cap := c.drvRegistry.IPAM(name)
+	id, cap := c.ipamRegistry.IPAM(name)
 	if id == nil {
 		// Might be a plugin name. Try loading it
 		if err := c.loadIPAMDriver(name); err != nil {
@@ -1183,7 +1104,7 @@ func (c *Controller) getIPAMDriver(name string) (ipamapi.Ipam, *ipamapi.Capabili
 		}
 
 		// Now that we resolved the plugin, try again looking up the registry
-		id, cap = c.drvRegistry.IPAM(name)
+		id, cap = c.ipamRegistry.IPAM(name)
 		if id == nil {
 			return nil, nil, types.BadRequestErrorf("invalid ipam driver: %q", name)
 		}

+ 22 - 52
libnetwork/datastore/datastore.go

@@ -129,37 +129,28 @@ const (
 	EndpointKeyPrefix = "endpoint"
 )
 
-var (
-	defaultScopes = makeDefaultScopes()
-)
+var defaultRootChain = []string{"docker", "network", "v1.0"}
+var rootChain = defaultRootChain
+
+// DefaultScope returns a default scope config for clients to use.
+func DefaultScope(dataDir string) ScopeCfg {
+	var dbpath string
+	if dataDir == "" {
+		dbpath = defaultPrefix + "/local-kv.db"
+	} else {
+		dbpath = dataDir + "/network/files/local-kv.db"
+	}
 
-func makeDefaultScopes() map[string]*ScopeCfg {
-	def := make(map[string]*ScopeCfg)
-	def[LocalScope] = &ScopeCfg{
+	return ScopeCfg{
 		Client: ScopeClientCfg{
 			Provider: string(store.BOLTDB),
-			Address:  defaultPrefix + "/local-kv.db",
+			Address:  dbpath,
 			Config: &store.Config{
 				Bucket:            "libnetwork",
 				ConnectionTimeout: time.Minute,
 			},
 		},
 	}
-
-	return def
-}
-
-var defaultRootChain = []string{"docker", "network", "v1.0"}
-var rootChain = defaultRootChain
-
-// DefaultScopes returns a map of default scopes and its config for clients to use.
-func DefaultScopes(dataDir string) map[string]*ScopeCfg {
-	s := makeDefaultScopes()
-	if dataDir != "" {
-		s[LocalScope].Client.Address = dataDir + "/network/files/local-kv.db"
-	}
-
-	return s
 }
 
 // IsValid checks if the scope config has valid configuration.
@@ -192,16 +183,7 @@ func ParseKey(key string) ([]string, error) {
 }
 
 // newClient used to connect to KV Store
-func newClient(scope string, kv string, addr string, config *store.Config, cached bool) (DataStore, error) {
-
-	if cached && scope != LocalScope {
-		return nil, fmt.Errorf("caching supported only for scope %s", LocalScope)
-	}
-	sequential := false
-	if scope == LocalScope {
-		sequential = true
-	}
-
+func newClient(kv string, addr string, config *store.Config) (DataStore, error) {
 	if config == nil {
 		config = &store.Config{}
 	}
@@ -227,31 +209,19 @@ func newClient(scope string, kv string, addr string, config *store.Config, cache
 		return nil, err
 	}
 
-	ds := &datastore{scope: scope, store: s, active: true, watchCh: make(chan struct{}), sequential: sequential}
-	if cached {
-		ds.cache = newCache(ds)
-	}
+	ds := &datastore{scope: LocalScope, store: s, active: true, watchCh: make(chan struct{}), sequential: true}
+	ds.cache = newCache(ds)
 
 	return ds, nil
 }
 
 // NewDataStore creates a new instance of LibKV data store
-func NewDataStore(scope string, cfg *ScopeCfg) (DataStore, error) {
-	if cfg == nil || cfg.Client.Provider == "" || cfg.Client.Address == "" {
-		c, ok := defaultScopes[scope]
-		if !ok || c.Client.Provider == "" || c.Client.Address == "" {
-			return nil, fmt.Errorf("unexpected scope %s without configuration passed", scope)
-		}
-
-		cfg = c
-	}
-
-	var cached bool
-	if scope == LocalScope {
-		cached = true
+func NewDataStore(cfg ScopeCfg) (DataStore, error) {
+	if cfg.Client.Provider == "" || cfg.Client.Address == "" {
+		cfg = DefaultScope("")
 	}
 
-	return newClient(scope, cfg.Client.Provider, cfg.Client.Address, cfg.Client.Config, cached)
+	return newClient(cfg.Client.Provider, cfg.Client.Address, cfg.Client.Config)
 }
 
 // NewDataStoreFromConfig creates a new instance of LibKV data store starting from the datastore config data
@@ -266,7 +236,7 @@ func NewDataStoreFromConfig(dsc discoverapi.DatastoreConfigData) (DataStore, err
 		return nil, fmt.Errorf("cannot parse store configuration: %v", dsc.Config)
 	}
 
-	scopeCfg := &ScopeCfg{
+	scopeCfg := ScopeCfg{
 		Client: ScopeClientCfg{
 			Address:  dsc.Address,
 			Provider: dsc.Provider,
@@ -274,7 +244,7 @@ func NewDataStoreFromConfig(dsc discoverapi.DatastoreConfigData) (DataStore, err
 		},
 	}
 
-	ds, err := NewDataStore(dsc.Scope, scopeCfg)
+	ds, err := NewDataStore(scopeCfg)
 	if err != nil {
 		return nil, fmt.Errorf("failed to construct datastore client from datastore configuration %v: %v", dsc, err)
 	}

+ 7 - 4
libnetwork/datastore/datastore_test.go

@@ -36,10 +36,13 @@ func TestParseKey(t *testing.T) {
 }
 
 func TestInvalidDataStore(t *testing.T) {
-	config := &ScopeCfg{}
-	config.Client.Provider = "invalid"
-	config.Client.Address = "localhost:8500"
-	_, err := NewDataStore(GlobalScope, config)
+	config := ScopeCfg{
+		Client: ScopeClientCfg{
+			Provider: "invalid",
+			Address:  "localhost:8500",
+		},
+	}
+	_, err := NewDataStore(config)
 	if err == nil {
 		t.Fatal("Invalid Datastore connection configuration must result in a failure")
 	}

+ 0 - 2
libnetwork/discoverapi/discoverapi.go

@@ -16,8 +16,6 @@ type DiscoveryType int
 const (
 	// NodeDiscovery represents Node join/leave events provided by discovery
 	NodeDiscovery = iota + 1
-	// DatastoreConfig represents an add/remove datastore event
-	DatastoreConfig
 	// EncryptionKeysConfig represents the initial key(s) for performing datapath encryption
 	EncryptionKeysConfig
 	// EncryptionKeysUpdate represents an update to the datapath encryption key(s)

+ 6 - 2
libnetwork/driverapi/driverapi.go

@@ -156,12 +156,16 @@ type JoinInfo interface {
 	AddTableEntry(tableName string, key string, value []byte) error
 }
 
+// Registerer provides a way for network drivers to be dynamically registered.
+type Registerer interface {
+	RegisterDriver(name string, driver Driver, capability Capability) error
+}
+
 // DriverCallback provides a Callback interface for Drivers into LibNetwork
 type DriverCallback interface {
+	Registerer
 	// GetPluginGetter returns the pluginv2 getter.
 	GetPluginGetter() plugingetter.PluginGetter
-	// RegisterDriver provides a way for Remote drivers to dynamically register new NetworkType and associate with a driver instance
-	RegisterDriver(name string, driver Driver, capability Capability) error
 }
 
 // Capability represents the high level capabilities of the drivers which libnetwork can make use of

+ 3 - 3
libnetwork/drivers/bridge/bridge.go

@@ -166,8 +166,8 @@ func newDriver() *driver {
 	}
 }
 
-// Init registers a new instance of bridge driver
-func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
+// Register registers a new instance of bridge driver.
+func Register(r driverapi.Registerer, config map[string]interface{}) error {
 	d := newDriver()
 	if err := d.configure(config); err != nil {
 		return err
@@ -177,7 +177,7 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
 		DataScope:         datastore.LocalScope,
 		ConnectivityScope: datastore.LocalScope,
 	}
-	return dc.RegisterDriver(networkType, d, c)
+	return r.RegisterDriver(networkType, d, c)
 }
 
 // Validate performs a static validation on the network configuration parameters.

+ 9 - 2
libnetwork/drivers/bridge/brmanager/brmanager.go

@@ -11,13 +11,20 @@ const networkType = "bridge"
 
 type driver struct{}
 
-// Init registers a new instance of bridge manager driver
+// Init registers a new instance of bridge manager driver.
+//
+// Deprecated: use [Register].
 func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
+	return Register(dc, config)
+}
+
+// Register registers a new instance of the bridge manager driver with r.
+func Register(r driverapi.Registerer, config map[string]interface{}) error {
 	c := driverapi.Capability{
 		DataScope:         datastore.LocalScope,
 		ConnectivityScope: datastore.LocalScope,
 	}
-	return dc.RegisterDriver(networkType, &driver{}, c)
+	return r.RegisterDriver(networkType, &driver{}, c)
 }
 
 func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {

+ 8 - 2
libnetwork/drivers/host/host.go

@@ -16,13 +16,19 @@ type driver struct {
 	sync.Mutex
 }
 
-// Init registers a new instance of host driver
+// Init registers a new instance of host driver.
+//
+// Deprecated: use [Register].
 func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
+	return Register(dc, config)
+}
+
+func Register(r driverapi.Registerer, config map[string]interface{}) error {
 	c := driverapi.Capability{
 		DataScope:         datastore.LocalScope,
 		ConnectivityScope: datastore.LocalScope,
 	}
-	return dc.RegisterDriver(networkType, &driver{}, c)
+	return r.RegisterDriver(networkType, &driver{}, c)
 }
 
 func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {

+ 3 - 3
libnetwork/drivers/ipvlan/ipvlan.go

@@ -62,8 +62,8 @@ type network struct {
 	sync.Mutex
 }
 
-// Init initializes and registers the libnetwork ipvlan driver
-func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
+// Register initializes and registers the libnetwork ipvlan driver.
+func Register(r driverapi.Registerer, config map[string]interface{}) error {
 	c := driverapi.Capability{
 		DataScope:         datastore.LocalScope,
 		ConnectivityScope: datastore.GlobalScope,
@@ -75,7 +75,7 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
 		return err
 	}
 
-	return dc.RegisterDriver(driverName, d, c)
+	return r.RegisterDriver(driverName, d, c)
 }
 
 func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {

+ 4 - 9
libnetwork/drivers/ipvlan/ipvlan_test.go

@@ -7,7 +7,6 @@ import (
 	"testing"
 
 	"github.com/docker/docker/libnetwork/driverapi"
-	"github.com/docker/docker/pkg/plugingetter"
 )
 
 const testNetworkType = "ipvlan"
@@ -17,10 +16,6 @@ type driverTester struct {
 	d *driver
 }
 
-func (dt *driverTester) GetPluginGetter() plugingetter.PluginGetter {
-	return nil
-}
-
 func (dt *driverTester) RegisterDriver(name string, drv driverapi.Driver,
 	cap driverapi.Capability) error {
 	if name != testNetworkType {
@@ -37,15 +32,15 @@ func (dt *driverTester) RegisterDriver(name string, drv driverapi.Driver,
 	return nil
 }
 
-func TestIpvlanInit(t *testing.T) {
-	if err := Init(&driverTester{t: t}, nil); err != nil {
+func TestIpvlanRegister(t *testing.T) {
+	if err := Register(&driverTester{t: t}, nil); err != nil {
 		t.Fatal(err)
 	}
 }
 
 func TestIpvlanNilConfig(t *testing.T) {
 	dt := &driverTester{t: t}
-	if err := Init(dt, nil); err != nil {
+	if err := Register(dt, nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -56,7 +51,7 @@ func TestIpvlanNilConfig(t *testing.T) {
 
 func TestIpvlanType(t *testing.T) {
 	dt := &driverTester{t: t}
-	if err := Init(dt, nil); err != nil {
+	if err := Register(dt, nil); err != nil {
 		t.Fatal(err)
 	}
 

+ 9 - 2
libnetwork/drivers/ipvlan/ivmanager/ivmanager.go

@@ -11,13 +11,20 @@ const networkType = "ipvlan"
 
 type driver struct{}
 
-// Init registers a new instance of ipvlan manager driver
+// Init registers a new instance of the ipvlan manager driver.
+//
+// Deprecated: use [Register].
 func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
+	return Register(dc, config)
+}
+
+// Register registers a new instance of the ipvlan manager driver.
+func Register(r driverapi.Registerer, config map[string]interface{}) error {
 	c := driverapi.Capability{
 		DataScope:         datastore.LocalScope,
 		ConnectivityScope: datastore.GlobalScope,
 	}
-	return dc.RegisterDriver(networkType, &driver{}, c)
+	return r.RegisterDriver(networkType, &driver{}, c)
 }
 
 func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {

+ 3 - 3
libnetwork/drivers/macvlan/macvlan.go

@@ -56,8 +56,8 @@ type network struct {
 	sync.Mutex
 }
 
-// Init initializes and registers the libnetwork macvlan driver
-func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
+// Register initializes and registers the libnetwork macvlan driver
+func Register(r driverapi.Registerer, config map[string]interface{}) error {
 	c := driverapi.Capability{
 		DataScope:         datastore.LocalScope,
 		ConnectivityScope: datastore.GlobalScope,
@@ -69,7 +69,7 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
 		return err
 	}
 
-	return dc.RegisterDriver(driverName, d, c)
+	return r.RegisterDriver(driverName, d, c)
 }
 
 func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {

+ 4 - 9
libnetwork/drivers/macvlan/macvlan_test.go

@@ -7,7 +7,6 @@ import (
 	"testing"
 
 	"github.com/docker/docker/libnetwork/driverapi"
-	"github.com/docker/docker/pkg/plugingetter"
 )
 
 const testNetworkType = "macvlan"
@@ -17,10 +16,6 @@ type driverTester struct {
 	d *driver
 }
 
-func (dt *driverTester) GetPluginGetter() plugingetter.PluginGetter {
-	return nil
-}
-
 func (dt *driverTester) RegisterDriver(name string, drv driverapi.Driver,
 	cap driverapi.Capability) error {
 	if name != testNetworkType {
@@ -37,15 +32,15 @@ func (dt *driverTester) RegisterDriver(name string, drv driverapi.Driver,
 	return nil
 }
 
-func TestMacvlanInit(t *testing.T) {
-	if err := Init(&driverTester{t: t}, nil); err != nil {
+func TestMacvlanRegister(t *testing.T) {
+	if err := Register(&driverTester{t: t}, nil); err != nil {
 		t.Fatal(err)
 	}
 }
 
 func TestMacvlanNilConfig(t *testing.T) {
 	dt := &driverTester{t: t}
-	if err := Init(dt, nil); err != nil {
+	if err := Register(dt, nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -56,7 +51,7 @@ func TestMacvlanNilConfig(t *testing.T) {
 
 func TestMacvlanType(t *testing.T) {
 	dt := &driverTester{t: t}
-	if err := Init(dt, nil); err != nil {
+	if err := Register(dt, nil); err != nil {
 		t.Fatal(err)
 	}
 

+ 9 - 2
libnetwork/drivers/macvlan/mvmanager/mvmanager.go

@@ -11,13 +11,20 @@ const networkType = "macvlan"
 
 type driver struct{}
 
-// Init registers a new instance of macvlan manager driver
+// Init registers a new instance of the macvlan manager driver.
+//
+// Deprecated: use [Register].
 func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
+	return Register(dc, config)
+}
+
+// Register registers a new instance of the macvlan manager driver.
+func Register(r driverapi.Registerer, config map[string]interface{}) error {
 	c := driverapi.Capability{
 		DataScope:         datastore.LocalScope,
 		ConnectivityScope: datastore.GlobalScope,
 	}
-	return dc.RegisterDriver(networkType, &driver{}, c)
+	return r.RegisterDriver(networkType, &driver{}, c)
 }
 
 func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {

+ 3 - 3
libnetwork/drivers/null/null.go

@@ -16,12 +16,12 @@ type driver struct {
 	sync.Mutex
 }
 
-// Init registers a new instance of null driver
-func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
+// Register registers a new instance of the null driver.
+func Register(r driverapi.Registerer, config map[string]interface{}) error {
 	c := driverapi.Capability{
 		DataScope: datastore.LocalScope,
 	}
-	return dc.RegisterDriver(networkType, &driver{}, c)
+	return r.RegisterDriver(networkType, &driver{}, c)
 }
 
 func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {

+ 3 - 16
libnetwork/drivers/overlay/overlay.go

@@ -58,8 +58,8 @@ type driver struct {
 	sync.Mutex
 }
 
-// Init registers a new instance of overlay driver
-func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
+// Register registers a new instance of the overlay driver.
+func Register(r driverapi.Registerer, config map[string]interface{}) error {
 	c := driverapi.Capability{
 		DataScope:         datastore.GlobalScope,
 		ConnectivityScope: datastore.GlobalScope,
@@ -107,7 +107,7 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
 		logrus.Warnf("Failure during overlay endpoints restore: %v", err)
 	}
 
-	return dc.RegisterDriver(networkType, d, c)
+	return r.RegisterDriver(networkType, d, c)
 }
 
 // Endpoints are stored in the local store. Restore them and reconstruct the overlay sandbox
@@ -319,7 +319,6 @@ func (d *driver) pushLocalEndpointEvent(action, nid, eid string) {
 
 // DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster
 func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error {
-	var err error
 	switch dType {
 	case discoverapi.NodeDiscovery:
 		nodeData, ok := data.(discoverapi.NodeDiscoveryData)
@@ -327,18 +326,6 @@ func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{})
 			return fmt.Errorf("invalid discovery data")
 		}
 		d.nodeJoin(nodeData.Address, nodeData.BindAddress, nodeData.Self)
-	case discoverapi.DatastoreConfig:
-		if d.store != nil {
-			return types.ForbiddenErrorf("cannot accept datastore configuration: Overlay driver has a datastore configured already")
-		}
-		dsc, ok := data.(discoverapi.DatastoreConfigData)
-		if !ok {
-			return types.InternalErrorf("incorrect data in datastore configuration: %v", data)
-		}
-		d.store, err = datastore.NewDataStoreFromConfig(dsc)
-		if err != nil {
-			return types.InternalErrorf("failed to initialize data store: %v", err)
-		}
 	case discoverapi.EncryptionKeysConfig:
 		encrData, ok := data.(discoverapi.DriverEncryptionConfig)
 		if !ok {

+ 4 - 4
libnetwork/drivers/overlay/overlay_test.go

@@ -59,7 +59,7 @@ func setupDriver(t *testing.T) *driverTester {
 		},
 	}
 
-	if err := Init(dt, config); err != nil {
+	if err := Register(dt, config); err != nil {
 		t.Fatal(err)
 	}
 
@@ -114,14 +114,14 @@ func (dt *driverTester) RegisterDriver(name string, drv driverapi.Driver,
 }
 
 func TestOverlayInit(t *testing.T) {
-	if err := Init(&driverTester{t: t}, nil); err != nil {
+	if err := Register(&driverTester{t: t}, nil); err != nil {
 		t.Fatal(err)
 	}
 }
 
 func TestOverlayFiniWithoutConfig(t *testing.T) {
 	dt := &driverTester{t: t}
-	if err := Init(dt, nil); err != nil {
+	if err := Register(dt, nil); err != nil {
 		t.Fatal(err)
 	}
 
@@ -151,7 +151,7 @@ func TestOverlayConfig(t *testing.T) {
 
 func TestOverlayType(t *testing.T) {
 	dt := &driverTester{t: t}
-	if err := Init(dt, nil); err != nil {
+	if err := Register(dt, nil); err != nil {
 		t.Fatal(err)
 	}
 

+ 9 - 2
libnetwork/drivers/overlay/ovmanager/ovmanager.go

@@ -44,8 +44,15 @@ type network struct {
 	sync.Mutex
 }
 
-// Init registers a new instance of overlay driver
+// Init registers a new instance of the overlay driver.
+//
+// Deprecated: use [Register].
 func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
+	return Register(dc, config)
+}
+
+// Register registers a new instance of the overlay driver.
+func Register(r driverapi.DriverCallback, config map[string]interface{}) error {
 	var err error
 	c := driverapi.Capability{
 		DataScope:         datastore.GlobalScope,
@@ -62,7 +69,7 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
 		return fmt.Errorf("failed to initialize vxlan id manager: %v", err)
 	}
 
-	return dc.RegisterDriver(networkType, d, c)
+	return r.RegisterDriver(networkType, d, c)
 }
 
 func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {

+ 10 - 2
libnetwork/drivers/remote/driver.go

@@ -30,7 +30,15 @@ func newDriver(name string, client *plugins.Client) driverapi.Driver {
 
 // Init makes sure a remote driver is registered when a network driver
 // plugin is activated.
+//
+// Deprecated: use [Register].
 func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
+	return Register(dc, dc.GetPluginGetter())
+}
+
+// Register makes sure a remote driver is registered with r when a network
+// driver plugin is activated.
+func Register(r driverapi.Registerer, pg plugingetter.PluginGetter) error {
 	newPluginHandler := func(name string, client *plugins.Client) {
 		// negotiate driver capability with client
 		d := newDriver(name, client)
@@ -39,14 +47,14 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
 			logrus.Errorf("error getting capability for %s due to %v", name, err)
 			return
 		}
-		if err = dc.RegisterDriver(name, d, *c); err != nil {
+		if err = r.RegisterDriver(name, d, *c); err != nil {
 			logrus.Errorf("error registering driver for %s due to %v", name, err)
 		}
 	}
 
 	// Unit test code is unaware of a true PluginStore. So we fall back to v1 plugins.
 	handleFunc := plugins.Handle
-	if pg := dc.GetPluginGetter(); pg != nil {
+	if pg != nil {
 		handleFunc = pg.Handle
 		activePlugins := pg.GetAllManagedPluginsByCap(driverapi.NetworkPluginEndpointType)
 		for _, ap := range activePlugins {

+ 3 - 3
libnetwork/drivers/windows/overlay/overlay_windows.go

@@ -30,8 +30,8 @@ type driver struct {
 	sync.Mutex
 }
 
-// Init registers a new instance of overlay driver
-func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
+// Register registers a new instance of the overlay driver.
+func Register(r driverapi.Registerer, config map[string]interface{}) error {
 	c := driverapi.Capability{
 		DataScope:         datastore.GlobalScope,
 		ConnectivityScope: datastore.GlobalScope,
@@ -68,7 +68,7 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
 
 	d.restoreHNSNetworks()
 
-	return dc.RegisterDriver(networkType, d, c)
+	return r.RegisterDriver(networkType, d, c)
 }
 
 func (d *driver) restoreHNSNetworks() error {

+ 2 - 2
libnetwork/drivers/windows/windows.go

@@ -127,8 +127,8 @@ func newDriver(networkType string) *driver {
 }
 
 // GetInit returns an initializer for the given network type
-func GetInit(networkType string) func(dc driverapi.DriverCallback, config map[string]interface{}) error {
-	return func(dc driverapi.DriverCallback, config map[string]interface{}) error {
+func GetInit(networkType string) func(dc driverapi.Registerer, config map[string]interface{}) error {
+	return func(dc driverapi.Registerer, config map[string]interface{}) error {
 		if !IsBuiltinLocalDriver(networkType) {
 			return types.BadRequestErrorf("Network type not supported: %s", networkType)
 		}

+ 1 - 3
libnetwork/drivers_freebsd.go

@@ -2,12 +2,10 @@ package libnetwork
 
 import (
 	"github.com/docker/docker/libnetwork/drivers/null"
-	"github.com/docker/docker/libnetwork/drivers/remote"
 )
 
 func getInitializers() []initializer {
 	return []initializer{
-		{null.Init, "null"},
-		{remote.Init, "remote"},
+		{null.Register, "null"},
 	}
 }

+ 8 - 8
libnetwork/drivers_ipam.go

@@ -1,30 +1,30 @@
 package libnetwork
 
 import (
-	"github.com/docker/docker/libnetwork/drvregistry"
 	"github.com/docker/docker/libnetwork/ipamapi"
 	builtinIpam "github.com/docker/docker/libnetwork/ipams/builtin"
 	nullIpam "github.com/docker/docker/libnetwork/ipams/null"
 	remoteIpam "github.com/docker/docker/libnetwork/ipams/remote"
 	"github.com/docker/docker/libnetwork/ipamutils"
+	"github.com/docker/docker/pkg/plugingetter"
 )
 
-func initIPAMDrivers(r *drvregistry.DrvRegistry, lDs, gDs interface{}, addressPool []*ipamutils.NetworkToSplit) error {
+func initIPAMDrivers(r ipamapi.Registerer, pg plugingetter.PluginGetter, addressPool []*ipamutils.NetworkToSplit) error {
 	// TODO: pass address pools as arguments to builtinIpam.Init instead of
 	// indirectly through global mutable state. Swarmkit references that
 	// function so changing its signature breaks the build.
 	if err := builtinIpam.SetDefaultIPAddressPool(addressPool); err != nil {
 		return err
 	}
-	for _, fn := range [](func(ipamapi.Callback, interface{}, interface{}) error){
-		builtinIpam.Init,
-		remoteIpam.Init,
-		nullIpam.Init,
+
+	for _, fn := range [](func(ipamapi.Registerer) error){
+		builtinIpam.Register,
+		nullIpam.Register,
 	} {
-		if err := fn(r, lDs, gDs); err != nil {
+		if err := fn(r); err != nil {
 			return err
 		}
 	}
 
-	return nil
+	return remoteIpam.Register(r, pg)
 }

+ 6 - 8
libnetwork/drivers_linux.go

@@ -7,18 +7,16 @@ import (
 	"github.com/docker/docker/libnetwork/drivers/macvlan"
 	"github.com/docker/docker/libnetwork/drivers/null"
 	"github.com/docker/docker/libnetwork/drivers/overlay"
-	"github.com/docker/docker/libnetwork/drivers/remote"
 )
 
 func getInitializers() []initializer {
 	in := []initializer{
-		{bridge.Init, "bridge"},
-		{host.Init, "host"},
-		{ipvlan.Init, "ipvlan"},
-		{macvlan.Init, "macvlan"},
-		{null.Init, "null"},
-		{overlay.Init, "overlay"},
-		{remote.Init, "remote"},
+		{bridge.Register, "bridge"},
+		{host.Register, "host"},
+		{ipvlan.Register, "ipvlan"},
+		{macvlan.Register, "macvlan"},
+		{null.Register, "null"},
+		{overlay.Register, "overlay"},
 	}
 	return in
 }

+ 2 - 4
libnetwork/drivers_windows.go

@@ -2,16 +2,14 @@ package libnetwork
 
 import (
 	"github.com/docker/docker/libnetwork/drivers/null"
-	"github.com/docker/docker/libnetwork/drivers/remote"
 	"github.com/docker/docker/libnetwork/drivers/windows"
 	"github.com/docker/docker/libnetwork/drivers/windows/overlay"
 )
 
 func getInitializers() []initializer {
 	return []initializer{
-		{null.Init, "null"},
-		{overlay.Init, "overlay"},
-		{remote.Init, "remote"},
+		{null.Register, "null"},
+		{overlay.Register, "overlay"},
 		{windows.GetInit("transparent"), "transparent"},
 		{windows.GetInit("l2bridge"), "l2bridge"},
 		{windows.GetInit("l2tunnel"), "l2tunnel"},

+ 28 - 185
libnetwork/drvregistry/drvregistry.go

@@ -1,156 +1,58 @@
 package drvregistry
 
 import (
-	"errors"
 	"fmt"
-	"strings"
-	"sync"
 
 	"github.com/docker/docker/libnetwork/driverapi"
 	"github.com/docker/docker/libnetwork/ipamapi"
-	"github.com/docker/docker/libnetwork/types"
 	"github.com/docker/docker/pkg/plugingetter"
 )
 
-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
+	Networks
+	IPAMs
 	pluginGetter plugingetter.PluginGetter
 }
 
-// Functors definition
+var _ driverapi.DriverCallback = (*DrvRegistry)(nil)
+var _ ipamapi.Callback = (*DrvRegistry)(nil)
 
 // 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
+// Placeholder is a type for function arguments which need to be present for Swarmkit
+// to compile, but for which the only acceptable value is nil.
+type Placeholder *struct{}
 
-// New returns a new driver registry handle.
-func New(lDs, gDs interface{}, dfn DriverNotifyFunc, ifn IPAMNotifyFunc, pg plugingetter.PluginGetter) (*DrvRegistry, error) {
-	r := &DrvRegistry{
-		drivers:      make(driverTable),
-		ipamDrivers:  make(ipamTable),
-		dfn:          dfn,
-		ifn:          ifn,
+// New returns a new legacy driver registry.
+//
+// Deprecated: use the separate [Networks] and [IPAMs] registries.
+func New(lDs, gDs Placeholder, dfn DriverNotifyFunc, ifn Placeholder, pg plugingetter.PluginGetter) (*DrvRegistry, error) {
+	return &DrvRegistry{
+		Networks:     Networks{Notify: dfn},
 		pluginGetter: pg,
-	}
-
-	return r, nil
+	}, nil
 }
 
 // AddDriver adds a network driver to the registry.
-func (r *DrvRegistry) AddDriver(ntype string, fn InitFunc, config map[string]interface{}) error {
+//
+// Deprecated: call fn(r, config) directly.
+func (r *DrvRegistry) AddDriver(_ 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.
+//
+// Deprecated: call GetDefaultAddressSpaces() on the IPAM driver.
 func (r *DrvRegistry) IPAMDefaultAddressSpaces(name string) (string, string, error) {
-	r.Lock()
-	defer r.Unlock()
+	d, _ := r.IPAM(name)
 
-	i, ok := r.ipamDrivers[name]
-	if !ok {
+	if d == nil {
 		return "", "", fmt.Errorf("ipam %s not found", name)
 	}
 
-	return i.defaultLocalAddressSpace, i.defaultGlobalAddressSpace, nil
+	return d.GetDefaultAddressSpaces()
 }
 
 // GetPluginGetter returns the plugingetter
@@ -158,71 +60,12 @@ func (r *DrvRegistry) GetPluginGetter() plugingetter.PluginGetter {
 	return r.pluginGetter
 }
 
-// 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 errors.New("network type string cannot be empty")
-	}
-
-	r.Lock()
-	dd, ok := r.drivers[ntype]
-	r.Unlock()
-
-	if ok && dd.driver.IsBuiltIn() {
-		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 errors.New("ipam driver name string cannot be empty")
-	}
-
-	r.Lock()
-	dd, ok := r.ipamDrivers[name]
-	r.Unlock()
-	if ok && dd.driver.IsBuiltIn() {
-		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)
-	}
+// Driver returns the network driver instance registered under name, and its capability.
+func (r *DrvRegistry) Driver(name string) (driverapi.Driver, *driverapi.Capability) {
+	d, c := r.Networks.Driver(name)
 
-	if r.ifn != nil {
-		if err := r.ifn(name, driver, caps); err != nil {
-			return err
-		}
+	if c == (driverapi.Capability{}) {
+		return d, nil
 	}
-
-	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)
+	return d, &c
 }

+ 12 - 68
libnetwork/drvregistry/drvregistry_test.go

@@ -6,7 +6,6 @@ import (
 	"testing"
 
 	"github.com/docker/docker/libnetwork/datastore"
-	"github.com/docker/docker/libnetwork/discoverapi"
 	"github.com/docker/docker/libnetwork/driverapi"
 	"github.com/docker/docker/libnetwork/ipamapi"
 	builtinIpam "github.com/docker/docker/libnetwork/ipams/builtin"
@@ -18,48 +17,16 @@ import (
 
 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{}, nInfo driverapi.NetworkInfo, 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
+type mockDriver struct {
+	driverapi.Driver
 }
 
-func (m *mockDriver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) {
-	return nil, nil
-}
+var mockDriverCaps = driverapi.Capability{DataScope: datastore.LocalScope}
 
-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
-}
+var md = mockDriver{}
 
-func (m *mockDriver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
-	return nil
+func mockDriverInit(reg driverapi.DriverCallback, opt map[string]interface{}) error {
+	return reg.RegisterDriver(mockDriverName, &md, mockDriverCaps)
 }
 
 func (m *mockDriver) Type() string {
@@ -70,49 +37,26 @@ func (m *mockDriver) IsBuiltIn() bool {
 	return true
 }
 
-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 (m *mockDriver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {
-	return nil, nil
-}
-
-func (m *mockDriver) NetworkFree(id string) error {
-	return nil
-}
-
-func (m *mockDriver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
-}
-
-func (m *mockDriver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
-	return "", nil
-}
-
 func getNew(t *testing.T) *DrvRegistry {
 	reg, err := New(nil, nil, nil, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	err = initIPAMDrivers(reg, nil, nil)
+	err = initIPAMDrivers(reg)
 	if err != nil {
 		t.Fatal(err)
 	}
 	return reg
 }
 
-func initIPAMDrivers(r *DrvRegistry, lDs, gDs interface{}) error {
+func initIPAMDrivers(r *DrvRegistry) error {
 	for _, fn := range [](func(ipamapi.Callback, interface{}, interface{}) error){
-		builtinIpam.Init,
-		remoteIpam.Init,
-		nullIpam.Init,
+		builtinIpam.Init, //nolint:staticcheck
+		remoteIpam.Init,  //nolint:staticcheck
+		nullIpam.Init,    //nolint:staticcheck
 	} {
-		if err := fn(r, lDs, gDs); err != nil {
+		if err := fn(r, nil, nil); err != nil {
 			return err
 		}
 	}

+ 84 - 0
libnetwork/drvregistry/ipams.go

@@ -0,0 +1,84 @@
+package drvregistry
+
+import (
+	"errors"
+	"strings"
+	"sync"
+
+	"github.com/docker/docker/libnetwork/ipamapi"
+	"github.com/docker/docker/libnetwork/types"
+)
+
+type ipamDriver struct {
+	driver     ipamapi.Ipam
+	capability *ipamapi.Capability
+}
+
+// IPAMs is a registry of IPAM drivers. The zero value is an empty IPAM driver
+// registry, ready to use.
+type IPAMs struct {
+	mu      sync.Mutex
+	drivers map[string]ipamDriver
+}
+
+var _ ipamapi.Registerer = (*IPAMs)(nil)
+
+// IPAM returns the actual IPAM driver instance and its capability which registered with the passed name.
+func (ir *IPAMs) IPAM(name string) (ipamapi.Ipam, *ipamapi.Capability) {
+	ir.mu.Lock()
+	defer ir.mu.Unlock()
+
+	d := ir.drivers[name]
+	return d.driver, d.capability
+}
+
+// RegisterIpamDriverWithCapabilities registers the IPAM driver discovered with specified capabilities.
+func (ir *IPAMs) RegisterIpamDriverWithCapabilities(name string, driver ipamapi.Ipam, caps *ipamapi.Capability) error {
+	if strings.TrimSpace(name) == "" {
+		return errors.New("ipam driver name string cannot be empty")
+	}
+
+	ir.mu.Lock()
+	defer ir.mu.Unlock()
+
+	dd, ok := ir.drivers[name]
+	if ok && dd.driver.IsBuiltIn() {
+		return types.ForbiddenErrorf("ipam driver %q already registered", name)
+	}
+
+	if ir.drivers == nil {
+		ir.drivers = make(map[string]ipamDriver)
+	}
+	ir.drivers[name] = ipamDriver{driver: driver, capability: caps}
+
+	return nil
+}
+
+// RegisterIpamDriver registers the IPAM driver discovered with default capabilities.
+func (ir *IPAMs) RegisterIpamDriver(name string, driver ipamapi.Ipam) error {
+	return ir.RegisterIpamDriverWithCapabilities(name, driver, &ipamapi.Capability{})
+}
+
+// IPAMWalkFunc defines the IPAM driver table walker function signature.
+type IPAMWalkFunc func(name string, driver ipamapi.Ipam, cap *ipamapi.Capability) bool
+
+// WalkIPAMs walks the IPAM drivers registered in the registry and invokes the passed walk function and each one of them.
+func (ir *IPAMs) WalkIPAMs(ifn IPAMWalkFunc) {
+	type ipamVal struct {
+		name string
+		data ipamDriver
+	}
+
+	ir.mu.Lock()
+	ivl := make([]ipamVal, 0, len(ir.drivers))
+	for k, v := range ir.drivers {
+		ivl = append(ivl, ipamVal{name: k, data: v})
+	}
+	ir.mu.Unlock()
+
+	for _, iv := range ivl {
+		if ifn(iv.name, iv.data.driver, iv.data.capability) {
+			break
+		}
+	}
+}

+ 51 - 0
libnetwork/drvregistry/ipams_test.go

@@ -0,0 +1,51 @@
+package drvregistry
+
+import (
+	"runtime"
+	"sort"
+	"testing"
+
+	"github.com/docker/docker/libnetwork/ipamapi"
+	builtinIpam "github.com/docker/docker/libnetwork/ipams/builtin"
+	nullIpam "github.com/docker/docker/libnetwork/ipams/null"
+	remoteIpam "github.com/docker/docker/libnetwork/ipams/remote"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+)
+
+func getNewIPAMs(t *testing.T) *IPAMs {
+	r := &IPAMs{}
+
+	assert.Assert(t, builtinIpam.Register(r))
+	assert.Assert(t, remoteIpam.Register(r, nil))
+	assert.Assert(t, nullIpam.Register(r))
+
+	return r
+}
+
+func TestIPAMs(t *testing.T) {
+	t.Run("IPAM", func(t *testing.T) {
+		reg := getNewIPAMs(t)
+
+		i, cap := reg.IPAM("default")
+		assert.Check(t, i != nil)
+		assert.Check(t, cap != nil)
+	})
+
+	t.Run("WalkIPAMs", func(t *testing.T) {
+		reg := getNewIPAMs(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)
+		expected := []string{"default", "null"}
+		if runtime.GOOS == "windows" {
+			expected = append(expected, "windows")
+		}
+		assert.Check(t, is.DeepEqual(ipams, expected))
+	})
+}

+ 93 - 0
libnetwork/drvregistry/networks.go

@@ -0,0 +1,93 @@
+package drvregistry
+
+import (
+	"errors"
+	"strings"
+	"sync"
+
+	"github.com/docker/docker/libnetwork/driverapi"
+)
+
+// DriverWalkFunc defines the network driver table walker function signature.
+type DriverWalkFunc func(name string, driver driverapi.Driver, capability driverapi.Capability) bool
+
+// 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
+
+type driverData struct {
+	driver     driverapi.Driver
+	capability driverapi.Capability
+}
+
+// Networks is a registry of network drivers. The zero value is an empty network
+// driver registry, ready to use.
+type Networks struct {
+	// Notify is called whenever a network driver is registered.
+	Notify DriverNotifyFunc
+
+	mu      sync.Mutex
+	drivers map[string]driverData
+}
+
+var _ driverapi.Registerer = (*Networks)(nil)
+
+// WalkDrivers walks the network drivers registered in the registry and invokes the passed walk function and each one of them.
+func (nr *Networks) WalkDrivers(dfn DriverWalkFunc) {
+	type driverVal struct {
+		name string
+		data driverData
+	}
+
+	nr.mu.Lock()
+	dvl := make([]driverVal, 0, len(nr.drivers))
+	for k, v := range nr.drivers {
+		dvl = append(dvl, driverVal{name: k, data: v})
+	}
+	nr.mu.Unlock()
+
+	for _, dv := range dvl {
+		if dfn(dv.name, dv.data.driver, dv.data.capability) {
+			break
+		}
+	}
+}
+
+// Driver returns the network driver instance registered under name, and its capability.
+func (nr *Networks) Driver(name string) (driverapi.Driver, driverapi.Capability) {
+	nr.mu.Lock()
+	defer nr.mu.Unlock()
+
+	d := nr.drivers[name]
+	return d.driver, d.capability
+}
+
+// RegisterDriver registers the network driver with nr.
+func (nr *Networks) RegisterDriver(ntype string, driver driverapi.Driver, capability driverapi.Capability) error {
+	if strings.TrimSpace(ntype) == "" {
+		return errors.New("network type string cannot be empty")
+	}
+
+	nr.mu.Lock()
+	dd, ok := nr.drivers[ntype]
+	nr.mu.Unlock()
+
+	if ok && dd.driver.IsBuiltIn() {
+		return driverapi.ErrActiveRegistration(ntype)
+	}
+
+	if nr.Notify != nil {
+		if err := nr.Notify(ntype, driver, capability); err != nil {
+			return err
+		}
+	}
+
+	nr.mu.Lock()
+	defer nr.mu.Unlock()
+
+	if nr.drivers == nil {
+		nr.drivers = make(map[string]driverData)
+	}
+	nr.drivers[ntype] = driverData{driver: driver, capability: capability}
+
+	return nil
+}

+ 51 - 0
libnetwork/drvregistry/networks_test.go

@@ -0,0 +1,51 @@
+package drvregistry
+
+import (
+	"testing"
+
+	"github.com/docker/docker/libnetwork/driverapi"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+)
+
+func TestNetworks(t *testing.T) {
+	t.Run("RegisterDriver", func(t *testing.T) {
+		var reg Networks
+		err := reg.RegisterDriver(mockDriverName, &md, mockDriverCaps)
+		assert.NilError(t, err)
+	})
+
+	t.Run("RegisterDuplicateDriver", func(t *testing.T) {
+		var reg Networks
+		err := reg.RegisterDriver(mockDriverName, &md, mockDriverCaps)
+		assert.NilError(t, err)
+
+		// Try adding the same driver
+		err = reg.RegisterDriver(mockDriverName, &md, mockDriverCaps)
+		assert.Check(t, is.ErrorContains(err, ""))
+	})
+
+	t.Run("Driver", func(t *testing.T) {
+		var reg Networks
+		err := reg.RegisterDriver(mockDriverName, &md, mockDriverCaps)
+		assert.NilError(t, err)
+
+		d, cap := reg.Driver(mockDriverName)
+		assert.Check(t, d != nil)
+		assert.Check(t, is.DeepEqual(cap, mockDriverCaps))
+	})
+
+	t.Run("WalkDrivers", func(t *testing.T) {
+		var reg Networks
+		err := reg.RegisterDriver(mockDriverName, &md, mockDriverCaps)
+		assert.NilError(t, err)
+
+		var driverName string
+		reg.WalkDrivers(func(name string, driver driverapi.Driver, capability driverapi.Capability) bool {
+			driverName = name
+			return false
+		})
+
+		assert.Check(t, is.Equal(driverName, mockDriverName))
+	})
+}

+ 1 - 1
libnetwork/endpoint.go

@@ -1151,7 +1151,7 @@ func (c *Controller) cleanupLocalEndpoints() {
 			eps[ep.id] = true
 		}
 	}
-	nl, err := c.getNetworksForScope(datastore.LocalScope)
+	nl, err := c.getNetworks()
 	if err != nil {
 		logrus.Warnf("Could not get list of networks during endpoint cleanup: %v", err)
 		return

+ 2 - 2
libnetwork/endpoint_cnt.go

@@ -109,7 +109,7 @@ func (ec *endpointCnt) EndpointCnt() uint64 {
 }
 
 func (ec *endpointCnt) updateStore() error {
-	store := ec.n.getController().getStore(ec.DataScope())
+	store := ec.n.getController().getStore()
 	if store == nil {
 		return fmt.Errorf("store not found for scope %s on endpoint count update", ec.DataScope())
 	}
@@ -138,7 +138,7 @@ func (ec *endpointCnt) setCnt(cnt uint64) error {
 }
 
 func (ec *endpointCnt) atomicIncDecEpCnt(inc bool) error {
-	store := ec.n.getController().getStore(ec.DataScope())
+	store := ec.n.getController().getStore()
 	if store == nil {
 		return fmt.Errorf("store not found for scope %s", ec.DataScope())
 	}

+ 2 - 5
libnetwork/error.go

@@ -1,6 +1,7 @@
 package libnetwork
 
 import (
+	"errors"
 	"fmt"
 )
 
@@ -186,8 +187,4 @@ func (mr ManagerRedirectError) Maskable() {}
 
 // ErrDataStoreNotInitialized is returned if an invalid data scope is passed
 // for getting data store
-type ErrDataStoreNotInitialized string
-
-func (dsni ErrDataStoreNotInitialized) Error() string {
-	return fmt.Sprintf("datastore for scope %q is not initialized", string(dsni))
-}
+var ErrDataStoreNotInitialized = errors.New("datastore is not initialized")

+ 21 - 195
libnetwork/ipam/allocator.go

@@ -7,8 +7,6 @@ import (
 	"sync"
 
 	"github.com/docker/docker/libnetwork/bitseq"
-	"github.com/docker/docker/libnetwork/datastore"
-	"github.com/docker/docker/libnetwork/discoverapi"
 	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/libnetwork/types"
 	"github.com/sirupsen/logrus"
@@ -17,9 +15,6 @@ import (
 const (
 	localAddressSpace  = "LocalDefault"
 	globalAddressSpace = "GlobalDefault"
-	// datastore keyes for ipam objects
-	dsConfigKey = "ipam/" + ipamapi.DefaultIPAM + "/config"
-	dsDataKey   = "ipam/" + ipamapi.DefaultIPAM + "/data"
 )
 
 // Allocator provides per address space ipv4/ipv6 book keeping
@@ -28,168 +23,37 @@ type Allocator struct {
 	// Separate from the addrSpace because they should not be serialized
 	predefined             map[string][]*net.IPNet
 	predefinedStartIndices map[string]int
-	// The (potentially serialized) address spaces
+	// The address spaces
 	addrSpaces map[string]*addrSpace
-	// stores        []datastore.Datastore
 	// Allocated addresses in each address space's subnet
 	addresses map[SubnetKey]*bitseq.Handle
 	sync.Mutex
 }
 
 // NewAllocator returns an instance of libnetwork ipam
-func NewAllocator(lcDs, glDs datastore.DataStore, lcAs, glAs []*net.IPNet) (*Allocator, error) {
-	a := &Allocator{}
-
-	// Load predefined subnet pools
-
-	a.predefined = map[string][]*net.IPNet{
-		localAddressSpace:  lcAs,
-		globalAddressSpace: glAs,
+func NewAllocator(lcAs, glAs []*net.IPNet) (*Allocator, error) {
+	a := &Allocator{
+		predefined: map[string][]*net.IPNet{
+			localAddressSpace:  lcAs,
+			globalAddressSpace: glAs,
+		},
+		predefinedStartIndices: map[string]int{},
+		addresses:              map[SubnetKey]*bitseq.Handle{},
 	}
 
-	// Initialize asIndices map
-	a.predefinedStartIndices = make(map[string]int)
-
-	// Initialize bitseq map
-	a.addresses = make(map[SubnetKey]*bitseq.Handle)
-
-	// Initialize address spaces
-	a.addrSpaces = make(map[string]*addrSpace)
-	for _, aspc := range []struct {
-		as string
-		ds datastore.DataStore
-	}{
-		{localAddressSpace, lcDs},
-		{globalAddressSpace, glDs},
-	} {
-		a.initializeAddressSpace(aspc.as, aspc.ds)
+	a.addrSpaces = map[string]*addrSpace{
+		localAddressSpace:  a.newAddressSpace(),
+		globalAddressSpace: a.newAddressSpace(),
 	}
 
 	return a, nil
 }
 
-func (a *Allocator) refresh(as string) error {
-	aSpace, err := a.getAddressSpaceFromStore(as)
-	if err != nil {
-		return types.InternalErrorf("error getting pools config from store: %v", err)
-	}
-
-	if aSpace == nil {
-		return nil
-	}
-
-	a.Lock()
-	a.addrSpaces[as] = aSpace
-	a.Unlock()
-
-	return nil
-}
-
-func (a *Allocator) updateBitMasks(aSpace *addrSpace) error {
-	var inserterList []func() error
-
-	aSpace.Lock()
-	for k, v := range aSpace.subnets {
-		if v.Range == nil {
-			kk := k
-			vv := v
-			inserterList = append(inserterList, func() error { return a.insertBitMask(kk, vv.Pool) })
-		}
-	}
-	aSpace.Unlock()
-
-	// Add the bitmasks (data could come from datastore)
-	for _, f := range inserterList {
-		if err := f(); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-// Checks for and fixes damaged bitmask.
-func (a *Allocator) checkConsistency(as string) {
-	var sKeyList []SubnetKey
-
-	// Retrieve this address space's configuration and bitmasks from the datastore
-	a.refresh(as)
-	a.Lock()
-	aSpace, ok := a.addrSpaces[as]
-	a.Unlock()
-	if !ok {
-		return
-	}
-	a.updateBitMasks(aSpace)
-
-	aSpace.Lock()
-	for sk, pd := range aSpace.subnets {
-		if pd.Range != nil {
-			continue
-		}
-		sKeyList = append(sKeyList, sk)
-	}
-	aSpace.Unlock()
-
-	for _, sk := range sKeyList {
-		a.Lock()
-		bm := a.addresses[sk]
-		a.Unlock()
-		if err := bm.CheckConsistency(); err != nil {
-			logrus.Warnf("Error while running consistency check for %s: %v", sk, err)
-		}
-	}
-}
-
-func (a *Allocator) initializeAddressSpace(as string, ds datastore.DataStore) error {
-	scope := ""
-	if ds != nil {
-		scope = ds.Scope()
-	}
-
-	a.Lock()
-	if currAS, ok := a.addrSpaces[as]; ok {
-		if currAS.ds != nil {
-			a.Unlock()
-			return types.ForbiddenErrorf("a datastore is already configured for the address space %s", as)
-		}
-	}
-	a.addrSpaces[as] = &addrSpace{
+func (a *Allocator) newAddressSpace() *addrSpace {
+	return &addrSpace{
 		subnets: map[SubnetKey]*PoolData{},
-		id:      dsConfigKey + "/" + as,
-		scope:   scope,
-		ds:      ds,
 		alloc:   a,
 	}
-	a.Unlock()
-
-	a.checkConsistency(as)
-
-	return nil
-}
-
-// DiscoverNew informs the allocator about a new global scope datastore
-func (a *Allocator) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error {
-	if dType != discoverapi.DatastoreConfig {
-		return nil
-	}
-
-	dsc, ok := data.(discoverapi.DatastoreConfigData)
-	if !ok {
-		return types.InternalErrorf("incorrect data in datastore update notification: %v", data)
-	}
-
-	ds, err := datastore.NewDataStoreFromConfig(dsc)
-	if err != nil {
-		return err
-	}
-
-	return a.initializeAddressSpace(globalAddressSpace, ds)
-}
-
-// DiscoverDelete is a notification of no interest for the allocator
-func (a *Allocator) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
-	return nil
 }
 
 // GetDefaultAddressSpaces returns the local and global default address spaces
@@ -220,10 +84,6 @@ retry:
 		k = &SubnetKey{AddressSpace: addressSpace, Subnet: nw.String()}
 	}
 
-	if err := a.refresh(addressSpace); err != nil {
-		return "", nil, nil, err
-	}
-
 	aSpace, err := a.getAddrSpace(addressSpace)
 	if err != nil {
 		return "", nil, nil, err
@@ -238,14 +98,6 @@ retry:
 		return "", nil, nil, err
 	}
 
-	if err := a.writeToStore(aSpace); err != nil {
-		if _, ok := err.(types.RetryError); !ok {
-			return "", nil, nil, types.InternalErrorf("pool configuration failed because of %s", err.Error())
-		}
-
-		goto retry
-	}
-
 	return k.String(), nw, nil, insert()
 }
 
@@ -257,29 +109,12 @@ func (a *Allocator) ReleasePool(poolID string) error {
 		return types.BadRequestErrorf("invalid pool id: %s", poolID)
 	}
 
-retry:
-	if err := a.refresh(k.AddressSpace); err != nil {
-		return err
-	}
-
 	aSpace, err := a.getAddrSpace(k.AddressSpace)
 	if err != nil {
 		return err
 	}
 
-	remove, err := aSpace.updatePoolDBOnRemoval(k)
-	if err != nil {
-		return err
-	}
-
-	if err = a.writeToStore(aSpace); err != nil {
-		if _, ok := err.(types.RetryError); !ok {
-			return types.InternalErrorf("pool (%s) removal failed because of %v", poolID, err)
-		}
-		goto retry
-	}
-
-	return remove()
+	return aSpace.updatePoolDBOnRemoval(k)
 }
 
 // Given the address space, returns the local or global PoolConfig based on whether the
@@ -289,7 +124,7 @@ func (a *Allocator) getAddrSpace(as string) (*addrSpace, error) {
 	defer a.Unlock()
 	aSpace, ok := a.addrSpaces[as]
 	if !ok {
-		return nil, types.BadRequestErrorf("cannot find address space %s (most likely the backing datastore is not configured)", as)
+		return nil, types.BadRequestErrorf("cannot find address space %s", as)
 	}
 	return aSpace, nil
 }
@@ -331,7 +166,6 @@ func (a *Allocator) parsePoolRequest(addressSpace, pool, subPool string, v6 bool
 func (a *Allocator) insertBitMask(key SubnetKey, pool *net.IPNet) error {
 	//logrus.Debugf("Inserting bitmask (%s, %s)", key.String(), pool.String())
 
-	store := a.getStore(key.AddressSpace)
 	ipVer := getAddressVersion(pool.IP)
 	ones, bits := pool.Mask.Size()
 	numAddresses := uint64(1 << uint(bits-ones))
@@ -341,8 +175,8 @@ func (a *Allocator) insertBitMask(key SubnetKey, pool *net.IPNet) error {
 		numAddresses--
 	}
 
-	// Generate the new address masks. AddressMask content may come from datastore
-	h, err := bitseq.NewHandle(dsDataKey, store, key.String(), numAddresses)
+	// Generate the new address masks.
+	h, err := bitseq.NewHandle("", nil, "", numAddresses)
 	if err != nil {
 		return err
 	}
@@ -372,7 +206,7 @@ func (a *Allocator) retrieveBitmask(k SubnetKey, n *net.IPNet) (*bitseq.Handle,
 	if !ok {
 		logrus.Debugf("Retrieving bitmask (%s, %s)", k.String(), n.String())
 		if err := a.insertBitMask(k, n); err != nil {
-			return nil, types.InternalErrorf("could not find bitmask in datastore for %s", k.String())
+			return nil, types.InternalErrorf("could not find bitmask for %s", k.String())
 		}
 		a.Lock()
 		bm = a.addresses[k]
@@ -452,10 +286,6 @@ func (a *Allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[s
 		return nil, nil, types.BadRequestErrorf("invalid pool id: %s", poolID)
 	}
 
-	if err := a.refresh(k.AddressSpace); err != nil {
-		return nil, nil, err
-	}
-
 	aSpace, err := a.getAddrSpace(k.AddressSpace)
 	if err != nil {
 		return nil, nil, err
@@ -482,7 +312,7 @@ func (a *Allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[s
 
 	bm, err := a.retrieveBitmask(k, c.Pool)
 	if err != nil {
-		return nil, nil, types.InternalErrorf("could not find bitmask in datastore for %s on address %v request from pool %s: %v",
+		return nil, nil, types.InternalErrorf("could not find bitmask for %s on address %v request from pool %s: %v",
 			k.String(), prefAddress, poolID, err)
 	}
 	// In order to request for a serial ip address allocation, callers can pass in the option to request
@@ -509,10 +339,6 @@ func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
 		return types.BadRequestErrorf("invalid pool id: %s", poolID)
 	}
 
-	if err := a.refresh(k.AddressSpace); err != nil {
-		return err
-	}
-
 	aSpace, err := a.getAddrSpace(k.AddressSpace)
 	if err != nil {
 		return err
@@ -551,7 +377,7 @@ func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
 
 	bm, err := a.retrieveBitmask(k, c.Pool)
 	if err != nil {
-		return types.InternalErrorf("could not find bitmask in datastore for %s on address %v release from pool %s: %v",
+		return types.InternalErrorf("could not find bitmask for %s on address %v release from pool %s: %v",
 			k.String(), address, poolID, err)
 	}
 	defer logrus.Debugf("Released address PoolID:%s, Address:%v Sequence:%s", poolID, address, bm.String())

File diff suppressed because it is too large
+ 473 - 649
libnetwork/ipam/allocator_test.go


+ 3 - 5
libnetwork/ipam/parallel_test.go

@@ -12,6 +12,7 @@ import (
 	"time"
 
 	"github.com/docker/docker/libnetwork/ipamapi"
+	"github.com/docker/docker/libnetwork/ipamutils"
 	"golang.org/x/sync/errgroup"
 	"golang.org/x/sync/semaphore"
 	"gotest.tools/v3/assert"
@@ -36,15 +37,12 @@ type testContext struct {
 }
 
 func newTestContext(t *testing.T, mask int, options map[string]string) *testContext {
-	a, err := getAllocator(false)
+	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	if err != nil {
 		t.Fatal(err)
 	}
 	a.addrSpaces["giallo"] = &addrSpace{
-		id:      dsConfigKey + "/" + "giallo",
-		ds:      a.addrSpaces[localAddressSpace].ds,
 		alloc:   a.addrSpaces[localAddressSpace].alloc,
-		scope:   a.addrSpaces[localAddressSpace].scope,
 		subnets: map[SubnetKey]*PoolData{},
 	}
 
@@ -84,7 +82,7 @@ func (o *op) String() string {
 }
 
 func TestRequestPoolParallel(t *testing.T) {
-	a, err := getAllocator(false)
+	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	if err != nil {
 		t.Fatal(err)
 	}

+ 0 - 125
libnetwork/ipam/store.go

@@ -1,125 +0,0 @@
-package ipam
-
-import (
-	"encoding/json"
-
-	"github.com/docker/docker/libnetwork/datastore"
-	"github.com/docker/docker/libnetwork/types"
-	"github.com/sirupsen/logrus"
-)
-
-// Key provides the Key to be used in KV Store
-func (aSpace *addrSpace) Key() []string {
-	aSpace.Lock()
-	defer aSpace.Unlock()
-	return []string{aSpace.id}
-}
-
-// KeyPrefix returns the immediate parent key that can be used for tree walk
-func (aSpace *addrSpace) KeyPrefix() []string {
-	aSpace.Lock()
-	defer aSpace.Unlock()
-	return []string{dsConfigKey}
-}
-
-// Value marshals the data to be stored in the KV store
-func (aSpace *addrSpace) Value() []byte {
-	b, err := json.Marshal(aSpace)
-	if err != nil {
-		logrus.Warnf("Failed to marshal ipam configured pools: %v", err)
-		return nil
-	}
-	return b
-}
-
-// SetValue unmarshalls the data from the KV store.
-func (aSpace *addrSpace) SetValue(value []byte) error {
-	rc := &addrSpace{subnets: make(map[SubnetKey]*PoolData)}
-	if err := json.Unmarshal(value, rc); err != nil {
-		return err
-	}
-	aSpace.subnets = rc.subnets
-	return nil
-}
-
-// Index returns the latest DB Index as seen by this object
-func (aSpace *addrSpace) Index() uint64 {
-	aSpace.Lock()
-	defer aSpace.Unlock()
-	return aSpace.dbIndex
-}
-
-// SetIndex method allows the datastore to store the latest DB Index into this object
-func (aSpace *addrSpace) SetIndex(index uint64) {
-	aSpace.Lock()
-	aSpace.dbIndex = index
-	aSpace.dbExists = true
-	aSpace.Unlock()
-}
-
-// Exists method is true if this object has been stored in the DB.
-func (aSpace *addrSpace) Exists() bool {
-	aSpace.Lock()
-	defer aSpace.Unlock()
-	return aSpace.dbExists
-}
-
-// Skip provides a way for a KV Object to avoid persisting it in the KV Store
-func (aSpace *addrSpace) Skip() bool {
-	return false
-}
-
-func (a *Allocator) getStore(as string) datastore.DataStore {
-	a.Lock()
-	defer a.Unlock()
-
-	if aSpace, ok := a.addrSpaces[as]; ok {
-		return aSpace.ds
-	}
-
-	return nil
-}
-
-func (a *Allocator) getAddressSpaceFromStore(as string) (*addrSpace, error) {
-	store := a.getStore(as)
-
-	// IPAM may not have a valid store. In such cases it is just in-memory state.
-	if store == nil {
-		return nil, nil
-	}
-
-	pc := &addrSpace{id: dsConfigKey + "/" + as, ds: store, alloc: a}
-	if err := store.GetObject(datastore.Key(pc.Key()...), pc); err != nil {
-		if err == datastore.ErrKeyNotFound {
-			return nil, nil
-		}
-
-		return nil, types.InternalErrorf("could not get pools config from store: %v", err)
-	}
-
-	return pc, nil
-}
-
-func (a *Allocator) writeToStore(aSpace *addrSpace) error {
-	store := aSpace.store()
-
-	// IPAM may not have a valid store. In such cases it is just in-memory state.
-	if store == nil {
-		return nil
-	}
-
-	err := store.PutObjectAtomic(aSpace)
-	if err == datastore.ErrKeyModified {
-		return types.RetryErrorf("failed to perform atomic write (%v). retry might fix the error", err)
-	}
-
-	return err
-}
-
-// DataScope method returns the storage scope of the datastore
-func (aSpace *addrSpace) DataScope() string {
-	aSpace.Lock()
-	defer aSpace.Unlock()
-
-	return aSpace.scope
-}

+ 6 - 198
libnetwork/ipam/structures.go

@@ -1,13 +1,11 @@
 package ipam
 
 import (
-	"encoding/json"
 	"fmt"
 	"net"
 	"strings"
 	"sync"
 
-	"github.com/docker/docker/libnetwork/datastore"
 	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/libnetwork/types"
 )
@@ -29,13 +27,8 @@ type PoolData struct {
 
 // addrSpace contains the pool configurations for the address space
 type addrSpace struct {
-	subnets  map[SubnetKey]*PoolData
-	dbIndex  uint64
-	dbExists bool
-	id       string
-	scope    string
-	ds       datastore.DataStore
-	alloc    *Allocator
+	subnets map[SubnetKey]*PoolData
+	alloc   *Allocator
 	sync.Mutex
 }
 
@@ -51,31 +44,6 @@ func (r *AddressRange) String() string {
 	return fmt.Sprintf("Sub: %s, range [%d, %d]", r.Sub, r.Start, r.End)
 }
 
-// MarshalJSON returns the JSON encoding of the Range object
-func (r *AddressRange) MarshalJSON() ([]byte, error) {
-	m := map[string]interface{}{
-		"Sub":   r.Sub.String(),
-		"Start": r.Start,
-		"End":   r.End,
-	}
-	return json.Marshal(m)
-}
-
-// UnmarshalJSON decodes data into the Range object
-func (r *AddressRange) UnmarshalJSON(data []byte) error {
-	m := map[string]interface{}{}
-	err := json.Unmarshal(data, &m)
-	if err != nil {
-		return err
-	}
-	if r.Sub, err = types.ParseCIDR(m["Sub"].(string)); err != nil {
-		return err
-	}
-	r.Start = uint64(m["Start"].(float64))
-	r.End = uint64(m["End"].(float64))
-	return nil
-}
-
 // String returns the string form of the SubnetKey object
 func (s *SubnetKey) String() string {
 	k := fmt.Sprintf("%s/%s", s.AddressSpace, s.Subnet)
@@ -110,153 +78,6 @@ func (p *PoolData) String() string {
 		p.ParentKey.String(), p.Pool.String(), p.Range, p.RefCount)
 }
 
-// MarshalJSON returns the JSON encoding of the PoolData object
-func (p *PoolData) MarshalJSON() ([]byte, error) {
-	m := map[string]interface{}{
-		"ParentKey": p.ParentKey,
-		"RefCount":  p.RefCount,
-	}
-	if p.Pool != nil {
-		m["Pool"] = p.Pool.String()
-	}
-	if p.Range != nil {
-		m["Range"] = p.Range
-	}
-	return json.Marshal(m)
-}
-
-// UnmarshalJSON decodes data into the PoolData object
-func (p *PoolData) UnmarshalJSON(data []byte) error {
-	var (
-		err error
-		t   struct {
-			ParentKey SubnetKey
-			Pool      string
-			Range     *AddressRange `json:",omitempty"`
-			RefCount  int
-		}
-	)
-
-	if err = json.Unmarshal(data, &t); err != nil {
-		return err
-	}
-
-	p.ParentKey = t.ParentKey
-	p.Range = t.Range
-	p.RefCount = t.RefCount
-	if t.Pool != "" {
-		if p.Pool, err = types.ParseCIDR(t.Pool); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-// MarshalJSON returns the JSON encoding of the addrSpace object
-func (aSpace *addrSpace) MarshalJSON() ([]byte, error) {
-	aSpace.Lock()
-	defer aSpace.Unlock()
-
-	m := map[string]interface{}{
-		"Scope": aSpace.scope,
-	}
-
-	if aSpace.subnets != nil {
-		s := map[string]*PoolData{}
-		for k, v := range aSpace.subnets {
-			s[k.String()] = v
-		}
-		m["Subnets"] = s
-	}
-
-	return json.Marshal(m)
-}
-
-// UnmarshalJSON decodes data into the addrSpace object
-func (aSpace *addrSpace) UnmarshalJSON(data []byte) error {
-	aSpace.Lock()
-	defer aSpace.Unlock()
-
-	m := map[string]interface{}{}
-	err := json.Unmarshal(data, &m)
-	if err != nil {
-		return err
-	}
-
-	aSpace.scope = datastore.LocalScope
-	s := m["Scope"].(string)
-	if s == datastore.GlobalScope {
-		aSpace.scope = datastore.GlobalScope
-	}
-
-	if v, ok := m["Subnets"]; ok {
-		sb, _ := json.Marshal(v)
-		var s map[string]*PoolData
-		err := json.Unmarshal(sb, &s)
-		if err != nil {
-			return err
-		}
-		for ks, v := range s {
-			k := SubnetKey{}
-			k.FromString(ks)
-			aSpace.subnets[k] = v
-		}
-	}
-
-	return nil
-}
-
-// CopyTo deep copies the pool data to the destination pooldata
-func (p *PoolData) CopyTo(dstP *PoolData) error {
-	dstP.ParentKey = p.ParentKey
-	dstP.Pool = types.GetIPNetCopy(p.Pool)
-
-	if p.Range != nil {
-		dstP.Range = &AddressRange{}
-		dstP.Range.Sub = types.GetIPNetCopy(p.Range.Sub)
-		dstP.Range.Start = p.Range.Start
-		dstP.Range.End = p.Range.End
-	}
-
-	dstP.RefCount = p.RefCount
-	return nil
-}
-
-func (aSpace *addrSpace) CopyTo(o datastore.KVObject) error {
-	aSpace.Lock()
-	defer aSpace.Unlock()
-
-	dstAspace := o.(*addrSpace)
-
-	dstAspace.id = aSpace.id
-	dstAspace.ds = aSpace.ds
-	dstAspace.alloc = aSpace.alloc
-	dstAspace.scope = aSpace.scope
-	dstAspace.dbIndex = aSpace.dbIndex
-	dstAspace.dbExists = aSpace.dbExists
-
-	dstAspace.subnets = make(map[SubnetKey]*PoolData)
-	for k, v := range aSpace.subnets {
-		dstAspace.subnets[k] = &PoolData{}
-		v.CopyTo(dstAspace.subnets[k])
-	}
-
-	return nil
-}
-
-func (aSpace *addrSpace) New() datastore.KVObject {
-	aSpace.Lock()
-	defer aSpace.Unlock()
-
-	return &addrSpace{
-		id:    aSpace.id,
-		ds:    aSpace.ds,
-		alloc: aSpace.alloc,
-		scope: aSpace.scope,
-	}
-}
-
 // updatePoolDBOnAdd returns a closure which will add the subnet k to the address space when executed.
 func (aSpace *addrSpace) updatePoolDBOnAdd(k SubnetKey, nw *net.IPNet, ipr *AddressRange, pdf bool) (func() error, error) {
 	aSpace.Lock()
@@ -303,13 +124,13 @@ func (aSpace *addrSpace) updatePoolDBOnAdd(k SubnetKey, nw *net.IPNet, ipr *Addr
 	return func() error { return aSpace.alloc.insertBitMask(p.ParentKey, nw) }, nil
 }
 
-func (aSpace *addrSpace) updatePoolDBOnRemoval(k SubnetKey) (func() error, error) {
+func (aSpace *addrSpace) updatePoolDBOnRemoval(k SubnetKey) error {
 	aSpace.Lock()
 	defer aSpace.Unlock()
 
 	p, ok := aSpace.subnets[k]
 	if !ok {
-		return nil, ipamapi.ErrBadPool
+		return ipamapi.ErrBadPool
 	}
 
 	aSpace.incRefCount(p, -1)
@@ -319,20 +140,14 @@ func (aSpace *addrSpace) updatePoolDBOnRemoval(k SubnetKey) (func() error, error
 		if c.RefCount == 0 {
 			delete(aSpace.subnets, k)
 			if c.Range == nil {
-				return func() error {
-					bm, err := aSpace.alloc.retrieveBitmask(k, c.Pool)
-					if err != nil {
-						return types.InternalErrorf("could not find bitmask in datastore for pool %s removal: %v", k.String(), err)
-					}
-					return bm.Destroy()
-				}, nil
+				return nil
 			}
 		}
 		k = c.ParentKey
 		c, ok = aSpace.subnets[k]
 	}
 
-	return func() error { return nil }, nil
+	return nil
 }
 
 func (aSpace *addrSpace) incRefCount(p *PoolData, delta int) {
@@ -355,10 +170,3 @@ func (aSpace *addrSpace) contains(space string, nw *net.IPNet) bool {
 	}
 	return false
 }
-
-func (aSpace *addrSpace) store() datastore.DataStore {
-	aSpace.Lock()
-	defer aSpace.Unlock()
-
-	return aSpace.ds
-}

+ 12 - 8
libnetwork/ipamapi/contract.go

@@ -4,7 +4,6 @@ package ipamapi
 import (
 	"net"
 
-	"github.com/docker/docker/libnetwork/discoverapi"
 	"github.com/docker/docker/libnetwork/types"
 	"github.com/docker/docker/pkg/plugingetter"
 )
@@ -21,14 +20,21 @@ const (
 	RequestAddressType = "RequestAddressType"
 )
 
-// Callback provides a Callback interface for registering an IPAM instance into LibNetwork
+// Registerer provides a callback interface for registering IPAM instances into libnetwork.
+type Registerer interface {
+	// RegisterIpamDriver provides a way for drivers to dynamically register with libnetwork
+	RegisterIpamDriver(name string, driver Ipam) error
+	// RegisterIpamDriverWithCapabilities provides a way for drivers to dynamically register with libnetwork and specify capabilities
+	RegisterIpamDriverWithCapabilities(name string, driver Ipam, capability *Capability) error
+}
+
+// Callback is a legacy interface for registering an IPAM instance into LibNetwork.
+//
+// The narrower [Registerer] interface is preferred for new code.
 type Callback interface {
+	Registerer
 	// GetPluginGetter returns the pluginv2 getter.
 	GetPluginGetter() plugingetter.PluginGetter
-	// RegisterIpamDriver provides a way for Remote drivers to dynamically register with libnetwork
-	RegisterIpamDriver(name string, driver Ipam) error
-	// RegisterIpamDriverWithCapabilities provides a way for Remote drivers to dynamically register with libnetwork and specify capabilities
-	RegisterIpamDriverWithCapabilities(name string, driver Ipam, capability *Capability) error
 }
 
 // Well-known errors returned by IPAM
@@ -52,8 +58,6 @@ var (
 // Ipam represents the interface the IPAM service plugins must implement
 // in order to allow injection/modification of IPAM database.
 type Ipam interface {
-	discoverapi.Discover
-
 	// GetDefaultAddressSpaces returns the default local and global address spaces for this ipam
 	GetDefaultAddressSpaces() (string, string, error)
 	// RequestPool returns an address pool along with its unique id. Address space is a mandatory field

+ 3 - 22
libnetwork/ipams/builtin/builtin.go

@@ -1,10 +1,8 @@
 package builtin
 
 import (
-	"errors"
 	"net"
 
-	"github.com/docker/docker/libnetwork/datastore"
 	"github.com/docker/docker/libnetwork/ipam"
 	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/libnetwork/ipamutils"
@@ -15,25 +13,8 @@ var (
 	defaultAddressPool []*net.IPNet
 )
 
-// initBuiltin registers the built-in ipam service with libnetwork
-func initBuiltin(ic ipamapi.Callback, l, g interface{}) error {
-	var (
-		ok                bool
-		localDs, globalDs datastore.DataStore
-	)
-
-	if l != nil {
-		if localDs, ok = l.(datastore.DataStore); !ok {
-			return errors.New("incorrect local datastore passed to built-in ipam init")
-		}
-	}
-
-	if g != nil {
-		if globalDs, ok = g.(datastore.DataStore); !ok {
-			return errors.New("incorrect global datastore passed to built-in ipam init")
-		}
-	}
-
+// registerBuiltin registers the built-in ipam driver with libnetwork.
+func registerBuiltin(ic ipamapi.Registerer) error {
 	var localAddressPool []*net.IPNet
 	if len(defaultAddressPool) > 0 {
 		localAddressPool = append([]*net.IPNet(nil), defaultAddressPool...)
@@ -41,7 +22,7 @@ func initBuiltin(ic ipamapi.Callback, l, g interface{}) error {
 		localAddressPool = ipamutils.GetLocalScopeDefaultNetworks()
 	}
 
-	a, err := ipam.NewAllocator(localDs, globalDs, localAddressPool, ipamutils.GetGlobalScopeDefaultNetworks())
+	a, err := ipam.NewAllocator(localAddressPool, ipamutils.GetGlobalScopeDefaultNetworks())
 	if err != nil {
 		return err
 	}

+ 21 - 2
libnetwork/ipams/builtin/builtin_unix.go

@@ -3,9 +3,28 @@
 
 package builtin
 
-import "github.com/docker/docker/libnetwork/ipamapi"
+import (
+	"errors"
+
+	"github.com/docker/docker/libnetwork/ipamapi"
+)
 
 // Init registers the built-in ipam service with libnetwork
+//
+// Deprecated: use [Register].
 func Init(ic ipamapi.Callback, l, g interface{}) error {
-	return initBuiltin(ic, l, g)
+	if l != nil {
+		return errors.New("non-nil local datastore passed to built-in ipam init")
+	}
+
+	if g != nil {
+		return errors.New("non-nil global datastore passed to built-in ipam init")
+	}
+
+	return Register(ic)
+}
+
+// Register registers the built-in ipam service with libnetwork.
+func Register(r ipamapi.Registerer) error {
+	return registerBuiltin(r)
 }

+ 19 - 5
libnetwork/ipams/builtin/builtin_windows.go

@@ -4,18 +4,32 @@
 package builtin
 
 import (
+	"errors"
+
 	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/libnetwork/ipams/windowsipam"
 )
 
-// Init registers the built-in ipam service with libnetwork
+// Init registers the built-in ipam services with libnetwork.
+//
+// Deprecated: use [Register].
 func Init(ic ipamapi.Callback, l, g interface{}) error {
-	initFunc := windowsipam.GetInit(windowsipam.DefaultIPAM)
+	if l != nil {
+		return errors.New("non-nil local datastore passed to built-in ipam init")
+	}
+
+	if g != nil {
+		return errors.New("non-nil global datastore passed to built-in ipam init")
+	}
+
+	return Register(ic)
+}
 
-	err := initBuiltin(ic, l, g)
-	if err != nil {
+// Register registers the built-in ipam services with libnetwork.
+func Register(r ipamapi.Registerer) error {
+	if err := registerBuiltin(r); err != nil {
 		return err
 	}
 
-	return initFunc(ic, l, g)
+	return windowsipam.Register(windowsipam.DefaultIPAM, r)
 }

+ 9 - 2
libnetwork/ipams/null/null.go

@@ -69,7 +69,14 @@ func (a *allocator) IsBuiltIn() bool {
 	return true
 }
 
-// Init registers a remote ipam when its plugin is activated
+// Init registers the null ipam driver with ic.
+//
+// Deprecated: use [Register].
 func Init(ic ipamapi.Callback, l, g interface{}) error {
-	return ic.RegisterIpamDriver(ipamapi.NullIPAM, &allocator{})
+	return Register(ic)
+}
+
+// Register registers the null ipam driver with r.
+func Register(r ipamapi.Registerer) error {
+	return r.RegisterIpamDriver(ipamapi.NullIPAM, &allocator{})
 }

+ 8 - 2
libnetwork/ipams/remote/remote.go

@@ -30,9 +30,15 @@ func newAllocator(name string, client *plugins.Client) ipamapi.Ipam {
 	return a
 }
 
-// Init registers a remote ipam when its plugin is activated
+// Init registers a remote ipam when its plugin is activated.
+//
+// Deprecated: use [Register].
 func Init(cb ipamapi.Callback, l, g interface{}) error {
+	return Register(cb, cb.GetPluginGetter())
+}
 
+// Register registers a remote ipam when its plugin is activated.
+func Register(cb ipamapi.Registerer, pg plugingetter.PluginGetter) error {
 	newPluginHandler := func(name string, client *plugins.Client) {
 		a := newAllocator(name, client)
 		if cps, err := a.(*allocator).getCapabilities(); err == nil {
@@ -50,7 +56,7 @@ func Init(cb ipamapi.Callback, l, g interface{}) error {
 
 	// Unit test code is unaware of a true PluginStore. So we fall back to v1 plugins.
 	handleFunc := plugins.Handle
-	if pg := cb.GetPluginGetter(); pg != nil {
+	if pg != nil {
 		handleFunc = pg.Handle
 		activePlugins := pg.GetAllManagedPluginsByCap(ipamapi.PluginEndpointType)
 		for _, ap := range activePlugins {

+ 3 - 16
libnetwork/ipams/windowsipam/windowsipam.go

@@ -3,7 +3,6 @@ package windowsipam
 import (
 	"net"
 
-	"github.com/docker/docker/libnetwork/discoverapi"
 	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/libnetwork/types"
 	"github.com/sirupsen/logrus"
@@ -24,11 +23,9 @@ var (
 type allocator struct {
 }
 
-// GetInit registers the built-in ipam service with libnetwork
-func GetInit(ipamName string) func(ic ipamapi.Callback, l, g interface{}) error {
-	return func(ic ipamapi.Callback, l, g interface{}) error {
-		return ic.RegisterIpamDriver(ipamName, &allocator{})
-	}
+// Register registers the built-in ipam service with libnetwork
+func Register(ipamName string, r ipamapi.Registerer) error {
+	return r.RegisterIpamDriver(ipamName, &allocator{})
 }
 
 func (a *allocator) GetDefaultAddressSpaces() (string, string, error) {
@@ -87,16 +84,6 @@ func (a *allocator) ReleaseAddress(poolID string, address net.IP) error {
 	return nil
 }
 
-// DiscoverNew informs the allocator about a new global scope datastore
-func (a *allocator) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error {
-	return nil
-}
-
-// DiscoverDelete is a notification of no interest for the allocator
-func (a *allocator) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
-	return nil
-}
-
 func (a *allocator) IsBuiltIn() bool {
 	return true
 }

+ 2 - 2
libnetwork/libnetwork_internal_test.go

@@ -576,7 +576,7 @@ func TestIpamReleaseOnNetDriverFailures(t *testing.T) {
 	}
 	defer c.Stop()
 
-	if err := c.drvRegistry.AddDriver(badDriverName, badDriverInit, nil); err != nil {
+	if err := badDriverRegister(&c.drvRegistry); err != nil {
 		t.Fatal(err)
 	}
 
@@ -643,7 +643,7 @@ type badDriver struct {
 
 var bd = badDriver{failNetworkCreation: true}
 
-func badDriverInit(reg driverapi.DriverCallback, opt map[string]interface{}) error {
+func badDriverRegister(reg driverapi.Registerer) error {
 	return reg.RegisterDriver(badDriverName, &bd, driverapi.Capability{DataScope: datastore.LocalScope})
 }
 

+ 1 - 1
libnetwork/libnetwork_test.go

@@ -37,7 +37,7 @@ func TestMain(m *testing.M) {
 	}
 
 	// Cleanup local datastore file
-	_ = os.Remove(datastore.DefaultScopes("")[datastore.LocalScope].Client.Address)
+	_ = os.Remove(datastore.DefaultScope("").Client.Address)
 
 	os.Exit(m.Run())
 }

+ 10 - 6
libnetwork/network.go

@@ -916,7 +916,7 @@ func NetworkDeleteOptionRemoveLB(p *networkDeleteParams) {
 	p.rmLBEndpoint = true
 }
 
-func (n *network) resolveDriver(name string, load bool) (driverapi.Driver, *driverapi.Capability, error) {
+func (n *network) resolveDriver(name string, load bool) (driverapi.Driver, driverapi.Capability, error) {
 	c := n.getController()
 
 	// Check if a driver for the specified network type is available
@@ -925,16 +925,16 @@ func (n *network) resolveDriver(name string, load bool) (driverapi.Driver, *driv
 		if load {
 			err := c.loadDriver(name)
 			if err != nil {
-				return nil, nil, err
+				return nil, driverapi.Capability{}, err
 			}
 
 			d, cap = c.drvRegistry.Driver(name)
 			if d == nil {
-				return nil, nil, fmt.Errorf("could not resolve driver %s in registry", name)
+				return nil, driverapi.Capability{}, 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 nil, driverapi.Capability{}, nil
 		}
 	}
 
@@ -957,7 +957,7 @@ func (n *network) driver(load bool) (driverapi.Driver, error) {
 
 	n.mu.Lock()
 	// If load is not required, driver, cap and err may all be nil
-	if n.scope == "" && cap != nil {
+	if n.scope == "" {
 		n.scope = cap.DataScope
 	}
 	if n.dynamic {
@@ -1772,7 +1772,11 @@ func (n *network) getIPData(ipVer int) []driverapi.IPAMData {
 }
 
 func (n *network) deriveAddressSpace() (string, error) {
-	local, global, err := n.getController().drvRegistry.IPAMDefaultAddressSpaces(n.ipamType)
+	ipam, _ := n.getController().ipamRegistry.IPAM(n.ipamType)
+	if ipam == nil {
+		return "", types.NotFoundErrorf("failed to get default address space: unknown ipam type %q", n.ipamType)
+	}
+	local, global, err := ipam.GetDefaultAddressSpaces()
 	if err != nil {
 		return "", types.NotFoundErrorf("failed to get default address space: %v", err)
 	}

+ 1 - 1
libnetwork/sandbox_store.go

@@ -187,7 +187,7 @@ func (sb *Sandbox) storeDelete() error {
 }
 
 func (c *Controller) sandboxCleanup(activeSandboxes map[string]interface{}) {
-	store := c.getStore(datastore.LocalScope)
+	store := c.getStore()
 	if store == nil {
 		logrus.Error("Could not find local scope store while trying to cleanup sandboxes")
 		return

+ 64 - 104
libnetwork/store.go

@@ -13,34 +13,19 @@ func registerKVStores() {
 	boltdb.Register()
 }
 
-func (c *Controller) initScopedStore(scope string, scfg *datastore.ScopeCfg) error {
-	store, err := datastore.NewDataStore(scope, scfg)
-	if err != nil {
-		return err
-	}
-	c.mu.Lock()
-	c.stores = append(c.stores, store)
-	c.mu.Unlock()
-
-	return nil
-}
-
 func (c *Controller) initStores() error {
 	registerKVStores()
 
 	c.mu.Lock()
+	defer c.mu.Unlock()
+
 	if c.cfg == nil {
-		c.mu.Unlock()
 		return nil
 	}
-	scopeConfigs := c.cfg.Scopes
-	c.stores = nil
-	c.mu.Unlock()
-
-	for scope, scfg := range scopeConfigs {
-		if err := c.initScopedStore(scope, scfg); err != nil {
-			return err
-		}
+	var err error
+	c.store, err = datastore.NewDataStore(c.cfg.Scope)
+	if err != nil {
+		return err
 	}
 
 	c.startWatch()
@@ -48,29 +33,16 @@ func (c *Controller) initStores() error {
 }
 
 func (c *Controller) closeStores() {
-	for _, store := range c.getStores() {
+	if store := c.store; store != nil {
 		store.Close()
 	}
 }
 
-func (c *Controller) getStore(scope string) datastore.DataStore {
-	c.mu.Lock()
-	defer c.mu.Unlock()
-
-	for _, store := range c.stores {
-		if store.Scope() == scope {
-			return store
-		}
-	}
-
-	return nil
-}
-
-func (c *Controller) getStores() []datastore.DataStore {
+func (c *Controller) getStore() datastore.DataStore {
 	c.mu.Lock()
 	defer c.mu.Unlock()
 
-	return c.stores
+	return c.store
 }
 
 func (c *Controller) getNetworkFromStore(nid string) (*network, error) {
@@ -82,10 +54,10 @@ func (c *Controller) getNetworkFromStore(nid string) (*network, error) {
 	return nil, ErrNoSuchNetwork(nid)
 }
 
-func (c *Controller) getNetworksForScope(scope string) ([]*network, error) {
+func (c *Controller) getNetworks() ([]*network, error) {
 	var nl []*network
 
-	store := c.getStore(scope)
+	store := c.getStore()
 	if store == nil {
 		return nil, nil
 	}
@@ -93,8 +65,7 @@ func (c *Controller) getNetworksForScope(scope string) ([]*network, error) {
 	kvol, err := store.List(datastore.Key(datastore.NetworkKeyPrefix),
 		&network{ctrlr: c})
 	if err != nil && err != datastore.ErrKeyNotFound {
-		return nil, fmt.Errorf("failed to get networks for scope %s: %v",
-			scope, err)
+		return nil, fmt.Errorf("failed to get networks: %w", err)
 	}
 
 	for _, kvo := range kvol {
@@ -110,7 +81,7 @@ func (c *Controller) getNetworksForScope(scope string) ([]*network, error) {
 
 		n.epCnt = ec
 		if n.scope == "" {
-			n.scope = scope
+			n.scope = store.Scope()
 		}
 		nl = append(nl, n)
 	}
@@ -118,92 +89,80 @@ func (c *Controller) getNetworksForScope(scope string) ([]*network, error) {
 	return nl, nil
 }
 
-func (c *Controller) getNetworksFromStore() []*network {
+func (c *Controller) getNetworksFromStore() []*network { // FIXME: unify with c.getNetworks()
 	var nl []*network
 
-	for _, store := range c.getStores() {
-		kvol, err := store.List(datastore.Key(datastore.NetworkKeyPrefix), &network{ctrlr: c})
-		// Continue searching in the next store if no keys found in this store
-		if err != nil {
-			if err != datastore.ErrKeyNotFound {
-				logrus.Debugf("failed to get networks for scope %s: %v", store.Scope(), err)
-			}
-			continue
+	store := c.getStore()
+	kvol, err := store.List(datastore.Key(datastore.NetworkKeyPrefix), &network{ctrlr: c})
+	if err != nil {
+		if err != datastore.ErrKeyNotFound {
+			logrus.Debugf("failed to get networks from store: %v", err)
 		}
+		return nil
+	}
 
-		kvep, err := store.Map(datastore.Key(epCntKeyPrefix), &endpointCnt{})
-		if err != nil && err != datastore.ErrKeyNotFound {
-			logrus.Warnf("failed to get endpoint_count map for scope %s: %v", store.Scope(), err)
-		}
+	kvep, err := store.Map(datastore.Key(epCntKeyPrefix), &endpointCnt{})
+	if err != nil && err != datastore.ErrKeyNotFound {
+		logrus.Warnf("failed to get endpoint_count map from store: %v", err)
+	}
 
-		for _, kvo := range kvol {
-			n := kvo.(*network)
-			n.mu.Lock()
-			n.ctrlr = c
-			ec := &endpointCnt{n: n}
-			// Trim the leading & trailing "/" to make it consistent across all stores
-			if val, ok := kvep[strings.Trim(datastore.Key(ec.Key()...), "/")]; ok {
-				ec = val.(*endpointCnt)
-				ec.n = n
-				n.epCnt = ec
-			}
-			if n.scope == "" {
-				n.scope = store.Scope()
-			}
-			n.mu.Unlock()
-			nl = append(nl, n)
+	for _, kvo := range kvol {
+		n := kvo.(*network)
+		n.mu.Lock()
+		n.ctrlr = c
+		ec := &endpointCnt{n: n}
+		// Trim the leading & trailing "/" to make it consistent across all stores
+		if val, ok := kvep[strings.Trim(datastore.Key(ec.Key()...), "/")]; ok {
+			ec = val.(*endpointCnt)
+			ec.n = n
+			n.epCnt = ec
+		}
+		if n.scope == "" {
+			n.scope = store.Scope()
 		}
+		n.mu.Unlock()
+		nl = append(nl, n)
 	}
 
 	return nl
 }
 
 func (n *network) getEndpointFromStore(eid string) (*Endpoint, error) {
-	var errors []string
-	for _, store := range n.ctrlr.getStores() {
-		ep := &Endpoint{id: eid, network: n}
-		err := store.GetObject(datastore.Key(ep.Key()...), ep)
-		// Continue searching in the next store if the key is not found in this store
-		if err != nil {
-			if err != datastore.ErrKeyNotFound {
-				errors = append(errors, fmt.Sprintf("{%s:%v}, ", store.Scope(), err))
-				logrus.Debugf("could not find endpoint %s in %s: %v", eid, store.Scope(), err)
-			}
-			continue
-		}
-		return ep, nil
+	store := n.ctrlr.getStore()
+	ep := &Endpoint{id: eid, network: n}
+	err := store.GetObject(datastore.Key(ep.Key()...), ep)
+	if err != nil {
+		return nil, fmt.Errorf("could not find endpoint %s: %w", eid, err)
 	}
-	return nil, fmt.Errorf("could not find endpoint %s: %v", eid, errors)
+	return ep, nil
 }
 
 func (n *network) getEndpointsFromStore() ([]*Endpoint, error) {
 	var epl []*Endpoint
 
 	tmp := Endpoint{network: n}
-	for _, store := range n.getController().getStores() {
-		kvol, err := store.List(datastore.Key(tmp.KeyPrefix()...), &Endpoint{network: n})
-		// Continue searching in the next store if no keys found in this store
-		if err != nil {
-			if err != datastore.ErrKeyNotFound {
-				logrus.Debugf("failed to get endpoints for network %s scope %s: %v",
-					n.Name(), store.Scope(), err)
-			}
-			continue
+	store := n.getController().getStore()
+	kvol, err := store.List(datastore.Key(tmp.KeyPrefix()...), &Endpoint{network: n})
+	if err != nil {
+		if err != datastore.ErrKeyNotFound {
+			return nil, fmt.Errorf("failed to get endpoints for network %s scope %s: %w",
+				n.Name(), store.Scope(), err)
 		}
+		return nil, nil
+	}
 
-		for _, kvo := range kvol {
-			ep := kvo.(*Endpoint)
-			epl = append(epl, ep)
-		}
+	for _, kvo := range kvol {
+		ep := kvo.(*Endpoint)
+		epl = append(epl, ep)
 	}
 
 	return epl, nil
 }
 
 func (c *Controller) updateToStore(kvObject datastore.KVObject) error {
-	cs := c.getStore(kvObject.DataScope())
+	cs := c.getStore()
 	if cs == nil {
-		return ErrDataStoreNotInitialized(kvObject.DataScope())
+		return ErrDataStoreNotInitialized
 	}
 
 	if err := cs.PutObjectAtomic(kvObject); err != nil {
@@ -217,9 +176,9 @@ func (c *Controller) updateToStore(kvObject datastore.KVObject) error {
 }
 
 func (c *Controller) deleteFromStore(kvObject datastore.KVObject) error {
-	cs := c.getStore(kvObject.DataScope())
+	cs := c.getStore()
 	if cs == nil {
-		return ErrDataStoreNotInitialized(kvObject.DataScope())
+		return ErrDataStoreNotInitialized
 	}
 
 retry:
@@ -273,7 +232,8 @@ func (c *Controller) networkWatchLoop(nw *netWatch, ep *Endpoint, ecCh <-chan da
 
 			epl, err := ec.n.getEndpointsFromStore()
 			if err != nil {
-				break
+				logrus.WithError(err).Debug("error getting endpoints from store")
+				continue
 			}
 
 			c.mu.Lock()
@@ -371,7 +331,7 @@ func (c *Controller) processEndpointCreate(nmap map[string]*netWatch, ep *Endpoi
 	nw.stopCh = make(chan struct{})
 	c.mu.Unlock()
 
-	store := c.getStore(n.DataScope())
+	store := c.getStore()
 	if store == nil {
 		return
 	}

+ 2 - 2
libnetwork/store_linux_test.go

@@ -9,7 +9,7 @@ import (
 )
 
 func TestBoltdbBackend(t *testing.T) {
-	defer os.Remove(datastore.DefaultScopes("")[datastore.LocalScope].Client.Address)
+	defer os.Remove(datastore.DefaultScope("").Client.Address)
 	testLocalBackend(t, "", "", nil)
 	defer os.Remove("/tmp/boltdb.db")
 	config := &store.Config{Bucket: "testBackend"}
@@ -30,7 +30,7 @@ func TestNoPersist(t *testing.T) {
 	if err != nil {
 		t.Fatalf("Error creating endpoint: %v", err)
 	}
-	store := ctrl.getStore(datastore.LocalScope).KVStore()
+	store := ctrl.getStore().KVStore()
 	if exists, _ := store.Exists(datastore.Key(datastore.NetworkKeyPrefix, nw.ID())); exists {
 		t.Fatalf("Network with persist=false should not be stored in KV Store")
 	}

+ 1 - 1
libnetwork/store_test.go

@@ -36,7 +36,7 @@ func testLocalBackend(t *testing.T, provider, url string, storeConfig *store.Con
 	if err != nil {
 		t.Fatalf("Error creating endpoint: %v", err)
 	}
-	store := ctrl.getStore(datastore.LocalScope).KVStore()
+	store := ctrl.getStore().KVStore()
 	if exists, err := store.Exists(datastore.Key(datastore.NetworkKeyPrefix, nw.ID())); !exists || err != nil {
 		t.Fatalf("Network key should have been created.")
 	}

Some files were not shown because too many files changed in this diff