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 }