/* * * Copyright 2021 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package xdsresource import ( "encoding/json" "regexp" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "google.golang.org/grpc/internal/envconfig" "google.golang.org/grpc/internal/pretty" "google.golang.org/grpc/internal/testutils" xdsinternal "google.golang.org/grpc/internal/xds" "google.golang.org/grpc/internal/xds/bootstrap" "google.golang.org/grpc/internal/xds/matcher" "google.golang.org/grpc/internal/xds/xdsclient/xdsresource/version" v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" v3endpointpb "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" v3aggregateclusterpb "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/aggregate/v3" v3gcpauthnpb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/gcp_authn/v3" v3leastrequestpb "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/least_request/v3" v3ringhashpb "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/ring_hash/v3" v3http11proxypb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/http_11_proxy/v3" v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" v3discoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" v3matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/wrapperspb" ) const ( clusterName = "clusterName" serviceName = "service" ) func (s) TestValidateCluster_Failure(t *testing.T) { tests := []struct { name string cluster *v3clusterpb.Cluster wantErr bool }{ { name: "non-supported-cluster-type-static", cluster: &v3clusterpb.Cluster{ ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_STATIC}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, }, LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST, }, wantErr: true, }, { name: "non-supported-cluster-type-original-dst", cluster: &v3clusterpb.Cluster{ ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_ORIGINAL_DST}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, }, LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST, }, wantErr: true, }, { name: "no-eds-config", cluster: &v3clusterpb.Cluster{ ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, }, wantErr: true, }, { name: "no-ads-config-source", cluster: &v3clusterpb.Cluster{ ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{}, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, }, wantErr: true, }, { name: "unsupported-lb-policy", cluster: &v3clusterpb.Cluster{ ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, }, LbPolicy: v3clusterpb.Cluster_MAGLEV, }, wantErr: true, }, { name: "logical-dns-multiple-localities", cluster: &v3clusterpb.Cluster{ Name: clusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_LOGICAL_DNS}, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, LoadAssignment: &v3endpointpb.ClusterLoadAssignment{ Endpoints: []*v3endpointpb.LocalityLbEndpoints{ // Invalid if there are more than one locality. {LbEndpoints: nil}, {LbEndpoints: nil}, }, }, }, wantErr: true, }, { name: "ring-hash-hash-function-not-xx-hash", cluster: &v3clusterpb.Cluster{ LbPolicy: v3clusterpb.Cluster_RING_HASH, LbConfig: &v3clusterpb.Cluster_RingHashLbConfig_{ RingHashLbConfig: &v3clusterpb.Cluster_RingHashLbConfig{ HashFunction: v3clusterpb.Cluster_RingHashLbConfig_MURMUR_HASH_2, }, }, }, wantErr: true, }, { name: "least-request-choice-count-less-than-two", cluster: &v3clusterpb.Cluster{ LbPolicy: v3clusterpb.Cluster_RING_HASH, LbConfig: &v3clusterpb.Cluster_LeastRequestLbConfig_{ LeastRequestLbConfig: &v3clusterpb.Cluster_LeastRequestLbConfig{ ChoiceCount: wrapperspb.UInt32(1), }, }, }, wantErr: true, }, { name: "ring-hash-max-bound-greater-than-upper-bound", cluster: &v3clusterpb.Cluster{ LbPolicy: v3clusterpb.Cluster_RING_HASH, LbConfig: &v3clusterpb.Cluster_RingHashLbConfig_{ RingHashLbConfig: &v3clusterpb.Cluster_RingHashLbConfig{ MaximumRingSize: wrapperspb.UInt64(ringHashSizeUpperBound + 1), }, }, }, wantErr: true, }, { name: "ring-hash-max-bound-greater-than-upper-bound-load-balancing-policy", cluster: &v3clusterpb.Cluster{ Name: clusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LoadBalancingPolicy: &v3clusterpb.LoadBalancingPolicy{ Policies: []*v3clusterpb.LoadBalancingPolicy_Policy{ { TypedExtensionConfig: &v3corepb.TypedExtensionConfig{ TypedConfig: testutils.MarshalAny(t, &v3ringhashpb.RingHash{ HashFunction: v3ringhashpb.RingHash_XX_HASH, MinimumRingSize: wrapperspb.UInt64(10), MaximumRingSize: wrapperspb.UInt64(ringHashSizeUpperBound + 1), }), }, }, }, }, }, wantErr: true, }, { name: "least-request-unsupported-in-converter-since-env-var-unset", cluster: &v3clusterpb.Cluster{ Name: clusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LoadBalancingPolicy: &v3clusterpb.LoadBalancingPolicy{ Policies: []*v3clusterpb.LoadBalancingPolicy_Policy{ { TypedExtensionConfig: &v3corepb.TypedExtensionConfig{ TypedConfig: testutils.MarshalAny(t, &v3leastrequestpb.LeastRequest{}), }, }, }, }, }, wantErr: true, }, { name: "aggregate-nil-clusters", cluster: &v3clusterpb.Cluster{ Name: clusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_ClusterType{ ClusterType: &v3clusterpb.Cluster_CustomClusterType{ Name: "envoy.clusters.aggregate", TypedConfig: testutils.MarshalAny(t, &v3aggregateclusterpb.ClusterConfig{}), }, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, }, wantErr: true, }, { name: "aggregate-empty-clusters", cluster: &v3clusterpb.Cluster{ Name: clusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_ClusterType{ ClusterType: &v3clusterpb.Cluster_CustomClusterType{ Name: "envoy.clusters.aggregate", TypedConfig: testutils.MarshalAny(t, &v3aggregateclusterpb.ClusterConfig{ Clusters: []string{}, }), }, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, }, wantErr: true, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { if update, err := validateClusterAndConstructClusterUpdate(test.cluster, nil); err == nil { t.Errorf("validateClusterAndConstructClusterUpdate(%+v) = %v, wanted error", test.cluster, update) } }) } } func (s) TestSecurityConfigFromCommonTLSContextUsingNewFields_ErrorCases(t *testing.T) { tests := []struct { name string common *v3tlspb.CommonTlsContext server bool wantErr string enableSystemRootCertsFlag bool }{ { name: "unsupported-tls_certificates-field-for-identity-certs", common: &v3tlspb.CommonTlsContext{ TlsCertificates: []*v3tlspb.TlsCertificate{ {CertificateChain: &v3corepb.DataSource{}}, }, }, wantErr: "unsupported field tls_certificates is set in CommonTlsContext message", }, { name: "unsupported-tls_certificates_sds_secret_configs-field-for-identity-certs", common: &v3tlspb.CommonTlsContext{ TlsCertificateSdsSecretConfigs: []*v3tlspb.SdsSecretConfig{ {Name: "sds-secrets-config"}, }, }, wantErr: "unsupported field tls_certificate_sds_secret_configs is set in CommonTlsContext message", }, { name: "unsupported-sds-validation-context", common: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextSdsSecretConfig{ ValidationContextSdsSecretConfig: &v3tlspb.SdsSecretConfig{ Name: "foo-sds-secret", }, }, }, wantErr: "validation context contains unexpected type", }, { name: "missing-ca_certificate_provider_instance-in-validation-context", common: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{}, }, }, wantErr: "expected field ca_certificate_provider_instance is missing in CommonTlsContext message", }, { name: "unsupported-field-verify_certificate_spki-in-validation-context", common: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{ CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: "rootPluginInstance", CertificateName: "rootCertName", }, VerifyCertificateSpki: []string{"spki"}, }, }, }, wantErr: "unsupported verify_certificate_spki field in CommonTlsContext message", }, { name: "unsupported-field-verify_certificate_hash-in-validation-context", common: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{ CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: "rootPluginInstance", CertificateName: "rootCertName", }, VerifyCertificateHash: []string{"hash"}, }, }, }, wantErr: "unsupported verify_certificate_hash field in CommonTlsContext message", }, { name: "unsupported-field-require_signed_certificate_timestamp-in-validation-context", common: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{ CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: "rootPluginInstance", CertificateName: "rootCertName", }, RequireSignedCertificateTimestamp: &wrapperspb.BoolValue{Value: true}, }, }, }, wantErr: "unsupported require_signed_certificate_timestamp field in CommonTlsContext message", }, { name: "unsupported-field-crl-in-validation-context", common: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{ CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: "rootPluginInstance", CertificateName: "rootCertName", }, Crl: &v3corepb.DataSource{}, }, }, }, wantErr: "unsupported crl field in CommonTlsContext message", }, { name: "unsupported-field-custom_validator_config-in-validation-context", common: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{ CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: "rootPluginInstance", CertificateName: "rootCertName", }, CustomValidatorConfig: &v3corepb.TypedExtensionConfig{}, }, }, }, wantErr: "unsupported custom_validator_config field in CommonTlsContext message", }, { name: "invalid-match_subject_alt_names-field-in-validation-context", common: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{ CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: "rootPluginInstance", CertificateName: "rootCertName", }, MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ {MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: ""}}, }, }, }, }, wantErr: "empty prefix is not allowed in StringMatcher", }, { name: "unsupported-field-matching-subject-alt-names-in-validation-context-of-server", common: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{ CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: "rootPluginInstance", CertificateName: "rootCertName", }, MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ {MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "sanPrefix"}}, }, }, }, }, server: true, wantErr: "match_subject_alt_names field in validation context is not supported on the server", }, { name: "client-missing-root-cert-provider-and-use-system-certs-fields", enableSystemRootCertsFlag: true, common: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{}, }, }, wantErr: "expected fields ca_certificate_provider_instance and system_root_certs are missing", }, { name: "server-missing-root-cert-provider-and-use-system-certs-fields", enableSystemRootCertsFlag: true, common: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{}, }, }, server: true, wantErr: "expected field ca_certificate_provider_instance is missing", }, { name: "client-missing-root-cert-provider-and-use-system-certs-fields-env-var-unset", common: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{}, }, }, wantErr: "expected field ca_certificate_provider_instance is missing", }, { name: "server-missing-root-cert-provider-and-use-system-certs-fields-env-var-unset", common: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{}, }, }, server: true, wantErr: "expected field ca_certificate_provider_instance is missing", }, { name: "server-missing-root-cert-provider-and-set-use-system-certs-fields", enableSystemRootCertsFlag: true, common: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{ SystemRootCerts: &v3tlspb.CertificateValidationContext_SystemRootCerts{}, }, }, }, server: true, wantErr: "expected field ca_certificate_provider_instance is missing and unexpected field system_root_certs is set", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { testutils.SetEnvConfig(t, &envconfig.XDSSystemRootCertsEnabled, test.enableSystemRootCertsFlag) _, err := securityConfigFromCommonTLSContextUsingNewFields(test.common, test.server) if err == nil { t.Fatal("securityConfigFromCommonTLSContextUsingNewFields() succeeded when expected to fail") } if !strings.Contains(err.Error(), test.wantErr) { t.Fatalf("securityConfigFromCommonTLSContextUsingNewFields() returned err: %v, wantErr: %v", err, test.wantErr) } }) } } func (s) TestValidateClusterWithSecurityConfig(t *testing.T) { const ( identityPluginInstance = "identityPluginInstance" identityCertName = "identityCert" rootPluginInstance = "rootPluginInstance" rootCertName = "rootCert" clusterName = "cluster" serviceName = "service" sanExact = "san-exact" sanPrefix = "san-prefix" sanSuffix = "san-suffix" sanRegexBad = "??" sanRegexGood = "san?regex?" sanContains = "san-contains" sniString = "test-sni" ) var sanRE = regexp.MustCompile("^(?:san?regex?)$") tests := []struct { name string cluster *v3clusterpb.Cluster wantUpdate ClusterUpdate wantErr bool enableSystemRootCertsFlag bool enableSNIFlag bool }{ { name: "transport-socket-matches", cluster: &v3clusterpb.Cluster{ ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocketMatches: []*v3clusterpb.Cluster_TransportSocketMatch{ {Name: "transport-socket-match-1"}, }, }, wantErr: true, }, { name: "transport-socket-unsupported-name", cluster: &v3clusterpb.Cluster{ ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ Name: "unsupported-foo", ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: &anypb.Any{ TypeUrl: version.V3UpstreamTLSContextURL, }, }, }, }, wantErr: true, }, { name: "transport-socket-unsupported-typeURL", cluster: &v3clusterpb.Cluster{ ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: &anypb.Any{ TypeUrl: version.V3HTTPConnManagerURL, }, }, }, }, wantErr: true, }, { name: "transport-socket-unsupported-type", cluster: &v3clusterpb.Cluster{ ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: &anypb.Any{ TypeUrl: version.V3UpstreamTLSContextURL, Value: []byte{1, 2, 3, 4}, }, }, }, }, wantErr: true, }, { name: "transport-socket-unsupported-tls-params-field", cluster: &v3clusterpb.Cluster{ ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ TlsParams: &v3tlspb.TlsParameters{}, }, }), }, }, }, wantErr: true, }, { name: "transport-socket-unsupported-custom-handshaker-field", cluster: &v3clusterpb.Cluster{ ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ CustomHandshaker: &v3corepb.TypedExtensionConfig{}, }, }), }, }, }, wantErr: true, }, { name: "transport-socket-unsupported-validation-context", cluster: &v3clusterpb.Cluster{ ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextSdsSecretConfig{ ValidationContextSdsSecretConfig: &v3tlspb.SdsSecretConfig{ Name: "foo-sds-secret", }, }, }, }), }, }, }, wantErr: true, }, { name: "transport-socket-without-validation-context", cluster: &v3clusterpb.Cluster{ ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{}, }), }, }, }, wantErr: true, }, { name: "empty-prefix-in-matching-SAN", cluster: &v3clusterpb.Cluster{ ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ DefaultValidationContext: &v3tlspb.CertificateValidationContext{ MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ {MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: ""}}, }, }, ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ InstanceName: rootPluginInstance, CertificateName: rootCertName, }, }, }, }, }), }, }, }, wantErr: true, }, { name: "empty-suffix-in-matching-SAN", cluster: &v3clusterpb.Cluster{ ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ DefaultValidationContext: &v3tlspb.CertificateValidationContext{ MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ {MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: ""}}, }, }, ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ InstanceName: rootPluginInstance, CertificateName: rootCertName, }, }, }, }, }), }, }, }, wantErr: true, }, { name: "empty-contains-in-matching-SAN", cluster: &v3clusterpb.Cluster{ ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ DefaultValidationContext: &v3tlspb.CertificateValidationContext{ MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ {MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: ""}}, }, }, ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ InstanceName: rootPluginInstance, CertificateName: rootCertName, }, }, }, }, }), }, }, }, wantErr: true, }, { name: "invalid-regex-in-matching-SAN", cluster: &v3clusterpb.Cluster{ ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ DefaultValidationContext: &v3tlspb.CertificateValidationContext{ MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ {MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexBad}}}, }, }, ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ InstanceName: rootPluginInstance, CertificateName: rootCertName, }, }, }, }, }), }, }, }, wantErr: true, }, { name: "invalid-regex-in-matching-SAN-with-new-fields", cluster: &v3clusterpb.Cluster{ ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ DefaultValidationContext: &v3tlspb.CertificateValidationContext{ MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ {MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexBad}}}, }, CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: rootPluginInstance, CertificateName: rootCertName, }, }, }, }, }, }), }, }, }, wantErr: true, }, { name: "happy-case-with-no-identity-certs-using-deprecated-fields", cluster: &v3clusterpb.Cluster{ Name: clusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ Name: "envoy.transport_sockets.tls", ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{ ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ InstanceName: rootPluginInstance, CertificateName: rootCertName, }, }, }, }), }, }, }, wantUpdate: ClusterUpdate{ ClusterName: clusterName, EDSServiceName: serviceName, SecurityCfg: &SecurityConfig{ RootInstanceName: rootPluginInstance, RootCertName: rootCertName, }, TelemetryLabels: xdsinternal.UnknownCSMLabels, }, }, { name: "happy-case-with-no-identity-certs-using-new-fields", cluster: &v3clusterpb.Cluster{ Name: clusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ Name: "envoy.transport_sockets.tls", ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{ CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: rootPluginInstance, CertificateName: rootCertName, }, }, }, }, }), }, }, }, wantUpdate: ClusterUpdate{ ClusterName: clusterName, EDSServiceName: serviceName, SecurityCfg: &SecurityConfig{ RootInstanceName: rootPluginInstance, RootCertName: rootCertName, }, TelemetryLabels: xdsinternal.UnknownCSMLabels, }, }, { name: "happy-case-with-validation-context-provider-instance-using-deprecated-fields", cluster: &v3clusterpb.Cluster{ Name: clusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ Name: "envoy.transport_sockets.tls", ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ InstanceName: identityPluginInstance, CertificateName: identityCertName, }, ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{ ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ InstanceName: rootPluginInstance, CertificateName: rootCertName, }, }, }, }), }, }, }, wantUpdate: ClusterUpdate{ ClusterName: clusterName, EDSServiceName: serviceName, SecurityCfg: &SecurityConfig{ RootInstanceName: rootPluginInstance, RootCertName: rootCertName, IdentityInstanceName: identityPluginInstance, IdentityCertName: identityCertName, }, TelemetryLabels: xdsinternal.UnknownCSMLabels, }, }, { name: "happy-case-with-validation-context-provider-instance-using-new-fields", enableSystemRootCertsFlag: true, cluster: &v3clusterpb.Cluster{ Name: clusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ Name: "envoy.transport_sockets.tls", ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: identityPluginInstance, CertificateName: identityCertName, }, ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{ CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: rootPluginInstance, CertificateName: rootCertName, }, // SystemRootCerts will be ignored due // to the presence of // CaCertificateProviderInstance. SystemRootCerts: &v3tlspb.CertificateValidationContext_SystemRootCerts{}, }, }, }, }), }, }, }, wantUpdate: ClusterUpdate{ ClusterName: clusterName, EDSServiceName: serviceName, SecurityCfg: &SecurityConfig{ RootInstanceName: rootPluginInstance, RootCertName: rootCertName, IdentityInstanceName: identityPluginInstance, IdentityCertName: identityCertName, }, TelemetryLabels: xdsinternal.UnknownCSMLabels, }, }, { name: "happy-case-with-validation-context-provider-instance-using-new-fields-and-system-root-certs", enableSystemRootCertsFlag: true, cluster: &v3clusterpb.Cluster{ Name: clusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ Name: "envoy.transport_sockets.tls", ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: identityPluginInstance, CertificateName: identityCertName, }, ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{ SystemRootCerts: &v3tlspb.CertificateValidationContext_SystemRootCerts{}, }, }, }, }), }, }, }, wantUpdate: ClusterUpdate{ ClusterName: clusterName, EDSServiceName: serviceName, SecurityCfg: &SecurityConfig{ UseSystemRootCerts: true, IdentityInstanceName: identityPluginInstance, IdentityCertName: identityCertName, }, TelemetryLabels: xdsinternal.UnknownCSMLabels, }, }, { name: "failure-case-with-validation-context-provider-instance-using-new-fields-and-system-root-certs-env-flag-disabled", cluster: &v3clusterpb.Cluster{ Name: clusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ Name: "envoy.transport_sockets.tls", ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: identityPluginInstance, CertificateName: identityCertName, }, ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{ SystemRootCerts: &v3tlspb.CertificateValidationContext_SystemRootCerts{}, }, }, }, }), }, }, }, wantErr: true, }, { name: "happy-case-with-combined-validation-context-using-deprecated-fields", cluster: &v3clusterpb.Cluster{ Name: clusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ Name: "envoy.transport_sockets.tls", ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ InstanceName: identityPluginInstance, CertificateName: identityCertName, }, ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ DefaultValidationContext: &v3tlspb.CertificateValidationContext{ MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ { MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: sanExact}, IgnoreCase: true, }, {MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: sanPrefix}}, {MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: sanSuffix}}, {MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexGood}}}, {MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: sanContains}}, }, }, ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ InstanceName: rootPluginInstance, CertificateName: rootCertName, }, }, }, }, }), }, }, }, wantUpdate: ClusterUpdate{ ClusterName: clusterName, EDSServiceName: serviceName, SecurityCfg: &SecurityConfig{ RootInstanceName: rootPluginInstance, RootCertName: rootCertName, IdentityInstanceName: identityPluginInstance, IdentityCertName: identityCertName, SubjectAltNameMatchers: []matcher.StringMatcher{ matcher.NewExactStringMatcher(sanExact, true), matcher.NewPrefixStringMatcher(sanPrefix, false), matcher.NewSuffixStringMatcher(sanSuffix, false), matcher.NewRegexStringMatcher(sanRE), matcher.NewContainsStringMatcher(sanContains, false), }, }, TelemetryLabels: xdsinternal.UnknownCSMLabels, }, }, { name: "happy-case-with-combined-validation-context-using-new-fields", cluster: &v3clusterpb.Cluster{ Name: clusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ Name: "envoy.transport_sockets.tls", ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: identityPluginInstance, CertificateName: identityCertName, }, ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ DefaultValidationContext: &v3tlspb.CertificateValidationContext{ MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ { MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: sanExact}, IgnoreCase: true, }, {MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: sanPrefix}}, {MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: sanSuffix}}, {MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexGood}}}, {MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: sanContains}}, }, CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: rootPluginInstance, CertificateName: rootCertName, }, }, }, }, }, }), }, }, }, wantUpdate: ClusterUpdate{ ClusterName: clusterName, EDSServiceName: serviceName, SecurityCfg: &SecurityConfig{ RootInstanceName: rootPluginInstance, RootCertName: rootCertName, IdentityInstanceName: identityPluginInstance, IdentityCertName: identityCertName, SubjectAltNameMatchers: []matcher.StringMatcher{ matcher.NewExactStringMatcher(sanExact, true), matcher.NewPrefixStringMatcher(sanPrefix, false), matcher.NewSuffixStringMatcher(sanSuffix, false), matcher.NewRegexStringMatcher(sanRE), matcher.NewContainsStringMatcher(sanContains, false), }, }, TelemetryLabels: xdsinternal.UnknownCSMLabels, }, }, { name: "happy-case-with-combined-validation-context-using-new-fields-and-system-root-certs", enableSystemRootCertsFlag: true, cluster: &v3clusterpb.Cluster{ Name: clusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ Name: "envoy.transport_sockets.tls", ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: identityPluginInstance, CertificateName: identityCertName, }, ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ DefaultValidationContext: &v3tlspb.CertificateValidationContext{ MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ { MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: sanExact}, IgnoreCase: true, }, {MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: sanPrefix}}, {MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: sanSuffix}}, {MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexGood}}}, {MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: sanContains}}, }, SystemRootCerts: &v3tlspb.CertificateValidationContext_SystemRootCerts{}, }, }, }, }, }), }, }, }, wantUpdate: ClusterUpdate{ ClusterName: clusterName, EDSServiceName: serviceName, SecurityCfg: &SecurityConfig{ UseSystemRootCerts: true, IdentityInstanceName: identityPluginInstance, IdentityCertName: identityCertName, SubjectAltNameMatchers: []matcher.StringMatcher{ matcher.NewExactStringMatcher(sanExact, true), matcher.NewPrefixStringMatcher(sanPrefix, false), matcher.NewSuffixStringMatcher(sanSuffix, false), matcher.NewRegexStringMatcher(sanRE), matcher.NewContainsStringMatcher(sanContains, false), }, }, TelemetryLabels: xdsinternal.UnknownCSMLabels, }, }, { name: "happy-case-with-sni-flag-enabled", enableSNIFlag: true, cluster: &v3clusterpb.Cluster{ Name: clusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ Name: "envoy.transport_sockets.tls", ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{ CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: rootPluginInstance, CertificateName: rootCertName, }, }, }, }, Sni: sniString, AutoHostSni: true, AutoSniSanValidation: true, }), }, }, }, wantUpdate: ClusterUpdate{ ClusterName: clusterName, EDSServiceName: serviceName, SecurityCfg: &SecurityConfig{ RootInstanceName: rootPluginInstance, RootCertName: rootCertName, SNI: sniString, UseAutoHostSNI: true, AutoSNISANValidation: true, }, TelemetryLabels: xdsinternal.UnknownCSMLabels, }, }, { name: "sni-env-variable-disabled-should-not-be-populated-in-update", cluster: &v3clusterpb.Cluster{ Name: clusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: serviceName, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ Name: "envoy.transport_sockets.tls", ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{ CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: rootPluginInstance, CertificateName: rootCertName, }, }, }, }, Sni: sniString, AutoHostSni: true, AutoSniSanValidation: true, }), }, }, }, wantUpdate: ClusterUpdate{ ClusterName: clusterName, EDSServiceName: serviceName, SecurityCfg: &SecurityConfig{ RootInstanceName: rootPluginInstance, RootCertName: rootCertName, }, TelemetryLabels: xdsinternal.UnknownCSMLabels, }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { testutils.SetEnvConfig(t, &envconfig.XDSSystemRootCertsEnabled, test.enableSystemRootCertsFlag) testutils.SetEnvConfig(t, &envconfig.XDSSNIEnabled, test.enableSNIFlag) update, err := validateClusterAndConstructClusterUpdate(test.cluster, nil) if (err != nil) != test.wantErr { t.Errorf("validateClusterAndConstructClusterUpdate() returned err %v wantErr %v)", err, test.wantErr) } if diff := cmp.Diff(test.wantUpdate, update, cmpopts.EquateEmpty(), cmp.AllowUnexported(regexp.Regexp{}), cmpopts.IgnoreFields(ClusterUpdate{}, "LBPolicy")); diff != "" { t.Errorf("validateClusterAndConstructClusterUpdate() returned unexpected diff (-want, +got):\n%s", diff) } }) } } func (s) TestUnmarshalCluster(t *testing.T) { const ( v3ClusterName = "v3clusterName" v3Service = "v3Service" ) var ( v3ClusterAny = testutils.MarshalAny(t, &v3clusterpb.Cluster{ Name: v3ClusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: v3Service, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, LrsServer: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{ Self: &v3corepb.SelfConfigSource{}, }, }, }) v3ClusterWithEmptyName = testutils.MarshalAny(t, &v3clusterpb.Cluster{ Name: "", ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: v3Service, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, LrsServer: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{ Self: &v3corepb.SelfConfigSource{}, }, }, }) v3ClusterAnyWithEDSConfigSourceSelf = testutils.MarshalAny(t, &v3clusterpb.Cluster{ Name: v3ClusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{}, }, ServiceName: v3Service, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, LrsServer: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{ Self: &v3corepb.SelfConfigSource{}, }, }, }) v3ClusterAnyWithTelemetryLabels = testutils.MarshalAny(t, &v3clusterpb.Cluster{ Name: v3ClusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: v3Service, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, LrsServer: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{ Self: &v3corepb.SelfConfigSource{}, }, }, Metadata: &v3corepb.Metadata{ FilterMetadata: map[string]*structpb.Struct{ "com.google.csm.telemetry_labels": { Fields: map[string]*structpb.Value{ "service_name": structpb.NewStringValue("grpc-service"), "service_namespace": structpb.NewStringValue("grpc-service-namespace"), }, }, }, }, }) v3ClusterAnyWithTelemetryLabelsIgnoreSome = testutils.MarshalAny(t, &v3clusterpb.Cluster{ Name: v3ClusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: v3Service, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, LrsServer: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{ Self: &v3corepb.SelfConfigSource{}, }, }, Metadata: &v3corepb.Metadata{ FilterMetadata: map[string]*structpb.Struct{ "com.google.csm.telemetry_labels": { Fields: map[string]*structpb.Value{ "string-value-should-ignore": structpb.NewStringValue("string-val"), "float-value-ignore": structpb.NewNumberValue(3), "bool-value-ignore": structpb.NewBoolValue(false), "service_name": structpb.NewStringValue("grpc-service"), // shouldn't ignore "service_namespace": structpb.NewNullValue(), // should ignore - wrong type }, }, "ignore-this-metadata": { // should ignore this filter_metadata Fields: map[string]*structpb.Value{ "service_namespace": structpb.NewStringValue("string-val-should-ignore"), }, }, }, }, }) ) serverCfg, err := bootstrap.ServerConfigForTesting(bootstrap.ServerConfigTestingOptions{URI: "test-server"}) if err != nil { t.Fatalf("Failed to create server config for testing: %v", err) } tests := []struct { name string resource *anypb.Any serverCfg *bootstrap.ServerConfig wantName string wantUpdate ClusterUpdate wantErr bool }{ { name: "non-cluster resource type", resource: &anypb.Any{TypeUrl: version.V3HTTPConnManagerURL}, wantErr: true, }, { name: "badly marshaled cluster resource", resource: &anypb.Any{ TypeUrl: version.V3ClusterURL, Value: []byte{1, 2, 3, 4}, }, wantErr: true, }, { name: "bad_cluster_resource", resource: testutils.MarshalAny(t, &v3clusterpb.Cluster{ Name: "test", ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_STATIC}, }), wantName: "test", wantErr: true, }, { name: "cluster_resource_with_non-self_lrs_server_field", resource: testutils.MarshalAny(t, &v3clusterpb.Cluster{ Name: "test", ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: v3Service, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, LrsServer: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, }), wantName: "test", wantErr: true, }, { name: "v3_cluster", resource: v3ClusterAny, serverCfg: serverCfg, wantName: v3ClusterName, wantUpdate: ClusterUpdate{ ClusterName: v3ClusterName, EDSServiceName: v3Service, LRSServerConfig: serverCfg, Raw: v3ClusterAny, TelemetryLabels: xdsinternal.UnknownCSMLabels, }, }, { name: "cluster_resource_with_empty_name", resource: v3ClusterWithEmptyName, serverCfg: serverCfg, wantName: "", wantErr: true, }, { name: "v3_cluster_wrapped", resource: testutils.MarshalAny(t, &v3discoverypb.Resource{Resource: v3ClusterAny}), serverCfg: serverCfg, wantName: v3ClusterName, wantUpdate: ClusterUpdate{ ClusterName: v3ClusterName, EDSServiceName: v3Service, LRSServerConfig: serverCfg, Raw: v3ClusterAny, TelemetryLabels: xdsinternal.UnknownCSMLabels, }, }, { name: "v3_cluster_with_EDS_config_source_self", resource: v3ClusterAnyWithEDSConfigSourceSelf, serverCfg: serverCfg, wantName: v3ClusterName, wantUpdate: ClusterUpdate{ ClusterName: v3ClusterName, EDSServiceName: v3Service, LRSServerConfig: serverCfg, Raw: v3ClusterAnyWithEDSConfigSourceSelf, TelemetryLabels: xdsinternal.UnknownCSMLabels, }, }, { name: "v3_cluster_with_telemetry_case", resource: v3ClusterAnyWithTelemetryLabels, serverCfg: serverCfg, wantName: v3ClusterName, wantUpdate: ClusterUpdate{ ClusterName: v3ClusterName, EDSServiceName: v3Service, LRSServerConfig: serverCfg, Raw: v3ClusterAnyWithTelemetryLabels, TelemetryLabels: map[string]string{ "csm.service_name": "grpc-service", "csm.service_namespace_name": "grpc-service-namespace", }, }, }, { name: "v3_metadata_ignore_other_types_not_string_and_not_com.google.csm.telemetry_labels", resource: v3ClusterAnyWithTelemetryLabelsIgnoreSome, serverCfg: serverCfg, wantName: v3ClusterName, wantUpdate: ClusterUpdate{ ClusterName: v3ClusterName, EDSServiceName: v3Service, LRSServerConfig: serverCfg, Raw: v3ClusterAnyWithTelemetryLabelsIgnoreSome, TelemetryLabels: map[string]string{ "csm.service_name": "grpc-service", "csm.service_namespace_name": "unknown", }, }, }, { name: "xdstp_cluster_resource_with_unset_EDS_service_name", resource: testutils.MarshalAny(t, &v3clusterpb.Cluster{ Name: "xdstp:foo", ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: "", }, }), wantName: "xdstp:foo", wantErr: true, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { name, update, err := unmarshalClusterResource(test.resource, test.serverCfg) if (err != nil) != test.wantErr { t.Fatalf("unmarshalClusterResource(%s), got err: %v, wantErr: %v", pretty.ToJSON(test.resource), err, test.wantErr) } if name != test.wantName { t.Errorf("unmarshalClusterResource(%s), got name: %s, want: %s", pretty.ToJSON(test.resource), name, test.wantName) } if diff := cmp.Diff(update, test.wantUpdate, cmpOpts, cmpopts.IgnoreFields(ClusterUpdate{}, "LBPolicy")); diff != "" { t.Errorf("unmarshalClusterResource(%s), got unexpected update, diff (-got +want): %v", pretty.ToJSON(test.resource), diff) } }) } } // enableGCPAuthenticationFilter enables A83 support for the duration of the // test by: // 1. Setting GRPC_EXPERIMENTAL_XDS_GCP_AUTHENTICATION_FILTER env var to true. // 2. Registering the audience converter (and restore the original on cleanup). func enableGCPAuthenticationFilter(t *testing.T) { testutils.SetEnvConfig(t, &envconfig.GCPAuthenticationFilterEnabled, true) cleanup, err := RegisterMetadataConverterForTesting(version.V3AudienceURL) if err != nil { t.Fatalf("RegisterMetadataConverterForTesting(%q) failed: %v", version.V3AudienceURL, err) } t.Cleanup(cleanup) } // disableGCPAuthenticationFilter disables A83 support for the duration of the // test by: // 1. Setting GRPC_EXPERIMENTAL_XDS_GCP_AUTHENTICATION_FILTER env var to false. // 2. Unregistering the audience converter for the duration of the test // (and restoring the original on cleanup). func disableGCPAuthenticationFilter(t *testing.T) { testutils.SetEnvConfig(t, &envconfig.GCPAuthenticationFilterEnabled, false) cleanup := UnregisterMetadataConverterForTesting(version.V3AudienceURL) t.Cleanup(cleanup) } // Tests custom metadata parsing for success cases when the // GRPC_EXPERIMENTAL_XDS_GCP_AUTHENTICATION_FILTER env var is set. func (s) TestValidateClusterAndConstructClusterUpdate_GCP_AUTHENTICATION_FILTER_EnvVarOn(t *testing.T) { enableGCPAuthenticationFilter(t) const ( v3ClusterName = "v3clusterName" v3Service = "v3Service" ) tests := []struct { name string cluster *v3clusterpb.Cluster wantUpdate ClusterUpdate }{ { name: "typed_filter_metadata_in_cluster", cluster: &v3clusterpb.Cluster{ Name: v3ClusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: v3Service, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, Metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "com.google.grpc.gcp_authn": testutils.MarshalAny(t, &v3gcpauthnpb.Audience{ Url: "https://example.com", }), }, }, }, wantUpdate: ClusterUpdate{ ClusterName: v3ClusterName, EDSServiceName: v3Service, Metadata: map[string]any{ "com.google.grpc.gcp_authn": AudienceMetadataValue{ Audience: "https://example.com", }, }, TelemetryLabels: xdsinternal.UnknownCSMLabels, }, }, { name: "typed_filter_metadata_over_filter_metadata_in_cluster", cluster: &v3clusterpb.Cluster{ Name: v3ClusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: v3Service, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, Metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "com.google.grpc.gcp_authn": testutils.MarshalAny(t, &v3gcpauthnpb.Audience{ Url: "https://example.com", }), }, FilterMetadata: map[string]*structpb.Struct{ "com.google.grpc.gcp_authn": { Fields: map[string]*structpb.Value{ "url": structpb.NewStringValue("https://example.com"), }, }, }, }, }, wantUpdate: ClusterUpdate{ ClusterName: v3ClusterName, EDSServiceName: v3Service, Metadata: map[string]any{ "com.google.grpc.gcp_authn": AudienceMetadataValue{ Audience: "https://example.com", }, }, TelemetryLabels: xdsinternal.UnknownCSMLabels, }, }, { name: "both_filter_and_typed_filter_metadata_in_cluster", cluster: &v3clusterpb.Cluster{ Name: v3ClusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: v3Service, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, Metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "com.google.grpc.gcp_authn": testutils.MarshalAny(t, &v3gcpauthnpb.Audience{ Url: "https://example.com", }), }, FilterMetadata: map[string]*structpb.Struct{ "another-test-key": { Fields: map[string]*structpb.Value{ "url": structpb.NewStringValue("https://example.com"), }, }, }, }, }, wantUpdate: ClusterUpdate{ ClusterName: v3ClusterName, EDSServiceName: v3Service, Metadata: map[string]any{ "com.google.grpc.gcp_authn": AudienceMetadataValue{ Audience: "https://example.com", }, "another-test-key": StructMetadataValue{Data: map[string]any{ "url": "https://example.com", }}, }, TelemetryLabels: xdsinternal.UnknownCSMLabels, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := validateClusterAndConstructClusterUpdate(tt.cluster, nil) if err != nil { t.Fatalf("validateClusterAndConstructClusterUpdate() failed: %v", err) } if diff := cmp.Diff(tt.wantUpdate, got, cmpopts.EquateEmpty(), cmpopts.IgnoreFields(ClusterUpdate{}, "LBPolicy")); diff != "" { t.Errorf("validateClusterAndConstructClusterUpdate() returned unexpected diff (-want +got):\n%s", diff) } }) } } // Tests custom metadata parsing for failure cases when the // GRPC_EXPERIMENTAL_XDS_GCP_AUTHENTICATION_FILTER env var is not set. func (s) TestValidateClusterAndConstructClusterUpdate_GCP_AUTHENTICATION_FILTER_EnvVarOff(t *testing.T) { disableGCPAuthenticationFilter(t) const ( v3ClusterName = "v3ClusterName" v3Service = "v3Service" ) tests := []struct { name string cluster *v3clusterpb.Cluster wantUpdate ClusterUpdate }{ { name: "typed_filter_metadata_in_cluster", cluster: &v3clusterpb.Cluster{ Name: v3ClusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: v3Service, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, Metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "com.google.grpc.gcp_authn": testutils.MarshalAny(t, &v3gcpauthnpb.Audience{ Url: "https://example.com", }), }, }, }, wantUpdate: ClusterUpdate{ ClusterName: v3ClusterName, EDSServiceName: v3Service, TelemetryLabels: xdsinternal.UnknownCSMLabels, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := validateClusterAndConstructClusterUpdate(tt.cluster, nil) if err != nil { t.Fatalf("validateClusterAndConstructClusterUpdate() failed: %v", err) } if diff := cmp.Diff(tt.wantUpdate, got, cmpopts.EquateEmpty(), cmpopts.IgnoreFields(ClusterUpdate{}, "LBPolicy")); diff != "" { t.Errorf("validateClusterAndConstructClusterUpdate() returned unexpected diff (-want +got):\n%s", diff) } }) } } // Tests custom metadata parsing for converter failure cases when the // GRPC_EXPERIMENTAL_XDS_GCP_AUTHENTICATION_FILTER environment variable is set. func (s) TestValidateClusterAndConstructClusterUpdate_GCP_AUTHENTICATION_FILTER_ConverterFailure(t *testing.T) { enableGCPAuthenticationFilter(t) tests := []struct { name string cluster *v3clusterpb.Cluster wantErr string }{ { name: "converter_failure_in_cluster", cluster: &v3clusterpb.Cluster{ Name: "v3clusterName", ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: "v3Service", }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, Metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "com.google.grpc.gcp_authn": testutils.MarshalAny(t, &v3gcpauthnpb.Audience{ Url: "", }), }, }, }, wantErr: "empty url field in audience metadata", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if _, err := validateClusterAndConstructClusterUpdate(tt.cluster, nil); err == nil || !strings.Contains(err.Error(), tt.wantErr) { t.Fatalf("validateClusterAndConstructClusterUpdate() did not return error when expected: %v", err) } }) } } func (s) TestValidateClusterWithOutlierDetection(t *testing.T) { odToClusterProto := func(od *v3clusterpb.OutlierDetection) *v3clusterpb.Cluster { // Cluster parsing doesn't fail with respect to fields orthogonal to // outlier detection. return &v3clusterpb.Cluster{ Name: clusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, OutlierDetection: od, } } tests := []struct { name string cluster *v3clusterpb.Cluster wantODCfg string wantErr bool }{ { name: "success-and-failure-null", cluster: odToClusterProto(&v3clusterpb.OutlierDetection{}), wantODCfg: `{"successRateEjection": {}}`, }, { name: "success-and-failure-zero", cluster: odToClusterProto(&v3clusterpb.OutlierDetection{ EnforcingSuccessRate: &wrapperspb.UInt32Value{Value: 0}, // Thus doesn't create sre - to focus on fpe EnforcingFailurePercentage: &wrapperspb.UInt32Value{Value: 0}, }), wantODCfg: `{}`, }, { name: "some-fields-set", cluster: odToClusterProto(&v3clusterpb.OutlierDetection{ Interval: &durationpb.Duration{Seconds: 1}, MaxEjectionTime: &durationpb.Duration{Seconds: 3}, EnforcingSuccessRate: &wrapperspb.UInt32Value{Value: 3}, SuccessRateRequestVolume: &wrapperspb.UInt32Value{Value: 5}, EnforcingFailurePercentage: &wrapperspb.UInt32Value{Value: 7}, FailurePercentageRequestVolume: &wrapperspb.UInt32Value{Value: 9}, }), wantODCfg: `{ "interval": "1s", "maxEjectionTime": "3s", "successRateEjection": { "enforcementPercentage": 3, "requestVolume": 5 }, "failurePercentageEjection": { "enforcementPercentage": 7, "requestVolume": 9 } }`, }, { name: "every-field-set-non-zero", cluster: odToClusterProto(&v3clusterpb.OutlierDetection{ // all fields set (including ones that will be layered) should // pick up those too and explicitly all fields, including those // put in layers, in the JSON generated. Interval: &durationpb.Duration{Seconds: 1}, BaseEjectionTime: &durationpb.Duration{Seconds: 2}, MaxEjectionTime: &durationpb.Duration{Seconds: 3}, MaxEjectionPercent: &wrapperspb.UInt32Value{Value: 1}, SuccessRateStdevFactor: &wrapperspb.UInt32Value{Value: 2}, EnforcingSuccessRate: &wrapperspb.UInt32Value{Value: 3}, SuccessRateMinimumHosts: &wrapperspb.UInt32Value{Value: 4}, SuccessRateRequestVolume: &wrapperspb.UInt32Value{Value: 5}, FailurePercentageThreshold: &wrapperspb.UInt32Value{Value: 6}, EnforcingFailurePercentage: &wrapperspb.UInt32Value{Value: 7}, FailurePercentageMinimumHosts: &wrapperspb.UInt32Value{Value: 8}, FailurePercentageRequestVolume: &wrapperspb.UInt32Value{Value: 9}, }), wantODCfg: `{ "interval": "1s", "baseEjectionTime": "2s", "maxEjectionTime": "3s", "maxEjectionPercent": 1, "successRateEjection": { "stdevFactor": 2, "enforcementPercentage": 3, "minimumHosts": 4, "requestVolume": 5 }, "failurePercentageEjection": { "threshold": 6, "enforcementPercentage": 7, "minimumHosts": 8, "requestVolume": 9 } }`, }, { name: "interval-is-negative", cluster: odToClusterProto(&v3clusterpb.OutlierDetection{Interval: &durationpb.Duration{Seconds: -10}}), wantErr: true, }, { name: "interval-overflows", cluster: odToClusterProto(&v3clusterpb.OutlierDetection{Interval: &durationpb.Duration{Seconds: 315576000001}}), wantErr: true, }, { name: "base-ejection-time-is-negative", cluster: odToClusterProto(&v3clusterpb.OutlierDetection{BaseEjectionTime: &durationpb.Duration{Seconds: -10}}), wantErr: true, }, { name: "base-ejection-time-overflows", cluster: odToClusterProto(&v3clusterpb.OutlierDetection{BaseEjectionTime: &durationpb.Duration{Seconds: 315576000001}}), wantErr: true, }, { name: "max-ejection-time-is-negative", cluster: odToClusterProto(&v3clusterpb.OutlierDetection{MaxEjectionTime: &durationpb.Duration{Seconds: -10}}), wantErr: true, }, { name: "max-ejection-time-overflows", cluster: odToClusterProto(&v3clusterpb.OutlierDetection{MaxEjectionTime: &durationpb.Duration{Seconds: 315576000001}}), wantErr: true, }, { name: "max-ejection-percent-is-greater-than-100", cluster: odToClusterProto(&v3clusterpb.OutlierDetection{MaxEjectionPercent: &wrapperspb.UInt32Value{Value: 150}}), wantErr: true, }, { name: "enforcing-success-rate-is-greater-than-100", cluster: odToClusterProto(&v3clusterpb.OutlierDetection{EnforcingSuccessRate: &wrapperspb.UInt32Value{Value: 150}}), wantErr: true, }, { name: "failure-percentage-threshold-is-greater-than-100", cluster: odToClusterProto(&v3clusterpb.OutlierDetection{FailurePercentageThreshold: &wrapperspb.UInt32Value{Value: 150}}), wantErr: true, }, { name: "enforcing-failure-percentage-is-greater-than-100", cluster: odToClusterProto(&v3clusterpb.OutlierDetection{EnforcingFailurePercentage: &wrapperspb.UInt32Value{Value: 150}}), wantErr: true, }, // A Outlier Detection proto not present should lead to a nil // OutlierDetection field in the ClusterUpdate, which is implicitly // tested in every other test in this file. } for _, test := range tests { t.Run(test.name, func(t *testing.T) { update, err := validateClusterAndConstructClusterUpdate(test.cluster, nil) if (err != nil) != test.wantErr { t.Errorf("validateClusterAndConstructClusterUpdate() returned err %v wantErr %v)", err, test.wantErr) } if test.wantErr { return } // got and want must be unmarshalled since JSON strings shouldn't // generally be directly compared. var got map[string]any if err := json.Unmarshal(update.OutlierDetection, &got); err != nil { t.Fatalf("Error unmarshalling update.OutlierDetection (%q): %v", update.OutlierDetection, err) } var want map[string]any if err := json.Unmarshal(json.RawMessage(test.wantODCfg), &want); err != nil { t.Fatalf("Error unmarshalling wantODCfg (%q): %v", test.wantODCfg, err) } if diff := cmp.Diff(got, want); diff != "" { t.Fatalf("cluster.OutlierDetection got unexpected output, diff (-got, +want): %v", diff) } }) } } func (s) TestValidateClusterWithSecurityConfig_SNITooLong(t *testing.T) { testutils.SetEnvConfig(t, &envconfig.XDSSNIEnabled, true) cluster := &v3clusterpb.Cluster{ Name: "cluster", ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: "service", }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ Name: "envoy.transport_sockets.tls", ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{ CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: "rootPluginInstance", CertificateName: "rootCert", }, }, }, }, Sni: strings.Repeat("a", 256), }), }, }, } wantErr := "exceeds max length" if _, err := validateClusterAndConstructClusterUpdate(cluster, nil); err == nil || !strings.Contains(err.Error(), wantErr) { t.Fatalf("validateClusterAndConstructClusterUpdate() returned err: %v, want err containing: %s", err, wantErr) } } // TestValidateCluster_LRSReportEndpointMetrics tests the parsing of CDS // lrs_report_endpoint_metrics field and verifies it is correctly ignored when // the feature flag is disabled and parsed correctly when enabled, including // precedence rules for named_metrics.*. func (s) TestValidateCluster_LRSReportEndpointMetrics(t *testing.T) { tests := []struct { desc string lrsPropEnabled bool lrsReportEndpointMetrics []string wantMetrics *LRSReportEndpointMetricsConfig }{ { desc: "DisabledByEnvVar", lrsPropEnabled: false, lrsReportEndpointMetrics: []string{"cpu_utilization", "named_metrics.foo", "named_metrics.*"}, wantMetrics: nil, }, { desc: "AllValidMetrics", lrsPropEnabled: true, lrsReportEndpointMetrics: []string{"cpu_utilization", "mem_utilization", "application_utilization", "named_metrics.foo", "named_metrics.bar"}, wantMetrics: &LRSReportEndpointMetricsConfig{ CPUUtilization: true, MemUtilization: true, ApplicationUtilization: true, NamedMetricsAll: false, NamedMetrics: map[string]struct{}{ "foo": {}, "bar": {}, }, }, }, { desc: "EnabledByEnvVar", lrsPropEnabled: true, lrsReportEndpointMetrics: []string{"named_metrics.*", "named_metrics.foo", "cpu_utilization"}, wantMetrics: &LRSReportEndpointMetricsConfig{ CPUUtilization: true, NamedMetricsAll: true, NamedMetrics: nil, }, }, { desc: "IgnoresInvalidMetrics", lrsPropEnabled: true, lrsReportEndpointMetrics: []string{"named_metrics.", "invalid_metric", "named_metrics.valid"}, wantMetrics: &LRSReportEndpointMetricsConfig{ NamedMetricsAll: false, NamedMetrics: map[string]struct{}{ "valid": {}, }, }, }, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { testutils.SetEnvConfig(t, &envconfig.XDSORCAToLRSPropEnabled, tt.lrsPropEnabled) cluster := &v3clusterpb.Cluster{ Name: "test-cluster", ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: "test-service", }, LrsReportEndpointMetrics: tt.lrsReportEndpointMetrics, } update, err := validateClusterAndConstructClusterUpdate(cluster, nil) if err != nil { t.Fatalf("validateClusterAndConstructClusterUpdate() failed: %v", err) } if !update.LRSReportEndpointMetrics.Equal(tt.wantMetrics) { t.Fatalf("LRSReportEndpointMetrics:\n got %+v\nwant %+v", update.LRSReportEndpointMetrics, tt.wantMetrics) } }) } } // Test verifies the validation and unmarshaling of CDS resources containing // Http11ProxyUpstreamTransport transport socket wrapper when the env var // is disabled. func (s) TestValidateCluster_Http11ProxyUpstreamTransport_EnvVarOff(t *testing.T) { testutils.SetEnvConfig(t, &envconfig.XDSHTTPConnectEnabled, false) const ( v3ClusterName = "v3clusterName" v3Service = "v3Service" ) tests := []struct { desc string cluster *v3clusterpb.Cluster wantErr bool }{ { desc: "HTTP11ProxyUpstreamTransport_with_UpstreamTLSContext", cluster: &v3clusterpb.Cluster{ Name: v3ClusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: v3Service, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ Name: "envoy.transport_sockets.http_11_proxy", ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3http11proxypb.Http11ProxyUpstreamTransport{ TransportSocket: &v3corepb.TransportSocket{ Name: "envoy.transport_sockets.tls", ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: "identity_provider", }, ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{ CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: "root_provider", }, }, }, }, }), }, }, }), }, }, }, wantErr: true, }, { desc: "HTTP11ProxyUpstreamTransport_with_plaintext_inner_socket", cluster: &v3clusterpb.Cluster{ Name: v3ClusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: v3Service, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ Name: "envoy.transport_sockets.tls", ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3http11proxypb.Http11ProxyUpstreamTransport{ TransportSocket: nil, }), }, }, }, wantErr: true, }, { desc: "HTTP11ProxyUpstreamTransport_with_unsupported_inner_socket", cluster: &v3clusterpb.Cluster{ Name: v3ClusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: v3Service, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: &anypb.Any{ TypeUrl: version.V3UpstreamTLSContextURL, Value: []byte{1, 2, 3, 4}, }, }, }, }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { if _, err := validateClusterAndConstructClusterUpdate(tt.cluster, nil); err == nil { t.Fatalf("validateClusterAndConstructClusterUpdate() did not return error when expected") } }) } } // Test verifies the validation and unmarshaling of CDS resources containing // Http11ProxyUpstreamTransport transport socket wrapper when the env var // is enabled. func (s) TestValidateCluster_Http11ProxyUpstreamTransport_EnvVarOn(t *testing.T) { testutils.SetEnvConfig(t, &envconfig.XDSHTTPConnectEnabled, true) const ( v3ClusterName = "v3clusterName" v3Service = "v3Service" ) cmpOpts := []cmp.Option{ cmpopts.EquateEmpty(), cmpopts.IgnoreFields(ClusterUpdate{}, "Raw", "LBPolicy", "TelemetryLabels"), } tests := []struct { desc string cluster *v3clusterpb.Cluster wantUpdate ClusterUpdate wantErr bool }{ { desc: "HTTP11ProxyUpstreamTransport_with_UpstreamTLSContext", cluster: &v3clusterpb.Cluster{ Name: v3ClusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: v3Service, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ Name: "envoy.transport_sockets.http_11_proxy", ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3http11proxypb.Http11ProxyUpstreamTransport{ TransportSocket: &v3corepb.TransportSocket{ Name: "envoy.transport_sockets.tls", ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ CommonTlsContext: &v3tlspb.CommonTlsContext{ TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: "identity_provider", }, ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ ValidationContext: &v3tlspb.CertificateValidationContext{ CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ InstanceName: "root_provider", }, }, }, }, }), }, }, }), }, }, }, wantUpdate: ClusterUpdate{ ClusterName: v3ClusterName, EDSServiceName: v3Service, IsHTTP11ProxyEnabled: true, SecurityCfg: &SecurityConfig{ IdentityInstanceName: "identity_provider", RootInstanceName: "root_provider", }, TelemetryLabels: xdsinternal.UnknownCSMLabels, }, }, { desc: "HTTP11ProxyUpstreamTransport_with_plaintext_inner_socket", cluster: &v3clusterpb.Cluster{ Name: v3ClusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: v3Service, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ Name: "envoy.transport_sockets.tls", ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: testutils.MarshalAny(t, &v3http11proxypb.Http11ProxyUpstreamTransport{ TransportSocket: nil, }), }, }, }, wantUpdate: ClusterUpdate{ ClusterName: v3ClusterName, EDSServiceName: v3Service, IsHTTP11ProxyEnabled: true, SecurityCfg: nil, TelemetryLabels: xdsinternal.UnknownCSMLabels, }, }, { desc: "HTTP11ProxyUpstreamTransport_with_unsupported_inner_socket", cluster: &v3clusterpb.Cluster{ Name: v3ClusterName, ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ EdsConfig: &v3corepb.ConfigSource{ ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ Ads: &v3corepb.AggregatedConfigSource{}, }, }, ServiceName: v3Service, }, LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, TransportSocket: &v3corepb.TransportSocket{ ConfigType: &v3corepb.TransportSocket_TypedConfig{ TypedConfig: &anypb.Any{ TypeUrl: version.V3UpstreamTLSContextURL, Value: []byte{1, 2, 3, 4}, }, }, }, }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { gotUpdate, err := validateClusterAndConstructClusterUpdate(tt.cluster, nil) if tt.wantErr { if err == nil { t.Fatalf("validateClusterAndConstructClusterUpdate() did not return an error when expected") } return } if err != nil { t.Fatalf("validateClusterAndConstructClusterUpdate() returned an error: %v", err) } if diff := cmp.Diff(gotUpdate, tt.wantUpdate, cmpOpts...); diff != "" { t.Errorf("validateClusterAndConstructClusterUpdate() returned unexpected diff (-got +want):\n%s", diff) } }) } }