Skip to content

Commit 29711cd

Browse files
committed
grpc-js-xds: CSDS: add tracing and fix bugs
1 parent adc25c2 commit 29711cd

File tree

3 files changed

+31
-7
lines changed

3 files changed

+31
-7
lines changed

packages/grpc-js-xds/scripts/xds.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ git clone -b master --single-branch --depth=1 https://github.com/grpc/grpc.git
4848

4949
grpc/tools/run_tests/helper_scripts/prep_xds.sh
5050

51-
GRPC_NODE_TRACE=xds_client,xds_resolver,cds_balancer,eds_balancer,priority,weighted_target,round_robin,resolving_load_balancer,subchannel,keepalive,dns_resolver,fault_injection,http_filter \
51+
GRPC_NODE_TRACE=xds_client,xds_resolver,cds_balancer,eds_balancer,priority,weighted_target,round_robin,resolving_load_balancer,subchannel,keepalive,dns_resolver,fault_injection,http_filter,csds \
5252
GRPC_NODE_VERBOSITY=DEBUG \
5353
NODE_XDS_INTEROP_VERBOSITY=1 \
5454
python3 grpc/tools/run_tests/run_xds_tests.py \

packages/grpc-js-xds/src/csds.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,18 @@ import { ClientStatusResponse } from "./generated/envoy/service/status/v3/Client
2323
import { Timestamp } from "./generated/google/protobuf/Timestamp";
2424
import { AdsTypeUrl, CDS_TYPE_URL_V2, CDS_TYPE_URL_V3, EDS_TYPE_URL_V2, EDS_TYPE_URL_V3, LDS_TYPE_URL_V2, LDS_TYPE_URL_V3, RDS_TYPE_URL_V2, RDS_TYPE_URL_V3 } from "./resources";
2525
import { HandleResponseResult } from "./xds-stream-state/xds-stream-state";
26-
import { sendUnaryData, ServerDuplexStream, ServerUnaryCall, status, experimental, loadPackageDefinition } from '@grpc/grpc-js';
26+
import { sendUnaryData, ServerDuplexStream, ServerUnaryCall, status, experimental, loadPackageDefinition, logVerbosity } from '@grpc/grpc-js';
2727
import { loadSync } from "@grpc/proto-loader";
2828
import { ProtoGrpcType as CsdsProtoGrpcType } from "./generated/csds";
2929

3030
import registerAdminService = experimental.registerAdminService;
3131

32+
const TRACER_NAME = 'csds';
33+
34+
function trace(text: string): void {
35+
experimental.trace(logVerbosity.DEBUG, TRACER_NAME, text);
36+
}
37+
3238

3339
function dateToProtoTimestamp(date?: Date | null): Timestamp | null {
3440
if (!date) {
@@ -72,7 +78,8 @@ export function setCsdsClientNode(node: Node) {
7278
* @param typeUrl The resource type URL
7379
* @param names The list of resource names that are being requested
7480
*/
75-
export function updateRequestedNameList(typeUrl: AdsTypeUrl, names: string[]) {
81+
export function updateCsdsRequestedNameList(typeUrl: AdsTypeUrl, names: string[]) {
82+
trace('Update type URL ' + typeUrl + ' with names [' + names + ']');
7683
const currentTime = dateToProtoTimestamp(new Date());
7784
const configMap = configStatus[typeUrl];
7885
for (const name of names) {
@@ -100,12 +107,13 @@ export function updateRequestedNameList(typeUrl: AdsTypeUrl, names: string[]) {
100107
* @param versionInfo The version info field from this response
101108
* @param updates The lists of resources that passed and failed validation
102109
*/
103-
export function updateResourceResponse(typeUrl: AdsTypeUrl, versionInfo: string, updates: HandleResponseResult) {
110+
export function updateCsdsResourceResponse(typeUrl: AdsTypeUrl, versionInfo: string, updates: HandleResponseResult) {
104111
const currentTime = dateToProtoTimestamp(new Date());
105112
const configMap = configStatus[typeUrl];
106113
for (const {name, raw} of updates.accepted) {
107114
const mapEntry = configMap.get(name);
108115
if (mapEntry) {
116+
trace('Updated ' + typeUrl + ' resource ' + name + ' to state ACKED');
109117
mapEntry.client_status = 'ACKED';
110118
mapEntry.version_info = versionInfo;
111119
mapEntry.xds_config = raw;
@@ -116,6 +124,7 @@ export function updateResourceResponse(typeUrl: AdsTypeUrl, versionInfo: string,
116124
for (const {name, error, raw} of updates.rejected) {
117125
const mapEntry = configMap.get(name);
118126
if (mapEntry) {
127+
trace('Updated ' + typeUrl + ' resource ' + name + ' to state NACKED');
119128
mapEntry.client_status = 'NACKED';
120129
mapEntry.error_state = {
121130
failed_configuration: raw,
@@ -128,6 +137,7 @@ export function updateResourceResponse(typeUrl: AdsTypeUrl, versionInfo: string,
128137
for (const name of updates.missing) {
129138
const mapEntry = configMap.get(name);
130139
if (mapEntry) {
140+
trace('Updated ' + typeUrl + ' resource ' + name + ' to state DOES_NOT_EXIST');
131141
mapEntry.client_status = 'DOES_NOT_EXIST';
132142
mapEntry.version_info = versionInfo;
133143
mapEntry.xds_config = null;
@@ -144,16 +154,18 @@ function getCurrentConfig(): ClientConfig {
144154
genericConfigList.push(configValue);
145155
}
146156
}
147-
return {
157+
const config = {
148158
node: clientNode,
149159
generic_xds_configs: genericConfigList
150160
};
161+
trace('Sending curent config ' + JSON.stringify(config, undefined, 2));
162+
return config;
151163
}
152164

153165
const csdsImplementation: ClientStatusDiscoveryServiceHandlers = {
154166
FetchClientStatus(call: ServerUnaryCall<ClientStatusRequest__Output, ClientStatusResponse>, callback: sendUnaryData<ClientStatusResponse>) {
155167
const request = call.request;
156-
if (request.node_matchers !== null) {
168+
if (request.node_matchers.length > 0) {
157169
callback({
158170
code: status.INVALID_ARGUMENT,
159171
details: 'Node matchers not supported'
@@ -166,7 +178,7 @@ const csdsImplementation: ClientStatusDiscoveryServiceHandlers = {
166178
},
167179
StreamClientStatus(call: ServerDuplexStream<ClientStatusRequest__Output, ClientStatusResponse>) {
168180
call.on('data', (request: ClientStatusRequest__Output) => {
169-
if (request.node_matchers !== null) {
181+
if (request.node_matchers.length > 0) {
170182
call.emit('error', {
171183
code: status.INVALID_ARGUMENT,
172184
details: 'Node matchers not supported'

packages/grpc-js-xds/src/xds-client.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import { Cluster__Output } from './generated/envoy/config/cluster/v3/Cluster';
5151
import { RouteConfiguration__Output } from './generated/envoy/config/route/v3/RouteConfiguration';
5252
import { Duration } from './generated/google/protobuf/Duration';
5353
import { AdsOutputType, AdsTypeUrl, CDS_TYPE_URL_V2, CDS_TYPE_URL_V3, decodeSingleResource, EDS_TYPE_URL_V2, EDS_TYPE_URL_V3, LDS_TYPE_URL_V2, LDS_TYPE_URL_V3, RDS_TYPE_URL_V2, RDS_TYPE_URL_V3 } from './resources';
54+
import { setCsdsClientNode, updateCsdsRequestedNameList, updateCsdsResourceResponse } from './csds';
5455

5556
const TRACER_NAME = 'xds_client';
5657

@@ -384,6 +385,7 @@ export class XdsClient {
384385
...nodeV3,
385386
client_features: ['envoy.lrs.supports_send_all_clusters'],
386387
};
388+
setCsdsClientNode(this.adsNodeV3);
387389
if (this.apiVersion === XdsApiVersion.V2) {
388390
trace('ADS Node: ' + JSON.stringify(this.adsNodeV2, undefined, 2));
389391
trace('LRS Node: ' + JSON.stringify(this.lrsNodeV2, undefined, 2));
@@ -514,12 +516,14 @@ export class XdsClient {
514516
} catch (e) {
515517
trace('Nacking message with protobuf parsing error: ' + e.message);
516518
this.nack(message.type_url, e.message);
519+
return;
517520
}
518521
if (handleResponseResult === null) {
519522
// Null handleResponseResult means that the type_url was unrecognized
520523
trace('Nacking message with unknown type URL ' + message.type_url);
521524
this.nack(message.type_url, `Unknown type_url ${message.type_url}`);
522525
} else {
526+
updateCsdsResourceResponse(message.type_url as AdsTypeUrl, message.version_info, handleResponseResult.result);
523527
if (handleResponseResult.result.rejected.length > 0) {
524528
// rejected.length > 0 means that at least one message validation failed
525529
const errorString = `${handleResponseResult.serviceKind.toUpperCase()} Error: ${handleResponseResult.result.rejected[0].error}`;
@@ -754,8 +758,16 @@ export class XdsClient {
754758
}
755759
this.maybeStartAdsStream();
756760
this.maybeStartLrsStream();
761+
if (!this.adsCallV2 && !this.adsCallV3) {
762+
/* If the stream is not set up yet at this point, shortcut the rest
763+
* becuase nothing will actually be sent. This would mainly happen if
764+
* the bootstrap file has not been read yet. In that case, the output
765+
* of getTypeUrl is garbage and everything after that is invalid. */
766+
return;
767+
}
757768
trace('Sending update for ' + serviceKind + ' with names ' + this.adsState[serviceKind].getResourceNames());
758769
const typeUrl = this.getTypeUrl(serviceKind);
770+
updateCsdsRequestedNameList(typeUrl, this.adsState[serviceKind].getResourceNames());
759771
this.maybeSendAdsMessage(typeUrl, this.adsState[serviceKind].getResourceNames(), this.adsState[serviceKind].nonce, this.adsState[serviceKind].versionInfo);
760772
}
761773

0 commit comments

Comments
 (0)