google.golang.org/grpc@v1.62.1/xds/internal/xdsclient/xdsresource/unmarshal_cds_test.go (about)

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