google.golang.org/grpc@v1.72.2/xds/internal/balancer/clusterresolver/e2e_test/eds_impl_test.go (about) 1 /* 2 * Copyright 2022 gRPC authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package e2e_test 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "strings" 24 "sync/atomic" 25 "testing" 26 "time" 27 28 "github.com/google/go-cmp/cmp" 29 "github.com/google/uuid" 30 "google.golang.org/grpc" 31 "google.golang.org/grpc/balancer" 32 "google.golang.org/grpc/balancer/roundrobin" 33 "google.golang.org/grpc/codes" 34 "google.golang.org/grpc/credentials/insecure" 35 "google.golang.org/grpc/internal" 36 "google.golang.org/grpc/internal/balancer/stub" 37 "google.golang.org/grpc/internal/envconfig" 38 "google.golang.org/grpc/internal/grpctest" 39 "google.golang.org/grpc/internal/stubserver" 40 "google.golang.org/grpc/internal/testutils" 41 rrutil "google.golang.org/grpc/internal/testutils/roundrobin" 42 "google.golang.org/grpc/internal/testutils/xds/e2e" 43 "google.golang.org/grpc/internal/xds/bootstrap" 44 "google.golang.org/grpc/peer" 45 "google.golang.org/grpc/resolver" 46 "google.golang.org/grpc/resolver/manual" 47 "google.golang.org/grpc/serviceconfig" 48 "google.golang.org/grpc/status" 49 "google.golang.org/grpc/xds/internal/xdsclient" 50 "google.golang.org/grpc/xds/internal/xdsclient/xdsresource/version" 51 "google.golang.org/protobuf/types/known/wrapperspb" 52 53 v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" 54 v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 55 v3endpointpb "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" 56 v3discoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" 57 testgrpc "google.golang.org/grpc/interop/grpc_testing" 58 testpb "google.golang.org/grpc/interop/grpc_testing" 59 60 _ "google.golang.org/grpc/xds/internal/balancer/clusterresolver" // Register the "cluster_resolver_experimental" LB policy. 61 "google.golang.org/grpc/xds/internal/balancer/priority" 62 ) 63 64 const ( 65 clusterName = "cluster-my-service-client-side-xds" 66 edsServiceName = "endpoints-my-service-client-side-xds" 67 localityName1 = "my-locality-1" 68 localityName2 = "my-locality-2" 69 localityName3 = "my-locality-3" 70 71 defaultTestTimeout = 5 * time.Second 72 defaultTestShortTimeout = 10 * time.Millisecond 73 defaultTestWatchExpiryTimeout = 500 * time.Millisecond 74 ) 75 76 type s struct { 77 grpctest.Tester 78 } 79 80 func Test(t *testing.T) { 81 grpctest.RunSubTests(t, s{}) 82 } 83 84 // backendAddressesAndPorts extracts the address and port of each of the 85 // StubServers passed in and returns them. Fails the test if any of the 86 // StubServers passed have an invalid address. 87 func backendAddressesAndPorts(t *testing.T, servers []*stubserver.StubServer) ([]resolver.Address, []uint32) { 88 addrs := make([]resolver.Address, len(servers)) 89 ports := make([]uint32, len(servers)) 90 for i := 0; i < len(servers); i++ { 91 addrs[i] = resolver.Address{Addr: servers[i].Address} 92 ports[i] = testutils.ParsePort(t, servers[i].Address) 93 } 94 return addrs, ports 95 } 96 97 func startTestServiceBackends(t *testing.T, numBackends int) ([]*stubserver.StubServer, func()) { 98 var servers []*stubserver.StubServer 99 for i := 0; i < numBackends; i++ { 100 servers = append(servers, stubserver.StartTestService(t, nil)) 101 servers[i].StartServer() 102 } 103 104 return servers, func() { 105 for _, server := range servers { 106 server.Stop() 107 } 108 } 109 } 110 111 // clientEndpointsResource returns an EDS resource for the specified nodeID, 112 // service name and localities. 113 func clientEndpointsResource(nodeID, edsServiceName string, localities []e2e.LocalityOptions) e2e.UpdateOptions { 114 return e2e.UpdateOptions{ 115 NodeID: nodeID, 116 Endpoints: []*v3endpointpb.ClusterLoadAssignment{e2e.EndpointResourceWithOptions(e2e.EndpointOptions{ 117 ClusterName: edsServiceName, 118 Host: "localhost", 119 Localities: localities, 120 })}, 121 SkipValidation: true, 122 } 123 } 124 125 // TestEDS_OneLocality tests the cluster_resolver LB policy using an EDS 126 // resource with one locality. The following scenarios are tested: 127 // 1. Single backend. Test verifies that RPCs reach this backend. 128 // 2. Add a backend. Test verifies that RPCs are roundrobined across the two 129 // backends. 130 // 3. Remove one backend. Test verifies that all RPCs reach the other backend. 131 // 4. Replace the backend. Test verifies that all RPCs reach the new backend. 132 func (s) TestEDS_OneLocality(t *testing.T) { 133 // Spin up a management server to receive xDS resources from. 134 managementServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 135 136 // Create bootstrap configuration pointing to the above management server. 137 nodeID := uuid.New().String() 138 bootstrapContents := e2e.DefaultBootstrapContents(t, nodeID, managementServer.Address) 139 140 // Start backend servers which provide an implementation of the TestService. 141 servers, cleanup2 := startTestServiceBackends(t, 3) 142 defer cleanup2() 143 addrs, ports := backendAddressesAndPorts(t, servers) 144 145 // Create xDS resources for consumption by the test. We start off with a 146 // single backend in a single EDS locality. 147 resources := clientEndpointsResource(nodeID, edsServiceName, []e2e.LocalityOptions{{ 148 Name: localityName1, 149 Weight: 1, 150 Backends: []e2e.BackendOptions{{Ports: []uint32{ports[0]}}}, 151 }}) 152 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 153 defer cancel() 154 if err := managementServer.Update(ctx, resources); err != nil { 155 t.Fatal(err) 156 } 157 158 // Create an xDS client for use by the cluster_resolver LB policy. 159 config, err := bootstrap.NewConfigFromContents(bootstrapContents) 160 if err != nil { 161 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bootstrapContents), err) 162 } 163 pool := xdsclient.NewPool(config) 164 client, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 165 Name: t.Name(), 166 }) 167 if err != nil { 168 t.Fatalf("Failed to create xDS client: %v", err) 169 } 170 defer close() 171 172 // Create a manual resolver and push a service config specifying the use of 173 // the cluster_resolver LB policy with a single discovery mechanism. 174 r := manual.NewBuilderWithScheme("whatever") 175 jsonSC := fmt.Sprintf(`{ 176 "loadBalancingConfig":[{ 177 "cluster_resolver_experimental":{ 178 "discoveryMechanisms": [{ 179 "cluster": "%s", 180 "type": "EDS", 181 "edsServiceName": "%s", 182 "outlierDetection": {} 183 }], 184 "xdsLbPolicy":[{"round_robin":{}}] 185 } 186 }] 187 }`, clusterName, edsServiceName) 188 scpr := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(jsonSC) 189 r.InitialState(xdsclient.SetClient(resolver.State{ServiceConfig: scpr}, client)) 190 191 // Create a ClientConn and make a successful RPC. 192 cc, err := grpc.NewClient(r.Scheme()+":///test.service", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(r)) 193 if err != nil { 194 t.Fatalf("failed to create new client for local test server: %v", err) 195 } 196 defer cc.Close() 197 198 // Ensure RPCs are being roundrobined across the single backend. 199 testClient := testgrpc.NewTestServiceClient(cc) 200 if err := rrutil.CheckRoundRobinRPCs(ctx, testClient, addrs[:1]); err != nil { 201 t.Fatal(err) 202 } 203 204 // Add a backend to the same locality, and ensure RPCs are sent in a 205 // roundrobin fashion across the two backends. 206 resources = clientEndpointsResource(nodeID, edsServiceName, []e2e.LocalityOptions{{ 207 Name: localityName1, 208 Weight: 1, 209 Backends: []e2e.BackendOptions{{Ports: []uint32{ports[0]}}, {Ports: []uint32{ports[1]}}}, 210 }}) 211 if err := managementServer.Update(ctx, resources); err != nil { 212 t.Fatal(err) 213 } 214 if err := rrutil.CheckRoundRobinRPCs(ctx, testClient, addrs[:2]); err != nil { 215 t.Fatal(err) 216 } 217 218 // Remove the first backend, and ensure all RPCs are sent to the second 219 // backend. 220 resources = clientEndpointsResource(nodeID, edsServiceName, []e2e.LocalityOptions{{ 221 Name: localityName1, 222 Weight: 1, 223 Backends: []e2e.BackendOptions{{Ports: []uint32{ports[1]}}}, 224 }}) 225 if err := managementServer.Update(ctx, resources); err != nil { 226 t.Fatal(err) 227 } 228 if err := rrutil.CheckRoundRobinRPCs(ctx, testClient, addrs[1:2]); err != nil { 229 t.Fatal(err) 230 } 231 232 // Replace the backend, and ensure all RPCs are sent to the new backend. 233 resources = clientEndpointsResource(nodeID, edsServiceName, []e2e.LocalityOptions{{ 234 Name: localityName1, 235 Weight: 1, 236 Backends: []e2e.BackendOptions{{Ports: []uint32{ports[2]}}}, 237 }}) 238 if err := managementServer.Update(ctx, resources); err != nil { 239 t.Fatal(err) 240 } 241 if err := rrutil.CheckRoundRobinRPCs(ctx, testClient, addrs[2:3]); err != nil { 242 t.Fatal(err) 243 } 244 } 245 246 // TestEDS_MultipleLocalities tests the cluster_resolver LB policy using an EDS 247 // resource with multiple localities. The following scenarios are tested: 248 // 1. Two localities, each with a single backend. Test verifies that RPCs are 249 // weighted roundrobined across these two backends. 250 // 2. Add another locality, with a single backend. Test verifies that RPCs are 251 // weighted roundrobined across all the backends. 252 // 3. Remove one locality. Test verifies that RPCs are weighted roundrobined 253 // across backends from the remaining localities. 254 // 4. Add a backend to one locality. Test verifies that RPCs are weighted 255 // roundrobined across localities. 256 // 5. Change the weight of one of the localities. Test verifies that RPCs are 257 // weighted roundrobined across the localities. 258 // 259 // In our LB policy tree, one of the descendents of the "cluster_resolver" LB 260 // policy is the "weighted_target" LB policy which performs weighted roundrobin 261 // across localities (and this has a randomness component associated with it). 262 // Therefore, the moment we have backends from more than one locality, RPCs are 263 // weighted roundrobined across them. 264 func (s) TestEDS_MultipleLocalities(t *testing.T) { 265 // Spin up a management server to receive xDS resources from. 266 managementServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 267 268 // Create bootstrap configuration pointing to the above management server. 269 nodeID := uuid.New().String() 270 bootstrapContents := e2e.DefaultBootstrapContents(t, nodeID, managementServer.Address) 271 272 // Start backend servers which provide an implementation of the TestService. 273 servers, cleanup2 := startTestServiceBackends(t, 4) 274 defer cleanup2() 275 addrs, ports := backendAddressesAndPorts(t, servers) 276 277 // Create xDS resources for consumption by the test. We start off with two 278 // localities, and single backend in each of them. 279 resources := clientEndpointsResource(nodeID, edsServiceName, []e2e.LocalityOptions{ 280 { 281 Name: localityName1, 282 Weight: 1, 283 Backends: []e2e.BackendOptions{{Ports: []uint32{ports[0]}}}, 284 }, 285 { 286 Name: localityName2, 287 Weight: 1, 288 Backends: []e2e.BackendOptions{{Ports: []uint32{ports[1]}}}, 289 }, 290 }) 291 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 292 defer cancel() 293 if err := managementServer.Update(ctx, resources); err != nil { 294 t.Fatal(err) 295 } 296 297 // Create an xDS client for use by the cluster_resolver LB policy. 298 config, err := bootstrap.NewConfigFromContents(bootstrapContents) 299 if err != nil { 300 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bootstrapContents), err) 301 } 302 pool := xdsclient.NewPool(config) 303 client, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 304 Name: t.Name(), 305 }) 306 if err != nil { 307 t.Fatalf("Failed to create xDS client: %v", err) 308 } 309 defer close() 310 311 // Create a manual resolver and push service config specifying the use of 312 // the cluster_resolver LB policy with a single discovery mechanism. 313 r := manual.NewBuilderWithScheme("whatever") 314 jsonSC := fmt.Sprintf(`{ 315 "loadBalancingConfig":[{ 316 "cluster_resolver_experimental":{ 317 "discoveryMechanisms": [{ 318 "cluster": "%s", 319 "type": "EDS", 320 "edsServiceName": "%s", 321 "outlierDetection": {} 322 }], 323 "xdsLbPolicy":[{"round_robin":{}}] 324 } 325 }] 326 }`, clusterName, edsServiceName) 327 scpr := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(jsonSC) 328 r.InitialState(xdsclient.SetClient(resolver.State{ServiceConfig: scpr}, client)) 329 330 // Create a ClientConn and make a successful RPC. 331 cc, err := grpc.NewClient(r.Scheme()+":///test.service", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(r)) 332 if err != nil { 333 t.Fatalf("failed to create new client for local test server: %v", err) 334 } 335 defer cc.Close() 336 337 // Ensure RPCs are being weighted roundrobined across the two backends. 338 testClient := testgrpc.NewTestServiceClient(cc) 339 if err := rrutil.CheckWeightedRoundRobinRPCs(ctx, testClient, addrs[0:2]); err != nil { 340 t.Fatal(err) 341 } 342 343 // Add another locality with a single backend, and ensure RPCs are being 344 // weighted roundrobined across the three backends. 345 resources = clientEndpointsResource(nodeID, edsServiceName, []e2e.LocalityOptions{ 346 { 347 Name: localityName1, 348 Weight: 1, 349 Backends: []e2e.BackendOptions{{Ports: []uint32{ports[0]}}}, 350 }, 351 { 352 Name: localityName2, 353 Weight: 1, 354 Backends: []e2e.BackendOptions{{Ports: []uint32{ports[1]}}}, 355 }, 356 { 357 Name: localityName3, 358 Weight: 1, 359 Backends: []e2e.BackendOptions{{Ports: []uint32{ports[2]}}}, 360 }, 361 }) 362 if err := managementServer.Update(ctx, resources); err != nil { 363 t.Fatal(err) 364 } 365 if err := rrutil.CheckWeightedRoundRobinRPCs(ctx, testClient, addrs[0:3]); err != nil { 366 t.Fatal(err) 367 } 368 369 // Remove the first locality, and ensure RPCs are being weighted 370 // roundrobined across the remaining two backends. 371 resources = clientEndpointsResource(nodeID, edsServiceName, []e2e.LocalityOptions{ 372 { 373 Name: localityName2, 374 Weight: 1, 375 Backends: []e2e.BackendOptions{{Ports: []uint32{ports[1]}}}, 376 }, 377 { 378 Name: localityName3, 379 Weight: 1, 380 Backends: []e2e.BackendOptions{{Ports: []uint32{ports[2]}}}, 381 }, 382 }) 383 if err := managementServer.Update(ctx, resources); err != nil { 384 t.Fatal(err) 385 } 386 if err := rrutil.CheckWeightedRoundRobinRPCs(ctx, testClient, addrs[1:3]); err != nil { 387 t.Fatal(err) 388 } 389 390 // Add a backend to one locality, and ensure weighted roundrobin. Since RPCs 391 // are roundrobined across localities, locality2's backend will receive 392 // twice the traffic. 393 resources = clientEndpointsResource(nodeID, edsServiceName, []e2e.LocalityOptions{ 394 { 395 Name: localityName2, 396 Weight: 1, 397 Backends: []e2e.BackendOptions{{Ports: []uint32{ports[1]}}}, 398 }, 399 { 400 Name: localityName3, 401 Weight: 1, 402 Backends: []e2e.BackendOptions{{Ports: []uint32{ports[2]}}, {Ports: []uint32{ports[3]}}}, 403 }, 404 }) 405 if err := managementServer.Update(ctx, resources); err != nil { 406 t.Fatal(err) 407 } 408 wantAddrs := []resolver.Address{addrs[1], addrs[1], addrs[2], addrs[3]} 409 if err := rrutil.CheckWeightedRoundRobinRPCs(ctx, testClient, wantAddrs); err != nil { 410 t.Fatal(err) 411 } 412 } 413 414 // TestEDS_EndpointsHealth tests the cluster_resolver LB policy using an EDS 415 // resource which specifies endpoint health information and verifies that 416 // traffic is routed only to backends deemed capable of receiving traffic. 417 func (s) TestEDS_EndpointsHealth(t *testing.T) { 418 // Spin up a management server to receive xDS resources from. 419 managementServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 420 421 // Create bootstrap configuration pointing to the above management server. 422 nodeID := uuid.New().String() 423 bootstrapContents := e2e.DefaultBootstrapContents(t, nodeID, managementServer.Address) 424 425 // Start backend servers which provide an implementation of the TestService. 426 servers, cleanup2 := startTestServiceBackends(t, 12) 427 defer cleanup2() 428 addrs, ports := backendAddressesAndPorts(t, servers) 429 430 // Create xDS resources for consumption by the test. Two localities with 431 // six backends each, with two of the six backends being healthy. Both 432 // UNKNOWN and HEALTHY are considered by gRPC for load balancing. 433 resources := clientEndpointsResource(nodeID, edsServiceName, []e2e.LocalityOptions{ 434 { 435 Name: localityName1, 436 Weight: 1, 437 Backends: []e2e.BackendOptions{ 438 {Ports: []uint32{ports[0]}, HealthStatus: v3corepb.HealthStatus_UNKNOWN}, 439 {Ports: []uint32{ports[1]}, HealthStatus: v3corepb.HealthStatus_HEALTHY}, 440 {Ports: []uint32{ports[2]}, HealthStatus: v3corepb.HealthStatus_UNHEALTHY}, 441 {Ports: []uint32{ports[3]}, HealthStatus: v3corepb.HealthStatus_DRAINING}, 442 {Ports: []uint32{ports[4]}, HealthStatus: v3corepb.HealthStatus_TIMEOUT}, 443 {Ports: []uint32{ports[5]}, HealthStatus: v3corepb.HealthStatus_DEGRADED}, 444 }, 445 }, 446 { 447 Name: localityName2, 448 Weight: 1, 449 Backends: []e2e.BackendOptions{ 450 {Ports: []uint32{ports[6]}, HealthStatus: v3corepb.HealthStatus_UNKNOWN}, 451 {Ports: []uint32{ports[7]}, HealthStatus: v3corepb.HealthStatus_HEALTHY}, 452 {Ports: []uint32{ports[8]}, HealthStatus: v3corepb.HealthStatus_UNHEALTHY}, 453 {Ports: []uint32{ports[9]}, HealthStatus: v3corepb.HealthStatus_DRAINING}, 454 {Ports: []uint32{ports[10]}, HealthStatus: v3corepb.HealthStatus_TIMEOUT}, 455 {Ports: []uint32{ports[11]}, HealthStatus: v3corepb.HealthStatus_DEGRADED}, 456 }, 457 }, 458 }) 459 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 460 defer cancel() 461 if err := managementServer.Update(ctx, resources); err != nil { 462 t.Fatal(err) 463 } 464 465 // Create an xDS client for use by the cluster_resolver LB policy. 466 config, err := bootstrap.NewConfigFromContents(bootstrapContents) 467 if err != nil { 468 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bootstrapContents), err) 469 } 470 pool := xdsclient.NewPool(config) 471 client, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 472 Name: t.Name(), 473 }) 474 if err != nil { 475 t.Fatalf("Failed to create xDS client: %v", err) 476 } 477 defer close() 478 479 // Create a manual resolver and push service config specifying the use of 480 // the cluster_resolver LB policy with a single discovery mechanism. 481 r := manual.NewBuilderWithScheme("whatever") 482 jsonSC := fmt.Sprintf(`{ 483 "loadBalancingConfig":[{ 484 "cluster_resolver_experimental":{ 485 "discoveryMechanisms": [{ 486 "cluster": "%s", 487 "type": "EDS", 488 "edsServiceName": "%s", 489 "outlierDetection": {} 490 }], 491 "xdsLbPolicy":[{"round_robin":{}}] 492 } 493 }] 494 }`, clusterName, edsServiceName) 495 scpr := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(jsonSC) 496 r.InitialState(xdsclient.SetClient(resolver.State{ServiceConfig: scpr}, client)) 497 498 // Create a ClientConn and make a successful RPC. 499 cc, err := grpc.NewClient(r.Scheme()+":///test.service", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(r)) 500 if err != nil { 501 t.Fatalf("failed to create new client for local test server: %v", err) 502 } 503 defer cc.Close() 504 505 // Ensure RPCs are being weighted roundrobined across healthy backends from 506 // both localities. 507 testClient := testgrpc.NewTestServiceClient(cc) 508 if err := rrutil.CheckWeightedRoundRobinRPCs(ctx, testClient, append(addrs[0:2], addrs[6:8]...)); err != nil { 509 t.Fatal(err) 510 } 511 } 512 513 // TestEDS_EmptyUpdate tests the cluster_resolver LB policy using an EDS 514 // resource with no localities and verifies that RPCs fail with "all priorities 515 // removed" error. 516 func (s) TestEDS_EmptyUpdate(t *testing.T) { 517 // Spin up a management server to receive xDS resources from. 518 managementServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 519 520 // Create bootstrap configuration pointing to the above management server. 521 nodeID := uuid.New().String() 522 bootstrapContents := e2e.DefaultBootstrapContents(t, nodeID, managementServer.Address) 523 524 // Start backend servers which provide an implementation of the TestService. 525 servers, cleanup2 := startTestServiceBackends(t, 4) 526 defer cleanup2() 527 addrs, ports := backendAddressesAndPorts(t, servers) 528 529 oldCacheTimeout := priority.DefaultSubBalancerCloseTimeout 530 priority.DefaultSubBalancerCloseTimeout = 100 * time.Microsecond 531 defer func() { priority.DefaultSubBalancerCloseTimeout = oldCacheTimeout }() 532 533 // Create xDS resources for consumption by the test. The first update is an 534 // empty update. This should put the channel in TRANSIENT_FAILURE. 535 resources := clientEndpointsResource(nodeID, edsServiceName, nil) 536 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 537 defer cancel() 538 if err := managementServer.Update(ctx, resources); err != nil { 539 t.Fatal(err) 540 } 541 542 // Create an xDS client for use by the cluster_resolver LB policy. 543 config, err := bootstrap.NewConfigFromContents(bootstrapContents) 544 if err != nil { 545 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bootstrapContents), err) 546 } 547 pool := xdsclient.NewPool(config) 548 client, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 549 Name: t.Name(), 550 }) 551 if err != nil { 552 t.Fatalf("Failed to create xDS client: %v", err) 553 } 554 defer close() 555 556 // Create a manual resolver and push service config specifying the use of 557 // the cluster_resolver LB policy with a single discovery mechanism. 558 r := manual.NewBuilderWithScheme("whatever") 559 jsonSC := fmt.Sprintf(`{ 560 "loadBalancingConfig":[{ 561 "cluster_resolver_experimental":{ 562 "discoveryMechanisms": [{ 563 "cluster": "%s", 564 "type": "EDS", 565 "edsServiceName": "%s", 566 "outlierDetection": {} 567 }], 568 "xdsLbPolicy":[{"round_robin":{}}] 569 } 570 }] 571 }`, clusterName, edsServiceName) 572 scpr := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(jsonSC) 573 r.InitialState(xdsclient.SetClient(resolver.State{ServiceConfig: scpr}, client)) 574 575 // Create a ClientConn and ensure that RPCs fail with "all priorities 576 // removed" error. This is the expected error when the cluster_resolver LB 577 // policy receives an EDS update with no localities. 578 cc, err := grpc.NewClient(r.Scheme()+":///test.service", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(r)) 579 if err != nil { 580 t.Fatalf("failed to create new client for local test server: %v", err) 581 } 582 defer cc.Close() 583 testClient := testgrpc.NewTestServiceClient(cc) 584 if err := waitForProducedZeroAddressesError(ctx, t, testClient); err != nil { 585 t.Fatal(err) 586 } 587 588 // Add a locality with one backend and ensure RPCs are successful. 589 resources = clientEndpointsResource(nodeID, edsServiceName, []e2e.LocalityOptions{{ 590 Name: localityName1, 591 Weight: 1, 592 Backends: []e2e.BackendOptions{{Ports: []uint32{ports[0]}}}, 593 }}) 594 if err := managementServer.Update(ctx, resources); err != nil { 595 t.Fatal(err) 596 } 597 if err := rrutil.CheckRoundRobinRPCs(ctx, testClient, addrs[:1]); err != nil { 598 t.Fatal(err) 599 } 600 601 // Push another empty update and ensure that RPCs fail with "all priorities 602 // removed" error again. 603 resources = clientEndpointsResource(nodeID, edsServiceName, nil) 604 if err := managementServer.Update(ctx, resources); err != nil { 605 t.Fatal(err) 606 } 607 if err := waitForProducedZeroAddressesError(ctx, t, testClient); err != nil { 608 t.Fatal(err) 609 } 610 } 611 612 // TestEDS_ResourceRemoved tests the case where the EDS resource requested by 613 // the clusterresolver LB policy is removed from the management server. The test 614 // verifies that the EDS watch is not canceled and that RPCs continue to succeed 615 // with the previously received configuration. 616 func (s) TestEDS_ResourceRemoved(t *testing.T) { 617 // Start an xDS management server that uses a couple of channels to 618 // notify the test about the following events: 619 // - an EDS requested with the expected resource name is requested 620 // - EDS resource is unrequested, i.e, an EDS request with no resource name 621 // is received, which indicates that we are no longer interested in that 622 // resource. 623 edsResourceRequestedCh := make(chan struct{}, 1) 624 edsResourceCanceledCh := make(chan struct{}, 1) 625 managementServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{ 626 OnStreamRequest: func(_ int64, req *v3discoverypb.DiscoveryRequest) error { 627 if req.GetTypeUrl() == version.V3EndpointsURL { 628 switch len(req.GetResourceNames()) { 629 case 0: 630 select { 631 case edsResourceCanceledCh <- struct{}{}: 632 default: 633 } 634 case 1: 635 if req.GetResourceNames()[0] == edsServiceName { 636 select { 637 case edsResourceRequestedCh <- struct{}{}: 638 default: 639 } 640 } 641 default: 642 t.Errorf("Unexpected number of resources, %d, in an EDS request", len(req.GetResourceNames())) 643 } 644 } 645 return nil 646 }, 647 }) 648 649 // Create bootstrap configuration pointing to the above management server. 650 nodeID := uuid.New().String() 651 bootstrapContents := e2e.DefaultBootstrapContents(t, nodeID, managementServer.Address) 652 653 server := stubserver.StartTestService(t, nil) 654 defer server.Stop() 655 656 // Configure cluster and endpoints resources in the management server. 657 resources := e2e.UpdateOptions{ 658 NodeID: nodeID, 659 Clusters: []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, edsServiceName, e2e.SecurityLevelNone)}, 660 Endpoints: []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(edsServiceName, "localhost", []uint32{testutils.ParsePort(t, server.Address)})}, 661 SkipValidation: true, 662 } 663 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 664 defer cancel() 665 if err := managementServer.Update(ctx, resources); err != nil { 666 t.Fatal(err) 667 } 668 669 // Create xDS client, configure cds_experimental LB policy with a manual 670 // resolver, and dial the test backends. 671 cc, cleanup := setupAndDial(t, bootstrapContents) 672 defer cleanup() 673 674 client := testgrpc.NewTestServiceClient(cc) 675 if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil { 676 t.Fatalf("EmptyCall() failed: %v", err) 677 } 678 679 // Delete the endpoints resource from the management server. 680 resources.Endpoints = nil 681 if err := managementServer.Update(ctx, resources); err != nil { 682 t.Fatal(err) 683 } 684 685 // Ensure that RPCs continue to succeed for the next second, and that the 686 // EDS watch is not canceled. 687 for end := time.Now().Add(time.Second); time.Now().Before(end); <-time.After(defaultTestShortTimeout) { 688 if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil { 689 t.Fatalf("EmptyCall() failed: %v", err) 690 } 691 select { 692 case <-edsResourceCanceledCh: 693 t.Fatal("EDS watch canceled when not expected to be canceled") 694 default: 695 } 696 } 697 } 698 699 // TestEDS_ClusterResourceDoesNotContainEDSServiceName tests the case where the 700 // Cluster resource sent by the management server does not contain an EDS 701 // service name. The test verifies that the cluster_resolver LB policy uses the 702 // cluster name for the EDS resource. 703 func (s) TestEDS_ClusterResourceDoesNotContainEDSServiceName(t *testing.T) { 704 edsResourceCh := make(chan string, 1) 705 managementServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{ 706 OnStreamRequest: func(_ int64, req *v3discoverypb.DiscoveryRequest) error { 707 if req.GetTypeUrl() != version.V3EndpointsURL { 708 return nil 709 } 710 if len(req.GetResourceNames()) > 0 { 711 select { 712 case edsResourceCh <- req.GetResourceNames()[0]: 713 default: 714 } 715 } 716 return nil 717 }, 718 }) 719 720 // Create bootstrap configuration pointing to the above management server. 721 nodeID := uuid.New().String() 722 bootstrapContents := e2e.DefaultBootstrapContents(t, nodeID, managementServer.Address) 723 724 server := stubserver.StartTestService(t, nil) 725 defer server.Stop() 726 727 // Configure cluster and endpoints resources with the same name in the management server. The cluster resource does not specify an EDS service name. 728 resources := e2e.UpdateOptions{ 729 NodeID: nodeID, 730 Clusters: []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, "", e2e.SecurityLevelNone)}, 731 Endpoints: []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(clusterName, "localhost", []uint32{testutils.ParsePort(t, server.Address)})}, 732 SkipValidation: true, 733 } 734 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 735 defer cancel() 736 if err := managementServer.Update(ctx, resources); err != nil { 737 t.Fatal(err) 738 } 739 740 // Create xDS client, configure cds_experimental LB policy with a manual 741 // resolver, and dial the test backends. 742 cc, cleanup := setupAndDial(t, bootstrapContents) 743 defer cleanup() 744 745 client := testgrpc.NewTestServiceClient(cc) 746 if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil { 747 t.Fatalf("EmptyCall() failed: %v", err) 748 } 749 750 select { 751 case <-ctx.Done(): 752 t.Fatal("Timeout when waiting for EDS request to be received on the management server") 753 case name := <-edsResourceCh: 754 if name != clusterName { 755 t.Fatalf("Received EDS request with resource name %q, want %q", name, clusterName) 756 } 757 } 758 } 759 760 // TestEDS_ClusterResourceUpdates verifies different scenarios with regards to 761 // cluster resource updates. 762 // 763 // - The first cluster resource contains an eds_service_name. The test verifies 764 // that an EDS request is sent for the received eds_service_name. It also 765 // verifies that a subsequent RPC gets routed to a backend belonging to that 766 // service name. 767 // - The next cluster resource update contains no eds_service_name. The test 768 // verifies that a subsequent EDS request is sent for the cluster_name and 769 // that the previously received eds_service_name is no longer requested. It 770 // also verifies that a subsequent RPC gets routed to a backend belonging to 771 // the service represented by the cluster_name. 772 // - The next cluster resource update changes the circuit breaking 773 // configuration, but does not change the service name. The test verifies 774 // that a subsequent RPC gets routed to the same backend as before. 775 func (s) TestEDS_ClusterResourceUpdates(t *testing.T) { 776 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 777 defer cancel() 778 779 // Start an xDS management server that pushes the EDS resource names onto a 780 // channel. 781 edsResourceNameCh := make(chan []string, 1) 782 managementServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{ 783 OnStreamRequest: func(_ int64, req *v3discoverypb.DiscoveryRequest) error { 784 if req.GetTypeUrl() != version.V3EndpointsURL { 785 return nil 786 } 787 if len(req.GetResourceNames()) == 0 { 788 // This is the case for ACKs. Do nothing here. 789 return nil 790 } 791 select { 792 case <-ctx.Done(): 793 case edsResourceNameCh <- req.GetResourceNames(): 794 } 795 return nil 796 }, 797 AllowResourceSubset: true, 798 }) 799 800 // Create bootstrap configuration pointing to the above management server. 801 nodeID := uuid.New().String() 802 bootstrapContents := e2e.DefaultBootstrapContents(t, nodeID, managementServer.Address) 803 804 // Start two test backends and extract their host and port. The first 805 // backend is used for the EDS resource identified by the eds_service_name, 806 // and the second backend is used for the EDS resource identified by the 807 // cluster_name. 808 servers, cleanup2 := startTestServiceBackends(t, 2) 809 defer cleanup2() 810 addrs, ports := backendAddressesAndPorts(t, servers) 811 812 // Configure cluster and endpoints resources in the management server. 813 resources := e2e.UpdateOptions{ 814 NodeID: nodeID, 815 Clusters: []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, edsServiceName, e2e.SecurityLevelNone)}, 816 Endpoints: []*v3endpointpb.ClusterLoadAssignment{ 817 e2e.DefaultEndpoint(edsServiceName, "localhost", []uint32{uint32(ports[0])}), 818 e2e.DefaultEndpoint(clusterName, "localhost", []uint32{uint32(ports[1])}), 819 }, 820 SkipValidation: true, 821 } 822 if err := managementServer.Update(ctx, resources); err != nil { 823 t.Fatal(err) 824 } 825 826 // Create xDS client, configure cds_experimental LB policy with a manual 827 // resolver, and dial the test backends. 828 cc, cleanup := setupAndDial(t, bootstrapContents) 829 defer cleanup() 830 831 client := testgrpc.NewTestServiceClient(cc) 832 peer := &peer.Peer{} 833 if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.Peer(peer)); err != nil { 834 t.Fatalf("EmptyCall() failed: %v", err) 835 } 836 if peer.Addr.String() != addrs[0].Addr { 837 t.Fatalf("EmptyCall() routed to backend %q, want %q", peer.Addr, addrs[0].Addr) 838 } 839 840 // Ensure EDS watch is registered for eds_service_name. 841 select { 842 case <-ctx.Done(): 843 t.Fatal("Timeout when waiting for EDS request to be received on the management server") 844 case names := <-edsResourceNameCh: 845 if !cmp.Equal(names, []string{edsServiceName}) { 846 t.Fatalf("Received EDS request with resource names %v, want %v", names, []string{edsServiceName}) 847 } 848 } 849 850 // Change the cluster resource to not contain an eds_service_name. 851 resources.Clusters = []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, "", e2e.SecurityLevelNone)} 852 if err := managementServer.Update(ctx, resources); err != nil { 853 t.Fatal(err) 854 } 855 856 // Ensure that an EDS watch for eds_service_name is canceled and new watch 857 // for cluster_name is registered. The actual order in which this happens is 858 // not deterministic, i.e the watch for old resource could be canceled 859 // before the new one is registered or vice-versa. In either case, 860 // eventually, we want to see a request to the management server for just 861 // the cluster_name. 862 for ; ctx.Err() == nil; <-time.After(defaultTestShortTimeout) { 863 names := <-edsResourceNameCh 864 if cmp.Equal(names, []string{clusterName}) { 865 break 866 } 867 } 868 if ctx.Err() != nil { 869 t.Fatalf("Timeout when waiting for old EDS watch %q to be canceled and new one %q to be registered", edsServiceName, clusterName) 870 } 871 872 // Make an RPC, and ensure that it gets routed to second backend, 873 // corresponding to the cluster_name. 874 for ; ctx.Err() == nil; <-time.After(defaultTestShortTimeout) { 875 if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.Peer(peer)); err != nil { 876 continue 877 } 878 if peer.Addr.String() == addrs[1].Addr { 879 break 880 } 881 } 882 if ctx.Err() != nil { 883 t.Fatalf("Timeout when waiting for EmptyCall() to be routed to correct backend %q", addrs[1].Addr) 884 } 885 886 // Change cluster resource circuit breaking count. 887 resources.Clusters[0].CircuitBreakers = &v3clusterpb.CircuitBreakers{ 888 Thresholds: []*v3clusterpb.CircuitBreakers_Thresholds{ 889 { 890 Priority: v3corepb.RoutingPriority_DEFAULT, 891 MaxRequests: wrapperspb.UInt32(512), 892 }, 893 }, 894 } 895 if err := managementServer.Update(ctx, resources); err != nil { 896 t.Fatal(err) 897 } 898 899 // Ensure that RPCs continue to get routed to the second backend for the 900 // next second. 901 for end := time.Now().Add(time.Second); time.Now().Before(end); <-time.After(defaultTestShortTimeout) { 902 if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.Peer(peer)); err != nil { 903 t.Fatalf("EmptyCall() failed: %v", err) 904 } 905 if peer.Addr.String() != addrs[1].Addr { 906 t.Fatalf("EmptyCall() routed to backend %q, want %q", peer.Addr, addrs[1].Addr) 907 } 908 } 909 } 910 911 // TestEDS_BadUpdateWithoutPreviousGoodUpdate tests the case where the 912 // management server sends a bad update (one that is NACKed by the xDS client). 913 // Since the cluster_resolver LB policy does not have a previously received good 914 // update, it is expected to treat this bad update as though it received an 915 // update with no endpoints. Hence RPCs are expected to fail with "all 916 // priorities removed" error. 917 func (s) TestEDS_BadUpdateWithoutPreviousGoodUpdate(t *testing.T) { 918 // Spin up a management server to receive xDS resources from. 919 managementServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 920 921 // Create bootstrap configuration pointing to the above management server. 922 nodeID := uuid.New().String() 923 bootstrapContents := e2e.DefaultBootstrapContents(t, nodeID, managementServer.Address) 924 925 // Start a backend server that implements the TestService. 926 server := stubserver.StartTestService(t, nil) 927 defer server.Stop() 928 929 // Create an EDS resource with a load balancing weight of 0. This will 930 // result in the resource being NACKed by the xDS client. Since the 931 // cluster_resolver LB policy does not have a previously received good EDS 932 // update, it should treat this update as an empty EDS update. 933 resources := clientEndpointsResource(nodeID, edsServiceName, []e2e.LocalityOptions{{ 934 Name: localityName1, 935 Weight: 1, 936 Backends: []e2e.BackendOptions{{Ports: []uint32{testutils.ParsePort(t, server.Address)}}}, 937 }}) 938 resources.Endpoints[0].Endpoints[0].LbEndpoints[0].LoadBalancingWeight = &wrapperspb.UInt32Value{Value: 0} 939 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 940 defer cancel() 941 if err := managementServer.Update(ctx, resources); err != nil { 942 t.Fatal(err) 943 } 944 945 // Create an xDS client for use by the cluster_resolver LB policy. 946 config, err := bootstrap.NewConfigFromContents(bootstrapContents) 947 if err != nil { 948 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bootstrapContents), err) 949 } 950 pool := xdsclient.NewPool(config) 951 xdsClient, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 952 Name: t.Name(), 953 }) 954 if err != nil { 955 t.Fatalf("Failed to create xDS client: %v", err) 956 } 957 defer close() 958 959 // Create a manual resolver and push a service config specifying the use of 960 // the cluster_resolver LB policy with a single discovery mechanism. 961 r := manual.NewBuilderWithScheme("whatever") 962 jsonSC := fmt.Sprintf(`{ 963 "loadBalancingConfig":[{ 964 "cluster_resolver_experimental":{ 965 "discoveryMechanisms": [{ 966 "cluster": "%s", 967 "type": "EDS", 968 "edsServiceName": "%s", 969 "outlierDetection": {} 970 }], 971 "xdsLbPolicy":[{"round_robin":{}}] 972 } 973 }] 974 }`, clusterName, edsServiceName) 975 scpr := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(jsonSC) 976 r.InitialState(xdsclient.SetClient(resolver.State{ServiceConfig: scpr}, xdsClient)) 977 978 // Create a ClientConn and verify that RPCs fail with "all priorities 979 // removed" error. 980 cc, err := grpc.NewClient(r.Scheme()+":///test.service", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(r)) 981 if err != nil { 982 t.Fatalf("failed to create new client for local test server: %v", err) 983 } 984 defer cc.Close() 985 client := testgrpc.NewTestServiceClient(cc) 986 if err := waitForProducedZeroAddressesError(ctx, t, client); err != nil { 987 t.Fatal(err) 988 } 989 } 990 991 // TestEDS_BadUpdateWithPreviousGoodUpdate tests the case where the 992 // cluster_resolver LB policy receives a good EDS update from the management 993 // server and the test verifies that RPCs are successful. Then, a bad update is 994 // received from the management server (one that is NACKed by the xDS client). 995 // The test verifies that the previously received good update is still being 996 // used and that RPCs are still successful. 997 func (s) TestEDS_BadUpdateWithPreviousGoodUpdate(t *testing.T) { 998 // Spin up a management server to receive xDS resources from. 999 managementServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 1000 1001 // Create bootstrap configuration pointing to the above management server. 1002 nodeID := uuid.New().String() 1003 bootstrapContents := e2e.DefaultBootstrapContents(t, nodeID, managementServer.Address) 1004 1005 // Start a backend server that implements the TestService. 1006 server := stubserver.StartTestService(t, nil) 1007 defer server.Stop() 1008 1009 // Create an EDS resource for consumption by the test. 1010 resources := clientEndpointsResource(nodeID, edsServiceName, []e2e.LocalityOptions{{ 1011 Name: localityName1, 1012 Weight: 1, 1013 Backends: []e2e.BackendOptions{{Ports: []uint32{testutils.ParsePort(t, server.Address)}}}, 1014 }}) 1015 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 1016 defer cancel() 1017 if err := managementServer.Update(ctx, resources); err != nil { 1018 t.Fatal(err) 1019 } 1020 1021 // Create an xDS client for use by the cluster_resolver LB policy. 1022 config, err := bootstrap.NewConfigFromContents(bootstrapContents) 1023 if err != nil { 1024 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bootstrapContents), err) 1025 } 1026 pool := xdsclient.NewPool(config) 1027 xdsClient, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 1028 Name: t.Name(), 1029 }) 1030 if err != nil { 1031 t.Fatalf("Failed to create xDS client: %v", err) 1032 } 1033 defer close() 1034 1035 // Create a manual resolver and push a service config specifying the use of 1036 // the cluster_resolver LB policy with a single discovery mechanism. 1037 r := manual.NewBuilderWithScheme("whatever") 1038 jsonSC := fmt.Sprintf(`{ 1039 "loadBalancingConfig":[{ 1040 "cluster_resolver_experimental":{ 1041 "discoveryMechanisms": [{ 1042 "cluster": "%s", 1043 "type": "EDS", 1044 "edsServiceName": "%s", 1045 "outlierDetection": {} 1046 }], 1047 "xdsLbPolicy":[{"round_robin":{}}] 1048 } 1049 }] 1050 }`, clusterName, edsServiceName) 1051 scpr := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(jsonSC) 1052 r.InitialState(xdsclient.SetClient(resolver.State{ServiceConfig: scpr}, xdsClient)) 1053 1054 // Create a ClientConn and make a successful RPC. 1055 cc, err := grpc.NewClient(r.Scheme()+":///test.service", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(r)) 1056 if err != nil { 1057 t.Fatalf("failed to create new client for local test server: %v", err) 1058 } 1059 defer cc.Close() 1060 1061 // Ensure RPCs are being roundrobined across the single backend. 1062 client := testgrpc.NewTestServiceClient(cc) 1063 if err := rrutil.CheckRoundRobinRPCs(ctx, client, []resolver.Address{{Addr: server.Address}}); err != nil { 1064 t.Fatal(err) 1065 } 1066 1067 // Update the endpoints resource in the management server with a load 1068 // balancing weight of 0. This will result in the resource being NACKed by 1069 // the xDS client. But since the cluster_resolver LB policy has a previously 1070 // received good EDS update, it should continue using it. 1071 resources.Endpoints[0].Endpoints[0].LbEndpoints[0].LoadBalancingWeight = &wrapperspb.UInt32Value{Value: 0} 1072 if err := managementServer.Update(ctx, resources); err != nil { 1073 t.Fatal(err) 1074 } 1075 1076 // Ensure that RPCs continue to succeed for the next second. 1077 for end := time.Now().Add(time.Second); time.Now().Before(end); <-time.After(defaultTestShortTimeout) { 1078 if err := rrutil.CheckRoundRobinRPCs(ctx, client, []resolver.Address{{Addr: server.Address}}); err != nil { 1079 t.Fatal(err) 1080 } 1081 } 1082 } 1083 1084 // TestEDS_ResourceNotFound tests the case where the requested EDS resource does 1085 // not exist on the management server. Once the watch timer associated with the 1086 // requested resource expires, the cluster_resolver LB policy receives a 1087 // "resource-not-found" callback from the xDS client and is expected to treat it 1088 // as though it received an update with no endpoints. Hence RPCs are expected to 1089 // fail with "all priorities removed" error. 1090 func (s) TestEDS_ResourceNotFound(t *testing.T) { 1091 // Spin up a management server to receive xDS resources from. 1092 mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 1093 1094 // Create an xDS client talking to the above management server, configured 1095 // with a short watch expiry timeout. 1096 nodeID := uuid.New().String() 1097 bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address) 1098 config, err := bootstrap.NewConfigFromContents(bc) 1099 if err != nil { 1100 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bc), err) 1101 } 1102 pool := xdsclient.NewPool(config) 1103 xdsClient, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 1104 Name: t.Name(), 1105 WatchExpiryTimeout: defaultTestWatchExpiryTimeout, 1106 }) 1107 if err != nil { 1108 t.Fatalf("Failed to create an xDS client: %v", err) 1109 } 1110 defer close() 1111 1112 // Configure no resources on the management server. 1113 resources := e2e.UpdateOptions{NodeID: nodeID, SkipValidation: true} 1114 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 1115 defer cancel() 1116 if err := mgmtServer.Update(ctx, resources); err != nil { 1117 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 1118 } 1119 1120 // Create a manual resolver and push a service config specifying the use of 1121 // the cluster_resolver LB policy with a single discovery mechanism. 1122 r := manual.NewBuilderWithScheme("whatever") 1123 jsonSC := fmt.Sprintf(`{ 1124 "loadBalancingConfig":[{ 1125 "cluster_resolver_experimental":{ 1126 "discoveryMechanisms": [{ 1127 "cluster": "%s", 1128 "type": "EDS", 1129 "edsServiceName": "%s", 1130 "outlierDetection": {} 1131 }], 1132 "xdsLbPolicy":[{"round_robin":{}}] 1133 } 1134 }] 1135 }`, clusterName, edsServiceName) 1136 scpr := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(jsonSC) 1137 r.InitialState(xdsclient.SetClient(resolver.State{ServiceConfig: scpr}, xdsClient)) 1138 1139 // Create a ClientConn and verify that RPCs fail with "all priorities 1140 // removed" error. 1141 cc, err := grpc.NewClient(r.Scheme()+":///test.service", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(r)) 1142 if err != nil { 1143 t.Fatalf("failed to create new client for local test server: %v", err) 1144 } 1145 defer cc.Close() 1146 client := testgrpc.NewTestServiceClient(cc) 1147 if err := waitForProducedZeroAddressesError(ctx, t, client); err != nil { 1148 t.Fatal(err) 1149 } 1150 } 1151 1152 // waitForAllPrioritiesRemovedError repeatedly makes RPCs using the 1153 // TestServiceClient until they fail with an error which indicates that no 1154 // resolver addresses have been produced. A non-nil error is returned if the 1155 // context expires before RPCs fail with the expected error. 1156 func waitForProducedZeroAddressesError(ctx context.Context, t *testing.T, client testgrpc.TestServiceClient) error { 1157 for ; ctx.Err() == nil; <-time.After(time.Millisecond) { 1158 _, err := client.EmptyCall(ctx, &testpb.Empty{}) 1159 if err == nil { 1160 t.Log("EmptyCall() succeeded after error in Discovery Mechanism") 1161 continue 1162 } 1163 if code := status.Code(err); code != codes.Unavailable { 1164 t.Logf("EmptyCall() returned code: %v, want: %v", code, codes.Unavailable) 1165 continue 1166 } 1167 if !strings.Contains(err.Error(), "no children to pick from") { 1168 t.Logf("EmptyCall() = %v, want %v", err, "no children to pick from") 1169 continue 1170 } 1171 return nil 1172 } 1173 return errors.New("timeout when waiting for RPCs to fail with UNAVAILABLE status and produced zero addresses") 1174 } 1175 1176 // Test runs a server which listens on multiple ports. The test updates xds resouce 1177 // cache to contain a single endpoint with multiple addresses. The test intercepts 1178 // the resolver updates sent to the petiole policy and verifies that the 1179 // additional endpoint addresses are correctly propagated. 1180 func (s) TestEDS_EndpointWithMultipleAddresses(t *testing.T) { 1181 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 1182 defer cancel() 1183 1184 // Start a backend server which listens to multiple ports to simulate a 1185 // backend with multiple addresses. 1186 server := &stubserver.StubServer{ 1187 EmptyCallF: func(context.Context, *testpb.Empty) (*testpb.Empty, error) { return &testpb.Empty{}, nil }, 1188 UnaryCallF: func(context.Context, *testpb.SimpleRequest) (*testpb.SimpleResponse, error) { 1189 return &testpb.SimpleResponse{}, nil 1190 }, 1191 } 1192 lis1, err := testutils.LocalTCPListener() 1193 if err != nil { 1194 t.Fatalf("Failed to create listener: %v", err) 1195 } 1196 defer lis1.Close() 1197 lis2, err := testutils.LocalTCPListener() 1198 if err != nil { 1199 t.Fatalf("Failed to create listener: %v", err) 1200 } 1201 defer lis2.Close() 1202 lis3, err := testutils.LocalTCPListener() 1203 if err != nil { 1204 t.Fatalf("Failed to create listener: %v", err) 1205 } 1206 defer lis3.Close() 1207 1208 server.Listener = lis1 1209 if err := server.StartServer(); err != nil { 1210 t.Fatalf("Failed to start stub server: %v", err) 1211 } 1212 go server.S.Serve(lis2) 1213 go server.S.Serve(lis3) 1214 1215 t.Logf("Started test service backend at addresses %q, %q, %q", lis1.Addr(), lis2.Addr(), lis3.Addr()) 1216 1217 ports := []uint32{ 1218 testutils.ParsePort(t, lis1.Addr().String()), 1219 testutils.ParsePort(t, lis2.Addr().String()), 1220 testutils.ParsePort(t, lis3.Addr().String()), 1221 } 1222 1223 testCases := []struct { 1224 name string 1225 dualstackEndpointsEnabled bool 1226 wantEndpointPorts []uint32 1227 wantAddrPorts []uint32 1228 }{ 1229 { 1230 name: "flag_enabled", 1231 dualstackEndpointsEnabled: true, 1232 wantEndpointPorts: ports, 1233 wantAddrPorts: ports[:1], 1234 }, 1235 { 1236 name: "flag_disabled", 1237 wantEndpointPorts: ports[:1], 1238 wantAddrPorts: ports[:1], 1239 }, 1240 } 1241 1242 for _, tc := range testCases { 1243 t.Run(tc.name, func(t *testing.T) { 1244 origDualstackEndpointsEnabled := envconfig.XDSDualstackEndpointsEnabled 1245 defer func() { 1246 envconfig.XDSDualstackEndpointsEnabled = origDualstackEndpointsEnabled 1247 }() 1248 envconfig.XDSDualstackEndpointsEnabled = tc.dualstackEndpointsEnabled 1249 1250 // Wrap the round robin balancer to intercept resolver updates. 1251 originalRRBuilder := balancer.Get(roundrobin.Name) 1252 defer func() { 1253 balancer.Register(originalRRBuilder) 1254 }() 1255 resolverState := atomic.Pointer[resolver.State]{} 1256 resolverState.Store(&resolver.State{}) 1257 stub.Register(roundrobin.Name, stub.BalancerFuncs{ 1258 Init: func(bd *stub.BalancerData) { 1259 bd.Data = originalRRBuilder.Build(bd.ClientConn, bd.BuildOptions) 1260 }, 1261 Close: func(bd *stub.BalancerData) { 1262 bd.Data.(balancer.Balancer).Close() 1263 }, 1264 UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { 1265 resolverState.Store(&ccs.ResolverState) 1266 return bd.Data.(balancer.Balancer).UpdateClientConnState(ccs) 1267 }, 1268 }) 1269 1270 // Spin up a management server to receive xDS resources from. 1271 mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 1272 1273 // Create bootstrap configuration pointing to the above management server. 1274 nodeID := uuid.New().String() 1275 bootstrapContents := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address) 1276 config, err := bootstrap.NewConfigFromContents(bootstrapContents) 1277 if err != nil { 1278 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bootstrapContents), err) 1279 } 1280 pool := xdsclient.NewPool(config) 1281 1282 // Create xDS resources for consumption by the test. We start off with a 1283 // single backend in a single EDS locality. 1284 resources := clientEndpointsResource(nodeID, edsServiceName, []e2e.LocalityOptions{{ 1285 Name: localityName1, 1286 Weight: 1, 1287 Backends: []e2e.BackendOptions{{ 1288 Ports: ports, 1289 }}, 1290 }}) 1291 if err := mgmtServer.Update(ctx, resources); err != nil { 1292 t.Fatal(err) 1293 } 1294 1295 // Create an xDS client talking to the above management server, configured 1296 // with a short watch expiry timeout. 1297 xdsClient, close, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 1298 Name: t.Name(), 1299 }) 1300 if err != nil { 1301 t.Fatalf("Failed to create an xDS client: %v", err) 1302 } 1303 defer close() 1304 1305 // Create a manual resolver and push a service config specifying the use of 1306 // the cluster_resolver LB policy with a single discovery mechanism. 1307 r := manual.NewBuilderWithScheme("whatever") 1308 jsonSC := fmt.Sprintf(`{ 1309 "loadBalancingConfig":[{ 1310 "cluster_resolver_experimental":{ 1311 "discoveryMechanisms": [{ 1312 "cluster": "%s", 1313 "type": "EDS", 1314 "edsServiceName": "%s", 1315 "outlierDetection": {} 1316 }], 1317 "xdsLbPolicy":[{"round_robin":{}}] 1318 } 1319 }] 1320 }`, clusterName, edsServiceName) 1321 scpr := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(jsonSC) 1322 r.InitialState(xdsclient.SetClient(resolver.State{ServiceConfig: scpr}, xdsClient)) 1323 1324 cc, err := grpc.NewClient(r.Scheme()+":///test.service", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(r)) 1325 if err != nil { 1326 t.Fatalf("failed to create new client for local test server: %v", err) 1327 } 1328 defer cc.Close() 1329 client := testgrpc.NewTestServiceClient(cc) 1330 if err := rrutil.CheckRoundRobinRPCs(ctx, client, []resolver.Address{{Addr: lis1.Addr().String()}}); err != nil { 1331 t.Fatal(err) 1332 } 1333 1334 gotState := resolverState.Load() 1335 1336 gotEndpointPorts := []uint32{} 1337 for _, a := range gotState.Endpoints[0].Addresses { 1338 gotEndpointPorts = append(gotEndpointPorts, testutils.ParsePort(t, a.Addr)) 1339 } 1340 if diff := cmp.Diff(gotEndpointPorts, tc.wantEndpointPorts); diff != "" { 1341 t.Errorf("Unexpected endpoint address ports in resolver update, diff (-got +want): %v", diff) 1342 } 1343 1344 gotAddrPorts := []uint32{} 1345 for _, a := range gotState.Addresses { 1346 gotAddrPorts = append(gotAddrPorts, testutils.ParsePort(t, a.Addr)) 1347 } 1348 if diff := cmp.Diff(gotAddrPorts, tc.wantAddrPorts); diff != "" { 1349 t.Errorf("Unexpected address ports in resolver update, diff (-got +want): %v", diff) 1350 } 1351 }) 1352 } 1353 }