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