google.golang.org/grpc@v1.72.2/xds/internal/xdsclient/xdsresource/tests/unmarshal_cds_test.go (about) 1 /* 2 * 3 * Copyright 2023 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 19 // Package tests_test contains test cases for unmarshalling of CDS resources. 20 package tests_test 21 22 import ( 23 "encoding/json" 24 "testing" 25 26 "github.com/google/go-cmp/cmp" 27 "github.com/google/go-cmp/cmp/cmpopts" 28 "google.golang.org/grpc/balancer/leastrequest" 29 "google.golang.org/grpc/internal/balancer/stub" 30 "google.golang.org/grpc/internal/envconfig" 31 "google.golang.org/grpc/internal/grpctest" 32 iserviceconfig "google.golang.org/grpc/internal/serviceconfig" 33 "google.golang.org/grpc/internal/testutils" 34 "google.golang.org/grpc/internal/testutils/xds/e2e" 35 "google.golang.org/grpc/internal/xds/bootstrap" 36 "google.golang.org/grpc/serviceconfig" 37 "google.golang.org/grpc/xds/internal" 38 "google.golang.org/grpc/xds/internal/balancer/ringhash" 39 "google.golang.org/grpc/xds/internal/balancer/wrrlocality" 40 "google.golang.org/grpc/xds/internal/xdsclient/xdsresource" 41 "google.golang.org/protobuf/proto" 42 "google.golang.org/protobuf/types/known/anypb" 43 "google.golang.org/protobuf/types/known/structpb" 44 "google.golang.org/protobuf/types/known/wrapperspb" 45 46 v3xdsxdstypepb "github.com/cncf/xds/go/xds/type/v3" 47 v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" 48 v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 49 v3endpointpb "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" 50 v3aggregateclusterpb "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/aggregate/v3" 51 v3ringhashpb "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/ring_hash/v3" 52 v3roundrobinpb "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/round_robin/v3" 53 v3wrrlocalitypb "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/wrr_locality/v3" 54 55 _ "google.golang.org/grpc/balancer/roundrobin" // To register round_robin load balancer. 56 _ "google.golang.org/grpc/xds" // Register the xDS LB Registry Converters. 57 ) 58 59 type s struct { 60 grpctest.Tester 61 } 62 63 func Test(t *testing.T) { 64 grpctest.RunSubTests(t, s{}) 65 } 66 67 const ( 68 clusterName = "clusterName" 69 serviceName = "service" 70 ) 71 72 func wrrLocality(t *testing.T, m proto.Message) *v3wrrlocalitypb.WrrLocality { 73 return &v3wrrlocalitypb.WrrLocality{ 74 EndpointPickingPolicy: &v3clusterpb.LoadBalancingPolicy{ 75 Policies: []*v3clusterpb.LoadBalancingPolicy_Policy{ 76 { 77 TypedExtensionConfig: &v3corepb.TypedExtensionConfig{ 78 TypedConfig: testutils.MarshalAny(t, m), 79 }, 80 }, 81 }, 82 }, 83 } 84 } 85 86 func wrrLocalityAny(t *testing.T, m proto.Message) *anypb.Any { 87 return testutils.MarshalAny(t, wrrLocality(t, m)) 88 } 89 90 type customLBConfig struct { 91 serviceconfig.LoadBalancingConfig 92 } 93 94 // We have this test in a separate test package in order to not take a 95 // dependency on the internal xDS balancer packages within the xDS Client. 96 func (s) TestValidateCluster_Success(t *testing.T) { 97 const customLBPolicyName = "myorg.MyCustomLeastRequestPolicy" 98 stub.Register(customLBPolicyName, stub.BalancerFuncs{ 99 ParseConfig: func(json.RawMessage) (serviceconfig.LoadBalancingConfig, error) { 100 return customLBConfig{}, nil 101 }, 102 }) 103 serverCfg, err := bootstrap.ServerConfigForTesting(bootstrap.ServerConfigTestingOptions{URI: "test-server"}) 104 if err != nil { 105 t.Fatalf("Failed to create server config for testing: %v", err) 106 } 107 108 defer func(old bool) { envconfig.LeastRequestLB = old }(envconfig.LeastRequestLB) 109 envconfig.LeastRequestLB = true 110 tests := []struct { 111 name string 112 cluster *v3clusterpb.Cluster 113 serverCfg *bootstrap.ServerConfig 114 wantUpdate xdsresource.ClusterUpdate 115 wantLBConfig *iserviceconfig.BalancerConfig 116 }{ 117 { 118 name: "happy-case-logical-dns", 119 cluster: &v3clusterpb.Cluster{ 120 Name: clusterName, 121 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_LOGICAL_DNS}, 122 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 123 LoadAssignment: &v3endpointpb.ClusterLoadAssignment{ 124 Endpoints: []*v3endpointpb.LocalityLbEndpoints{{ 125 LbEndpoints: []*v3endpointpb.LbEndpoint{{ 126 HostIdentifier: &v3endpointpb.LbEndpoint_Endpoint{ 127 Endpoint: &v3endpointpb.Endpoint{ 128 Address: &v3corepb.Address{ 129 Address: &v3corepb.Address_SocketAddress{ 130 SocketAddress: &v3corepb.SocketAddress{ 131 Address: "dns_host", 132 PortSpecifier: &v3corepb.SocketAddress_PortValue{ 133 PortValue: 8080, 134 }, 135 }, 136 }, 137 }, 138 }, 139 }, 140 }}, 141 }}, 142 }, 143 }, 144 wantUpdate: xdsresource.ClusterUpdate{ 145 ClusterName: clusterName, 146 ClusterType: xdsresource.ClusterTypeLogicalDNS, 147 DNSHostName: "dns_host:8080", 148 TelemetryLabels: internal.UnknownCSMLabels, 149 }, 150 wantLBConfig: &iserviceconfig.BalancerConfig{ 151 Name: wrrlocality.Name, 152 Config: &wrrlocality.LBConfig{ 153 ChildPolicy: &iserviceconfig.BalancerConfig{ 154 Name: "round_robin", 155 }, 156 }, 157 }, 158 }, 159 { 160 name: "happy-case-aggregate-v3", 161 cluster: &v3clusterpb.Cluster{ 162 Name: clusterName, 163 ClusterDiscoveryType: &v3clusterpb.Cluster_ClusterType{ 164 ClusterType: &v3clusterpb.Cluster_CustomClusterType{ 165 Name: "envoy.clusters.aggregate", 166 TypedConfig: testutils.MarshalAny(t, &v3aggregateclusterpb.ClusterConfig{ 167 Clusters: []string{"a", "b", "c"}, 168 }), 169 }, 170 }, 171 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 172 }, 173 wantUpdate: xdsresource.ClusterUpdate{ 174 ClusterName: clusterName, 175 ClusterType: xdsresource.ClusterTypeAggregate, 176 PrioritizedClusterNames: []string{"a", "b", "c"}, 177 TelemetryLabels: internal.UnknownCSMLabels, 178 }, 179 wantLBConfig: &iserviceconfig.BalancerConfig{ 180 Name: wrrlocality.Name, 181 Config: &wrrlocality.LBConfig{ 182 ChildPolicy: &iserviceconfig.BalancerConfig{ 183 Name: "round_robin", 184 }, 185 }, 186 }, 187 }, 188 { 189 name: "happy-case-no-service-name-no-lrs", 190 cluster: e2e.DefaultCluster(clusterName, "", e2e.SecurityLevelNone), 191 wantUpdate: xdsresource.ClusterUpdate{ 192 ClusterName: clusterName, 193 TelemetryLabels: internal.UnknownCSMLabels, 194 }, 195 wantLBConfig: &iserviceconfig.BalancerConfig{ 196 Name: wrrlocality.Name, 197 Config: &wrrlocality.LBConfig{ 198 ChildPolicy: &iserviceconfig.BalancerConfig{ 199 Name: "round_robin", 200 }, 201 }, 202 }, 203 }, 204 { 205 name: "happy-case-no-lrs", 206 cluster: e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone), 207 wantUpdate: xdsresource.ClusterUpdate{ 208 ClusterName: clusterName, 209 EDSServiceName: serviceName, 210 TelemetryLabels: internal.UnknownCSMLabels, 211 }, 212 wantLBConfig: &iserviceconfig.BalancerConfig{ 213 Name: wrrlocality.Name, 214 Config: &wrrlocality.LBConfig{ 215 ChildPolicy: &iserviceconfig.BalancerConfig{ 216 Name: "round_robin", 217 }, 218 }, 219 }, 220 }, 221 { 222 name: "happiest-case-with-lrs", 223 cluster: e2e.ClusterResourceWithOptions(e2e.ClusterOptions{ 224 ClusterName: clusterName, 225 ServiceName: serviceName, 226 EnableLRS: true, 227 }), 228 serverCfg: serverCfg, 229 wantUpdate: xdsresource.ClusterUpdate{ 230 ClusterName: clusterName, 231 EDSServiceName: serviceName, 232 LRSServerConfig: serverCfg, 233 TelemetryLabels: internal.UnknownCSMLabels, 234 }, 235 wantLBConfig: &iserviceconfig.BalancerConfig{ 236 Name: wrrlocality.Name, 237 Config: &wrrlocality.LBConfig{ 238 ChildPolicy: &iserviceconfig.BalancerConfig{ 239 Name: "round_robin", 240 }, 241 }, 242 }, 243 }, 244 { 245 name: "happiest-case-with-circuitbreakers", 246 cluster: func() *v3clusterpb.Cluster { 247 c := e2e.ClusterResourceWithOptions(e2e.ClusterOptions{ 248 ClusterName: clusterName, 249 ServiceName: serviceName, 250 EnableLRS: true, 251 }) 252 c.CircuitBreakers = &v3clusterpb.CircuitBreakers{ 253 Thresholds: []*v3clusterpb.CircuitBreakers_Thresholds{ 254 { 255 Priority: v3corepb.RoutingPriority_DEFAULT, 256 MaxRequests: wrapperspb.UInt32(512), 257 }, 258 { 259 Priority: v3corepb.RoutingPriority_HIGH, 260 MaxRequests: nil, 261 }, 262 }, 263 } 264 return c 265 }(), 266 serverCfg: serverCfg, 267 wantUpdate: xdsresource.ClusterUpdate{ 268 ClusterName: clusterName, 269 EDSServiceName: serviceName, 270 LRSServerConfig: serverCfg, 271 MaxRequests: func() *uint32 { i := uint32(512); return &i }(), 272 TelemetryLabels: internal.UnknownCSMLabels, 273 }, 274 wantLBConfig: &iserviceconfig.BalancerConfig{ 275 Name: wrrlocality.Name, 276 Config: &wrrlocality.LBConfig{ 277 ChildPolicy: &iserviceconfig.BalancerConfig{ 278 Name: "round_robin", 279 }, 280 }, 281 }, 282 }, 283 { 284 name: "happiest-case-with-ring-hash-lb-policy-with-default-config", 285 cluster: func() *v3clusterpb.Cluster { 286 c := e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone) 287 c.LbPolicy = v3clusterpb.Cluster_RING_HASH 288 return c 289 }(), 290 wantUpdate: xdsresource.ClusterUpdate{ 291 ClusterName: clusterName, 292 EDSServiceName: serviceName, 293 TelemetryLabels: internal.UnknownCSMLabels, 294 }, 295 wantLBConfig: &iserviceconfig.BalancerConfig{ 296 Name: "ring_hash_experimental", 297 Config: &ringhash.LBConfig{ 298 MinRingSize: 1024, 299 MaxRingSize: 4096, 300 }, 301 }, 302 }, 303 { 304 name: "happiest-case-with-least-request-lb-policy-with-default-config", 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_LEAST_REQUEST, 317 }, 318 wantUpdate: xdsresource.ClusterUpdate{ 319 ClusterName: clusterName, 320 EDSServiceName: serviceName, 321 TelemetryLabels: internal.UnknownCSMLabels, 322 }, 323 wantLBConfig: &iserviceconfig.BalancerConfig{ 324 Name: "least_request_experimental", 325 Config: &leastrequest.LBConfig{ 326 ChoiceCount: 2, 327 }, 328 }, 329 }, 330 { 331 name: "happiest-case-with-ring-hash-lb-policy-with-none-default-config", 332 cluster: func() *v3clusterpb.Cluster { 333 c := e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone) 334 c.LbPolicy = v3clusterpb.Cluster_RING_HASH 335 c.LbConfig = &v3clusterpb.Cluster_RingHashLbConfig_{ 336 RingHashLbConfig: &v3clusterpb.Cluster_RingHashLbConfig{ 337 MinimumRingSize: wrapperspb.UInt64(10), 338 MaximumRingSize: wrapperspb.UInt64(100), 339 }, 340 } 341 return c 342 }(), 343 wantUpdate: xdsresource.ClusterUpdate{ 344 ClusterName: clusterName, 345 EDSServiceName: serviceName, 346 TelemetryLabels: internal.UnknownCSMLabels, 347 }, 348 wantLBConfig: &iserviceconfig.BalancerConfig{ 349 Name: "ring_hash_experimental", 350 Config: &ringhash.LBConfig{ 351 MinRingSize: 10, 352 MaxRingSize: 100, 353 }, 354 }, 355 }, 356 { 357 name: "happiest-case-with-least-request-lb-policy-with-none-default-config", 358 cluster: &v3clusterpb.Cluster{ 359 Name: clusterName, 360 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 361 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 362 EdsConfig: &v3corepb.ConfigSource{ 363 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 364 Ads: &v3corepb.AggregatedConfigSource{}, 365 }, 366 }, 367 ServiceName: serviceName, 368 }, 369 LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST, 370 LbConfig: &v3clusterpb.Cluster_LeastRequestLbConfig_{ 371 LeastRequestLbConfig: &v3clusterpb.Cluster_LeastRequestLbConfig{ 372 ChoiceCount: wrapperspb.UInt32(3), 373 }, 374 }, 375 }, 376 wantUpdate: xdsresource.ClusterUpdate{ 377 ClusterName: clusterName, 378 EDSServiceName: serviceName, 379 TelemetryLabels: internal.UnknownCSMLabels, 380 }, 381 wantLBConfig: &iserviceconfig.BalancerConfig{ 382 Name: "least_request_experimental", 383 Config: &leastrequest.LBConfig{ 384 ChoiceCount: 3, 385 }, 386 }, 387 }, 388 { 389 name: "happiest-case-with-ring-hash-lb-policy-configured-through-LoadBalancingPolicy", 390 cluster: &v3clusterpb.Cluster{ 391 Name: clusterName, 392 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 393 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 394 EdsConfig: &v3corepb.ConfigSource{ 395 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 396 Ads: &v3corepb.AggregatedConfigSource{}, 397 }, 398 }, 399 ServiceName: serviceName, 400 }, 401 LoadBalancingPolicy: &v3clusterpb.LoadBalancingPolicy{ 402 Policies: []*v3clusterpb.LoadBalancingPolicy_Policy{ 403 { 404 TypedExtensionConfig: &v3corepb.TypedExtensionConfig{ 405 TypedConfig: testutils.MarshalAny(t, &v3ringhashpb.RingHash{ 406 HashFunction: v3ringhashpb.RingHash_XX_HASH, 407 MinimumRingSize: wrapperspb.UInt64(10), 408 MaximumRingSize: wrapperspb.UInt64(100), 409 }), 410 }, 411 }, 412 }, 413 }, 414 }, 415 wantUpdate: xdsresource.ClusterUpdate{ 416 ClusterName: clusterName, 417 EDSServiceName: serviceName, 418 TelemetryLabels: internal.UnknownCSMLabels, 419 }, 420 wantLBConfig: &iserviceconfig.BalancerConfig{ 421 Name: "ring_hash_experimental", 422 Config: &ringhash.LBConfig{ 423 MinRingSize: 10, 424 MaxRingSize: 100, 425 }, 426 }, 427 }, 428 { 429 name: "happiest-case-with-wrrlocality-rr-child-configured-through-LoadBalancingPolicy", 430 cluster: &v3clusterpb.Cluster{ 431 Name: clusterName, 432 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 433 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 434 EdsConfig: &v3corepb.ConfigSource{ 435 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 436 Ads: &v3corepb.AggregatedConfigSource{}, 437 }, 438 }, 439 ServiceName: serviceName, 440 }, 441 LoadBalancingPolicy: &v3clusterpb.LoadBalancingPolicy{ 442 Policies: []*v3clusterpb.LoadBalancingPolicy_Policy{ 443 { 444 TypedExtensionConfig: &v3corepb.TypedExtensionConfig{ 445 TypedConfig: wrrLocalityAny(t, &v3roundrobinpb.RoundRobin{}), 446 }, 447 }, 448 }, 449 }, 450 }, 451 wantUpdate: xdsresource.ClusterUpdate{ 452 ClusterName: clusterName, 453 EDSServiceName: serviceName, 454 TelemetryLabels: internal.UnknownCSMLabels, 455 }, 456 wantLBConfig: &iserviceconfig.BalancerConfig{ 457 Name: wrrlocality.Name, 458 Config: &wrrlocality.LBConfig{ 459 ChildPolicy: &iserviceconfig.BalancerConfig{ 460 Name: "round_robin", 461 }, 462 }, 463 }, 464 }, 465 { 466 name: "happiest-case-with-custom-lb-configured-through-LoadBalancingPolicy", 467 cluster: &v3clusterpb.Cluster{ 468 Name: clusterName, 469 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 470 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 471 EdsConfig: &v3corepb.ConfigSource{ 472 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 473 Ads: &v3corepb.AggregatedConfigSource{}, 474 }, 475 }, 476 ServiceName: serviceName, 477 }, 478 LoadBalancingPolicy: &v3clusterpb.LoadBalancingPolicy{ 479 Policies: []*v3clusterpb.LoadBalancingPolicy_Policy{ 480 { 481 TypedExtensionConfig: &v3corepb.TypedExtensionConfig{ 482 TypedConfig: wrrLocalityAny(t, &v3xdsxdstypepb.TypedStruct{ 483 TypeUrl: "type.googleapis.com/myorg.MyCustomLeastRequestPolicy", 484 Value: &structpb.Struct{}, 485 }), 486 }, 487 }, 488 }, 489 }, 490 }, 491 wantUpdate: xdsresource.ClusterUpdate{ 492 ClusterName: clusterName, 493 EDSServiceName: serviceName, 494 TelemetryLabels: internal.UnknownCSMLabels, 495 }, 496 wantLBConfig: &iserviceconfig.BalancerConfig{ 497 Name: wrrlocality.Name, 498 Config: &wrrlocality.LBConfig{ 499 ChildPolicy: &iserviceconfig.BalancerConfig{ 500 Name: "myorg.MyCustomLeastRequestPolicy", 501 Config: customLBConfig{}, 502 }, 503 }, 504 }, 505 }, 506 { 507 name: "load-balancing-policy-takes-precedence-over-lb-policy-and-enum", 508 cluster: &v3clusterpb.Cluster{ 509 Name: clusterName, 510 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 511 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 512 EdsConfig: &v3corepb.ConfigSource{ 513 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 514 Ads: &v3corepb.AggregatedConfigSource{}, 515 }, 516 }, 517 ServiceName: serviceName, 518 }, 519 LbPolicy: v3clusterpb.Cluster_RING_HASH, 520 LbConfig: &v3clusterpb.Cluster_RingHashLbConfig_{ 521 RingHashLbConfig: &v3clusterpb.Cluster_RingHashLbConfig{ 522 MinimumRingSize: wrapperspb.UInt64(20), 523 MaximumRingSize: wrapperspb.UInt64(200), 524 }, 525 }, 526 LoadBalancingPolicy: &v3clusterpb.LoadBalancingPolicy{ 527 Policies: []*v3clusterpb.LoadBalancingPolicy_Policy{ 528 { 529 TypedExtensionConfig: &v3corepb.TypedExtensionConfig{ 530 TypedConfig: testutils.MarshalAny(t, &v3ringhashpb.RingHash{ 531 HashFunction: v3ringhashpb.RingHash_XX_HASH, 532 MinimumRingSize: wrapperspb.UInt64(10), 533 MaximumRingSize: wrapperspb.UInt64(100), 534 }), 535 }, 536 }, 537 }, 538 }, 539 }, 540 wantUpdate: xdsresource.ClusterUpdate{ 541 ClusterName: clusterName, 542 EDSServiceName: serviceName, 543 TelemetryLabels: internal.UnknownCSMLabels, 544 }, 545 wantLBConfig: &iserviceconfig.BalancerConfig{ 546 Name: "ring_hash_experimental", 547 Config: &ringhash.LBConfig{ 548 MinRingSize: 10, 549 MaxRingSize: 100, 550 }, 551 }, 552 }, 553 } 554 555 for _, test := range tests { 556 t.Run(test.name, func(t *testing.T) { 557 update, err := xdsresource.ValidateClusterAndConstructClusterUpdateForTesting(test.cluster, test.serverCfg) 558 if err != nil { 559 t.Errorf("validateClusterAndConstructClusterUpdate(%+v) failed: %v", test.cluster, err) 560 } 561 // Ignore the raw JSON string into the cluster update. JSON bytes 562 // are nondeterministic (whitespace etc.) so we cannot reliably 563 // compare JSON bytes in a test. Thus, marshal into a Balancer 564 // Config struct and compare on that. Only need to test this JSON 565 // emission here, as this covers the possible output space. 566 if diff := cmp.Diff(update, test.wantUpdate, cmpopts.EquateEmpty(), cmpopts.IgnoreFields(xdsresource.ClusterUpdate{}, "LBPolicy")); diff != "" { 567 t.Errorf("validateClusterAndConstructClusterUpdate(%+v) got diff: %v (-got, +want)", test.cluster, diff) 568 } 569 bc := &iserviceconfig.BalancerConfig{} 570 if err := json.Unmarshal(update.LBPolicy, bc); err != nil { 571 t.Fatalf("failed to unmarshal JSON: %v", err) 572 } 573 if diff := cmp.Diff(bc, test.wantLBConfig); diff != "" { 574 t.Fatalf("update.LBConfig got unexpected output, diff (-got +want): %v", diff) 575 } 576 }) 577 } 578 }