...

Source file src/google.golang.org/grpc/xds/internal/xdsclient/xdsresource/unmarshal_cds_test.go

Documentation: google.golang.org/grpc/xds/internal/xdsclient/xdsresource

     1  /*
     2   *
     3   * Copyright 2021 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package xdsresource
    19  
    20  import (
    21  	"encoding/json"
    22  	"regexp"
    23  	"strings"
    24  	"testing"
    25  
    26  	"github.com/google/go-cmp/cmp"
    27  	"github.com/google/go-cmp/cmp/cmpopts"
    28  	"google.golang.org/grpc/internal/pretty"
    29  	"google.golang.org/grpc/internal/testutils"
    30  	"google.golang.org/grpc/internal/xds/bootstrap"
    31  	"google.golang.org/grpc/internal/xds/matcher"
    32  	"google.golang.org/grpc/xds/internal/xdsclient/xdsresource/version"
    33  
    34  	v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
    35  	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    36  	v3endpointpb "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
    37  	v3aggregateclusterpb "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/aggregate/v3"
    38  	v3leastrequestpb "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/least_request/v3"
    39  	v3ringhashpb "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/ring_hash/v3"
    40  	v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
    41  	v3discoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
    42  	v3matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
    43  	"google.golang.org/protobuf/types/known/anypb"
    44  	"google.golang.org/protobuf/types/known/durationpb"
    45  	"google.golang.org/protobuf/types/known/structpb"
    46  	"google.golang.org/protobuf/types/known/wrapperspb"
    47  )
    48  
    49  const (
    50  	clusterName = "clusterName"
    51  	serviceName = "service"
    52  )
    53  
    54  func (s) TestValidateCluster_Failure(t *testing.T) {
    55  	tests := []struct {
    56  		name    string
    57  		cluster *v3clusterpb.Cluster
    58  		wantErr bool
    59  	}{
    60  		{
    61  			name: "non-supported-cluster-type-static",
    62  			cluster: &v3clusterpb.Cluster{
    63  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_STATIC},
    64  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
    65  					EdsConfig: &v3corepb.ConfigSource{
    66  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
    67  							Ads: &v3corepb.AggregatedConfigSource{},
    68  						},
    69  					},
    70  				},
    71  				LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST,
    72  			},
    73  			wantErr: true,
    74  		},
    75  		{
    76  			name: "non-supported-cluster-type-original-dst",
    77  			cluster: &v3clusterpb.Cluster{
    78  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_ORIGINAL_DST},
    79  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
    80  					EdsConfig: &v3corepb.ConfigSource{
    81  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
    82  							Ads: &v3corepb.AggregatedConfigSource{},
    83  						},
    84  					},
    85  				},
    86  				LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST,
    87  			},
    88  			wantErr: true,
    89  		},
    90  		{
    91  			name: "no-eds-config",
    92  			cluster: &v3clusterpb.Cluster{
    93  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
    94  				LbPolicy:             v3clusterpb.Cluster_ROUND_ROBIN,
    95  			},
    96  			wantErr: true,
    97  		},
    98  		{
    99  			name: "no-ads-config-source",
   100  			cluster: &v3clusterpb.Cluster{
   101  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   102  				EdsClusterConfig:     &v3clusterpb.Cluster_EdsClusterConfig{},
   103  				LbPolicy:             v3clusterpb.Cluster_ROUND_ROBIN,
   104  			},
   105  			wantErr: true,
   106  		},
   107  		{
   108  			name: "non-round-robin-or-ring-hash-lb-policy",
   109  			cluster: &v3clusterpb.Cluster{
   110  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   111  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   112  					EdsConfig: &v3corepb.ConfigSource{
   113  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   114  							Ads: &v3corepb.AggregatedConfigSource{},
   115  						},
   116  					},
   117  				},
   118  				LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST,
   119  			},
   120  			wantErr: true,
   121  		},
   122  		{
   123  			name: "logical-dns-multiple-localities",
   124  			cluster: &v3clusterpb.Cluster{
   125  				Name:                 clusterName,
   126  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_LOGICAL_DNS},
   127  				LbPolicy:             v3clusterpb.Cluster_ROUND_ROBIN,
   128  				LoadAssignment: &v3endpointpb.ClusterLoadAssignment{
   129  					Endpoints: []*v3endpointpb.LocalityLbEndpoints{
   130  						// Invalid if there are more than one locality.
   131  						{LbEndpoints: nil},
   132  						{LbEndpoints: nil},
   133  					},
   134  				},
   135  			},
   136  			wantErr: true,
   137  		},
   138  		{
   139  			name: "ring-hash-hash-function-not-xx-hash",
   140  			cluster: &v3clusterpb.Cluster{
   141  				LbPolicy: v3clusterpb.Cluster_RING_HASH,
   142  				LbConfig: &v3clusterpb.Cluster_RingHashLbConfig_{
   143  					RingHashLbConfig: &v3clusterpb.Cluster_RingHashLbConfig{
   144  						HashFunction: v3clusterpb.Cluster_RingHashLbConfig_MURMUR_HASH_2,
   145  					},
   146  				},
   147  			},
   148  			wantErr: true,
   149  		},
   150  		{
   151  			name: "least-request-choice-count-less-than-two",
   152  			cluster: &v3clusterpb.Cluster{
   153  				LbPolicy: v3clusterpb.Cluster_RING_HASH,
   154  				LbConfig: &v3clusterpb.Cluster_LeastRequestLbConfig_{
   155  					LeastRequestLbConfig: &v3clusterpb.Cluster_LeastRequestLbConfig{
   156  						ChoiceCount: wrapperspb.UInt32(1),
   157  					},
   158  				},
   159  			},
   160  			wantErr: true,
   161  		},
   162  		{
   163  			name: "ring-hash-max-bound-greater-than-upper-bound",
   164  			cluster: &v3clusterpb.Cluster{
   165  				LbPolicy: v3clusterpb.Cluster_RING_HASH,
   166  				LbConfig: &v3clusterpb.Cluster_RingHashLbConfig_{
   167  					RingHashLbConfig: &v3clusterpb.Cluster_RingHashLbConfig{
   168  						MaximumRingSize: wrapperspb.UInt64(ringHashSizeUpperBound + 1),
   169  					},
   170  				},
   171  			},
   172  			wantErr: true,
   173  		},
   174  		{
   175  			name: "ring-hash-max-bound-greater-than-upper-bound-load-balancing-policy",
   176  			cluster: &v3clusterpb.Cluster{
   177  				Name:                 clusterName,
   178  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   179  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   180  					EdsConfig: &v3corepb.ConfigSource{
   181  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   182  							Ads: &v3corepb.AggregatedConfigSource{},
   183  						},
   184  					},
   185  					ServiceName: serviceName,
   186  				},
   187  				LoadBalancingPolicy: &v3clusterpb.LoadBalancingPolicy{
   188  					Policies: []*v3clusterpb.LoadBalancingPolicy_Policy{
   189  						{
   190  							TypedExtensionConfig: &v3corepb.TypedExtensionConfig{
   191  								TypedConfig: testutils.MarshalAny(t, &v3ringhashpb.RingHash{
   192  									HashFunction:    v3ringhashpb.RingHash_XX_HASH,
   193  									MinimumRingSize: wrapperspb.UInt64(10),
   194  									MaximumRingSize: wrapperspb.UInt64(ringHashSizeUpperBound + 1),
   195  								}),
   196  							},
   197  						},
   198  					},
   199  				},
   200  			},
   201  			wantErr: true,
   202  		},
   203  		{
   204  			name: "least-request-unsupported-in-converter-since-env-var-unset",
   205  			cluster: &v3clusterpb.Cluster{
   206  				Name:                 clusterName,
   207  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   208  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   209  					EdsConfig: &v3corepb.ConfigSource{
   210  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   211  							Ads: &v3corepb.AggregatedConfigSource{},
   212  						},
   213  					},
   214  					ServiceName: serviceName,
   215  				},
   216  				LoadBalancingPolicy: &v3clusterpb.LoadBalancingPolicy{
   217  					Policies: []*v3clusterpb.LoadBalancingPolicy_Policy{
   218  						{
   219  							TypedExtensionConfig: &v3corepb.TypedExtensionConfig{
   220  								TypedConfig: testutils.MarshalAny(t, &v3leastrequestpb.LeastRequest{}),
   221  							},
   222  						},
   223  					},
   224  				},
   225  			},
   226  			wantErr: true,
   227  		},
   228  		{
   229  			name: "aggregate-nil-clusters",
   230  			cluster: &v3clusterpb.Cluster{
   231  				Name: clusterName,
   232  				ClusterDiscoveryType: &v3clusterpb.Cluster_ClusterType{
   233  					ClusterType: &v3clusterpb.Cluster_CustomClusterType{
   234  						Name:        "envoy.clusters.aggregate",
   235  						TypedConfig: testutils.MarshalAny(t, &v3aggregateclusterpb.ClusterConfig{}),
   236  					},
   237  				},
   238  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
   239  			},
   240  			wantErr: true,
   241  		},
   242  		{
   243  			name: "aggregate-empty-clusters",
   244  			cluster: &v3clusterpb.Cluster{
   245  				Name: clusterName,
   246  				ClusterDiscoveryType: &v3clusterpb.Cluster_ClusterType{
   247  					ClusterType: &v3clusterpb.Cluster_CustomClusterType{
   248  						Name: "envoy.clusters.aggregate",
   249  						TypedConfig: testutils.MarshalAny(t, &v3aggregateclusterpb.ClusterConfig{
   250  							Clusters: []string{},
   251  						}),
   252  					},
   253  				},
   254  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
   255  			},
   256  			wantErr: true,
   257  		},
   258  	}
   259  
   260  	for _, test := range tests {
   261  		t.Run(test.name, func(t *testing.T) {
   262  			if update, err := validateClusterAndConstructClusterUpdate(test.cluster, nil); err == nil {
   263  				t.Errorf("validateClusterAndConstructClusterUpdate(%+v) = %v, wanted error", test.cluster, update)
   264  			}
   265  		})
   266  	}
   267  }
   268  
   269  func (s) TestSecurityConfigFromCommonTLSContextUsingNewFields_ErrorCases(t *testing.T) {
   270  	tests := []struct {
   271  		name    string
   272  		common  *v3tlspb.CommonTlsContext
   273  		server  bool
   274  		wantErr string
   275  	}{
   276  		{
   277  			name: "unsupported-tls_certificates-field-for-identity-certs",
   278  			common: &v3tlspb.CommonTlsContext{
   279  				TlsCertificates: []*v3tlspb.TlsCertificate{
   280  					{CertificateChain: &v3corepb.DataSource{}},
   281  				},
   282  			},
   283  			wantErr: "unsupported field tls_certificates is set in CommonTlsContext message",
   284  		},
   285  		{
   286  			name: "unsupported-tls_certificates_sds_secret_configs-field-for-identity-certs",
   287  			common: &v3tlspb.CommonTlsContext{
   288  				TlsCertificateSdsSecretConfigs: []*v3tlspb.SdsSecretConfig{
   289  					{Name: "sds-secrets-config"},
   290  				},
   291  			},
   292  			wantErr: "unsupported field tls_certificate_sds_secret_configs is set in CommonTlsContext message",
   293  		},
   294  		{
   295  			name: "unsupported-sds-validation-context",
   296  			common: &v3tlspb.CommonTlsContext{
   297  				ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextSdsSecretConfig{
   298  					ValidationContextSdsSecretConfig: &v3tlspb.SdsSecretConfig{
   299  						Name: "foo-sds-secret",
   300  					},
   301  				},
   302  			},
   303  			wantErr: "validation context contains unexpected type",
   304  		},
   305  		{
   306  			name: "missing-ca_certificate_provider_instance-in-validation-context",
   307  			common: &v3tlspb.CommonTlsContext{
   308  				ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
   309  					ValidationContext: &v3tlspb.CertificateValidationContext{},
   310  				},
   311  			},
   312  			wantErr: "expected field ca_certificate_provider_instance is missing in CommonTlsContext message",
   313  		},
   314  		{
   315  			name: "unsupported-field-verify_certificate_spki-in-validation-context",
   316  			common: &v3tlspb.CommonTlsContext{
   317  				ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
   318  					ValidationContext: &v3tlspb.CertificateValidationContext{
   319  						CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   320  							InstanceName:    "rootPluginInstance",
   321  							CertificateName: "rootCertName",
   322  						},
   323  						VerifyCertificateSpki: []string{"spki"},
   324  					},
   325  				},
   326  			},
   327  			wantErr: "unsupported verify_certificate_spki field in CommonTlsContext message",
   328  		},
   329  		{
   330  			name: "unsupported-field-verify_certificate_hash-in-validation-context",
   331  			common: &v3tlspb.CommonTlsContext{
   332  				ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
   333  					ValidationContext: &v3tlspb.CertificateValidationContext{
   334  						CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   335  							InstanceName:    "rootPluginInstance",
   336  							CertificateName: "rootCertName",
   337  						},
   338  						VerifyCertificateHash: []string{"hash"},
   339  					},
   340  				},
   341  			},
   342  			wantErr: "unsupported verify_certificate_hash field in CommonTlsContext message",
   343  		},
   344  		{
   345  			name: "unsupported-field-require_signed_certificate_timestamp-in-validation-context",
   346  			common: &v3tlspb.CommonTlsContext{
   347  				ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
   348  					ValidationContext: &v3tlspb.CertificateValidationContext{
   349  						CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   350  							InstanceName:    "rootPluginInstance",
   351  							CertificateName: "rootCertName",
   352  						},
   353  						RequireSignedCertificateTimestamp: &wrapperspb.BoolValue{Value: true},
   354  					},
   355  				},
   356  			},
   357  			wantErr: "unsupported require_sugned_ceritificate_timestamp field in CommonTlsContext message",
   358  		},
   359  		{
   360  			name: "unsupported-field-crl-in-validation-context",
   361  			common: &v3tlspb.CommonTlsContext{
   362  				ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
   363  					ValidationContext: &v3tlspb.CertificateValidationContext{
   364  						CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   365  							InstanceName:    "rootPluginInstance",
   366  							CertificateName: "rootCertName",
   367  						},
   368  						Crl: &v3corepb.DataSource{},
   369  					},
   370  				},
   371  			},
   372  			wantErr: "unsupported crl field in CommonTlsContext message",
   373  		},
   374  		{
   375  			name: "unsupported-field-custom_validator_config-in-validation-context",
   376  			common: &v3tlspb.CommonTlsContext{
   377  				ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
   378  					ValidationContext: &v3tlspb.CertificateValidationContext{
   379  						CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   380  							InstanceName:    "rootPluginInstance",
   381  							CertificateName: "rootCertName",
   382  						},
   383  						CustomValidatorConfig: &v3corepb.TypedExtensionConfig{},
   384  					},
   385  				},
   386  			},
   387  			wantErr: "unsupported custom_validator_config field in CommonTlsContext message",
   388  		},
   389  		{
   390  			name: "invalid-match_subject_alt_names-field-in-validation-context",
   391  			common: &v3tlspb.CommonTlsContext{
   392  				ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
   393  					ValidationContext: &v3tlspb.CertificateValidationContext{
   394  						CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   395  							InstanceName:    "rootPluginInstance",
   396  							CertificateName: "rootCertName",
   397  						},
   398  						MatchSubjectAltNames: []*v3matcherpb.StringMatcher{
   399  							{MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: ""}},
   400  						},
   401  					},
   402  				},
   403  			},
   404  			wantErr: "empty prefix is not allowed in StringMatcher",
   405  		},
   406  		{
   407  			name: "unsupported-field-matching-subject-alt-names-in-validation-context-of-server",
   408  			common: &v3tlspb.CommonTlsContext{
   409  				ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
   410  					ValidationContext: &v3tlspb.CertificateValidationContext{
   411  						CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   412  							InstanceName:    "rootPluginInstance",
   413  							CertificateName: "rootCertName",
   414  						},
   415  						MatchSubjectAltNames: []*v3matcherpb.StringMatcher{
   416  							{MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "sanPrefix"}},
   417  						},
   418  					},
   419  				},
   420  			},
   421  			server:  true,
   422  			wantErr: "match_subject_alt_names field in validation context is not supported on the server",
   423  		},
   424  	}
   425  
   426  	for _, test := range tests {
   427  		t.Run(test.name, func(t *testing.T) {
   428  			_, err := securityConfigFromCommonTLSContextUsingNewFields(test.common, test.server)
   429  			if err == nil {
   430  				t.Fatal("securityConfigFromCommonTLSContextUsingNewFields() succeeded when expected to fail")
   431  			}
   432  			if !strings.Contains(err.Error(), test.wantErr) {
   433  				t.Fatalf("securityConfigFromCommonTLSContextUsingNewFields() returned err: %v, wantErr: %v", err, test.wantErr)
   434  			}
   435  		})
   436  	}
   437  }
   438  
   439  func (s) TestValidateClusterWithSecurityConfig(t *testing.T) {
   440  	const (
   441  		identityPluginInstance = "identityPluginInstance"
   442  		identityCertName       = "identityCert"
   443  		rootPluginInstance     = "rootPluginInstance"
   444  		rootCertName           = "rootCert"
   445  		clusterName            = "cluster"
   446  		serviceName            = "service"
   447  		sanExact               = "san-exact"
   448  		sanPrefix              = "san-prefix"
   449  		sanSuffix              = "san-suffix"
   450  		sanRegexBad            = "??"
   451  		sanRegexGood           = "san?regex?"
   452  		sanContains            = "san-contains"
   453  	)
   454  	var sanRE = regexp.MustCompile(sanRegexGood)
   455  
   456  	tests := []struct {
   457  		name       string
   458  		cluster    *v3clusterpb.Cluster
   459  		wantUpdate ClusterUpdate
   460  		wantErr    bool
   461  	}{
   462  		{
   463  			name: "transport-socket-matches",
   464  			cluster: &v3clusterpb.Cluster{
   465  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   466  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   467  					EdsConfig: &v3corepb.ConfigSource{
   468  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   469  							Ads: &v3corepb.AggregatedConfigSource{},
   470  						},
   471  					},
   472  					ServiceName: serviceName,
   473  				},
   474  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
   475  				TransportSocketMatches: []*v3clusterpb.Cluster_TransportSocketMatch{
   476  					{Name: "transport-socket-match-1"},
   477  				},
   478  			},
   479  			wantErr: true,
   480  		},
   481  		{
   482  			name: "transport-socket-unsupported-name",
   483  			cluster: &v3clusterpb.Cluster{
   484  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   485  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   486  					EdsConfig: &v3corepb.ConfigSource{
   487  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   488  							Ads: &v3corepb.AggregatedConfigSource{},
   489  						},
   490  					},
   491  					ServiceName: serviceName,
   492  				},
   493  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
   494  				TransportSocket: &v3corepb.TransportSocket{
   495  					Name: "unsupported-foo",
   496  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   497  						TypedConfig: &anypb.Any{
   498  							TypeUrl: version.V3UpstreamTLSContextURL,
   499  						},
   500  					},
   501  				},
   502  			},
   503  			wantErr: true,
   504  		},
   505  		{
   506  			name: "transport-socket-unsupported-typeURL",
   507  			cluster: &v3clusterpb.Cluster{
   508  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   509  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   510  					EdsConfig: &v3corepb.ConfigSource{
   511  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   512  							Ads: &v3corepb.AggregatedConfigSource{},
   513  						},
   514  					},
   515  					ServiceName: serviceName,
   516  				},
   517  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
   518  				TransportSocket: &v3corepb.TransportSocket{
   519  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   520  						TypedConfig: &anypb.Any{
   521  							TypeUrl: version.V3HTTPConnManagerURL,
   522  						},
   523  					},
   524  				},
   525  			},
   526  			wantErr: true,
   527  		},
   528  		{
   529  			name: "transport-socket-unsupported-type",
   530  			cluster: &v3clusterpb.Cluster{
   531  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   532  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   533  					EdsConfig: &v3corepb.ConfigSource{
   534  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   535  							Ads: &v3corepb.AggregatedConfigSource{},
   536  						},
   537  					},
   538  					ServiceName: serviceName,
   539  				},
   540  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
   541  				TransportSocket: &v3corepb.TransportSocket{
   542  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   543  						TypedConfig: &anypb.Any{
   544  							TypeUrl: version.V3UpstreamTLSContextURL,
   545  							Value:   []byte{1, 2, 3, 4},
   546  						},
   547  					},
   548  				},
   549  			},
   550  			wantErr: true,
   551  		},
   552  		{
   553  			name: "transport-socket-unsupported-tls-params-field",
   554  			cluster: &v3clusterpb.Cluster{
   555  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   556  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   557  					EdsConfig: &v3corepb.ConfigSource{
   558  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   559  							Ads: &v3corepb.AggregatedConfigSource{},
   560  						},
   561  					},
   562  					ServiceName: serviceName,
   563  				},
   564  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
   565  				TransportSocket: &v3corepb.TransportSocket{
   566  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   567  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
   568  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   569  								TlsParams: &v3tlspb.TlsParameters{},
   570  							},
   571  						}),
   572  					},
   573  				},
   574  			},
   575  			wantErr: true,
   576  		},
   577  		{
   578  			name: "transport-socket-unsupported-custom-handshaker-field",
   579  			cluster: &v3clusterpb.Cluster{
   580  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   581  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   582  					EdsConfig: &v3corepb.ConfigSource{
   583  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   584  							Ads: &v3corepb.AggregatedConfigSource{},
   585  						},
   586  					},
   587  					ServiceName: serviceName,
   588  				},
   589  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
   590  				TransportSocket: &v3corepb.TransportSocket{
   591  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   592  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
   593  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   594  								CustomHandshaker: &v3corepb.TypedExtensionConfig{},
   595  							},
   596  						}),
   597  					},
   598  				},
   599  			},
   600  			wantErr: true,
   601  		},
   602  		{
   603  			name: "transport-socket-unsupported-validation-context",
   604  			cluster: &v3clusterpb.Cluster{
   605  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   606  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   607  					EdsConfig: &v3corepb.ConfigSource{
   608  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   609  							Ads: &v3corepb.AggregatedConfigSource{},
   610  						},
   611  					},
   612  					ServiceName: serviceName,
   613  				},
   614  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
   615  				TransportSocket: &v3corepb.TransportSocket{
   616  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   617  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
   618  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   619  								ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextSdsSecretConfig{
   620  									ValidationContextSdsSecretConfig: &v3tlspb.SdsSecretConfig{
   621  										Name: "foo-sds-secret",
   622  									},
   623  								},
   624  							},
   625  						}),
   626  					},
   627  				},
   628  			},
   629  			wantErr: true,
   630  		},
   631  		{
   632  			name: "transport-socket-without-validation-context",
   633  			cluster: &v3clusterpb.Cluster{
   634  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   635  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   636  					EdsConfig: &v3corepb.ConfigSource{
   637  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   638  							Ads: &v3corepb.AggregatedConfigSource{},
   639  						},
   640  					},
   641  					ServiceName: serviceName,
   642  				},
   643  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
   644  				TransportSocket: &v3corepb.TransportSocket{
   645  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   646  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
   647  							CommonTlsContext: &v3tlspb.CommonTlsContext{},
   648  						}),
   649  					},
   650  				},
   651  			},
   652  			wantErr: true,
   653  		},
   654  		{
   655  			name: "empty-prefix-in-matching-SAN",
   656  			cluster: &v3clusterpb.Cluster{
   657  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   658  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   659  					EdsConfig: &v3corepb.ConfigSource{
   660  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   661  							Ads: &v3corepb.AggregatedConfigSource{},
   662  						},
   663  					},
   664  					ServiceName: serviceName,
   665  				},
   666  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
   667  				TransportSocket: &v3corepb.TransportSocket{
   668  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   669  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
   670  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   671  								ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{
   672  									CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{
   673  										DefaultValidationContext: &v3tlspb.CertificateValidationContext{
   674  											MatchSubjectAltNames: []*v3matcherpb.StringMatcher{
   675  												{MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: ""}},
   676  											},
   677  										},
   678  										ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   679  											InstanceName:    rootPluginInstance,
   680  											CertificateName: rootCertName,
   681  										},
   682  									},
   683  								},
   684  							},
   685  						}),
   686  					},
   687  				},
   688  			},
   689  			wantErr: true,
   690  		},
   691  		{
   692  			name: "empty-suffix-in-matching-SAN",
   693  			cluster: &v3clusterpb.Cluster{
   694  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   695  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   696  					EdsConfig: &v3corepb.ConfigSource{
   697  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   698  							Ads: &v3corepb.AggregatedConfigSource{},
   699  						},
   700  					},
   701  					ServiceName: serviceName,
   702  				},
   703  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
   704  				TransportSocket: &v3corepb.TransportSocket{
   705  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   706  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
   707  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   708  								ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{
   709  									CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{
   710  										DefaultValidationContext: &v3tlspb.CertificateValidationContext{
   711  											MatchSubjectAltNames: []*v3matcherpb.StringMatcher{
   712  												{MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: ""}},
   713  											},
   714  										},
   715  										ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   716  											InstanceName:    rootPluginInstance,
   717  											CertificateName: rootCertName,
   718  										},
   719  									},
   720  								},
   721  							},
   722  						}),
   723  					},
   724  				},
   725  			},
   726  			wantErr: true,
   727  		},
   728  		{
   729  			name: "empty-contains-in-matching-SAN",
   730  			cluster: &v3clusterpb.Cluster{
   731  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   732  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   733  					EdsConfig: &v3corepb.ConfigSource{
   734  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   735  							Ads: &v3corepb.AggregatedConfigSource{},
   736  						},
   737  					},
   738  					ServiceName: serviceName,
   739  				},
   740  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
   741  				TransportSocket: &v3corepb.TransportSocket{
   742  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   743  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
   744  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   745  								ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{
   746  									CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{
   747  										DefaultValidationContext: &v3tlspb.CertificateValidationContext{
   748  											MatchSubjectAltNames: []*v3matcherpb.StringMatcher{
   749  												{MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: ""}},
   750  											},
   751  										},
   752  										ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   753  											InstanceName:    rootPluginInstance,
   754  											CertificateName: rootCertName,
   755  										},
   756  									},
   757  								},
   758  							},
   759  						}),
   760  					},
   761  				},
   762  			},
   763  			wantErr: true,
   764  		},
   765  		{
   766  			name: "invalid-regex-in-matching-SAN",
   767  			cluster: &v3clusterpb.Cluster{
   768  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   769  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   770  					EdsConfig: &v3corepb.ConfigSource{
   771  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   772  							Ads: &v3corepb.AggregatedConfigSource{},
   773  						},
   774  					},
   775  					ServiceName: serviceName,
   776  				},
   777  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
   778  				TransportSocket: &v3corepb.TransportSocket{
   779  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   780  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
   781  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   782  								ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{
   783  									CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{
   784  										DefaultValidationContext: &v3tlspb.CertificateValidationContext{
   785  											MatchSubjectAltNames: []*v3matcherpb.StringMatcher{
   786  												{MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexBad}}},
   787  											},
   788  										},
   789  										ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   790  											InstanceName:    rootPluginInstance,
   791  											CertificateName: rootCertName,
   792  										},
   793  									},
   794  								},
   795  							},
   796  						}),
   797  					},
   798  				},
   799  			},
   800  			wantErr: true,
   801  		},
   802  		{
   803  			name: "invalid-regex-in-matching-SAN-with-new-fields",
   804  			cluster: &v3clusterpb.Cluster{
   805  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   806  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   807  					EdsConfig: &v3corepb.ConfigSource{
   808  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   809  							Ads: &v3corepb.AggregatedConfigSource{},
   810  						},
   811  					},
   812  					ServiceName: serviceName,
   813  				},
   814  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
   815  				TransportSocket: &v3corepb.TransportSocket{
   816  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   817  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
   818  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   819  								ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{
   820  									CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{
   821  										DefaultValidationContext: &v3tlspb.CertificateValidationContext{
   822  											MatchSubjectAltNames: []*v3matcherpb.StringMatcher{
   823  												{MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexBad}}},
   824  											},
   825  											CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   826  												InstanceName:    rootPluginInstance,
   827  												CertificateName: rootCertName,
   828  											},
   829  										},
   830  									},
   831  								},
   832  							},
   833  						}),
   834  					},
   835  				},
   836  			},
   837  			wantErr: true,
   838  		},
   839  		{
   840  			name: "happy-case-with-no-identity-certs-using-deprecated-fields",
   841  			cluster: &v3clusterpb.Cluster{
   842  				Name:                 clusterName,
   843  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   844  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   845  					EdsConfig: &v3corepb.ConfigSource{
   846  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   847  							Ads: &v3corepb.AggregatedConfigSource{},
   848  						},
   849  					},
   850  					ServiceName: serviceName,
   851  				},
   852  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
   853  				TransportSocket: &v3corepb.TransportSocket{
   854  					Name: "envoy.transport_sockets.tls",
   855  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   856  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
   857  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   858  								ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
   859  									ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   860  										InstanceName:    rootPluginInstance,
   861  										CertificateName: rootCertName,
   862  									},
   863  								},
   864  							},
   865  						}),
   866  					},
   867  				},
   868  			},
   869  			wantUpdate: ClusterUpdate{
   870  				ClusterName:    clusterName,
   871  				EDSServiceName: serviceName,
   872  				SecurityCfg: &SecurityConfig{
   873  					RootInstanceName: rootPluginInstance,
   874  					RootCertName:     rootCertName,
   875  				},
   876  			},
   877  		},
   878  		{
   879  			name: "happy-case-with-no-identity-certs-using-new-fields",
   880  			cluster: &v3clusterpb.Cluster{
   881  				Name:                 clusterName,
   882  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   883  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   884  					EdsConfig: &v3corepb.ConfigSource{
   885  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   886  							Ads: &v3corepb.AggregatedConfigSource{},
   887  						},
   888  					},
   889  					ServiceName: serviceName,
   890  				},
   891  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
   892  				TransportSocket: &v3corepb.TransportSocket{
   893  					Name: "envoy.transport_sockets.tls",
   894  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   895  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
   896  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   897  								ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
   898  									ValidationContext: &v3tlspb.CertificateValidationContext{
   899  										CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   900  											InstanceName:    rootPluginInstance,
   901  											CertificateName: rootCertName,
   902  										},
   903  									},
   904  								},
   905  							},
   906  						}),
   907  					},
   908  				},
   909  			},
   910  			wantUpdate: ClusterUpdate{
   911  				ClusterName:    clusterName,
   912  				EDSServiceName: serviceName,
   913  				SecurityCfg: &SecurityConfig{
   914  					RootInstanceName: rootPluginInstance,
   915  					RootCertName:     rootCertName,
   916  				},
   917  			},
   918  		},
   919  		{
   920  			name: "happy-case-with-validation-context-provider-instance-using-deprecated-fields",
   921  			cluster: &v3clusterpb.Cluster{
   922  				Name:                 clusterName,
   923  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   924  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   925  					EdsConfig: &v3corepb.ConfigSource{
   926  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   927  							Ads: &v3corepb.AggregatedConfigSource{},
   928  						},
   929  					},
   930  					ServiceName: serviceName,
   931  				},
   932  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
   933  				TransportSocket: &v3corepb.TransportSocket{
   934  					Name: "envoy.transport_sockets.tls",
   935  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   936  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
   937  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   938  								TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   939  									InstanceName:    identityPluginInstance,
   940  									CertificateName: identityCertName,
   941  								},
   942  								ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
   943  									ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   944  										InstanceName:    rootPluginInstance,
   945  										CertificateName: rootCertName,
   946  									},
   947  								},
   948  							},
   949  						}),
   950  					},
   951  				},
   952  			},
   953  			wantUpdate: ClusterUpdate{
   954  				ClusterName:    clusterName,
   955  				EDSServiceName: serviceName,
   956  				SecurityCfg: &SecurityConfig{
   957  					RootInstanceName:     rootPluginInstance,
   958  					RootCertName:         rootCertName,
   959  					IdentityInstanceName: identityPluginInstance,
   960  					IdentityCertName:     identityCertName,
   961  				},
   962  			},
   963  		},
   964  		{
   965  			name: "happy-case-with-validation-context-provider-instance-using-new-fields",
   966  			cluster: &v3clusterpb.Cluster{
   967  				Name:                 clusterName,
   968  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
   969  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
   970  					EdsConfig: &v3corepb.ConfigSource{
   971  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
   972  							Ads: &v3corepb.AggregatedConfigSource{},
   973  						},
   974  					},
   975  					ServiceName: serviceName,
   976  				},
   977  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
   978  				TransportSocket: &v3corepb.TransportSocket{
   979  					Name: "envoy.transport_sockets.tls",
   980  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   981  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
   982  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   983  								TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   984  									InstanceName:    identityPluginInstance,
   985  									CertificateName: identityCertName,
   986  								},
   987  								ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
   988  									ValidationContext: &v3tlspb.CertificateValidationContext{
   989  										CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   990  											InstanceName:    rootPluginInstance,
   991  											CertificateName: rootCertName,
   992  										},
   993  									},
   994  								},
   995  							},
   996  						}),
   997  					},
   998  				},
   999  			},
  1000  			wantUpdate: ClusterUpdate{
  1001  				ClusterName:    clusterName,
  1002  				EDSServiceName: serviceName,
  1003  				SecurityCfg: &SecurityConfig{
  1004  					RootInstanceName:     rootPluginInstance,
  1005  					RootCertName:         rootCertName,
  1006  					IdentityInstanceName: identityPluginInstance,
  1007  					IdentityCertName:     identityCertName,
  1008  				},
  1009  			},
  1010  		},
  1011  		{
  1012  			name: "happy-case-with-combined-validation-context-using-deprecated-fields",
  1013  			cluster: &v3clusterpb.Cluster{
  1014  				Name:                 clusterName,
  1015  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
  1016  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
  1017  					EdsConfig: &v3corepb.ConfigSource{
  1018  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
  1019  							Ads: &v3corepb.AggregatedConfigSource{},
  1020  						},
  1021  					},
  1022  					ServiceName: serviceName,
  1023  				},
  1024  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
  1025  				TransportSocket: &v3corepb.TransportSocket{
  1026  					Name: "envoy.transport_sockets.tls",
  1027  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1028  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
  1029  							CommonTlsContext: &v3tlspb.CommonTlsContext{
  1030  								TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
  1031  									InstanceName:    identityPluginInstance,
  1032  									CertificateName: identityCertName,
  1033  								},
  1034  								ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{
  1035  									CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{
  1036  										DefaultValidationContext: &v3tlspb.CertificateValidationContext{
  1037  											MatchSubjectAltNames: []*v3matcherpb.StringMatcher{
  1038  												{
  1039  													MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: sanExact},
  1040  													IgnoreCase:   true,
  1041  												},
  1042  												{MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: sanPrefix}},
  1043  												{MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: sanSuffix}},
  1044  												{MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexGood}}},
  1045  												{MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: sanContains}},
  1046  											},
  1047  										},
  1048  										ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
  1049  											InstanceName:    rootPluginInstance,
  1050  											CertificateName: rootCertName,
  1051  										},
  1052  									},
  1053  								},
  1054  							},
  1055  						}),
  1056  					},
  1057  				},
  1058  			},
  1059  			wantUpdate: ClusterUpdate{
  1060  				ClusterName:    clusterName,
  1061  				EDSServiceName: serviceName,
  1062  				SecurityCfg: &SecurityConfig{
  1063  					RootInstanceName:     rootPluginInstance,
  1064  					RootCertName:         rootCertName,
  1065  					IdentityInstanceName: identityPluginInstance,
  1066  					IdentityCertName:     identityCertName,
  1067  					SubjectAltNameMatchers: []matcher.StringMatcher{
  1068  						matcher.StringMatcherForTesting(newStringP(sanExact), nil, nil, nil, nil, true),
  1069  						matcher.StringMatcherForTesting(nil, newStringP(sanPrefix), nil, nil, nil, false),
  1070  						matcher.StringMatcherForTesting(nil, nil, newStringP(sanSuffix), nil, nil, false),
  1071  						matcher.StringMatcherForTesting(nil, nil, nil, nil, sanRE, false),
  1072  						matcher.StringMatcherForTesting(nil, nil, nil, newStringP(sanContains), nil, false),
  1073  					},
  1074  				},
  1075  			},
  1076  		},
  1077  		{
  1078  			name: "happy-case-with-combined-validation-context-using-new-fields",
  1079  			cluster: &v3clusterpb.Cluster{
  1080  				Name:                 clusterName,
  1081  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
  1082  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
  1083  					EdsConfig: &v3corepb.ConfigSource{
  1084  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
  1085  							Ads: &v3corepb.AggregatedConfigSource{},
  1086  						},
  1087  					},
  1088  					ServiceName: serviceName,
  1089  				},
  1090  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
  1091  				TransportSocket: &v3corepb.TransportSocket{
  1092  					Name: "envoy.transport_sockets.tls",
  1093  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1094  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
  1095  							CommonTlsContext: &v3tlspb.CommonTlsContext{
  1096  								TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
  1097  									InstanceName:    identityPluginInstance,
  1098  									CertificateName: identityCertName,
  1099  								},
  1100  								ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{
  1101  									CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{
  1102  										DefaultValidationContext: &v3tlspb.CertificateValidationContext{
  1103  											MatchSubjectAltNames: []*v3matcherpb.StringMatcher{
  1104  												{
  1105  													MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: sanExact},
  1106  													IgnoreCase:   true,
  1107  												},
  1108  												{MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: sanPrefix}},
  1109  												{MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: sanSuffix}},
  1110  												{MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexGood}}},
  1111  												{MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: sanContains}},
  1112  											},
  1113  											CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
  1114  												InstanceName:    rootPluginInstance,
  1115  												CertificateName: rootCertName,
  1116  											},
  1117  										},
  1118  									},
  1119  								},
  1120  							},
  1121  						}),
  1122  					},
  1123  				},
  1124  			},
  1125  			wantUpdate: ClusterUpdate{
  1126  				ClusterName:    clusterName,
  1127  				EDSServiceName: serviceName,
  1128  				SecurityCfg: &SecurityConfig{
  1129  					RootInstanceName:     rootPluginInstance,
  1130  					RootCertName:         rootCertName,
  1131  					IdentityInstanceName: identityPluginInstance,
  1132  					IdentityCertName:     identityCertName,
  1133  					SubjectAltNameMatchers: []matcher.StringMatcher{
  1134  						matcher.StringMatcherForTesting(newStringP(sanExact), nil, nil, nil, nil, true),
  1135  						matcher.StringMatcherForTesting(nil, newStringP(sanPrefix), nil, nil, nil, false),
  1136  						matcher.StringMatcherForTesting(nil, nil, newStringP(sanSuffix), nil, nil, false),
  1137  						matcher.StringMatcherForTesting(nil, nil, nil, nil, sanRE, false),
  1138  						matcher.StringMatcherForTesting(nil, nil, nil, newStringP(sanContains), nil, false),
  1139  					},
  1140  				},
  1141  			},
  1142  		},
  1143  	}
  1144  
  1145  	for _, test := range tests {
  1146  		t.Run(test.name, func(t *testing.T) {
  1147  			update, err := validateClusterAndConstructClusterUpdate(test.cluster, nil)
  1148  			if (err != nil) != test.wantErr {
  1149  				t.Errorf("validateClusterAndConstructClusterUpdate() returned err %v wantErr %v)", err, test.wantErr)
  1150  			}
  1151  			if diff := cmp.Diff(test.wantUpdate, update, cmpopts.EquateEmpty(), cmp.AllowUnexported(regexp.Regexp{}), cmpopts.IgnoreFields(ClusterUpdate{}, "LBPolicy")); diff != "" {
  1152  				t.Errorf("validateClusterAndConstructClusterUpdate() returned unexpected diff (-want, +got):\n%s", diff)
  1153  			}
  1154  		})
  1155  	}
  1156  }
  1157  
  1158  func (s) TestUnmarshalCluster(t *testing.T) {
  1159  	const (
  1160  		v3ClusterName = "v3clusterName"
  1161  		v3Service     = "v3Service"
  1162  	)
  1163  	var (
  1164  		v3ClusterAny = testutils.MarshalAny(t, &v3clusterpb.Cluster{
  1165  			Name:                 v3ClusterName,
  1166  			ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
  1167  			EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
  1168  				EdsConfig: &v3corepb.ConfigSource{
  1169  					ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
  1170  						Ads: &v3corepb.AggregatedConfigSource{},
  1171  					},
  1172  				},
  1173  				ServiceName: v3Service,
  1174  			},
  1175  			LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
  1176  			LrsServer: &v3corepb.ConfigSource{
  1177  				ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{
  1178  					Self: &v3corepb.SelfConfigSource{},
  1179  				},
  1180  			},
  1181  		})
  1182  
  1183  		v3ClusterAnyWithEDSConfigSourceSelf = testutils.MarshalAny(t, &v3clusterpb.Cluster{
  1184  			Name:                 v3ClusterName,
  1185  			ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
  1186  			EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
  1187  				EdsConfig: &v3corepb.ConfigSource{
  1188  					ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{},
  1189  				},
  1190  				ServiceName: v3Service,
  1191  			},
  1192  			LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
  1193  			LrsServer: &v3corepb.ConfigSource{
  1194  				ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{
  1195  					Self: &v3corepb.SelfConfigSource{},
  1196  				},
  1197  			},
  1198  		})
  1199  
  1200  		v3ClusterAnyWithTelemetryLabels = testutils.MarshalAny(t, &v3clusterpb.Cluster{
  1201  			Name:                 v3ClusterName,
  1202  			ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
  1203  			EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
  1204  				EdsConfig: &v3corepb.ConfigSource{
  1205  					ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
  1206  						Ads: &v3corepb.AggregatedConfigSource{},
  1207  					},
  1208  				},
  1209  				ServiceName: v3Service,
  1210  			},
  1211  			LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
  1212  			LrsServer: &v3corepb.ConfigSource{
  1213  				ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{
  1214  					Self: &v3corepb.SelfConfigSource{},
  1215  				},
  1216  			},
  1217  			Metadata: &v3corepb.Metadata{
  1218  				FilterMetadata: map[string]*structpb.Struct{
  1219  					"com.google.csm.telemetry_labels": {
  1220  						Fields: map[string]*structpb.Value{
  1221  							"service_name":      structpb.NewStringValue("grpc-service"),
  1222  							"service_namespace": structpb.NewStringValue("grpc-service-namespace"),
  1223  						},
  1224  					},
  1225  				},
  1226  			},
  1227  		})
  1228  		v3ClusterAnyWithTelemetryLabelsIgnoreSome = testutils.MarshalAny(t, &v3clusterpb.Cluster{
  1229  			Name:                 v3ClusterName,
  1230  			ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
  1231  			EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
  1232  				EdsConfig: &v3corepb.ConfigSource{
  1233  					ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
  1234  						Ads: &v3corepb.AggregatedConfigSource{},
  1235  					},
  1236  				},
  1237  				ServiceName: v3Service,
  1238  			},
  1239  			LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
  1240  			LrsServer: &v3corepb.ConfigSource{
  1241  				ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{
  1242  					Self: &v3corepb.SelfConfigSource{},
  1243  				},
  1244  			},
  1245  			Metadata: &v3corepb.Metadata{
  1246  				FilterMetadata: map[string]*structpb.Struct{
  1247  					"com.google.csm.telemetry_labels": {
  1248  						Fields: map[string]*structpb.Value{
  1249  							"string-value-should-ignore": structpb.NewStringValue("string-val"),
  1250  							"float-value-ignore":         structpb.NewNumberValue(3),
  1251  							"bool-value-ignore":          structpb.NewBoolValue(false),
  1252  							"service_name":               structpb.NewStringValue("grpc-service"), // shouldn't ignore
  1253  							"service_namespace":          structpb.NewNullValue(),                 // should ignore - wrong type
  1254  						},
  1255  					},
  1256  					"ignore-this-metadata": { // should ignore this filter_metadata
  1257  						Fields: map[string]*structpb.Value{
  1258  							"service_namespace": structpb.NewStringValue("string-val-should-ignore"),
  1259  						},
  1260  					},
  1261  				},
  1262  			},
  1263  		})
  1264  	)
  1265  
  1266  	tests := []struct {
  1267  		name       string
  1268  		resource   *anypb.Any
  1269  		serverCfg  *bootstrap.ServerConfig
  1270  		wantName   string
  1271  		wantUpdate ClusterUpdate
  1272  		wantErr    bool
  1273  	}{
  1274  		{
  1275  			name:     "non-cluster resource type",
  1276  			resource: &anypb.Any{TypeUrl: version.V3HTTPConnManagerURL},
  1277  			wantErr:  true,
  1278  		},
  1279  		{
  1280  			name: "badly marshaled cluster resource",
  1281  			resource: &anypb.Any{
  1282  				TypeUrl: version.V3ClusterURL,
  1283  				Value:   []byte{1, 2, 3, 4},
  1284  			},
  1285  			wantErr: true,
  1286  		},
  1287  		{
  1288  			name: "bad cluster resource",
  1289  			resource: testutils.MarshalAny(t, &v3clusterpb.Cluster{
  1290  				Name:                 "test",
  1291  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_STATIC},
  1292  			}),
  1293  			wantName: "test",
  1294  			wantErr:  true,
  1295  		},
  1296  		{
  1297  			name: "cluster resource with non-self lrs_server field",
  1298  			resource: testutils.MarshalAny(t, &v3clusterpb.Cluster{
  1299  				Name:                 "test",
  1300  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
  1301  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
  1302  					EdsConfig: &v3corepb.ConfigSource{
  1303  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
  1304  							Ads: &v3corepb.AggregatedConfigSource{},
  1305  						},
  1306  					},
  1307  					ServiceName: v3Service,
  1308  				},
  1309  				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
  1310  				LrsServer: &v3corepb.ConfigSource{
  1311  					ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
  1312  						Ads: &v3corepb.AggregatedConfigSource{},
  1313  					},
  1314  				},
  1315  			}),
  1316  			wantName: "test",
  1317  			wantErr:  true,
  1318  		},
  1319  		{
  1320  			name:      "v3 cluster",
  1321  			resource:  v3ClusterAny,
  1322  			serverCfg: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
  1323  			wantName:  v3ClusterName,
  1324  			wantUpdate: ClusterUpdate{
  1325  				ClusterName:     v3ClusterName,
  1326  				EDSServiceName:  v3Service,
  1327  				LRSServerConfig: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
  1328  				Raw:             v3ClusterAny,
  1329  			},
  1330  		},
  1331  		{
  1332  			name:      "v3 cluster wrapped",
  1333  			resource:  testutils.MarshalAny(t, &v3discoverypb.Resource{Resource: v3ClusterAny}),
  1334  			serverCfg: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
  1335  			wantName:  v3ClusterName,
  1336  			wantUpdate: ClusterUpdate{
  1337  				ClusterName:     v3ClusterName,
  1338  				EDSServiceName:  v3Service,
  1339  				LRSServerConfig: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
  1340  				Raw:             v3ClusterAny,
  1341  			},
  1342  		},
  1343  		{
  1344  			name:      "v3 cluster with EDS config source self",
  1345  			resource:  v3ClusterAnyWithEDSConfigSourceSelf,
  1346  			serverCfg: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
  1347  			wantName:  v3ClusterName,
  1348  			wantUpdate: ClusterUpdate{
  1349  				ClusterName:     v3ClusterName,
  1350  				EDSServiceName:  v3Service,
  1351  				LRSServerConfig: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
  1352  				Raw:             v3ClusterAnyWithEDSConfigSourceSelf,
  1353  			},
  1354  		},
  1355  		{
  1356  			name:      "v3 cluster with telemetry case",
  1357  			resource:  v3ClusterAnyWithTelemetryLabels,
  1358  			serverCfg: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
  1359  			wantName:  v3ClusterName,
  1360  			wantUpdate: ClusterUpdate{
  1361  				ClusterName:     v3ClusterName,
  1362  				EDSServiceName:  v3Service,
  1363  				LRSServerConfig: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
  1364  				Raw:             v3ClusterAnyWithTelemetryLabels,
  1365  				TelemetryLabels: map[string]string{
  1366  					"service_name":      "grpc-service",
  1367  					"service_namespace": "grpc-service-namespace",
  1368  				},
  1369  			},
  1370  		},
  1371  		{
  1372  			name:      "v3 metadata ignore other types not string and not com.google.csm.telemetry_labels",
  1373  			resource:  v3ClusterAnyWithTelemetryLabelsIgnoreSome,
  1374  			serverCfg: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
  1375  			wantName:  v3ClusterName,
  1376  			wantUpdate: ClusterUpdate{
  1377  				ClusterName:     v3ClusterName,
  1378  				EDSServiceName:  v3Service,
  1379  				LRSServerConfig: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
  1380  				Raw:             v3ClusterAnyWithTelemetryLabelsIgnoreSome,
  1381  				TelemetryLabels: map[string]string{
  1382  					"service_name": "grpc-service",
  1383  				},
  1384  			},
  1385  		},
  1386  		{
  1387  			name: "xdstp cluster resource with unset EDS service name",
  1388  			resource: testutils.MarshalAny(t, &v3clusterpb.Cluster{
  1389  				Name:                 "xdstp:foo",
  1390  				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
  1391  				EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
  1392  					EdsConfig: &v3corepb.ConfigSource{
  1393  						ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
  1394  							Ads: &v3corepb.AggregatedConfigSource{},
  1395  						},
  1396  					},
  1397  					ServiceName: "",
  1398  				},
  1399  			}),
  1400  			wantName: "xdstp:foo",
  1401  			wantErr:  true,
  1402  		},
  1403  	}
  1404  	for _, test := range tests {
  1405  		t.Run(test.name, func(t *testing.T) {
  1406  			name, update, err := unmarshalClusterResource(test.resource, test.serverCfg)
  1407  			if (err != nil) != test.wantErr {
  1408  				t.Fatalf("unmarshalClusterResource(%s), got err: %v, wantErr: %v", pretty.ToJSON(test.resource), err, test.wantErr)
  1409  			}
  1410  			if name != test.wantName {
  1411  				t.Errorf("unmarshalClusterResource(%s), got name: %s, want: %s", pretty.ToJSON(test.resource), name, test.wantName)
  1412  			}
  1413  			if diff := cmp.Diff(update, test.wantUpdate, cmpOpts, cmpopts.IgnoreFields(ClusterUpdate{}, "LBPolicy")); diff != "" {
  1414  				t.Errorf("unmarshalClusterResource(%s), got unexpected update, diff (-got +want): %v", pretty.ToJSON(test.resource), diff)
  1415  			}
  1416  		})
  1417  	}
  1418  }
  1419  
  1420  func (s) TestValidateClusterWithOutlierDetection(t *testing.T) {
  1421  	odToClusterProto := func(od *v3clusterpb.OutlierDetection) *v3clusterpb.Cluster {
  1422  		// Cluster parsing doesn't fail with respect to fields orthogonal to
  1423  		// outlier detection.
  1424  		return &v3clusterpb.Cluster{
  1425  			Name:                 clusterName,
  1426  			ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
  1427  			EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{
  1428  				EdsConfig: &v3corepb.ConfigSource{
  1429  					ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{
  1430  						Ads: &v3corepb.AggregatedConfigSource{},
  1431  					},
  1432  				},
  1433  			},
  1434  			LbPolicy:         v3clusterpb.Cluster_ROUND_ROBIN,
  1435  			OutlierDetection: od,
  1436  		}
  1437  	}
  1438  
  1439  	tests := []struct {
  1440  		name      string
  1441  		cluster   *v3clusterpb.Cluster
  1442  		wantODCfg string
  1443  		wantErr   bool
  1444  	}{
  1445  		{
  1446  			name:      "success-and-failure-null",
  1447  			cluster:   odToClusterProto(&v3clusterpb.OutlierDetection{}),
  1448  			wantODCfg: `{"successRateEjection": {}}`,
  1449  		},
  1450  		{
  1451  			name: "success-and-failure-zero",
  1452  			cluster: odToClusterProto(&v3clusterpb.OutlierDetection{
  1453  				EnforcingSuccessRate:       &wrapperspb.UInt32Value{Value: 0}, // Thus doesn't create sre - to focus on fpe
  1454  				EnforcingFailurePercentage: &wrapperspb.UInt32Value{Value: 0},
  1455  			}),
  1456  			wantODCfg: `{}`,
  1457  		},
  1458  		{
  1459  			name: "some-fields-set",
  1460  			cluster: odToClusterProto(&v3clusterpb.OutlierDetection{
  1461  				Interval:                       &durationpb.Duration{Seconds: 1},
  1462  				MaxEjectionTime:                &durationpb.Duration{Seconds: 3},
  1463  				EnforcingSuccessRate:           &wrapperspb.UInt32Value{Value: 3},
  1464  				SuccessRateRequestVolume:       &wrapperspb.UInt32Value{Value: 5},
  1465  				EnforcingFailurePercentage:     &wrapperspb.UInt32Value{Value: 7},
  1466  				FailurePercentageRequestVolume: &wrapperspb.UInt32Value{Value: 9},
  1467  			}),
  1468  			wantODCfg: `{
  1469  				"interval": "1s",
  1470  				"maxEjectionTime": "3s",
  1471  				"successRateEjection": {
  1472  					"enforcementPercentage": 3,
  1473  					"requestVolume": 5
  1474  				},
  1475  				"failurePercentageEjection": {
  1476  					"enforcementPercentage": 7,
  1477  					"requestVolume": 9
  1478  				}
  1479  			}`,
  1480  		},
  1481  		{
  1482  			name: "every-field-set-non-zero",
  1483  			cluster: odToClusterProto(&v3clusterpb.OutlierDetection{
  1484  				// all fields set (including ones that will be layered) should
  1485  				// pick up those too and explicitly all fields, including those
  1486  				// put in layers, in the JSON generated.
  1487  				Interval:                       &durationpb.Duration{Seconds: 1},
  1488  				BaseEjectionTime:               &durationpb.Duration{Seconds: 2},
  1489  				MaxEjectionTime:                &durationpb.Duration{Seconds: 3},
  1490  				MaxEjectionPercent:             &wrapperspb.UInt32Value{Value: 1},
  1491  				SuccessRateStdevFactor:         &wrapperspb.UInt32Value{Value: 2},
  1492  				EnforcingSuccessRate:           &wrapperspb.UInt32Value{Value: 3},
  1493  				SuccessRateMinimumHosts:        &wrapperspb.UInt32Value{Value: 4},
  1494  				SuccessRateRequestVolume:       &wrapperspb.UInt32Value{Value: 5},
  1495  				FailurePercentageThreshold:     &wrapperspb.UInt32Value{Value: 6},
  1496  				EnforcingFailurePercentage:     &wrapperspb.UInt32Value{Value: 7},
  1497  				FailurePercentageMinimumHosts:  &wrapperspb.UInt32Value{Value: 8},
  1498  				FailurePercentageRequestVolume: &wrapperspb.UInt32Value{Value: 9},
  1499  			}),
  1500  			wantODCfg: `{
  1501  				"interval": "1s",
  1502  				"baseEjectionTime": "2s",
  1503  				"maxEjectionTime": "3s",
  1504  				"maxEjectionPercent": 1,
  1505  				"successRateEjection": {
  1506  					"stdevFactor": 2,
  1507  					"enforcementPercentage": 3,
  1508  					"minimumHosts": 4,
  1509  					"requestVolume": 5
  1510  				},
  1511  				"failurePercentageEjection": {
  1512  					"threshold": 6,
  1513  					"enforcementPercentage": 7,
  1514  					"minimumHosts": 8,
  1515  					"requestVolume": 9
  1516  				}
  1517  			}`,
  1518  		},
  1519  		{
  1520  			name:    "interval-is-negative",
  1521  			cluster: odToClusterProto(&v3clusterpb.OutlierDetection{Interval: &durationpb.Duration{Seconds: -10}}),
  1522  			wantErr: true,
  1523  		},
  1524  		{
  1525  			name:    "interval-overflows",
  1526  			cluster: odToClusterProto(&v3clusterpb.OutlierDetection{Interval: &durationpb.Duration{Seconds: 315576000001}}),
  1527  			wantErr: true,
  1528  		},
  1529  		{
  1530  			name:    "base-ejection-time-is-negative",
  1531  			cluster: odToClusterProto(&v3clusterpb.OutlierDetection{BaseEjectionTime: &durationpb.Duration{Seconds: -10}}),
  1532  			wantErr: true,
  1533  		},
  1534  		{
  1535  			name:    "base-ejection-time-overflows",
  1536  			cluster: odToClusterProto(&v3clusterpb.OutlierDetection{BaseEjectionTime: &durationpb.Duration{Seconds: 315576000001}}),
  1537  			wantErr: true,
  1538  		},
  1539  		{
  1540  			name:    "max-ejection-time-is-negative",
  1541  			cluster: odToClusterProto(&v3clusterpb.OutlierDetection{MaxEjectionTime: &durationpb.Duration{Seconds: -10}}),
  1542  			wantErr: true,
  1543  		},
  1544  		{
  1545  			name:    "max-ejection-time-overflows",
  1546  			cluster: odToClusterProto(&v3clusterpb.OutlierDetection{MaxEjectionTime: &durationpb.Duration{Seconds: 315576000001}}),
  1547  			wantErr: true,
  1548  		},
  1549  		{
  1550  			name:    "max-ejection-percent-is-greater-than-100",
  1551  			cluster: odToClusterProto(&v3clusterpb.OutlierDetection{MaxEjectionPercent: &wrapperspb.UInt32Value{Value: 150}}),
  1552  			wantErr: true,
  1553  		},
  1554  		{
  1555  			name:    "enforcing-success-rate-is-greater-than-100",
  1556  			cluster: odToClusterProto(&v3clusterpb.OutlierDetection{EnforcingSuccessRate: &wrapperspb.UInt32Value{Value: 150}}),
  1557  			wantErr: true,
  1558  		},
  1559  		{
  1560  			name:    "failure-percentage-threshold-is-greater-than-100",
  1561  			cluster: odToClusterProto(&v3clusterpb.OutlierDetection{FailurePercentageThreshold: &wrapperspb.UInt32Value{Value: 150}}),
  1562  			wantErr: true,
  1563  		},
  1564  		{
  1565  			name:    "enforcing-failure-percentage-is-greater-than-100",
  1566  			cluster: odToClusterProto(&v3clusterpb.OutlierDetection{EnforcingFailurePercentage: &wrapperspb.UInt32Value{Value: 150}}),
  1567  			wantErr: true,
  1568  		},
  1569  		// A Outlier Detection proto not present should lead to a nil
  1570  		// OutlierDetection field in the ClusterUpdate, which is implicitly
  1571  		// tested in every other test in this file.
  1572  	}
  1573  	for _, test := range tests {
  1574  		t.Run(test.name, func(t *testing.T) {
  1575  			update, err := validateClusterAndConstructClusterUpdate(test.cluster, nil)
  1576  			if (err != nil) != test.wantErr {
  1577  				t.Errorf("validateClusterAndConstructClusterUpdate() returned err %v wantErr %v)", err, test.wantErr)
  1578  			}
  1579  			if test.wantErr {
  1580  				return
  1581  			}
  1582  			// got and want must be unmarshalled since JSON strings shouldn't
  1583  			// generally be directly compared.
  1584  			var got map[string]any
  1585  			if err := json.Unmarshal(update.OutlierDetection, &got); err != nil {
  1586  				t.Fatalf("Error unmarshalling update.OutlierDetection (%q): %v", update.OutlierDetection, err)
  1587  			}
  1588  			var want map[string]any
  1589  			if err := json.Unmarshal(json.RawMessage(test.wantODCfg), &want); err != nil {
  1590  				t.Fatalf("Error unmarshalling wantODCfg (%q): %v", test.wantODCfg, err)
  1591  			}
  1592  			if diff := cmp.Diff(got, want); diff != "" {
  1593  				t.Fatalf("cluster.OutlierDetection got unexpected output, diff (-got, +want): %v", diff)
  1594  			}
  1595  		})
  1596  	}
  1597  }
  1598  

View as plain text