Skip to content

Commit 388468b

Browse files
bpdohalldboslee
authored andcommitted
Skip reconciles on endpoint slices when endpoint routing not in use (#72)
backport from upstream: envoyproxy#5345 Co-authored-by: David Boslee <david@goteleport.com>
1 parent e49bc6c commit 388468b

File tree

2 files changed

+170
-5
lines changed

2 files changed

+170
-5
lines changed

internal/provider/kubernetes/predicates.go

Lines changed: 138 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626

2727
egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
2828
"github.com/envoyproxy/gateway/internal/gatewayapi"
29+
"github.com/envoyproxy/gateway/internal/gatewayapi/resource"
2930
"github.com/envoyproxy/gateway/internal/utils"
3031
)
3132

@@ -491,7 +492,7 @@ func (r *gatewayAPIReconciler) validateEndpointSliceForReconcile(obj client.Obje
491492
nsName.Name = multiClusterSvcName
492493
}
493494

494-
if r.isRouteReferencingBackend(&nsName) {
495+
if r.hasRouteWithEndpointRouting(&nsName) {
495496
return true
496497
}
497498

@@ -516,6 +517,142 @@ func (r *gatewayAPIReconciler) validateEndpointSliceForReconcile(obj client.Obje
516517
return false
517518
}
518519

520+
// hasRouteWithEndpointRouting returns true if the backend(service and serviceImport) is referenced by any of the xRoutes
521+
// in the system and that route has a parent reference with EndpointRouting.
522+
func (r *gatewayAPIReconciler) hasRouteWithEndpointRouting(nsName *types.NamespacedName) bool {
523+
ctx := context.Background()
524+
httpRouteList := &gwapiv1.HTTPRouteList{}
525+
if err := r.client.List(ctx, httpRouteList, &client.ListOptions{
526+
FieldSelector: fields.OneTermEqualSelector(backendHTTPRouteIndex, nsName.String()),
527+
}); err != nil && !kerrors.IsNotFound(err) {
528+
r.log.Error(err, "failed to find associated HTTPRoutes")
529+
return false
530+
}
531+
for _, route := range httpRouteList.Items {
532+
if r.hasEndpointRouting(route.Namespace, route.Spec.CommonRouteSpec) {
533+
return true
534+
}
535+
}
536+
537+
if r.grpcRouteCRDExists {
538+
grpcRouteList := &gwapiv1.GRPCRouteList{}
539+
if err := r.client.List(ctx, grpcRouteList, &client.ListOptions{
540+
FieldSelector: fields.OneTermEqualSelector(backendGRPCRouteIndex, nsName.String()),
541+
}); err != nil && !kerrors.IsNotFound(err) {
542+
r.log.Error(err, "failed to find associated GRPCRoutes")
543+
return false
544+
}
545+
for _, route := range grpcRouteList.Items {
546+
if r.hasEndpointRouting(route.Namespace, route.Spec.CommonRouteSpec) {
547+
return true
548+
}
549+
}
550+
}
551+
552+
if r.tlsRouteCRDExists {
553+
tlsRouteList := &gwapiv1a2.TLSRouteList{}
554+
if err := r.client.List(ctx, tlsRouteList, &client.ListOptions{
555+
FieldSelector: fields.OneTermEqualSelector(backendTLSRouteIndex, nsName.String()),
556+
}); err != nil && !kerrors.IsNotFound(err) {
557+
r.log.Error(err, "failed to find associated TLSRoutes")
558+
return false
559+
}
560+
for _, route := range tlsRouteList.Items {
561+
if r.hasEndpointRouting(route.Namespace, route.Spec.CommonRouteSpec) {
562+
return true
563+
}
564+
}
565+
}
566+
567+
if r.tcpRouteCRDExists {
568+
tcpRouteList := &gwapiv1a2.TCPRouteList{}
569+
if err := r.client.List(ctx, tcpRouteList, &client.ListOptions{
570+
FieldSelector: fields.OneTermEqualSelector(backendTCPRouteIndex, nsName.String()),
571+
}); err != nil && !kerrors.IsNotFound(err) {
572+
r.log.Error(err, "failed to find associated TCPRoutes")
573+
return false
574+
}
575+
for _, route := range tcpRouteList.Items {
576+
if r.hasEndpointRouting(route.Namespace, route.Spec.CommonRouteSpec) {
577+
return true
578+
}
579+
}
580+
}
581+
582+
if r.udpRouteCRDExists {
583+
udpRouteList := &gwapiv1a2.UDPRouteList{}
584+
if err := r.client.List(ctx, udpRouteList, &client.ListOptions{
585+
FieldSelector: fields.OneTermEqualSelector(backendUDPRouteIndex, nsName.String()),
586+
}); err != nil && !kerrors.IsNotFound(err) {
587+
r.log.Error(err, "failed to find associated UDPRoutes")
588+
return false
589+
}
590+
for _, route := range udpRouteList.Items {
591+
if r.hasEndpointRouting(route.Namespace, route.Spec.CommonRouteSpec) {
592+
return true
593+
}
594+
}
595+
}
596+
597+
return false
598+
}
599+
600+
// hasEndpointRouting checks that the associated egv1a1.EnvoyProxy has endpoint routing.
601+
func (r *gatewayAPIReconciler) hasEndpointRouting(namespace string, spec gwapiv1.CommonRouteSpec) bool {
602+
ctx := context.Background()
603+
for _, ref := range spec.ParentRefs {
604+
if ref.Kind != nil && *ref.Kind != resource.KindGateway {
605+
return false
606+
}
607+
if ref.Namespace != nil {
608+
namespace = string(*ref.Namespace)
609+
}
610+
611+
gw := gwapiv1.Gateway{
612+
ObjectMeta: metav1.ObjectMeta{
613+
Name: string(ref.Name),
614+
Namespace: namespace,
615+
},
616+
}
617+
err := r.client.Get(ctx, client.ObjectKeyFromObject(&gw), &gw)
618+
if err != nil {
619+
r.log.Error(err, "unable to find associated gateway")
620+
return false
621+
}
622+
623+
gwc := gwapiv1.GatewayClass{
624+
ObjectMeta: metav1.ObjectMeta{
625+
Name: string(gw.Spec.GatewayClassName),
626+
},
627+
}
628+
err = r.client.Get(ctx, client.ObjectKeyFromObject(&gwc), &gwc)
629+
if err != nil {
630+
r.log.Error(err, "unable to find associated gateway class")
631+
return false
632+
}
633+
634+
var epNs string
635+
if gwc.Spec.ParametersRef.Namespace != nil {
636+
epNs = string(*gwc.Spec.ParametersRef.Namespace)
637+
}
638+
ep := egv1a1.EnvoyProxy{
639+
ObjectMeta: metav1.ObjectMeta{
640+
Name: gwc.Spec.ParametersRef.Name,
641+
Namespace: epNs,
642+
},
643+
}
644+
if err := r.client.Get(ctx, client.ObjectKeyFromObject(&ep), &ep); err != nil {
645+
r.log.Error(err, "unable to find associated EnvoyProxy")
646+
return false
647+
}
648+
rt := ep.Spec.RoutingType
649+
if rt == nil || *rt == egv1a1.EndpointRoutingType {
650+
return true
651+
}
652+
}
653+
return false
654+
}
655+
519656
// validateObjectForReconcile tries finding the owning Gateway of the Deployment or DaemonSet
520657
// if it exists, finds the Gateway's Service, and further updates the Gateway
521658
// status Ready condition. No Deployments or DaemonSets are pushed for reconciliation.

internal/provider/kubernetes/predicates_test.go

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -413,8 +413,20 @@ func TestValidateSecretForReconcile(t *testing.T) {
413413
// TestValidateEndpointSliceForReconcile tests the validateEndpointSliceForReconcile
414414
// predicate function.
415415
func TestValidateEndpointSliceForReconcile(t *testing.T) {
416-
sampleGatewayClass := test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, nil)
417-
sampleGateway := test.GetGateway(types.NamespacedName{Namespace: "default", Name: "scheduled-status-test"}, "test-gc", 8080)
416+
ep := test.GetEnvoyProxy(types.NamespacedName{Name: "test-ep"}, false)
417+
epWithServiceRouting := ep.DeepCopy()
418+
routing := egv1a1.ServiceRoutingType
419+
epWithServiceRouting.Spec.RoutingType = &routing
420+
421+
epRef := &test.GroupKindNamespacedName{
422+
Group: gwapiv1.Group(ep.GroupVersionKind().Group),
423+
Kind: gwapiv1.Kind(ep.GroupVersionKind().Kind),
424+
Namespace: gwapiv1.Namespace(ep.Namespace),
425+
Name: gwapiv1.ObjectName(ep.Name),
426+
}
427+
428+
sampleGatewayClass := test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, epRef)
429+
sampleGateway := test.GetGateway(types.NamespacedName{Name: "scheduled-status-test"}, "test-gc", 8080)
418430
sampleServiceBackendRef := test.GetServiceBackendRef(types.NamespacedName{Name: "service"}, 80)
419431
sampleServiceImportBackendRef := test.GetServiceImportBackendRef(types.NamespacedName{Name: "imported-service"}, 80)
420432

@@ -428,6 +440,7 @@ func TestValidateEndpointSliceForReconcile(t *testing.T) {
428440
name: "route service but no routes exist",
429441
configs: []client.Object{
430442
sampleGatewayClass,
443+
ep,
431444
sampleGateway,
432445
},
433446
endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "service", false),
@@ -437,16 +450,18 @@ func TestValidateEndpointSliceForReconcile(t *testing.T) {
437450
name: "http route service routes exist, but endpointslice is associated with another service",
438451
configs: []client.Object{
439452
sampleGatewayClass,
453+
ep,
440454
sampleGateway,
441455
test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", sampleServiceBackendRef, ""),
442456
},
443457
endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "other-service", false),
444458
expect: false,
445459
},
446460
{
447-
name: "http route service routes exist",
461+
name: "http route service routes exist with endpoint routing",
448462
configs: []client.Object{
449463
sampleGatewayClass,
464+
ep,
450465
sampleGateway,
451466
test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", sampleServiceBackendRef, ""),
452467
},
@@ -457,6 +472,7 @@ func TestValidateEndpointSliceForReconcile(t *testing.T) {
457472
name: "http route serviceimport routes exist",
458473
configs: []client.Object{
459474
sampleGatewayClass,
475+
ep,
460476
sampleGateway,
461477
test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", sampleServiceImportBackendRef, ""),
462478
},
@@ -466,7 +482,8 @@ func TestValidateEndpointSliceForReconcile(t *testing.T) {
466482
{
467483
name: "mirrored backend route exists",
468484
configs: []client.Object{
469-
test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, nil),
485+
test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, epRef),
486+
ep,
470487
sampleGateway,
471488
&gwapiv1.HTTPRoute{
472489
ObjectMeta: metav1.ObjectMeta{
@@ -509,6 +526,17 @@ func TestValidateEndpointSliceForReconcile(t *testing.T) {
509526
endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "mirror-service", false),
510527
expect: true,
511528
},
529+
{
530+
name: "http route service routes exist with service routing",
531+
configs: []client.Object{
532+
test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, epRef),
533+
epWithServiceRouting,
534+
sampleGateway,
535+
test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", sampleServiceBackendRef, ""),
536+
},
537+
endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "service", false),
538+
expect: false,
539+
},
512540
}
513541

514542
// Create the reconciler.

0 commit comments

Comments
 (0)