Skip to content

Commit 354bd2d

Browse files
authored
Merge pull request #2555 from murgatroid99/grpc-js-xds_custom_lb_policy
grpc-js-xds: Implement custom LB policies
2 parents 4daa6dc + 91631ba commit 354bd2d

File tree

158 files changed

+6579
-1516
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

158 files changed

+6579
-1516
lines changed

packages/grpc-js-xds/deps/envoy-api

Submodule envoy-api updated 703 files

packages/grpc-js-xds/gulpfile.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ const compile = checkTask(() => execNpmCommand('compile'));
6262

6363
const runTests = checkTask(() => {
6464
process.env.GRPC_EXPERIMENTAL_XDS_FEDERATION = 'true';
65+
process.env.GRPC_EXPERIMENTAL_XDS_CUSTOM_LB_CONFIG = 'true';
6566
return gulp.src(`${outDir}/test/**/*.js`)
6667
.pipe(mocha({reporter: 'mocha-jenkins-reporter',
6768
require: ['ts-node/register']}));

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

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,98 @@ import { XdsUpdateClientConfigureServiceHandlers } from './generated/grpc/testin
3030
import { Empty__Output } from './generated/grpc/testing/Empty';
3131
import { LoadBalancerAccumulatedStatsResponse } from './generated/grpc/testing/LoadBalancerAccumulatedStatsResponse';
3232

33+
import TypedLoadBalancingConfig = grpc.experimental.TypedLoadBalancingConfig;
34+
import LoadBalancer = grpc.experimental.LoadBalancer;
35+
import ChannelControlHelper = grpc.experimental.ChannelControlHelper;
36+
import ChildLoadBalancerHandler = grpc.experimental.ChildLoadBalancerHandler;
37+
import SubchannelAddress = grpc.experimental.SubchannelAddress;
38+
import Picker = grpc.experimental.Picker;
39+
import PickArgs = grpc.experimental.PickArgs;
40+
import PickResult = grpc.experimental.PickResult;
41+
import PickResultType = grpc.experimental.PickResultType;
42+
import createChildChannelControlHelper = grpc.experimental.createChildChannelControlHelper;
43+
import parseLoadBalancingConfig = grpc.experimental.parseLoadBalancingConfig;
44+
3345
grpc_xds.register();
3446

47+
const LB_POLICY_NAME = 'test.RpcBehaviorLoadBalancer';
48+
49+
class RpcBehaviorLoadBalancingConfig implements TypedLoadBalancingConfig {
50+
constructor(private rpcBehavior: string) {}
51+
getLoadBalancerName(): string {
52+
return LB_POLICY_NAME;
53+
}
54+
toJsonObject(): object {
55+
return {
56+
[LB_POLICY_NAME]: {
57+
'rpcBehavior': this.rpcBehavior
58+
}
59+
};
60+
}
61+
getRpcBehavior() {
62+
return this.rpcBehavior;
63+
}
64+
static createFromJson(obj: any): RpcBehaviorLoadBalancingConfig {
65+
if (!('rpcBehavior' in obj && typeof obj.rpcBehavior === 'string')) {
66+
throw new Error(`${LB_POLICY_NAME} parsing error: expected string field rpcBehavior`);
67+
}
68+
return new RpcBehaviorLoadBalancingConfig(obj.rpcBehavior);
69+
}
70+
}
71+
72+
class RpcBehaviorPicker implements Picker {
73+
constructor(private wrappedPicker: Picker, private rpcBehavior: string) {}
74+
pick(pickArgs: PickArgs): PickResult {
75+
const wrappedPick = this.wrappedPicker.pick(pickArgs);
76+
if (wrappedPick.pickResultType === PickResultType.COMPLETE) {
77+
pickArgs.metadata.add('rpc-behavior', this.rpcBehavior);
78+
}
79+
return wrappedPick;
80+
}
81+
}
82+
83+
const RPC_BEHAVIOR_CHILD_CONFIG = parseLoadBalancingConfig({round_robin: {}});
84+
85+
/**
86+
* Load balancer implementation for Custom LB policy test
87+
*/
88+
class RpcBehaviorLoadBalancer implements LoadBalancer {
89+
private child: ChildLoadBalancerHandler;
90+
private latestConfig: RpcBehaviorLoadBalancingConfig | null = null;
91+
constructor(channelControlHelper: ChannelControlHelper) {
92+
const childChannelControlHelper = createChildChannelControlHelper(channelControlHelper, {
93+
updateState: (connectivityState, picker) => {
94+
if (connectivityState === grpc.connectivityState.READY && this.latestConfig) {
95+
picker = new RpcBehaviorPicker(picker, this.latestConfig.getLoadBalancerName());
96+
}
97+
channelControlHelper.updateState(connectivityState, picker);
98+
}
99+
});
100+
this.child = new ChildLoadBalancerHandler(childChannelControlHelper);
101+
}
102+
updateAddressList(addressList: SubchannelAddress[], lbConfig: TypedLoadBalancingConfig, attributes: { [key: string]: unknown; }): void {
103+
if (!(lbConfig instanceof RpcBehaviorLoadBalancingConfig)) {
104+
return;
105+
}
106+
this.latestConfig = lbConfig;
107+
this.child.updateAddressList(addressList, RPC_BEHAVIOR_CHILD_CONFIG, attributes);
108+
}
109+
exitIdle(): void {
110+
this.child.exitIdle();
111+
}
112+
resetBackoff(): void {
113+
this.child.resetBackoff();
114+
}
115+
destroy(): void {
116+
this.child.destroy();
117+
}
118+
getTypeName(): string {
119+
return LB_POLICY_NAME;
120+
}
121+
}
122+
123+
grpc.experimental.registerLoadBalancerType(LB_POLICY_NAME, RpcBehaviorLoadBalancer, RpcBehaviorLoadBalancingConfig);
124+
35125
const packageDefinition = protoLoader.loadSync('grpc/testing/test.proto', {
36126
keepCase: true,
37127
defaults: true,
@@ -91,7 +181,7 @@ class CallSubscriber {
91181
}
92182
if (peerName in this.callsSucceededByPeer) {
93183
this.callsSucceededByPeer[peerName] += 1;
94-
} else {
184+
} else {
95185
this.callsSucceededByPeer[peerName] = 1;
96186
}
97187
this.callsSucceeded += 1;
@@ -426,9 +516,9 @@ function main() {
426516
* channels do not share any subchannels. It does not have any
427517
* inherent function. */
428518
console.log(`Interop client channel ${i} starting sending ${argv.qps} QPS to ${argv.server}`);
429-
sendConstantQps(new loadedProto.grpc.testing.TestService(argv.server, grpc.credentials.createInsecure(), {'unique': i}),
430-
argv.qps,
431-
argv.fail_on_failed_rpcs === 'true',
519+
sendConstantQps(new loadedProto.grpc.testing.TestService(argv.server, grpc.credentials.createInsecure(), {'unique': i}),
520+
argv.qps,
521+
argv.fail_on_failed_rpcs === 'true',
432522
callStatsTracker);
433523
}
434524

@@ -486,4 +576,4 @@ function main() {
486576

487577
if (require.main === module) {
488578
main();
489-
}
579+
}

packages/grpc-js-xds/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"prepare": "npm run compile",
1313
"pretest": "npm run compile",
1414
"posttest": "npm run check",
15-
"generate-types": "proto-loader-gen-types --keepCase --longs String --enums String --defaults --oneofs --includeComments --includeDirs deps/envoy-api/ deps/xds/ deps/googleapis/ deps/protoc-gen-validate/ -O src/generated/ --grpcLib @grpc/grpc-js envoy/service/discovery/v3/ads.proto envoy/service/load_stats/v3/lrs.proto envoy/config/listener/v3/listener.proto envoy/config/route/v3/route.proto envoy/config/cluster/v3/cluster.proto envoy/config/endpoint/v3/endpoint.proto envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto udpa/type/v1/typed_struct.proto xds/type/v3/typed_struct.proto envoy/extensions/filters/http/fault/v3/fault.proto envoy/service/status/v3/csds.proto",
15+
"generate-types": "proto-loader-gen-types --keepCase --longs String --enums String --defaults --oneofs --includeComments --includeDirs deps/envoy-api/ deps/xds/ deps/googleapis/ deps/protoc-gen-validate/ -O src/generated/ --grpcLib @grpc/grpc-js envoy/service/discovery/v3/ads.proto envoy/service/load_stats/v3/lrs.proto envoy/config/listener/v3/listener.proto envoy/config/route/v3/route.proto envoy/config/cluster/v3/cluster.proto envoy/config/endpoint/v3/endpoint.proto envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto udpa/type/v1/typed_struct.proto xds/type/v3/typed_struct.proto envoy/extensions/filters/http/fault/v3/fault.proto envoy/service/status/v3/csds.proto envoy/extensions/load_balancing_policies/wrr_locality/v3/wrr_locality.proto",
1616
"generate-interop-types": "proto-loader-gen-types --keep-case --longs String --enums String --defaults --oneofs --json --includeComments --includeDirs proto/ -O interop/generated --grpcLib @grpc/grpc-js grpc/testing/test.proto",
1717
"generate-test-types": "proto-loader-gen-types --keep-case --longs String --enums String --defaults --oneofs --json --includeComments --includeDirs proto/ -O test/generated --grpcLib @grpc/grpc-js grpc/testing/echo.proto"
1818
},
@@ -59,13 +59,15 @@
5959
"build/src/**/*.{js,d.ts,js.map}",
6060
"deps/envoy-api/envoy/admin/v3/**/*.proto",
6161
"deps/envoy-api/envoy/config/**/*.proto",
62+
"deps/envoy-api/envoy/data/**/*.proto",
6263
"deps/envoy-api/envoy/service/**/*.proto",
6364
"deps/envoy-api/envoy/type/**/*.proto",
6465
"deps/envoy-api/envoy/annotations/**/*.proto",
6566
"deps/envoy-api/envoy/extensions/**/*.proto",
6667
"deps/googleapis/google/api/**/*.proto",
6768
"deps/googleapis/google/protobuf/**/*.proto",
6869
"deps/googleapis/google/rpc/**/*.proto",
70+
"deps/protoc-gen-validate/**/*.proto",
6971
"deps/xds/udpa/annotations/**/*.proto",
7072
"deps/xds/udpa/type/**/*.proto",
7173
"deps/xds/xds/annotations/**/*.proto",

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ export const EXPERIMENTAL_FAULT_INJECTION = (process.env.GRPC_XDS_EXPERIMENTAL_F
1919
export const EXPERIMENTAL_OUTLIER_DETECTION = (process.env.GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION ?? 'true') === 'true';
2020
export const EXPERIMENTAL_RETRY = (process.env.GRPC_XDS_EXPERIMENTAL_ENABLE_RETRY ?? 'true') === 'true';
2121
export const EXPERIMENTAL_FEDERATION = (process.env.GRPC_EXPERIMENTAL_XDS_FEDERATION ?? 'false') === 'true';
22+
export const EXPERIMENTAL_CUSTOM_LB_CONFIG = (process.env.GRPC_EXPERIMENTAL_XDS_CUSTOM_LB_CONFIG ?? 'false') === 'true';

packages/grpc-js-xds/src/generated/ads.ts

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/grpc-js-xds/src/generated/cluster.ts

Lines changed: 5 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)