github.com/cilium/cilium@v1.16.2/operator/cmd/allocator_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package cmd 5 6 import ( 7 "context" 8 "fmt" 9 "net" 10 "sync" 11 "testing" 12 "time" 13 14 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 "k8s.io/apimachinery/pkg/runtime/schema" 16 17 "github.com/cilium/cilium/pkg/ipam/allocator/clusterpool/cidralloc" 18 "github.com/cilium/cilium/pkg/ipam/allocator/podcidr" 19 "github.com/cilium/cilium/pkg/ipam/cidrset" 20 "github.com/cilium/cilium/pkg/ipam/types" 21 cilium_api_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2" 22 k8sClient "github.com/cilium/cilium/pkg/k8s/client" 23 cilium_fake "github.com/cilium/cilium/pkg/k8s/client/clientset/versioned/fake" 24 "github.com/cilium/cilium/pkg/testutils" 25 ) 26 27 // TestPodCIDRAllocatorOverlap tests that, on startup all nodes with assigned podCIDRs are processed so that nodes 28 // without pod CIDRs will not get the same CIDR ranges assigned as existing nodes. 29 func TestPodCIDRAllocatorOverlap(t *testing.T) { 30 // We need to run the test multiple times since we are testing a race condition which is dependant on the order 31 // of a hash map. 32 for i := 0; i < 5; i++ { 33 fmt.Printf("Run %d/5\n", i+1) 34 35 podCIDRAllocatorOverlapTestRun(t) 36 } 37 } 38 39 func podCIDRAllocatorOverlapTestRun(t *testing.T) { 40 var wg sync.WaitGroup 41 defer wg.Wait() 42 43 ctx, cancel := context.WithCancel(context.Background()) 44 defer cancel() 45 46 // Create a new CIDR allocator 47 48 _, cidr, err := net.ParseCIDR("10.129.0.0/16") 49 if err != nil { 50 panic(err) 51 } 52 53 set, err := cidrset.NewCIDRSet(cidr, 24) 54 if err != nil { 55 panic(err) 56 } 57 58 // Create a mock APIServer client where we have 2 existing nodes, one with a PodCIDR and one without. 59 // When List'ed from the client, first node-a is returned then node-b 60 61 fakeClient := cilium_fake.NewSimpleClientset(&cilium_api_v2.CiliumNode{ 62 ObjectMeta: v1.ObjectMeta{ 63 Name: "node-a", 64 }, 65 Spec: cilium_api_v2.NodeSpec{ 66 IPAM: types.IPAMSpec{ 67 PodCIDRs: []string{}, 68 }, 69 }, 70 }, &cilium_api_v2.CiliumNode{ 71 ObjectMeta: v1.ObjectMeta{ 72 Name: "node-b", 73 }, 74 Spec: cilium_api_v2.NodeSpec{ 75 IPAM: types.IPAMSpec{ 76 PodCIDRs: []string{ 77 "10.129.0.0/24", 78 }, 79 }, 80 }, 81 }) 82 83 // Make a set out of the fake cilium client. 84 fakeSet := &k8sClient.FakeClientset{ 85 CiliumFakeClientset: fakeClient, 86 } 87 88 // Create a new pod manager with only our IPv4 allocator and fake client set. 89 podCidrManager := podcidr.NewNodesPodCIDRManager([]cidralloc.CIDRAllocator{ 90 set, 91 }, nil, &ciliumNodeUpdateImplementation{clientset: fakeSet}, nil) 92 93 // start synchronization. 94 cns := newCiliumNodeSynchronizer(fakeSet, podCidrManager, false) 95 if err := cns.Start(ctx, &wg); err != nil { 96 t.Fatal(err) 97 } 98 99 // Wait for the "node manager synced" signal, just like we would normally. 100 <-cns.ciliumNodeManagerQueueSynced 101 102 // Trigger the Resync after the cache sync signal 103 podCidrManager.Resync(ctx, time.Time{}) 104 105 err = testutils.WaitUntil(func() bool { 106 // Get node A from the mock APIServer 107 nodeAInt, err := fakeClient.Tracker().Get(ciliumnodesResource, "", "node-a") 108 if err != nil { 109 return false 110 } 111 nodeA := nodeAInt.(*cilium_api_v2.CiliumNode) 112 113 // Get node B from the mock APIServer 114 nodeBInt, err := fakeClient.Tracker().Get(ciliumnodesResource, "", "node-b") 115 if err != nil { 116 return false 117 } 118 nodeB := nodeBInt.(*cilium_api_v2.CiliumNode) 119 120 if len(nodeA.Spec.IPAM.PodCIDRs) != 1 { 121 return false 122 } 123 124 if len(nodeB.Spec.IPAM.PodCIDRs) != 1 { 125 return false 126 } 127 128 // The PodCIDRs should be distinct. 129 if nodeA.Spec.IPAM.PodCIDRs[0] == nodeB.Spec.IPAM.PodCIDRs[0] { 130 t.Fatal("Node A and Node B are assigned overlapping PodCIDRs") 131 } 132 133 return true 134 }, 2*time.Minute) 135 if err != nil { 136 t.Fatalf("nodes have no pod CIDR: %s", err) 137 } 138 } 139 140 var ciliumnodesResource = schema.GroupVersionResource{Group: "cilium.io", Version: "v2", Resource: "ciliumnodes"} 141 142 type MockObserver struct{} 143 144 // PostRun is called after a trigger run with the call duration, the 145 // latency between 1st queue request and the call run and the number of 146 // queued events folded into the last run 147 func (o *MockObserver) PostRun(callDuration, latency time.Duration, folds int) {} 148 149 // QueueEvent is called when Trigger() is called to schedule a trigger 150 // run 151 func (o *MockObserver) QueueEvent(reason string) {}