libnet/ipam: drop vestiges of custom addrSpaces

Only two address spaces are supported: LocalDefault and GlobalDefault.
Support for non-default address spaces in the IPAM Allocator is
vestigial, from a time when IPAM state was stored in a persistent shared
datastore. There is no way to create non-default address spaces through
the IPAM API so there is no need to retain code to support the use of
such address spaces. Drop all pretense that more address spaces can
exist, to the extent that the IPAM API allows.

Signed-off-by: Cory Snider <csnider@mirantis.com>
This commit is contained in:
Cory Snider 2023-02-06 18:01:47 -05:00
parent 18ac200efe
commit 9a8b45c133
4 changed files with 65 additions and 115 deletions

View file

@ -3,7 +3,6 @@ package ipam
import (
"fmt"
"net"
"sort"
"sync"
"github.com/docker/docker/libnetwork/bitseq"
@ -19,12 +18,8 @@ const (
// Allocator provides per address space ipv4/ipv6 book keeping
type Allocator struct {
// Predefined pools for default address spaces
// Separate from the addrSpace because they should not be serialized
predefined map[string][]*net.IPNet
predefinedStartIndices map[string]int
// The address spaces
addrSpaces map[string]*addrSpace
local, global *addrSpace
// Allocated addresses in each address space's subnet
addresses map[SubnetKey]*bitseq.Handle
sync.Mutex
@ -33,26 +28,20 @@ type Allocator struct {
// NewAllocator returns an instance of libnetwork ipam
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{},
addresses: map[SubnetKey]*bitseq.Handle{},
}
a.addrSpaces = map[string]*addrSpace{
localAddressSpace: a.newAddressSpace(),
globalAddressSpace: a.newAddressSpace(),
}
a.local = newAddrSpace(a, lcAs)
a.global = newAddrSpace(a, glAs)
return a, nil
}
func (a *Allocator) newAddressSpace() *addrSpace {
func newAddrSpace(a *Allocator, predefined []*net.IPNet) *addrSpace {
return &addrSpace{
subnets: map[SubnetKey]*PoolData{},
alloc: a,
subnets: map[SubnetKey]*PoolData{},
alloc: a,
predefined: predefined,
}
}
@ -121,11 +110,13 @@ func (a *Allocator) ReleasePool(poolID string) error {
func (a *Allocator) getAddrSpace(as string) (*addrSpace, error) {
a.Lock()
defer a.Unlock()
aSpace, ok := a.addrSpaces[as]
if !ok {
return nil, types.BadRequestErrorf("cannot find address space %s", as)
switch as {
case localAddressSpace:
return a.local, nil
case globalAddressSpace:
return a.global, nil
}
return aSpace, nil
return nil, types.BadRequestErrorf("cannot find address space %s", as)
}
// parsePoolRequest parses and validates a request to create a new pool under addressSpace and returns
@ -214,27 +205,27 @@ func (a *Allocator) retrieveBitmask(k SubnetKey, n *net.IPNet) (*bitseq.Handle,
return bm, nil
}
func (a *Allocator) getPredefineds(as string) []*net.IPNet {
a.Lock()
defer a.Unlock()
p := a.predefined[as]
i := a.predefinedStartIndices[as]
// getPredefineds returns the predefined subnets for the address space.
//
// It should not be called concurrently with any other method on the addrSpace.
func (aSpace *addrSpace) getPredefineds() []*net.IPNet {
i := aSpace.predefinedStartIndex
// defensive in case the list changed since last update
if i >= len(p) {
if i >= len(aSpace.predefined) {
i = 0
}
return append(p[i:], p[:i]...)
return append(aSpace.predefined[i:], aSpace.predefined[:i]...)
}
func (a *Allocator) updateStartIndex(as string, amt int) {
a.Lock()
i := a.predefinedStartIndices[as] + amt
if i < 0 || i >= len(a.predefined[as]) {
// updatePredefinedStartIndex rotates the predefined subnet list by amt.
//
// It should not be called concurrently with any other method on the addrSpace.
func (aSpace *addrSpace) updatePredefinedStartIndex(amt int) {
i := aSpace.predefinedStartIndex + amt
if i < 0 || i >= len(aSpace.predefined) {
i = 0
}
a.predefinedStartIndices[as] = i
a.Unlock()
aSpace.predefinedStartIndex = i
}
func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error) {
@ -244,19 +235,15 @@ func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error)
v = v6
}
if as != localAddressSpace && as != globalAddressSpace {
aSpace, _ := a.getAddrSpace(as)
if aSpace == nil {
return nil, types.NotImplementedErrorf("no default pool available for non-default address spaces")
}
aSpace, err := a.getAddrSpace(as)
if err != nil {
return nil, err
}
predefined := a.getPredefineds(as)
aSpace.Lock()
for i, nw := range predefined {
defer aSpace.Unlock()
for i, nw := range aSpace.getPredefineds() {
if v != getAddressVersion(nw.IP) {
continue
}
@ -267,12 +254,10 @@ func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error)
// Shouldn't be necessary, but check prevents IP collisions should
// predefined pools overlap for any reason.
if !aSpace.contains(as, nw) {
aSpace.Unlock()
a.updateStartIndex(as, i+1)
aSpace.updatePredefinedStartIndex(i + 1)
return nw, nil
}
}
aSpace.Unlock()
return nil, types.NotFoundErrorf("could not find an available, non-overlapping IPv%d address pool among the defaults to assign to the network", v)
}
@ -426,18 +411,14 @@ func (a *Allocator) getAddress(nw *net.IPNet, bitmask *bitseq.Handle, prefAddres
// DumpDatabase dumps the internal info
func (a *Allocator) DumpDatabase() string {
a.Lock()
aspaces := make(map[string]*addrSpace, len(a.addrSpaces))
orderedAS := make([]string, 0, len(a.addrSpaces))
for as, aSpace := range a.addrSpaces {
orderedAS = append(orderedAS, as)
aspaces[as] = aSpace
aspaces := map[string]*addrSpace{
localAddressSpace: a.local,
globalAddressSpace: a.global,
}
a.Unlock()
sort.Strings(orderedAS)
var s string
for _, as := range orderedAS {
for _, as := range []string{localAddressSpace, globalAddressSpace} {
aSpace := aspaces[as]
s = fmt.Sprintf("\n\n%s Config", as)
aSpace.Lock()

View file

@ -83,14 +83,13 @@ func TestAddSubnets(t *testing.T) {
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")
}
pid1, _, _, err := a.RequestPool("abc", "10.0.0.0/8", "", nil, false)
pid1, _, _, err := a.RequestPool(globalAddressSpace, "10.0.0.0/8", "", nil, false)
if err != nil {
t.Fatalf("Unexpected failure in adding overlapping subnets to different address spaces: %v", err)
}
@ -99,21 +98,21 @@ func TestAddSubnets(t *testing.T) {
t.Fatal("returned same pool id for same subnets in different namespaces")
}
_, _, _, err = a.RequestPool("abc", "10.0.0.0/8", "", nil, false)
_, _, _, err = a.RequestPool(globalAddressSpace, "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)
_, _, _, err = a.RequestPool(globalAddressSpace, "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)
_, _, _, err = a.RequestPool(globalAddressSpace, "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)
_, _, _, err = a.RequestPool(globalAddressSpace, "10.0.0.0/8", "10.128.0.0/9", nil, false)
if err == nil {
t.Fatalf("Expected failure on adding overlapping sub pool")
}
@ -317,11 +316,6 @@ func TestRemoveSubnet(t *testing.T) {
a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
assert.NilError(t, err)
a.addrSpaces["splane"] = &addrSpace{
alloc: a.addrSpaces[localAddressSpace].alloc,
subnets: map[SubnetKey]*PoolData{},
}
input := []struct {
addrSpace string
subnet string
@ -331,10 +325,10 @@ func TestRemoveSubnet(t *testing.T) {
{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},
{globalAddressSpace, "172.17.0.0/16", false},
{globalAddressSpace, "10.0.0.0/8", false},
{globalAddressSpace, "2001:db8:1:2:3:4:5::/112", true},
{globalAddressSpace, "2001:db8:1:2:3:4:ffff::/112", true},
}
poolIDs := make([]string, len(input))
@ -356,12 +350,7 @@ func TestGetSameAddress(t *testing.T) {
a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
assert.NilError(t, err)
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)
pid, _, _, err := a.RequestPool(localAddressSpace, "192.168.100.0/24", "", nil, false)
if err != nil {
t.Fatal(err)
}
@ -435,12 +424,7 @@ func TestRequestReleaseAddressFromSubPool(t *testing.T) {
a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
assert.NilError(t, err)
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)
poolID, _, _, err := a.RequestPool(localAddressSpace, "172.28.0.0/16", "172.28.30.0/24", nil, false)
if err != nil {
t.Fatal(err)
}
@ -470,11 +454,11 @@ func TestRequestReleaseAddressFromSubPool(t *testing.T) {
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)
_, _, _, err = a.RequestPool(localAddressSpace, "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)
poolID, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/16", "10.0.0.0/24", nil, false)
if err != nil {
t.Fatal(err)
}
@ -507,7 +491,7 @@ func TestRequestReleaseAddressFromSubPool(t *testing.T) {
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 {
if poolID, _, _, err = a.RequestPool(localAddressSpace, "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)
@ -563,12 +547,7 @@ func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) {
a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
assert.NilError(t, err)
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)
poolID, _, _, err := a.RequestPool(localAddressSpace, "172.28.0.0/16", "172.28.30.0/24", nil, false)
if err != nil {
t.Fatal(err)
}
@ -598,11 +577,11 @@ func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) {
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)
_, _, _, err = a.RequestPool(localAddressSpace, "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)
poolID, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/16", "10.0.0.0/24", nil, false)
if err != nil {
t.Fatal(err)
}
@ -636,7 +615,7 @@ func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) {
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 {
if poolID, _, _, err = a.RequestPool(localAddressSpace, "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)
@ -702,17 +681,11 @@ func TestRequestSyntaxCheck(t *testing.T) {
var (
pool = "192.168.0.0/16"
subPool = "192.168.0.0/24"
as = "green"
)
a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
assert.NilError(t, err)
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")
@ -723,12 +696,12 @@ func TestRequestSyntaxCheck(t *testing.T) {
t.Fatal("Failed to detect wrong request: empty address space")
}
_, _, _, err = a.RequestPool(as, "", subPool, nil, false)
_, _, _, err = a.RequestPool(localAddressSpace, "", 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)
pid, _, _, err := a.RequestPool(localAddressSpace, pool, subPool, nil, false)
if err != nil {
t.Fatalf("Unexpected failure: %v", err)
}
@ -1234,17 +1207,13 @@ func TestRequestReleaseAddressDuplicate(t *testing.T) {
}
ips := []IP{}
allocatedIPs := []*net.IPNet{}
a.addrSpaces["rosso"] = &addrSpace{
alloc: a.addrSpaces[localAddressSpace].alloc,
subnets: map[SubnetKey]*PoolData{},
}
opts := map[string]string{
ipamapi.AllocSerialPrefix: "true",
}
var l sync.Mutex
poolID, _, _, err := a.RequestPool("rosso", "198.168.0.0/23", "", nil, false)
poolID, _, _, err := a.RequestPool(localAddressSpace, "198.168.0.0/23", "", nil, false)
if err != nil {
t.Fatal(err)
}

View file

@ -41,16 +41,11 @@ func newTestContext(t *testing.T, mask int, options map[string]string) *testCont
if err != nil {
t.Fatal(err)
}
a.addrSpaces["giallo"] = &addrSpace{
alloc: a.addrSpaces[localAddressSpace].alloc,
subnets: map[SubnetKey]*PoolData{},
}
network := fmt.Sprintf("192.168.100.0/%d", mask)
// total ips 2^(32-mask) - 2 (network and broadcast)
totalIps := 1<<uint(32-mask) - 2
pid, _, _, err := a.RequestPool("giallo", network, "", nil, false)
pid, _, _, err := a.RequestPool(localAddressSpace, network, "", nil, false)
if err != nil {
t.Fatal(err)
}

View file

@ -29,6 +29,11 @@ type PoolData struct {
type addrSpace struct {
subnets map[SubnetKey]*PoolData
alloc *Allocator
// Predefined pool for the address space
predefined []*net.IPNet
predefinedStartIndex int
sync.Mutex
}