diff --git a/libnetwork/discoverapi/discoverapi.go b/libnetwork/discoverapi/discoverapi.go index 7ac36155db..8ee2f3edcb 100644 --- a/libnetwork/discoverapi/discoverapi.go +++ b/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) diff --git a/libnetwork/drivers/overlay/overlay.go b/libnetwork/drivers/overlay/overlay.go index 412d7f51be..2c3f4788c2 100644 --- a/libnetwork/drivers/overlay/overlay.go +++ b/libnetwork/drivers/overlay/overlay.go @@ -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 { diff --git a/libnetwork/ipam/allocator.go b/libnetwork/ipam/allocator.go index fd0fc540fa..7c2f8e4466 100644 --- a/libnetwork/ipam/allocator.go +++ b/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,11 +109,6 @@ 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 @@ -272,13 +119,6 @@ retry: 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() } @@ -289,7 +129,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 +171,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 +180,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 +211,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 +291,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 +317,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 +344,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 +382,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()) diff --git a/libnetwork/ipam/allocator_test.go b/libnetwork/ipam/allocator_test.go index 371fb483b4..60b9aa0e3d 100644 --- a/libnetwork/ipam/allocator_test.go +++ b/libnetwork/ipam/allocator_test.go @@ -1,70 +1,24 @@ package ipam import ( - "encoding/json" "flag" "fmt" "math/rand" "net" - "os" - "path/filepath" "strconv" "sync" "testing" "time" "github.com/docker/docker/libnetwork/bitseq" - "github.com/docker/docker/libnetwork/datastore" "github.com/docker/docker/libnetwork/ipamapi" "github.com/docker/docker/libnetwork/ipamutils" "github.com/docker/docker/libnetwork/types" - "github.com/docker/libkv/store" - "github.com/docker/libkv/store/boltdb" "golang.org/x/sync/errgroup" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) -var ( - defaultPrefix = filepath.Join(os.TempDir(), "libnetwork", "test", "ipam") -) - -func init() { - boltdb.Register() -} - -// OptionBoltdbWithRandomDBFile function returns a random dir for local store backend -func randomLocalStore(needStore bool) (datastore.DataStore, error) { - if !needStore { - return nil, nil - } - tmp, err := os.CreateTemp("", "libnetwork-") - if err != nil { - return nil, fmt.Errorf("Error creating temp file: %v", err) - } - if err := tmp.Close(); err != nil { - return nil, fmt.Errorf("Error closing temp file: %v", err) - } - return datastore.NewDataStore(datastore.ScopeCfg{ - Client: datastore.ScopeClientCfg{ - Provider: "boltdb", - Address: filepath.Join(defaultPrefix, filepath.Base(tmp.Name())), - Config: &store.Config{ - Bucket: "libnetwork", - ConnectionTimeout: 3 * time.Second, - }, - }, - }) -} - -func getAllocator(store bool) (*Allocator, error) { - ds, err := randomLocalStore(store) - if err != nil { - return nil, err - } - return NewAllocator(ds, nil, ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) -} - func TestInt2IP2IntConversion(t *testing.T) { for i := uint64(0); i < 256*256*256; i++ { var array [4]byte // new array at each cycle @@ -122,741 +76,611 @@ func TestKeyString(t *testing.T) { } } -func TestPoolDataMarshal(t *testing.T) { - _, nw, err := net.ParseCIDR("172.28.30.1/24") - if err != nil { - t.Fatal(err) - } - - p := &PoolData{ - ParentKey: SubnetKey{AddressSpace: "Blue", Subnet: "172.28.0.0/16"}, - Pool: nw, - Range: &AddressRange{Sub: &net.IPNet{IP: net.IP{172, 28, 20, 0}, Mask: net.IPMask{255, 255, 255, 0}}, Start: 0, End: 255}, - RefCount: 4, - } - - ba, err := json.Marshal(p) - if err != nil { - t.Fatal(err) - } - var q PoolData - err = json.Unmarshal(ba, &q) - if err != nil { - t.Fatal(err) - } - - if p.ParentKey != q.ParentKey || !types.CompareIPNet(p.Range.Sub, q.Range.Sub) || - p.Range.Start != q.Range.Start || p.Range.End != q.Range.End || p.RefCount != q.RefCount || - !types.CompareIPNet(p.Pool, q.Pool) { - t.Fatalf("\n%#v\n%#v", p, &q) - } - - p = &PoolData{ - ParentKey: SubnetKey{AddressSpace: "Blue", Subnet: "172.28.0.0/16"}, - Pool: nw, - RefCount: 4, - } - - ba, err = json.Marshal(p) - if err != nil { - t.Fatal(err) - } - err = json.Unmarshal(ba, &q) - if err != nil { - t.Fatal(err) - } - - if q.Range != nil { - t.Fatal("Unexpected Range") - } -} - -func TestSubnetsMarshal(t *testing.T) { - for _, store := range []bool{false, true} { - a, err := getAllocator(store) - if err != nil { - t.Fatal(err) - } - pid0, _, _, err := a.RequestPool(localAddressSpace, "192.168.0.0/16", "", nil, false) - if err != nil { - t.Fatal(err) - } - pid1, _, _, err := a.RequestPool(localAddressSpace, "192.169.0.0/16", "", nil, false) - if err != nil { - t.Fatal(err) - } - _, _, err = a.RequestAddress(pid0, nil, nil) - if err != nil { - t.Fatal(err) - } - - cfg, err := a.getAddrSpace(localAddressSpace) - if err != nil { - t.Fatal(err) - } - - ba := cfg.Value() - if err := cfg.SetValue(ba); err != nil { - t.Fatal(err) - } - - expIP := &net.IPNet{IP: net.IP{192, 168, 0, 2}, Mask: net.IPMask{255, 255, 0, 0}} - ip, _, err := a.RequestAddress(pid0, nil, nil) - if err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(expIP, ip) { - t.Fatalf("Got unexpected ip after pool config restore: %s", ip) - } - - expIP = &net.IPNet{IP: net.IP{192, 169, 0, 1}, Mask: net.IPMask{255, 255, 0, 0}} - ip, _, err = a.RequestAddress(pid1, nil, nil) - if err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(expIP, ip) { - t.Fatalf("Got unexpected ip after pool config restore: %s", ip) - } - } -} - func TestAddSubnets(t *testing.T) { - for _, store := range []bool{false, true} { - a, err := getAllocator(store) - if err != nil { - t.Fatal(err) - } - a.addrSpaces["abc"] = a.addrSpaces[localAddressSpace] + a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) + if err != nil { + t.Fatal(err) + } + a.addrSpaces["abc"] = a.addrSpaces[localAddressSpace] - pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) - if err != nil { - t.Fatal("Unexpected failure in adding subnet") - } + pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) + if err != nil { + t.Fatal("Unexpected failure in adding subnet") + } - pid1, _, _, err := a.RequestPool("abc", "10.0.0.0/8", "", nil, false) - if err != nil { - t.Fatalf("Unexpected failure in adding overlapping subnets to different address spaces: %v", err) - } + pid1, _, _, err := a.RequestPool("abc", "10.0.0.0/8", "", nil, false) + if err != nil { + t.Fatalf("Unexpected failure in adding overlapping subnets to different address spaces: %v", err) + } - if pid0 == pid1 { - t.Fatal("returned same pool id for same subnets in different namespaces") - } + if pid0 == pid1 { + t.Fatal("returned same pool id for same subnets in different namespaces") + } - _, _, _, err = a.RequestPool("abc", "10.0.0.0/8", "", nil, false) - if err == nil { - t.Fatalf("Expected failure requesting existing subnet") - } + _, _, _, err = a.RequestPool("abc", "10.0.0.0/8", "", nil, false) + if err == nil { + t.Fatalf("Expected failure requesting existing subnet") + } - _, _, _, err = a.RequestPool("abc", "10.128.0.0/9", "", nil, false) - if err == nil { - t.Fatal("Expected failure on adding overlapping base subnet") - } + _, _, _, err = a.RequestPool("abc", "10.128.0.0/9", "", nil, false) + if err == nil { + t.Fatal("Expected failure on adding overlapping base subnet") + } - _, _, _, err = a.RequestPool("abc", "10.0.0.0/8", "10.128.0.0/9", nil, false) - if err != nil { - t.Fatalf("Unexpected failure on adding sub pool: %v", err) - } - _, _, _, err = a.RequestPool("abc", "10.0.0.0/8", "10.128.0.0/9", nil, false) - if err == nil { - t.Fatalf("Expected failure on adding overlapping sub pool") - } + _, _, _, err = a.RequestPool("abc", "10.0.0.0/8", "10.128.0.0/9", nil, false) + if err != nil { + t.Fatalf("Unexpected failure on adding sub pool: %v", err) + } + _, _, _, err = a.RequestPool("abc", "10.0.0.0/8", "10.128.0.0/9", nil, false) + if err == nil { + t.Fatalf("Expected failure on adding overlapping sub pool") + } - _, _, _, err = a.RequestPool(localAddressSpace, "10.20.2.0/24", "", nil, false) - if err == nil { - t.Fatal("Failed to detect overlapping subnets") - } + _, _, _, err = a.RequestPool(localAddressSpace, "10.20.2.0/24", "", nil, false) + if err == nil { + t.Fatal("Failed to detect overlapping subnets") + } - _, _, _, err = a.RequestPool(localAddressSpace, "10.128.0.0/9", "", nil, false) - if err == nil { - t.Fatal("Failed to detect overlapping subnets") - } + _, _, _, err = a.RequestPool(localAddressSpace, "10.128.0.0/9", "", nil, false) + if err == nil { + t.Fatal("Failed to detect overlapping subnets") + } - _, _, _, err = a.RequestPool(localAddressSpace, "1003:1:2:3:4:5:6::/112", "", nil, false) - if err != nil { - t.Fatalf("Failed to add v6 subnet: %s", err.Error()) - } + _, _, _, err = a.RequestPool(localAddressSpace, "1003:1:2:3:4:5:6::/112", "", nil, false) + if err != nil { + t.Fatalf("Failed to add v6 subnet: %s", err.Error()) + } - _, _, _, err = a.RequestPool(localAddressSpace, "1003:1:2:3::/64", "", nil, false) - if err == nil { - t.Fatal("Failed to detect overlapping v6 subnet") - } + _, _, _, err = a.RequestPool(localAddressSpace, "1003:1:2:3::/64", "", nil, false) + if err == nil { + t.Fatal("Failed to detect overlapping v6 subnet") } } // TestDoublePoolRelease tests that releasing a pool which has already // been released raises an error. func TestDoublePoolRelease(t *testing.T) { - for _, store := range []bool{false, true} { - a, err := getAllocator(store) - assert.NilError(t, err) + a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) + assert.NilError(t, err) - pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) - assert.NilError(t, err) + pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) + assert.NilError(t, err) - err = a.ReleasePool(pid0) - assert.NilError(t, err) + err = a.ReleasePool(pid0) + assert.NilError(t, err) - err = a.ReleasePool(pid0) - assert.Check(t, is.ErrorContains(err, "")) - } + err = a.ReleasePool(pid0) + assert.Check(t, is.ErrorContains(err, "")) } func TestAddReleasePoolID(t *testing.T) { - for _, store := range []bool{false, true} { - a, err := getAllocator(store) - assert.NilError(t, err) + a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) + assert.NilError(t, err) - var k0, k1 SubnetKey - _, err = a.getAddrSpace(localAddressSpace) - if err != nil { - t.Fatal(err) - } + var k0, k1 SubnetKey + _, err = a.getAddrSpace(localAddressSpace) + if err != nil { + t.Fatal(err) + } - pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) - if err != nil { - t.Fatal("Unexpected failure in adding pool") - } - if err := k0.FromString(pid0); err != nil { - t.Fatal(err) - } + pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) + if err != nil { + t.Fatal("Unexpected failure in adding pool") + } + if err := k0.FromString(pid0); err != nil { + t.Fatal(err) + } - aSpace, err := a.getAddrSpace(localAddressSpace) - if err != nil { - t.Fatal(err) - } + aSpace, err := a.getAddrSpace(localAddressSpace) + if err != nil { + t.Fatal(err) + } - subnets := aSpace.subnets + subnets := aSpace.subnets - if subnets[k0].RefCount != 1 { - t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) - } + if subnets[k0].RefCount != 1 { + t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) + } - pid1, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false) - if err != nil { - t.Fatal("Unexpected failure in adding sub pool") - } - if err := k1.FromString(pid1); err != nil { - t.Fatal(err) - } + pid1, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false) + if err != nil { + t.Fatal("Unexpected failure in adding sub pool") + } + if err := k1.FromString(pid1); err != nil { + t.Fatal(err) + } - if pid0 == pid1 { - t.Fatalf("Incorrect poolIDs returned %s, %s", pid0, pid1) - } + if pid0 == pid1 { + t.Fatalf("Incorrect poolIDs returned %s, %s", pid0, pid1) + } - aSpace, err = a.getAddrSpace(localAddressSpace) - if err != nil { - t.Fatal(err) - } + aSpace, err = a.getAddrSpace(localAddressSpace) + if err != nil { + t.Fatal(err) + } - subnets = aSpace.subnets - if subnets[k1].RefCount != 1 { - t.Fatalf("Unexpected ref count for %s: %d", k1, subnets[k1].RefCount) - } + subnets = aSpace.subnets + if subnets[k1].RefCount != 1 { + t.Fatalf("Unexpected ref count for %s: %d", k1, subnets[k1].RefCount) + } - _, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false) - if err == nil { - t.Fatal("Expected failure in adding sub pool") - } + _, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false) + if err == nil { + t.Fatal("Expected failure in adding sub pool") + } - aSpace, err = a.getAddrSpace(localAddressSpace) - if err != nil { - t.Fatal(err) - } + aSpace, err = a.getAddrSpace(localAddressSpace) + if err != nil { + t.Fatal(err) + } - subnets = aSpace.subnets + subnets = aSpace.subnets - if subnets[k0].RefCount != 2 { - t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) - } + if subnets[k0].RefCount != 2 { + t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) + } - if err := a.ReleasePool(pid1); err != nil { - t.Fatal(err) - } + if err := a.ReleasePool(pid1); err != nil { + t.Fatal(err) + } - aSpace, err = a.getAddrSpace(localAddressSpace) - if err != nil { - t.Fatal(err) - } + aSpace, err = a.getAddrSpace(localAddressSpace) + if err != nil { + t.Fatal(err) + } - subnets = aSpace.subnets - if subnets[k0].RefCount != 1 { - t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) - } - if err := a.ReleasePool(pid0); err != nil { - t.Fatal(err) - } + subnets = aSpace.subnets + if subnets[k0].RefCount != 1 { + t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) + } + if err := a.ReleasePool(pid0); err != nil { + t.Fatal(err) + } - pid00, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) - if err != nil { - t.Fatal("Unexpected failure in adding pool") - } - if pid00 != pid0 { - t.Fatal("main pool should still exist") - } + pid00, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) + if err != nil { + t.Fatal("Unexpected failure in adding pool") + } + if pid00 != pid0 { + t.Fatal("main pool should still exist") + } - aSpace, err = a.getAddrSpace(localAddressSpace) - if err != nil { - t.Fatal(err) - } + aSpace, err = a.getAddrSpace(localAddressSpace) + if err != nil { + t.Fatal(err) + } - subnets = aSpace.subnets - if subnets[k0].RefCount != 1 { - t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) - } + subnets = aSpace.subnets + if subnets[k0].RefCount != 1 { + t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) + } - if err := a.ReleasePool(pid00); err != nil { - t.Fatal(err) - } + if err := a.ReleasePool(pid00); err != nil { + t.Fatal(err) + } - aSpace, err = a.getAddrSpace(localAddressSpace) - if err != nil { - t.Fatal(err) - } + aSpace, err = a.getAddrSpace(localAddressSpace) + if err != nil { + t.Fatal(err) + } - subnets = aSpace.subnets - if bp, ok := subnets[k0]; ok { - t.Fatalf("Base pool %s is still present: %v", k0, bp) - } + subnets = aSpace.subnets + if bp, ok := subnets[k0]; ok { + t.Fatalf("Base pool %s is still present: %v", k0, bp) + } - _, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) - if err != nil { - t.Fatal("Unexpected failure in adding pool") - } + _, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) + if err != nil { + t.Fatal("Unexpected failure in adding pool") + } - aSpace, err = a.getAddrSpace(localAddressSpace) - if err != nil { - t.Fatal(err) - } + aSpace, err = a.getAddrSpace(localAddressSpace) + if err != nil { + t.Fatal(err) + } - subnets = aSpace.subnets - if subnets[k0].RefCount != 1 { - t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) - } + subnets = aSpace.subnets + if subnets[k0].RefCount != 1 { + t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) } } func TestPredefinedPool(t *testing.T) { - for _, store := range []bool{false, true} { - a, err := getAllocator(store) - assert.NilError(t, err) + a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) + assert.NilError(t, err) - if _, err := a.getPredefinedPool("blue", false); err == nil { - t.Fatal("Expected failure for non default addr space") - } + if _, err := a.getPredefinedPool("blue", false); err == nil { + t.Fatal("Expected failure for non default addr space") + } - pid, nw, _, err := a.RequestPool(localAddressSpace, "", "", nil, false) - if err != nil { - t.Fatal(err) - } + pid, nw, _, err := a.RequestPool(localAddressSpace, "", "", nil, false) + if err != nil { + t.Fatal(err) + } - nw2, err := a.getPredefinedPool(localAddressSpace, false) - if err != nil { - t.Fatal(err) - } - if types.CompareIPNet(nw, nw2) { - t.Fatalf("Unexpected default network returned: %s = %s", nw2, nw) - } + nw2, err := a.getPredefinedPool(localAddressSpace, false) + if err != nil { + t.Fatal(err) + } + if types.CompareIPNet(nw, nw2) { + t.Fatalf("Unexpected default network returned: %s = %s", nw2, nw) + } - if err := a.ReleasePool(pid); err != nil { - t.Fatal(err) - } + if err := a.ReleasePool(pid); err != nil { + t.Fatal(err) } } func TestRemoveSubnet(t *testing.T) { - for _, store := range []bool{false, true} { - a, err := getAllocator(store) - assert.NilError(t, err) + a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) + assert.NilError(t, err) - a.addrSpaces["splane"] = &addrSpace{ - id: dsConfigKey + "/" + "splane", - ds: a.addrSpaces[localAddressSpace].ds, - alloc: a.addrSpaces[localAddressSpace].alloc, - scope: a.addrSpaces[localAddressSpace].scope, - subnets: map[SubnetKey]*PoolData{}, + a.addrSpaces["splane"] = &addrSpace{ + alloc: a.addrSpaces[localAddressSpace].alloc, + subnets: map[SubnetKey]*PoolData{}, + } + + input := []struct { + addrSpace string + subnet string + v6 bool + }{ + {localAddressSpace, "192.168.0.0/16", false}, + {localAddressSpace, "172.17.0.0/16", false}, + {localAddressSpace, "10.0.0.0/8", false}, + {localAddressSpace, "2001:db8:1:2:3:4:ffff::/112", false}, + {"splane", "172.17.0.0/16", false}, + {"splane", "10.0.0.0/8", false}, + {"splane", "2001:db8:1:2:3:4:5::/112", true}, + {"splane", "2001:db8:1:2:3:4:ffff::/112", true}, + } + + poolIDs := make([]string, len(input)) + + for ind, i := range input { + if poolIDs[ind], _, _, err = a.RequestPool(i.addrSpace, i.subnet, "", nil, i.v6); err != nil { + t.Fatalf("Failed to apply input. Can't proceed: %s", err.Error()) } + } - input := []struct { - addrSpace string - subnet string - v6 bool - }{ - {localAddressSpace, "192.168.0.0/16", false}, - {localAddressSpace, "172.17.0.0/16", false}, - {localAddressSpace, "10.0.0.0/8", false}, - {localAddressSpace, "2001:db8:1:2:3:4:ffff::/112", false}, - {"splane", "172.17.0.0/16", false}, - {"splane", "10.0.0.0/8", false}, - {"splane", "2001:db8:1:2:3:4:5::/112", true}, - {"splane", "2001:db8:1:2:3:4:ffff::/112", true}, - } - - poolIDs := make([]string, len(input)) - - for ind, i := range input { - if poolIDs[ind], _, _, err = a.RequestPool(i.addrSpace, i.subnet, "", nil, i.v6); err != nil { - t.Fatalf("Failed to apply input. Can't proceed: %s", err.Error()) - } - } - - for ind, id := range poolIDs { - if err := a.ReleasePool(id); err != nil { - t.Fatalf("Failed to release poolID %s (%d)", id, ind) - } + for ind, id := range poolIDs { + if err := a.ReleasePool(id); err != nil { + t.Fatalf("Failed to release poolID %s (%d)", id, ind) } } } func TestGetSameAddress(t *testing.T) { - for _, store := range []bool{false, true} { - a, err := getAllocator(store) - assert.NilError(t, err) + a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) + assert.NilError(t, 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{}, - } + a.addrSpaces["giallo"] = &addrSpace{ + alloc: a.addrSpaces[localAddressSpace].alloc, + subnets: map[SubnetKey]*PoolData{}, + } - pid, _, _, err := a.RequestPool("giallo", "192.168.100.0/24", "", nil, false) - if err != nil { - t.Fatal(err) - } + pid, _, _, err := a.RequestPool("giallo", "192.168.100.0/24", "", nil, false) + if err != nil { + t.Fatal(err) + } - ip := net.ParseIP("192.168.100.250") - _, _, err = a.RequestAddress(pid, ip, nil) - if err != nil { - t.Fatal(err) - } + ip := net.ParseIP("192.168.100.250") + _, _, err = a.RequestAddress(pid, ip, nil) + if err != nil { + t.Fatal(err) + } - _, _, err = a.RequestAddress(pid, ip, nil) - if err == nil { - t.Fatal(err) - } + _, _, err = a.RequestAddress(pid, ip, nil) + if err == nil { + t.Fatal(err) } } func TestPoolAllocationReuse(t *testing.T) { - for _, store := range []bool{false, true} { - a, err := getAllocator(store) - assert.NilError(t, err) + a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) + assert.NilError(t, err) - // First get all pools until they are exhausted to - pList := []string{} - pool, _, _, err := a.RequestPool(localAddressSpace, "", "", nil, false) - for err == nil { - pList = append(pList, pool) - pool, _, _, err = a.RequestPool(localAddressSpace, "", "", nil, false) - } - nPools := len(pList) - for _, pool := range pList { - if err := a.ReleasePool(pool); err != nil { - t.Fatal(err) - } + // First get all pools until they are exhausted to + pList := []string{} + pool, _, _, err := a.RequestPool(localAddressSpace, "", "", nil, false) + for err == nil { + pList = append(pList, pool) + pool, _, _, err = a.RequestPool(localAddressSpace, "", "", nil, false) + } + nPools := len(pList) + for _, pool := range pList { + if err := a.ReleasePool(pool); err != nil { + t.Fatal(err) } + } - // Now try to allocate then free nPool pools sequentially. - // Verify that we don't see any repeat networks even though - // we have freed them. - seen := map[string]bool{} - for i := 0; i < nPools; i++ { - pool, nw, _, err := a.RequestPool(localAddressSpace, "", "", nil, false) - if err != nil { - t.Fatal(err) - } - if _, ok := seen[nw.String()]; ok { - t.Fatalf("Network %s was reused before exhausing the pool list", nw.String()) - } - seen[nw.String()] = true - if err := a.ReleasePool(pool); err != nil { - t.Fatal(err) - } + // Now try to allocate then free nPool pools sequentially. + // Verify that we don't see any repeat networks even though + // we have freed them. + seen := map[string]bool{} + for i := 0; i < nPools; i++ { + pool, nw, _, err := a.RequestPool(localAddressSpace, "", "", nil, false) + if err != nil { + t.Fatal(err) + } + if _, ok := seen[nw.String()]; ok { + t.Fatalf("Network %s was reused before exhausing the pool list", nw.String()) + } + seen[nw.String()] = true + if err := a.ReleasePool(pool); err != nil { + t.Fatal(err) } } } func TestGetAddressSubPoolEqualPool(t *testing.T) { - for _, store := range []bool{false, true} { - a, err := getAllocator(store) - assert.NilError(t, err) + a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) + assert.NilError(t, err) - // Requesting a subpool of same size of the master pool should not cause any problem on ip allocation - pid, _, _, err := a.RequestPool(localAddressSpace, "172.18.0.0/16", "172.18.0.0/16", nil, false) - if err != nil { - t.Fatal(err) - } + // Requesting a subpool of same size of the master pool should not cause any problem on ip allocation + pid, _, _, err := a.RequestPool(localAddressSpace, "172.18.0.0/16", "172.18.0.0/16", nil, false) + if err != nil { + t.Fatal(err) + } - _, _, err = a.RequestAddress(pid, nil, nil) - if err != nil { - t.Fatal(err) - } + _, _, err = a.RequestAddress(pid, nil, nil) + if err != nil { + t.Fatal(err) } } func TestRequestReleaseAddressFromSubPool(t *testing.T) { - for _, store := range []bool{false, true} { - a, err := getAllocator(store) - assert.NilError(t, err) + a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) + assert.NilError(t, err) - a.addrSpaces["rosso"] = &addrSpace{ - id: dsConfigKey + "/" + "rosso", - ds: a.addrSpaces[localAddressSpace].ds, - alloc: a.addrSpaces[localAddressSpace].alloc, - scope: a.addrSpaces[localAddressSpace].scope, - subnets: map[SubnetKey]*PoolData{}, - } + a.addrSpaces["rosso"] = &addrSpace{ + alloc: a.addrSpaces[localAddressSpace].alloc, + subnets: map[SubnetKey]*PoolData{}, + } - poolID, _, _, err := a.RequestPool("rosso", "172.28.0.0/16", "172.28.30.0/24", nil, false) - if err != nil { - t.Fatal(err) - } + poolID, _, _, err := a.RequestPool("rosso", "172.28.0.0/16", "172.28.30.0/24", nil, false) + if err != nil { + t.Fatal(err) + } - var ip *net.IPNet - expected := &net.IPNet{IP: net.IP{172, 28, 30, 255}, Mask: net.IPMask{255, 255, 0, 0}} - for err == nil { - var c *net.IPNet - if c, _, err = a.RequestAddress(poolID, nil, nil); err == nil { - ip = c - } - } - if err != ipamapi.ErrNoAvailableIPs { - t.Fatal(err) - } - if !types.CompareIPNet(expected, ip) { - t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip) - } - rp := &net.IPNet{IP: net.IP{172, 28, 30, 97}, Mask: net.IPMask{255, 255, 0, 0}} - if err = a.ReleaseAddress(poolID, rp.IP); err != nil { - t.Fatal(err) - } - if ip, _, err = a.RequestAddress(poolID, nil, nil); err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(rp, ip) { - t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip) + var ip *net.IPNet + expected := &net.IPNet{IP: net.IP{172, 28, 30, 255}, Mask: net.IPMask{255, 255, 0, 0}} + for err == nil { + var c *net.IPNet + if c, _, err = a.RequestAddress(poolID, nil, nil); err == nil { + ip = c } + } + if err != ipamapi.ErrNoAvailableIPs { + t.Fatal(err) + } + if !types.CompareIPNet(expected, ip) { + t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip) + } + rp := &net.IPNet{IP: net.IP{172, 28, 30, 97}, Mask: net.IPMask{255, 255, 0, 0}} + if err = a.ReleaseAddress(poolID, rp.IP); err != nil { + t.Fatal(err) + } + if ip, _, err = a.RequestAddress(poolID, nil, nil); err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(rp, ip) { + t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip) + } - _, _, _, err = a.RequestPool("rosso", "10.0.0.0/8", "10.0.0.0/16", nil, false) - if err != nil { - t.Fatal(err) - } - poolID, _, _, err = a.RequestPool("rosso", "10.0.0.0/16", "10.0.0.0/24", nil, false) - if err != nil { - t.Fatal(err) - } - expected = &net.IPNet{IP: net.IP{10, 0, 0, 255}, Mask: net.IPMask{255, 255, 0, 0}} - for err == nil { - var c *net.IPNet - if c, _, err = a.RequestAddress(poolID, nil, nil); err == nil { - ip = c - } - } - if err != ipamapi.ErrNoAvailableIPs { - t.Fatal(err) - } - if !types.CompareIPNet(expected, ip) { - t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip) - } - rp = &net.IPNet{IP: net.IP{10, 0, 0, 79}, Mask: net.IPMask{255, 255, 0, 0}} - if err = a.ReleaseAddress(poolID, rp.IP); err != nil { - t.Fatal(err) - } - if ip, _, err = a.RequestAddress(poolID, nil, nil); err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(rp, ip) { - t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip) + _, _, _, err = a.RequestPool("rosso", "10.0.0.0/8", "10.0.0.0/16", nil, false) + if err != nil { + t.Fatal(err) + } + poolID, _, _, err = a.RequestPool("rosso", "10.0.0.0/16", "10.0.0.0/24", nil, false) + if err != nil { + t.Fatal(err) + } + expected = &net.IPNet{IP: net.IP{10, 0, 0, 255}, Mask: net.IPMask{255, 255, 0, 0}} + for err == nil { + var c *net.IPNet + if c, _, err = a.RequestAddress(poolID, nil, nil); err == nil { + ip = c } + } + if err != ipamapi.ErrNoAvailableIPs { + t.Fatal(err) + } + if !types.CompareIPNet(expected, ip) { + t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip) + } + rp = &net.IPNet{IP: net.IP{10, 0, 0, 79}, Mask: net.IPMask{255, 255, 0, 0}} + if err = a.ReleaseAddress(poolID, rp.IP); err != nil { + t.Fatal(err) + } + if ip, _, err = a.RequestAddress(poolID, nil, nil); err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(rp, ip) { + t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip) + } - // Request any addresses from subpool after explicit address request - unoExp, _ := types.ParseCIDR("10.2.2.0/16") - dueExp, _ := types.ParseCIDR("10.2.2.2/16") - treExp, _ := types.ParseCIDR("10.2.2.1/16") + // Request any addresses from subpool after explicit address request + unoExp, _ := types.ParseCIDR("10.2.2.0/16") + dueExp, _ := types.ParseCIDR("10.2.2.2/16") + treExp, _ := types.ParseCIDR("10.2.2.1/16") - if poolID, _, _, err = a.RequestPool("rosso", "10.2.0.0/16", "10.2.2.0/24", nil, false); err != nil { - t.Fatal(err) - } - tre, _, err := a.RequestAddress(poolID, treExp.IP, nil) - if err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(tre, treExp) { - t.Fatalf("Unexpected address: %v", tre) - } + if poolID, _, _, err = a.RequestPool("rosso", "10.2.0.0/16", "10.2.2.0/24", nil, false); err != nil { + t.Fatal(err) + } + tre, _, err := a.RequestAddress(poolID, treExp.IP, nil) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(tre, treExp) { + t.Fatalf("Unexpected address: %v", tre) + } - uno, _, err := a.RequestAddress(poolID, nil, nil) - if err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(uno, unoExp) { - t.Fatalf("Unexpected address: %v", uno) - } + uno, _, err := a.RequestAddress(poolID, nil, nil) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(uno, unoExp) { + t.Fatalf("Unexpected address: %v", uno) + } - due, _, err := a.RequestAddress(poolID, nil, nil) - if err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(due, dueExp) { - t.Fatalf("Unexpected address: %v", due) - } + due, _, err := a.RequestAddress(poolID, nil, nil) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(due, dueExp) { + t.Fatalf("Unexpected address: %v", due) + } - if err = a.ReleaseAddress(poolID, uno.IP); err != nil { - t.Fatal(err) - } - uno, _, err = a.RequestAddress(poolID, nil, nil) - if err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(uno, unoExp) { - t.Fatalf("Unexpected address: %v", uno) - } + if err = a.ReleaseAddress(poolID, uno.IP); err != nil { + t.Fatal(err) + } + uno, _, err = a.RequestAddress(poolID, nil, nil) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(uno, unoExp) { + t.Fatalf("Unexpected address: %v", uno) + } - if err = a.ReleaseAddress(poolID, tre.IP); err != nil { - t.Fatal(err) - } - tre, _, err = a.RequestAddress(poolID, nil, nil) - if err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(tre, treExp) { - t.Fatalf("Unexpected address: %v", tre) - } + if err = a.ReleaseAddress(poolID, tre.IP); err != nil { + t.Fatal(err) + } + tre, _, err = a.RequestAddress(poolID, nil, nil) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(tre, treExp) { + t.Fatalf("Unexpected address: %v", tre) } } func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) { opts := map[string]string{ ipamapi.AllocSerialPrefix: "true"} - for _, store := range []bool{false, true} { - a, err := getAllocator(store) - assert.NilError(t, err) + a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) + assert.NilError(t, err) - a.addrSpaces["rosso"] = &addrSpace{ - id: dsConfigKey + "/" + "rosso", - ds: a.addrSpaces[localAddressSpace].ds, - alloc: a.addrSpaces[localAddressSpace].alloc, - scope: a.addrSpaces[localAddressSpace].scope, - subnets: map[SubnetKey]*PoolData{}, - } + a.addrSpaces["rosso"] = &addrSpace{ + alloc: a.addrSpaces[localAddressSpace].alloc, + subnets: map[SubnetKey]*PoolData{}, + } - poolID, _, _, err := a.RequestPool("rosso", "172.28.0.0/16", "172.28.30.0/24", nil, false) - if err != nil { - t.Fatal(err) - } + poolID, _, _, err := a.RequestPool("rosso", "172.28.0.0/16", "172.28.30.0/24", nil, false) + if err != nil { + t.Fatal(err) + } - var ip *net.IPNet - expected := &net.IPNet{IP: net.IP{172, 28, 30, 255}, Mask: net.IPMask{255, 255, 0, 0}} - for err == nil { - var c *net.IPNet - if c, _, err = a.RequestAddress(poolID, nil, opts); err == nil { - ip = c - } - } - if err != ipamapi.ErrNoAvailableIPs { - t.Fatal(err) - } - if !types.CompareIPNet(expected, ip) { - t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip) - } - rp := &net.IPNet{IP: net.IP{172, 28, 30, 97}, Mask: net.IPMask{255, 255, 0, 0}} - if err = a.ReleaseAddress(poolID, rp.IP); err != nil { - t.Fatal(err) - } - if ip, _, err = a.RequestAddress(poolID, nil, opts); err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(rp, ip) { - t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip) + var ip *net.IPNet + expected := &net.IPNet{IP: net.IP{172, 28, 30, 255}, Mask: net.IPMask{255, 255, 0, 0}} + for err == nil { + var c *net.IPNet + if c, _, err = a.RequestAddress(poolID, nil, opts); err == nil { + ip = c } + } + if err != ipamapi.ErrNoAvailableIPs { + t.Fatal(err) + } + if !types.CompareIPNet(expected, ip) { + t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip) + } + rp := &net.IPNet{IP: net.IP{172, 28, 30, 97}, Mask: net.IPMask{255, 255, 0, 0}} + if err = a.ReleaseAddress(poolID, rp.IP); err != nil { + t.Fatal(err) + } + if ip, _, err = a.RequestAddress(poolID, nil, opts); err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(rp, ip) { + t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip) + } - _, _, _, err = a.RequestPool("rosso", "10.0.0.0/8", "10.0.0.0/16", nil, false) - if err != nil { - t.Fatal(err) - } - poolID, _, _, err = a.RequestPool("rosso", "10.0.0.0/16", "10.0.0.0/24", nil, false) - if err != nil { - t.Fatal(err) - } - expected = &net.IPNet{IP: net.IP{10, 0, 0, 255}, Mask: net.IPMask{255, 255, 0, 0}} - for err == nil { - var c *net.IPNet - if c, _, err = a.RequestAddress(poolID, nil, opts); err == nil { - ip = c - } - } - if err != ipamapi.ErrNoAvailableIPs { - t.Fatal(err) - } - if !types.CompareIPNet(expected, ip) { - t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip) - } - rp = &net.IPNet{IP: net.IP{10, 0, 0, 79}, Mask: net.IPMask{255, 255, 0, 0}} - if err = a.ReleaseAddress(poolID, rp.IP); err != nil { - t.Fatal(err) - } - if ip, _, err = a.RequestAddress(poolID, nil, opts); err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(rp, ip) { - t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip) + _, _, _, err = a.RequestPool("rosso", "10.0.0.0/8", "10.0.0.0/16", nil, false) + if err != nil { + t.Fatal(err) + } + poolID, _, _, err = a.RequestPool("rosso", "10.0.0.0/16", "10.0.0.0/24", nil, false) + if err != nil { + t.Fatal(err) + } + expected = &net.IPNet{IP: net.IP{10, 0, 0, 255}, Mask: net.IPMask{255, 255, 0, 0}} + for err == nil { + var c *net.IPNet + if c, _, err = a.RequestAddress(poolID, nil, opts); err == nil { + ip = c } + } + if err != ipamapi.ErrNoAvailableIPs { + t.Fatal(err) + } + if !types.CompareIPNet(expected, ip) { + t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip) + } + rp = &net.IPNet{IP: net.IP{10, 0, 0, 79}, Mask: net.IPMask{255, 255, 0, 0}} + if err = a.ReleaseAddress(poolID, rp.IP); err != nil { + t.Fatal(err) + } + if ip, _, err = a.RequestAddress(poolID, nil, opts); err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(rp, ip) { + t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip) + } - // Request any addresses from subpool after explicit address request - unoExp, _ := types.ParseCIDR("10.2.2.0/16") - dueExp, _ := types.ParseCIDR("10.2.2.2/16") - treExp, _ := types.ParseCIDR("10.2.2.1/16") - quaExp, _ := types.ParseCIDR("10.2.2.3/16") - fivExp, _ := types.ParseCIDR("10.2.2.4/16") - if poolID, _, _, err = a.RequestPool("rosso", "10.2.0.0/16", "10.2.2.0/24", nil, false); err != nil { - t.Fatal(err) - } - tre, _, err := a.RequestAddress(poolID, treExp.IP, opts) - if err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(tre, treExp) { - t.Fatalf("Unexpected address: %v", tre) - } + // Request any addresses from subpool after explicit address request + unoExp, _ := types.ParseCIDR("10.2.2.0/16") + dueExp, _ := types.ParseCIDR("10.2.2.2/16") + treExp, _ := types.ParseCIDR("10.2.2.1/16") + quaExp, _ := types.ParseCIDR("10.2.2.3/16") + fivExp, _ := types.ParseCIDR("10.2.2.4/16") + if poolID, _, _, err = a.RequestPool("rosso", "10.2.0.0/16", "10.2.2.0/24", nil, false); err != nil { + t.Fatal(err) + } + tre, _, err := a.RequestAddress(poolID, treExp.IP, opts) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(tre, treExp) { + t.Fatalf("Unexpected address: %v", tre) + } - uno, _, err := a.RequestAddress(poolID, nil, opts) - if err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(uno, unoExp) { - t.Fatalf("Unexpected address: %v", uno) - } + uno, _, err := a.RequestAddress(poolID, nil, opts) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(uno, unoExp) { + t.Fatalf("Unexpected address: %v", uno) + } - due, _, err := a.RequestAddress(poolID, nil, opts) - if err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(due, dueExp) { - t.Fatalf("Unexpected address: %v", due) - } + due, _, err := a.RequestAddress(poolID, nil, opts) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(due, dueExp) { + t.Fatalf("Unexpected address: %v", due) + } - if err = a.ReleaseAddress(poolID, uno.IP); err != nil { - t.Fatal(err) - } - uno, _, err = a.RequestAddress(poolID, nil, opts) - if err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(uno, quaExp) { - t.Fatalf("Unexpected address: %v", uno) - } + if err = a.ReleaseAddress(poolID, uno.IP); err != nil { + t.Fatal(err) + } + uno, _, err = a.RequestAddress(poolID, nil, opts) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(uno, quaExp) { + t.Fatalf("Unexpected address: %v", uno) + } - if err = a.ReleaseAddress(poolID, tre.IP); err != nil { - t.Fatal(err) - } - tre, _, err = a.RequestAddress(poolID, nil, opts) - if err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(tre, fivExp) { - t.Fatalf("Unexpected address: %v", tre) - } + if err = a.ReleaseAddress(poolID, tre.IP); err != nil { + t.Fatal(err) + } + tre, _, err = a.RequestAddress(poolID, nil, opts) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(tre, fivExp) { + t.Fatalf("Unexpected address: %v", tre) } } @@ -879,69 +703,64 @@ func TestRequestSyntaxCheck(t *testing.T) { as = "green" ) - for _, store := range []bool{false, true} { - a, err := getAllocator(store) - assert.NilError(t, err) + a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) + assert.NilError(t, err) - a.addrSpaces[as] = &addrSpace{ - id: dsConfigKey + "/" + as, - ds: a.addrSpaces[localAddressSpace].ds, - alloc: a.addrSpaces[localAddressSpace].alloc, - scope: a.addrSpaces[localAddressSpace].scope, - subnets: map[SubnetKey]*PoolData{}, - } + a.addrSpaces[as] = &addrSpace{ + alloc: a.addrSpaces[localAddressSpace].alloc, + subnets: map[SubnetKey]*PoolData{}, + } - _, _, _, err = a.RequestPool("", pool, "", nil, false) - if err == nil { - t.Fatal("Failed to detect wrong request: empty address space") - } + _, _, _, err = a.RequestPool("", pool, "", nil, false) + if err == nil { + t.Fatal("Failed to detect wrong request: empty address space") + } - _, _, _, err = a.RequestPool("", pool, subPool, nil, false) - if err == nil { - t.Fatal("Failed to detect wrong request: empty address space") - } + _, _, _, err = a.RequestPool("", pool, subPool, nil, false) + if err == nil { + t.Fatal("Failed to detect wrong request: empty address space") + } - _, _, _, err = a.RequestPool(as, "", subPool, nil, false) - if err == nil { - t.Fatal("Failed to detect wrong request: subPool specified and no pool") - } + _, _, _, err = a.RequestPool(as, "", subPool, nil, false) + if err == nil { + t.Fatal("Failed to detect wrong request: subPool specified and no pool") + } - pid, _, _, err := a.RequestPool(as, pool, subPool, nil, false) - if err != nil { - t.Fatalf("Unexpected failure: %v", err) - } + pid, _, _, err := a.RequestPool(as, pool, subPool, nil, false) + if err != nil { + t.Fatalf("Unexpected failure: %v", err) + } - _, _, err = a.RequestAddress("", nil, nil) - if err == nil { - t.Fatal("Failed to detect wrong request: no pool id specified") - } + _, _, err = a.RequestAddress("", nil, nil) + if err == nil { + t.Fatal("Failed to detect wrong request: no pool id specified") + } - ip := net.ParseIP("172.17.0.23") - _, _, err = a.RequestAddress(pid, ip, nil) - if err == nil { - t.Fatal("Failed to detect wrong request: requested IP from different subnet") - } + ip := net.ParseIP("172.17.0.23") + _, _, err = a.RequestAddress(pid, ip, nil) + if err == nil { + t.Fatal("Failed to detect wrong request: requested IP from different subnet") + } - ip = net.ParseIP("192.168.0.50") - _, _, err = a.RequestAddress(pid, ip, nil) - if err != nil { - t.Fatalf("Unexpected failure: %v", err) - } + ip = net.ParseIP("192.168.0.50") + _, _, err = a.RequestAddress(pid, ip, nil) + if err != nil { + t.Fatalf("Unexpected failure: %v", err) + } - err = a.ReleaseAddress("", ip) - if err == nil { - t.Fatal("Failed to detect wrong request: no pool id specified") - } + err = a.ReleaseAddress("", ip) + if err == nil { + t.Fatal("Failed to detect wrong request: no pool id specified") + } - err = a.ReleaseAddress(pid, nil) - if err == nil { - t.Fatal("Failed to detect wrong request: no pool id specified") - } + err = a.ReleaseAddress(pid, nil) + if err == nil { + t.Fatal("Failed to detect wrong request: no pool id specified") + } - err = a.ReleaseAddress(pid, ip) - if err != nil { - t.Fatalf("Unexpected failure: %v: %s, %s", err, pid, ip) - } + err = a.ReleaseAddress(pid, ip) + if err != nil { + t.Fatalf("Unexpected failure: %v: %s, %s", err, pid, ip) } } @@ -1034,24 +853,22 @@ func TestOverlappingRequests(t *testing.T) { {[]string{"3ea1:bfa9:8691:d1c6:8c46:519b:db6d:e700/120"}, "3000::/4", false}, } - for _, store := range []bool{false, true} { - for _, tc := range input { - a, err := getAllocator(store) + for _, tc := range input { + a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) + assert.NilError(t, err) + + // Set up some existing allocations. This should always succeed. + for _, env := range tc.environment { + _, _, _, err = a.RequestPool(localAddressSpace, env, "", nil, false) assert.NilError(t, err) + } - // Set up some existing allocations. This should always succeed. - for _, env := range tc.environment { - _, _, _, err = a.RequestPool(localAddressSpace, env, "", nil, false) - assert.NilError(t, err) - } - - // Make the test allocation. - _, _, _, err = a.RequestPool(localAddressSpace, tc.subnet, "", nil, false) - if tc.ok { - assert.NilError(t, err) - } else { - assert.Check(t, is.ErrorContains(err, "")) - } + // Make the test allocation. + _, _, _, err = a.RequestPool(localAddressSpace, tc.subnet, "", nil, false) + if tc.ok { + assert.NilError(t, err) + } else { + assert.Check(t, is.ErrorContains(err, "")) } } } @@ -1074,48 +891,46 @@ func TestUnusualSubnets(t *testing.T) { {"192.168.0.3"}, } - for _, store := range []bool{false, true} { - allocator, err := getAllocator(store) - if err != nil { - t.Fatal(err) + allocator, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) + if err != nil { + t.Fatal(err) + } + + // + // IPv4 /31 blocks. See RFC 3021. + // + + pool, _, _, err := allocator.RequestPool(localAddressSpace, subnet, "", nil, false) + if err != nil { + t.Fatal(err) + } + + // Outside-the-range + + for _, outside := range outsideTheRangeAddresses { + _, _, errx := allocator.RequestAddress(pool, net.ParseIP(outside.address), nil) + if errx != ipamapi.ErrIPOutOfRange { + t.Fatalf("Address %s failed to throw expected error: %s", outside.address, errx.Error()) } + } - // - // IPv4 /31 blocks. See RFC 3021. - // + // Should get just these two IPs followed by exhaustion on the next request - pool, _, _, err := allocator.RequestPool(localAddressSpace, subnet, "", nil, false) - if err != nil { - t.Fatal(err) + for _, expected := range expectedAddresses { + got, _, errx := allocator.RequestAddress(pool, nil, nil) + if errx != nil { + t.Fatalf("Failed to obtain the address: %s", errx.Error()) } - - // Outside-the-range - - for _, outside := range outsideTheRangeAddresses { - _, _, errx := allocator.RequestAddress(pool, net.ParseIP(outside.address), nil) - if errx != ipamapi.ErrIPOutOfRange { - t.Fatalf("Address %s failed to throw expected error: %s", outside.address, errx.Error()) - } + expectedIP := net.ParseIP(expected.address) + gotIP := got.IP + if !gotIP.Equal(expectedIP) { + t.Fatalf("Failed to obtain sequentialaddress. Expected: %s, Got: %s", expectedIP, gotIP) } + } - // Should get just these two IPs followed by exhaustion on the next request - - for _, expected := range expectedAddresses { - got, _, errx := allocator.RequestAddress(pool, nil, nil) - if errx != nil { - t.Fatalf("Failed to obtain the address: %s", errx.Error()) - } - expectedIP := net.ParseIP(expected.address) - gotIP := got.IP - if !gotIP.Equal(expectedIP) { - t.Fatalf("Failed to obtain sequentialaddress. Expected: %s, Got: %s", expectedIP, gotIP) - } - } - - _, _, err = allocator.RequestAddress(pool, nil, nil) - if err != ipamapi.ErrNoAvailableIPs { - t.Fatal("Did not get expected error when pool is exhausted.") - } + _, _, err = allocator.RequestAddress(pool, nil, nil) + if err != ipamapi.ErrNoAvailableIPs { + t.Fatal("Did not get expected error when pool is exhausted.") } } @@ -1124,64 +939,62 @@ func TestRelease(t *testing.T) { subnet = "192.168.0.0/23" ) - for _, store := range []bool{false, true} { - a, err := getAllocator(store) - assert.NilError(t, err) + a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) + assert.NilError(t, err) - pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) + pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) + if err != nil { + t.Fatal(err) + } + + // Allocate all addresses + for err != ipamapi.ErrNoAvailableIPs { + _, _, err = a.RequestAddress(pid, nil, nil) + } + + toRelease := []struct { + address string + }{ + {"192.168.0.1"}, + {"192.168.0.2"}, + {"192.168.0.3"}, + {"192.168.0.4"}, + {"192.168.0.5"}, + {"192.168.0.6"}, + {"192.168.0.7"}, + {"192.168.0.8"}, + {"192.168.0.9"}, + {"192.168.0.10"}, + {"192.168.0.30"}, + {"192.168.0.31"}, + {"192.168.1.32"}, + + {"192.168.0.254"}, + {"192.168.1.1"}, + {"192.168.1.2"}, + + {"192.168.1.3"}, + + {"192.168.1.253"}, + {"192.168.1.254"}, + } + + // One by one, release the address and request again. We should get the same IP + for i, inp := range toRelease { + ip0 := net.ParseIP(inp.address) + a.ReleaseAddress(pid, ip0) + bm := a.addresses[SubnetKey{localAddressSpace, subnet, ""}] + if bm.Unselected() != 1 { + t.Fatalf("Failed to update free address count after release. Expected %d, Found: %d", i+1, bm.Unselected()) + } + + nw, _, err := a.RequestAddress(pid, nil, nil) if err != nil { - t.Fatal(err) + t.Fatalf("Failed to obtain the address: %s", err.Error()) } - - // Allocate all addresses - for err != ipamapi.ErrNoAvailableIPs { - _, _, err = a.RequestAddress(pid, nil, nil) - } - - toRelease := []struct { - address string - }{ - {"192.168.0.1"}, - {"192.168.0.2"}, - {"192.168.0.3"}, - {"192.168.0.4"}, - {"192.168.0.5"}, - {"192.168.0.6"}, - {"192.168.0.7"}, - {"192.168.0.8"}, - {"192.168.0.9"}, - {"192.168.0.10"}, - {"192.168.0.30"}, - {"192.168.0.31"}, - {"192.168.1.32"}, - - {"192.168.0.254"}, - {"192.168.1.1"}, - {"192.168.1.2"}, - - {"192.168.1.3"}, - - {"192.168.1.253"}, - {"192.168.1.254"}, - } - - // One by one, release the address and request again. We should get the same IP - for i, inp := range toRelease { - ip0 := net.ParseIP(inp.address) - a.ReleaseAddress(pid, ip0) - bm := a.addresses[SubnetKey{localAddressSpace, subnet, ""}] - if bm.Unselected() != 1 { - t.Fatalf("Failed to update free address count after release. Expected %d, Found: %d", i+1, bm.Unselected()) - } - - nw, _, err := a.RequestAddress(pid, nil, nil) - if err != nil { - t.Fatalf("Failed to obtain the address: %s", err.Error()) - } - ip := nw.IP - if !ip0.Equal(ip) { - t.Fatalf("Failed to obtain the same address. Expected: %s, Got: %s", ip0, ip) - } + ip := nw.IP + if !ip0.Equal(ip) { + t.Fatalf("Failed to obtain the same address. Expected: %s, Got: %s", ip0, ip) } } } @@ -1230,27 +1043,25 @@ func assertNRequests(t *testing.T, subnet string, numReq int, lastExpectedIP str ) lastIP := net.ParseIP(lastExpectedIP) - for _, store := range []bool{false, true} { - a, err := getAllocator(store) - assert.NilError(t, err) + a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) + assert.NilError(t, err) - pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) - if err != nil { - t.Fatal(err) - } + pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) + if err != nil { + t.Fatal(err) + } - i := 0 - start := time.Now() - for ; i < numReq; i++ { - nw, _, err = a.RequestAddress(pid, nil, nil) - } - if printTime { - fmt.Printf("\nTaken %v, to allocate %d addresses on %s\n", time.Since(start), numReq, subnet) - } + i := 0 + start := time.Now() + for ; i < numReq; i++ { + nw, _, err = a.RequestAddress(pid, nil, nil) + } + if printTime { + fmt.Printf("\nTaken %v, to allocate %d addresses on %s\n", time.Since(start), numReq, subnet) + } - if !lastIP.Equal(nw.IP) { - t.Fatalf("Wrong last IP. Expected %s. Got: %s (err: %v, ind: %d)", lastExpectedIP, nw.IP.String(), err, i) - } + if !lastIP.Equal(nw.IP) { + t.Fatalf("Wrong last IP. Expected %s. Got: %s (err: %v, ind: %d)", lastExpectedIP, nw.IP.String(), err, i) } } @@ -1271,7 +1082,7 @@ func BenchmarkRequest(b *testing.B) { for _, subnet := range subnets { name := fmt.Sprintf("%vSubnet", subnet) b.Run(name, func(b *testing.B) { - a, _ := getAllocator(true) + a, _ := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) benchmarkRequest(b, a, subnet) }) } @@ -1285,10 +1096,7 @@ func TestAllocateRandomDeallocate(t *testing.T) { } func testAllocateRandomDeallocate(t *testing.T, pool, subPool string, num int, store bool) { - ds, err := randomLocalStore(store) - assert.NilError(t, err) - - a, err := NewAllocator(ds, nil, ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) + a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) if err != nil { t.Fatal(err) } @@ -1350,120 +1158,15 @@ func testAllocateRandomDeallocate(t *testing.T, pool, subPool string, num int, s } } -func TestRetrieveFromStore(t *testing.T) { - num := 200 - ds, err := randomLocalStore(true) - if err != nil { - t.Fatal(err) - } - a, err := NewAllocator(ds, nil, ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) - if err != nil { - t.Fatal(err) - } - pid, _, _, err := a.RequestPool(localAddressSpace, "172.25.0.0/16", "", nil, false) - if err != nil { - t.Fatal(err) - } - for i := 0; i < num; i++ { - if _, _, err := a.RequestAddress(pid, nil, nil); err != nil { - t.Fatal(err) - } - } - - // Restore - a1, err := NewAllocator(ds, nil, ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) - if err != nil { - t.Fatal(err) - } - a1.refresh(localAddressSpace) - db := a.DumpDatabase() - db1 := a1.DumpDatabase() - if db != db1 { - t.Fatalf("Unexpected db change.\nExpected:%s\nGot:%s", db, db1) - } - checkDBEquality(a, a1, t) - pid, _, _, err = a1.RequestPool(localAddressSpace, "172.25.0.0/16", "172.25.1.0/24", nil, false) - if err != nil { - t.Fatal(err) - } - for i := 0; i < num/2; i++ { - if _, _, err := a1.RequestAddress(pid, nil, nil); err != nil { - t.Fatal(err) - } - } - - // Restore - a2, err := NewAllocator(ds, nil, ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) - if err != nil { - t.Fatal(err) - } - a2.refresh(localAddressSpace) - checkDBEquality(a1, a2, t) - pid, _, _, err = a2.RequestPool(localAddressSpace, "172.25.0.0/16", "172.25.2.0/24", nil, false) - if err != nil { - t.Fatal(err) - } - for i := 0; i < num/2; i++ { - if _, _, err := a2.RequestAddress(pid, nil, nil); err != nil { - t.Fatal(err) - } - } - - // Restore - a3, err := NewAllocator(ds, nil, ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) - if err != nil { - t.Fatal(err) - } - a3.refresh(localAddressSpace) - checkDBEquality(a2, a3, t) - pid, _, _, err = a3.RequestPool(localAddressSpace, "172.26.0.0/16", "", nil, false) - if err != nil { - t.Fatal(err) - } - for i := 0; i < num/2; i++ { - if _, _, err := a3.RequestAddress(pid, nil, nil); err != nil { - t.Fatal(err) - } - } - - // Restore - a4, err := NewAllocator(ds, nil, ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) - if err != nil { - t.Fatal(err) - } - a4.refresh(localAddressSpace) - checkDBEquality(a3, a4, t) -} - -func checkDBEquality(a1, a2 *Allocator, t *testing.T) { - for k, cnf1 := range a1.addrSpaces[localAddressSpace].subnets { - cnf2 := a2.addrSpaces[localAddressSpace].subnets[k] - if cnf1.String() != cnf2.String() { - t.Fatalf("%s\n%s", cnf1, cnf2) - } - if cnf1.Range == nil { - a2.retrieveBitmask(k, cnf1.Pool) - } - } - - for k, bm1 := range a1.addresses { - bm2 := a2.addresses[k] - if bm1.String() != bm2.String() { - t.Fatalf("%s\n%s", bm1, bm2) - } - } -} - const ( numInstances = 5 first = 0 - last = numInstances - 1 ) var ( allocator *Allocator start = make(chan struct{}) - done = make(chan chan struct{}, numInstances-1) + done sync.WaitGroup pools = make([]*net.IPNet, numInstances) ) @@ -1487,22 +1190,17 @@ func runParallelTests(t *testing.T, instance int) { // The first instance creates the allocator, gives the start // and finally checks the pools each instance was assigned if instance == first { - allocator, err = getAllocator(true) + allocator, err = NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) if err != nil { t.Fatal(err) } + done.Add(numInstances - 1) close(start) } if instance != first { <-start - instDone := make(chan struct{}) - done <- instDone - defer close(instDone) - - if instance == last { - defer close(done) - } + defer done.Done() } _, pools[instance], _, err = allocator.RequestPool(localAddressSpace, "", "", nil, false) @@ -1511,14 +1209,12 @@ func runParallelTests(t *testing.T, instance int) { } if instance == first { - for instDone := range done { - <-instDone - } + done.Wait() // Now check each instance got a different pool for i := 0; i < numInstances; i++ { for j := i + 1; j < numInstances; j++ { if types.CompareIPNet(pools[i], pools[j]) { - t.Fatalf("Instance %d and %d were given the same predefined pool: %v", i, j, pools) + t.Errorf("Instance %d and %d were given the same predefined pool: %v", i, j, pools) } } } @@ -1526,7 +1222,7 @@ func runParallelTests(t *testing.T, instance int) { } func TestRequestReleaseAddressDuplicate(t *testing.T) { - a, err := getAllocator(false) + a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) if err != nil { t.Fatal(err) } @@ -1537,10 +1233,7 @@ func TestRequestReleaseAddressDuplicate(t *testing.T) { ips := []IP{} allocatedIPs := []*net.IPNet{} a.addrSpaces["rosso"] = &addrSpace{ - id: dsConfigKey + "/" + "rosso", - ds: a.addrSpaces[localAddressSpace].ds, alloc: a.addrSpaces[localAddressSpace].alloc, - scope: a.addrSpaces[localAddressSpace].scope, subnets: map[SubnetKey]*PoolData{}, } diff --git a/libnetwork/ipam/parallel_test.go b/libnetwork/ipam/parallel_test.go index 636fc4bc60..6e96ebabdc 100644 --- a/libnetwork/ipam/parallel_test.go +++ b/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) } diff --git a/libnetwork/ipam/store.go b/libnetwork/ipam/store.go deleted file mode 100644 index e00401616b..0000000000 --- a/libnetwork/ipam/store.go +++ /dev/null @@ -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 -} diff --git a/libnetwork/ipam/structures.go b/libnetwork/ipam/structures.go index dd5edf13e1..542eb506cc 100644 --- a/libnetwork/ipam/structures.go +++ b/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() @@ -355,10 +176,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 -} diff --git a/libnetwork/ipamapi/contract.go b/libnetwork/ipamapi/contract.go index 36f1b5a9c4..3f687141e7 100644 --- a/libnetwork/ipamapi/contract.go +++ b/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" ) @@ -59,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 diff --git a/libnetwork/ipams/builtin/builtin.go b/libnetwork/ipams/builtin/builtin.go index b72bdd07b2..17afbeb029 100644 --- a/libnetwork/ipams/builtin/builtin.go +++ b/libnetwork/ipams/builtin/builtin.go @@ -22,7 +22,7 @@ func registerBuiltin(ic ipamapi.Registerer) error { localAddressPool = ipamutils.GetLocalScopeDefaultNetworks() } - a, err := ipam.NewAllocator(nil, nil, localAddressPool, ipamutils.GetGlobalScopeDefaultNetworks()) + a, err := ipam.NewAllocator(localAddressPool, ipamutils.GetGlobalScopeDefaultNetworks()) if err != nil { return err } diff --git a/libnetwork/ipams/windowsipam/windowsipam.go b/libnetwork/ipams/windowsipam/windowsipam.go index 24455f71b6..9eaeb63521 100644 --- a/libnetwork/ipams/windowsipam/windowsipam.go +++ b/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" @@ -85,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 }