From 457194ffc7b46eea3ec3d7060389d300e567fde3 Mon Sep 17 00:00:00 2001 From: Huabing Zhao Date: Tue, 21 Nov 2023 21:33:11 -0800 Subject: [PATCH] refactor: simplify filter patching with an unified interface (#2210) * refactor: simplify filter patching with an unified interface Signed-off-by: huabing zhao fix: check if secret exist before creat it Signed-off-by: huabing zhao * address comments Signed-off-by: huabing zhao --------- Signed-off-by: huabing zhao --- internal/xds/translator/cluster.go | 15 ++ internal/xds/translator/cors.go | 35 ++++- internal/xds/translator/httpfilters.go | 146 ++++++++++++------ .../xds/translator/{jwt_authn.go => jwt.go} | 29 +++- internal/xds/translator/oidc.go | 47 ++++-- .../out/xds-ir/jwt-ratelimit.clusters.yaml | 44 +++--- internal/xds/translator/translator.go | 72 +++++---- 7 files changed, 254 insertions(+), 134 deletions(-) rename internal/xds/translator/{jwt_authn.go => jwt.go} (91%) diff --git a/internal/xds/translator/cluster.go b/internal/xds/translator/cluster.go index 63cb2dfb502..00375200688 100644 --- a/internal/xds/translator/cluster.go +++ b/internal/xds/translator/cluster.go @@ -27,6 +27,21 @@ const ( tcpClusterPerConnectionBufferLimitBytes = 32768 ) +type xdsClusterArgs struct { + name string + settings []*ir.DestinationSetting + tSocket *corev3.TransportSocket + endpointType EndpointType + loadBalancer *ir.LoadBalancer +} + +type EndpointType int + +const ( + EndpointTypeDNS EndpointType = iota + EndpointTypeStatic +) + func buildXdsCluster(args *xdsClusterArgs) *clusterv3.Cluster { cluster := &clusterv3.Cluster{ Name: args.name, diff --git a/internal/xds/translator/cors.go b/internal/xds/translator/cors.go index 71663f21733..50ead8f3d2e 100644 --- a/internal/xds/translator/cors.go +++ b/internal/xds/translator/cors.go @@ -20,11 +20,23 @@ import ( "google.golang.org/protobuf/types/known/anypb" "github.com/envoyproxy/gateway/internal/ir" + "github.com/envoyproxy/gateway/internal/xds/types" ) -// patchHCMWithCORSFilter builds and appends the CORS Filter to the HTTP -// Connection Manager if applicable. -func patchHCMWithCORSFilter(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error { +func init() { + registerHTTPFilter(&cors{}) +} + +type cors struct { +} + +var _ httpFilter = &cors{} + +// patchHCM builds and appends the CORS Filter to the HTTP Connection Manager if +// applicable. +func (*cors) patchHCM( + mgr *hcmv3.HttpConnectionManager, + irListener *ir.HTTPListener) error { if mgr == nil { return errors.New("hcm is nil") } @@ -49,7 +61,7 @@ func patchHCMWithCORSFilter(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTT return err } - // Ensure the cors filter is the first one in the filter chain. + // Ensure the CORS filter is the first one in the filter chain. mgr.HttpFilters = append([]*hcmv3.HttpFilter{corsFilter}, mgr.HttpFilters...) return nil @@ -88,9 +100,8 @@ func listenerContainsCORS(irListener *ir.HTTPListener) bool { return false } -// patchRouteWithCORS patches the provided route with the CORS config if -// applicable. -func patchRouteWithCORS(route *routev3.Route, irRoute *ir.HTTPRoute) error { +// patchRoute patches the provided route with the CORS config if applicable. +func (*cors) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error { if route == nil { return errors.New("xds route is nil") } @@ -103,7 +114,7 @@ func patchRouteWithCORS(route *routev3.Route, irRoute *ir.HTTPRoute) error { filterCfg := route.GetTypedPerFilterConfig() if _, ok := filterCfg[wellknown.CORS]; ok { - // This should not happen since this is the only place where the cors + // This should not happen since this is the only place where the CORS // filter is added in a route. return fmt.Errorf("route already contains cors config: %+v", route) } @@ -152,3 +163,11 @@ func patchRouteWithCORS(route *routev3.Route, irRoute *ir.HTTPRoute) error { return nil } + +func (*cors) patchRouteConfig(*routev3.RouteConfiguration, *ir.HTTPListener) error { + return nil +} + +func (c *cors) patchResources(*types.ResourceVersionTable, []*ir.HTTPRoute) error { + return nil +} diff --git a/internal/xds/translator/httpfilters.go b/internal/xds/translator/httpfilters.go index 6c6ae99f994..7fc7e1d9089 100644 --- a/internal/xds/translator/httpfilters.go +++ b/internal/xds/translator/httpfilters.go @@ -13,10 +13,60 @@ import ( hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" "github.com/envoyproxy/go-control-plane/pkg/wellknown" + "github.com/envoyproxy/gateway/internal/xds/filters" + "github.com/envoyproxy/gateway/internal/xds/types" + "github.com/envoyproxy/gateway/internal/ir" - xdsfilters "github.com/envoyproxy/gateway/internal/xds/filters" ) +var httpFilters []httpFilter + +// registerHTTPFilter registers the provided HTTP filter. +func registerHTTPFilter(filter httpFilter) { + httpFilters = append(httpFilters, filter) +} + +// httpFilter is the interface for all the HTTP filters. +// +// There are two ways to support per-route configuration for an HTTP filter: +// - For the filters with native per-route configuration support: +// - patchHCM: EG adds the filter to the HCM filter chain only once. +// - patchRoute: EG adds the filter's native per-route configuration to each +// route's typedFilterConfig. +// +// - For the filters without native per-route configuration support: +// - patchHCM: EG adds a filter for each route in the HCM filter chain, the +// filter name is prefixed with the filter's type name, for example, +// "envoy.filters.http.oauth2", and suffixed with the route name. Each filter +// is configured with the route's per-route configuration. +// - patchRouteConfig: EG disables all the filters of this type in the +// typedFilterConfig of the route config. +// - PatchRouteWithPerRouteConfig: EG enables the corresponding filter for each +// route in the typedFilterConfig of that route. +// +// The filter types that haven't native per-route support: oauth2, basic authn +// Note: The filter types that have native per-route configuration support should +// always se their own native per-route configuration. +type httpFilter interface { + // patchHCM patches the HttpConnectionManager with the filter. + patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error + + // patchRouteConfig patches the provided RouteConfiguration with a filter's + // RouteConfiguration level configuration. + patchRouteConfig(rc *routev3.RouteConfiguration, irListener *ir.HTTPListener) error + + // patchRoute patches the provide Route with a filter's Route level configuration. + patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error + + // patchResources adds all the other needed resources referenced by this + // filter to the resource version table. + // for example: + // - a jwt filter needs to add the cluster for the jwks. + // - an oidc filter needs to add the cluster for token endpoint and the secret + // for the oauth2 client secret and the hmac secret. + patchResources(tCtx *types.ResourceVersionTable, routes []*ir.HTTPRoute) error +} + type OrderedHTTPFilter struct { filter *hcmv3.HttpFilter order int @@ -58,6 +108,7 @@ func newOrderedHTTPFilter(filter *hcmv3.HttpFilter) *OrderedHTTPFilter { } // sort.Interface implementation. + func (o OrderedHTTPFilters) Len() int { return len(o) } @@ -97,29 +148,21 @@ func (t *Translator) patchHCMWithFilters( irListener *ir.HTTPListener) error { // The order of filter patching is not relevant here. // All the filters will be sorted in correct order after the patching is done. + // // Important: don't forget to set the order for new filters in the // newOrderedHTTPFilter method. - // TODO: Make this a generic interface for all API Gateway features. - // https://github.com/envoyproxy/gateway/issues/882 - t.patchHCMWithRateLimit(mgr, irListener) - - // Add the cors filter, if needed - if err := patchHCMWithCORSFilter(mgr, irListener); err != nil { - return err + for _, filter := range httpFilters { + if err := filter.patchHCM(mgr, irListener); err != nil { + return err + } } - // Add the jwt authn filter, if needed. - if err := patchHCMWithJWTAuthnFilter(mgr, irListener); err != nil { - return err - } - - // Add oauth2 filters, if needed. - if err := patchHCMWithOAuth2Filters(mgr, irListener); err != nil { - return err - } + // RateLimit filter is handled separately because it relies on the global + // rate limit server configuration. + t.patchHCMWithRateLimit(mgr, irListener) // Add the router filter - mgr.HttpFilters = append(mgr.HttpFilters, xdsfilters.HTTPRouter) + mgr.HttpFilters = append(mgr.HttpFilters, filters.HTTPRouter) // Sort the filters in the correct order. mgr.HttpFilters = sortHTTPFilters(mgr.HttpFilters) @@ -127,50 +170,39 @@ func (t *Translator) patchHCMWithFilters( } // patchRouteCfgWithPerRouteConfig appends per-route filter configurations to the -// route config. -// This is a generic way to add per-route filter configurations for all filters -// that has none-native per-route configuration support. -// - For the filter type that without native per-route configuration support, EG -// adds a filter for each route in the HCM filter chain. -// - patchRouteCfgWithPerRouteConfig disables all the filters in the -// typedFilterConfig of the route config. -// - PatchRouteWithPerRouteConfig enables the corresponding oauth2 filter for each -// route in the typedFilterConfig of the route. -// -// The filter types that have non-native per-route support: oauth2, basic authn -// Note: The filter types that have native per-route configuration support should -// use their own native per-route configuration. +// provided listener's RouteConfiguration. +// This method is used to disable the filters without native per-route support. +// The disabled filters will be enabled by route in the patchRouteWithPerRouteConfig +// method. func patchRouteCfgWithPerRouteConfig( routeCfg *routev3.RouteConfiguration, irListener *ir.HTTPListener) error { // Only supports the oauth2 filter for now, other filters will be added later. - return patchRouteCfgWithOAuth2Filter(routeCfg, irListener) + for _, filter := range httpFilters { + if err := filter.patchRouteConfig(routeCfg, irListener); err != nil { + return err + } + } + return nil } -// patchRouteWithPerRouteConfig appends per-route filter configurations to the route. +// patchRouteWithPerRouteConfig appends per-route filter configuration to the +// provided route. func patchRouteWithPerRouteConfig( route *routev3.Route, irRoute *ir.HTTPRoute) error { - // TODO: Convert this into a generic interface for API Gateway features. - // https://github.com/envoyproxy/gateway/issues/882 - if err := - patchRouteWithRateLimit(route.GetRoute(), irRoute); err != nil { - return nil - } - // Add the cors per route config to the route, if needed. - if err := patchRouteWithCORS(route, irRoute); err != nil { - return err + for _, filter := range httpFilters { + if err := filter.patchRoute(route, irRoute); err != nil { + return err + } } - // Add the jwt per route config to the route, if needed. - if err := patchRouteWithJWT(route, irRoute); err != nil { - return err - } - - // Add the oauth2 per route config to the route, if needed. - if err := patchRouteWithOAuth2(route, irRoute); err != nil { - return err + // RateLimit filter is handled separately because it relies on the global + // rate limit server configuration. + if err := + patchRouteWithRateLimit(route.GetRoute(), irRoute); err != nil { + return nil } return nil @@ -182,3 +214,17 @@ func isOAuth2Filter(filter *hcmv3.HttpFilter) bool { // route. The oauth2 filter name is prefixed with "envoy.filters.http.oauth2". return strings.HasPrefix(filter.Name, oauth2Filter) } + +// patchResources adds all the other needed resources referenced by this +// filter to the resource version table. +// for example: +// - a jwt filter needs to add the cluster for the jwks. +// - an oidc filter needs to add the secret for the oauth2 client secret. +func patchResources(tCtx *types.ResourceVersionTable, routes []*ir.HTTPRoute) error { + for _, filter := range httpFilters { + if err := filter.patchResources(tCtx, routes); err != nil { + return err + } + } + return nil +} diff --git a/internal/xds/translator/jwt_authn.go b/internal/xds/translator/jwt.go similarity index 91% rename from internal/xds/translator/jwt_authn.go rename to internal/xds/translator/jwt.go index ae50494ac68..125fc7de16f 100644 --- a/internal/xds/translator/jwt_authn.go +++ b/internal/xds/translator/jwt.go @@ -29,9 +29,18 @@ const ( envoyTrustBundle = "/etc/ssl/certs/ca-certificates.crt" ) -// patchHCMWithJWTAuthnFilter builds and appends the Jwt Filter to the HTTP -// Connection Manager if applicable, and it does not already exist. -func patchHCMWithJWTAuthnFilter(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error { +func init() { + registerHTTPFilter(&jwt{}) +} + +type jwt struct { +} + +var _ httpFilter = &jwt{} + +// patchHCM builds and appends the JWT Filter to the HTTP Connection Manager if +// applicable, and it does not already exist. +func (*jwt) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error { if mgr == nil { return errors.New("hcm is nil") } @@ -193,9 +202,9 @@ func buildXdsUpstreamTLSSocket() (*corev3.TransportSocket, error) { }, nil } -// patchRouteWithJWT patches the provided route with a JWT PerRouteConfig, if the -// route doesn't contain it. -func patchRouteWithJWT(route *routev3.Route, irRoute *ir.HTTPRoute) error { +// patchRoute patches the provided route with a JWT PerRouteConfig, if the route +// doesn't contain it. +func (*jwt) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error { if route == nil { return errors.New("xds route is nil") } @@ -227,8 +236,8 @@ func patchRouteWithJWT(route *routev3.Route, irRoute *ir.HTTPRoute) error { return nil } -// createJWKSClusters creates JWKS clusters from the provided routes, if needed. -func createJWKSClusters(tCtx *types.ResourceVersionTable, routes []*ir.HTTPRoute) error { +// patchResources creates JWKS clusters from the provided routes, if needed. +func (*jwt) patchResources(tCtx *types.ResourceVersionTable, routes []*ir.HTTPRoute) error { if tCtx == nil || tCtx.XdsResources == nil { return errors.New("xds resource table is nil") } @@ -311,3 +320,7 @@ func routeContainsJWTAuthn(irRoute *ir.HTTPRoute) bool { return false } + +func (*jwt) patchRouteConfig(*routev3.RouteConfiguration, *ir.HTTPListener) error { + return nil +} diff --git a/internal/xds/translator/oidc.go b/internal/xds/translator/oidc.go index b79fe8039b1..c422049a634 100644 --- a/internal/xds/translator/oidc.go +++ b/internal/xds/translator/oidc.go @@ -16,7 +16,6 @@ import ( hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" - resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3" "github.com/golang/protobuf/ptypes/duration" "github.com/tetratelabs/multierror" "google.golang.org/protobuf/types/known/anypb" @@ -34,10 +33,19 @@ const ( defaultSignoutPath = "/signout" ) -// patchHCMWithOAuth2Filters builds and appends the oauth2 Filters to the HTTP -// Connection Manager if applicable, and it does not already exist. +func init() { + registerHTTPFilter(&oidc{}) +} + +type oidc struct { +} + +var _ httpFilter = &oidc{} + +// patchHCM builds and appends the oauth2 Filters to the HTTP Connection Manager +// if applicable, and it does not already exist. // Note: this method creates an oauth2 filter for each route that contains an OIDC config. -func patchHCMWithOAuth2Filters(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error { +func (*oidc) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error { var errs error if mgr == nil { @@ -156,7 +164,8 @@ func oauth2Config(route *ir.HTTPRoute) (*oauth2v3.OAuth2, error) { }, }, }, - AuthType: oauth2v3.OAuth2Config_BASIC_AUTH, // every OIDC provider supports basic auth + // every OIDC provider supports basic auth + AuthType: oauth2v3.OAuth2Config_BASIC_AUTH, AuthScopes: route.OIDC.Scopes, }, } @@ -177,6 +186,17 @@ func routeContainsOIDC(irRoute *ir.HTTPRoute) bool { return false } +func (*oidc) patchResources(tCtx *types.ResourceVersionTable, + routes []*ir.HTTPRoute) error { + if err := createOAuth2TokenEndpointClusters(tCtx, routes); err != nil { + return err + } + if err := createOAuth2Secrets(tCtx, routes); err != nil { + return err + } + return nil +} + // createOAuth2TokenEndpointClusters creates token endpoint clusters from the // provided routes, if needed. func createOAuth2TokenEndpointClusters(tCtx *types.ResourceVersionTable, @@ -261,8 +281,10 @@ func createOAuth2Secrets(tCtx *types.ResourceVersionTable, routes []*ir.HTTPRout continue } + // a separate secret is created for each route, even they share the same + // oauth2 client ID and secret. clientSecret := buildOAuth2ClientSecret(route) - if err := tCtx.AddXdsResource(resourcev3.SecretType, clientSecret); err != nil { + if err := addXdsSecret(tCtx, clientSecret); err != nil { errs = multierror.Append(errs, err) } @@ -270,7 +292,7 @@ func createOAuth2Secrets(tCtx *types.ResourceVersionTable, routes []*ir.HTTPRout if err != nil { errs = multierror.Append(errs, err) } - if err := tCtx.AddXdsResource(resourcev3.SecretType, hmacSecret); err != nil { + if err := addXdsSecret(tCtx, hmacSecret); err != nil { errs = multierror.Append(errs, err) } } @@ -340,11 +362,11 @@ func generateHMACSecretKey() ([]byte, error) { return key, nil } -// patchRouteCfgWithOAuth2Filter patches the provided route configuration with -// the oauth2 filter if applicable. +// patchRouteCfg patches the provided route configuration with the oauth2 filter +// if applicable. // Note: this method disables all the oauth2 filters by default. The filter will // be enabled per-route in the typePerFilterConfig of the route. -func patchRouteCfgWithOAuth2Filter(routeCfg *routev3.RouteConfiguration, irListener *ir.HTTPListener) error { +func (*oidc) patchRouteConfig(routeCfg *routev3.RouteConfiguration, irListener *ir.HTTPListener) error { if routeCfg == nil { return errors.New("route configuration is nil") } @@ -386,10 +408,9 @@ func patchRouteCfgWithOAuth2Filter(routeCfg *routev3.RouteConfiguration, irListe return errs } -// patchRouteWithOAuth2 patches the provided route with the oauth2 config if -// applicable. +// patchRoute patches the provided route with the oauth2 config if applicable. // Note: this method enables the corresponding oauth2 filter for the provided route. -func patchRouteWithOAuth2(route *routev3.Route, irRoute *ir.HTTPRoute) error { +func (*oidc) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error { if route == nil { return errors.New("xds route is nil") } diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-ratelimit.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-ratelimit.clusters.yaml index e7c4315a916..353a2a7f696 100644 --- a/internal/xds/translator/testdata/out/xds-ir/jwt-ratelimit.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-ratelimit.clusters.yaml @@ -40,6 +40,28 @@ outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: "192_168_1_250_443" + lbPolicy: LEAST_REQUEST + name: "192_168_1_250_443" + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContext: + trustedCa: + filename: /etc/ssl/certs/ca-certificates.crt + type: EDS - commonLbConfig: localityWeightedLbConfig: {} connectTimeout: 10s @@ -82,25 +104,3 @@ '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions explicitHttpConfig: http2ProtocolOptions: {} -- commonLbConfig: - localityWeightedLbConfig: {} - connectTimeout: 10s - dnsLookupFamily: V4_ONLY - edsClusterConfig: - edsConfig: - ads: {} - resourceApiVersion: V3 - serviceName: "192_168_1_250_443" - lbPolicy: LEAST_REQUEST - name: "192_168_1_250_443" - outlierDetection: {} - perConnectionBufferLimitBytes: 32768 - transportSocket: - name: envoy.transport_sockets.tls - typedConfig: - '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - commonTlsContext: - validationContext: - trustedCa: - filename: /etc/ssl/certs/ca-certificates.crt - type: EDS diff --git a/internal/xds/translator/translator.go b/internal/xds/translator/translator.go index e2d1a415109..ca074ba065d 100644 --- a/internal/xds/translator/translator.go +++ b/internal/xds/translator/translator.go @@ -16,6 +16,7 @@ import ( endpointv3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3" "github.com/tetratelabs/multierror" @@ -27,6 +28,7 @@ import ( var ( ErrXdsClusterExists = errors.New("xds cluster exists") + ErrXdsSecretExists = errors.New("xds secret exists") ) const AuthorityHeaderKey = ":authority" @@ -240,7 +242,7 @@ func (t *Translator) processHTTPListenerXdsTranslation( vHost.Routes = append(vHost.Routes, xdsRoute) if httpRoute.Destination != nil { - if err = addXdsCluster(tCtx, &xdsClusterArgs{ + if err := addXdsCluster(tCtx, &xdsClusterArgs{ name: httpRoute.Destination.Name, settings: httpRoute.Destination.Settings, tSocket: nil, @@ -279,25 +281,16 @@ func (t *Translator) processHTTPListenerXdsTranslation( errs = multierror.Append(errs, err) } - // TODO: Make this into a generic interface for API Gateway features. - // https://github.com/envoyproxy/gateway/issues/882 - // Check if a ratelimit cluster exists, if not, add it, if its needed. - if err := t.createRateLimitServiceCluster(tCtx, httpListener); err != nil { - errs = multierror.Append(errs, err) - } - - // Create authn jwks clusters, if needed. - if err := createJWKSClusters(tCtx, httpListener.Routes); err != nil { - errs = multierror.Append(errs, err) - } - - // Create oauth2 token endpoint clusters, if needed. - if err := createOAuth2TokenEndpointClusters(tCtx, httpListener.Routes); err != nil { - errs = multierror.Append(errs, err) + // Add all the other needed resources referenced by this filter to the + // resource version table. + if err := patchResources(tCtx, httpListener.Routes); err != nil { + return err } - // Create oauth2 client and HMAC secrets, if needed. - if err := createOAuth2Secrets(tCtx, httpListener.Routes); err != nil { + // RateLimit filter is handled separately because it relies on the global + // rate limit server configuration. + // Check if a ratelimit cluster exists, if not, add it, if it's needed. + if err := t.createRateLimitServiceCluster(tCtx, httpListener); err != nil { errs = multierror.Append(errs, err) } @@ -469,6 +462,34 @@ func findXdsEndpoint(tCtx *types.ResourceVersionTable, name string) *endpointv3. return nil } +// findXdsSecret finds a xds secret with the same name, and returns nil if there is no match. +func findXdsSecret(tCtx *types.ResourceVersionTable, name string) *tlsv3.Secret { + if tCtx == nil || tCtx.XdsResources == nil || tCtx.XdsResources[resourcev3.SecretType] == nil { + return nil + } + + for _, r := range tCtx.XdsResources[resourcev3.SecretType] { + secret := r.(*tlsv3.Secret) + if secret.Name == name { + return secret + } + } + + return nil +} + +func addXdsSecret(tCtx *types.ResourceVersionTable, secret *tlsv3.Secret) error { + // Return early if cluster with the same name exists + if c := findXdsSecret(tCtx, secret.Name); c != nil { + return ErrXdsSecretExists + } + + if err := tCtx.AddXdsResource(resourcev3.SecretType, secret); err != nil { + return err + } + return nil +} + func addXdsCluster(tCtx *types.ResourceVersionTable, args *xdsClusterArgs) error { // Return early if cluster with the same name exists if c := findXdsCluster(tCtx, args.name); c != nil { @@ -490,18 +511,3 @@ func addXdsCluster(tCtx *types.ResourceVersionTable, args *xdsClusterArgs) error } return nil } - -type xdsClusterArgs struct { - name string - settings []*ir.DestinationSetting - tSocket *corev3.TransportSocket - endpointType EndpointType - loadBalancer *ir.LoadBalancer -} - -type EndpointType int - -const ( - EndpointTypeDNS EndpointType = iota - EndpointTypeStatic -)