/* Copyright 2016 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package ipam import ( "net" "testing" "time" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes/fake" "k8s.io/kubernetes/pkg/controller/nodeipam/ipam/test" "k8s.io/kubernetes/pkg/controller/testutil" "k8s.io/kubernetes/test/utils/ktesting" netutils "k8s.io/utils/net" ) type testCase struct { description string fakeNodeHandler *testutil.FakeNodeHandler allocatorParams CIDRAllocatorParams // key is index of the cidr allocated expectedAllocatedCIDR map[int]string allocatedCIDRs map[int][]string // should controller creation fail? ctrlCreateFail bool } func TestOccupyPreExistingCIDR(t *testing.T) { // all tests operate on a single node testCases := []testCase{ { description: "success, single stack no node allocation", fakeNodeHandler: &testutil.FakeNodeHandler{ Existing: []*v1.Node{ { ObjectMeta: metav1.ObjectMeta{ Name: "node0", }, }, }, Clientset: fake.NewSimpleClientset(), }, allocatorParams: CIDRAllocatorParams{ ClusterCIDRs: func() []*net.IPNet { _, clusterCIDRv4, _ := netutils.ParseCIDRSloppy("10.10.0.0/16") return []*net.IPNet{clusterCIDRv4} }(), ServiceCIDR: nil, SecondaryServiceCIDR: nil, NodeCIDRMaskSizes: []int{24}, }, allocatedCIDRs: nil, expectedAllocatedCIDR: nil, ctrlCreateFail: false, }, { description: "success, dual stack no node allocation", fakeNodeHandler: &testutil.FakeNodeHandler{ Existing: []*v1.Node{ { ObjectMeta: metav1.ObjectMeta{ Name: "node0", }, }, }, Clientset: fake.NewSimpleClientset(), }, allocatorParams: CIDRAllocatorParams{ ClusterCIDRs: func() []*net.IPNet { _, clusterCIDRv4, _ := netutils.ParseCIDRSloppy("10.10.0.0/16") _, clusterCIDRv6, _ := netutils.ParseCIDRSloppy("ace:cab:deca::/8") return []*net.IPNet{clusterCIDRv4, clusterCIDRv6} }(), ServiceCIDR: nil, SecondaryServiceCIDR: nil, NodeCIDRMaskSizes: []int{24, 24}, }, allocatedCIDRs: nil, expectedAllocatedCIDR: nil, ctrlCreateFail: false, }, { description: "success, single stack correct node allocation", fakeNodeHandler: &testutil.FakeNodeHandler{ Existing: []*v1.Node{ { ObjectMeta: metav1.ObjectMeta{ Name: "node0", }, Spec: v1.NodeSpec{ PodCIDRs: []string{"10.10.0.1/24"}, }, }, }, Clientset: fake.NewSimpleClientset(), }, allocatorParams: CIDRAllocatorParams{ ClusterCIDRs: func() []*net.IPNet { _, clusterCIDRv4, _ := netutils.ParseCIDRSloppy("10.10.0.0/16") return []*net.IPNet{clusterCIDRv4} }(), ServiceCIDR: nil, SecondaryServiceCIDR: nil, NodeCIDRMaskSizes: []int{24}, }, allocatedCIDRs: nil, expectedAllocatedCIDR: nil, ctrlCreateFail: false, }, { description: "success, dual stack both allocated correctly", fakeNodeHandler: &testutil.FakeNodeHandler{ Existing: []*v1.Node{ { ObjectMeta: metav1.ObjectMeta{ Name: "node0", }, Spec: v1.NodeSpec{ PodCIDRs: []string{"10.10.0.1/24", "a00::/86"}, }, }, }, Clientset: fake.NewSimpleClientset(), }, allocatorParams: CIDRAllocatorParams{ ClusterCIDRs: func() []*net.IPNet { _, clusterCIDRv4, _ := netutils.ParseCIDRSloppy("10.10.0.0/16") _, clusterCIDRv6, _ := netutils.ParseCIDRSloppy("ace:cab:deca::/8") return []*net.IPNet{clusterCIDRv4, clusterCIDRv6} }(), ServiceCIDR: nil, SecondaryServiceCIDR: nil, NodeCIDRMaskSizes: []int{24, 24}, }, allocatedCIDRs: nil, expectedAllocatedCIDR: nil, ctrlCreateFail: false, }, // failure cases { description: "fail, single stack incorrect node allocation", fakeNodeHandler: &testutil.FakeNodeHandler{ Existing: []*v1.Node{ { ObjectMeta: metav1.ObjectMeta{ Name: "node0", }, Spec: v1.NodeSpec{ PodCIDRs: []string{"172.10.0.1/24"}, }, }, }, Clientset: fake.NewSimpleClientset(), }, allocatorParams: CIDRAllocatorParams{ ClusterCIDRs: func() []*net.IPNet { _, clusterCIDRv4, _ := netutils.ParseCIDRSloppy("10.10.0.0/16") return []*net.IPNet{clusterCIDRv4} }(), ServiceCIDR: nil, SecondaryServiceCIDR: nil, NodeCIDRMaskSizes: []int{24}, }, allocatedCIDRs: nil, expectedAllocatedCIDR: nil, ctrlCreateFail: true, }, { description: "fail, dualstack node allocating from non existing cidr", fakeNodeHandler: &testutil.FakeNodeHandler{ Existing: []*v1.Node{ { ObjectMeta: metav1.ObjectMeta{ Name: "node0", }, Spec: v1.NodeSpec{ PodCIDRs: []string{"10.10.0.1/24", "a00::/86"}, }, }, }, Clientset: fake.NewSimpleClientset(), }, allocatorParams: CIDRAllocatorParams{ ClusterCIDRs: func() []*net.IPNet { _, clusterCIDRv4, _ := netutils.ParseCIDRSloppy("10.10.0.0/16") return []*net.IPNet{clusterCIDRv4} }(), ServiceCIDR: nil, SecondaryServiceCIDR: nil, NodeCIDRMaskSizes: []int{24}, }, allocatedCIDRs: nil, expectedAllocatedCIDR: nil, ctrlCreateFail: true, }, { description: "fail, dualstack node allocating bad v4", fakeNodeHandler: &testutil.FakeNodeHandler{ Existing: []*v1.Node{ { ObjectMeta: metav1.ObjectMeta{ Name: "node0", }, Spec: v1.NodeSpec{ PodCIDRs: []string{"172.10.0.1/24", "a00::/86"}, }, }, }, Clientset: fake.NewSimpleClientset(), }, allocatorParams: CIDRAllocatorParams{ ClusterCIDRs: func() []*net.IPNet { _, clusterCIDRv4, _ := netutils.ParseCIDRSloppy("10.10.0.0/16") _, clusterCIDRv6, _ := netutils.ParseCIDRSloppy("ace:cab:deca::/8") return []*net.IPNet{clusterCIDRv4, clusterCIDRv6} }(), ServiceCIDR: nil, SecondaryServiceCIDR: nil, NodeCIDRMaskSizes: []int{24, 24}, }, allocatedCIDRs: nil, expectedAllocatedCIDR: nil, ctrlCreateFail: true, }, { description: "fail, dualstack node allocating bad v6", fakeNodeHandler: &testutil.FakeNodeHandler{ Existing: []*v1.Node{ { ObjectMeta: metav1.ObjectMeta{ Name: "node0", }, Spec: v1.NodeSpec{ PodCIDRs: []string{"10.10.0.1/24", "cdd::/86"}, }, }, }, Clientset: fake.NewSimpleClientset(), }, allocatorParams: CIDRAllocatorParams{ ClusterCIDRs: func() []*net.IPNet { _, clusterCIDRv4, _ := netutils.ParseCIDRSloppy("10.10.0.0/16") _, clusterCIDRv6, _ := netutils.ParseCIDRSloppy("ace:cab:deca::/8") return []*net.IPNet{clusterCIDRv4, clusterCIDRv6} }(), ServiceCIDR: nil, SecondaryServiceCIDR: nil, NodeCIDRMaskSizes: []int{24, 24}, }, allocatedCIDRs: nil, expectedAllocatedCIDR: nil, ctrlCreateFail: true, }, } // test function tCtx := ktesting.Init(t) for _, tc := range testCases { t.Run(tc.description, func(t *testing.T) { // Initialize the range allocator. fakeNodeInformer := test.FakeNodeInformer(tc.fakeNodeHandler) nodeList, _ := tc.fakeNodeHandler.List(tCtx, metav1.ListOptions{}) _, err := NewCIDRRangeAllocator(tCtx, tc.fakeNodeHandler, fakeNodeInformer, tc.allocatorParams, nodeList) if err == nil && tc.ctrlCreateFail { t.Fatalf("creating range allocator was expected to fail, but it did not") } if err != nil && !tc.ctrlCreateFail { t.Fatalf("creating range allocator was expected to succeed, but it did not") } }) } } func TestAllocateOrOccupyCIDRSuccess(t *testing.T) { // Non-parallel test (overrides global var) oldNodePollInterval := nodePollInterval nodePollInterval = test.NodePollInterval defer func() { nodePollInterval = oldNodePollInterval }() // all tests operate on a single node testCases := []testCase{ { description: "When there's no ServiceCIDR return first CIDR in range", fakeNodeHandler: &testutil.FakeNodeHandler{ Existing: []*v1.Node{ { ObjectMeta: metav1.ObjectMeta{ Name: "node0", }, }, }, Clientset: fake.NewSimpleClientset(), }, allocatorParams: CIDRAllocatorParams{ ClusterCIDRs: func() []*net.IPNet { _, clusterCIDR, _ := netutils.ParseCIDRSloppy("127.123.234.0/24") return []*net.IPNet{clusterCIDR} }(), ServiceCIDR: nil, SecondaryServiceCIDR: nil, NodeCIDRMaskSizes: []int{30}, }, expectedAllocatedCIDR: map[int]string{ 0: "127.123.234.0/30", }, }, { description: "Correctly filter out ServiceCIDR", fakeNodeHandler: &testutil.FakeNodeHandler{ Existing: []*v1.Node{ { ObjectMeta: metav1.ObjectMeta{ Name: "node0", }, }, }, Clientset: fake.NewSimpleClientset(), }, allocatorParams: CIDRAllocatorParams{ ClusterCIDRs: func() []*net.IPNet { _, clusterCIDR, _ := netutils.ParseCIDRSloppy("127.123.234.0/24") return []*net.IPNet{clusterCIDR} }(), ServiceCIDR: func() *net.IPNet { _, serviceCIDR, _ := netutils.ParseCIDRSloppy("127.123.234.0/26") return serviceCIDR }(), SecondaryServiceCIDR: nil, NodeCIDRMaskSizes: []int{30}, }, // it should return first /30 CIDR after service range expectedAllocatedCIDR: map[int]string{ 0: "127.123.234.64/30", }, }, { description: "Correctly ignore already allocated CIDRs", fakeNodeHandler: &testutil.FakeNodeHandler{ Existing: []*v1.Node{ { ObjectMeta: metav1.ObjectMeta{ Name: "node0", }, }, }, Clientset: fake.NewSimpleClientset(), }, allocatorParams: CIDRAllocatorParams{ ClusterCIDRs: func() []*net.IPNet { _, clusterCIDR, _ := netutils.ParseCIDRSloppy("127.123.234.0/24") return []*net.IPNet{clusterCIDR} }(), ServiceCIDR: func() *net.IPNet { _, serviceCIDR, _ := netutils.ParseCIDRSloppy("127.123.234.0/26") return serviceCIDR }(), SecondaryServiceCIDR: nil, NodeCIDRMaskSizes: []int{30}, }, allocatedCIDRs: map[int][]string{ 0: {"127.123.234.64/30", "127.123.234.68/30", "127.123.234.72/30", "127.123.234.80/30"}, }, expectedAllocatedCIDR: map[int]string{ 0: "127.123.234.76/30", }, }, { description: "Dualstack CIDRs v4,v6", fakeNodeHandler: &testutil.FakeNodeHandler{ Existing: []*v1.Node{ { ObjectMeta: metav1.ObjectMeta{ Name: "node0", }, }, }, Clientset: fake.NewSimpleClientset(), }, allocatorParams: CIDRAllocatorParams{ ClusterCIDRs: func() []*net.IPNet { _, clusterCIDRv4, _ := netutils.ParseCIDRSloppy("127.123.234.0/8") _, clusterCIDRv6, _ := netutils.ParseCIDRSloppy("ace:cab:deca::/84") return []*net.IPNet{clusterCIDRv4, clusterCIDRv6} }(), ServiceCIDR: func() *net.IPNet { _, serviceCIDR, _ := netutils.ParseCIDRSloppy("127.123.234.0/26") return serviceCIDR }(), SecondaryServiceCIDR: nil, NodeCIDRMaskSizes: []int{24, 98}, }, }, { description: "Dualstack CIDRs v6,v4", fakeNodeHandler: &testutil.FakeNodeHandler{ Existing: []*v1.Node{ { ObjectMeta: metav1.ObjectMeta{ Name: "node0", }, }, }, Clientset: fake.NewSimpleClientset(), }, allocatorParams: CIDRAllocatorParams{ ClusterCIDRs: func() []*net.IPNet { _, clusterCIDRv4, _ := netutils.ParseCIDRSloppy("127.123.234.0/8") _, clusterCIDRv6, _ := netutils.ParseCIDRSloppy("ace:cab:deca::/84") return []*net.IPNet{clusterCIDRv6, clusterCIDRv4} }(), ServiceCIDR: func() *net.IPNet { _, serviceCIDR, _ := netutils.ParseCIDRSloppy("127.123.234.0/26") return serviceCIDR }(), SecondaryServiceCIDR: nil, NodeCIDRMaskSizes: []int{98, 24}, }, }, { description: "Dualstack CIDRs, more than two", fakeNodeHandler: &testutil.FakeNodeHandler{ Existing: []*v1.Node{ { ObjectMeta: metav1.ObjectMeta{ Name: "node0", }, }, }, Clientset: fake.NewSimpleClientset(), }, allocatorParams: CIDRAllocatorParams{ ClusterCIDRs: func() []*net.IPNet { _, clusterCIDRv4, _ := netutils.ParseCIDRSloppy("127.123.234.0/8") _, clusterCIDRv6, _ := netutils.ParseCIDRSloppy("ace:cab:deca::/84") _, clusterCIDRv4_2, _ := netutils.ParseCIDRSloppy("10.0.0.0/8") return []*net.IPNet{clusterCIDRv4, clusterCIDRv6, clusterCIDRv4_2} }(), ServiceCIDR: func() *net.IPNet { _, serviceCIDR, _ := netutils.ParseCIDRSloppy("127.123.234.0/26") return serviceCIDR }(), SecondaryServiceCIDR: nil, NodeCIDRMaskSizes: []int{24, 98, 24}, }, }, { description: "no double counting", fakeNodeHandler: &testutil.FakeNodeHandler{ Existing: []*v1.Node{ { ObjectMeta: metav1.ObjectMeta{ Name: "node0", }, Spec: v1.NodeSpec{ PodCIDRs: []string{"10.10.0.0/24"}, }, }, { ObjectMeta: metav1.ObjectMeta{ Name: "node1", }, Spec: v1.NodeSpec{ PodCIDRs: []string{"10.10.2.0/24"}, }, }, { ObjectMeta: metav1.ObjectMeta{ Name: "node2", }, }, }, Clientset: fake.NewSimpleClientset(), }, allocatorParams: CIDRAllocatorParams{ ClusterCIDRs: func() []*net.IPNet { _, clusterCIDR, _ := netutils.ParseCIDRSloppy("10.10.0.0/22") return []*net.IPNet{clusterCIDR} }(), ServiceCIDR: nil, SecondaryServiceCIDR: nil, NodeCIDRMaskSizes: []int{24}, }, expectedAllocatedCIDR: map[int]string{ 0: "10.10.1.0/24", }, }, } // test function logger, tCtx := ktesting.NewTestContext(t) testFunc := func(tc testCase) { fakeNodeInformer := test.FakeNodeInformer(tc.fakeNodeHandler) nodeList, _ := tc.fakeNodeHandler.List(tCtx, metav1.ListOptions{}) // Initialize the range allocator. allocator, err := NewCIDRRangeAllocator(tCtx, tc.fakeNodeHandler, fakeNodeInformer, tc.allocatorParams, nodeList) if err != nil { t.Errorf("%v: failed to create CIDRRangeAllocator with error %v", tc.description, err) return } rangeAllocator, ok := allocator.(*rangeAllocator) if !ok { t.Logf("%v: found non-default implementation of CIDRAllocator, skipping white-box test...", tc.description) return } rangeAllocator.nodesSynced = test.AlwaysReady rangeAllocator.recorder = testutil.NewFakeRecorder() go allocator.Run(tCtx) // this is a bit of white box testing // pre allocate the cidrs as per the test for idx, allocatedList := range tc.allocatedCIDRs { for _, allocated := range allocatedList { _, cidr, err := netutils.ParseCIDRSloppy(allocated) if err != nil { t.Fatalf("%v: unexpected error when parsing CIDR %v: %v", tc.description, allocated, err) } if err = rangeAllocator.cidrSets[idx].Occupy(cidr); err != nil { t.Fatalf("%v: unexpected error when occupying CIDR %v: %v", tc.description, allocated, err) } } } updateCount := 0 for _, node := range tc.fakeNodeHandler.Existing { if node.Spec.PodCIDRs == nil { updateCount++ } if err := allocator.AllocateOrOccupyCIDR(logger, node); err != nil { t.Errorf("%v: unexpected error in AllocateOrOccupyCIDR: %v", tc.description, err) } } if updateCount != 1 { t.Fatalf("test error: all tests must update exactly one node") } if err := test.WaitForUpdatedNodeWithTimeout(tc.fakeNodeHandler, updateCount, wait.ForeverTestTimeout); err != nil { t.Fatalf("%v: timeout while waiting for Node update: %v", tc.description, err) } if len(tc.expectedAllocatedCIDR) == 0 { // nothing further expected return } for _, updatedNode := range tc.fakeNodeHandler.GetUpdatedNodesCopy() { if len(updatedNode.Spec.PodCIDRs) == 0 { continue // not assigned yet } //match for podCIDRIdx, expectedPodCIDR := range tc.expectedAllocatedCIDR { if updatedNode.Spec.PodCIDRs[podCIDRIdx] != expectedPodCIDR { t.Errorf("%v: Unable to find allocated CIDR %v, found updated Nodes with CIDRs: %v", tc.description, expectedPodCIDR, updatedNode.Spec.PodCIDRs) break } } } } // run the test cases for _, tc := range testCases { testFunc(tc) } } func TestAllocateOrOccupyCIDRFailure(t *testing.T) { testCases := []testCase{ { description: "When there's no ServiceCIDR return first CIDR in range", fakeNodeHandler: &testutil.FakeNodeHandler{ Existing: []*v1.Node{ { ObjectMeta: metav1.ObjectMeta{ Name: "node0", }, }, }, Clientset: fake.NewSimpleClientset(), }, allocatorParams: CIDRAllocatorParams{ ClusterCIDRs: func() []*net.IPNet { _, clusterCIDR, _ := netutils.ParseCIDRSloppy("127.123.234.0/28") return []*net.IPNet{clusterCIDR} }(), ServiceCIDR: nil, SecondaryServiceCIDR: nil, NodeCIDRMaskSizes: []int{30}, }, allocatedCIDRs: map[int][]string{ 0: {"127.123.234.0/30", "127.123.234.4/30", "127.123.234.8/30", "127.123.234.12/30"}, }, }, } logger, tCtx := ktesting.NewTestContext(t) testFunc := func(tc testCase) { // Initialize the range allocator. allocator, err := NewCIDRRangeAllocator(tCtx, tc.fakeNodeHandler, test.FakeNodeInformer(tc.fakeNodeHandler), tc.allocatorParams, nil) if err != nil { t.Logf("%v: failed to create CIDRRangeAllocator with error %v", tc.description, err) } rangeAllocator, ok := allocator.(*rangeAllocator) if !ok { t.Logf("%v: found non-default implementation of CIDRAllocator, skipping white-box test...", tc.description) return } rangeAllocator.nodesSynced = test.AlwaysReady rangeAllocator.recorder = testutil.NewFakeRecorder() go allocator.Run(tCtx) // this is a bit of white box testing for setIdx, allocatedList := range tc.allocatedCIDRs { for _, allocated := range allocatedList { _, cidr, err := netutils.ParseCIDRSloppy(allocated) if err != nil { t.Fatalf("%v: unexpected error when parsing CIDR %v: %v", tc.description, cidr, err) } err = rangeAllocator.cidrSets[setIdx].Occupy(cidr) if err != nil { t.Fatalf("%v: unexpected error when occupying CIDR %v: %v", tc.description, cidr, err) } } } if err := allocator.AllocateOrOccupyCIDR(logger, tc.fakeNodeHandler.Existing[0]); err == nil { t.Errorf("%v: unexpected success in AllocateOrOccupyCIDR: %v", tc.description, err) } // We don't expect any updates, so just sleep for some time time.Sleep(time.Second) if len(tc.fakeNodeHandler.GetUpdatedNodesCopy()) != 0 { t.Fatalf("%v: unexpected update of nodes: %v", tc.description, tc.fakeNodeHandler.GetUpdatedNodesCopy()) } if len(tc.expectedAllocatedCIDR) == 0 { // nothing further expected return } for _, updatedNode := range tc.fakeNodeHandler.GetUpdatedNodesCopy() { if len(updatedNode.Spec.PodCIDRs) == 0 { continue // not assigned yet } //match for podCIDRIdx, expectedPodCIDR := range tc.expectedAllocatedCIDR { if updatedNode.Spec.PodCIDRs[podCIDRIdx] == expectedPodCIDR { t.Errorf("%v: found cidr %v that should not be allocated on node with CIDRs:%v", tc.description, expectedPodCIDR, updatedNode.Spec.PodCIDRs) break } } } } for _, tc := range testCases { testFunc(tc) } } type releaseTestCase struct { description string fakeNodeHandler *testutil.FakeNodeHandler allocatorParams CIDRAllocatorParams expectedAllocatedCIDRFirstRound map[int]string expectedAllocatedCIDRSecondRound map[int]string allocatedCIDRs map[int][]string cidrsToRelease [][]string } func TestReleaseCIDRSuccess(t *testing.T) { // Non-parallel test (overrides global var) oldNodePollInterval := nodePollInterval nodePollInterval = test.NodePollInterval defer func() { nodePollInterval = oldNodePollInterval }() testCases := []releaseTestCase{ { description: "Correctly release preallocated CIDR", fakeNodeHandler: &testutil.FakeNodeHandler{ Existing: []*v1.Node{ { ObjectMeta: metav1.ObjectMeta{ Name: "node0", }, }, }, Clientset: fake.NewSimpleClientset(), }, allocatorParams: CIDRAllocatorParams{ ClusterCIDRs: func() []*net.IPNet { _, clusterCIDR, _ := netutils.ParseCIDRSloppy("127.123.234.0/28") return []*net.IPNet{clusterCIDR} }(), ServiceCIDR: nil, SecondaryServiceCIDR: nil, NodeCIDRMaskSizes: []int{30}, }, allocatedCIDRs: map[int][]string{ 0: {"127.123.234.0/30", "127.123.234.4/30", "127.123.234.8/30", "127.123.234.12/30"}, }, expectedAllocatedCIDRFirstRound: nil, cidrsToRelease: [][]string{ {"127.123.234.4/30"}, }, expectedAllocatedCIDRSecondRound: map[int]string{ 0: "127.123.234.4/30", }, }, { description: "Correctly recycle CIDR", fakeNodeHandler: &testutil.FakeNodeHandler{ Existing: []*v1.Node{ { ObjectMeta: metav1.ObjectMeta{ Name: "node0", }, }, }, Clientset: fake.NewSimpleClientset(), }, allocatorParams: CIDRAllocatorParams{ ClusterCIDRs: func() []*net.IPNet { _, clusterCIDR, _ := netutils.ParseCIDRSloppy("127.123.234.0/28") return []*net.IPNet{clusterCIDR} }(), ServiceCIDR: nil, SecondaryServiceCIDR: nil, NodeCIDRMaskSizes: []int{30}, }, allocatedCIDRs: map[int][]string{ 0: {"127.123.234.4/30", "127.123.234.8/30", "127.123.234.12/30"}, }, expectedAllocatedCIDRFirstRound: map[int]string{ 0: "127.123.234.0/30", }, cidrsToRelease: [][]string{ {"127.123.234.0/30"}, }, expectedAllocatedCIDRSecondRound: map[int]string{ 0: "127.123.234.0/30", }, }, } logger, tCtx := ktesting.NewTestContext(t) testFunc := func(tc releaseTestCase) { // Initialize the range allocator. allocator, _ := NewCIDRRangeAllocator(tCtx, tc.fakeNodeHandler, test.FakeNodeInformer(tc.fakeNodeHandler), tc.allocatorParams, nil) rangeAllocator, ok := allocator.(*rangeAllocator) if !ok { t.Logf("%v: found non-default implementation of CIDRAllocator, skipping white-box test...", tc.description) return } rangeAllocator.nodesSynced = test.AlwaysReady rangeAllocator.recorder = testutil.NewFakeRecorder() go allocator.Run(tCtx) // this is a bit of white box testing for setIdx, allocatedList := range tc.allocatedCIDRs { for _, allocated := range allocatedList { _, cidr, err := netutils.ParseCIDRSloppy(allocated) if err != nil { t.Fatalf("%v: unexpected error when parsing CIDR %v: %v", tc.description, allocated, err) } err = rangeAllocator.cidrSets[setIdx].Occupy(cidr) if err != nil { t.Fatalf("%v: unexpected error when occupying CIDR %v: %v", tc.description, allocated, err) } } } err := allocator.AllocateOrOccupyCIDR(logger, tc.fakeNodeHandler.Existing[0]) if len(tc.expectedAllocatedCIDRFirstRound) != 0 { if err != nil { t.Fatalf("%v: unexpected error in AllocateOrOccupyCIDR: %v", tc.description, err) } if err := test.WaitForUpdatedNodeWithTimeout(tc.fakeNodeHandler, 1, wait.ForeverTestTimeout); err != nil { t.Fatalf("%v: timeout while waiting for Node update: %v", tc.description, err) } } else { if err == nil { t.Fatalf("%v: unexpected success in AllocateOrOccupyCIDR: %v", tc.description, err) } // We don't expect any updates here time.Sleep(time.Second) if len(tc.fakeNodeHandler.GetUpdatedNodesCopy()) != 0 { t.Fatalf("%v: unexpected update of nodes: %v", tc.description, tc.fakeNodeHandler.GetUpdatedNodesCopy()) } } for _, cidrToRelease := range tc.cidrsToRelease { nodeToRelease := v1.Node{ ObjectMeta: metav1.ObjectMeta{ Name: "node0", }, } nodeToRelease.Spec.PodCIDRs = cidrToRelease err = allocator.ReleaseCIDR(logger, &nodeToRelease) if err != nil { t.Fatalf("%v: unexpected error in ReleaseCIDR: %v", tc.description, err) } } if err = allocator.AllocateOrOccupyCIDR(logger, tc.fakeNodeHandler.Existing[0]); err != nil { t.Fatalf("%v: unexpected error in AllocateOrOccupyCIDR: %v", tc.description, err) } if err := test.WaitForUpdatedNodeWithTimeout(tc.fakeNodeHandler, 1, wait.ForeverTestTimeout); err != nil { t.Fatalf("%v: timeout while waiting for Node update: %v", tc.description, err) } if len(tc.expectedAllocatedCIDRSecondRound) == 0 { // nothing further expected return } for _, updatedNode := range tc.fakeNodeHandler.GetUpdatedNodesCopy() { if len(updatedNode.Spec.PodCIDRs) == 0 { continue // not assigned yet } //match for podCIDRIdx, expectedPodCIDR := range tc.expectedAllocatedCIDRSecondRound { if updatedNode.Spec.PodCIDRs[podCIDRIdx] != expectedPodCIDR { t.Errorf("%v: found cidr %v that should not be allocated on node with CIDRs:%v", tc.description, expectedPodCIDR, updatedNode.Spec.PodCIDRs) break } } } } for _, tc := range testCases { testFunc(tc) } }