From ac9ad67f0af515a11f150dc615e721acee5b47b9 Mon Sep 17 00:00:00 2001 From: Michael Lumish Date: Thu, 7 Nov 2024 11:32:28 -0800 Subject: [PATCH 1/2] grpc-js-xds: xds_cluster_manager: pass along updates to existing children --- .../src/load-balancer-xds-cluster-manager.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/grpc-js-xds/src/load-balancer-xds-cluster-manager.ts b/packages/grpc-js-xds/src/load-balancer-xds-cluster-manager.ts index b8d4a7dac..90e8756f9 100644 --- a/packages/grpc-js-xds/src/load-balancer-xds-cluster-manager.ts +++ b/packages/grpc-js-xds/src/load-balancer-xds-cluster-manager.ts @@ -227,13 +227,14 @@ class XdsClusterManager implements LoadBalancer { this.children.get(name)!.destroy(); this.children.delete(name); } - // Add new children that were not in the previous config + // Update all children, and add any new ones for (const [name, childConfig] of configChildren.entries()) { - if (!this.children.has(name)) { - const newChild = new this.XdsClusterManagerChildImpl(this, name); - newChild.updateAddressList(endpointList, childConfig, attributes); - this.children.set(name, newChild); + let child = this.children.get(name); + if (!child) { + child = new this.XdsClusterManagerChildImpl(this, name); + this.children.set(name, child); } + child.updateAddressList(endpointList, childConfig, attributes); } this.updatesPaused = false; this.updateState(); From ec9b3cfbb0d485e8a78bfe06b4341fca7f32122b Mon Sep 17 00:00:00 2001 From: Michael Lumish Date: Thu, 7 Nov 2024 12:03:04 -0800 Subject: [PATCH 2/2] Add a test --- packages/grpc-js-xds/test/framework.ts | 4 ++-- packages/grpc-js-xds/test/test-core.ts | 31 ++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/packages/grpc-js-xds/test/framework.ts b/packages/grpc-js-xds/test/framework.ts index 44154afa3..87973265b 100644 --- a/packages/grpc-js-xds/test/framework.ts +++ b/packages/grpc-js-xds/test/framework.ts @@ -386,7 +386,7 @@ export class FakeServerRoute { private listener: Listener; private routeConfiguration: RouteConfiguration; constructor(port: number, routeName: string, baseListener?: Listener | undefined, baseRouteConfiguration?: RouteConfiguration) { - this.listener = baseListener ?? DEFAULT_BASE_SERVER_LISTENER; + this.listener = baseListener ?? {...DEFAULT_BASE_SERVER_LISTENER}; this.listener.name = `[::1]:${port}`; this.listener.address = { socket_address: { @@ -414,7 +414,7 @@ export class FakeServerRoute { filterChain.filters = filterList; } - this.routeConfiguration = baseRouteConfiguration ?? DEFAULT_BASE_SERVER_ROUTE_CONFIG; + this.routeConfiguration = baseRouteConfiguration ?? {...DEFAULT_BASE_SERVER_ROUTE_CONFIG}; this.routeConfiguration.name = routeName; } diff --git a/packages/grpc-js-xds/test/test-core.ts b/packages/grpc-js-xds/test/test-core.ts index 45a96d683..267524bc6 100644 --- a/packages/grpc-js-xds/test/test-core.ts +++ b/packages/grpc-js-xds/test/test-core.ts @@ -134,5 +134,36 @@ describe('core xDS functionality', () => { }); }, reason => done(reason)); }, reason => done(reason)); + }); + it('should handle cluster config changes', async () => { + const [backend1, backend2] = await createBackends(2); + const serverRoute1 = new FakeServerRoute(backend1.getPort(), 'serverRoute'); + const serverRoute2 = new FakeServerRoute(backend2.getPort(), 'serverRoute2'); + xdsServer.setRdsResource(serverRoute1.getRouteConfiguration()); + xdsServer.setLdsResource(serverRoute1.getListener()); + xdsServer.setRdsResource(serverRoute2.getRouteConfiguration()); + xdsServer.setLdsResource(serverRoute2.getListener()); + xdsServer.addResponseListener((typeUrl, responseState) => { + if (responseState.state === 'NACKED') { + client?.stopCalls(); + assert.fail(`Client NACKED ${typeUrl} resource with message ${responseState.errorMessage}`); + } + }); + const cluster1 = new FakeEdsCluster('cluster1', 'endpoint1', [{backends: [backend1], locality:{region: 'region1'}}]); + const routeGroup1 = new FakeRouteGroup('listener1', 'route1', [{cluster: cluster1}]); + await routeGroup1.startAllBackends(xdsServer); + xdsServer.setEdsResource(cluster1.getEndpointConfig()); + xdsServer.setCdsResource(cluster1.getClusterConfig()); + xdsServer.setRdsResource(routeGroup1.getRouteConfiguration()); + xdsServer.setLdsResource(routeGroup1.getListener()); + client = XdsTestClient.createFromServer('listener1', xdsServer); + client.startCalls(100); + await cluster1.waitForAllBackendsToReceiveTraffic(); + const cluster2 = new FakeEdsCluster('cluster1', 'endpoint1', [{backends: [backend2], locality:{region: 'region2'}}]); + await cluster2.startAllBackends(xdsServer); + xdsServer.setEdsResource(cluster2.getEndpointConfig()); + await cluster2.waitForAllBackendsToReceiveTraffic(); + client.stopCalls(); + }) });