github.com/cilium/cilium@v1.16.2/pkg/bgpv1/manager/reconciler/preflight_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 "fmt" 9 "testing" 10 11 "github.com/stretchr/testify/require" 12 meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 13 "k8s.io/utils/ptr" 14 15 "github.com/cilium/cilium/pkg/bgpv1/manager/instance" 16 "github.com/cilium/cilium/pkg/bgpv1/manager/store" 17 "github.com/cilium/cilium/pkg/bgpv1/types" 18 "github.com/cilium/cilium/pkg/k8s" 19 v2api "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2" 20 v2alpha1api "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1" 21 slim_corev1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/core/v1" 22 slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1" 23 "github.com/cilium/cilium/pkg/option" 24 ) 25 26 // We use similar local listen ports as the tests in the pkg/bgpv1/test package. 27 // It is important to NOT use ports from the /proc/sys/net/ipv4/ip_local_port_range 28 // (defaulted to 32768-60999 on most Linux distributions) to avoid collisions with 29 // the ephemeral (source) ports. As this range is configurable, ideally, we should 30 // use the IANA-assigned ports below 1024 (e.g. 179) or mock GoBGP in these tests. 31 // See https://github.com/cilium/cilium/issues/26209 for more info. 32 const ( 33 localListenPort = 1793 34 localListenPort2 = 1794 35 ) 36 37 // TestPreflightReconciler ensures if a BgpServer must be recreated, due to 38 // permanent configuration of the said server changing, its done so correctly. 39 func TestPreflightReconciler(t *testing.T) { 40 var table = []struct { 41 // name of test 42 name string 43 // routerID of original server 44 routerID string 45 // routerID to reconcile 46 newRouterID string 47 // local listen port of original server 48 localPort int32 49 // local listen port to reconcile 50 newLocalPort int32 51 // virtual router configuration to reconcile, used mostly for pointer 52 // comparison 53 config *v2alpha1api.CiliumBGPVirtualRouter 54 // should a recreation of the BgpServer 55 shouldRecreate bool 56 // export a nil error or not 57 err error 58 }{ 59 { 60 name: "no change", 61 routerID: "192.168.0.1", 62 newRouterID: "192.168.0.1", 63 localPort: localListenPort, 64 newLocalPort: localListenPort, 65 config: &v2alpha1api.CiliumBGPVirtualRouter{}, 66 shouldRecreate: false, 67 err: nil, 68 }, 69 { 70 name: "router-id change", 71 routerID: "192.168.0.1", 72 newRouterID: "192.168.0.2", 73 localPort: localListenPort, 74 newLocalPort: localListenPort, 75 config: &v2alpha1api.CiliumBGPVirtualRouter{}, 76 shouldRecreate: true, 77 err: nil, 78 }, 79 { 80 name: "local-port change", 81 routerID: "192.168.0.1", 82 newRouterID: "192.168.0.1", 83 localPort: localListenPort, 84 newLocalPort: localListenPort2, 85 config: &v2alpha1api.CiliumBGPVirtualRouter{}, 86 shouldRecreate: true, 87 err: nil, 88 }, 89 { 90 name: "local-port, router-id change", 91 routerID: "192.168.0.1", 92 newRouterID: "192.168.0.2", 93 localPort: localListenPort, 94 newLocalPort: localListenPort2, 95 config: &v2alpha1api.CiliumBGPVirtualRouter{}, 96 shouldRecreate: true, 97 err: nil, 98 }, 99 } 100 for _, tt := range table { 101 t.Run(tt.name, func(t *testing.T) { 102 // our test BgpServer with our original router ID and local port 103 srvParams := types.ServerParameters{ 104 Global: types.BGPGlobal{ 105 ASN: 64125, 106 RouterID: tt.routerID, 107 ListenPort: tt.localPort, 108 }, 109 } 110 testSC, err := instance.NewServerWithConfig(context.Background(), log, srvParams) 111 if err != nil { 112 t.Fatalf("failed to create test BgpServer: %v", err) 113 } 114 115 // keep a pointer to the original server to avoid gc and to check 116 // later 117 originalServer := testSC.Server 118 t.Cleanup(func() { 119 originalServer.Stop() // stop our test server 120 testSC.Server.Stop() // stop any recreated server 121 }) 122 123 // attach original config 124 testSC.Config = tt.config 125 newc := &v2alpha1api.CiliumBGPVirtualRouter{ 126 LocalASN: 64125, 127 } 128 129 preflightReconciler := NewPreflightReconciler().Reconciler 130 params := ReconcileParams{ 131 CurrentServer: testSC, 132 DesiredConfig: newc, 133 CiliumNode: &v2api.CiliumNode{ 134 ObjectMeta: meta_v1.ObjectMeta{ 135 Name: "Test Node", 136 Annotations: map[string]string{ 137 "cilium.io/bgp-virtual-router.64125": fmt.Sprintf("router-id=%s,local-port=%d", tt.newRouterID, tt.newLocalPort), 138 }, 139 }, 140 }, 141 } 142 143 // Run the reconciler twice to ensure idempotency. This 144 // simulates the retrying behavior of the controller. 145 for i := 0; i < 2; i++ { 146 t.Run(tt.name, func(t *testing.T) { 147 err = preflightReconciler.Reconcile(context.Background(), params) 148 if (tt.err == nil) != (err == nil) { 149 t.Fatalf("wanted error: %v", (tt.err == nil)) 150 } 151 }) 152 } 153 if tt.shouldRecreate && testSC.Server == originalServer { 154 t.Fatalf("preflightReconciler did not recreate server") 155 } 156 getBgpResp, err := testSC.Server.GetBGP(context.Background()) 157 if err != nil { 158 t.Fatalf("failed to retrieve BGP Info for BgpServer under test: %v", err) 159 } 160 bgpInfo := getBgpResp.Global 161 if bgpInfo.RouterID != tt.newRouterID { 162 t.Fatalf("got: %v, want: %v", bgpInfo.RouterID, tt.newRouterID) 163 } 164 if bgpInfo.ListenPort != int32(tt.newLocalPort) { 165 t.Fatalf("got: %v, want: %v", bgpInfo.ListenPort, tt.newLocalPort) 166 } 167 }) 168 } 169 } 170 171 // TestReconcileAfterServerReinit reproduces issue #24975, validates service reconcile works after router-id is 172 // modified. 173 func TestReconcileAfterServerReinit(t *testing.T) { 174 var ( 175 routerID = "192.168.0.1" 176 localPort = int32(localListenPort) 177 localASN = int64(64125) 178 newRouterID = "192.168.0.2" 179 diffstore = store.NewFakeDiffStore[*slim_corev1.Service]() 180 epDiffStore = store.NewFakeDiffStore[*k8s.Endpoints]() 181 serviceSelector = &slim_metav1.LabelSelector{MatchLabels: map[string]string{"color": "blue"}} 182 obj = &slim_corev1.Service{ 183 ObjectMeta: slim_metav1.ObjectMeta{ 184 Name: "svc-1", 185 Namespace: "default", 186 Labels: map[string]string{ 187 "color": "blue", 188 }, 189 }, 190 Spec: slim_corev1.ServiceSpec{ 191 Type: slim_corev1.ServiceTypeLoadBalancer, 192 }, 193 Status: slim_corev1.ServiceStatus{ 194 LoadBalancer: slim_corev1.LoadBalancerStatus{ 195 Ingress: []slim_corev1.LoadBalancerIngress{ 196 { 197 IP: "1.2.3.4", 198 }, 199 }, 200 }, 201 }, 202 } 203 ) 204 205 // Initial router configuration 206 srvParams := types.ServerParameters{ 207 Global: types.BGPGlobal{ 208 ASN: 64125, 209 RouterID: "127.0.0.1", 210 ListenPort: -1, 211 }, 212 } 213 214 testSC, err := instance.NewServerWithConfig(context.Background(), log, srvParams) 215 require.NoError(t, err) 216 217 originalServer := testSC.Server 218 t.Cleanup(func() { 219 originalServer.Stop() // stop our test server 220 testSC.Server.Stop() // stop any recreated server 221 }) 222 223 // Validate pod CIDR and service announcements work as expected 224 newc := &v2alpha1api.CiliumBGPVirtualRouter{ 225 LocalASN: localASN, 226 ExportPodCIDR: ptr.To[bool](true), 227 Neighbors: []v2alpha1api.CiliumBGPNeighbor{}, 228 ServiceSelector: serviceSelector, 229 } 230 231 daemonConfig := &option.DaemonConfig{IPAM: "Kubernetes"} 232 exportPodCIDRReconciler := NewExportPodCIDRReconciler(daemonConfig).Reconciler 233 params := ReconcileParams{ 234 CurrentServer: testSC, 235 DesiredConfig: newc, 236 CiliumNode: &v2api.CiliumNode{ 237 ObjectMeta: meta_v1.ObjectMeta{ 238 Name: "Test Node", 239 Annotations: map[string]string{ 240 "cilium.io/bgp-virtual-router.64125": fmt.Sprintf("router-id=%s,local-port=%d", routerID, localPort), 241 }, 242 }, 243 }, 244 } 245 246 err = exportPodCIDRReconciler.Reconcile(context.Background(), params) 247 require.NoError(t, err) 248 249 diffstore.Upsert(obj) 250 reconciler := NewServiceReconciler(diffstore, epDiffStore) 251 err = reconciler.Reconciler.Reconcile(context.Background(), params) 252 require.NoError(t, err) 253 254 // update server config, this is done outside of reconcilers 255 testSC.Config = newc 256 257 params.CiliumNode.Annotations = map[string]string{ 258 "cilium.io/bgp-virtual-router.64125": fmt.Sprintf("router-id=%s,local-port=%d", newRouterID, localPort), 259 } 260 261 preflightReconciler := NewPreflightReconciler().Reconciler 262 263 // Trigger pre flight reconciler 264 err = preflightReconciler.Reconcile(context.Background(), params) 265 require.NoError(t, err) 266 267 // Test pod CIDR reconciler is working 268 err = exportPodCIDRReconciler.Reconcile(context.Background(), params) 269 require.NoError(t, err) 270 271 // Update LB service 272 reconciler = NewServiceReconciler(diffstore, epDiffStore) 273 err = reconciler.Reconciler.Reconcile(context.Background(), params) 274 require.NoError(t, err) 275 }