From 9f33ec4ff8d1cac83049590a62f78de417a3d1dd Mon Sep 17 00:00:00 2001 From: Rudrakh Panigrahi Date: Sat, 21 Jun 2025 14:08:17 +0530 Subject: [PATCH] fix: add configMap indexers for EEP reconciler Signed-off-by: Rudrakh Panigrahi --- internal/provider/kubernetes/controller.go | 1 + .../provider/kubernetes/controller_offline.go | 1 + internal/provider/kubernetes/indexers.go | 29 +++++ internal/provider/kubernetes/predicates.go | 14 +++ .../provider/kubernetes/predicates_test.go | 109 ++++++++++++++++++ internal/provider/kubernetes/test/utils.go | 12 ++ release-notes/current.yaml | 1 + 7 files changed, 167 insertions(+) diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go index bf081eda0f..448ab5a107 100644 --- a/internal/provider/kubernetes/controller.go +++ b/internal/provider/kubernetes/controller.go @@ -2213,6 +2213,7 @@ func (r *gatewayAPIReconciler) processEnvoyExtensionPolicies( // It will be recomputed by the gateway-api layer envoyExtensionPolicy.Status = gwapiv1a2.PolicyStatus{} if !resourceMap.allAssociatedEnvoyExtensionPolicies.Has(utils.NamespacedName(&envoyExtensionPolicy).String()) { + r.log.Info("processing EnvoyExtensionPolicy", "namespace", policy.Namespace, "name", policy.Name) resourceMap.allAssociatedEnvoyExtensionPolicies.Insert(utils.NamespacedName(&envoyExtensionPolicy).String()) resourceTree.EnvoyExtensionPolicies = append(resourceTree.EnvoyExtensionPolicies, &envoyExtensionPolicy) } diff --git a/internal/provider/kubernetes/controller_offline.go b/internal/provider/kubernetes/controller_offline.go index 74f64d1a01..9a55f8a318 100644 --- a/internal/provider/kubernetes/controller_offline.go +++ b/internal/provider/kubernetes/controller_offline.go @@ -139,6 +139,7 @@ func newOfflineGatewayAPIClient() client.Client { WithIndex(&egv1a1.SecurityPolicy{}, configMapSecurityPolicyIndex, configMapSecurityPolicyIndexFunc). WithIndex(&egv1a1.EnvoyExtensionPolicy{}, backendEnvoyExtensionPolicyIndex, backendEnvoyExtensionPolicyIndexFunc). WithIndex(&egv1a1.EnvoyExtensionPolicy{}, secretEnvoyExtensionPolicyIndex, secretEnvoyExtensionPolicyIndexFunc). + WithIndex(&egv1a1.EnvoyExtensionPolicy{}, configMapEepIndex, configMapEepIndexFunc). WithIndex(&gwapiv1a3.BackendTLSPolicy{}, configMapBtlsIndex, configMapBtlsIndexFunc). WithIndex(&gwapiv1a3.BackendTLSPolicy{}, secretBtlsIndex, secretBtlsIndexFunc). WithIndex(&egv1a1.HTTPRouteFilter{}, configMapHTTPRouteFilterIndex, configMapRouteFilterIndexFunc). diff --git a/internal/provider/kubernetes/indexers.go b/internal/provider/kubernetes/indexers.go index 17fcb85968..188117da0e 100644 --- a/internal/provider/kubernetes/indexers.go +++ b/internal/provider/kubernetes/indexers.go @@ -52,6 +52,7 @@ const ( secretEnvoyExtensionPolicyIndex = "secretEnvoyExtensionPolicyIndex" httpRouteFilterHTTPRouteIndex = "httpRouteFilterHTTPRouteIndex" configMapBtpIndex = "configMapBtpIndex" + configMapEepIndex = "configMapEepIndex" configMapHTTPRouteFilterIndex = "configMapHTTPRouteFilterIndex" secretHTTPRouteFilterIndex = "secretHTTPRouteFilterIndex" ) @@ -809,6 +810,28 @@ func configMapBtpIndexFunc(rawObj client.Object) []string { return configMapReferences } +func configMapEepIndexFunc(rawObj client.Object) []string { + eep := rawObj.(*egv1a1.EnvoyExtensionPolicy) + var configMapReferences []string + if eep.Spec.Lua == nil { + return configMapReferences + } + + for _, p := range eep.Spec.Lua { + if p.ValueRef != nil { + if string(p.ValueRef.Kind) == resource.KindConfigMap { + configMapReferences = append(configMapReferences, + types.NamespacedName{ + Namespace: eep.Namespace, + Name: string(p.ValueRef.Name), + }.String(), + ) + } + } + } + return configMapReferences +} + // addRouteFilterIndexers adds indexing on HTTPRouteFilter, for ConfigMap objects that are // referenced in HTTPRouteFilter objects. This helps in querying for HTTPRouteFilters that are // affected by a particular ConfigMap CRUD. @@ -928,6 +951,12 @@ func addEnvoyExtensionPolicyIndexers(ctx context.Context, mgr manager.Manager) e return err } + if err = mgr.GetFieldIndexer().IndexField( + ctx, &egv1a1.EnvoyExtensionPolicy{}, configMapEepIndex, + configMapEepIndexFunc); err != nil { + return err + } + return nil } diff --git a/internal/provider/kubernetes/predicates.go b/internal/provider/kubernetes/predicates.go index ae18b3f039..27b92e5cc9 100644 --- a/internal/provider/kubernetes/predicates.go +++ b/internal/provider/kubernetes/predicates.go @@ -779,6 +779,20 @@ func (r *gatewayAPIReconciler) validateConfigMapForReconcile(obj client.Object) } } + if r.eepCRDExists { + eepList := &egv1a1.EnvoyExtensionPolicyList{} + if err := r.client.List(context.Background(), eepList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(configMapEepIndex, utils.NamespacedName(configMap).String()), + }); err != nil { + r.log.Error(err, "unable to find associated EnvoyExtensionPolicy") + return false + } + + if len(eepList.Items) > 0 { + return true + } + } + if r.hrfCRDExists { routeFilterList := &egv1a1.HTTPRouteFilterList{} if err := r.client.List(context.Background(), routeFilterList, &client.ListOptions{ diff --git a/internal/provider/kubernetes/predicates_test.go b/internal/provider/kubernetes/predicates_test.go index 0ccb4999c2..ec34defd07 100644 --- a/internal/provider/kubernetes/predicates_test.go +++ b/internal/provider/kubernetes/predicates_test.go @@ -175,6 +175,115 @@ func TestValidateGatewayForReconcile(t *testing.T) { } } +// TestValidateConfigMapForReconcile tests the validateConfigMapForReconcile +// predicate function. +func TestValidateConfigMapForReconcile(t *testing.T) { + testCases := []struct { + name string + configs []client.Object + configMap client.Object + expect bool + }{ + { + name: "references EnvoyExtensionPolicy Lua config map", + configs: []client.Object{ + test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, nil), + test.GetGateway(types.NamespacedName{Name: "scheduled-status-test"}, "test-gc", 8080), + &egv1a1.EnvoyExtensionPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "lua-cm", + Namespace: "test", + }, + Spec: egv1a1.EnvoyExtensionPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRefs: []gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + { + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Kind: "Gateway", + Name: "scheduled-status-test", + }, + }, + }, + }, + Lua: []egv1a1.Lua{ + { + Type: egv1a1.LuaValueTypeValueRef, + ValueRef: &gwapiv1.LocalObjectReference{ + Kind: gwapiv1a2.Kind("ConfigMap"), + Name: gwapiv1a2.ObjectName("lua"), + Group: gwapiv1a2.Group("v1"), + }, + }, + }, + }, + }, + }, + configMap: test.GetConfigMap(types.NamespacedName{Name: "lua", Namespace: "test"}, make(map[string]string), make(map[string]string)), + expect: true, + }, + { + name: "does not reference EnvoyExtensionPolicy Lua config map", + configs: []client.Object{ + test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, nil), + test.GetGateway(types.NamespacedName{Name: "scheduled-status-test"}, "test-gc", 8080), + &egv1a1.EnvoyExtensionPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "lua-cm", + Namespace: "test", + }, + Spec: egv1a1.EnvoyExtensionPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRefs: []gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + { + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Kind: "Gateway", + Name: "scheduled-status-test", + }, + }, + }, + }, + Lua: []egv1a1.Lua{ + { + Type: egv1a1.LuaValueTypeValueRef, + ValueRef: &gwapiv1.LocalObjectReference{ + Kind: gwapiv1a2.Kind("ConfigMap"), + Name: gwapiv1a2.ObjectName("lua"), + Group: gwapiv1a2.Group("v1"), + }, + }, + }, + }, + }, + }, + configMap: test.GetConfigMap(types.NamespacedName{Name: "not-lua", Namespace: "test"}, make(map[string]string), make(map[string]string)), + expect: false, + }, + } + + // Create the reconciler. + logger := logging.DefaultLogger(os.Stdout, egv1a1.LogLevelInfo) + + r := gatewayAPIReconciler{ + classController: egv1a1.GatewayControllerName, + log: logger, + spCRDExists: true, + epCRDExists: true, + eepCRDExists: true, + } + + for _, tc := range testCases { + r.client = fakeclient.NewClientBuilder(). + WithScheme(envoygateway.GetScheme()). + WithObjects(tc.configs...). + WithIndex(&egv1a1.EnvoyExtensionPolicy{}, configMapEepIndex, configMapEepIndexFunc). + Build() + t.Run(tc.name, func(t *testing.T) { + res := r.validateConfigMapForReconcile(tc.configMap) + require.Equal(t, tc.expect, res) + }) + } +} + // TestValidateSecretForReconcile tests the validateSecretForReconcile // predicate function. func TestValidateSecretForReconcile(t *testing.T) { diff --git a/internal/provider/kubernetes/test/utils.go b/internal/provider/kubernetes/test/utils.go index a6b2ef3256..3eac5782a8 100644 --- a/internal/provider/kubernetes/test/utils.go +++ b/internal/provider/kubernetes/test/utils.go @@ -364,6 +364,18 @@ func GetService(nsName types.NamespacedName, labels map[string]string, ports map return service } +// GetConfigMap returns a sample ConfigMap with labels and data +func GetConfigMap(nsName types.NamespacedName, labels, data map[string]string) *corev1.ConfigMap { + return &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: nsName.Name, + Namespace: nsName.Namespace, + Labels: labels, + }, + Data: data, + } +} + // GetEndpointSlice returns a sample EndpointSlice. func GetEndpointSlice(nsName types.NamespacedName, svcName string, isServiceImport bool) *discoveryv1.EndpointSlice { var labels map[string]string diff --git a/release-notes/current.yaml b/release-notes/current.yaml index 50832849a8..0ba2e7b6d4 100644 --- a/release-notes/current.yaml +++ b/release-notes/current.yaml @@ -39,6 +39,7 @@ bug fixes: | Fixed bug in certificate SANs overlap detection in listeners. Fixed issue where EnvoyExtensionPolicy ExtProc body processing mode is set to FullDuplexStreamed, but trailers were not sent. Fixed validation issue where EnvoyExtensionPolicy ExtProc failOpen is true, and body processing mode FullDuplexStreamed is not rejected. + Add ConfigMap indexers for EnvoyExtensionPolicies to reconcile Lua changes # Enhancements that improve performance.