From b8c14de74a8e2f1552152a00392a05a401fd03a1 Mon Sep 17 00:00:00 2001 From: Flavio Crisciani Date: Thu, 8 Mar 2018 10:35:27 -0800 Subject: [PATCH 1/2] Vendor golang/sync Signed-off-by: Flavio Crisciani --- libnetwork/vendor.conf | 1 + .../vendor/github.com/golang/sync/LICENSE | 27 ++++ .../vendor/github.com/golang/sync/PATENTS | 22 +++ .../vendor/github.com/golang/sync/README.md | 18 +++ .../golang/sync/semaphore/semaphore.go | 131 ++++++++++++++++++ 5 files changed, 199 insertions(+) create mode 100644 libnetwork/vendor/github.com/golang/sync/LICENSE create mode 100644 libnetwork/vendor/github.com/golang/sync/PATENTS create mode 100644 libnetwork/vendor/github.com/golang/sync/README.md create mode 100644 libnetwork/vendor/github.com/golang/sync/semaphore/semaphore.go diff --git a/libnetwork/vendor.conf b/libnetwork/vendor.conf index 73e2a6495b..ba55d85e6c 100644 --- a/libnetwork/vendor.conf +++ b/libnetwork/vendor.conf @@ -50,5 +50,6 @@ github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25 golang.org/x/crypto 558b6879de74bc843225cde5686419267ff707ca golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6 golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5 +github.com/golang/sync fd80eb99c8f653c847d294a001bdf2a3a6f768f5 github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9 github.com/ishidawataru/sctp 07191f837fedd2f13d1ec7b5f885f0f3ec54b1cb diff --git a/libnetwork/vendor/github.com/golang/sync/LICENSE b/libnetwork/vendor/github.com/golang/sync/LICENSE new file mode 100644 index 0000000000..6a66aea5ea --- /dev/null +++ b/libnetwork/vendor/github.com/golang/sync/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libnetwork/vendor/github.com/golang/sync/PATENTS b/libnetwork/vendor/github.com/golang/sync/PATENTS new file mode 100644 index 0000000000..733099041f --- /dev/null +++ b/libnetwork/vendor/github.com/golang/sync/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/libnetwork/vendor/github.com/golang/sync/README.md b/libnetwork/vendor/github.com/golang/sync/README.md new file mode 100644 index 0000000000..1f8436cc9c --- /dev/null +++ b/libnetwork/vendor/github.com/golang/sync/README.md @@ -0,0 +1,18 @@ +# Go Sync + +This repository provides Go concurrency primitives in addition to the +ones provided by the language and "sync" and "sync/atomic" packages. + +## Download/Install + +The easiest way to install is to run `go get -u golang.org/x/sync`. You can +also manually git clone the repository to `$GOPATH/src/golang.org/x/sync`. + +## Report Issues / Send Patches + +This repository uses Gerrit for code changes. To learn how to submit changes to +this repository, see https://golang.org/doc/contribute.html. + +The main issue tracker for the sync repository is located at +https://github.com/golang/go/issues. Prefix your issue with "x/sync:" in the +subject line, so it is easy to find. diff --git a/libnetwork/vendor/github.com/golang/sync/semaphore/semaphore.go b/libnetwork/vendor/github.com/golang/sync/semaphore/semaphore.go new file mode 100644 index 0000000000..e9d2d79a97 --- /dev/null +++ b/libnetwork/vendor/github.com/golang/sync/semaphore/semaphore.go @@ -0,0 +1,131 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package semaphore provides a weighted semaphore implementation. +package semaphore // import "golang.org/x/sync/semaphore" + +import ( + "container/list" + "sync" + + // Use the old context because packages that depend on this one + // (e.g. cloud.google.com/go/...) must run on Go 1.6. + // TODO(jba): update to "context" when possible. + "golang.org/x/net/context" +) + +type waiter struct { + n int64 + ready chan<- struct{} // Closed when semaphore acquired. +} + +// NewWeighted creates a new weighted semaphore with the given +// maximum combined weight for concurrent access. +func NewWeighted(n int64) *Weighted { + w := &Weighted{size: n} + return w +} + +// Weighted provides a way to bound concurrent access to a resource. +// The callers can request access with a given weight. +type Weighted struct { + size int64 + cur int64 + mu sync.Mutex + waiters list.List +} + +// Acquire acquires the semaphore with a weight of n, blocking only until ctx +// is done. On success, returns nil. On failure, returns ctx.Err() and leaves +// the semaphore unchanged. +// +// If ctx is already done, Acquire may still succeed without blocking. +func (s *Weighted) Acquire(ctx context.Context, n int64) error { + s.mu.Lock() + if s.size-s.cur >= n && s.waiters.Len() == 0 { + s.cur += n + s.mu.Unlock() + return nil + } + + if n > s.size { + // Don't make other Acquire calls block on one that's doomed to fail. + s.mu.Unlock() + <-ctx.Done() + return ctx.Err() + } + + ready := make(chan struct{}) + w := waiter{n: n, ready: ready} + elem := s.waiters.PushBack(w) + s.mu.Unlock() + + select { + case <-ctx.Done(): + err := ctx.Err() + s.mu.Lock() + select { + case <-ready: + // Acquired the semaphore after we were canceled. Rather than trying to + // fix up the queue, just pretend we didn't notice the cancelation. + err = nil + default: + s.waiters.Remove(elem) + } + s.mu.Unlock() + return err + + case <-ready: + return nil + } +} + +// TryAcquire acquires the semaphore with a weight of n without blocking. +// On success, returns true. On failure, returns false and leaves the semaphore unchanged. +func (s *Weighted) TryAcquire(n int64) bool { + s.mu.Lock() + success := s.size-s.cur >= n && s.waiters.Len() == 0 + if success { + s.cur += n + } + s.mu.Unlock() + return success +} + +// Release releases the semaphore with a weight of n. +func (s *Weighted) Release(n int64) { + s.mu.Lock() + s.cur -= n + if s.cur < 0 { + s.mu.Unlock() + panic("semaphore: bad release") + } + for { + next := s.waiters.Front() + if next == nil { + break // No more waiters blocked. + } + + w := next.Value.(waiter) + if s.size-s.cur < w.n { + // Not enough tokens for the next waiter. We could keep going (to try to + // find a waiter with a smaller request), but under load that could cause + // starvation for large requests; instead, we leave all remaining waiters + // blocked. + // + // Consider a semaphore used as a read-write lock, with N tokens, N + // readers, and one writer. Each reader can Acquire(1) to obtain a read + // lock. The writer can Acquire(N) to obtain a write lock, excluding all + // of the readers. If we allow the readers to jump ahead in the queue, + // the writer will starve — there is always one token available for every + // reader. + break + } + + s.cur += w.n + s.waiters.Remove(next) + close(w.ready) + } + s.mu.Unlock() +} From 4c8d7514003eb6d3ebb0c11a37c0880b1cdd404a Mon Sep 17 00:00:00 2001 From: Flavio Crisciani Date: Wed, 7 Mar 2018 11:14:13 -0800 Subject: [PATCH 2/2] IPAM tests Added tests for swarm mode and also some new parallel tests Signed-off-by: Flavio Crisciani --- libnetwork/ipam/allocator_test.go | 1376 +++++++++++++++-------------- libnetwork/ipam/parallel_test.go | 239 +++++ 2 files changed, 932 insertions(+), 683 deletions(-) create mode 100644 libnetwork/ipam/parallel_test.go diff --git a/libnetwork/ipam/allocator_test.go b/libnetwork/ipam/allocator_test.go index b1fc6b69eb..de97461992 100644 --- a/libnetwork/ipam/allocator_test.go +++ b/libnetwork/ipam/allocator_test.go @@ -20,6 +20,7 @@ import ( "github.com/docker/libnetwork/ipamutils" _ "github.com/docker/libnetwork/testutils" "github.com/docker/libnetwork/types" + "github.com/stretchr/testify/assert" ) const ( @@ -170,662 +171,669 @@ func TestPoolDataMarshal(t *testing.T) { } func TestSubnetsMarshal(t *testing.T) { - a, err := getAllocator(true) - 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) - } + 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) - } + cfg, err := a.getAddrSpace(localAddressSpace) + if err != nil { + t.Fatal(err) + } - ba := cfg.Value() - if err := cfg.SetValue(ba); 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, 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) + 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) { - a, err := getAllocator(true) - if err != nil { - t.Fatal(err) - } - a.addrSpaces["abc"] = a.addrSpaces[localAddressSpace] + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + 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") + } - pid, _, _, err := a.RequestPool("abc", "10.0.0.0/8", "", nil, false) - if err != nil { - t.Fatalf("Unexpected failure requesting existing subnet: %v", err) - } - if pid != pid1 { - t.Fatal("returned different pool id for same subnet requests") - } + pid, _, _, err := a.RequestPool("abc", "10.0.0.0/8", "", nil, false) + if err != nil { + t.Fatalf("Unexpected failure requesting existing subnet: %v", err) + } + if pid != pid1 { + t.Fatal("returned different pool id for same subnet requests") + } - _, _, _, 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") + } - pid2, _, _, 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) - } - pid3, _, _, 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 overlapping sub pool: %v", err) - } - if pid2 != pid3 { - t.Fatal("returned different pool id for same sub pool requests") - } + pid2, _, _, 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) + } + pid3, _, _, 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 overlapping sub pool: %v", err) + } + if pid2 != pid3 { + t.Fatal("returned different pool id for same sub pool requests") + } - _, _, _, 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") + } } } func TestAddReleasePoolID(t *testing.T) { - var k0, k1, k2 SubnetKey + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NoError(t, err) - a, err := getAllocator(true) - if err != nil { - t.Fatal(err) - } + var k0, k1, k2 SubnetKey + aSpace, err := a.getAddrSpace(localAddressSpace) + if err != nil { + t.Fatal(err) + } - aSpace, 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) - } + 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) - } + pid2, _, _, 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 pid0 == pid1 || pid0 == pid2 || pid1 != pid2 { + t.Fatalf("Incorrect poolIDs returned %s, %s, %s", pid0, pid1, pid2) + } + if err := k2.FromString(pid2); err != nil { + t.Fatal(err) + } - pid2, _, _, 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 pid0 == pid1 || pid0 == pid2 || pid1 != pid2 { - t.Fatalf("Incorrect poolIDs returned %s, %s, %s", pid0, pid1, pid2) - } - if err := k2.FromString(pid2); 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[k2].RefCount != 2 { + t.Fatalf("Unexpected ref count for %s: %d", k2, subnets[k2].RefCount) + } - subnets = aSpace.subnets - if subnets[k2].RefCount != 2 { - t.Fatalf("Unexpected ref count for %s: %d", k2, subnets[k2].RefCount) - } + if subnets[k0].RefCount != 3 { + t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) + } - if subnets[k0].RefCount != 3 { - 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 != 2 { + 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 != 2 { - t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) - } - if err := a.ReleasePool(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 + 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) - } + 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 != 2 { + t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) + } - subnets = aSpace.subnets - if subnets[k0].RefCount != 2 { - t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) - } + if err := a.ReleasePool(pid2); err != nil { + t.Fatal(err) + } - if err := a.ReleasePool(pid2); 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) + } - 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) { - a, err := getAllocator(true) - if err != nil { - t.Fatal(err) - } + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NoError(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) { - a, err := getAllocator(true) - if err != nil { - t.Fatal(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{}, - } + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NoError(t, err) - 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()) + 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{}, } - } - for ind, id := range poolIDs { - if err := a.ReleasePool(id); err != nil { - t.Fatalf("Failed to release poolID %s (%d)", id, ind) + 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) + } } } } func TestGetSameAddress(t *testing.T) { - 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{}, - } + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NoError(t, err) - pid, _, _, err := a.RequestPool("giallo", "192.168.100.0/24", "", nil, false) - 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{}, + } - ip := net.ParseIP("192.168.100.250") - _, _, err = a.RequestAddress(pid, ip, nil) - if err != nil { - t.Fatal(err) - } + pid, _, _, err := a.RequestPool("giallo", "192.168.100.0/24", "", nil, false) + if err != nil { + t.Fatal(err) + } - _, _, 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) + } } } func TestGetAddressSubPoolEqualPool(t *testing.T) { - a, err := getAllocator(true) - 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) - } + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NoError(t, err) - _, _, err = a.RequestAddress(pid, nil, nil) - 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) + } } } func TestRequestReleaseAddressFromSubPool(t *testing.T) { - a, err := getAllocator(true) - if err != nil { - t.Fatal(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{}, - } + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NoError(t, 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 + 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{}, } - } - 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 + poolID, _, _, err := a.RequestPool("rosso", "172.28.0.0/16", "172.28.30.0/24", nil, false) + if err != nil { + t.Fatal(err) } - } - 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") + 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) + } - 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) - } + _, _, _, 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) + } - uno, _, err := a.RequestAddress(poolID, nil, nil) - if err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(uno, unoExp) { - t.Fatalf("Unexpected address: %v", uno) - } + // 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") - 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 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 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) - } + 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) + 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, 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", - } - a, err := getAllocator(false) - if err != nil { - t.Fatal(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{}, - } + ipamapi.AllocSerialPrefix: "true"} + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NoError(t, 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 + 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{}, } - } - 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 + poolID, _, _, err := a.RequestPool("rosso", "172.28.0.0/16", "172.28.30.0/24", nil, false) + if err != nil { + t.Fatal(err) } - } - 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) - } + 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) + } - uno, _, err := a.RequestAddress(poolID, nil, opts) - if err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(uno, unoExp) { - t.Fatalf("Unexpected address: %v", uno) - } + _, _, _, 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) + } - due, _, err := a.RequestAddress(poolID, nil, opts) - if err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(due, dueExp) { - t.Fatalf("Unexpected address: %v", due) - } + // 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) + } - 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) - } + uno, _, err := a.RequestAddress(poolID, nil, opts) + 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, opts) - if err != nil { - t.Fatal(err) - } - if !types.CompareIPNet(tre, fivExp) { - t.Fatalf("Unexpected address: %v", tre) + 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, 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) + } } } @@ -846,71 +854,71 @@ func TestRequestSyntaxCheck(t *testing.T) { pool = "192.168.0.0/16" subPool = "192.168.0.0/24" as = "green" - err error ) - a, err := getAllocator(true) - if err != nil { - t.Fatal(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{}, - } + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NoError(t, err) - _, _, _, err = a.RequestPool("", pool, "", nil, false) - if err == nil { - t.Fatal("Failed to detect wrong request: empty address space") - } + 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{}, + } - _, _, _, err = a.RequestPool("", pool, subPool, 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(as, "", subPool, nil, false) - if err == nil { - t.Fatal("Failed to detect wrong request: subPool specified and no pool") - } + _, _, _, err = a.RequestPool("", pool, subPool, nil, false) + if err == nil { + t.Fatal("Failed to detect wrong request: empty address space") + } - pid, _, _, err := a.RequestPool(as, pool, subPool, nil, false) - if err != nil { - t.Fatalf("Unexpected failure: %v", err) - } + _, _, _, err = a.RequestPool(as, "", subPool, nil, false) + if err == nil { + t.Fatal("Failed to detect wrong request: subPool specified and no pool") + } - _, _, err = a.RequestAddress("", nil, nil) - if err == nil { - t.Fatal("Failed to detect wrong request: no pool id specified") - } + pid, _, _, err := a.RequestPool(as, pool, subPool, nil, false) + if err != nil { + t.Fatalf("Unexpected failure: %v", err) + } - 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") - } + _, _, err = a.RequestAddress("", nil, nil) + if err == nil { + t.Fatal("Failed to detect wrong request: no pool id specified") + } - 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("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") + } - err = a.ReleaseAddress("", ip) - if err == nil { - t.Fatal("Failed to detect wrong request: no pool id specified") - } + 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(pid, nil) - 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, ip) - if err != nil { - t.Fatalf("Unexpected failure: %v: %s, %s", err, pid, ip) + 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) + } } } @@ -951,65 +959,66 @@ func TestRelease(t *testing.T) { subnet = "192.168.0.0/23" ) - a, err := getAllocator(true) - if err != nil { - t.Fatal(err) - } - pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) - if err != nil { - t.Fatal(err) - } + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NoError(t, err) - bm := a.addresses[SubnetKey{localAddressSpace, subnet, ""}] - - // 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, relase 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) + pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) if err != nil { - t.Fatalf("Failed to obtain the address: %s", err.Error()) + t.Fatal(err) } - ip := nw.IP - if !ip0.Equal(ip) { - t.Fatalf("Failed to obtain the same address. Expected: %s, Got: %s", ip0, ip) + + bm := a.addresses[SubnetKey{localAddressSpace, subnet, ""}] + + // 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, relase 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) + } } } } @@ -1058,26 +1067,27 @@ func assertNRequests(t *testing.T, subnet string, numReq int, lastExpectedIP str ) lastIP := net.ParseIP(lastExpectedIP) - a, err := getAllocator(true) - if err != nil { - t.Fatal(err) - } - pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) - if err != nil { - t.Fatal(err) - } + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NoError(t, 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) - } + pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) + if err != nil { + t.Fatal(err) + } - if !lastIP.Equal(nw.IP) { - t.Fatalf("Wrong last IP. Expected %s. Got: %s (err: %v, ind: %d)", lastExpectedIP, nw.IP.String(), err, i) + 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) + } } } @@ -1111,15 +1121,15 @@ func BenchmarkRequest_8(b *testing.B) { } func TestAllocateRandomDeallocate(t *testing.T) { - testAllocateRandomDeallocate(t, "172.25.0.0/16", "", 384) - testAllocateRandomDeallocate(t, "172.25.0.0/16", "172.25.252.0/22", 384) + for _, store := range []bool{false, true} { + 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) { - ds, err := randomLocalStore(true) - if err != nil { - t.Fatal(err) - } +func testAllocateRandomDeallocate(t *testing.T, pool, subPool string, num int, store bool) { + ds, err := randomLocalStore(store) + assert.NoError(t, err) a, err := NewAllocator(ds, nil) if err != nil { diff --git a/libnetwork/ipam/parallel_test.go b/libnetwork/ipam/parallel_test.go new file mode 100644 index 0000000000..7f9bc94661 --- /dev/null +++ b/libnetwork/ipam/parallel_test.go @@ -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< 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) +}