diff --git a/internal/cmd/egctl/translate.go b/internal/cmd/egctl/translate.go index 9c06ce3c79..089a675851 100644 --- a/internal/cmd/egctl/translate.go +++ b/internal/cmd/egctl/translate.go @@ -41,6 +41,13 @@ const ( irType = "ir" ) +type TranslationOptions struct { + GlobalRateLimitEnabled bool + EndpointRoutingDisabled bool + EnvoyPatchPolicyEnabled bool + BackendEnabled bool +} + type TranslationResult struct { resource.Resources XdsIR resource.XdsIRMap `json:"xdsIR,omitempty" yaml:"xdsIR,omitempty"` @@ -241,7 +248,13 @@ func translate(w io.Writer, inFile, inType string, outTypes []string, output, re } } if outType == xdsType { - res, err := TranslateGatewayAPIToXds(namespace, dnsDomain, resourceType, resources) + opts := &TranslationOptions{ + GlobalRateLimitEnabled: true, + EndpointRoutingDisabled: true, + EnvoyPatchPolicyEnabled: true, + BackendEnabled: true, + } + res, err := TranslateGatewayAPIToXds(namespace, dnsDomain, resourceType, resources, opts) if err != nil { return err } @@ -336,7 +349,7 @@ func translateGatewayAPIToGatewayAPI(resources *resource.Resources) (resource.Re return gRes.Resources, nil } -func TranslateGatewayAPIToXds(namespace, dnsDomain, resourceType string, resources *resource.Resources) (map[string]any, error) { +func TranslateGatewayAPIToXds(namespace, dnsDomain, resourceType string, resources *resource.Resources, opts *TranslationOptions) (map[string]any, error) { if resources.GatewayClass == nil { return nil, fmt.Errorf("the GatewayClass resource is required") } @@ -345,10 +358,10 @@ func TranslateGatewayAPIToXds(namespace, dnsDomain, resourceType string, resourc gTranslator := &gatewayapi.Translator{ GatewayControllerName: string(resources.GatewayClass.Spec.ControllerName), GatewayClassName: gwapiv1.ObjectName(resources.GatewayClass.Name), - GlobalRateLimitEnabled: true, - EndpointRoutingDisabled: true, - EnvoyPatchPolicyEnabled: true, - BackendEnabled: true, + GlobalRateLimitEnabled: opts.GlobalRateLimitEnabled, + EndpointRoutingDisabled: opts.EndpointRoutingDisabled, + EnvoyPatchPolicyEnabled: opts.EnvoyPatchPolicyEnabled, + BackendEnabled: opts.BackendEnabled, Logger: logging.DefaultLogger(io.Discard, egv1a1.LogLevelInfo), } gRes, _ := gTranslator.Translate(resources) diff --git a/internal/gatewayapi/backendtlspolicy.go b/internal/gatewayapi/backendtlspolicy.go index a1ee9045bd..2374ee466e 100644 --- a/internal/gatewayapi/backendtlspolicy.go +++ b/internal/gatewayapi/backendtlspolicy.go @@ -93,19 +93,20 @@ func (t *Translator) applyBackendTLSSetting( // If the backendRef is a Backend resource, we need to check if it has TLS settings. if KindDerefOr(backendRef.Kind, resource.KindService) == egv1a1.KindBackend { - backend := resources.GetBackend(backendNamespace, string(backendRef.Name)) + backend := t.GetBackend(backendNamespace, string(backendRef.Name)) if backend == nil { return nil, fmt.Errorf("backend %s not found", backendRef.Name) } if backend.Spec.TLS != nil { // Get the server certificate validation settings from Backend resource. - if backendValidationTLSConfig, err = t.processServerValidationTLSSettings(backend, resources); err != nil { + if backendValidationTLSConfig, err = t.processServerValidationTLSSettings(backend); err != nil { return nil, err } // Get the client certificate and common TLS settings from Backend resource. if backend.Spec.TLS.BackendTLSConfig != nil { - if backendClientTLSConfig, err = t.processClientTLSSettings(resources, backend.Spec.TLS.BackendTLSConfig, backend.Namespace, backend.Name, false); err != nil { + if backendClientTLSConfig, err = t.processClientTLSSettings( + backend.Spec.TLS.BackendTLSConfig, backend.Namespace, backend.Name, false); err != nil { return nil, err } } @@ -132,7 +133,8 @@ func (t *Translator) applyBackendTLSSetting( // Get the client certificate and common TLS settings from EnvoyProxy resource. if envoyProxy != nil && envoyProxy.Spec.BackendTLS != nil { - if envoyProxyClientTLSConfig, err = t.processClientTLSSettings(resources, envoyProxy.Spec.BackendTLS, envoyProxy.Namespace, envoyProxy.Name, true); err != nil { + if envoyProxyClientTLSConfig, err = t.processClientTLSSettings( + envoyProxy.Spec.BackendTLS, envoyProxy.Namespace, envoyProxy.Name, true); err != nil { return nil, err } } @@ -239,7 +241,6 @@ func mergeClientTLSConfigs( func (t *Translator) processServerValidationTLSSettings( backend *egv1a1.Backend, - resources *resource.Resources, ) (*ir.TLSUpstreamConfig, error) { tlsConfig := &ir.TLSUpstreamConfig{ InsecureSkipVerify: ptr.Deref(backend.Spec.TLS.InsecureSkipVerify, false), @@ -257,7 +258,7 @@ func (t *Translator) processServerValidationTLSSettings( Name: fmt.Sprintf("%s/%s-ca", backend.Name, backend.Namespace), } } else if len(backend.Spec.TLS.CACertificateRefs) > 0 { - caCert, err := getCaCertsFromCARefs(backend.Namespace, backend.Spec.TLS.CACertificateRefs, resources) + caCert, err := t.getCaCertsFromCARefs(backend.Namespace, backend.Spec.TLS.CACertificateRefs) if err != nil { return nil, err } @@ -276,12 +277,12 @@ func (t *Translator) processBackendTLSPolicy( parent gwapiv1.ParentReference, resources *resource.Resources, ) (*ir.TLSUpstreamConfig, error) { - policy := getBackendTLSPolicy(resources.BackendTLSPolicies, backendRef, backendNamespace, resources) + policy := t.getBackendTLSPolicy(resources.BackendTLSPolicies, backendRef, backendNamespace) if policy == nil { return nil, nil } - tlsBundle, err := getBackendTLSBundle(policy, resources) + tlsBundle, err := t.getBackendTLSBundle(policy) ancestorRefs := getAncestorRefs(policy) ancestorRefs = append(ancestorRefs, &parent) @@ -326,7 +327,11 @@ func (t *Translator) processBackendTLSPolicy( return tlsBundle, nil } -func (t *Translator) processClientTLSSettings(resources *resource.Resources, clientTLS *egv1a1.BackendTLSConfig, ownerNs, ownerName string, fromEnvoyProxy bool) (*ir.TLSConfig, error) { +func (t *Translator) processClientTLSSettings( + clientTLS *egv1a1.BackendTLSConfig, + ownerNs, ownerName string, + fromEnvoyProxy bool, +) (*ir.TLSConfig, error) { tlsConfig := &ir.TLSConfig{} if len(clientTLS.Ciphers) > 0 { @@ -365,7 +370,7 @@ func (t *Translator) processClientTLSSettings(resources *resource.Resources, cli err = fmt.Errorf("ClientCertificateRef Secret is not located in the same namespace as %s. Secret namespace: %s does not match %s namespace: %s", ownerResource, ns, ownerResource, ownerNs) return tlsConfig, err } - secret := resources.GetSecret(ns, string(clientTLS.ClientCertificateRef.Name)) + secret := t.GetSecret(ns, string(clientTLS.ClientCertificateRef.Name)) if secret == nil { err = fmt.Errorf( "failed to locate TLS secret for client auth: %s specified in %s %s", @@ -404,14 +409,13 @@ func backendTLSTargetMatched(policy *gwapiv1.BackendTLSPolicy, target gwapiv1.Lo return false } -func getBackendTLSPolicy( +func (t *Translator) getBackendTLSPolicy( policies []*gwapiv1.BackendTLSPolicy, backendRef gwapiv1.BackendObjectReference, backendNamespace string, - resources *resource.Resources, ) *gwapiv1.BackendTLSPolicy { // SectionName is port number for EG Backend object - target := getTargetBackendReference(backendRef, backendNamespace, resources) + target := t.getTargetBackendReference(backendRef, backendNamespace) for _, policy := range policies { if backendTLSTargetMatched(policy, target, backendNamespace) { return policy @@ -420,7 +424,7 @@ func getBackendTLSPolicy( return nil } -func getBackendTLSBundle(backendTLSPolicy *gwapiv1.BackendTLSPolicy, resources *resource.Resources) (*ir.TLSUpstreamConfig, error) { +func (t *Translator) getBackendTLSBundle(backendTLSPolicy *gwapiv1.BackendTLSPolicy) (*ir.TLSUpstreamConfig, error) { // Translate SubjectAltNames from gwapiv1a3 to ir subjectAltNames := make([]ir.SubjectAltName, 0, len(backendTLSPolicy.Spec.Validation.SubjectAltNames)) for _, san := range backendTLSPolicy.Spec.Validation.SubjectAltNames { @@ -448,7 +452,8 @@ func getBackendTLSBundle(backendTLSPolicy *gwapiv1.BackendTLSPolicy, resources * return tlsBundle, nil } - caCert, err := getCaCertsFromCARefs(backendTLSPolicy.Namespace, backendTLSPolicy.Spec.Validation.CACertificateRefs, resources) + caCert, err := t.getCaCertsFromCARefs( + backendTLSPolicy.Namespace, backendTLSPolicy.Spec.Validation.CACertificateRefs) if err != nil { return nil, err } @@ -459,14 +464,17 @@ func getBackendTLSBundle(backendTLSPolicy *gwapiv1.BackendTLSPolicy, resources * return tlsBundle, nil } -func getCaCertsFromCARefs(namespace string, caCertificates []gwapiv1.LocalObjectReference, resources *resource.Resources) ([]byte, error) { +func (t *Translator) getCaCertsFromCARefs( + namespace string, + caCertificates []gwapiv1.LocalObjectReference, +) ([]byte, error) { ca := "" for _, caRef := range caCertificates { kind := string(caRef.Kind) switch kind { case resource.KindConfigMap: - cm := resources.GetConfigMap(namespace, string(caRef.Name)) + cm := t.GetConfigMap(namespace, string(caRef.Name)) if cm != nil { if crt, dataOk := getOrFirstFromData(cm.Data, caCertKey); dataOk { if ca != "" { @@ -480,7 +488,7 @@ func getCaCertsFromCARefs(namespace string, caCertificates []gwapiv1.LocalObject return nil, fmt.Errorf("configmap %s not found in namespace %s", caRef.Name, namespace) } case resource.KindSecret: - secret := resources.GetSecret(namespace, string(caRef.Name)) + secret := t.GetSecret(namespace, string(caRef.Name)) if secret != nil { if crt, dataOk := getOrFirstFromData(secret.Data, caCertKey); dataOk { if ca != "" { @@ -494,7 +502,7 @@ func getCaCertsFromCARefs(namespace string, caCertificates []gwapiv1.LocalObject return nil, fmt.Errorf("secret %s not found in namespace %s", caRef.Name, namespace) } case resource.KindClusterTrustBundle: - ctb := resources.GetClusterTrustBundle(string(caRef.Name)) + ctb := t.GetClusterTrustBundle(string(caRef.Name)) if ctb != nil { if ca != "" { ca += "\n" diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go index 2dfef92eea..aaa3c8eeb9 100644 --- a/internal/gatewayapi/backendtrafficpolicy.go +++ b/internal/gatewayapi/backendtrafficpolicy.go @@ -34,7 +34,8 @@ const ( MaxConsistentHashTableSize = 5000011 // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#config-cluster-v3-cluster-maglevlbconfig ) -func (t *Translator) ProcessBackendTrafficPolicies(resources *resource.Resources, +func (t *Translator) ProcessBackendTrafficPolicies( + resources *resource.Resources, gateways []*GatewayContext, routes []RouteContext, xdsIR resource.XdsIRMap, @@ -105,7 +106,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(resources *resource.Resources res = append(res, policy) } - t.processBackendTrafficPolicyForRoute(resources, xdsIR, + t.processBackendTrafficPolicyForRoute(xdsIR, routeMap, gatewayRouteMap, gatewayPolicyMerged, gatewayPolicyMap, policy, currTarget) } } @@ -125,7 +126,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(resources *resource.Resources res = append(res, policy) } - t.processBackendTrafficPolicyForRoute(resources, xdsIR, + t.processBackendTrafficPolicyForRoute(xdsIR, routeMap, gatewayRouteMap, gatewayPolicyMerged, gatewayPolicyMap, policy, currTarget) } } @@ -144,7 +145,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(resources *resource.Resources handledPolicies[policyName] = policy res = append(res, policy) } - t.processBackendTrafficPolicyForGateway(resources, xdsIR, + t.processBackendTrafficPolicyForGateway(xdsIR, gatewayMap, gatewayRouteMap, gatewayPolicyMerged, policy, currTarget) } } @@ -163,7 +164,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(resources *resource.Resources handledPolicies[policyName] = policy res = append(res, policy) } - t.processBackendTrafficPolicyForGateway(resources, xdsIR, + t.processBackendTrafficPolicyForGateway(xdsIR, gatewayMap, gatewayRouteMap, gatewayPolicyMerged, policy, currTarget) } } @@ -223,7 +224,6 @@ func (t *Translator) buildGatewayPolicyMap( } func (t *Translator) processBackendTrafficPolicyForRoute( - resources *resource.Resources, xdsIR resource.XdsIRMap, routeMap map[policyTargetRouteKey]*policyRouteTargetContext, gatewayRouteMap *GatewayPolicyRouteMap, @@ -303,7 +303,7 @@ func (t *Translator) processBackendTrafficPolicyForRoute( if policy.Spec.MergeType == nil { // Set conditions for translation error if it got any - if err := t.translateBackendTrafficPolicyForRoute(policy, targetedRoute, currTarget, xdsIR, resources, nil, nil); err != nil { + if err := t.translateBackendTrafficPolicyForRoute(policy, targetedRoute, currTarget, xdsIR, nil, nil); err != nil { status.SetTranslationErrorForPolicyAncestors(&policy.Status, ancestorRefs, t.GatewayControllerName, @@ -331,7 +331,7 @@ func (t *Translator) processBackendTrafficPolicyForRoute( gwPolicy := gatewayPolicyMap[gwMapKey] if gwPolicy == nil && listenerPolicy == nil { // not found, fall back to the current policy - if err := t.translateBackendTrafficPolicyForRoute(policy, targetedRoute, currTarget, xdsIR, resources, &gwNN, &listener.Name); err != nil { + if err := t.translateBackendTrafficPolicyForRoute(policy, targetedRoute, currTarget, xdsIR, &gwNN, &listener.Name); err != nil { status.SetConditionForPolicyAncestor(&policy.Status, &ancestorRef, t.GatewayControllerName, @@ -351,7 +351,7 @@ func (t *Translator) processBackendTrafficPolicyForRoute( // merge with parent policy if err := t.translateBackendTrafficPolicyForRouteWithMerge( policy, parentPolicy, currTarget, gwNN, &listener.Name, - targetedRoute, xdsIR, resources, + targetedRoute, xdsIR, ); err != nil { status.SetConditionForPolicyAncestor(&policy.Status, &ancestorRef, @@ -418,7 +418,6 @@ func (t *Translator) processBackendTrafficPolicyForRoute( } func (t *Translator) processBackendTrafficPolicyForGateway( - resources *resource.Resources, xdsIR resource.XdsIRMap, gatewayMap map[types.NamespacedName]*policyGatewayTargetContext, gatewayRouteMap *GatewayPolicyRouteMap, @@ -453,7 +452,7 @@ func (t *Translator) processBackendTrafficPolicyForGateway( } // Set conditions for translation error if it got any - if err := t.translateBackendTrafficPolicyForGateway(policy, currTarget, targetedGateway, xdsIR, resources); err != nil { + if err := t.translateBackendTrafficPolicyForGateway(policy, currTarget, targetedGateway, xdsIR); err != nil { status.SetTranslationErrorForPolicyAncestor(&policy.Status, &ancestorRef, t.GatewayControllerName, @@ -620,11 +619,10 @@ func (t *Translator) translateBackendTrafficPolicyForRoute( route RouteContext, target gwapiv1.LocalPolicyTargetReferenceWithSectionName, xdsIR resource.XdsIRMap, - resources *resource.Resources, policyTargetGatewayNN *types.NamespacedName, policyTargetListener *gwapiv1.SectionName, ) error { - tf, errs := t.buildTrafficFeatures(policy, resources) + tf, errs := t.buildTrafficFeatures(policy) if tf == nil { // should not happen return nil @@ -647,7 +645,7 @@ func (t *Translator) translateBackendTrafficPolicyForRouteWithMerge( policy, parentPolicy *egv1a1.BackendTrafficPolicy, target gwapiv1.LocalPolicyTargetReferenceWithSectionName, policyTargetGatewayNN types.NamespacedName, policyTargetListener *gwapiv1.SectionName, route RouteContext, - xdsIR resource.XdsIRMap, resources *resource.Resources, + xdsIR resource.XdsIRMap, ) error { mergedPolicy, err := mergeBackendTrafficPolicy(policy, parentPolicy) if err != nil { @@ -655,7 +653,7 @@ func (t *Translator) translateBackendTrafficPolicyForRouteWithMerge( } // Build traffic features from the merged policy - tf, errs := t.buildTrafficFeatures(mergedPolicy, resources) + tf, errs := t.buildTrafficFeatures(mergedPolicy) if tf == nil { // should not happen return nil @@ -670,8 +668,8 @@ func (t *Translator) translateBackendTrafficPolicyForRouteWithMerge( // 2. Only gateway policy has rate limits - preserve gateway policy's rule names // 3. Only route policy has rate limits - use route policy's rule names (default behavior) if policy.Spec.RateLimit != nil && parentPolicy.Spec.RateLimit != nil { - tfGW, _ := t.buildTrafficFeatures(parentPolicy, resources) - tfRoute, _ := t.buildTrafficFeatures(policy, resources) + tfGW, _ := t.buildTrafficFeatures(parentPolicy) + tfRoute, _ := t.buildTrafficFeatures(policy) if tfGW != nil && tfRoute != nil && tfGW.RateLimit != nil && tfRoute.RateLimit != nil { @@ -685,7 +683,7 @@ func (t *Translator) translateBackendTrafficPolicyForRouteWithMerge( } } else if policy.Spec.RateLimit == nil && parentPolicy.Spec.RateLimit != nil { // Case 2: Only gateway policy has rate limits - preserve gateway policy's rule names - tfGW, _ := t.buildTrafficFeatures(parentPolicy, resources) + tfGW, _ := t.buildTrafficFeatures(parentPolicy) if tfGW != nil && tfGW.RateLimit != nil { // Use the gateway policy's rate limit with its original rule names tf.RateLimit = tfGW.RateLimit @@ -826,7 +824,7 @@ func mergeBackendTrafficPolicy(routePolicy, gwPolicy *egv1a1.BackendTrafficPolic return utils.Merge[*egv1a1.BackendTrafficPolicy](gwPolicy, routePolicy, *routePolicy.Spec.MergeType) } -func (t *Translator) buildTrafficFeatures(policy *egv1a1.BackendTrafficPolicy, resources *resource.Resources) (*ir.TrafficFeatures, error) { +func (t *Translator) buildTrafficFeatures(policy *egv1a1.BackendTrafficPolicy) (*ir.TrafficFeatures, error) { var ( rl *ir.RateLimit lb *ir.LoadBalancer @@ -891,7 +889,7 @@ func (t *Translator) buildTrafficFeatures(policy *egv1a1.BackendTrafficPolicy, r errs = errors.Join(errs, err) } - if ro, err = buildResponseOverride(policy, resources); err != nil { + if ro, err = t.buildResponseOverride(policy); err != nil { err = perr.WithMessage(err, "ResponseOverride") errs = errors.Join(errs, err) } @@ -934,9 +932,9 @@ func (t *Translator) buildTrafficFeatures(policy *egv1a1.BackendTrafficPolicy, r func (t *Translator) translateBackendTrafficPolicyForGateway( policy *egv1a1.BackendTrafficPolicy, target gwapiv1.LocalPolicyTargetReferenceWithSectionName, - gateway *GatewayContext, xdsIR resource.XdsIRMap, resources *resource.Resources, + gateway *GatewayContext, xdsIR resource.XdsIRMap, ) error { - tf, errs := t.buildTrafficFeatures(policy, resources) + tf, errs := t.buildTrafficFeatures(policy) if tf == nil { // should not happen return errs @@ -1426,7 +1424,7 @@ func buildRequestBuffer(spec *egv1a1.RequestBuffer) (*ir.RequestBuffer, error) { }, nil } -func buildResponseOverride(policy *egv1a1.BackendTrafficPolicy, resources *resource.Resources) (*ir.ResponseOverride, error) { +func (t *Translator) buildResponseOverride(policy *egv1a1.BackendTrafficPolicy) (*ir.ResponseOverride, error) { if len(policy.Spec.ResponseOverride) == 0 { return nil, nil } @@ -1487,7 +1485,7 @@ func buildResponseOverride(policy *egv1a1.BackendTrafficPolicy, resources *resou } var err error - response.Body, err = getCustomResponseBody(ro.Response.Body, resources, policy.Namespace) + response.Body, err = t.getCustomResponseBody(ro.Response.Body, policy.Namespace) if err != nil { return nil, err } @@ -1535,9 +1533,12 @@ func checkResponseBodySize(b []byte) error { return nil } -func getCustomResponseBody(body *egv1a1.CustomResponseBody, resources *resource.Resources, policyNs string) ([]byte, error) { +func (t *Translator) getCustomResponseBody( + body *egv1a1.CustomResponseBody, + policyNs string, +) ([]byte, error) { if body != nil && body.Type != nil && *body.Type == egv1a1.ResponseValueTypeValueRef { - cm := resources.GetConfigMap(policyNs, string(body.ValueRef.Name)) + cm := t.GetConfigMap(policyNs, string(body.ValueRef.Name)) if cm != nil { b, dataOk := cm.Data["response.body"] switch { diff --git a/internal/gatewayapi/clienttrafficpolicy.go b/internal/gatewayapi/clienttrafficpolicy.go index 9d50b62728..df831bc0a2 100644 --- a/internal/gatewayapi/clienttrafficpolicy.go +++ b/internal/gatewayapi/clienttrafficpolicy.go @@ -355,7 +355,8 @@ func validatePortOverlapForClientTrafficPolicy(l *ListenerContext, xds *ir.Xds, return nil } -func (t *Translator) translateClientTrafficPolicyForListener(policy *egv1a1.ClientTrafficPolicy, l *ListenerContext, +func (t *Translator) translateClientTrafficPolicyForListener( + policy *egv1a1.ClientTrafficPolicy, l *ListenerContext, xdsIR resource.XdsIRMap, infraIR resource.InfraIRMap, resources *resource.Resources, ) error { // Find IR @@ -756,7 +757,8 @@ func translateHealthCheckSettings(healthCheckSettings *egv1a1.HealthCheckSetting httpIR.HealthCheck = (*ir.HealthCheckSettings)(healthCheckSettings) } -func (t *Translator) buildListenerTLSParameters(policy *egv1a1.ClientTrafficPolicy, +func (t *Translator) buildListenerTLSParameters( + policy *egv1a1.ClientTrafficPolicy, irTLSConfig *ir.TLSConfig, resources *resource.Resources, ) (*ir.TLSConfig, error) { // Return if this listener isn't a TLS listener. There has to be @@ -865,7 +867,12 @@ func (t *Translator) buildListenerTLSParameters(policy *egv1a1.ClientTrafficPoli } // validateAndGetDataAtKeyInRef validates the secret object reference and gets the data at the key in the secret or configmap -func (t *Translator) validateAndGetDataAtKeyInRef(ref gwapiv1.SecretObjectReference, key string, resources *resource.Resources, from crossNamespaceFrom) ([]byte, error) { +func (t *Translator) validateAndGetDataAtKeyInRef( + ref gwapiv1.SecretObjectReference, + key string, + resources *resource.Resources, + from crossNamespaceFrom, +) ([]byte, error) { refKind := string(ptr.Deref(ref.Kind, resource.KindSecret)) switch refKind { case resource.KindSecret: @@ -891,7 +898,7 @@ func (t *Translator) validateAndGetDataAtKeyInRef(ref gwapiv1.SecretObjectRefere } return []byte(configMapData), nil case resource.KindClusterTrustBundle: - trustBundle := resources.GetClusterTrustBundle(string(ref.Name)) + trustBundle := t.GetClusterTrustBundle(string(ref.Name)) if trustBundle == nil { return nil, fmt.Errorf("ref ClusterTrustBundle [%s] not found", ref.Name) } diff --git a/internal/gatewayapi/contexts.go b/internal/gatewayapi/contexts.go index 8ee05a241d..41189f4327 100644 --- a/internal/gatewayapi/contexts.go +++ b/internal/gatewayapi/contexts.go @@ -6,7 +6,9 @@ package gatewayapi import ( + certificatesv1b1 "k8s.io/api/certificates/v1beta1" corev1 "k8s.io/api/core/v1" + discoveryv1 "k8s.io/api/discovery/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" @@ -14,10 +16,12 @@ import ( gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwapiv1a3 "sigs.k8s.io/gateway-api/apis/v1alpha3" + mcsapiv1a1 "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/gatewayapi/resource" "github.com/envoyproxy/gateway/internal/ir" + "github.com/envoyproxy/gateway/internal/utils" ) // GatewayContext wraps a Gateway and provides helper methods for @@ -661,3 +665,161 @@ func (d DirectBackendRef) GetBackendRef() *gwapiv1.BackendRef { func (d DirectBackendRef) GetFilters() any { return nil } + +type backendServiceKey struct { + kind string + namespace string + name string +} + +type TranslatorContext struct { + NamespaceMap map[types.NamespacedName]*corev1.Namespace + ServiceMap map[types.NamespacedName]*corev1.Service + ServiceImportMap map[types.NamespacedName]*mcsapiv1a1.ServiceImport + BackendMap map[types.NamespacedName]*egv1a1.Backend + SecretMap map[types.NamespacedName]*corev1.Secret + ConfigMapMap map[types.NamespacedName]*corev1.ConfigMap + ClusterTrustBundleMap map[types.NamespacedName]*certificatesv1b1.ClusterTrustBundle + EndpointSliceMap map[backendServiceKey][]*discoveryv1.EndpointSlice +} + +func (t *TranslatorContext) GetNamespace(name string) *corev1.Namespace { + if ns, ok := t.NamespaceMap[types.NamespacedName{Name: name}]; ok { + return ns + } + return nil +} + +func (t *TranslatorContext) SetNamespaces(namespaces []*corev1.Namespace) { + namespaceMap := make(map[types.NamespacedName]*corev1.Namespace, len(namespaces)) + for _, ns := range namespaces { + namespaceMap[types.NamespacedName{Name: ns.Name}] = ns + } + t.NamespaceMap = namespaceMap +} + +func (t *TranslatorContext) GetService(namespace, name string) *corev1.Service { + if svc, ok := t.ServiceMap[types.NamespacedName{Namespace: namespace, Name: name}]; ok { + return svc + } + return nil +} + +func (t *TranslatorContext) SetServices(svcs []*corev1.Service) { + serviceMap := make(map[types.NamespacedName]*corev1.Service, len(svcs)) + for _, svc := range svcs { + serviceMap[utils.NamespacedName(svc)] = svc + } + t.ServiceMap = serviceMap +} + +func (t *TranslatorContext) GetServiceImport(namespace, name string) *mcsapiv1a1.ServiceImport { + if svcImp, ok := t.ServiceImportMap[types.NamespacedName{Namespace: namespace, Name: name}]; ok { + return svcImp + } + return nil +} + +func (t *TranslatorContext) SetServiceImports(svcImps []*mcsapiv1a1.ServiceImport) { + serviceImportMap := make(map[types.NamespacedName]*mcsapiv1a1.ServiceImport, len(svcImps)) + for _, svcImp := range svcImps { + serviceImportMap[utils.NamespacedName(svcImp)] = svcImp + } + t.ServiceImportMap = serviceImportMap +} + +func (t *TranslatorContext) GetBackend(namespace, name string) *egv1a1.Backend { + if backend, ok := t.BackendMap[types.NamespacedName{Namespace: namespace, Name: name}]; ok { + return backend + } + return nil +} + +func (t *TranslatorContext) SetBackends(backends []*egv1a1.Backend) { + backendMap := make(map[types.NamespacedName]*egv1a1.Backend, len(backends)) + for _, backend := range backends { + backendMap[utils.NamespacedName(backend)] = backend + } + t.BackendMap = backendMap +} + +func (t *TranslatorContext) GetSecret(namespace, name string) *corev1.Secret { + if secret, ok := t.SecretMap[types.NamespacedName{Namespace: namespace, Name: name}]; ok { + return secret + } + return nil +} + +func (t *TranslatorContext) SetSecrets(secrets []*corev1.Secret) { + secretMap := make(map[types.NamespacedName]*corev1.Secret, len(secrets)) + for _, secret := range secrets { + secretMap[utils.NamespacedName(secret)] = secret + } + t.SecretMap = secretMap +} + +func (t *TranslatorContext) GetConfigMap(namespace, name string) *corev1.ConfigMap { + if configMap, ok := t.ConfigMapMap[types.NamespacedName{Namespace: namespace, Name: name}]; ok { + return configMap + } + return nil +} + +func (t *TranslatorContext) SetConfigMaps(configMaps []*corev1.ConfigMap) { + configMapMap := make(map[types.NamespacedName]*corev1.ConfigMap, len(configMaps)) + for _, configMap := range configMaps { + configMapMap[utils.NamespacedName(configMap)] = configMap + } + t.ConfigMapMap = configMapMap +} + +func (t *TranslatorContext) GetClusterTrustBundle(name string) *certificatesv1b1.ClusterTrustBundle { + if ctb, ok := t.ClusterTrustBundleMap[types.NamespacedName{Name: name}]; ok { + return ctb + } + return nil +} + +func (t *TranslatorContext) SetClusterTrustBundles(ctbs []*certificatesv1b1.ClusterTrustBundle) { + ctbMap := make(map[types.NamespacedName]*certificatesv1b1.ClusterTrustBundle, len(ctbs)) + for _, ctb := range ctbs { + ctbMap[types.NamespacedName{Name: ctb.Name}] = ctb + } + t.ClusterTrustBundleMap = ctbMap +} + +func (t *TranslatorContext) GetEndpointSlicesForBackend(svcNamespace, svcName, backendKind string) []*discoveryv1.EndpointSlice { + key := backendServiceKey{ + kind: backendKind, + namespace: svcNamespace, + name: svcName, + } + if slices, ok := t.EndpointSliceMap[key]; ok { + return slices + } + return nil +} + +func (t *TranslatorContext) SetEndpointSlicesForBackend(slices []*discoveryv1.EndpointSlice) { + t.EndpointSliceMap = make(map[backendServiceKey][]*discoveryv1.EndpointSlice) + + var kind, svcName string + for _, slice := range slices { + if name, ok := slice.Labels[discoveryv1.LabelServiceName]; ok { + kind = resource.KindService + svcName = name + } else if name, ok := slice.Labels[mcsapiv1a1.LabelServiceName]; ok { + kind = resource.KindServiceImport + svcName = name + } else { + continue + } + + key := backendServiceKey{ + kind: kind, + namespace: slice.Namespace, + name: svcName, + } + t.EndpointSliceMap[key] = append(t.EndpointSliceMap[key], slice) + } +} diff --git a/internal/gatewayapi/envoyextensionpolicy.go b/internal/gatewayapi/envoyextensionpolicy.go index 2284e5d3c5..aa773c4576 100644 --- a/internal/gatewayapi/envoyextensionpolicy.go +++ b/internal/gatewayapi/envoyextensionpolicy.go @@ -33,7 +33,8 @@ import ( // oci URL prefix const ociURLPrefix = "oci://" -func (t *Translator) ProcessEnvoyExtensionPolicies(envoyExtensionPolicies []*egv1a1.EnvoyExtensionPolicy, +func (t *Translator) ProcessEnvoyExtensionPolicies( + envoyExtensionPolicies []*egv1a1.EnvoyExtensionPolicy, gateways []*GatewayContext, routes []RouteContext, resources *resource.Resources, @@ -486,7 +487,7 @@ func (t *Translator) translateEnvoyExtensionPolicyForRoute( continue } - if luas, luaError = t.buildLuas(policy, resources, gtwCtx.envoyProxy); luaError != nil { + if luas, luaError = t.buildLuas(policy, gtwCtx.envoyProxy); luaError != nil { luaError = perr.WithMessage(luaError, "Lua") errs = errors.Join(errs, luaError) } @@ -575,7 +576,7 @@ func (t *Translator) translateEnvoyExtensionPolicyForGateway( wasmError = perr.WithMessage(wasmError, "Wasm") errs = errors.Join(errs, wasmError) } - if luas, luaError = t.buildLuas(policy, resources, gateway.envoyProxy); luaError != nil { + if luas, luaError = t.buildLuas(policy, gateway.envoyProxy); luaError != nil { luaError = perr.WithMessage(luaError, "Lua") errs = errors.Join(errs, luaError) } @@ -640,7 +641,10 @@ func (t *Translator) translateEnvoyExtensionPolicyForGateway( return errs } -func (t *Translator) buildLuas(policy *egv1a1.EnvoyExtensionPolicy, resources *resource.Resources, envoyProxy *egv1a1.EnvoyProxy) ([]ir.Lua, error) { +func (t *Translator) buildLuas( + policy *egv1a1.EnvoyExtensionPolicy, + envoyProxy *egv1a1.EnvoyProxy, +) ([]ir.Lua, error) { if policy == nil { return nil, nil } @@ -649,7 +653,7 @@ func (t *Translator) buildLuas(policy *egv1a1.EnvoyExtensionPolicy, resources *r for idx, ep := range policy.Spec.Lua { name := irConfigNameForLua(policy, idx) - luaIR, err := t.buildLua(name, policy, ep, resources, envoyProxy) + luaIR, err := t.buildLua(name, policy, ep, envoyProxy) if err != nil { return nil, err } @@ -662,13 +666,12 @@ func (t *Translator) buildLua( name string, policy *egv1a1.EnvoyExtensionPolicy, lua egv1a1.Lua, - resources *resource.Resources, envoyProxy *egv1a1.EnvoyProxy, ) (*ir.Lua, error) { var luaCode *string var err error if lua.Type == egv1a1.LuaValueTypeValueRef { - luaCode, err = getLuaBodyFromLocalObjectReference(lua.ValueRef, resources, policy.Namespace) + luaCode, err = t.getLuaBodyFromLocalObjectReference(lua.ValueRef, policy.Namespace) } else { luaCode = lua.Inline } @@ -686,8 +689,11 @@ func (t *Translator) buildLua( } // getLuaBodyFromLocalObjectReference assumes the local object reference points to a Kubernetes ConfigMap -func getLuaBodyFromLocalObjectReference(valueRef *gwapiv1.LocalObjectReference, resources *resource.Resources, policyNs string) (*string, error) { - cm := resources.GetConfigMap(policyNs, string(valueRef.Name)) +func (t *Translator) getLuaBodyFromLocalObjectReference( + valueRef *gwapiv1.LocalObjectReference, + policyNs string, +) (*string, error) { + cm := t.GetConfigMap(policyNs, string(valueRef.Name)) if cm != nil { b, dataOk := cm.Data["lua"] switch { diff --git a/internal/gatewayapi/ext_service.go b/internal/gatewayapi/ext_service.go index 397d26e9c7..13ce8ff07e 100644 --- a/internal/gatewayapi/ext_service.go +++ b/internal/gatewayapi/ext_service.go @@ -110,12 +110,12 @@ func (t *Translator) processExtServiceDestination( switch KindDerefOr(backendRef.Kind, resource.KindService) { case resource.KindService: - ds, err = t.processServiceDestinationSetting(settingName, backendRef.BackendObjectReference, backendNamespace, protocol, resources, envoyProxy) + ds, err = t.processServiceDestinationSetting(settingName, backendRef.BackendObjectReference, backendNamespace, protocol, envoyProxy) if err != nil { return nil, err } case resource.KindServiceImport: - ds, err = t.processServiceImportDestinationSetting(settingName, backendRef.BackendObjectReference, backendNamespace, protocol, resources, envoyProxy) + ds, err = t.processServiceImportDestinationSetting(settingName, backendRef.BackendObjectReference, backendNamespace, protocol, envoyProxy) if err != nil { return nil, err } @@ -123,7 +123,7 @@ func (t *Translator) processExtServiceDestination( if !t.BackendEnabled { return nil, fmt.Errorf("resource %s of type Backend cannot be used since Backend is disabled in Envoy Gateway configuration", string(backendRef.Name)) } - ds = t.processBackendDestinationSetting(settingName, backendRef.BackendObjectReference, backendNamespace, protocol, resources) + ds = t.processBackendDestinationSetting(settingName, backendRef.BackendObjectReference, backendNamespace, protocol) // Dynamic resolver destinations are not supported for none-route destinations if ds.IsDynamicResolver { return nil, errors.New("dynamic resolver destinations are not supported") diff --git a/internal/gatewayapi/filters.go b/internal/gatewayapi/filters.go index c4ed21c26e..d4666aa90c 100644 --- a/internal/gatewayapi/filters.go +++ b/internal/gatewayapi/filters.go @@ -70,7 +70,8 @@ type HTTPFilterIR struct { var HeaderValueRegexp = regexp.MustCompile(`^[!-~]+([\t ]?[!-~]+)*$`) // ProcessHTTPFilters translates gateway api http filters to IRs. -func (t *Translator) ProcessHTTPFilters(parentRef *RouteParentContext, +func (t *Translator) ProcessHTTPFilters( + parentRef *RouteParentContext, route RouteContext, filters []gwapiv1.HTTPRouteFilter, ruleIdx int, @@ -131,7 +132,8 @@ func (t *Translator) ProcessHTTPFilters(parentRef *RouteParentContext, } // ProcessGRPCFilters translates gateway api grpc filters to IRs. -func (t *Translator) ProcessGRPCFilters(parentRef *RouteParentContext, +func (t *Translator) ProcessGRPCFilters( + parentRef *RouteParentContext, route RouteContext, filters []gwapiv1.GRPCRouteFilter, resources *resource.Resources, @@ -853,7 +855,7 @@ func (t *Translator) processExtensionRefHTTPFilter(extFilter *gwapiv1.LocalObjec dr := &ir.CustomResponse{} if hrf.Spec.DirectResponse.Body != nil { var err error - if dr.Body, err = getCustomResponseBody(hrf.Spec.DirectResponse.Body, resources, filterNs); err != nil { + if dr.Body, err = t.getCustomResponseBody(hrf.Spec.DirectResponse.Body, filterNs); err != nil { return t.processInvalidHTTPFilter(string(extFilter.Kind), filterContext, err) } } diff --git a/internal/gatewayapi/globalresources.go b/internal/gatewayapi/globalresources.go index 2aff4718f8..4dcaab1449 100644 --- a/internal/gatewayapi/globalresources.go +++ b/internal/gatewayapi/globalresources.go @@ -41,7 +41,7 @@ func (t *Translator) ProcessGlobalResources(resources *resource.Resources, xdsIR // Get the envoy client TLS secret. It is used for envoy to establish a TLS connection with control plane components, // including the rate limit server and the wasm HTTP server. - envoyTLSSecret := resources.GetSecret(t.ControllerNamespace, envoyTLSSecretName) + envoyTLSSecret := t.GetSecret(t.ControllerNamespace, envoyTLSSecretName) if envoyTLSSecret == nil { return fmt.Errorf("envoy TLS secret %s/%s not found", t.ControllerNamespace, envoyTLSSecretName) } @@ -85,7 +85,7 @@ func (t *Translator) processServiceClusterForGateway(gateway *GatewayContext, re Namespace: NamespacePtr(svcCluster.Namespace), Port: PortNumPtr(svcCluster.Spec.Ports[0].Port), } - dst, err := t.processServiceDestinationSetting(irKey, bRef, svcCluster.Namespace, ir.AppProtocol(svcCluster.Spec.Ports[0].Protocol), resources, resources.EnvoyProxyForGatewayClass) + dst, err := t.processServiceDestinationSetting(irKey, bRef, svcCluster.Namespace, ir.AppProtocol(svcCluster.Spec.Ports[0].Protocol), resources.EnvoyProxyForGatewayClass) if err != nil { return "", nil } diff --git a/internal/gatewayapi/listener.go b/internal/gatewayapi/listener.go index 75ef2aab92..2332f444dc 100644 --- a/internal/gatewayapi/listener.go +++ b/internal/gatewayapi/listener.go @@ -835,10 +835,10 @@ func (t *Translator) processBackendRefs(name string, backendCluster egv1a1.Backe kind := KindDerefOr(ref.Kind, resource.KindService) switch kind { case resource.KindService: - if err := validateBackendRefService(ref.BackendObjectReference, resources, ns, corev1.ProtocolTCP); err != nil { + if err := t.validateBackendRefService(ref.BackendObjectReference, ns, corev1.ProtocolTCP); err != nil { return nil, nil, err } - ds, err := t.processServiceDestinationSetting(name, ref.BackendObjectReference, ns, ir.TCP, resources, envoyProxy) + ds, err := t.processServiceDestinationSetting(name, ref.BackendObjectReference, ns, ir.TCP, envoyProxy) if err != nil { return nil, nil, err } @@ -847,7 +847,7 @@ func (t *Translator) processBackendRefs(name string, backendCluster egv1a1.Backe if err := t.validateBackendRefBackend(ref.BackendObjectReference, resources, ns, true); err != nil { return nil, nil, err } - ds := t.processBackendDestinationSetting(name, ref.BackendObjectReference, ns, ir.TCP, resources) + ds := t.processBackendDestinationSetting(name, ref.BackendObjectReference, ns, ir.TCP) // Dynamic resolver destinations are not supported for none-route destinations if ds.IsDynamicResolver { return nil, nil, errors.New("dynamic resolver destinations are not supported") diff --git a/internal/gatewayapi/listener_test.go b/internal/gatewayapi/listener_test.go index e0533178c9..cdbf8134f1 100644 --- a/internal/gatewayapi/listener_test.go +++ b/internal/gatewayapi/listener_test.go @@ -846,6 +846,7 @@ func TestProcessTracingServiceName(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { translator := &Translator{} + translatorContext := &TranslatorContext{} resources := &resource.Resources{} // Mock service to resolve BackendRefs @@ -895,6 +896,9 @@ func TestProcessTracingServiceName(t *testing.T) { }, }, ) + translatorContext.SetServices(resources.Services) + translatorContext.SetEndpointSlicesForBackend(resources.EndpointSlices) + translator.TranslatorContext = translatorContext result, err := translator.processTracing(tc.gateway, tc.envoyProxy, tc.mergeGateways, resources) diff --git a/internal/gatewayapi/resource/resource.go b/internal/gatewayapi/resource/resource.go index f06fbfe291..49291c883b 100644 --- a/internal/gatewayapi/resource/resource.go +++ b/internal/gatewayapi/resource/resource.go @@ -93,16 +93,6 @@ func NewResources() *Resources { } } -func (r *Resources) GetNamespace(name string) *corev1.Namespace { - for _, ns := range r.Namespaces { - if ns.Name == name { - return ns - } - } - - return nil -} - func (r *Resources) GetEnvoyProxy(namespace, name string) *egv1a1.EnvoyProxy { for _, ep := range r.EnvoyProxiesForGateways { if ep.Namespace == namespace && ep.Name == name { @@ -113,16 +103,6 @@ func (r *Resources) GetEnvoyProxy(namespace, name string) *egv1a1.EnvoyProxy { return nil } -// GetService returns the Service with the given namespace and name. -func (r *Resources) GetService(namespace, name string) *corev1.Service { - for _, svc := range r.Services { - if svc.Namespace == namespace && svc.Name == name { - return svc - } - } - return nil -} - // GetServiceByLabels returns the Service matching the given labels and namespace target. func (r *Resources) GetServiceByLabels(labels map[string]string, namespace string) *corev1.Service { for _, svc := range r.Services { @@ -137,56 +117,9 @@ func (r *Resources) GetServiceByLabels(labels map[string]string, namespace strin return nil } -func (r *Resources) GetServiceImport(namespace, name string) *mcsapiv1a1.ServiceImport { - for _, svcImp := range r.ServiceImports { - if svcImp.Namespace == namespace && svcImp.Name == name { - return svcImp - } - } - - return nil -} - -func (r *Resources) GetBackend(namespace, name string) *egv1a1.Backend { - for _, be := range r.Backends { - if be.Namespace == namespace && be.Name == name { - return be - } - } - - return nil -} - -func (r *Resources) GetSecret(namespace, name string) *corev1.Secret { - for _, secret := range r.Secrets { - if secret.Namespace == namespace && secret.Name == name { - return secret - } - } - - return nil -} - -func (r *Resources) GetClusterTrustBundle(name string) *certificatesv1b1.ClusterTrustBundle { - for _, ctb := range r.ClusterTrustBundles { - if ctb.Name == name { - return ctb - } - } - - return nil -} - -func (r *Resources) GetConfigMap(namespace, name string) *corev1.ConfigMap { - for _, configMap := range r.ConfigMaps { - if configMap.Namespace == namespace && configMap.Name == name { - return configMap - } - } - - return nil -} - +// TODO: +// this method is only used in testfile. +// SHOULD be removed after refactoring the gatewayapi package structure to resolve the cyclic import issue. func (r *Resources) GetEndpointSlicesForBackend(svcNamespace, svcName, backendKind string) []*discoveryv1.EndpointSlice { var endpointSlices []*discoveryv1.EndpointSlice for _, endpointSlice := range r.EndpointSlices { diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index 18e4f41e09..faf070b6d9 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -68,7 +68,7 @@ func (t *Translator) ProcessHTTPRoutes(httpRoutes []*gwapiv1.HTTPRoute, gateways // Find out if this route attaches to one of our Gateway's listeners, // and if so, get the list of listeners that allow it to attach for each // parentRef. - relevantRoute := t.processAllowedListenersForParentRefs(httpRoute, gateways, resources) + relevantRoute := t.processAllowedListenersForParentRefs(httpRoute, gateways) if !relevantRoute { continue } @@ -95,7 +95,7 @@ func (t *Translator) ProcessGRPCRoutes(grpcRoutes []*gwapiv1.GRPCRoute, gateways // Find out if this route attaches to one of our Gateway's listeners, // and if so, get the list of listeners that allow it to attach for each // parentRef. - relevantRoute := t.processAllowedListenersForParentRefs(grpcRoute, gateways, resources) + relevantRoute := t.processAllowedListenersForParentRefs(grpcRoute, gateways) if !relevantRoute { continue } @@ -1229,7 +1229,7 @@ func (t *Translator) ProcessTLSRoutes(tlsRoutes []*gwapiv1a3.TLSRoute, gateways // Find out if this route attaches to one of our Gateway's listeners, // and if so, get the list of listeners that allow it to attach for each // parentRef. - relevantRoute := t.processAllowedListenersForParentRefs(tlsRoute, gateways, resources) + relevantRoute := t.processAllowedListenersForParentRefs(tlsRoute, gateways) if !relevantRoute { continue } @@ -1377,7 +1377,7 @@ func (t *Translator) ProcessUDPRoutes(udpRoutes []*gwapiv1a2.UDPRoute, gateways // Find out if this route attaches to one of our Gateway's listeners, // and if so, get the list of listeners that allow it to attach for each // parentRef. - relevantRoute := t.processAllowedListenersForParentRefs(udpRoute, gateways, resources) + relevantRoute := t.processAllowedListenersForParentRefs(udpRoute, gateways) if !relevantRoute { continue } @@ -1528,7 +1528,7 @@ func (t *Translator) ProcessTCPRoutes(tcpRoutes []*gwapiv1a2.TCPRoute, gateways // Find out if this route attaches to one of our Gateway's listeners, // and if so, get the list of listeners that allow it to attach for each // parentRef. - relevantRoute := t.processAllowedListenersForParentRefs(tcpRoute, gateways, resources) + relevantRoute := t.processAllowedListenersForParentRefs(tcpRoute, gateways) if !relevantRoute { continue } @@ -1739,20 +1739,20 @@ func (t *Translator) processDestination(name string, backendRefContext BackendRe switch KindDerefOr(backendRef.Kind, resource.KindService) { case resource.KindServiceImport: - ds, err = t.processServiceImportDestinationSetting(name, backendRef.BackendObjectReference, backendNamespace, protocol, resources, envoyProxy) + ds, err = t.processServiceImportDestinationSetting(name, backendRef.BackendObjectReference, backendNamespace, protocol, envoyProxy) if err != nil { return emptyDS, nil, err } case resource.KindService: - ds, err = t.processServiceDestinationSetting(name, backendRef.BackendObjectReference, backendNamespace, protocol, resources, envoyProxy) + ds, err = t.processServiceDestinationSetting(name, backendRef.BackendObjectReference, backendNamespace, protocol, envoyProxy) if err != nil { return emptyDS, nil, err } - svc := resources.GetService(backendNamespace, string(backendRef.Name)) + svc := t.GetService(backendNamespace, string(backendRef.Name)) ds.IPFamily = getServiceIPFamily(svc) ds.PreferLocal = processPreferLocalZone(svc) case egv1a1.KindBackend: - ds = t.processBackendDestinationSetting(name, backendRef.BackendObjectReference, backendNamespace, protocol, resources) + ds = t.processBackendDestinationSetting(name, backendRef.BackendObjectReference, backendNamespace, protocol) default: // Handle custom backend resources defined in extension manager if t.isCustomBackendResource(backendRef.Group, KindDerefOr(backendRef.Kind, resource.KindService)) { @@ -1835,7 +1835,6 @@ func (t *Translator) processServiceImportDestinationSetting( backendRef gwapiv1.BackendObjectReference, backendNamespace string, protocol ir.AppProtocol, - resources *resource.Resources, envoyProxy *egv1a1.EnvoyProxy, ) (*ir.DestinationSetting, status.Error) { var ( @@ -1843,7 +1842,7 @@ func (t *Translator) processServiceImportDestinationSetting( addrType *ir.DestinationAddressType ) - serviceImport := resources.GetServiceImport(backendNamespace, string(backendRef.Name)) + serviceImport := t.GetServiceImport(backendNamespace, string(backendRef.Name)) var servicePort mcsapiv1a1.ServicePort for _, port := range serviceImport.Spec.Ports { if port.Port == *backendRef.Port { @@ -1858,7 +1857,7 @@ func (t *Translator) processServiceImportDestinationSetting( // Route to endpoints by default if !t.IsEnvoyServiceRouting(envoyProxy) { - endpointSlices := resources.GetEndpointSlicesForBackend(backendNamespace, string(backendRef.Name), resource.KindServiceImport) + endpointSlices := t.GetEndpointSlicesForBackend(backendNamespace, string(backendRef.Name), resource.KindServiceImport) endpoints, addrType = getIREndpointsFromEndpointSlices(endpointSlices, servicePort.Name, getServicePortProtocol(servicePort.Protocol)) if len(endpoints) == 0 { return nil, status.NewRouteStatusError( @@ -1868,7 +1867,7 @@ func (t *Translator) processServiceImportDestinationSetting( } } else { // Fall back to Service ClusterIP routing - backendIps := resources.GetServiceImport(backendNamespace, string(backendRef.Name)).Spec.IPs + backendIps := t.GetServiceImport(backendNamespace, string(backendRef.Name)).Spec.IPs for _, ip := range backendIps { ep := ir.NewDestEndpoint(nil, ip, uint32(*backendRef.Port), false, nil) endpoints = append(endpoints, ep) @@ -1889,7 +1888,6 @@ func (t *Translator) processServiceDestinationSetting( backendRef gwapiv1.BackendObjectReference, backendNamespace string, protocol ir.AppProtocol, - resources *resource.Resources, envoyProxy *egv1a1.EnvoyProxy, ) (*ir.DestinationSetting, status.Error) { var ( @@ -1897,7 +1895,7 @@ func (t *Translator) processServiceDestinationSetting( addrType *ir.DestinationAddressType ) - service := resources.GetService(backendNamespace, string(backendRef.Name)) + service := t.GetService(backendNamespace, string(backendRef.Name)) var servicePort corev1.ServicePort for _, port := range service.Spec.Ports { if port.Port == *backendRef.Port { @@ -1913,7 +1911,7 @@ func (t *Translator) processServiceDestinationSetting( // Route to endpoints by default if !t.IsEnvoyServiceRouting(envoyProxy) { - endpointSlices := resources.GetEndpointSlicesForBackend(backendNamespace, string(backendRef.Name), KindDerefOr(backendRef.Kind, resource.KindService)) + endpointSlices := t.GetEndpointSlicesForBackend(backendNamespace, string(backendRef.Name), KindDerefOr(backendRef.Kind, resource.KindService)) endpoints, addrType = getIREndpointsFromEndpointSlices(endpointSlices, servicePort.Name, getServicePortProtocol(servicePort.Protocol)) if len(endpoints) == 0 { return nil, status.NewRouteStatusError( @@ -2051,7 +2049,10 @@ func inspectAppProtocolByRouteKind(kind gwapiv1.Kind) ir.AppProtocol { // processAllowedListenersForParentRefs finds out if the route attaches to one of our // Gateways' listeners, and if so, gets the list of listeners that allow it to // attach for each parentRef. -func (t *Translator) processAllowedListenersForParentRefs(routeContext RouteContext, gateways []*GatewayContext, resources *resource.Resources) bool { +func (t *Translator) processAllowedListenersForParentRefs( + routeContext RouteContext, + gateways []*GatewayContext, +) bool { var relevantRoute bool ns := gwapiv1.Namespace(routeContext.GetNamespace()) for _, parentRef := range GetParentReferences(routeContext) { @@ -2084,7 +2085,7 @@ func (t *Translator) processAllowedListenersForParentRefs(routeContext RouteCont for _, listener := range selectedListeners { acceptedKind := routeContext.GetRouteType() if listener.AllowsKind(gwapiv1.RouteGroupKind{Group: GroupPtr(gwapiv1.GroupName), Kind: acceptedKind}) && - listener.AllowsNamespace(resources.GetNamespace(routeContext.GetNamespace())) { + listener.AllowsNamespace(t.GetNamespace(routeContext.GetNamespace())) { allowedListeners = append(allowedListeners, listener) } } @@ -2239,7 +2240,10 @@ func (t *Translator) processBackendExtensions( return nil } -func getTargetBackendReference(backendRef gwapiv1.BackendObjectReference, backendNamespace string, resources *resource.Resources) gwapiv1.LocalPolicyTargetReferenceWithSectionName { +func (t *Translator) getTargetBackendReference( + backendRef gwapiv1.BackendObjectReference, + backendNamespace string, +) gwapiv1.LocalPolicyTargetReferenceWithSectionName { ref := gwapiv1.LocalPolicyTargetReferenceWithSectionName{ LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{ Group: func() gwapiv1.Group { @@ -2263,7 +2267,7 @@ func getTargetBackendReference(backendRef gwapiv1.BackendObjectReference, backen switch { case backendRef.Kind == nil || *backendRef.Kind == resource.KindService: - if service := resources.GetService(backendNamespace, string(backendRef.Name)); service != nil { + if service := t.GetService(backendNamespace, string(backendRef.Name)); service != nil { for _, port := range service.Spec.Ports { if port.Port == *backendRef.Port { if port.Name != "" { @@ -2275,7 +2279,7 @@ func getTargetBackendReference(backendRef gwapiv1.BackendObjectReference, backen } case *backendRef.Kind == resource.KindServiceImport: - if si := resources.GetServiceImport(backendNamespace, string(backendRef.Name)); si != nil { + if si := t.GetServiceImport(backendNamespace, string(backendRef.Name)); si != nil { for _, port := range si.Spec.Ports { if port.Port == *backendRef.Port { if port.Name != "" { @@ -2299,12 +2303,11 @@ func (t *Translator) processBackendDestinationSetting( backendRef gwapiv1.BackendObjectReference, backendNamespace string, protocol ir.AppProtocol, - resources *resource.Resources, ) *ir.DestinationSetting { var dstAddrType *ir.DestinationAddressType addrTypeMap := make(map[ir.DestinationAddressType]int) - backend := resources.GetBackend(backendNamespace, string(backendRef.Name)) + backend := t.GetBackend(backendNamespace, string(backendRef.Name)) for _, ap := range backend.Spec.AppProtocols { protocol = backendAppProtocolToIRAppProtocol(ap, protocol) } diff --git a/internal/gatewayapi/securitypolicy.go b/internal/gatewayapi/securitypolicy.go index 118fd164c3..dc4f2db030 100644 --- a/internal/gatewayapi/securitypolicy.go +++ b/internal/gatewayapi/securitypolicy.go @@ -50,7 +50,8 @@ const ( oidcHMACSecretKey = "hmac-secret" ) -func (t *Translator) ProcessSecurityPolicies(securityPolicies []*egv1a1.SecurityPolicy, +func (t *Translator) ProcessSecurityPolicies( + securityPolicies []*egv1a1.SecurityPolicy, gateways []*GatewayContext, routes []RouteContext, resources *resource.Resources, @@ -1035,7 +1036,7 @@ func (t *Translator) buildJWT( } provider.RemoteJWKS = remoteJWKS } else { - localJWKS, err := t.buildLocalJWKS(policy, p.LocalJWKS, resources) + localJWKS, err := t.buildLocalJWKS(policy, p.LocalJWKS) if err != nil { return nil, err } @@ -1192,7 +1193,6 @@ func (t *Translator) buildRemoteJWKS( func (t *Translator) buildLocalJWKS( policy *egv1a1.SecurityPolicy, localJWKS *egv1a1.LocalJWKS, - resources *resource.Resources, ) (string, error) { jwksType := egv1a1.LocalJWKSTypeInline if localJWKS.Type != nil { @@ -1200,7 +1200,7 @@ func (t *Translator) buildLocalJWKS( } if jwksType == egv1a1.LocalJWKSTypeValueRef { - cm := resources.GetConfigMap(policy.Namespace, string(localJWKS.ValueRef.Name)) + cm := t.GetConfigMap(policy.Namespace, string(localJWKS.ValueRef.Name)) if cm == nil { return "", fmt.Errorf("local JWKS ConfigMap %s/%s not found", policy.Namespace, localJWKS.ValueRef.Name) } @@ -1273,8 +1273,7 @@ func (t *Translator) buildOIDC( return nil, fmt.Errorf("client ID must be specified in OIDC policy %s/%s", policy.Namespace, policy.Name) } - if clientSecret, err = t.validateSecretRef( - false, from, oidc.ClientSecret, resources); err != nil { + if clientSecret, err = t.validateSecretRef(false, from, oidc.ClientSecret, resources); err != nil { return nil, err } @@ -1321,7 +1320,7 @@ func (t *Translator) buildOIDC( // HMAC secret is generated by the CertGen job and stored in a secret // We need to rotate the HMAC secret in the future, probably the same // way we rotate the certs generated by the CertGen job. - hmacSecret := resources.GetSecret(t.ControllerNamespace, oidcHMACSecretName) + hmacSecret := t.GetSecret(t.ControllerNamespace, oidcHMACSecretName) if hmacSecret == nil { return nil, fmt.Errorf("HMAC secret %s/%s not found", t.ControllerNamespace, oidcHMACSecretName) } @@ -1380,7 +1379,11 @@ func (t *Translator) buildOIDC( return irOIDC, nil } -func (t *Translator) buildOIDCProvider(policy *egv1a1.SecurityPolicy, resources *resource.Resources, envoyProxy *egv1a1.EnvoyProxy) (*ir.OIDCProvider, error) { +func (t *Translator) buildOIDCProvider( + policy *egv1a1.SecurityPolicy, + resources *resource.Resources, + envoyProxy *egv1a1.EnvoyProxy, +) (*ir.OIDCProvider, error) { var ( provider = policy.Spec.OIDC.Provider tokenEndpoint string @@ -1411,7 +1414,8 @@ func (t *Translator) buildOIDCProvider(policy *egv1a1.SecurityPolicy, resources } if len(provider.BackendRefs) > 0 { - if rd, err = t.translateExtServiceBackendRefs(policy, provider.BackendRefs, protocol, resources, envoyProxy, "oidc", 0); err != nil { + if rd, err = t.translateExtServiceBackendRefs( + policy, provider.BackendRefs, protocol, resources, envoyProxy, "oidc", 0); err != nil { return nil, err } } @@ -1705,8 +1709,7 @@ func (t *Translator) buildAPIKeyAuth( seenClients := make(sets.Set[string]) for _, ref := range policy.Spec.APIKeyAuth.CredentialRefs { - credentialsSecret, err := t.validateSecretRef( - false, from, ref, resources) + credentialsSecret, err := t.validateSecretRef(false, from, ref, resources) if err != nil { return nil, err } @@ -1761,8 +1764,7 @@ func (t *Translator) buildBasicAuth( kind: resource.KindSecurityPolicy, namespace: policy.Namespace, } - if usersSecret, err = t.validateSecretRef( - false, from, basicAuth.Users, resources); err != nil { + if usersSecret, err = t.validateSecretRef(false, from, basicAuth.Users, resources); err != nil { return nil, err } @@ -1868,7 +1870,8 @@ func (t *Translator) buildExtAuth( } } - if rd, err = t.translateExtServiceBackendRefs(policy, backendRefs, protocol, resources, envoyProxy, "extauth", 0); err != nil { + if rd, err = t.translateExtServiceBackendRefs( + policy, backendRefs, protocol, resources, envoyProxy, "extauth", 0); err != nil { return nil, err } @@ -1878,7 +1881,7 @@ func (t *Translator) buildExtAuth( // When translated to XDS, the authority is used on the filter level not on the cluster level. // There's no way to translate to XDS and use a different authority for each backendref if authority == "" { - authority = backendRefAuthority(resources, &backendRef.BackendObjectReference, policy) + authority = t.backendRefAuthority(&backendRef.BackendObjectReference, policy) } } @@ -1931,7 +1934,10 @@ func parseExtAuthTimeout(timeout *gwapiv1.Duration) *metav1.Duration { } } -func backendRefAuthority(resources *resource.Resources, backendRef *gwapiv1.BackendObjectReference, policy *egv1a1.SecurityPolicy) string { +func (t *Translator) backendRefAuthority( + backendRef *gwapiv1.BackendObjectReference, + policy *egv1a1.SecurityPolicy, +) string { if backendRef == nil { return "" } @@ -1939,7 +1945,7 @@ func backendRefAuthority(resources *resource.Resources, backendRef *gwapiv1.Back backendNamespace := NamespaceDerefOr(backendRef.Namespace, policy.Namespace) backendKind := KindDerefOr(backendRef.Kind, resource.KindService) if backendKind == resource.KindBackend { - backend := resources.GetBackend(backendNamespace, string(backendRef.Name)) + backend := t.GetBackend(backendNamespace, string(backendRef.Name)) if backend != nil { // TODO: exists multi FQDN endpoints? for _, ep := range backend.Spec.Endpoints { diff --git a/internal/gatewayapi/securitypolicy_test.go b/internal/gatewayapi/securitypolicy_test.go index 16067ef8a7..c27159e05d 100644 --- a/internal/gatewayapi/securitypolicy_test.go +++ b/internal/gatewayapi/securitypolicy_test.go @@ -879,6 +879,7 @@ func hasParentFalseCondition(p *egv1a1.SecurityPolicy) bool { // --- TCP branch: validateSecurityPolicyForTCP(...) returns err -> SetTranslationErrorForPolicyAncestors(...) + return func Test_SecurityPolicy_TCP_Invalid_setsStatus_and_returns(t *testing.T) { tr := &Translator{GatewayControllerName: "gateway.envoyproxy.io/gatewayclass-controller"} + trContext := &TranslatorContext{} // Create an invalid TCP policy (has CORS which is not allowed for TCP) policy := sp("default", "bad-tcp") @@ -936,6 +937,8 @@ func Test_SecurityPolicy_TCP_Invalid_setsStatus_and_returns(t *testing.T) { gatewayRouteMap := make(map[string]map[string]sets.Set[string]) resources := resource.NewResources() xdsIR := make(resource.XdsIRMap) + trContext.SetServices(resources.Services) + tr.TranslatorContext = trContext // Process the policy - this should set error status tr.processSecurityPolicyForRoute(resources, xdsIR, routeMap, gatewayRouteMap, policy, target) @@ -947,6 +950,7 @@ func Test_SecurityPolicy_TCP_Invalid_setsStatus_and_returns(t *testing.T) { // --- non-TCP branch: malformed CIDR should return err -> SetTranslationErrorForPolicyAncestors(...) + return func Test_SecurityPolicy_HTTP_Invalid_setsStatus_and_returns(t *testing.T) { tr := &Translator{GatewayControllerName: "gateway.envoyproxy.io/gatewayclass-controller"} + trContext := &TranslatorContext{} // Create an invalid HTTP policy (malformed CIDR) policy := sp("default", "bad-http") @@ -1010,6 +1014,8 @@ func Test_SecurityPolicy_HTTP_Invalid_setsStatus_and_returns(t *testing.T) { gatewayRouteMap := make(map[string]map[string]sets.Set[string]) resources := resource.NewResources() xdsIR := make(resource.XdsIRMap) + trContext.SetServices(resources.Services) + tr.TranslatorContext = trContext // Process the policy - this should set error status tr.processSecurityPolicyForRoute(resources, xdsIR, routeMap, gatewayRouteMap, policy, target) diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-dns-lookup-family.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-dns-lookup-family.in.yaml index 91d1f3b3b5..3bab4e5565 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-dns-lookup-family.in.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-dns-lookup-family.in.yaml @@ -140,16 +140,6 @@ backends: - fqdn: hostname: backend-v3.gateway-conformance-infra.svc.cluster.local port: 8080 - - apiVersion: gateway.envoyproxy.io/v1alpha1 - kind: Backend - metadata: - name: backend-fqdn - namespace: default - spec: - endpoints: - - fqdn: - hostname: grpc-infra-backend-v1.gateway-conformance-infra.svc.cluster.local - port: 8080 backendTrafficPolicies: - apiVersion: gateway.envoyproxy.io/v1alpha1 kind: BackendTrafficPolicy diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-dns-lookup-family.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-dns-lookup-family.out.yaml index b019fa1930..bcd1adcf54 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-dns-lookup-family.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-dns-lookup-family.out.yaml @@ -80,23 +80,6 @@ backends: reason: Accepted status: "True" type: Accepted -- apiVersion: gateway.envoyproxy.io/v1alpha1 - kind: Backend - metadata: - name: backend-fqdn - namespace: default - spec: - endpoints: - - fqdn: - hostname: grpc-infra-backend-v1.gateway-conformance-infra.svc.cluster.local - port: 8080 - status: - conditions: - - lastTransitionTime: null - message: The Backend was accepted - reason: Accepted - status: "True" - type: Accepted envoyExtensionPolicies: - apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyExtensionPolicy diff --git a/internal/gatewayapi/translator.go b/internal/gatewayapi/translator.go index cf943962ce..39551433dd 100644 --- a/internal/gatewayapi/translator.go +++ b/internal/gatewayapi/translator.go @@ -64,6 +64,10 @@ type TranslatorManager interface { // Translator translates Gateway API resources to IRs and computes status // for Gateway API resources. type Translator struct { + // TranslatorContext holds pre-indexed resource maps for efficient lookup resources + // during translation operations. + *TranslatorContext + // GatewayControllerName is the name of the Gateway API controller GatewayControllerName string @@ -220,6 +224,19 @@ func newTranslateResult( func (t *Translator) Translate(resources *resource.Resources) (*TranslateResult, error) { var errs error + // Preprocessing to improve get resources operations performance. + translatorContext := &TranslatorContext{} + translatorContext.SetNamespaces(resources.Namespaces) + translatorContext.SetServices(resources.Services) + translatorContext.SetServiceImports(resources.ServiceImports) + translatorContext.SetBackends(resources.Backends) + translatorContext.SetSecrets(resources.Secrets) + translatorContext.SetConfigMaps(resources.ConfigMaps) + translatorContext.SetClusterTrustBundles(resources.ClusterTrustBundles) + translatorContext.SetEndpointSlicesForBackend(resources.EndpointSlices) + + t.TranslatorContext = translatorContext + // Get Gateways belonging to our GatewayClass. acceptedGateways, failedGateways := t.GetRelevantGateways(resources) @@ -281,8 +298,7 @@ func (t *Translator) Translate(resources *resource.Resources) (*TranslateResult, } // Process BackendTrafficPolicies - backendTrafficPolicies := t.ProcessBackendTrafficPolicies( - resources, acceptedGateways, routes, xdsIR) + backendTrafficPolicies := t.ProcessBackendTrafficPolicies(resources, acceptedGateways, routes, xdsIR) // Process SecurityPolicies securityPolicies := t.ProcessSecurityPolicies( diff --git a/internal/gatewayapi/validate.go b/internal/gatewayapi/validate.go index 699e3c41e8..866f562b27 100644 --- a/internal/gatewayapi/validate.go +++ b/internal/gatewayapi/validate.go @@ -55,11 +55,11 @@ func (t *Translator) validateBackendRef(backendRefContext BackendRefContext, rou backendRefKind := KindDerefOr(backendRef.Kind, resource.KindService) switch backendRefKind { case resource.KindService: - if err := validateBackendRefService(backendRef.BackendObjectReference, resources, backendNamespace, protocol); err != nil { + if err := t.validateBackendRefService(backendRef.BackendObjectReference, backendNamespace, protocol); err != nil { return err } case resource.KindServiceImport: - if err := t.validateBackendServiceImport(backendRef.BackendObjectReference, resources, backendNamespace, protocol); err != nil { + if err := t.validateBackendServiceImport(backendRef.BackendObjectReference, backendNamespace, protocol); err != nil { return err } case egv1a1.KindBackend: @@ -181,10 +181,10 @@ func (t *Translator) validateBackendPort(backendRef *gwapiv1a2.BackendRef) statu return nil } -func validateBackendRefService(backendRef gwapiv1.BackendObjectReference, resources *resource.Resources, +func (t *Translator) validateBackendRefService(backendRef gwapiv1.BackendObjectReference, serviceNamespace string, protocol corev1.Protocol, ) status.Error { - service := resources.GetService(serviceNamespace, string(backendRef.Name)) + service := t.GetService(serviceNamespace, string(backendRef.Name)) if service == nil { return status.NewRouteStatusError( fmt.Errorf("service %s/%s not found", serviceNamespace, string(backendRef.Name)), @@ -207,10 +207,12 @@ func validateBackendRefService(backendRef gwapiv1.BackendObjectReference, resour return nil } -func (t *Translator) validateBackendServiceImport(backendRef gwapiv1.BackendObjectReference, resources *resource.Resources, - serviceImportNamespace string, protocol corev1.Protocol, +func (t *Translator) validateBackendServiceImport( + backendRef gwapiv1.BackendObjectReference, + serviceImportNamespace string, + protocol corev1.Protocol, ) status.Error { - serviceImport := resources.GetServiceImport(serviceImportNamespace, string(backendRef.Name)) + serviceImport := t.GetServiceImport(serviceImportNamespace, string(backendRef.Name)) if serviceImport == nil { return status.NewRouteStatusError( fmt.Errorf("service import %s/%s not found", serviceImportNamespace, backendRef.Name), @@ -235,7 +237,9 @@ func (t *Translator) validateBackendServiceImport(backendRef gwapiv1.BackendObje return nil } -func (t *Translator) validateBackendRefBackend(backendRef gwapiv1.BackendObjectReference, resources *resource.Resources, +func (t *Translator) validateBackendRefBackend( + backendRef gwapiv1.BackendObjectReference, + resources *resource.Resources, backendNamespace string, allowUDS bool, ) status.Error { if !t.BackendEnabled { @@ -245,7 +249,7 @@ func (t *Translator) validateBackendRefBackend(backendRef gwapiv1.BackendObjectR ) } - backend := resources.GetBackend(backendNamespace, string(backendRef.Name)) + backend := t.GetBackend(backendNamespace, string(backendRef.Name)) if backend == nil { return status.NewRouteStatusError( fmt.Errorf("Backend %s/%s not found", backendNamespace, backendRef.Name), @@ -352,7 +356,10 @@ func (t *Translator) validateAllowedNamespaces(listener *ListenerContext) { } } -func (t *Translator) validateTerminateModeAndGetTLSSecrets(listener *ListenerContext, resources *resource.Resources) ([]*corev1.Secret, []*x509.Certificate) { +func (t *Translator) validateTerminateModeAndGetTLSSecrets( + listener *ListenerContext, + resources *resource.Resources, +) ([]*corev1.Secret, []*x509.Certificate) { if len(listener.TLS.CertificateRefs) == 0 { status.SetGatewayListenerStatusCondition(listener.gateway.Gateway, listener.listenerStatusIdx, @@ -419,7 +426,7 @@ func (t *Translator) validateTerminateModeAndGetTLSSecrets(listener *ListenerCon secretNamespace = string(*certificateRef.Namespace) } - secret := resources.GetSecret(secretNamespace, string(certificateRef.Name)) + secret := t.GetSecret(secretNamespace, string(certificateRef.Name)) if secret == nil { status.SetGatewayListenerStatusCondition(listener.gateway.Gateway, @@ -471,7 +478,10 @@ func (t *Translator) validateTerminateModeAndGetTLSSecrets(listener *ListenerCon return secrets, certs } -func (t *Translator) validateTLSConfiguration(listener *ListenerContext, resources *resource.Resources) { +func (t *Translator) validateTLSConfiguration( + listener *ListenerContext, + resources *resource.Resources, +) { switch listener.Protocol { case gwapiv1.HTTPProtocolType, gwapiv1.UDPProtocolType, gwapiv1.TCPProtocolType: if listener.TLS != nil { @@ -853,7 +863,7 @@ func (t *Translator) validateSecretRef( if secretObjRef.Namespace != nil { secretNamespace = string(*secretObjRef.Namespace) } - secret := resources.GetSecret(secretNamespace, string(secretObjRef.Name)) + secret := t.GetSecret(secretNamespace, string(secretObjRef.Name)) if secret == nil { return nil, fmt.Errorf( @@ -877,7 +887,7 @@ func (t *Translator) validateConfigMapRef( if secretObjRef.Namespace != nil { configMapNamespace = string(*secretObjRef.Namespace) } - configMap := resources.GetConfigMap(configMapNamespace, string(secretObjRef.Name)) + configMap := t.GetConfigMap(configMapNamespace, string(secretObjRef.Name)) if configMap == nil { return nil, fmt.Errorf( @@ -973,7 +983,7 @@ func (t *Translator) validateExtServiceBackendReference( case resource.KindService: // check if the service is valid serviceNamespace := NamespaceDerefOr(backendRef.Namespace, ownerNamespace) - service := resources.GetService(serviceNamespace, string(backendRef.Name)) + service := t.GetService(serviceNamespace, string(backendRef.Name)) if service == nil { return fmt.Errorf("service %s/%s not found", serviceNamespace, backendRef.Name) } @@ -996,7 +1006,7 @@ func (t *Translator) validateExtServiceBackendReference( case resource.KindServiceImport: // check if the service import is valid serviceImportNamespace := NamespaceDerefOr(backendRef.Namespace, ownerNamespace) - serviceImport := resources.GetServiceImport(serviceImportNamespace, string(backendRef.Name)) + serviceImport := t.GetServiceImport(serviceImportNamespace, string(backendRef.Name)) if serviceImport == nil { return fmt.Errorf("serviceimport %s/%s not found", serviceImportNamespace, backendRef.Name) } @@ -1018,7 +1028,7 @@ func (t *Translator) validateExtServiceBackendReference( } case egv1a1.KindBackend: backendNamespace := NamespaceDerefOr(backendRef.Namespace, ownerNamespace) - backend := resources.GetBackend(backendNamespace, string(backendRef.Name)) + backend := t.GetBackend(backendNamespace, string(backendRef.Name)) if backend == nil { return fmt.Errorf("backend %s/%s not found", backendNamespace, backendRef.Name) } diff --git a/test/fuzz/xds_fuzz_test.go b/test/fuzz/xds_fuzz_test.go index 95120682f0..42f1313143 100644 --- a/test/fuzz/xds_fuzz_test.go +++ b/test/fuzz/xds_fuzz_test.go @@ -116,7 +116,13 @@ spec: return } - _, err = egctl.TranslateGatewayAPIToXds("default", "cluster.local", "all", rs) + opts := &egctl.TranslationOptions{ + GlobalRateLimitEnabled: true, + EndpointRoutingDisabled: true, + EnvoyPatchPolicyEnabled: true, + BackendEnabled: true, + } + _, err = egctl.TranslateGatewayAPIToXds("default", "cluster.local", "all", rs, opts) if err != nil && strings.Contains(err.Error(), "failed to translate xds") { t.Fatalf("%v", err) } diff --git a/test/gobench/translate_test.go b/test/gobench/translate_test.go index ee2410d686..5780fc9772 100644 --- a/test/gobench/translate_test.go +++ b/test/gobench/translate_test.go @@ -177,6 +177,37 @@ spec: timeout: http: requestReceivedTimeout: 30s +` + envoyExtensionPolicyYAML = `--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyExtensionPolicy +metadata: + name: envoy-extension-policy + namespace: default +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: backend + extProc: + - backendRefs: + - kind: Service + name: myExtProc + port: 3000 + messageTimeout: 5s +--- +apiVersion: v1 +kind: Service +metadata: + name: myExtProc + namespace: default +spec: + clusterIP: 10.11.12.13 + ports: + - port: 3000 + name: http + protocol: TCP + targetPort: 3000 ` ) @@ -195,15 +226,17 @@ spec: - group: gateway.networking.k8s.io kind: HTTPRoute name: backend-%d - cors: - allowOrigins: - - "https://www.example-%d.com" - allowMethods: - - GET - - POST - allowHeaders: - - "Content-Type" -`, i, i, i)) + jwt: + providers: + - name: local-jwks-%d + issuer: https://one.example.com + localJWKS: + type: ValueRef + valueRef: + group: "" + kind: ConfigMap + name: jwks-cm-%d +`, i, i, i, i)) } return sb.String() } @@ -232,6 +265,48 @@ spec: return sb.String() } +func genEnvoyExtensionPolicies(n int) string { + var sb strings.Builder + for i := 0; i < n; i++ { + sb.WriteString(fmt.Sprintf(`--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyExtensionPolicy +metadata: + name: envoy-extension-policy-%d + namespace: default +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: backend-%d + extProc: + - backendRefs: + - kind: Service + name: myExtProc + port: 3000 + messageTimeout: 5s +`, i, i)) + } + return sb.String() +} + +// Helpers for benchmark Secret/ConfigMap generation. +func genJWKSConfigMaps(n int) string { + var sb strings.Builder + for i := 0; i < n; i++ { + sb.WriteString(fmt.Sprintf(`--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: jwks-cm-%d + namespace: default +data: + local.jwt: '{"keys": [{"kty": "RSA","n": "some-modulus","e": "AQAB","kid": "example1-key"}]}' +`, i)) + } + return sb.String() +} + // Helpers for benchmark route generation. func genHTTPRoutes(n int) string { var sb strings.Builder @@ -249,9 +324,9 @@ spec: - "www.example-%d.com" rules: - backendRefs: - - name: provided-backend + - name: service-backend-%d port: 8000 -`, i, i)) +`, i, i, i)) } return sb.String() } @@ -277,9 +352,9 @@ spec: service: com.example.Service%d method: Call backendRefs: - - name: provided-backend + - name: service-backend-%d port: 9000 -`, i, i, i)) +`, i, i, i, i)) } return sb.String() } @@ -299,9 +374,56 @@ spec: sectionName: udp rules: - backendRefs: - - name: provided-backend + - name: service-backend-%d port: %d -`, i, 3000+i)) +`, i, i, 3000+i)) + } + return sb.String() +} + +func genService(n int) string { + var sb strings.Builder + for i := 0; i < n; i++ { + sb.WriteString(fmt.Sprintf(`--- +apiVersion: v1 +kind: Service +metadata: + name: service-backend-%d + namespace: default +spec: + clusterIP: 10.11.12.13 + ports: + - port: 8000 + name: http + protocol: TCP + targetPort: 8000 +`, i)) + } + return sb.String() +} + +func genEndpointSlice(n int) string { + var sb strings.Builder + for i := 0; i < n; i++ { + sb.WriteString(fmt.Sprintf(`--- +apiVersion: discovery.k8s.io/v1 +kind: EndpointSlice +metadata: + name: service-backend-%d-slice + namespace: default + labels: + kubernetes.io/service-name: service-backend-%d +addressType: IPv4 +ports: + - name: http + protocol: TCP + port: 8000 +endpoints: + - addresses: + - 192.168.1.1 + conditions: + ready: true +`, i, i)) } return sb.String() } @@ -316,19 +438,27 @@ func BenchmarkGatewayAPItoXDS(b *testing.B) { genHTTPRoutes(50) + genGRPCRoutes(25) + genUDPRoutes(10) + + genJWKSConfigMaps(50) + genSecurityPolicies(50) + - genBackendTrafficPolicies(50) + genBackendTrafficPolicies(50) + + genEnvoyExtensionPolicies(50) + + genService(50) + + genEndpointSlice(50) large := baseYAML + backendYAML + tlsSecretYAML + clientTrafficPolicyYAML + genHTTPRoutes(500) + genGRPCRoutes(250) + genUDPRoutes(100) + + genJWKSConfigMaps(500) + genSecurityPolicies(500) + - genBackendTrafficPolicies(500) + genBackendTrafficPolicies(500) + + genEnvoyExtensionPolicies(500) + + genService(500) + + genEndpointSlice(500) cases := []benchCase{ { name: "small", - yaml: baseYAML + httpRouteYAML + backendYAML + tlsSecretYAML + securityPolicyYAML + backendTrafficPolicyYAML + clientTrafficPolicyYAML, + yaml: baseYAML + httpRouteYAML + backendYAML + tlsSecretYAML + securityPolicyYAML + backendTrafficPolicyYAML + clientTrafficPolicyYAML + envoyExtensionPolicyYAML, }, { name: "medium", @@ -346,10 +476,17 @@ func BenchmarkGatewayAPItoXDS(b *testing.B) { if err != nil { b.Fatalf("load: %v", err) } + opts := &egctl.TranslationOptions{ + GlobalRateLimitEnabled: true, + EndpointRoutingDisabled: false, + EnvoyPatchPolicyEnabled: true, + BackendEnabled: true, + } + b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err = egctl.TranslateGatewayAPIToXds("default", "cluster.local", "all", rs) + _, err = egctl.TranslateGatewayAPIToXds("default", "cluster.local", "all", rs, opts) if err != nil && strings.Contains(err.Error(), "failed to translate xds") { b.Fatalf("%v", err) }