github.com/cilium/cilium@v1.16.2/pkg/bgpv1/manager/reconciler/pod_cidr_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package reconciler 5 6 import ( 7 "context" 8 "net/netip" 9 "testing" 10 11 meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 "k8s.io/utils/ptr" 13 14 "github.com/cilium/cilium/pkg/bgpv1/manager/instance" 15 "github.com/cilium/cilium/pkg/bgpv1/types" 16 ipamtypes "github.com/cilium/cilium/pkg/ipam/types" 17 v2api "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2" 18 v2alpha1api "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1" 19 "github.com/cilium/cilium/pkg/option" 20 ) 21 22 func TestExportPodCIDRReconciler(t *testing.T) { 23 var table = []struct { 24 // name of the test case 25 name string 26 // whether ExportPodCIDR is enabled at start of test 27 enabled bool 28 // whether ExportPodCIDR should be enabled before reconciliation 29 shouldEnable bool 30 // the advertised PodCIDR blocks the test begins with, these are encoded 31 // into Golang structs for the convenience of passing directly to the 32 // ServerWithConfig.AdvertisePath() method. 33 advertised []netip.Prefix 34 // the updated PodCIDR blocks to reconcile. 35 updated []string 36 // error nil or not 37 err error 38 }{ 39 { 40 name: "disable", 41 enabled: true, 42 shouldEnable: false, 43 advertised: []netip.Prefix{ 44 netip.MustParsePrefix("192.168.0.0/24"), 45 }, 46 }, 47 { 48 name: "enable", 49 enabled: false, 50 shouldEnable: true, 51 updated: []string{"192.168.0.0/24"}, 52 }, 53 { 54 name: "no change", 55 enabled: true, 56 shouldEnable: true, 57 advertised: []netip.Prefix{ 58 netip.MustParsePrefix("192.168.0.0/24"), 59 }, 60 updated: []string{"192.168.0.0/24"}, 61 }, 62 { 63 name: "additional network", 64 enabled: true, 65 shouldEnable: true, 66 advertised: []netip.Prefix{ 67 netip.MustParsePrefix("192.168.0.0/24"), 68 }, 69 updated: []string{"192.168.0.0/24", "192.168.1.0/24"}, 70 }, 71 { 72 name: "removal of both networks", 73 enabled: true, 74 shouldEnable: true, 75 advertised: []netip.Prefix{ 76 netip.MustParsePrefix("192.168.0.0/24"), 77 netip.MustParsePrefix("192.168.1.0/24"), 78 }, 79 updated: []string{}, 80 }, 81 } 82 83 // Dummy daemon config and logger 84 daemonConfig := &option.DaemonConfig{IPAM: "Kubernetes"} 85 86 for _, tt := range table { 87 t.Run(tt.name, func(t *testing.T) { 88 // setup our test server, create a BgpServer, advertise the tt.advertised 89 // networks, and store each returned Advertisement in testSC.PodCIDRAnnouncements 90 srvParams := types.ServerParameters{ 91 Global: types.BGPGlobal{ 92 ASN: 64125, 93 RouterID: "127.0.0.1", 94 ListenPort: -1, 95 }, 96 } 97 oldc := &v2alpha1api.CiliumBGPVirtualRouter{ 98 LocalASN: 64125, 99 ExportPodCIDR: ptr.To[bool](tt.enabled), 100 Neighbors: []v2alpha1api.CiliumBGPNeighbor{}, 101 } 102 testSC, err := instance.NewServerWithConfig(context.Background(), log, srvParams) 103 if err != nil { 104 t.Fatalf("failed to create test bgp server: %v", err) 105 } 106 testSC.Config = oldc 107 reconciler := NewExportPodCIDRReconciler(daemonConfig).Reconciler.(*ExportPodCIDRReconciler) 108 podCIDRAnnouncements := reconciler.getMetadata(testSC) 109 for _, cidr := range tt.advertised { 110 advrtResp, err := testSC.Server.AdvertisePath(context.Background(), types.PathRequest{ 111 Path: types.NewPathForPrefix(cidr), 112 }) 113 if err != nil { 114 t.Fatalf("failed to advertise initial pod cidr routes: %v", err) 115 } 116 podCIDRAnnouncements = append(podCIDRAnnouncements, advrtResp.Path) 117 } 118 reconciler.storeMetadata(testSC, podCIDRAnnouncements) 119 120 newc := &v2alpha1api.CiliumBGPVirtualRouter{ 121 LocalASN: 64125, 122 ExportPodCIDR: ptr.To[bool](tt.shouldEnable), 123 Neighbors: []v2alpha1api.CiliumBGPNeighbor{}, 124 } 125 126 exportPodCIDRReconciler := NewExportPodCIDRReconciler(daemonConfig).Reconciler 127 params := ReconcileParams{ 128 CurrentServer: testSC, 129 DesiredConfig: newc, 130 CiliumNode: &v2api.CiliumNode{ 131 ObjectMeta: meta_v1.ObjectMeta{ 132 Name: "Test Node", 133 }, 134 Spec: v2api.NodeSpec{ 135 IPAM: ipamtypes.IPAMSpec{ 136 PodCIDRs: tt.updated, 137 }, 138 }, 139 }, 140 } 141 142 // Run the reconciler twice to ensure idempotency. This 143 // simulates the retrying behavior of the controller. 144 for i := 0; i < 2; i++ { 145 t.Run(tt.name, func(t *testing.T) { 146 err = exportPodCIDRReconciler.Reconcile(context.Background(), params) 147 if err != nil { 148 t.Fatalf("failed to reconcile new pod cidr advertisements: %v", err) 149 } 150 }) 151 } 152 podCIDRAnnouncements = reconciler.getMetadata(testSC) 153 154 // if we disable exports of pod cidr ensure no advertisements are 155 // still present. 156 if tt.shouldEnable == false { 157 if len(podCIDRAnnouncements) > 0 { 158 t.Fatal("disabled export but advertisements till present") 159 } 160 } 161 162 log.Printf("%+v %+v", podCIDRAnnouncements, tt.updated) 163 164 // ensure we see tt.updated in testSC.PodCIDRAnnoucements 165 for _, cidr := range tt.updated { 166 prefix := netip.MustParsePrefix(cidr) 167 var seen bool 168 for _, advrt := range podCIDRAnnouncements { 169 if advrt.NLRI.String() == prefix.String() { 170 seen = true 171 } 172 } 173 if !seen { 174 t.Fatalf("failed to advertise %v", cidr) 175 } 176 } 177 178 // ensure testSC.PodCIDRAnnouncements does not contain advertisements 179 // not in tt.updated 180 for _, advrt := range podCIDRAnnouncements { 181 var seen bool 182 for _, cidr := range tt.updated { 183 if advrt.NLRI.String() == cidr { 184 seen = true 185 } 186 } 187 if !seen { 188 t.Fatalf("unwanted advert %+v", advrt) 189 } 190 } 191 192 }) 193 } 194 }