Skip to content

Commit 502dd42

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 2962534 commit 502dd42

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
@@ -28,6 +28,7 @@ import (
2828

2929
egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
3030
"github.com/envoyproxy/gateway/internal/gatewayapi"
31+
"github.com/envoyproxy/gateway/internal/gatewayapi/resource"
3132
"github.com/envoyproxy/gateway/internal/utils"
3233
)
3334

@@ -592,7 +593,7 @@ func (r *gatewayAPIReconciler) validateEndpointSliceForReconcile(obj client.Obje
592593
nsName.Name = multiClusterSvcName
593594
}
594595

595-
if r.isRouteReferencingBackend(&nsName) {
596+
if r.hasRouteWithEndpointRouting(&nsName) {
596597
return true
597598
}
598599

@@ -620,6 +621,142 @@ func (r *gatewayAPIReconciler) validateEndpointSliceForReconcile(obj client.Obje
620621
return false
621622
}
622623

624+
// hasRouteWithEndpointRouting returns true if the backend(service and serviceImport) is referenced by any of the xRoutes
625+
// in the system and that route has a parent reference with EndpointRouting.
626+
func (r *gatewayAPIReconciler) hasRouteWithEndpointRouting(nsName *types.NamespacedName) bool {
627+
ctx := context.Background()
628+
httpRouteList := &gwapiv1.HTTPRouteList{}
629+
if err := r.client.List(ctx, httpRouteList, &client.ListOptions{
630+
FieldSelector: fields.OneTermEqualSelector(backendHTTPRouteIndex, nsName.String()),
631+
}); err != nil && !kerrors.IsNotFound(err) {
632+
r.log.Error(err, "failed to find associated HTTPRoutes")
633+
return false
634+
}
635+
for _, route := range httpRouteList.Items {
636+
if r.hasEndpointRouting(route.Namespace, route.Spec.CommonRouteSpec) {
637+
return true
638+
}
639+
}
640+
641+
if r.grpcRouteCRDExists {
642+
grpcRouteList := &gwapiv1.GRPCRouteList{}
643+
if err := r.client.List(ctx, grpcRouteList, &client.ListOptions{
644+
FieldSelector: fields.OneTermEqualSelector(backendGRPCRouteIndex, nsName.String()),
645+
}); err != nil && !kerrors.IsNotFound(err) {
646+
r.log.Error(err, "failed to find associated GRPCRoutes")
647+
return false
648+
}
649+
for _, route := range grpcRouteList.Items {
650+
if r.hasEndpointRouting(route.Namespace, route.Spec.CommonRouteSpec) {
651+
return true
652+
}
653+
}
654+
}
655+
656+
if r.tlsRouteCRDExists {
657+
tlsRouteList := &gwapiv1a2.TLSRouteList{}
658+
if err := r.client.List(ctx, tlsRouteList, &client.ListOptions{
659+
FieldSelector: fields.OneTermEqualSelector(backendTLSRouteIndex, nsName.String()),
660+
}); err != nil && !kerrors.IsNotFound(err) {
661+
r.log.Error(err, "failed to find associated TLSRoutes")
662+
return false
663+
}
664+
for _, route := range tlsRouteList.Items {
665+
if r.hasEndpointRouting(route.Namespace, route.Spec.CommonRouteSpec) {
666+
return true
667+
}
668+
}
669+
}
670+
671+
if r.tcpRouteCRDExists {
672+
tcpRouteList := &gwapiv1a2.TCPRouteList{}
673+
if err := r.client.List(ctx, tcpRouteList, &client.ListOptions{
674+
FieldSelector: fields.OneTermEqualSelector(backendTCPRouteIndex, nsName.String()),
675+
}); err != nil && !kerrors.IsNotFound(err) {
676+
r.log.Error(err, "failed to find associated TCPRoutes")
677+
return false
678+
}
679+
for _, route := range tcpRouteList.Items {
680+
if r.hasEndpointRouting(route.Namespace, route.Spec.CommonRouteSpec) {
681+
return true
682+
}
683+
}
684+
}
685+
686+
if r.udpRouteCRDExists {
687+
udpRouteList := &gwapiv1a2.UDPRouteList{}
688+
if err := r.client.List(ctx, udpRouteList, &client.ListOptions{
689+
FieldSelector: fields.OneTermEqualSelector(backendUDPRouteIndex, nsName.String()),
690+
}); err != nil && !kerrors.IsNotFound(err) {
691+
r.log.Error(err, "failed to find associated UDPRoutes")
692+
return false
693+
}
694+
for _, route := range udpRouteList.Items {
695+
if r.hasEndpointRouting(route.Namespace, route.Spec.CommonRouteSpec) {
696+
return true
697+
}
698+
}
699+
}
700+
701+
return false
702+
}
703+
704+
// hasEndpointRouting checks that the associated egv1a1.EnvoyProxy has endpoint routing.
705+
func (r *gatewayAPIReconciler) hasEndpointRouting(namespace string, spec gwapiv1.CommonRouteSpec) bool {
706+
ctx := context.Background()
707+
for _, ref := range spec.ParentRefs {
708+
if ref.Kind != nil && *ref.Kind != resource.KindGateway {
709+
return false
710+
}
711+
if ref.Namespace != nil {
712+
namespace = string(*ref.Namespace)
713+
}
714+
715+
gw := gwapiv1.Gateway{
716+
ObjectMeta: metav1.ObjectMeta{
717+
Name: string(ref.Name),
718+
Namespace: namespace,
719+
},
720+
}
721+
err := r.client.Get(ctx, client.ObjectKeyFromObject(&gw), &gw)
722+
if err != nil {
723+
r.log.Error(err, "unable to find associated gateway")
724+
return false
725+
}
726+
727+
gwc := gwapiv1.GatewayClass{
728+
ObjectMeta: metav1.ObjectMeta{
729+
Name: string(gw.Spec.GatewayClassName),
730+
},
731+
}
732+
err = r.client.Get(ctx, client.ObjectKeyFromObject(&gwc), &gwc)
733+
if err != nil {
734+
r.log.Error(err, "unable to find associated gateway class")
735+
return false
736+
}
737+
738+
var epNs string
739+
if gwc.Spec.ParametersRef.Namespace != nil {
740+
epNs = string(*gwc.Spec.ParametersRef.Namespace)
741+
}
742+
ep := egv1a1.EnvoyProxy{
743+
ObjectMeta: metav1.ObjectMeta{
744+
Name: gwc.Spec.ParametersRef.Name,
745+
Namespace: epNs,
746+
},
747+
}
748+
if err := r.client.Get(ctx, client.ObjectKeyFromObject(&ep), &ep); err != nil {
749+
r.log.Error(err, "unable to find associated EnvoyProxy")
750+
return false
751+
}
752+
rt := ep.Spec.RoutingType
753+
if rt == nil || *rt == egv1a1.EndpointRoutingType {
754+
return true
755+
}
756+
}
757+
return false
758+
}
759+
623760
// validateObjectForReconcile tries finding the owning Gateway of the Deployment or DaemonSet
624761
// if it exists, finds the Gateway's Service, and further updates the Gateway
625762
// 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
@@ -561,8 +561,20 @@ func TestValidateSecretForReconcile(t *testing.T) {
561561
// TestValidateEndpointSliceForReconcile tests the validateEndpointSliceForReconcile
562562
// predicate function.
563563
func TestValidateEndpointSliceForReconcile(t *testing.T) {
564-
sampleGatewayClass := test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, nil)
565-
sampleGateway := test.GetGateway(types.NamespacedName{Namespace: "default", Name: "scheduled-status-test"}, "test-gc", 8080)
564+
ep := test.GetEnvoyProxy(types.NamespacedName{Name: "test-ep"}, false)
565+
epWithServiceRouting := ep.DeepCopy()
566+
routing := egv1a1.ServiceRoutingType
567+
epWithServiceRouting.Spec.RoutingType = &routing
568+
569+
epRef := &test.GroupKindNamespacedName{
570+
Group: gwapiv1.Group(ep.GroupVersionKind().Group),
571+
Kind: gwapiv1.Kind(ep.GroupVersionKind().Kind),
572+
Namespace: gwapiv1.Namespace(ep.Namespace),
573+
Name: gwapiv1.ObjectName(ep.Name),
574+
}
575+
576+
sampleGatewayClass := test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, epRef)
577+
sampleGateway := test.GetGateway(types.NamespacedName{Name: "scheduled-status-test"}, "test-gc", 8080)
566578
sampleServiceBackendRef := test.GetServiceBackendRef(types.NamespacedName{Name: "service"}, 80)
567579
sampleServiceImportBackendRef := test.GetServiceImportBackendRef(types.NamespacedName{Name: "imported-service"}, 80)
568580

@@ -576,6 +588,7 @@ func TestValidateEndpointSliceForReconcile(t *testing.T) {
576588
name: "route service but no routes exist",
577589
configs: []client.Object{
578590
sampleGatewayClass,
591+
ep,
579592
sampleGateway,
580593
},
581594
endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "service", false),
@@ -585,17 +598,19 @@ func TestValidateEndpointSliceForReconcile(t *testing.T) {
585598
name: "http route service routes exist, but endpointslice is associated with another service",
586599
configs: []client.Object{
587600
sampleGatewayClass,
601+
ep,
588602
sampleGateway,
589603
test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", sampleServiceBackendRef, ""),
590604
},
591605
endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "other-service", false),
592606
expect: false,
593607
},
594608
{
595-
name: "http route service routes exist",
609+
name: "http route service routes exist with endpoint routing",
596610
configs: []client.Object{
597611
sampleGatewayClass,
598612
sampleGateway,
613+
ep,
599614
test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", sampleServiceBackendRef, ""),
600615
},
601616
endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "service", false),
@@ -606,6 +621,7 @@ func TestValidateEndpointSliceForReconcile(t *testing.T) {
606621
configs: []client.Object{
607622
sampleGatewayClass,
608623
sampleGateway,
624+
ep,
609625
test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", sampleServiceImportBackendRef, ""),
610626
},
611627
endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "imported-service", true),
@@ -614,7 +630,8 @@ func TestValidateEndpointSliceForReconcile(t *testing.T) {
614630
{
615631
name: "mirrored backend route exists",
616632
configs: []client.Object{
617-
test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, nil),
633+
test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, epRef),
634+
ep,
618635
sampleGateway,
619636
&gwapiv1.HTTPRoute{
620637
ObjectMeta: metav1.ObjectMeta{
@@ -657,6 +674,17 @@ func TestValidateEndpointSliceForReconcile(t *testing.T) {
657674
endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "mirror-service", false),
658675
expect: true,
659676
},
677+
{
678+
name: "http route service routes exist with service routing",
679+
configs: []client.Object{
680+
test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, epRef),
681+
epWithServiceRouting,
682+
sampleGateway,
683+
test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", sampleServiceBackendRef, ""),
684+
},
685+
endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "service", false),
686+
expect: false,
687+
},
660688
}
661689

662690
// Create the reconciler.

0 commit comments

Comments
 (0)