1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

IPAM tests

Added tests for swarm mode and also some new parallel tests

Signed-off-by: Flavio Crisciani <flavio.crisciani@docker.com>
This commit is contained in:
Flavio Crisciani 2018-03-07 11:14:13 -08:00
parent b8c14de74a
commit 4c8d751400
2 changed files with 932 additions and 683 deletions

View file

@ -20,6 +20,7 @@ import (
"github.com/docker/libnetwork/ipamutils" "github.com/docker/libnetwork/ipamutils"
_ "github.com/docker/libnetwork/testutils" _ "github.com/docker/libnetwork/testutils"
"github.com/docker/libnetwork/types" "github.com/docker/libnetwork/types"
"github.com/stretchr/testify/assert"
) )
const ( const (
@ -170,7 +171,8 @@ func TestPoolDataMarshal(t *testing.T) {
} }
func TestSubnetsMarshal(t *testing.T) { func TestSubnetsMarshal(t *testing.T) {
a, err := getAllocator(true) for _, store := range []bool{false, true} {
a, err := getAllocator(store)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -215,9 +217,11 @@ func TestSubnetsMarshal(t *testing.T) {
t.Fatalf("Got unexpected ip after pool config restore: %s", ip) t.Fatalf("Got unexpected ip after pool config restore: %s", ip)
} }
} }
}
func TestAddSubnets(t *testing.T) { func TestAddSubnets(t *testing.T) {
a, err := getAllocator(true) for _, store := range []bool{false, true} {
a, err := getAllocator(store)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -282,15 +286,14 @@ func TestAddSubnets(t *testing.T) {
t.Fatal("Failed to detect overlapping v6 subnet") t.Fatal("Failed to detect overlapping v6 subnet")
} }
} }
func TestAddReleasePoolID(t *testing.T) {
var k0, k1, k2 SubnetKey
a, err := getAllocator(true)
if err != nil {
t.Fatal(err)
} }
func TestAddReleasePoolID(t *testing.T) {
for _, store := range []bool{false, true} {
a, err := getAllocator(store)
assert.NoError(t, err)
var k0, k1, k2 SubnetKey
aSpace, err := a.getAddrSpace(localAddressSpace) aSpace, err := a.getAddrSpace(localAddressSpace)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -446,12 +449,12 @@ func TestAddReleasePoolID(t *testing.T) {
t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount)
} }
} }
}
func TestPredefinedPool(t *testing.T) { func TestPredefinedPool(t *testing.T) {
a, err := getAllocator(true) for _, store := range []bool{false, true} {
if err != nil { a, err := getAllocator(store)
t.Fatal(err) assert.NoError(t, err)
}
if _, err := a.getPredefinedPool("blue", false); err == nil { if _, err := a.getPredefinedPool("blue", false); err == nil {
t.Fatal("Expected failure for non default addr space") t.Fatal("Expected failure for non default addr space")
@ -474,12 +477,13 @@ func TestPredefinedPool(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
} }
}
func TestRemoveSubnet(t *testing.T) { func TestRemoveSubnet(t *testing.T) {
a, err := getAllocator(true) for _, store := range []bool{false, true} {
if err != nil { a, err := getAllocator(store)
t.Fatal(err) assert.NoError(t, err)
}
a.addrSpaces["splane"] = &addrSpace{ a.addrSpaces["splane"] = &addrSpace{
id: dsConfigKey + "/" + "splane", id: dsConfigKey + "/" + "splane",
ds: a.addrSpaces[localAddressSpace].ds, ds: a.addrSpaces[localAddressSpace].ds,
@ -517,12 +521,13 @@ func TestRemoveSubnet(t *testing.T) {
} }
} }
} }
}
func TestGetSameAddress(t *testing.T) { func TestGetSameAddress(t *testing.T) {
a, err := getAllocator(true) for _, store := range []bool{false, true} {
if err != nil { a, err := getAllocator(store)
t.Fatal(err) assert.NoError(t, err)
}
a.addrSpaces["giallo"] = &addrSpace{ a.addrSpaces["giallo"] = &addrSpace{
id: dsConfigKey + "/" + "giallo", id: dsConfigKey + "/" + "giallo",
ds: a.addrSpaces[localAddressSpace].ds, ds: a.addrSpaces[localAddressSpace].ds,
@ -547,12 +552,13 @@ func TestGetSameAddress(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
} }
}
func TestGetAddressSubPoolEqualPool(t *testing.T) { func TestGetAddressSubPoolEqualPool(t *testing.T) {
a, err := getAllocator(true) for _, store := range []bool{false, true} {
if err != nil { a, err := getAllocator(store)
t.Fatal(err) assert.NoError(t, err)
}
// Requesting a subpool of same size of the master pool should not cause any problem on ip allocation // 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) pid, _, _, err := a.RequestPool(localAddressSpace, "172.18.0.0/16", "172.18.0.0/16", nil, false)
if err != nil { if err != nil {
@ -564,12 +570,13 @@ func TestGetAddressSubPoolEqualPool(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
} }
}
func TestRequestReleaseAddressFromSubPool(t *testing.T) { func TestRequestReleaseAddressFromSubPool(t *testing.T) {
a, err := getAllocator(true) for _, store := range []bool{false, true} {
if err != nil { a, err := getAllocator(store)
t.Fatal(err) assert.NoError(t, err)
}
a.addrSpaces["rosso"] = &addrSpace{ a.addrSpaces["rosso"] = &addrSpace{
id: dsConfigKey + "/" + "rosso", id: dsConfigKey + "/" + "rosso",
ds: a.addrSpaces[localAddressSpace].ds, ds: a.addrSpaces[localAddressSpace].ds,
@ -694,15 +701,15 @@ func TestRequestReleaseAddressFromSubPool(t *testing.T) {
t.Fatalf("Unexpected address: %v", tre) t.Fatalf("Unexpected address: %v", tre)
} }
} }
}
func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) { func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) {
opts := map[string]string{ opts := map[string]string{
ipamapi.AllocSerialPrefix: "true", ipamapi.AllocSerialPrefix: "true"}
} for _, store := range []bool{false, true} {
a, err := getAllocator(false) a, err := getAllocator(store)
if err != nil { assert.NoError(t, err)
t.Fatal(err)
}
a.addrSpaces["rosso"] = &addrSpace{ a.addrSpaces["rosso"] = &addrSpace{
id: dsConfigKey + "/" + "rosso", id: dsConfigKey + "/" + "rosso",
ds: a.addrSpaces[localAddressSpace].ds, ds: a.addrSpaces[localAddressSpace].ds,
@ -828,6 +835,7 @@ func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) {
t.Fatalf("Unexpected address: %v", tre) t.Fatalf("Unexpected address: %v", tre)
} }
} }
}
func TestGetAddress(t *testing.T) { func TestGetAddress(t *testing.T) {
input := []string{ input := []string{
@ -846,13 +854,12 @@ func TestRequestSyntaxCheck(t *testing.T) {
pool = "192.168.0.0/16" pool = "192.168.0.0/16"
subPool = "192.168.0.0/24" subPool = "192.168.0.0/24"
as = "green" as = "green"
err error
) )
a, err := getAllocator(true) for _, store := range []bool{false, true} {
if err != nil { a, err := getAllocator(store)
t.Fatal(err) assert.NoError(t, err)
}
a.addrSpaces[as] = &addrSpace{ a.addrSpaces[as] = &addrSpace{
id: dsConfigKey + "/" + as, id: dsConfigKey + "/" + as,
ds: a.addrSpaces[localAddressSpace].ds, ds: a.addrSpaces[localAddressSpace].ds,
@ -913,6 +920,7 @@ func TestRequestSyntaxCheck(t *testing.T) {
t.Fatalf("Unexpected failure: %v: %s, %s", err, pid, ip) t.Fatalf("Unexpected failure: %v: %s, %s", err, pid, ip)
} }
} }
}
func TestRequest(t *testing.T) { func TestRequest(t *testing.T) {
// Request N addresses from different size subnets, verifying last request // Request N addresses from different size subnets, verifying last request
@ -951,10 +959,10 @@ func TestRelease(t *testing.T) {
subnet = "192.168.0.0/23" subnet = "192.168.0.0/23"
) )
a, err := getAllocator(true) for _, store := range []bool{false, true} {
if err != nil { a, err := getAllocator(store)
t.Fatal(err) assert.NoError(t, err)
}
pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -1013,6 +1021,7 @@ func TestRelease(t *testing.T) {
} }
} }
} }
}
func assertGetAddress(t *testing.T, subnet string) { func assertGetAddress(t *testing.T, subnet string) {
var ( var (
@ -1058,10 +1067,10 @@ func assertNRequests(t *testing.T, subnet string, numReq int, lastExpectedIP str
) )
lastIP := net.ParseIP(lastExpectedIP) lastIP := net.ParseIP(lastExpectedIP)
a, err := getAllocator(true) for _, store := range []bool{false, true} {
if err != nil { a, err := getAllocator(store)
t.Fatal(err) assert.NoError(t, err)
}
pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -1080,6 +1089,7 @@ func assertNRequests(t *testing.T, subnet string, numReq int, lastExpectedIP str
t.Fatalf("Wrong last IP. Expected %s. Got: %s (err: %v, ind: %d)", lastExpectedIP, nw.IP.String(), err, i) t.Fatalf("Wrong last IP. Expected %s. Got: %s (err: %v, ind: %d)", lastExpectedIP, nw.IP.String(), err, i)
} }
} }
}
func benchmarkRequest(b *testing.B, a *Allocator, subnet string) { func benchmarkRequest(b *testing.B, a *Allocator, subnet string) {
pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false)
@ -1111,15 +1121,15 @@ func BenchmarkRequest_8(b *testing.B) {
} }
func TestAllocateRandomDeallocate(t *testing.T) { func TestAllocateRandomDeallocate(t *testing.T) {
testAllocateRandomDeallocate(t, "172.25.0.0/16", "", 384) for _, store := range []bool{false, true} {
testAllocateRandomDeallocate(t, "172.25.0.0/16", "172.25.252.0/22", 384) testAllocateRandomDeallocate(t, "172.25.0.0/16", "", 384, store)
testAllocateRandomDeallocate(t, "172.25.0.0/16", "172.25.252.0/22", 384, store)
}
} }
func testAllocateRandomDeallocate(t *testing.T, pool, subPool string, num int) { func testAllocateRandomDeallocate(t *testing.T, pool, subPool string, num int, store bool) {
ds, err := randomLocalStore(true) ds, err := randomLocalStore(store)
if err != nil { assert.NoError(t, err)
t.Fatal(err)
}
a, err := NewAllocator(ds, nil) a, err := NewAllocator(ds, nil)
if err != nil { if err != nil {

View file

@ -0,0 +1,239 @@
package ipam
import (
"context"
"fmt"
"net"
"sync"
"testing"
"github.com/golang/sync/semaphore"
"github.com/docker/libnetwork/ipamapi"
"github.com/stretchr/testify/assert"
)
const (
all = iota
even
odd
)
type releaseMode uint
type testContext struct {
a *Allocator
opts map[string]string
ipList []*net.IPNet
ipMap map[string]bool
pid string
maxIP int
}
func newTestContext(t *testing.T, mask int, options map[string]string) *testContext {
a, err := getAllocator(true)
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{},
}
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)
if err != nil {
t.Fatal(err)
}
return &testContext{
a: a,
opts: options,
ipList: make([]*net.IPNet, 0, totalIps),
ipMap: make(map[string]bool),
pid: pid,
maxIP: totalIps,
}
}
func TestDebug(t *testing.T) {
tctx := newTestContext(t, 23, map[string]string{ipamapi.AllocSerialPrefix: "true"})
tctx.a.RequestAddress(tctx.pid, nil, map[string]string{ipamapi.AllocSerialPrefix: "true"})
tctx.a.RequestAddress(tctx.pid, nil, map[string]string{ipamapi.AllocSerialPrefix: "true"})
}
func TestFullAllocateRelease(t *testing.T) {
for _, parallelism := range []int64{2, 4, 8} {
for _, mask := range []int{29, 25, 24, 21} {
tctx := newTestContext(t, mask, map[string]string{ipamapi.AllocSerialPrefix: "true"})
allocate(t, tctx, parallelism)
release(t, tctx, all, parallelism)
}
}
}
func TestOddAllocateRelease(t *testing.T) {
for _, parallelism := range []int64{2, 4, 8} {
for _, mask := range []int{29, 25, 24, 21} {
tctx := newTestContext(t, mask, map[string]string{ipamapi.AllocSerialPrefix: "true"})
allocate(t, tctx, parallelism)
release(t, tctx, odd, parallelism)
}
}
}
func TestFullAllocateSerialReleaseParallel(t *testing.T) {
for _, parallelism := range []int64{1, 4, 8} {
tctx := newTestContext(t, 23, map[string]string{ipamapi.AllocSerialPrefix: "true"})
allocate(t, tctx, 1)
release(t, tctx, all, parallelism)
}
}
func TestOddAllocateSerialReleaseParallel(t *testing.T) {
for _, parallelism := range []int64{1, 4, 8} {
tctx := newTestContext(t, 23, map[string]string{ipamapi.AllocSerialPrefix: "true"})
allocate(t, tctx, 1)
release(t, tctx, odd, parallelism)
}
}
func TestEvenAllocateSerialReleaseParallel(t *testing.T) {
for _, parallelism := range []int64{1, 4, 8} {
tctx := newTestContext(t, 23, map[string]string{ipamapi.AllocSerialPrefix: "true"})
allocate(t, tctx, 1)
release(t, tctx, even, parallelism)
}
}
func allocate(t *testing.T, tctx *testContext, parallel int64) {
// Allocate the whole space
parallelExec := semaphore.NewWeighted(parallel)
routineNum := tctx.maxIP + 10
ch := make(chan *net.IPNet, routineNum)
var id int
var wg sync.WaitGroup
// routine loop
for {
wg.Add(1)
go func(id int) {
parallelExec.Acquire(context.Background(), 1)
ip, _, _ := tctx.a.RequestAddress(tctx.pid, nil, tctx.opts)
ch <- ip
parallelExec.Release(1)
wg.Done()
}(id)
id++
if id == routineNum {
break
}
}
// give time to all the go routines to finish
wg.Wait()
// process results
for i := 0; i < routineNum; i++ {
ip := <-ch
if ip == nil {
continue
}
if there, ok := tctx.ipMap[ip.String()]; ok && there {
t.Fatalf("Got duplicate IP %s", ip.String())
break
}
tctx.ipList = append(tctx.ipList, ip)
tctx.ipMap[ip.String()] = true
}
assert.Len(t, tctx.ipList, tctx.maxIP)
if len(tctx.ipList) != tctx.maxIP {
t.Fatal("missmatch number allocation")
}
}
func release(t *testing.T, tctx *testContext, mode releaseMode, parallel int64) {
var startIndex, increment, stopIndex, length int
switch mode {
case all:
startIndex = 0
increment = 1
stopIndex = tctx.maxIP - 1
length = tctx.maxIP
case odd, even:
if mode == odd {
startIndex = 1
}
increment = 2
stopIndex = tctx.maxIP - 1
length = tctx.maxIP / 2
if tctx.maxIP%2 > 0 {
length++
}
default:
t.Fatal("unsupported mode yet")
}
ipIndex := make([]int, 0, length)
// calculate the index to release from the ipList
for i := startIndex; ; i += increment {
ipIndex = append(ipIndex, i)
if i+increment > stopIndex {
break
}
}
var id int
parallelExec := semaphore.NewWeighted(parallel)
ch := make(chan *net.IPNet, len(ipIndex))
wg := sync.WaitGroup{}
for index := range ipIndex {
wg.Add(1)
go func(id, index int) {
parallelExec.Acquire(context.Background(), 1)
// logrus.Errorf("index %v", index)
// logrus.Errorf("list %v", tctx.ipList)
err := tctx.a.ReleaseAddress(tctx.pid, tctx.ipList[index].IP)
if err != nil {
t.Fatalf("routine %d got %v", id, err)
}
ch <- tctx.ipList[index]
parallelExec.Release(1)
wg.Done()
}(id, index)
id++
}
wg.Wait()
for i := 0; i < len(ipIndex); i++ {
ip := <-ch
// check if it is really free
_, _, err := tctx.a.RequestAddress(tctx.pid, ip.IP, nil)
assert.NoError(t, err, "ip %v not properly released", ip)
if err != nil {
t.Fatalf("ip %v not properly released, error:%v", ip, err)
}
err = tctx.a.ReleaseAddress(tctx.pid, ip.IP)
assert.NoError(t, err)
if there, ok := tctx.ipMap[ip.String()]; !ok || !there {
t.Fatalf("ip %v got double deallocated", ip)
}
tctx.ipMap[ip.String()] = false
for j, v := range tctx.ipList {
if v == ip {
tctx.ipList = append(tctx.ipList[:j], tctx.ipList[j+1:]...)
break
}
}
}
assert.Len(t, tctx.ipList, tctx.maxIP-length)
}