github.com/cilium/cilium@v1.16.2/pkg/bgpv1/manager/reconcilerv2/pod_ip_pool_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package reconcilerv2 5 6 import ( 7 "context" 8 "net/netip" 9 "testing" 10 11 "github.com/sirupsen/logrus" 12 "github.com/stretchr/testify/require" 13 metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 "k8s.io/utils/ptr" 15 16 "github.com/cilium/cilium/pkg/bgpv1/manager/instance" 17 "github.com/cilium/cilium/pkg/bgpv1/manager/store" 18 "github.com/cilium/cilium/pkg/bgpv1/types" 19 ipamtypes "github.com/cilium/cilium/pkg/ipam/types" 20 v2api "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2" 21 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1" 22 "github.com/cilium/cilium/pkg/k8s/resource" 23 slimv1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1" 24 ) 25 26 var ( 27 podIPPoolTestLogger = logrus.WithField("unit_test", "reconcilerv2_podippool") 28 ) 29 30 var ( 31 redPoolCIDRv4 = v2alpha1.PoolCIDR("10.0.0.0/16") 32 redPoolCIDRv6 = v2alpha1.PoolCIDR("2001:db8::/64") 33 redPoolNodePrefix1v4 = ipamtypes.IPAMPodCIDR("10.0.1.0/24") 34 redPoolNodePrefix2v4 = ipamtypes.IPAMPodCIDR("10.0.2.0/24") 35 redPoolNodePrefix1v6 = ipamtypes.IPAMPodCIDR("2001:db8:0:0:1234::/96") 36 redPoolNodePrefix2v6 = ipamtypes.IPAMPodCIDR("2001:db8:0:0:1235::/96") 37 38 redPoolName = "red-pool" 39 redLabelSelector = slimv1.LabelSelector{MatchLabels: map[string]string{"pool": "red"}} 40 redNameNSSelector = slimv1.LabelSelector{MatchLabels: map[string]string{ 41 podIPPoolNameLabel: redPoolName, 42 }} 43 redPool = &v2alpha1.CiliumPodIPPool{ 44 ObjectMeta: metaV1.ObjectMeta{ 45 Name: redPoolName, 46 Labels: redLabelSelector.MatchLabels, 47 }, 48 Spec: v2alpha1.IPPoolSpec{ 49 IPv4: &v2alpha1.IPv4PoolSpec{ 50 CIDRs: []v2alpha1.PoolCIDR{redPoolCIDRv4}, 51 MaskSize: 24, 52 }, 53 IPv6: &v2alpha1.IPv6PoolSpec{ 54 CIDRs: []v2alpha1.PoolCIDR{redPoolCIDRv6}, 55 MaskSize: 96, 56 }, 57 }, 58 } 59 redPeer65001v4PodIPPoolRPName = PolicyName("red-peer-65001", "ipv4", v2alpha1.BGPCiliumPodIPPoolAdvert, redPoolName) 60 redPeer65001v4PodIPPoolRP = &types.RoutePolicy{ 61 Name: redPeer65001v4PodIPPoolRPName, 62 Type: types.RoutePolicyTypeExport, 63 Statements: []*types.RoutePolicyStatement{ 64 { 65 Conditions: types.RoutePolicyConditions{ 66 MatchNeighbors: []string{"10.10.10.1/32"}, 67 MatchPrefixes: []*types.RoutePolicyPrefixMatch{ 68 { 69 CIDR: netip.MustParsePrefix(string(redPoolNodePrefix1v4)), 70 PrefixLenMin: 24, 71 PrefixLenMax: 24, 72 }, 73 { 74 CIDR: netip.MustParsePrefix(string(redPoolNodePrefix2v4)), 75 PrefixLenMin: 24, 76 PrefixLenMax: 24, 77 }, 78 }, 79 }, 80 Actions: types.RoutePolicyActions{ 81 RouteAction: types.RoutePolicyActionAccept, 82 AddCommunities: []string{"65000:200"}, 83 }, 84 }, 85 }, 86 } 87 redPeer65001v6PodIPPoolRPName = PolicyName("red-peer-65001", "ipv6", v2alpha1.BGPCiliumPodIPPoolAdvert, redPoolName) 88 redPeer65001v6PodIPPoolRP = &types.RoutePolicy{ 89 Name: redPeer65001v6PodIPPoolRPName, 90 Type: types.RoutePolicyTypeExport, 91 Statements: []*types.RoutePolicyStatement{ 92 { 93 Conditions: types.RoutePolicyConditions{ 94 MatchNeighbors: []string{"10.10.10.1/32"}, 95 MatchPrefixes: []*types.RoutePolicyPrefixMatch{ 96 { 97 CIDR: netip.MustParsePrefix(string(redPoolNodePrefix1v6)), 98 PrefixLenMin: 96, 99 PrefixLenMax: 96, 100 }, 101 { 102 CIDR: netip.MustParsePrefix(string(redPoolNodePrefix2v6)), 103 PrefixLenMin: 96, 104 PrefixLenMax: 96, 105 }, 106 }, 107 }, 108 Actions: types.RoutePolicyActions{ 109 RouteAction: types.RoutePolicyActionAccept, 110 AddCommunities: []string{"65000:200"}, 111 }, 112 }, 113 }, 114 } 115 116 bluePoolCIDR1v4 = v2alpha1.PoolCIDR("10.1.0.0/16") 117 bluePoolNodePrefix1v4 = ipamtypes.IPAMPodCIDR("10.1.1.0/24") 118 bluePoolCIDR2v4 = v2alpha1.PoolCIDR("10.2.0.0/16") 119 bluePoolNodePrefix2v4 = ipamtypes.IPAMPodCIDR("10.2.1.0/24") 120 bluePoolCIDR3v4 = v2alpha1.PoolCIDR("10.3.0.0/16") 121 bluePoolCIDR1v6 = v2alpha1.PoolCIDR("2001:db8:1::/64") 122 bluePoolNodePrefix1v6 = ipamtypes.IPAMPodCIDR("2001:db8:1:0:1234::/96") 123 bluePoolCIDR2v6 = v2alpha1.PoolCIDR("2001:db8:2::/64") 124 bluePoolNodePrefix2v6 = ipamtypes.IPAMPodCIDR("2001:db8:2:0:1234::/96") 125 bluePoolCIDR3v6 = v2alpha1.PoolCIDR("2001:db8:3::/64") 126 127 bluePoolName = "blue-pool" 128 blueLabelSelector = slimv1.LabelSelector{MatchLabels: map[string]string{"pool": "blue"}} 129 blueNameNSSelector = slimv1.LabelSelector{MatchLabels: map[string]string{ 130 podIPPoolNameLabel: bluePoolName, 131 }} 132 bluePool = &v2alpha1.CiliumPodIPPool{ 133 ObjectMeta: metaV1.ObjectMeta{ 134 Name: bluePoolName, 135 Labels: blueLabelSelector.MatchLabels, 136 }, 137 Spec: v2alpha1.IPPoolSpec{ 138 IPv4: &v2alpha1.IPv4PoolSpec{ 139 CIDRs: []v2alpha1.PoolCIDR{ 140 bluePoolCIDR1v4, 141 bluePoolCIDR2v4, 142 bluePoolCIDR3v4, 143 }, 144 MaskSize: 24, 145 }, 146 IPv6: &v2alpha1.IPv6PoolSpec{ 147 CIDRs: []v2alpha1.PoolCIDR{ 148 bluePoolCIDR1v6, 149 bluePoolCIDR2v6, 150 bluePoolCIDR3v6, 151 }, 152 MaskSize: 96, 153 }, 154 }, 155 } 156 bluePeer65001v4PodIPPoolRPName = PolicyName("blue-peer-65001", "ipv4", v2alpha1.BGPCiliumPodIPPoolAdvert, bluePoolName) 157 bluePeer65001v4PodIPPoolRP = &types.RoutePolicy{ 158 Name: bluePeer65001v4PodIPPoolRPName, 159 Type: types.RoutePolicyTypeExport, 160 Statements: []*types.RoutePolicyStatement{ 161 { 162 Conditions: types.RoutePolicyConditions{ 163 MatchNeighbors: []string{"10.10.10.2/32"}, 164 MatchPrefixes: []*types.RoutePolicyPrefixMatch{ 165 { 166 CIDR: netip.MustParsePrefix(string(bluePoolNodePrefix1v4)), 167 PrefixLenMin: 24, 168 PrefixLenMax: 24, 169 }, 170 { 171 CIDR: netip.MustParsePrefix(string(bluePoolNodePrefix2v4)), 172 PrefixLenMin: 24, 173 PrefixLenMax: 24, 174 }, 175 }, 176 }, 177 Actions: types.RoutePolicyActions{ 178 RouteAction: types.RoutePolicyActionAccept, 179 AddCommunities: []string{"65355:200"}, 180 }, 181 }, 182 }, 183 } 184 bluePeer65001v6PodIPPoolRPName = PolicyName("blue-peer-65001", "ipv6", v2alpha1.BGPCiliumPodIPPoolAdvert, bluePoolName) 185 bluePeer65001v6PodIPPoolRP = &types.RoutePolicy{ 186 Name: bluePeer65001v6PodIPPoolRPName, 187 Type: types.RoutePolicyTypeExport, 188 Statements: []*types.RoutePolicyStatement{ 189 { 190 Conditions: types.RoutePolicyConditions{ 191 MatchNeighbors: []string{"10.10.10.2/32"}, 192 MatchPrefixes: []*types.RoutePolicyPrefixMatch{ 193 { 194 CIDR: netip.MustParsePrefix(string(bluePoolNodePrefix1v6)), 195 PrefixLenMin: 96, 196 PrefixLenMax: 96, 197 }, 198 { 199 CIDR: netip.MustParsePrefix(string(bluePoolNodePrefix2v6)), 200 PrefixLenMin: 96, 201 PrefixLenMax: 96, 202 }, 203 }, 204 }, 205 Actions: types.RoutePolicyActions{ 206 RouteAction: types.RoutePolicyActionAccept, 207 AddCommunities: []string{"65355:200"}, 208 }, 209 }, 210 }, 211 } 212 ) 213 214 func Test_PodIPPoolAdvertisements(t *testing.T) { 215 logrus.SetLevel(logrus.DebugLevel) 216 217 tests := []struct { 218 name string 219 peerConfig []*v2alpha1.CiliumBGPPeerConfig 220 advertisements []*v2alpha1.CiliumBGPAdvertisement 221 pools []*v2alpha1.CiliumPodIPPool 222 preconfiguredPoolAFPaths map[resource.Key]map[types.Family]map[string]struct{} 223 preconfiguredRPs ResourceRoutePolicyMap 224 testCiliumNode *v2api.CiliumNode 225 testBGPInstanceConfig *v2alpha1.CiliumBGPNodeInstance 226 expectedPoolAFPaths map[resource.Key]map[types.Family]map[string]struct{} 227 expectedRPs ResourceRoutePolicyMap 228 }{ 229 { 230 name: "dual stack, advertisement selects pools (by label), pool present on the node", 231 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{ 232 redPeerConfig, 233 bluePeerConfig, 234 }, 235 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 236 redAdvertWithSelector(&redLabelSelector), 237 blueAdvertWithSelector(&blueLabelSelector), 238 }, 239 pools: []*v2alpha1.CiliumPodIPPool{ 240 redPool, 241 bluePool, 242 }, 243 preconfiguredPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{}, 244 preconfiguredRPs: ResourceRoutePolicyMap{}, 245 testCiliumNode: &v2api.CiliumNode{ 246 ObjectMeta: metaV1.ObjectMeta{ 247 Name: "Test Node", 248 }, 249 Spec: v2api.NodeSpec{ 250 IPAM: ipamtypes.IPAMSpec{ 251 Pools: ipamtypes.IPAMPoolSpec{ 252 Allocated: []ipamtypes.IPAMPoolAllocation{ 253 { 254 Pool: redPoolName, 255 CIDRs: []ipamtypes.IPAMPodCIDR{ 256 redPoolNodePrefix1v4, 257 redPoolNodePrefix2v4, 258 redPoolNodePrefix1v6, 259 redPoolNodePrefix2v6, 260 }, 261 }, 262 { 263 Pool: bluePoolName, 264 CIDRs: []ipamtypes.IPAMPodCIDR{ 265 bluePoolNodePrefix1v4, 266 bluePoolNodePrefix2v4, 267 bluePoolNodePrefix1v6, 268 bluePoolNodePrefix2v6, 269 }, 270 }, 271 }, 272 }, 273 }, 274 }, 275 }, 276 277 testBGPInstanceConfig: &v2alpha1.CiliumBGPNodeInstance{ 278 Name: "bgp-65001", 279 LocalASN: ptr.To[int64](65001), 280 Peers: []v2alpha1.CiliumBGPNodePeer{redPeer65001, bluePeer65001}, 281 }, 282 expectedPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{ 283 {Name: redPoolName}: { 284 {Afi: types.AfiIPv4, Safi: types.SafiUnicast}: { 285 string(redPoolNodePrefix1v4): struct{}{}, 286 string(redPoolNodePrefix2v4): struct{}{}, 287 }, 288 {Afi: types.AfiIPv6, Safi: types.SafiUnicast}: { 289 string(redPoolNodePrefix1v6): struct{}{}, 290 string(redPoolNodePrefix2v6): struct{}{}, 291 }, 292 }, 293 {Name: bluePoolName}: { 294 {Afi: types.AfiIPv4, Safi: types.SafiUnicast}: { 295 string(bluePoolNodePrefix1v4): struct{}{}, 296 string(bluePoolNodePrefix2v4): struct{}{}, 297 }, 298 {Afi: types.AfiIPv6, Safi: types.SafiUnicast}: { 299 string(bluePoolNodePrefix1v6): struct{}{}, 300 string(bluePoolNodePrefix2v6): struct{}{}, 301 }, 302 }, 303 }, 304 expectedRPs: ResourceRoutePolicyMap{ 305 resource.Key{Name: redPoolName}: RoutePolicyMap{ 306 redPeer65001v4PodIPPoolRPName: redPeer65001v4PodIPPoolRP, 307 redPeer65001v6PodIPPoolRPName: redPeer65001v6PodIPPoolRP, 308 }, 309 resource.Key{Name: bluePoolName}: RoutePolicyMap{ 310 bluePeer65001v4PodIPPoolRPName: bluePeer65001v4PodIPPoolRP, 311 bluePeer65001v6PodIPPoolRPName: bluePeer65001v6PodIPPoolRP, 312 }, 313 }, 314 }, 315 { 316 name: "dual stack, advertisement selects pools (by nameNS selector), pool present on the node", 317 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{ 318 redPeerConfig, 319 bluePeerConfig, 320 }, 321 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 322 redAdvertWithSelector(&redNameNSSelector), 323 blueAdvertWithSelector(&blueNameNSSelector), 324 }, 325 pools: []*v2alpha1.CiliumPodIPPool{ 326 redPool, 327 bluePool, 328 }, 329 preconfiguredPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{}, 330 preconfiguredRPs: ResourceRoutePolicyMap{}, 331 testCiliumNode: &v2api.CiliumNode{ 332 ObjectMeta: metaV1.ObjectMeta{ 333 Name: "Test Node", 334 }, 335 Spec: v2api.NodeSpec{ 336 IPAM: ipamtypes.IPAMSpec{ 337 Pools: ipamtypes.IPAMPoolSpec{ 338 Allocated: []ipamtypes.IPAMPoolAllocation{ 339 { 340 Pool: redPoolName, 341 CIDRs: []ipamtypes.IPAMPodCIDR{ 342 redPoolNodePrefix1v4, 343 redPoolNodePrefix2v4, 344 redPoolNodePrefix1v6, 345 redPoolNodePrefix2v6, 346 }, 347 }, 348 { 349 Pool: bluePoolName, 350 CIDRs: []ipamtypes.IPAMPodCIDR{ 351 bluePoolNodePrefix1v4, 352 bluePoolNodePrefix2v4, 353 bluePoolNodePrefix1v6, 354 bluePoolNodePrefix2v6, 355 }, 356 }, 357 }, 358 }, 359 }, 360 }, 361 }, 362 363 testBGPInstanceConfig: &v2alpha1.CiliumBGPNodeInstance{ 364 Name: "bgp-65001", 365 LocalASN: ptr.To[int64](65001), 366 Peers: []v2alpha1.CiliumBGPNodePeer{redPeer65001, bluePeer65001}, 367 }, 368 expectedPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{ 369 {Name: redPoolName}: { 370 {Afi: types.AfiIPv4, Safi: types.SafiUnicast}: { 371 string(redPoolNodePrefix1v4): struct{}{}, 372 string(redPoolNodePrefix2v4): struct{}{}, 373 }, 374 {Afi: types.AfiIPv6, Safi: types.SafiUnicast}: { 375 string(redPoolNodePrefix1v6): struct{}{}, 376 string(redPoolNodePrefix2v6): struct{}{}, 377 }, 378 }, 379 {Name: bluePoolName}: { 380 {Afi: types.AfiIPv4, Safi: types.SafiUnicast}: { 381 string(bluePoolNodePrefix1v4): struct{}{}, 382 string(bluePoolNodePrefix2v4): struct{}{}, 383 }, 384 {Afi: types.AfiIPv6, Safi: types.SafiUnicast}: { 385 string(bluePoolNodePrefix1v6): struct{}{}, 386 string(bluePoolNodePrefix2v6): struct{}{}, 387 }, 388 }, 389 }, 390 expectedRPs: ResourceRoutePolicyMap{ 391 resource.Key{Name: redPoolName}: RoutePolicyMap{ 392 redPeer65001v4PodIPPoolRPName: redPeer65001v4PodIPPoolRP, 393 redPeer65001v6PodIPPoolRPName: redPeer65001v6PodIPPoolRP, 394 }, 395 resource.Key{Name: bluePoolName}: RoutePolicyMap{ 396 bluePeer65001v4PodIPPoolRPName: bluePeer65001v4PodIPPoolRP, 397 bluePeer65001v6PodIPPoolRPName: bluePeer65001v6PodIPPoolRP, 398 }, 399 }, 400 }, 401 { 402 name: "dual stack, pool NOT selected by advertisement, pool present on the node", 403 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{ 404 redPeerConfig, 405 bluePeerConfig, 406 }, 407 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 408 redAdvert, // no selector matching red pool 409 blueAdvert, // no selector matching blue pool 410 }, 411 pools: []*v2alpha1.CiliumPodIPPool{ 412 redPool, 413 bluePool, 414 }, 415 preconfiguredPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{}, 416 testCiliumNode: &v2api.CiliumNode{ 417 ObjectMeta: metaV1.ObjectMeta{ 418 Name: "Test Node", 419 }, 420 Spec: v2api.NodeSpec{ 421 IPAM: ipamtypes.IPAMSpec{ 422 Pools: ipamtypes.IPAMPoolSpec{ 423 Allocated: []ipamtypes.IPAMPoolAllocation{ 424 { 425 Pool: redPoolName, 426 CIDRs: []ipamtypes.IPAMPodCIDR{ 427 redPoolNodePrefix1v4, 428 redPoolNodePrefix2v4, 429 redPoolNodePrefix1v6, 430 redPoolNodePrefix2v6, 431 }, 432 }, 433 { 434 Pool: bluePoolName, 435 CIDRs: []ipamtypes.IPAMPodCIDR{ 436 bluePoolNodePrefix1v4, 437 bluePoolNodePrefix2v4, 438 bluePoolNodePrefix1v6, 439 bluePoolNodePrefix2v6, 440 }, 441 }, 442 }, 443 }, 444 }, 445 }, 446 }, 447 testBGPInstanceConfig: &v2alpha1.CiliumBGPNodeInstance{ 448 Name: "bgp-65001", 449 LocalASN: ptr.To[int64](65001), 450 Peers: []v2alpha1.CiliumBGPNodePeer{redPeer65001, bluePeer65001}, 451 }, 452 expectedPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{}, 453 expectedRPs: nil, 454 }, 455 { 456 name: "dual stack, pool selected by advertisement, pool NOT present on the node", 457 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{ 458 redPeerConfig, 459 bluePeerConfig, 460 }, 461 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 462 redAdvertWithSelector(&redLabelSelector), 463 blueAdvertWithSelector(&blueLabelSelector), 464 }, 465 pools: []*v2alpha1.CiliumPodIPPool{ 466 redPool, 467 bluePool, 468 }, 469 preconfiguredPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{}, 470 testCiliumNode: &v2api.CiliumNode{ 471 ObjectMeta: metaV1.ObjectMeta{ 472 Name: "Test Node", 473 }, 474 Spec: v2api.NodeSpec{ 475 IPAM: ipamtypes.IPAMSpec{ 476 Pools: ipamtypes.IPAMPoolSpec{ 477 Allocated: []ipamtypes.IPAMPoolAllocation{ 478 { 479 Pool: redPoolName, 480 CIDRs: []ipamtypes.IPAMPodCIDR{}, 481 }, 482 { 483 Pool: bluePoolName, 484 CIDRs: []ipamtypes.IPAMPodCIDR{}, 485 }, 486 }, 487 }, 488 }, 489 }, 490 }, 491 492 testBGPInstanceConfig: &v2alpha1.CiliumBGPNodeInstance{ 493 Name: "bgp-65001", 494 LocalASN: ptr.To[int64](65001), 495 Peers: []v2alpha1.CiliumBGPNodePeer{redPeer65001, bluePeer65001}, 496 }, 497 expectedPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{}, 498 expectedRPs: nil, 499 }, 500 { 501 name: "dual stack, clean up of preconfigured advertisements", 502 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{ 503 redPeerConfig, 504 bluePeerConfig, 505 }, 506 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 507 redAdvertWithSelector(&redLabelSelector), 508 }, 509 pools: []*v2alpha1.CiliumPodIPPool{ 510 redPool, 511 }, 512 preconfiguredPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{ 513 {Name: "unknown"}: { 514 {Afi: types.AfiIPv4, Safi: types.SafiUnicast}: { 515 "10.10.1.0/24": struct{}{}, 516 "10.10.2.0/24": struct{}{}, 517 }, 518 {Afi: types.AfiIPv6, Safi: types.SafiUnicast}: { 519 "2001:db8:100:0:1234::/96": struct{}{}, 520 "2001:db8:101:0:1234::/96": struct{}{}, 521 }, 522 }, 523 }, 524 preconfiguredRPs: ResourceRoutePolicyMap{ 525 resource.Key{Name: redPoolName}: RoutePolicyMap{ 526 redPeer65001v4PodIPPoolRPName: redPeer65001v4PodIPPoolRP, 527 redPeer65001v6PodIPPoolRPName: redPeer65001v6PodIPPoolRP, 528 }, 529 resource.Key{Name: bluePoolName}: RoutePolicyMap{ 530 bluePeer65001v4PodIPPoolRPName: bluePeer65001v4PodIPPoolRP, 531 bluePeer65001v6PodIPPoolRPName: bluePeer65001v6PodIPPoolRP, 532 }, 533 }, 534 testCiliumNode: &v2api.CiliumNode{ 535 ObjectMeta: metaV1.ObjectMeta{ 536 Name: "Test Node", 537 }, 538 Spec: v2api.NodeSpec{ 539 IPAM: ipamtypes.IPAMSpec{ 540 Pools: ipamtypes.IPAMPoolSpec{ 541 Allocated: []ipamtypes.IPAMPoolAllocation{ 542 { 543 Pool: redPoolName, 544 CIDRs: []ipamtypes.IPAMPodCIDR{ 545 redPoolNodePrefix1v4, 546 redPoolNodePrefix2v4, 547 redPoolNodePrefix1v6, 548 redPoolNodePrefix2v6, 549 }, 550 }, 551 }, 552 }, 553 }, 554 }, 555 }, 556 557 testBGPInstanceConfig: &v2alpha1.CiliumBGPNodeInstance{ 558 Name: "bgp-65001", 559 LocalASN: ptr.To[int64](65001), 560 Peers: []v2alpha1.CiliumBGPNodePeer{redPeer65001, bluePeer65001}, 561 }, 562 expectedPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{ 563 {Name: redPoolName}: { 564 {Afi: types.AfiIPv4, Safi: types.SafiUnicast}: { 565 string(redPoolNodePrefix1v4): struct{}{}, 566 string(redPoolNodePrefix2v4): struct{}{}, 567 }, 568 {Afi: types.AfiIPv6, Safi: types.SafiUnicast}: { 569 string(redPoolNodePrefix1v6): struct{}{}, 570 string(redPoolNodePrefix2v6): struct{}{}, 571 }, 572 }, 573 }, 574 expectedRPs: ResourceRoutePolicyMap{ 575 resource.Key{Name: redPoolName}: RoutePolicyMap{ 576 redPeer65001v4PodIPPoolRPName: redPeer65001v4PodIPPoolRP, 577 redPeer65001v6PodIPPoolRPName: redPeer65001v6PodIPPoolRP, 578 }, 579 }, 580 }, 581 } 582 583 for _, tt := range tests { 584 t.Run(tt.name, func(t *testing.T) { 585 req := require.New(t) 586 587 params := PodIPPoolReconcilerIn{ 588 Logger: podIPPoolTestLogger, 589 PeerAdvert: NewCiliumPeerAdvertisement( 590 PeerAdvertisementIn{ 591 Logger: podCIDRTestLogger, 592 PeerConfigStore: store.InitMockStore[*v2alpha1.CiliumBGPPeerConfig](tt.peerConfig), 593 AdvertStore: store.InitMockStore[*v2alpha1.CiliumBGPAdvertisement](tt.advertisements), 594 }), 595 PoolStore: store.InitMockStore[*v2alpha1.CiliumPodIPPool](tt.pools), 596 } 597 podIPPoolReconciler := NewPodIPPoolReconciler(params).Reconciler.(*PodIPPoolReconciler) 598 599 testBGPInstance := instance.NewFakeBGPInstance() 600 601 // set the preconfigured advertisements 602 presetPoolAFPaths := make(ResourceAFPathsMap) 603 for pool, prePoolAFPaths := range tt.preconfiguredPoolAFPaths { 604 presetPoolAFPaths[pool] = make(AFPathsMap) 605 for fam, afPaths := range prePoolAFPaths { 606 pathSet := make(PathMap) 607 for prePath := range afPaths { 608 path := types.NewPathForPrefix(netip.MustParsePrefix(prePath)) 609 path.Family = fam 610 pathSet[prePath] = path 611 } 612 presetPoolAFPaths[pool][fam] = pathSet 613 } 614 } 615 podIPPoolReconciler.setMetadata(testBGPInstance, PodIPPoolReconcilerMetadata{ 616 PoolAFPaths: presetPoolAFPaths, 617 PoolRoutePolicies: tt.preconfiguredRPs, 618 }) 619 620 // run podIPPoolReconciler twice to ensure idempotency 621 for i := 0; i < 2; i++ { 622 err := podIPPoolReconciler.Reconcile(context.Background(), ReconcileParams{ 623 BGPInstance: testBGPInstance, 624 DesiredConfig: tt.testBGPInstanceConfig, 625 CiliumNode: tt.testCiliumNode, 626 }) 627 req.NoError(err) 628 } 629 630 // check if the advertisements are as expected 631 runningPoolAFPaths := make(map[resource.Key]map[types.Family]map[string]struct{}) 632 for pool, poolAFPaths := range podIPPoolReconciler.getMetadata(testBGPInstance).PoolAFPaths { 633 runningPoolAFPaths[pool] = make(map[types.Family]map[string]struct{}) 634 for fam, afPaths := range poolAFPaths { 635 pathSet := make(map[string]struct{}) 636 for pathKey := range afPaths { 637 pathSet[pathKey] = struct{}{} 638 } 639 runningPoolAFPaths[pool][fam] = pathSet 640 } 641 } 642 643 req.Equal(tt.expectedPoolAFPaths, runningPoolAFPaths) 644 req.Equal(tt.expectedRPs, podIPPoolReconciler.getMetadata(testBGPInstance).PoolRoutePolicies) 645 }) 646 } 647 }