github.com/cilium/cilium@v1.16.2/pkg/bgpv1/manager/reconcilerv2/service_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 15 "k8s.io/utils/ptr" 16 17 "github.com/cilium/cilium/pkg/bgpv1/manager/instance" 18 "github.com/cilium/cilium/pkg/bgpv1/manager/store" 19 "github.com/cilium/cilium/pkg/bgpv1/types" 20 cmtypes "github.com/cilium/cilium/pkg/clustermesh/types" 21 "github.com/cilium/cilium/pkg/k8s" 22 v2api "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2" 23 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1" 24 "github.com/cilium/cilium/pkg/k8s/resource" 25 slim_corev1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/core/v1" 26 slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1" 27 ) 28 29 var ( 30 serviceReconcilerTestLogger = logrus.WithField("unit_test", "reconcilerv2_service") 31 ) 32 33 var ( 34 redSvcKey = resource.Key{Name: "red-svc", Namespace: "non-default"} 35 redSvcSelector = &slim_metav1.LabelSelector{MatchLabels: map[string]string{"color": "red"}} 36 mismatchSvcSelector = &slim_metav1.LabelSelector{MatchLabels: map[string]string{"color": "blue"}} 37 ingressV4 = "192.168.0.1" 38 ingressV4Prefix = "192.168.0.1/32" 39 externalV4 = "192.168.0.2" 40 externalV4Prefix = "192.168.0.2/32" 41 clusterV4 = "192.168.0.3" 42 clusterV4Prefix = "192.168.0.3/32" 43 ingressV6 = "2001:db8::1" 44 ingressV6Prefix = "2001:db8::1/128" 45 externalV6 = "2001:db8::2" 46 externalV6Prefix = "2001:db8::2/128" 47 clusterV6 = "2001:db8::3" 48 clusterV6Prefix = "2001:db8::3/128" 49 50 redLBSvc = &slim_corev1.Service{ 51 ObjectMeta: slim_metav1.ObjectMeta{ 52 Name: redSvcKey.Name, 53 Namespace: redSvcKey.Namespace, 54 Labels: redSvcSelector.MatchLabels, 55 }, 56 Spec: slim_corev1.ServiceSpec{ 57 Type: slim_corev1.ServiceTypeLoadBalancer, 58 }, 59 Status: slim_corev1.ServiceStatus{ 60 LoadBalancer: slim_corev1.LoadBalancerStatus{ 61 Ingress: []slim_corev1.LoadBalancerIngress{ 62 { 63 IP: ingressV4, 64 }, 65 { 66 IP: ingressV6, 67 }, 68 }, 69 }, 70 }, 71 } 72 redLBSvcWithETP = func(eTP slim_corev1.ServiceExternalTrafficPolicy) *slim_corev1.Service { 73 cp := redLBSvc.DeepCopy() 74 cp.Spec.ExternalTrafficPolicy = eTP 75 return cp 76 } 77 78 redPeer65001v4LBRPName = PolicyName("red-peer-65001", "ipv4", v2alpha1.BGPServiceAdvert, "red-svc-non-default-LoadBalancerIP") 79 redPeer65001v4LBRP = &types.RoutePolicy{ 80 Name: redPeer65001v4LBRPName, 81 Type: types.RoutePolicyTypeExport, 82 Statements: []*types.RoutePolicyStatement{ 83 { 84 Conditions: types.RoutePolicyConditions{ 85 MatchNeighbors: []string{"10.10.10.1/32"}, 86 MatchPrefixes: []*types.RoutePolicyPrefixMatch{ 87 { 88 CIDR: netip.MustParsePrefix(ingressV4Prefix), 89 PrefixLenMin: 32, 90 PrefixLenMax: 32, 91 }, 92 }, 93 }, 94 Actions: types.RoutePolicyActions{ 95 RouteAction: types.RoutePolicyActionAccept, 96 AddCommunities: []string{"65535:65281"}, 97 }, 98 }, 99 }, 100 } 101 102 redPeer65001v6LBRPName = PolicyName("red-peer-65001", "ipv6", v2alpha1.BGPServiceAdvert, "red-svc-non-default-LoadBalancerIP") 103 redPeer65001v6LBRP = &types.RoutePolicy{ 104 Name: redPeer65001v6LBRPName, 105 Type: types.RoutePolicyTypeExport, 106 Statements: []*types.RoutePolicyStatement{ 107 { 108 Conditions: types.RoutePolicyConditions{ 109 MatchNeighbors: []string{"10.10.10.1/32"}, 110 MatchPrefixes: []*types.RoutePolicyPrefixMatch{ 111 { 112 CIDR: netip.MustParsePrefix(ingressV6Prefix), 113 PrefixLenMin: 128, 114 PrefixLenMax: 128, 115 }, 116 }, 117 }, 118 Actions: types.RoutePolicyActions{ 119 RouteAction: types.RoutePolicyActionAccept, 120 AddCommunities: []string{"65535:65281"}, 121 }, 122 }, 123 }, 124 } 125 126 redExternalSvc = &slim_corev1.Service{ 127 ObjectMeta: slim_metav1.ObjectMeta{ 128 Name: redSvcKey.Name, 129 Namespace: redSvcKey.Namespace, 130 Labels: redSvcSelector.MatchLabels, 131 }, 132 Spec: slim_corev1.ServiceSpec{ 133 Type: slim_corev1.ServiceTypeClusterIP, 134 ExternalIPs: []string{ 135 externalV4, 136 externalV6, 137 }, 138 }, 139 } 140 141 redExternalSvcWithETP = func(eTP slim_corev1.ServiceExternalTrafficPolicy) *slim_corev1.Service { 142 cp := redExternalSvc.DeepCopy() 143 cp.Spec.ExternalTrafficPolicy = eTP 144 return cp 145 } 146 147 redPeer65001v4ExtRPName = PolicyName("red-peer-65001", "ipv4", v2alpha1.BGPServiceAdvert, "red-svc-non-default-ExternalIP") 148 redPeer65001v4ExtRP = &types.RoutePolicy{ 149 Name: redPeer65001v4ExtRPName, 150 Type: types.RoutePolicyTypeExport, 151 Statements: []*types.RoutePolicyStatement{ 152 { 153 Conditions: types.RoutePolicyConditions{ 154 MatchNeighbors: []string{"10.10.10.1/32"}, 155 MatchPrefixes: []*types.RoutePolicyPrefixMatch{ 156 { 157 CIDR: netip.MustParsePrefix(externalV4Prefix), 158 PrefixLenMin: 32, 159 PrefixLenMax: 32, 160 }, 161 }, 162 }, 163 Actions: types.RoutePolicyActions{ 164 RouteAction: types.RoutePolicyActionAccept, 165 AddCommunities: []string{"65535:65281"}, 166 }, 167 }, 168 }, 169 } 170 171 redPeer65001v6ExtRPName = PolicyName("red-peer-65001", "ipv6", v2alpha1.BGPServiceAdvert, "red-svc-non-default-ExternalIP") 172 redPeer65001v6ExtRP = &types.RoutePolicy{ 173 Name: redPeer65001v6ExtRPName, 174 Type: types.RoutePolicyTypeExport, 175 Statements: []*types.RoutePolicyStatement{ 176 { 177 Conditions: types.RoutePolicyConditions{ 178 MatchNeighbors: []string{"10.10.10.1/32"}, 179 MatchPrefixes: []*types.RoutePolicyPrefixMatch{ 180 { 181 CIDR: netip.MustParsePrefix(externalV6Prefix), 182 PrefixLenMin: 128, 183 PrefixLenMax: 128, 184 }, 185 }, 186 }, 187 Actions: types.RoutePolicyActions{ 188 RouteAction: types.RoutePolicyActionAccept, 189 AddCommunities: []string{"65535:65281"}, 190 }, 191 }, 192 }, 193 } 194 195 redClusterSvc = &slim_corev1.Service{ 196 ObjectMeta: slim_metav1.ObjectMeta{ 197 Name: redSvcKey.Name, 198 Namespace: redSvcKey.Namespace, 199 Labels: redSvcSelector.MatchLabels, 200 }, 201 Spec: slim_corev1.ServiceSpec{ 202 Type: slim_corev1.ServiceTypeClusterIP, 203 ClusterIP: clusterV4, 204 ClusterIPs: []string{ 205 clusterV4, 206 clusterV6, 207 }, 208 }, 209 } 210 211 redClusterSvcWithITP = func(iTP slim_corev1.ServiceInternalTrafficPolicy) *slim_corev1.Service { 212 cp := redClusterSvc.DeepCopy() 213 cp.Spec.InternalTrafficPolicy = &iTP 214 return cp 215 } 216 217 redPeer65001v4ClusterRPName = PolicyName("red-peer-65001", "ipv4", v2alpha1.BGPServiceAdvert, "red-svc-non-default-ClusterIP") 218 redPeer65001v4ClusterRP = &types.RoutePolicy{ 219 Name: redPeer65001v4ClusterRPName, 220 Type: types.RoutePolicyTypeExport, 221 Statements: []*types.RoutePolicyStatement{ 222 { 223 Conditions: types.RoutePolicyConditions{ 224 MatchNeighbors: []string{"10.10.10.1/32"}, 225 MatchPrefixes: []*types.RoutePolicyPrefixMatch{ 226 { 227 CIDR: netip.MustParsePrefix(clusterV4Prefix), 228 PrefixLenMin: 32, 229 PrefixLenMax: 32, 230 }, 231 }, 232 }, 233 Actions: types.RoutePolicyActions{ 234 RouteAction: types.RoutePolicyActionAccept, 235 AddCommunities: []string{"65535:65281"}, 236 }, 237 }, 238 }, 239 } 240 241 redPeer65001v6ClusterRPName = PolicyName("red-peer-65001", "ipv6", v2alpha1.BGPServiceAdvert, "red-svc-non-default-ClusterIP") 242 redPeer65001v6ClusterRP = &types.RoutePolicy{ 243 Name: redPeer65001v6ClusterRPName, 244 Type: types.RoutePolicyTypeExport, 245 Statements: []*types.RoutePolicyStatement{ 246 { 247 Conditions: types.RoutePolicyConditions{ 248 MatchNeighbors: []string{"10.10.10.1/32"}, 249 MatchPrefixes: []*types.RoutePolicyPrefixMatch{ 250 { 251 CIDR: netip.MustParsePrefix(clusterV6Prefix), 252 PrefixLenMin: 128, 253 PrefixLenMax: 128, 254 }, 255 }, 256 }, 257 Actions: types.RoutePolicyActions{ 258 RouteAction: types.RoutePolicyActionAccept, 259 AddCommunities: []string{"65535:65281"}, 260 }, 261 }, 262 }, 263 } 264 265 redExternalAndClusterSvc = &slim_corev1.Service{ 266 ObjectMeta: slim_metav1.ObjectMeta{ 267 Name: redSvcKey.Name, 268 Namespace: redSvcKey.Namespace, 269 Labels: redSvcSelector.MatchLabels, 270 }, 271 Spec: slim_corev1.ServiceSpec{ 272 Type: slim_corev1.ServiceTypeClusterIP, 273 ClusterIP: clusterV4, 274 ClusterIPs: []string{ 275 clusterV4, 276 clusterV6, 277 }, 278 ExternalIPs: []string{ 279 externalV4, 280 externalV6, 281 }, 282 }, 283 } 284 285 redExternalAndClusterSvcWithITP = func(svc *slim_corev1.Service, iTP slim_corev1.ServiceInternalTrafficPolicy) *slim_corev1.Service { 286 cp := svc.DeepCopy() 287 cp.Spec.InternalTrafficPolicy = &iTP 288 return cp 289 } 290 291 redExternalAndClusterSvcWithETP = func(svc *slim_corev1.Service, eTP slim_corev1.ServiceExternalTrafficPolicy) *slim_corev1.Service { 292 cp := svc.DeepCopy() 293 cp.Spec.ExternalTrafficPolicy = eTP 294 return cp 295 } 296 297 redSvcAdvert = &v2alpha1.CiliumBGPAdvertisement{ 298 ObjectMeta: metav1.ObjectMeta{ 299 Name: "red-podCIDR-advertisement", 300 Labels: map[string]string{ 301 "advertise": "red_bgp", 302 }, 303 }, 304 } 305 306 redSvcAdvertWithAdvertisements = func(adverts ...v2alpha1.BGPAdvertisement) *v2alpha1.CiliumBGPAdvertisement { 307 cp := redSvcAdvert.DeepCopy() 308 cp.Spec.Advertisements = adverts 309 return cp 310 } 311 312 lbSvcAdvert = v2alpha1.BGPAdvertisement{ 313 AdvertisementType: v2alpha1.BGPServiceAdvert, 314 Service: &v2alpha1.BGPServiceOptions{ 315 Addresses: []v2alpha1.BGPServiceAddressType{v2alpha1.BGPLoadBalancerIPAddr}, 316 }, 317 Attributes: &v2alpha1.BGPAttributes{ 318 Communities: &v2alpha1.BGPCommunities{ 319 Standard: []v2alpha1.BGPStandardCommunity{"65535:65281"}, 320 WellKnown: []v2alpha1.BGPWellKnownCommunity{"no-export"}, 321 }, 322 }, 323 } 324 lbSvcAdvertWithSelector = func(selector *slim_metav1.LabelSelector) v2alpha1.BGPAdvertisement { 325 cp := lbSvcAdvert.DeepCopy() 326 cp.Selector = selector 327 return *cp 328 } 329 330 externalSvcAdvert = v2alpha1.BGPAdvertisement{ 331 AdvertisementType: v2alpha1.BGPServiceAdvert, 332 Service: &v2alpha1.BGPServiceOptions{ 333 Addresses: []v2alpha1.BGPServiceAddressType{v2alpha1.BGPExternalIPAddr}, 334 }, 335 Attributes: &v2alpha1.BGPAttributes{ 336 Communities: &v2alpha1.BGPCommunities{ 337 Standard: []v2alpha1.BGPStandardCommunity{"65535:65281"}, 338 WellKnown: []v2alpha1.BGPWellKnownCommunity{"no-export"}, 339 }, 340 }, 341 } 342 343 externalSvcAdvertWithSelector = func(selector *slim_metav1.LabelSelector) v2alpha1.BGPAdvertisement { 344 cp := externalSvcAdvert.DeepCopy() 345 cp.Selector = selector 346 return *cp 347 } 348 349 clusterIPSvcAdvert = v2alpha1.BGPAdvertisement{ 350 AdvertisementType: v2alpha1.BGPServiceAdvert, 351 Service: &v2alpha1.BGPServiceOptions{ 352 Addresses: []v2alpha1.BGPServiceAddressType{v2alpha1.BGPClusterIPAddr}, 353 }, 354 Attributes: &v2alpha1.BGPAttributes{ 355 Communities: &v2alpha1.BGPCommunities{ 356 Standard: []v2alpha1.BGPStandardCommunity{"65535:65281"}, 357 WellKnown: []v2alpha1.BGPWellKnownCommunity{"no-export"}, 358 }, 359 }, 360 } 361 362 clusterIPSvcAdvertWithSelector = func(selector *slim_metav1.LabelSelector) v2alpha1.BGPAdvertisement { 363 cp := clusterIPSvcAdvert.DeepCopy() 364 cp.Selector = selector 365 return *cp 366 } 367 368 testBGPInstanceConfig = &v2alpha1.CiliumBGPNodeInstance{ 369 Name: "bgp-65001", 370 LocalASN: ptr.To[int64](65001), 371 Peers: []v2alpha1.CiliumBGPNodePeer{ 372 { 373 Name: "red-peer-65001", 374 PeerAddress: ptr.To[string]("10.10.10.1"), 375 PeerConfigRef: &v2alpha1.PeerConfigReference{ 376 Group: "cilium.io", 377 Kind: "CiliumBGPPeerConfig", 378 Name: "peer-config-red", 379 }, 380 }, 381 }, 382 } 383 384 testCiliumNodeConfig = &v2api.CiliumNode{ 385 ObjectMeta: metav1.ObjectMeta{ 386 Name: "node1", 387 }, 388 } 389 390 eps1Local = &k8s.Endpoints{ 391 ObjectMeta: slim_metav1.ObjectMeta{ 392 Name: "svc-1", 393 Namespace: "non-default", 394 }, 395 EndpointSliceID: k8s.EndpointSliceID{ 396 ServiceID: k8s.ServiceID{ 397 Name: redSvcKey.Name, 398 Namespace: redSvcKey.Namespace, 399 }, 400 EndpointSliceName: "svc-1", 401 }, 402 Backends: map[cmtypes.AddrCluster]*k8s.Backend{ 403 cmtypes.MustParseAddrCluster("10.0.0.1"): { 404 NodeName: "node1", 405 }, 406 cmtypes.MustParseAddrCluster("2001:db8:1000::1"): { 407 NodeName: "node1", 408 }, 409 }, 410 } 411 412 eps1LocalTerminating = &k8s.Endpoints{ 413 ObjectMeta: slim_metav1.ObjectMeta{ 414 Name: "svc-1", 415 Namespace: "non-default", 416 }, 417 EndpointSliceID: k8s.EndpointSliceID{ 418 ServiceID: k8s.ServiceID{ 419 Name: redSvcKey.Name, 420 Namespace: redSvcKey.Namespace, 421 }, 422 EndpointSliceName: "svc-1", 423 }, 424 Backends: map[cmtypes.AddrCluster]*k8s.Backend{ 425 cmtypes.MustParseAddrCluster("10.0.0.1"): { 426 NodeName: "node1", 427 Terminating: true, 428 }, 429 cmtypes.MustParseAddrCluster("2001:db8:1000::1"): { 430 NodeName: "node1", 431 Terminating: true, 432 }, 433 }, 434 } 435 436 eps1Remote = &k8s.Endpoints{ 437 ObjectMeta: slim_metav1.ObjectMeta{ 438 Name: "svc-1", 439 Namespace: "default", 440 }, 441 EndpointSliceID: k8s.EndpointSliceID{ 442 ServiceID: k8s.ServiceID{ 443 Name: redSvcKey.Name, 444 Namespace: redSvcKey.Namespace, 445 }, 446 EndpointSliceName: "svc-1", 447 }, 448 Backends: map[cmtypes.AddrCluster]*k8s.Backend{ 449 cmtypes.MustParseAddrCluster("10.0.0.2"): { 450 NodeName: "node2", 451 }, 452 cmtypes.MustParseAddrCluster("2001:db8:1000::2"): { 453 NodeName: "node2", 454 }, 455 }, 456 } 457 458 eps1Mixed = &k8s.Endpoints{ 459 ObjectMeta: slim_metav1.ObjectMeta{ 460 Name: "svc-1", 461 Namespace: "default", 462 }, 463 EndpointSliceID: k8s.EndpointSliceID{ 464 ServiceID: k8s.ServiceID{ 465 Name: redSvcKey.Name, 466 Namespace: redSvcKey.Namespace, 467 }, 468 EndpointSliceName: "svc-1", 469 }, 470 Backends: map[cmtypes.AddrCluster]*k8s.Backend{ 471 cmtypes.MustParseAddrCluster("10.0.0.1"): { 472 NodeName: "node1", 473 }, 474 cmtypes.MustParseAddrCluster("10.0.0.2"): { 475 NodeName: "node2", 476 }, 477 cmtypes.MustParseAddrCluster("2001:db8:1000::1"): { 478 NodeName: "node1", 479 }, 480 cmtypes.MustParseAddrCluster("2001:db8:1000::2"): { 481 NodeName: "node2", 482 }, 483 }, 484 } 485 ) 486 487 // Test_ServiceLBReconciler tests reconciliation of service of type load-balancer 488 func Test_ServiceLBReconciler(t *testing.T) { 489 logrus.SetLevel(logrus.DebugLevel) 490 491 tests := []struct { 492 name string 493 peerConfig []*v2alpha1.CiliumBGPPeerConfig 494 advertisements []*v2alpha1.CiliumBGPAdvertisement 495 services []*slim_corev1.Service 496 endpoints []*k8s.Endpoints 497 expectedMetadata ServiceReconcilerMetadata 498 }{ 499 { 500 name: "Service (LB) with advertisement( empty )", 501 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig}, 502 services: []*slim_corev1.Service{redLBSvc}, 503 advertisements: nil, 504 expectedMetadata: ServiceReconcilerMetadata{ 505 ServicePaths: ResourceAFPathsMap{}, 506 ServiceRoutePolicies: ResourceRoutePolicyMap{}, 507 ServiceAdvertisements: PeerAdvertisements{ 508 "red-peer-65001": PeerFamilyAdvertisements{ 509 {Afi: "ipv4", Safi: "unicast"}: nil, 510 {Afi: "ipv6", Safi: "unicast"}: nil, 511 }, 512 }, 513 }, 514 }, 515 { 516 name: "Service (LB) with advertisement(LB) - mismatch labels", 517 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig}, 518 services: []*slim_corev1.Service{redLBSvc}, 519 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 520 redSvcAdvertWithAdvertisements(lbSvcAdvertWithSelector(mismatchSvcSelector)), 521 }, 522 expectedMetadata: ServiceReconcilerMetadata{ 523 ServicePaths: ResourceAFPathsMap{}, 524 ServiceRoutePolicies: ResourceRoutePolicyMap{}, 525 ServiceAdvertisements: PeerAdvertisements{ 526 "red-peer-65001": PeerFamilyAdvertisements{ 527 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 528 lbSvcAdvertWithSelector(mismatchSvcSelector), 529 }, 530 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 531 lbSvcAdvertWithSelector(mismatchSvcSelector), 532 }, 533 }, 534 }, 535 }, 536 }, 537 { 538 name: "Service (LB) with advertisement(LB) - matching labels (eTP=cluster)", 539 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig}, 540 services: []*slim_corev1.Service{redLBSvcWithETP(slim_corev1.ServiceExternalTrafficPolicyCluster)}, 541 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 542 redSvcAdvertWithAdvertisements(lbSvcAdvertWithSelector(redSvcSelector)), 543 }, 544 expectedMetadata: ServiceReconcilerMetadata{ 545 ServicePaths: ResourceAFPathsMap{ 546 redSvcKey: AFPathsMap{ 547 {Afi: types.AfiIPv4, Safi: types.SafiUnicast}: { 548 ingressV4Prefix: types.NewPathForPrefix(netip.MustParsePrefix(ingressV4Prefix)), 549 }, 550 {Afi: types.AfiIPv6, Safi: types.SafiUnicast}: { 551 ingressV6Prefix: types.NewPathForPrefix(netip.MustParsePrefix(ingressV6Prefix)), 552 }, 553 }, 554 }, 555 ServiceRoutePolicies: ResourceRoutePolicyMap{ 556 redSvcKey: RoutePolicyMap{ 557 redPeer65001v4LBRPName: redPeer65001v4LBRP, 558 redPeer65001v6LBRPName: redPeer65001v6LBRP, 559 }, 560 }, 561 ServiceAdvertisements: PeerAdvertisements{ 562 "red-peer-65001": PeerFamilyAdvertisements{ 563 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 564 lbSvcAdvertWithSelector(redSvcSelector), 565 }, 566 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 567 lbSvcAdvertWithSelector(redSvcSelector), 568 }, 569 }, 570 }, 571 }, 572 }, 573 { 574 name: "Service (LB) with advertisement(LB) - matching labels (eTP=local, ep on node)", 575 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig}, 576 services: []*slim_corev1.Service{redLBSvcWithETP(slim_corev1.ServiceExternalTrafficPolicyLocal)}, 577 endpoints: []*k8s.Endpoints{eps1Local}, 578 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 579 redSvcAdvertWithAdvertisements(lbSvcAdvertWithSelector(redSvcSelector)), 580 }, 581 expectedMetadata: ServiceReconcilerMetadata{ 582 ServicePaths: ResourceAFPathsMap{ 583 redSvcKey: AFPathsMap{ 584 {Afi: types.AfiIPv4, Safi: types.SafiUnicast}: { 585 ingressV4Prefix: types.NewPathForPrefix(netip.MustParsePrefix(ingressV4Prefix)), 586 }, 587 {Afi: types.AfiIPv6, Safi: types.SafiUnicast}: { 588 ingressV6Prefix: types.NewPathForPrefix(netip.MustParsePrefix(ingressV6Prefix)), 589 }, 590 }, 591 }, 592 ServiceRoutePolicies: ResourceRoutePolicyMap{ 593 redSvcKey: RoutePolicyMap{ 594 redPeer65001v4LBRPName: redPeer65001v4LBRP, 595 redPeer65001v6LBRPName: redPeer65001v6LBRP, 596 }, 597 }, 598 ServiceAdvertisements: PeerAdvertisements{ 599 "red-peer-65001": PeerFamilyAdvertisements{ 600 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 601 lbSvcAdvertWithSelector(redSvcSelector), 602 }, 603 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 604 lbSvcAdvertWithSelector(redSvcSelector), 605 }, 606 }, 607 }, 608 }, 609 }, 610 { 611 name: "Service (LB) with advertisement(LB) - matching labels (eTP=local, mixed ep)", 612 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig}, 613 services: []*slim_corev1.Service{redLBSvcWithETP(slim_corev1.ServiceExternalTrafficPolicyLocal)}, 614 endpoints: []*k8s.Endpoints{eps1Mixed}, 615 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 616 redSvcAdvertWithAdvertisements(lbSvcAdvertWithSelector(redSvcSelector)), 617 }, 618 expectedMetadata: ServiceReconcilerMetadata{ 619 ServicePaths: ResourceAFPathsMap{ 620 redSvcKey: AFPathsMap{ 621 {Afi: types.AfiIPv4, Safi: types.SafiUnicast}: { 622 ingressV4Prefix: types.NewPathForPrefix(netip.MustParsePrefix(ingressV4Prefix)), 623 }, 624 {Afi: types.AfiIPv6, Safi: types.SafiUnicast}: { 625 ingressV6Prefix: types.NewPathForPrefix(netip.MustParsePrefix(ingressV6Prefix)), 626 }, 627 }, 628 }, 629 ServiceRoutePolicies: ResourceRoutePolicyMap{ 630 redSvcKey: RoutePolicyMap{ 631 redPeer65001v4LBRPName: redPeer65001v4LBRP, 632 redPeer65001v6LBRPName: redPeer65001v6LBRP, 633 }, 634 }, 635 ServiceAdvertisements: PeerAdvertisements{ 636 "red-peer-65001": PeerFamilyAdvertisements{ 637 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 638 lbSvcAdvertWithSelector(redSvcSelector), 639 }, 640 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 641 lbSvcAdvertWithSelector(redSvcSelector), 642 }, 643 }, 644 }, 645 }, 646 }, 647 { 648 name: "Service (LB) with advertisement(LB) - matching labels (eTP=local, ep on remote)", 649 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig}, 650 services: []*slim_corev1.Service{redLBSvcWithETP(slim_corev1.ServiceExternalTrafficPolicyLocal)}, 651 endpoints: []*k8s.Endpoints{eps1Remote}, 652 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 653 redSvcAdvertWithAdvertisements(lbSvcAdvertWithSelector(redSvcSelector)), 654 }, 655 expectedMetadata: ServiceReconcilerMetadata{ 656 ServicePaths: ResourceAFPathsMap{}, 657 ServiceRoutePolicies: ResourceRoutePolicyMap{}, 658 ServiceAdvertisements: PeerAdvertisements{ 659 "red-peer-65001": PeerFamilyAdvertisements{ 660 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 661 lbSvcAdvertWithSelector(redSvcSelector), 662 }, 663 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 664 lbSvcAdvertWithSelector(redSvcSelector), 665 }, 666 }, 667 }, 668 }, 669 }, 670 { 671 name: "Service (LB) with advertisement(LB) - matching labels (eTP=local, backends are terminating)", 672 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig}, 673 services: []*slim_corev1.Service{redLBSvcWithETP(slim_corev1.ServiceExternalTrafficPolicyLocal)}, 674 endpoints: []*k8s.Endpoints{eps1LocalTerminating}, 675 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 676 redSvcAdvertWithAdvertisements(lbSvcAdvertWithSelector(redSvcSelector)), 677 }, 678 expectedMetadata: ServiceReconcilerMetadata{ 679 ServicePaths: ResourceAFPathsMap{}, 680 ServiceRoutePolicies: ResourceRoutePolicyMap{}, 681 ServiceAdvertisements: PeerAdvertisements{ 682 "red-peer-65001": PeerFamilyAdvertisements{ 683 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 684 lbSvcAdvertWithSelector(redSvcSelector), 685 }, 686 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 687 lbSvcAdvertWithSelector(redSvcSelector), 688 }, 689 }, 690 }, 691 }, 692 }, 693 } 694 695 for _, tt := range tests { 696 t.Run(tt.name, func(t *testing.T) { 697 req := require.New(t) 698 699 params := ServiceReconcilerIn{ 700 Logger: serviceReconcilerTestLogger, 701 PeerAdvert: NewCiliumPeerAdvertisement( 702 PeerAdvertisementIn{ 703 Logger: podCIDRTestLogger, 704 PeerConfigStore: store.InitMockStore[*v2alpha1.CiliumBGPPeerConfig](tt.peerConfig), 705 AdvertStore: store.InitMockStore[*v2alpha1.CiliumBGPAdvertisement](tt.advertisements), 706 }), 707 SvcDiffStore: store.InitFakeDiffStore[*slim_corev1.Service](tt.services), 708 EPDiffStore: store.InitFakeDiffStore[*k8s.Endpoints](tt.endpoints), 709 } 710 711 svcReconciler := NewServiceReconciler(params).Reconciler.(*ServiceReconciler) 712 testBGPInstance := instance.NewFakeBGPInstance() 713 svcReconciler.Init(testBGPInstance) 714 defer svcReconciler.Cleanup(testBGPInstance) 715 716 // reconcile twice to validate idempotency 717 for i := 0; i < 2; i++ { 718 err := svcReconciler.Reconcile(context.Background(), ReconcileParams{ 719 BGPInstance: testBGPInstance, 720 DesiredConfig: testBGPInstanceConfig, 721 CiliumNode: testCiliumNodeConfig, 722 }) 723 req.NoError(err) 724 } 725 726 // validate new metadata 727 serviceMetadataEqual(req, tt.expectedMetadata, svcReconciler.getMetadata(testBGPInstance)) 728 }) 729 } 730 } 731 732 // Test_ServiceExternalIPReconciler tests reconciliation of cluster service with external IP 733 func Test_ServiceExternalIPReconciler(t *testing.T) { 734 logrus.SetLevel(logrus.DebugLevel) 735 736 tests := []struct { 737 name string 738 peerConfig []*v2alpha1.CiliumBGPPeerConfig 739 advertisements []*v2alpha1.CiliumBGPAdvertisement 740 services []*slim_corev1.Service 741 endpoints []*k8s.Endpoints 742 expectedMetadata ServiceReconcilerMetadata 743 }{ 744 { 745 name: "Service (External) with advertisement( empty )", 746 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig}, 747 services: []*slim_corev1.Service{redExternalSvc}, 748 advertisements: nil, 749 expectedMetadata: ServiceReconcilerMetadata{ 750 ServicePaths: ResourceAFPathsMap{}, 751 ServiceRoutePolicies: ResourceRoutePolicyMap{}, 752 ServiceAdvertisements: PeerAdvertisements{ 753 "red-peer-65001": PeerFamilyAdvertisements{ 754 {Afi: "ipv4", Safi: "unicast"}: nil, 755 {Afi: "ipv6", Safi: "unicast"}: nil, 756 }, 757 }, 758 }, 759 }, 760 { 761 name: "Service (External) with advertisement(External) - mismatch labels", 762 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig}, 763 services: []*slim_corev1.Service{redExternalSvc}, 764 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 765 redSvcAdvertWithAdvertisements(externalSvcAdvertWithSelector(mismatchSvcSelector)), 766 }, 767 expectedMetadata: ServiceReconcilerMetadata{ 768 ServicePaths: ResourceAFPathsMap{}, 769 ServiceRoutePolicies: ResourceRoutePolicyMap{}, 770 ServiceAdvertisements: PeerAdvertisements{ 771 "red-peer-65001": PeerFamilyAdvertisements{ 772 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 773 externalSvcAdvertWithSelector(mismatchSvcSelector), 774 }, 775 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 776 externalSvcAdvertWithSelector(mismatchSvcSelector), 777 }, 778 }, 779 }, 780 }, 781 }, 782 { 783 name: "Service (External) with advertisement(External) - matching labels (eTP=cluster)", 784 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig}, 785 services: []*slim_corev1.Service{redExternalSvcWithETP(slim_corev1.ServiceExternalTrafficPolicyCluster)}, 786 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 787 redSvcAdvertWithAdvertisements(externalSvcAdvertWithSelector(redSvcSelector)), 788 }, 789 expectedMetadata: ServiceReconcilerMetadata{ 790 ServicePaths: ResourceAFPathsMap{ 791 redSvcKey: AFPathsMap{ 792 {Afi: types.AfiIPv4, Safi: types.SafiUnicast}: { 793 externalV4Prefix: types.NewPathForPrefix(netip.MustParsePrefix(externalV4Prefix)), 794 }, 795 {Afi: types.AfiIPv6, Safi: types.SafiUnicast}: { 796 externalV6Prefix: types.NewPathForPrefix(netip.MustParsePrefix(externalV6Prefix)), 797 }, 798 }, 799 }, 800 ServiceRoutePolicies: ResourceRoutePolicyMap{ 801 redSvcKey: RoutePolicyMap{ 802 redPeer65001v4ExtRPName: redPeer65001v4ExtRP, 803 redPeer65001v6ExtRPName: redPeer65001v6ExtRP, 804 }, 805 }, 806 ServiceAdvertisements: PeerAdvertisements{ 807 "red-peer-65001": PeerFamilyAdvertisements{ 808 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 809 externalSvcAdvertWithSelector(redSvcSelector), 810 }, 811 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 812 externalSvcAdvertWithSelector(redSvcSelector), 813 }, 814 }, 815 }, 816 }, 817 }, 818 { 819 name: "Service (External) with advertisement(External) - matching labels (eTP=local, ep on node)", 820 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig}, 821 services: []*slim_corev1.Service{redExternalSvcWithETP(slim_corev1.ServiceExternalTrafficPolicyLocal)}, 822 endpoints: []*k8s.Endpoints{eps1Local}, 823 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 824 redSvcAdvertWithAdvertisements(externalSvcAdvertWithSelector(redSvcSelector)), 825 }, 826 expectedMetadata: ServiceReconcilerMetadata{ 827 ServicePaths: ResourceAFPathsMap{ 828 redSvcKey: AFPathsMap{ 829 {Afi: types.AfiIPv4, Safi: types.SafiUnicast}: { 830 externalV4Prefix: types.NewPathForPrefix(netip.MustParsePrefix(externalV4Prefix)), 831 }, 832 {Afi: types.AfiIPv6, Safi: types.SafiUnicast}: { 833 externalV6Prefix: types.NewPathForPrefix(netip.MustParsePrefix(externalV6Prefix)), 834 }, 835 }, 836 }, 837 ServiceRoutePolicies: ResourceRoutePolicyMap{ 838 redSvcKey: RoutePolicyMap{ 839 redPeer65001v4ExtRPName: redPeer65001v4ExtRP, 840 redPeer65001v6ExtRPName: redPeer65001v6ExtRP, 841 }, 842 }, 843 ServiceAdvertisements: PeerAdvertisements{ 844 "red-peer-65001": PeerFamilyAdvertisements{ 845 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 846 externalSvcAdvertWithSelector(redSvcSelector), 847 }, 848 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 849 externalSvcAdvertWithSelector(redSvcSelector), 850 }, 851 }, 852 }, 853 }, 854 }, 855 { 856 name: "Service (External) with advertisement(External) - matching labels (eTP=local, mixed ep)", 857 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig}, 858 services: []*slim_corev1.Service{redExternalSvcWithETP(slim_corev1.ServiceExternalTrafficPolicyLocal)}, 859 endpoints: []*k8s.Endpoints{eps1Mixed}, 860 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 861 redSvcAdvertWithAdvertisements(externalSvcAdvertWithSelector(redSvcSelector)), 862 }, 863 expectedMetadata: ServiceReconcilerMetadata{ 864 ServicePaths: ResourceAFPathsMap{ 865 redSvcKey: AFPathsMap{ 866 {Afi: types.AfiIPv4, Safi: types.SafiUnicast}: { 867 externalV4Prefix: types.NewPathForPrefix(netip.MustParsePrefix(externalV4Prefix)), 868 }, 869 {Afi: types.AfiIPv6, Safi: types.SafiUnicast}: { 870 externalV6Prefix: types.NewPathForPrefix(netip.MustParsePrefix(externalV6Prefix)), 871 }, 872 }, 873 }, 874 ServiceRoutePolicies: ResourceRoutePolicyMap{ 875 redSvcKey: RoutePolicyMap{ 876 redPeer65001v4ExtRPName: redPeer65001v4ExtRP, 877 redPeer65001v6ExtRPName: redPeer65001v6ExtRP, 878 }, 879 }, 880 ServiceAdvertisements: PeerAdvertisements{ 881 "red-peer-65001": PeerFamilyAdvertisements{ 882 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 883 externalSvcAdvertWithSelector(redSvcSelector), 884 }, 885 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 886 externalSvcAdvertWithSelector(redSvcSelector), 887 }, 888 }, 889 }, 890 }, 891 }, 892 { 893 name: "Service (External) with advertisement(External) - matching labels (eTP=local, ep on remote)", 894 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig}, 895 services: []*slim_corev1.Service{redExternalSvcWithETP(slim_corev1.ServiceExternalTrafficPolicyLocal)}, 896 endpoints: []*k8s.Endpoints{eps1Remote}, 897 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 898 redSvcAdvertWithAdvertisements(externalSvcAdvertWithSelector(redSvcSelector)), 899 }, 900 expectedMetadata: ServiceReconcilerMetadata{ 901 ServicePaths: ResourceAFPathsMap{}, 902 ServiceRoutePolicies: ResourceRoutePolicyMap{}, 903 ServiceAdvertisements: PeerAdvertisements{ 904 "red-peer-65001": PeerFamilyAdvertisements{ 905 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 906 externalSvcAdvertWithSelector(redSvcSelector), 907 }, 908 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 909 externalSvcAdvertWithSelector(redSvcSelector), 910 }, 911 }, 912 }, 913 }, 914 }, 915 } 916 917 for _, tt := range tests { 918 t.Run(tt.name, func(t *testing.T) { 919 req := require.New(t) 920 921 params := ServiceReconcilerIn{ 922 Logger: serviceReconcilerTestLogger, 923 PeerAdvert: NewCiliumPeerAdvertisement( 924 PeerAdvertisementIn{ 925 Logger: podCIDRTestLogger, 926 PeerConfigStore: store.InitMockStore[*v2alpha1.CiliumBGPPeerConfig](tt.peerConfig), 927 AdvertStore: store.InitMockStore[*v2alpha1.CiliumBGPAdvertisement](tt.advertisements), 928 }), 929 SvcDiffStore: store.InitFakeDiffStore[*slim_corev1.Service](tt.services), 930 EPDiffStore: store.InitFakeDiffStore[*k8s.Endpoints](tt.endpoints), 931 } 932 933 svcReconciler := NewServiceReconciler(params).Reconciler.(*ServiceReconciler) 934 testBGPInstance := instance.NewFakeBGPInstance() 935 svcReconciler.Init(testBGPInstance) 936 defer svcReconciler.Cleanup(testBGPInstance) 937 938 // reconcile twice to validate idempotency 939 for i := 0; i < 2; i++ { 940 err := svcReconciler.Reconcile(context.Background(), ReconcileParams{ 941 BGPInstance: testBGPInstance, 942 DesiredConfig: testBGPInstanceConfig, 943 CiliumNode: testCiliumNodeConfig, 944 }) 945 req.NoError(err) 946 } 947 948 // validate new metadata 949 serviceMetadataEqual(req, tt.expectedMetadata, svcReconciler.getMetadata(testBGPInstance)) 950 }) 951 } 952 } 953 954 // Test_ServiceClusterIPReconciler tests reconciliation of cluster service 955 func Test_ServiceClusterIPReconciler(t *testing.T) { 956 logrus.SetLevel(logrus.DebugLevel) 957 958 tests := []struct { 959 name string 960 peerConfig []*v2alpha1.CiliumBGPPeerConfig 961 advertisements []*v2alpha1.CiliumBGPAdvertisement 962 services []*slim_corev1.Service 963 endpoints []*k8s.Endpoints 964 expectedMetadata ServiceReconcilerMetadata 965 }{ 966 { 967 name: "Service (Cluster) with advertisement( empty )", 968 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig}, 969 services: []*slim_corev1.Service{redClusterSvc}, 970 advertisements: nil, 971 expectedMetadata: ServiceReconcilerMetadata{ 972 ServicePaths: ResourceAFPathsMap{}, 973 ServiceRoutePolicies: ResourceRoutePolicyMap{}, 974 ServiceAdvertisements: PeerAdvertisements{ 975 "red-peer-65001": PeerFamilyAdvertisements{ 976 {Afi: "ipv4", Safi: "unicast"}: nil, 977 {Afi: "ipv6", Safi: "unicast"}: nil, 978 }, 979 }, 980 }, 981 }, 982 { 983 name: "Service (Cluster) with advertisement(Cluster) - mismatch labels", 984 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig}, 985 services: []*slim_corev1.Service{redClusterSvc}, 986 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 987 redSvcAdvertWithAdvertisements(clusterIPSvcAdvertWithSelector(mismatchSvcSelector)), 988 }, 989 expectedMetadata: ServiceReconcilerMetadata{ 990 ServicePaths: ResourceAFPathsMap{}, 991 ServiceRoutePolicies: ResourceRoutePolicyMap{}, 992 ServiceAdvertisements: PeerAdvertisements{ 993 "red-peer-65001": PeerFamilyAdvertisements{ 994 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 995 clusterIPSvcAdvertWithSelector(mismatchSvcSelector), 996 }, 997 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 998 clusterIPSvcAdvertWithSelector(mismatchSvcSelector), 999 }, 1000 }, 1001 }, 1002 }, 1003 }, 1004 { 1005 name: "Service (Cluster) with advertisement(Cluster) - matching labels (iTP=cluster)", 1006 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig}, 1007 services: []*slim_corev1.Service{redClusterSvcWithITP(slim_corev1.ServiceInternalTrafficPolicyCluster)}, 1008 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 1009 redSvcAdvertWithAdvertisements(clusterIPSvcAdvertWithSelector(redSvcSelector)), 1010 }, 1011 expectedMetadata: ServiceReconcilerMetadata{ 1012 ServicePaths: ResourceAFPathsMap{ 1013 redSvcKey: AFPathsMap{ 1014 {Afi: types.AfiIPv4, Safi: types.SafiUnicast}: { 1015 clusterV4Prefix: types.NewPathForPrefix(netip.MustParsePrefix(clusterV4Prefix)), 1016 }, 1017 {Afi: types.AfiIPv6, Safi: types.SafiUnicast}: { 1018 clusterV6Prefix: types.NewPathForPrefix(netip.MustParsePrefix(clusterV6Prefix)), 1019 }, 1020 }, 1021 }, 1022 ServiceRoutePolicies: ResourceRoutePolicyMap{ 1023 redSvcKey: RoutePolicyMap{ 1024 redPeer65001v4ClusterRPName: redPeer65001v4ClusterRP, 1025 redPeer65001v6ClusterRPName: redPeer65001v6ClusterRP, 1026 }, 1027 }, 1028 ServiceAdvertisements: PeerAdvertisements{ 1029 "red-peer-65001": PeerFamilyAdvertisements{ 1030 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 1031 clusterIPSvcAdvertWithSelector(redSvcSelector), 1032 }, 1033 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 1034 clusterIPSvcAdvertWithSelector(redSvcSelector), 1035 }, 1036 }, 1037 }, 1038 }, 1039 }, 1040 { 1041 name: "Service (Cluster) with advertisement(Cluster) - matching labels (eTP=local, ep on node)", 1042 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig}, 1043 services: []*slim_corev1.Service{redClusterSvcWithITP(slim_corev1.ServiceInternalTrafficPolicyLocal)}, 1044 endpoints: []*k8s.Endpoints{eps1Local}, 1045 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 1046 redSvcAdvertWithAdvertisements(clusterIPSvcAdvertWithSelector(redSvcSelector)), 1047 }, 1048 expectedMetadata: ServiceReconcilerMetadata{ 1049 ServicePaths: ResourceAFPathsMap{ 1050 redSvcKey: AFPathsMap{ 1051 {Afi: types.AfiIPv4, Safi: types.SafiUnicast}: { 1052 clusterV4Prefix: types.NewPathForPrefix(netip.MustParsePrefix(clusterV4Prefix)), 1053 }, 1054 {Afi: types.AfiIPv6, Safi: types.SafiUnicast}: { 1055 clusterV6Prefix: types.NewPathForPrefix(netip.MustParsePrefix(clusterV6Prefix)), 1056 }, 1057 }, 1058 }, 1059 ServiceRoutePolicies: ResourceRoutePolicyMap{ 1060 redSvcKey: RoutePolicyMap{ 1061 redPeer65001v4ClusterRPName: redPeer65001v4ClusterRP, 1062 redPeer65001v6ClusterRPName: redPeer65001v6ClusterRP, 1063 }, 1064 }, 1065 ServiceAdvertisements: PeerAdvertisements{ 1066 "red-peer-65001": PeerFamilyAdvertisements{ 1067 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 1068 clusterIPSvcAdvertWithSelector(redSvcSelector), 1069 }, 1070 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 1071 clusterIPSvcAdvertWithSelector(redSvcSelector), 1072 }, 1073 }, 1074 }, 1075 }, 1076 }, 1077 { 1078 name: "Service (Cluster) with advertisement(Cluster) - matching labels (eTP=local, mixed ep)", 1079 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig}, 1080 services: []*slim_corev1.Service{redClusterSvcWithITP(slim_corev1.ServiceInternalTrafficPolicyLocal)}, 1081 endpoints: []*k8s.Endpoints{eps1Mixed}, 1082 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 1083 redSvcAdvertWithAdvertisements(clusterIPSvcAdvertWithSelector(redSvcSelector)), 1084 }, 1085 expectedMetadata: ServiceReconcilerMetadata{ 1086 ServicePaths: ResourceAFPathsMap{ 1087 redSvcKey: AFPathsMap{ 1088 {Afi: types.AfiIPv4, Safi: types.SafiUnicast}: { 1089 clusterV4Prefix: types.NewPathForPrefix(netip.MustParsePrefix(clusterV4Prefix)), 1090 }, 1091 {Afi: types.AfiIPv6, Safi: types.SafiUnicast}: { 1092 clusterV6Prefix: types.NewPathForPrefix(netip.MustParsePrefix(clusterV6Prefix)), 1093 }, 1094 }, 1095 }, 1096 ServiceRoutePolicies: ResourceRoutePolicyMap{ 1097 redSvcKey: RoutePolicyMap{ 1098 redPeer65001v4ClusterRPName: redPeer65001v4ClusterRP, 1099 redPeer65001v6ClusterRPName: redPeer65001v6ClusterRP, 1100 }, 1101 }, 1102 ServiceAdvertisements: PeerAdvertisements{ 1103 "red-peer-65001": PeerFamilyAdvertisements{ 1104 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 1105 clusterIPSvcAdvertWithSelector(redSvcSelector), 1106 }, 1107 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 1108 clusterIPSvcAdvertWithSelector(redSvcSelector), 1109 }, 1110 }, 1111 }, 1112 }, 1113 }, 1114 { 1115 name: "Service (Cluster) with advertisement(Cluster) - matching labels (eTP=local, ep on remote)", 1116 peerConfig: []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig}, 1117 services: []*slim_corev1.Service{redClusterSvcWithITP(slim_corev1.ServiceInternalTrafficPolicyLocal)}, 1118 endpoints: []*k8s.Endpoints{eps1Remote}, 1119 advertisements: []*v2alpha1.CiliumBGPAdvertisement{ 1120 redSvcAdvertWithAdvertisements(clusterIPSvcAdvertWithSelector(redSvcSelector)), 1121 }, 1122 expectedMetadata: ServiceReconcilerMetadata{ 1123 ServicePaths: ResourceAFPathsMap{}, 1124 ServiceRoutePolicies: ResourceRoutePolicyMap{}, 1125 ServiceAdvertisements: PeerAdvertisements{ 1126 "red-peer-65001": PeerFamilyAdvertisements{ 1127 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 1128 clusterIPSvcAdvertWithSelector(redSvcSelector), 1129 }, 1130 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 1131 clusterIPSvcAdvertWithSelector(redSvcSelector), 1132 }, 1133 }, 1134 }, 1135 }, 1136 }, 1137 } 1138 1139 for _, tt := range tests { 1140 t.Run(tt.name, func(t *testing.T) { 1141 req := require.New(t) 1142 1143 params := ServiceReconcilerIn{ 1144 Logger: serviceReconcilerTestLogger, 1145 PeerAdvert: NewCiliumPeerAdvertisement( 1146 PeerAdvertisementIn{ 1147 Logger: podCIDRTestLogger, 1148 PeerConfigStore: store.InitMockStore[*v2alpha1.CiliumBGPPeerConfig](tt.peerConfig), 1149 AdvertStore: store.InitMockStore[*v2alpha1.CiliumBGPAdvertisement](tt.advertisements), 1150 }), 1151 SvcDiffStore: store.InitFakeDiffStore[*slim_corev1.Service](tt.services), 1152 EPDiffStore: store.InitFakeDiffStore[*k8s.Endpoints](tt.endpoints), 1153 } 1154 1155 svcReconciler := NewServiceReconciler(params).Reconciler.(*ServiceReconciler) 1156 testBGPInstance := instance.NewFakeBGPInstance() 1157 svcReconciler.Init(testBGPInstance) 1158 defer svcReconciler.Cleanup(testBGPInstance) 1159 1160 // reconcile twice to validate idempotency 1161 for i := 0; i < 2; i++ { 1162 err := svcReconciler.Reconcile(context.Background(), ReconcileParams{ 1163 BGPInstance: testBGPInstance, 1164 DesiredConfig: testBGPInstanceConfig, 1165 CiliumNode: testCiliumNodeConfig, 1166 }) 1167 req.NoError(err) 1168 } 1169 1170 // validate new metadata 1171 serviceMetadataEqual(req, tt.expectedMetadata, svcReconciler.getMetadata(testBGPInstance)) 1172 }) 1173 } 1174 } 1175 1176 // Test_ServiceAndAdvertisementModifications is a step test, in which each step modifies the advertisement or service parameters. 1177 func Test_ServiceAndAdvertisementModifications(t *testing.T) { 1178 logrus.SetLevel(logrus.DebugLevel) 1179 1180 peerConfigs := []*v2alpha1.CiliumBGPPeerConfig{redPeerConfig} 1181 1182 steps := []struct { 1183 name string 1184 upsertAdverts []*v2alpha1.CiliumBGPAdvertisement 1185 upsertServices []*slim_corev1.Service 1186 upsertEPs []*k8s.Endpoints 1187 expectedMetadata ServiceReconcilerMetadata 1188 }{ 1189 { 1190 name: "Initial setup - Service (nil) with advertisement( empty )", 1191 upsertAdverts: nil, 1192 upsertServices: nil, 1193 upsertEPs: nil, 1194 expectedMetadata: ServiceReconcilerMetadata{ 1195 ServicePaths: ResourceAFPathsMap{}, 1196 ServiceRoutePolicies: ResourceRoutePolicyMap{}, 1197 ServiceAdvertisements: PeerAdvertisements{ 1198 "red-peer-65001": PeerFamilyAdvertisements{ 1199 {Afi: "ipv4", Safi: "unicast"}: nil, 1200 {Afi: "ipv6", Safi: "unicast"}: nil, 1201 }, 1202 }, 1203 }, 1204 }, 1205 { 1206 name: "Add service (Cluster, External) with advertisement(Cluster) - matching labels", 1207 upsertAdverts: []*v2alpha1.CiliumBGPAdvertisement{ 1208 redSvcAdvertWithAdvertisements(v2alpha1.BGPAdvertisement{ 1209 AdvertisementType: v2alpha1.BGPServiceAdvert, 1210 Service: &v2alpha1.BGPServiceOptions{ 1211 Addresses: []v2alpha1.BGPServiceAddressType{v2alpha1.BGPClusterIPAddr}, 1212 }, 1213 Selector: redSvcSelector, 1214 Attributes: &v2alpha1.BGPAttributes{ 1215 Communities: &v2alpha1.BGPCommunities{ 1216 Standard: []v2alpha1.BGPStandardCommunity{"65535:65281"}, 1217 WellKnown: []v2alpha1.BGPWellKnownCommunity{"no-export"}, 1218 }, 1219 }, 1220 }), 1221 }, 1222 upsertServices: []*slim_corev1.Service{redExternalAndClusterSvc}, 1223 expectedMetadata: ServiceReconcilerMetadata{ 1224 // Only cluster IPs are advertised 1225 ServicePaths: ResourceAFPathsMap{ 1226 redSvcKey: AFPathsMap{ 1227 {Afi: types.AfiIPv4, Safi: types.SafiUnicast}: { 1228 clusterV4Prefix: types.NewPathForPrefix(netip.MustParsePrefix(clusterV4Prefix)), 1229 }, 1230 {Afi: types.AfiIPv6, Safi: types.SafiUnicast}: { 1231 clusterV6Prefix: types.NewPathForPrefix(netip.MustParsePrefix(clusterV6Prefix)), 1232 }, 1233 }, 1234 }, 1235 ServiceRoutePolicies: ResourceRoutePolicyMap{ 1236 redSvcKey: RoutePolicyMap{ 1237 redPeer65001v4ClusterRPName: redPeer65001v4ClusterRP, 1238 redPeer65001v6ClusterRPName: redPeer65001v6ClusterRP, 1239 }, 1240 }, 1241 ServiceAdvertisements: PeerAdvertisements{ 1242 "red-peer-65001": PeerFamilyAdvertisements{ 1243 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 1244 { 1245 AdvertisementType: v2alpha1.BGPServiceAdvert, 1246 Service: &v2alpha1.BGPServiceOptions{ 1247 Addresses: []v2alpha1.BGPServiceAddressType{v2alpha1.BGPClusterIPAddr}, 1248 }, 1249 Selector: redSvcSelector, 1250 Attributes: &v2alpha1.BGPAttributes{ 1251 Communities: &v2alpha1.BGPCommunities{ 1252 Standard: []v2alpha1.BGPStandardCommunity{"65535:65281"}, 1253 WellKnown: []v2alpha1.BGPWellKnownCommunity{"no-export"}, 1254 }, 1255 }, 1256 }, 1257 }, 1258 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 1259 { 1260 AdvertisementType: v2alpha1.BGPServiceAdvert, 1261 Service: &v2alpha1.BGPServiceOptions{ 1262 Addresses: []v2alpha1.BGPServiceAddressType{v2alpha1.BGPClusterIPAddr}, 1263 }, 1264 Selector: redSvcSelector, 1265 Attributes: &v2alpha1.BGPAttributes{ 1266 Communities: &v2alpha1.BGPCommunities{ 1267 Standard: []v2alpha1.BGPStandardCommunity{"65535:65281"}, 1268 WellKnown: []v2alpha1.BGPWellKnownCommunity{"no-export"}, 1269 }, 1270 }, 1271 }, 1272 }, 1273 }, 1274 }, 1275 }, 1276 }, 1277 { 1278 name: "Update advertisement(Cluster, External) - matching labels", 1279 upsertAdverts: []*v2alpha1.CiliumBGPAdvertisement{ 1280 redSvcAdvertWithAdvertisements(v2alpha1.BGPAdvertisement{ 1281 AdvertisementType: v2alpha1.BGPServiceAdvert, 1282 Service: &v2alpha1.BGPServiceOptions{ 1283 Addresses: []v2alpha1.BGPServiceAddressType{ 1284 v2alpha1.BGPClusterIPAddr, 1285 v2alpha1.BGPExternalIPAddr, 1286 }, 1287 }, 1288 Selector: redSvcSelector, 1289 Attributes: &v2alpha1.BGPAttributes{ 1290 Communities: &v2alpha1.BGPCommunities{ 1291 Standard: []v2alpha1.BGPStandardCommunity{"65535:65281"}, 1292 WellKnown: []v2alpha1.BGPWellKnownCommunity{"no-export"}, 1293 }, 1294 }, 1295 }), 1296 }, 1297 expectedMetadata: ServiceReconcilerMetadata{ 1298 // Both cluster and external IPs are advertised 1299 ServicePaths: ResourceAFPathsMap{ 1300 redSvcKey: AFPathsMap{ 1301 {Afi: types.AfiIPv4, Safi: types.SafiUnicast}: { 1302 clusterV4Prefix: types.NewPathForPrefix(netip.MustParsePrefix(clusterV4Prefix)), 1303 externalV4Prefix: types.NewPathForPrefix(netip.MustParsePrefix(externalV4Prefix)), 1304 }, 1305 {Afi: types.AfiIPv6, Safi: types.SafiUnicast}: { 1306 clusterV6Prefix: types.NewPathForPrefix(netip.MustParsePrefix(clusterV6Prefix)), 1307 externalV6Prefix: types.NewPathForPrefix(netip.MustParsePrefix(externalV6Prefix)), 1308 }, 1309 }, 1310 }, 1311 ServiceRoutePolicies: ResourceRoutePolicyMap{ 1312 redSvcKey: RoutePolicyMap{ 1313 redPeer65001v4ClusterRPName: redPeer65001v4ClusterRP, 1314 redPeer65001v4ExtRPName: redPeer65001v4ExtRP, 1315 redPeer65001v6ClusterRPName: redPeer65001v6ClusterRP, 1316 redPeer65001v6ExtRPName: redPeer65001v6ExtRP, 1317 }, 1318 }, 1319 ServiceAdvertisements: PeerAdvertisements{ 1320 "red-peer-65001": PeerFamilyAdvertisements{ 1321 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 1322 { 1323 AdvertisementType: v2alpha1.BGPServiceAdvert, 1324 Service: &v2alpha1.BGPServiceOptions{ 1325 Addresses: []v2alpha1.BGPServiceAddressType{ 1326 v2alpha1.BGPClusterIPAddr, 1327 v2alpha1.BGPExternalIPAddr, 1328 }, 1329 }, 1330 Selector: redSvcSelector, 1331 Attributes: &v2alpha1.BGPAttributes{ 1332 Communities: &v2alpha1.BGPCommunities{ 1333 Standard: []v2alpha1.BGPStandardCommunity{"65535:65281"}, 1334 WellKnown: []v2alpha1.BGPWellKnownCommunity{"no-export"}, 1335 }, 1336 }, 1337 }, 1338 }, 1339 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 1340 { 1341 AdvertisementType: v2alpha1.BGPServiceAdvert, 1342 Service: &v2alpha1.BGPServiceOptions{ 1343 Addresses: []v2alpha1.BGPServiceAddressType{ 1344 v2alpha1.BGPClusterIPAddr, 1345 v2alpha1.BGPExternalIPAddr, 1346 }, 1347 }, 1348 Selector: redSvcSelector, 1349 Attributes: &v2alpha1.BGPAttributes{ 1350 Communities: &v2alpha1.BGPCommunities{ 1351 Standard: []v2alpha1.BGPStandardCommunity{"65535:65281"}, 1352 WellKnown: []v2alpha1.BGPWellKnownCommunity{"no-export"}, 1353 }, 1354 }, 1355 }, 1356 }, 1357 }, 1358 }, 1359 }, 1360 }, 1361 { 1362 name: "Update service (Cluster, External) traffic policy local", 1363 upsertServices: []*slim_corev1.Service{ 1364 redExternalAndClusterSvcWithITP( 1365 redExternalAndClusterSvcWithETP(redExternalAndClusterSvc, slim_corev1.ServiceExternalTrafficPolicyLocal), 1366 slim_corev1.ServiceInternalTrafficPolicyLocal), 1367 }, 1368 expectedMetadata: ServiceReconcilerMetadata{ 1369 // Both cluster and external IPs are withdrawn, since traffic policy is local and there are no endpoints. 1370 ServicePaths: ResourceAFPathsMap{}, 1371 ServiceRoutePolicies: ResourceRoutePolicyMap{}, 1372 ServiceAdvertisements: PeerAdvertisements{ 1373 "red-peer-65001": PeerFamilyAdvertisements{ 1374 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 1375 { 1376 AdvertisementType: v2alpha1.BGPServiceAdvert, 1377 Service: &v2alpha1.BGPServiceOptions{ 1378 Addresses: []v2alpha1.BGPServiceAddressType{ 1379 v2alpha1.BGPClusterIPAddr, 1380 v2alpha1.BGPExternalIPAddr, 1381 }, 1382 }, 1383 Selector: redSvcSelector, 1384 Attributes: &v2alpha1.BGPAttributes{ 1385 Communities: &v2alpha1.BGPCommunities{ 1386 Standard: []v2alpha1.BGPStandardCommunity{"65535:65281"}, 1387 WellKnown: []v2alpha1.BGPWellKnownCommunity{"no-export"}, 1388 }, 1389 }, 1390 }, 1391 }, 1392 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 1393 { 1394 AdvertisementType: v2alpha1.BGPServiceAdvert, 1395 Service: &v2alpha1.BGPServiceOptions{ 1396 Addresses: []v2alpha1.BGPServiceAddressType{ 1397 v2alpha1.BGPClusterIPAddr, 1398 v2alpha1.BGPExternalIPAddr, 1399 }, 1400 }, 1401 Selector: redSvcSelector, 1402 Attributes: &v2alpha1.BGPAttributes{ 1403 Communities: &v2alpha1.BGPCommunities{ 1404 Standard: []v2alpha1.BGPStandardCommunity{"65535:65281"}, 1405 WellKnown: []v2alpha1.BGPWellKnownCommunity{"no-export"}, 1406 }, 1407 }, 1408 }, 1409 }, 1410 }, 1411 }, 1412 }, 1413 }, 1414 { 1415 name: "Update local endpoints (Cluster, External)", 1416 upsertEPs: []*k8s.Endpoints{eps1Mixed}, 1417 expectedMetadata: ServiceReconcilerMetadata{ 1418 // Both cluster and external IPs are advertised since there is local endpoint. 1419 ServicePaths: ResourceAFPathsMap{ 1420 redSvcKey: AFPathsMap{ 1421 {Afi: types.AfiIPv4, Safi: types.SafiUnicast}: { 1422 clusterV4Prefix: types.NewPathForPrefix(netip.MustParsePrefix(clusterV4Prefix)), 1423 externalV4Prefix: types.NewPathForPrefix(netip.MustParsePrefix(externalV4Prefix)), 1424 }, 1425 {Afi: types.AfiIPv6, Safi: types.SafiUnicast}: { 1426 clusterV6Prefix: types.NewPathForPrefix(netip.MustParsePrefix(clusterV6Prefix)), 1427 externalV6Prefix: types.NewPathForPrefix(netip.MustParsePrefix(externalV6Prefix)), 1428 }, 1429 }, 1430 }, 1431 ServiceRoutePolicies: ResourceRoutePolicyMap{ 1432 redSvcKey: RoutePolicyMap{ 1433 redPeer65001v4ClusterRPName: redPeer65001v4ClusterRP, 1434 redPeer65001v4ExtRPName: redPeer65001v4ExtRP, 1435 redPeer65001v6ClusterRPName: redPeer65001v6ClusterRP, 1436 redPeer65001v6ExtRPName: redPeer65001v6ExtRP, 1437 }, 1438 }, 1439 ServiceAdvertisements: PeerAdvertisements{ 1440 "red-peer-65001": PeerFamilyAdvertisements{ 1441 {Afi: "ipv4", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 1442 { 1443 AdvertisementType: v2alpha1.BGPServiceAdvert, 1444 Service: &v2alpha1.BGPServiceOptions{ 1445 Addresses: []v2alpha1.BGPServiceAddressType{ 1446 v2alpha1.BGPClusterIPAddr, 1447 v2alpha1.BGPExternalIPAddr, 1448 }, 1449 }, 1450 Selector: redSvcSelector, 1451 Attributes: &v2alpha1.BGPAttributes{ 1452 Communities: &v2alpha1.BGPCommunities{ 1453 Standard: []v2alpha1.BGPStandardCommunity{"65535:65281"}, 1454 WellKnown: []v2alpha1.BGPWellKnownCommunity{"no-export"}, 1455 }, 1456 }, 1457 }, 1458 }, 1459 {Afi: "ipv6", Safi: "unicast"}: []v2alpha1.BGPAdvertisement{ 1460 { 1461 AdvertisementType: v2alpha1.BGPServiceAdvert, 1462 Service: &v2alpha1.BGPServiceOptions{ 1463 Addresses: []v2alpha1.BGPServiceAddressType{ 1464 v2alpha1.BGPClusterIPAddr, 1465 v2alpha1.BGPExternalIPAddr, 1466 }, 1467 }, 1468 Selector: redSvcSelector, 1469 Attributes: &v2alpha1.BGPAttributes{ 1470 Communities: &v2alpha1.BGPCommunities{ 1471 Standard: []v2alpha1.BGPStandardCommunity{"65535:65281"}, 1472 WellKnown: []v2alpha1.BGPWellKnownCommunity{"no-export"}, 1473 }, 1474 }, 1475 }, 1476 }, 1477 }, 1478 }, 1479 }, 1480 }, 1481 } 1482 1483 req := require.New(t) 1484 advertStore := store.NewMockBGPCPResourceStore[*v2alpha1.CiliumBGPAdvertisement]() 1485 serviceStore := store.NewFakeDiffStore[*slim_corev1.Service]() 1486 epStore := store.NewFakeDiffStore[*k8s.Endpoints]() 1487 1488 params := ServiceReconcilerIn{ 1489 Logger: serviceReconcilerTestLogger, 1490 PeerAdvert: NewCiliumPeerAdvertisement( 1491 PeerAdvertisementIn{ 1492 Logger: podCIDRTestLogger, 1493 PeerConfigStore: store.InitMockStore[*v2alpha1.CiliumBGPPeerConfig](peerConfigs), 1494 AdvertStore: advertStore, 1495 }), 1496 SvcDiffStore: serviceStore, 1497 EPDiffStore: epStore, 1498 } 1499 1500 svcReconciler := NewServiceReconciler(params).Reconciler.(*ServiceReconciler) 1501 testBGPInstance := instance.NewFakeBGPInstance() 1502 svcReconciler.Init(testBGPInstance) 1503 defer svcReconciler.Cleanup(testBGPInstance) 1504 1505 for _, tt := range steps { 1506 t.Logf("Running step - %s", tt.name) 1507 for _, advert := range tt.upsertAdverts { 1508 advertStore.Upsert(advert) 1509 } 1510 1511 for _, svc := range tt.upsertServices { 1512 serviceStore.Upsert(svc) 1513 } 1514 1515 for _, ep := range tt.upsertEPs { 1516 epStore.Upsert(ep) 1517 } 1518 1519 err := svcReconciler.Reconcile(context.Background(), ReconcileParams{ 1520 BGPInstance: testBGPInstance, 1521 DesiredConfig: testBGPInstanceConfig, 1522 CiliumNode: testCiliumNodeConfig, 1523 }) 1524 req.NoError(err) 1525 1526 // validate new metadata 1527 serviceMetadataEqual(req, tt.expectedMetadata, svcReconciler.getMetadata(testBGPInstance)) 1528 } 1529 } 1530 1531 func serviceMetadataEqual(req *require.Assertions, expectedMetadata, runningMetadata ServiceReconcilerMetadata) { 1532 req.Truef(PeerAdvertisementsEqual(expectedMetadata.ServiceAdvertisements, runningMetadata.ServiceAdvertisements), 1533 "ServiceAdvertisements mismatch, expected: %v, got: %v", expectedMetadata.ServiceAdvertisements, runningMetadata.ServiceAdvertisements) 1534 1535 req.Equalf(len(expectedMetadata.ServicePaths), len(runningMetadata.ServicePaths), 1536 "ServicePaths length mismatch, expected: %v, got: %v", expectedMetadata.ServicePaths, runningMetadata.ServicePaths) 1537 1538 for svc, expectedSvcPaths := range expectedMetadata.ServicePaths { 1539 runningSvcPaths, exists := runningMetadata.ServicePaths[svc] 1540 req.Truef(exists, "Service not found in running: %v", svc) 1541 1542 runningFamilyPaths := make(map[types.Family]map[string]struct{}) 1543 for family, paths := range runningSvcPaths { 1544 pathSet := make(map[string]struct{}) 1545 1546 for pathKey := range paths { 1547 pathSet[pathKey] = struct{}{} 1548 } 1549 runningFamilyPaths[family] = pathSet 1550 } 1551 1552 expectedFamilyPaths := make(map[types.Family]map[string]struct{}) 1553 for family, paths := range expectedSvcPaths { 1554 pathSet := make(map[string]struct{}) 1555 1556 for pathKey := range paths { 1557 pathSet[pathKey] = struct{}{} 1558 } 1559 expectedFamilyPaths[family] = pathSet 1560 } 1561 1562 req.Equal(expectedFamilyPaths, runningFamilyPaths) 1563 } 1564 1565 req.Equalf(expectedMetadata.ServiceRoutePolicies, runningMetadata.ServiceRoutePolicies, 1566 "ServiceRoutePolicies mismatch, expected: %v, got: %v", expectedMetadata.ServiceRoutePolicies, runningMetadata.ServiceRoutePolicies) 1567 }