1/* 2 * 3 * Copyright 2020 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 19package client 20 21import ( 22 "testing" 23 24 v2xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2" 25 v2corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" 26 v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" 27 v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 28 v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" 29 v3matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" 30 "github.com/golang/protobuf/proto" 31 anypb "github.com/golang/protobuf/ptypes/any" 32 "github.com/google/go-cmp/cmp" 33 "github.com/google/go-cmp/cmp/cmpopts" 34 "google.golang.org/grpc/xds/internal/version" 35) 36 37const ( 38 clusterName = "clusterName" 39 serviceName = "service" 40) 41 42var emptyUpdate = ClusterUpdate{ServiceName: "", EnableLRS: false} 43 44func (s) TestValidateCluster_Failure(t *testing.T) { 45 tests := []struct { 46 name string 47 cluster *v3clusterpb.Cluster 48 wantUpdate ClusterUpdate 49 wantErr bool 50 }{ 51 { 52 name: "non-eds-cluster-type", 53 cluster: &v3clusterpb.Cluster{ 54 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_STATIC}, 55 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 56 EdsConfig: &v3corepb.ConfigSource{ 57 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 58 Ads: &v3corepb.AggregatedConfigSource{}, 59 }, 60 }, 61 }, 62 LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST, 63 }, 64 wantUpdate: emptyUpdate, 65 wantErr: true, 66 }, 67 { 68 name: "no-eds-config", 69 cluster: &v3clusterpb.Cluster{ 70 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 71 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 72 }, 73 wantUpdate: emptyUpdate, 74 wantErr: true, 75 }, 76 { 77 name: "no-ads-config-source", 78 cluster: &v3clusterpb.Cluster{ 79 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 80 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{}, 81 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 82 }, 83 wantUpdate: emptyUpdate, 84 wantErr: true, 85 }, 86 { 87 name: "non-round-robin-lb-policy", 88 cluster: &v3clusterpb.Cluster{ 89 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 90 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 91 EdsConfig: &v3corepb.ConfigSource{ 92 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 93 Ads: &v3corepb.AggregatedConfigSource{}, 94 }, 95 }, 96 }, 97 LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST, 98 }, 99 wantUpdate: emptyUpdate, 100 wantErr: true, 101 }, 102 } 103 104 for _, test := range tests { 105 t.Run(test.name, func(t *testing.T) { 106 if update, err := validateCluster(test.cluster); err == nil { 107 t.Errorf("validateCluster(%+v) = %v, wanted error", test.cluster, update) 108 } 109 }) 110 } 111} 112 113func (s) TestValidateCluster_Success(t *testing.T) { 114 tests := []struct { 115 name string 116 cluster *v3clusterpb.Cluster 117 wantUpdate ClusterUpdate 118 }{ 119 { 120 name: "happy-case-no-service-name-no-lrs", 121 cluster: &v3clusterpb.Cluster{ 122 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 123 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 124 EdsConfig: &v3corepb.ConfigSource{ 125 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 126 Ads: &v3corepb.AggregatedConfigSource{}, 127 }, 128 }, 129 }, 130 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 131 }, 132 wantUpdate: emptyUpdate, 133 }, 134 { 135 name: "happy-case-no-lrs", 136 cluster: &v3clusterpb.Cluster{ 137 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 138 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 139 EdsConfig: &v3corepb.ConfigSource{ 140 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 141 Ads: &v3corepb.AggregatedConfigSource{}, 142 }, 143 }, 144 ServiceName: serviceName, 145 }, 146 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 147 }, 148 wantUpdate: ClusterUpdate{ServiceName: serviceName, EnableLRS: false}, 149 }, 150 { 151 name: "happiest-case", 152 cluster: &v3clusterpb.Cluster{ 153 Name: clusterName, 154 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 155 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 156 EdsConfig: &v3corepb.ConfigSource{ 157 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 158 Ads: &v3corepb.AggregatedConfigSource{}, 159 }, 160 }, 161 ServiceName: serviceName, 162 }, 163 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 164 LrsServer: &v3corepb.ConfigSource{ 165 ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{ 166 Self: &v3corepb.SelfConfigSource{}, 167 }, 168 }, 169 }, 170 wantUpdate: ClusterUpdate{ServiceName: serviceName, EnableLRS: true}, 171 }, 172 } 173 174 for _, test := range tests { 175 t.Run(test.name, func(t *testing.T) { 176 update, err := validateCluster(test.cluster) 177 if err != nil { 178 t.Errorf("validateCluster(%+v) failed: %v", test.cluster, err) 179 } 180 if !cmp.Equal(update, test.wantUpdate, cmpopts.EquateEmpty()) { 181 t.Errorf("validateCluster(%+v) = %v, want: %v", test.cluster, update, test.wantUpdate) 182 } 183 }) 184 } 185} 186 187func (s) TestValidateClusterWithSecurityConfig(t *testing.T) { 188 const ( 189 identityPluginInstance = "identityPluginInstance" 190 identityCertName = "identityCert" 191 rootPluginInstance = "rootPluginInstance" 192 rootCertName = "rootCert" 193 serviceName = "service" 194 san1 = "san1" 195 san2 = "san2" 196 ) 197 198 tests := []struct { 199 name string 200 cluster *v3clusterpb.Cluster 201 wantUpdate ClusterUpdate 202 wantErr bool 203 }{ 204 { 205 name: "transport-socket-unsupported-name", 206 cluster: &v3clusterpb.Cluster{ 207 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 208 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 209 EdsConfig: &v3corepb.ConfigSource{ 210 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 211 Ads: &v3corepb.AggregatedConfigSource{}, 212 }, 213 }, 214 ServiceName: serviceName, 215 }, 216 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 217 TransportSocket: &v3corepb.TransportSocket{ 218 Name: "unsupported-foo", 219 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 220 TypedConfig: &anypb.Any{ 221 TypeUrl: version.V3UpstreamTLSContextURL, 222 }, 223 }, 224 }, 225 }, 226 wantErr: true, 227 }, 228 { 229 name: "transport-socket-unsupported-typeURL", 230 cluster: &v3clusterpb.Cluster{ 231 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 232 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 233 EdsConfig: &v3corepb.ConfigSource{ 234 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 235 Ads: &v3corepb.AggregatedConfigSource{}, 236 }, 237 }, 238 ServiceName: serviceName, 239 }, 240 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 241 TransportSocket: &v3corepb.TransportSocket{ 242 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 243 TypedConfig: &anypb.Any{ 244 TypeUrl: version.V3HTTPConnManagerURL, 245 }, 246 }, 247 }, 248 }, 249 wantErr: true, 250 }, 251 { 252 name: "transport-socket-unsupported-type", 253 cluster: &v3clusterpb.Cluster{ 254 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 255 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 256 EdsConfig: &v3corepb.ConfigSource{ 257 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 258 Ads: &v3corepb.AggregatedConfigSource{}, 259 }, 260 }, 261 ServiceName: serviceName, 262 }, 263 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 264 TransportSocket: &v3corepb.TransportSocket{ 265 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 266 TypedConfig: &anypb.Any{ 267 TypeUrl: version.V3UpstreamTLSContextURL, 268 Value: []byte{1, 2, 3, 4}, 269 }, 270 }, 271 }, 272 }, 273 wantErr: true, 274 }, 275 { 276 name: "transport-socket-unsupported-validation-context", 277 cluster: &v3clusterpb.Cluster{ 278 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 279 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 280 EdsConfig: &v3corepb.ConfigSource{ 281 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 282 Ads: &v3corepb.AggregatedConfigSource{}, 283 }, 284 }, 285 ServiceName: serviceName, 286 }, 287 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 288 TransportSocket: &v3corepb.TransportSocket{ 289 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 290 TypedConfig: &anypb.Any{ 291 TypeUrl: version.V3UpstreamTLSContextURL, 292 Value: func() []byte { 293 tls := &v3tlspb.UpstreamTlsContext{ 294 CommonTlsContext: &v3tlspb.CommonTlsContext{ 295 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextSdsSecretConfig{ 296 ValidationContextSdsSecretConfig: &v3tlspb.SdsSecretConfig{ 297 Name: "foo-sds-secret", 298 }, 299 }, 300 }, 301 } 302 mtls, _ := proto.Marshal(tls) 303 return mtls 304 }(), 305 }, 306 }, 307 }, 308 }, 309 wantErr: true, 310 }, 311 { 312 name: "happy-case-with-no-identity-certs", 313 cluster: &v3clusterpb.Cluster{ 314 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 315 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 316 EdsConfig: &v3corepb.ConfigSource{ 317 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 318 Ads: &v3corepb.AggregatedConfigSource{}, 319 }, 320 }, 321 ServiceName: serviceName, 322 }, 323 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 324 TransportSocket: &v3corepb.TransportSocket{ 325 Name: "envoy.transport_sockets.tls", 326 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 327 TypedConfig: &anypb.Any{ 328 TypeUrl: version.V3UpstreamTLSContextURL, 329 Value: func() []byte { 330 tls := &v3tlspb.UpstreamTlsContext{ 331 CommonTlsContext: &v3tlspb.CommonTlsContext{ 332 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{ 333 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 334 InstanceName: rootPluginInstance, 335 CertificateName: rootCertName, 336 }, 337 }, 338 }, 339 } 340 mtls, _ := proto.Marshal(tls) 341 return mtls 342 }(), 343 }, 344 }, 345 }, 346 }, 347 wantUpdate: ClusterUpdate{ 348 ServiceName: serviceName, 349 EnableLRS: false, 350 SecurityCfg: &SecurityConfig{ 351 RootInstanceName: rootPluginInstance, 352 RootCertName: rootCertName, 353 }, 354 }, 355 }, 356 { 357 name: "happy-case-with-validation-context-provider-instance", 358 cluster: &v3clusterpb.Cluster{ 359 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 360 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 361 EdsConfig: &v3corepb.ConfigSource{ 362 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 363 Ads: &v3corepb.AggregatedConfigSource{}, 364 }, 365 }, 366 ServiceName: serviceName, 367 }, 368 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 369 TransportSocket: &v3corepb.TransportSocket{ 370 Name: "envoy.transport_sockets.tls", 371 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 372 TypedConfig: &anypb.Any{ 373 TypeUrl: version.V3UpstreamTLSContextURL, 374 Value: func() []byte { 375 tls := &v3tlspb.UpstreamTlsContext{ 376 CommonTlsContext: &v3tlspb.CommonTlsContext{ 377 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 378 InstanceName: identityPluginInstance, 379 CertificateName: identityCertName, 380 }, 381 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{ 382 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 383 InstanceName: rootPluginInstance, 384 CertificateName: rootCertName, 385 }, 386 }, 387 }, 388 } 389 mtls, _ := proto.Marshal(tls) 390 return mtls 391 }(), 392 }, 393 }, 394 }, 395 }, 396 wantUpdate: ClusterUpdate{ 397 ServiceName: serviceName, 398 EnableLRS: false, 399 SecurityCfg: &SecurityConfig{ 400 RootInstanceName: rootPluginInstance, 401 RootCertName: rootCertName, 402 IdentityInstanceName: identityPluginInstance, 403 IdentityCertName: identityCertName, 404 }, 405 }, 406 }, 407 { 408 name: "happy-case-with-combined-validation-context", 409 cluster: &v3clusterpb.Cluster{ 410 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 411 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 412 EdsConfig: &v3corepb.ConfigSource{ 413 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 414 Ads: &v3corepb.AggregatedConfigSource{}, 415 }, 416 }, 417 ServiceName: serviceName, 418 }, 419 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 420 TransportSocket: &v3corepb.TransportSocket{ 421 Name: "envoy.transport_sockets.tls", 422 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 423 TypedConfig: &anypb.Any{ 424 TypeUrl: version.V3UpstreamTLSContextURL, 425 Value: func() []byte { 426 tls := &v3tlspb.UpstreamTlsContext{ 427 CommonTlsContext: &v3tlspb.CommonTlsContext{ 428 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 429 InstanceName: identityPluginInstance, 430 CertificateName: identityCertName, 431 }, 432 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 433 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 434 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 435 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 436 {MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: san1}}, 437 {MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: san2}}, 438 }, 439 }, 440 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 441 InstanceName: rootPluginInstance, 442 CertificateName: rootCertName, 443 }, 444 }, 445 }, 446 }, 447 } 448 mtls, _ := proto.Marshal(tls) 449 return mtls 450 }(), 451 }, 452 }, 453 }, 454 }, 455 wantUpdate: ClusterUpdate{ 456 ServiceName: serviceName, 457 EnableLRS: false, 458 SecurityCfg: &SecurityConfig{ 459 RootInstanceName: rootPluginInstance, 460 RootCertName: rootCertName, 461 IdentityInstanceName: identityPluginInstance, 462 IdentityCertName: identityCertName, 463 AcceptedSANs: []string{san1, san2}, 464 }, 465 }, 466 }, 467 } 468 469 for _, test := range tests { 470 t.Run(test.name, func(t *testing.T) { 471 update, err := validateCluster(test.cluster) 472 if ((err != nil) != test.wantErr) || !cmp.Equal(update, test.wantUpdate, cmpopts.EquateEmpty()) { 473 t.Errorf("validateCluster(%+v) = (%+v, %v), want: (%+v, %v)", test.cluster, update, err, test.wantUpdate, test.wantErr) 474 } 475 }) 476 } 477} 478 479func (s) TestUnmarshalCluster(t *testing.T) { 480 const ( 481 v2ClusterName = "v2clusterName" 482 v3ClusterName = "v3clusterName" 483 v2Service = "v2Service" 484 v3Service = "v2Service" 485 ) 486 var ( 487 v2Cluster = &v2xdspb.Cluster{ 488 Name: v2ClusterName, 489 ClusterDiscoveryType: &v2xdspb.Cluster_Type{Type: v2xdspb.Cluster_EDS}, 490 EdsClusterConfig: &v2xdspb.Cluster_EdsClusterConfig{ 491 EdsConfig: &v2corepb.ConfigSource{ 492 ConfigSourceSpecifier: &v2corepb.ConfigSource_Ads{ 493 Ads: &v2corepb.AggregatedConfigSource{}, 494 }, 495 }, 496 ServiceName: v2Service, 497 }, 498 LbPolicy: v2xdspb.Cluster_ROUND_ROBIN, 499 LrsServer: &v2corepb.ConfigSource{ 500 ConfigSourceSpecifier: &v2corepb.ConfigSource_Self{ 501 Self: &v2corepb.SelfConfigSource{}, 502 }, 503 }, 504 } 505 506 v3Cluster = &v3clusterpb.Cluster{ 507 Name: v3ClusterName, 508 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 509 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 510 EdsConfig: &v3corepb.ConfigSource{ 511 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 512 Ads: &v3corepb.AggregatedConfigSource{}, 513 }, 514 }, 515 ServiceName: v3Service, 516 }, 517 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 518 LrsServer: &v3corepb.ConfigSource{ 519 ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{ 520 Self: &v3corepb.SelfConfigSource{}, 521 }, 522 }, 523 } 524 ) 525 526 tests := []struct { 527 name string 528 resources []*anypb.Any 529 wantUpdate map[string]ClusterUpdate 530 wantErr bool 531 }{ 532 { 533 name: "non-cluster resource type", 534 resources: []*anypb.Any{{TypeUrl: version.V3HTTPConnManagerURL}}, 535 wantErr: true, 536 }, 537 { 538 name: "badly marshaled cluster resource", 539 resources: []*anypb.Any{ 540 { 541 TypeUrl: version.V3ClusterURL, 542 Value: []byte{1, 2, 3, 4}, 543 }, 544 }, 545 wantErr: true, 546 }, 547 { 548 name: "bad cluster resource", 549 resources: []*anypb.Any{ 550 { 551 TypeUrl: version.V3ClusterURL, 552 Value: func() []byte { 553 cl := &v3clusterpb.Cluster{ 554 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_STATIC}, 555 } 556 mcl, _ := proto.Marshal(cl) 557 return mcl 558 }(), 559 }, 560 }, 561 wantErr: true, 562 }, 563 { 564 name: "v2 cluster", 565 resources: []*anypb.Any{ 566 { 567 TypeUrl: version.V3ClusterURL, 568 Value: func() []byte { 569 mcl, _ := proto.Marshal(v2Cluster) 570 return mcl 571 }(), 572 }, 573 }, 574 wantUpdate: map[string]ClusterUpdate{ 575 v2ClusterName: {ServiceName: v2Service, EnableLRS: true}, 576 }, 577 }, 578 { 579 name: "v3 cluster", 580 resources: []*anypb.Any{ 581 { 582 TypeUrl: version.V3ClusterURL, 583 Value: func() []byte { 584 mcl, _ := proto.Marshal(v3Cluster) 585 return mcl 586 }(), 587 }, 588 }, 589 wantUpdate: map[string]ClusterUpdate{ 590 v3ClusterName: {ServiceName: v3Service, EnableLRS: true}, 591 }, 592 }, 593 { 594 name: "multiple clusters", 595 resources: []*anypb.Any{ 596 { 597 TypeUrl: version.V3ClusterURL, 598 Value: func() []byte { 599 mcl, _ := proto.Marshal(v2Cluster) 600 return mcl 601 }(), 602 }, 603 { 604 TypeUrl: version.V3ClusterURL, 605 Value: func() []byte { 606 mcl, _ := proto.Marshal(v3Cluster) 607 return mcl 608 }(), 609 }, 610 }, 611 wantUpdate: map[string]ClusterUpdate{ 612 v2ClusterName: {ServiceName: v2Service, EnableLRS: true}, 613 v3ClusterName: {ServiceName: v3Service, EnableLRS: true}, 614 }, 615 }, 616 } 617 for _, test := range tests { 618 t.Run(test.name, func(t *testing.T) { 619 update, err := UnmarshalCluster(test.resources, nil) 620 if ((err != nil) != test.wantErr) || !cmp.Equal(update, test.wantUpdate, cmpopts.EquateEmpty()) { 621 t.Errorf("UnmarshalCluster(%v) = (%+v, %v) want (%+v, %v)", test.resources, update, err, test.wantUpdate, test.wantErr) 622 } 623 }) 624 } 625} 626