Skip to content

Commit

Permalink
grpc-js: Add support for grpc.service_config_disable_resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
kleinsch committed Dec 17, 2022
1 parent c9f8f93 commit b240ca1
Show file tree
Hide file tree
Showing 61 changed files with 2,897 additions and 1 deletion.
Binary file added packages/grpc-js-xds/grpc-grpc-js-xds-1.7.0.tgz
Binary file not shown.
Binary file added packages/grpc-js-xds/grpc-grpc-js-xds-1.8.0.tgz
Binary file not shown.
1 change: 1 addition & 0 deletions packages/grpc-js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Many channel arguments supported in `grpc` are not supported in `@grpc/grpc-js`.
- `grpc.enable_retries`
- `grpc.per_rpc_retry_buffer_size`
- `grpc.retry_buffer_size`
- `grpc.service_config_disable_resolution`
- `grpc-node.max_session_memory`
- `channelOverride`
- `channelFactoryOverride`
Expand Down
Binary file added packages/grpc-js/grpc-grpc-js-1.7.3.tgz
Binary file not shown.
Binary file added packages/grpc-js/grpc-grpc-js-1.8.0.tgz
Binary file not shown.
2 changes: 2 additions & 0 deletions packages/grpc-js/src/channel-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export interface ChannelOptions {
'grpc.max_connection_age_ms'?: number;
'grpc.max_connection_age_grace_ms'?: number;
'grpc-node.max_session_memory'?: number;
'grpc.service_config_disable_resolution'?: number;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[key: string]: any;
}
Expand Down Expand Up @@ -87,6 +88,7 @@ export const recognizedOptions = {
'grpc.max_connection_age_ms': true,
'grpc.max_connection_age_grace_ms': true,
'grpc-node.max_session_memory': true,
'grpc.service_config_disable_resolution': true,
};

export function channelOptionsEqual(
Expand Down
7 changes: 6 additions & 1 deletion packages/grpc-js/src/resolver-dns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class DnsResolver implements Resolver {
private continueResolving = false;
private nextResolutionTimer: NodeJS.Timer;
private isNextResolutionTimerRunning = false;
private isServiceConfigEnabled = true;
constructor(
private target: GrpcUri,
private listener: ResolverListener,
Expand Down Expand Up @@ -127,6 +128,10 @@ class DnsResolver implements Resolver {
}
this.percentage = Math.random() * 100;

if (channelOptions['grpc.service_config_disable_resolution'] === 1) {
this.isServiceConfigEnabled = false;
}

this.defaultResolutionError = {
code: Status.UNAVAILABLE,
details: `Name resolution failed for target ${uriToString(this.target)}`,
Expand Down Expand Up @@ -255,7 +260,7 @@ class DnsResolver implements Resolver {
);
/* If there already is a still-pending TXT resolution, we can just use
* that result when it comes in */
if (this.pendingTxtPromise === null) {
if (this.isServiceConfigEnabled && this.pendingTxtPromise === null) {
/* We handle the TXT query promise differently than the others because
* the name resolution attempt as a whole is a success even if the TXT
* lookup fails */
Expand Down
58 changes: 58 additions & 0 deletions packages/grpc-js/test/test-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,64 @@ describe('Name Resolver', () => {
const resolver = resolverManager.createResolver(target, listener, {});
resolver.updateResolution();
});
// Created DNS TXT record using TXT sample from https://github.com/grpc/proposal/blob/master/A2-service-configs-in-dns.md
// "grpc_config=[{\"serviceConfig\":{\"loadBalancingPolicy\":\"round_robin\",\"methodConfig\":[{\"name\":[{\"service\":\"MyService\",\"method\":\"Foo\"}],\"waitForReady\":true}]}}]"
it.skip('Should resolve a name with TXT service config', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('grpctest.kleinsch.com')!)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
serviceConfig: ServiceConfig | null,
serviceConfigError: StatusObject | null
) => {
if (serviceConfig !== null) {
assert(
serviceConfig.loadBalancingPolicy === 'round_robin',
'Should have found round robin LB policy'
);
done();
}
},
onError: (error: StatusObject) => {
done(new Error(`Failed with status ${error.details}`));
},
};
const resolver = resolverManager.createResolver(target, listener, {});
resolver.updateResolution();
});
it.skip(
'Should not resolve TXT service config if we disabled service config',
(done) => {
const target = resolverManager.mapUriDefaultScheme(
parseUri('grpctest.kleinsch.com')!
)!;
let count = 0;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
serviceConfig: ServiceConfig | null,
serviceConfigError: StatusObject | null
) => {
assert(
serviceConfig === null,
'Should not have found service config'
);
count++;
},
onError: (error: StatusObject) => {
done(new Error(`Failed with status ${error.details}`));
},
};
const resolver = resolverManager.createResolver(target, listener, {
'grpc.service_config_disable_resolution': 1,
});
resolver.updateResolution();
setTimeout(() => {
assert(count === 1, 'Should have only resolved once');
done();
}, 2_000);
}
);
/* The DNS entry for loopback4.unittest.grpc.io only has a single A record
* with the address 127.0.0.1, but the Mac DNS resolver appears to use
* NAT64 to create an IPv6 address in that case, so it instead returns
Expand Down
95 changes: 95 additions & 0 deletions packages/proto-loader/golden-generated-old/echo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import type * as grpc from '@grpc/grpc-js';
import type { EnumTypeDefinition, MessageTypeDefinition } from '@grpc/proto-loader';

import type { OperationsClient as _google_longrunning_OperationsClient, OperationsDefinition as _google_longrunning_OperationsDefinition } from './google/longrunning/Operations';
import type { EchoClient as _google_showcase_v1beta1_EchoClient, EchoDefinition as _google_showcase_v1beta1_EchoDefinition } from './google/showcase/v1beta1/Echo';

type SubtypeConstructor<Constructor extends new (...args: any) => any, Subtype> = {
new(...args: ConstructorParameters<Constructor>): Subtype;
};

export interface ProtoGrpcType {
google: {
api: {
CustomHttpPattern: MessageTypeDefinition
FieldBehavior: EnumTypeDefinition
Http: MessageTypeDefinition
HttpRule: MessageTypeDefinition
}
longrunning: {
CancelOperationRequest: MessageTypeDefinition
DeleteOperationRequest: MessageTypeDefinition
GetOperationRequest: MessageTypeDefinition
ListOperationsRequest: MessageTypeDefinition
ListOperationsResponse: MessageTypeDefinition
Operation: MessageTypeDefinition
OperationInfo: MessageTypeDefinition
/**
* Manages long-running operations with an API service.
*
* When an API method normally takes long time to complete, it can be designed
* to return [Operation][google.longrunning.Operation] to the client, and the client can use this
* interface to receive the real response asynchronously by polling the
* operation resource, or pass the operation resource to another API (such as
* Google Cloud Pub/Sub API) to receive the response. Any API service that
* returns long-running operations should implement the `Operations` interface
* so developers can have a consistent client experience.
*/
Operations: SubtypeConstructor<typeof grpc.Client, _google_longrunning_OperationsClient> & { service: _google_longrunning_OperationsDefinition }
WaitOperationRequest: MessageTypeDefinition
}
protobuf: {
Any: MessageTypeDefinition
DescriptorProto: MessageTypeDefinition
Duration: MessageTypeDefinition
Empty: MessageTypeDefinition
EnumDescriptorProto: MessageTypeDefinition
EnumOptions: MessageTypeDefinition
EnumValueDescriptorProto: MessageTypeDefinition
EnumValueOptions: MessageTypeDefinition
FieldDescriptorProto: MessageTypeDefinition
FieldOptions: MessageTypeDefinition
FileDescriptorProto: MessageTypeDefinition
FileDescriptorSet: MessageTypeDefinition
FileOptions: MessageTypeDefinition
GeneratedCodeInfo: MessageTypeDefinition
MessageOptions: MessageTypeDefinition
MethodDescriptorProto: MessageTypeDefinition
MethodOptions: MessageTypeDefinition
OneofDescriptorProto: MessageTypeDefinition
OneofOptions: MessageTypeDefinition
ServiceDescriptorProto: MessageTypeDefinition
ServiceOptions: MessageTypeDefinition
SourceCodeInfo: MessageTypeDefinition
Timestamp: MessageTypeDefinition
UninterpretedOption: MessageTypeDefinition
}
rpc: {
Status: MessageTypeDefinition
}
showcase: {
v1beta1: {
BlockRequest: MessageTypeDefinition
BlockResponse: MessageTypeDefinition
/**
* This service is used showcase the four main types of rpcs - unary, server
* side streaming, client side streaming, and bidirectional streaming. This
* service also exposes methods that explicitly implement server delay, and
* paginated calls. Set the 'showcase-trailer' metadata key on any method
* to have the values echoed in the response trailers.
*/
Echo: SubtypeConstructor<typeof grpc.Client, _google_showcase_v1beta1_EchoClient> & { service: _google_showcase_v1beta1_EchoDefinition }
EchoRequest: MessageTypeDefinition
EchoResponse: MessageTypeDefinition
ExpandRequest: MessageTypeDefinition
PagedExpandRequest: MessageTypeDefinition
PagedExpandResponse: MessageTypeDefinition
Severity: EnumTypeDefinition
WaitMetadata: MessageTypeDefinition
WaitRequest: MessageTypeDefinition
WaitResponse: MessageTypeDefinition
}
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Original file: deps/googleapis/google/api/http.proto


/**
* A custom pattern is used for defining custom HTTP verb.
*/
export interface ICustomHttpPattern {
/**
* The name of this custom HTTP verb.
*/
'kind'?: (string);
/**
* The path matched by this custom verb.
*/
'path'?: (string);
}

/**
* A custom pattern is used for defining custom HTTP verb.
*/
export interface OCustomHttpPattern {
/**
* The name of this custom HTTP verb.
*/
'kind': (string);
/**
* The path matched by this custom verb.
*/
'path': (string);
}
108 changes: 108 additions & 0 deletions packages/proto-loader/golden-generated-old/google/api/FieldBehavior.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Original file: deps/googleapis/google/api/field_behavior.proto

/**
* An indicator of the behavior of a given field (for example, that a field
* is required in requests, or given as output but ignored as input).
* This **does not** change the behavior in protocol buffers itself; it only
* denotes the behavior and may affect how API tooling handles the field.
*
* Note: This enum **may** receive new values in the future.
*/
export const FieldBehavior = {
/**
* Conventional default for enums. Do not use this.
*/
FIELD_BEHAVIOR_UNSPECIFIED: 'FIELD_BEHAVIOR_UNSPECIFIED',
/**
* Specifically denotes a field as optional.
* While all fields in protocol buffers are optional, this may be specified
* for emphasis if appropriate.
*/
OPTIONAL: 'OPTIONAL',
/**
* Denotes a field as required.
* This indicates that the field **must** be provided as part of the request,
* and failure to do so will cause an error (usually `INVALID_ARGUMENT`).
*/
REQUIRED: 'REQUIRED',
/**
* Denotes a field as output only.
* This indicates that the field is provided in responses, but including the
* field in a request does nothing (the server *must* ignore it and
* *must not* throw an error as a result of the field's presence).
*/
OUTPUT_ONLY: 'OUTPUT_ONLY',
/**
* Denotes a field as input only.
* This indicates that the field is provided in requests, and the
* corresponding field is not included in output.
*/
INPUT_ONLY: 'INPUT_ONLY',
/**
* Denotes a field as immutable.
* This indicates that the field may be set once in a request to create a
* resource, but may not be changed thereafter.
*/
IMMUTABLE: 'IMMUTABLE',
} as const;

/**
* An indicator of the behavior of a given field (for example, that a field
* is required in requests, or given as output but ignored as input).
* This **does not** change the behavior in protocol buffers itself; it only
* denotes the behavior and may affect how API tooling handles the field.
*
* Note: This enum **may** receive new values in the future.
*/
export type IFieldBehavior =
/**
* Conventional default for enums. Do not use this.
*/
| 'FIELD_BEHAVIOR_UNSPECIFIED'
| 0
/**
* Specifically denotes a field as optional.
* While all fields in protocol buffers are optional, this may be specified
* for emphasis if appropriate.
*/
| 'OPTIONAL'
| 1
/**
* Denotes a field as required.
* This indicates that the field **must** be provided as part of the request,
* and failure to do so will cause an error (usually `INVALID_ARGUMENT`).
*/
| 'REQUIRED'
| 2
/**
* Denotes a field as output only.
* This indicates that the field is provided in responses, but including the
* field in a request does nothing (the server *must* ignore it and
* *must not* throw an error as a result of the field's presence).
*/
| 'OUTPUT_ONLY'
| 3
/**
* Denotes a field as input only.
* This indicates that the field is provided in requests, and the
* corresponding field is not included in output.
*/
| 'INPUT_ONLY'
| 4
/**
* Denotes a field as immutable.
* This indicates that the field may be set once in a request to create a
* resource, but may not be changed thereafter.
*/
| 'IMMUTABLE'
| 5

/**
* An indicator of the behavior of a given field (for example, that a field
* is required in requests, or given as output but ignored as input).
* This **does not** change the behavior in protocol buffers itself; it only
* denotes the behavior and may affect how API tooling handles the field.
*
* Note: This enum **may** receive new values in the future.
*/
export type OFieldBehavior = typeof FieldBehavior[keyof typeof FieldBehavior]

0 comments on commit b240ca1

Please sign in to comment.