diff --git a/packages/grpc-js-xds/package.json b/packages/grpc-js-xds/package.json index 110576bc..8d937db5 100644 --- a/packages/grpc-js-xds/package.json +++ b/packages/grpc-js-xds/package.json @@ -12,7 +12,7 @@ "prepare": "npm run generate-types && npm run generate-interop-types && npm run generate-test-types && npm run compile", "pretest": "npm run compile", "posttest": "npm run check", - "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 envoy/extensions/load_balancing_policies/ring_hash/v3/ring_hash.proto envoy/extensions/load_balancing_policies/pick_first/v3/pick_first.proto envoy/extensions/clusters/aggregate/v3/cluster.proto envoy/extensions/transport_sockets/tls/v3/tls.proto envoy/config/rbac/v3/rbac.proto envoy/extensions/filters/http/rbac/v3/rbac.proto envoy/extensions/load_balancing_policies/client_side_weighted_round_robin/v3/client_side_weighted_round_robin.proto", + "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 envoy/extensions/load_balancing_policies/ring_hash/v3/ring_hash.proto envoy/extensions/load_balancing_policies/pick_first/v3/pick_first.proto envoy/extensions/clusters/aggregate/v3/cluster.proto envoy/extensions/transport_sockets/tls/v3/tls.proto envoy/config/rbac/v3/rbac.proto envoy/extensions/filters/http/rbac/v3/rbac.proto envoy/extensions/load_balancing_policies/client_side_weighted_round_robin/v3/client_side_weighted_round_robin.proto envoy/extensions/load_balancing_policies/wrr_locality/v3/wrr_locality.proto", "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", "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" }, diff --git a/packages/grpc-js-xds/src/index.ts b/packages/grpc-js-xds/src/index.ts index 457a7c20..59b8990e 100644 --- a/packages/grpc-js-xds/src/index.ts +++ b/packages/grpc-js-xds/src/index.ts @@ -31,6 +31,7 @@ import * as round_robin_lb from './lb-policy-registry/round-robin'; import * as typed_struct_lb from './lb-policy-registry/typed-struct'; import * as pick_first_lb from './lb-policy-registry/pick-first'; import * as weighted_round_robin_lb from './lb-policy-registry/weighted-round-robin'; +import * as wrr_locality from './lb-policy-registry/wrr-locality'; export { XdsServer } from './server'; export { XdsChannelCredentials, XdsServerCredentials } from './xds-credentials'; @@ -61,4 +62,5 @@ export function register() { typed_struct_lb.setup(); pick_first_lb.setup(); weighted_round_robin_lb.setup(); + wrr_locality.setup(); } diff --git a/packages/grpc-js-xds/src/lb-policy-registry/wrr-locality.ts b/packages/grpc-js-xds/src/lb-policy-registry/wrr-locality.ts new file mode 100644 index 00000000..f015f21c --- /dev/null +++ b/packages/grpc-js-xds/src/lb-policy-registry/wrr-locality.ts @@ -0,0 +1,75 @@ +/* + * Copyright 2025 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { LoadBalancingConfig } from "@grpc/grpc-js"; +import { LoadBalancingPolicy__Output } from "../generated/envoy/config/cluster/v3/LoadBalancingPolicy"; +import { TypedExtensionConfig__Output } from "../generated/envoy/config/core/v3/TypedExtensionConfig"; +import { loadProtosWithOptionsSync } from "@grpc/proto-loader/build/src/util"; +import { Any__Output } from "../generated/google/protobuf/Any"; +import { registerLbPolicy } from "../lb-policy-registry"; +import { WrrLocality__Output } from "../generated/envoy/extensions/load_balancing_policies/wrr_locality/v3/WrrLocality"; + +const WRR_LOCALITY_TYPE_URL = 'envoy.extensions.load_balancing_policies.wrr_locality.v3.WrrLocality'; + +const resourceRoot = loadProtosWithOptionsSync([ + 'envoy/extensions/load_balancing_policies/wrr_locality/v3/wrr_locality.proto'], { + keepCase: true, + includeDirs: [ + // Paths are relative to src/build/lb-policy-registry + __dirname + '/../../../deps/envoy-api/', + __dirname + '/../../../deps/xds/', + __dirname + '/../../../deps/protoc-gen-validate' + ], + } +); + +const toObjectOptions = { + longs: String, + enums: String, + defaults: true, + oneofs: true +} + +function decodePickFirstConfig(message: Any__Output): WrrLocality__Output { + const name = message.type_url.substring(message.type_url.lastIndexOf('/') + 1); + const type = resourceRoot.lookup(name); + if (type) { + const decodedMessage = (type as any).decode(message.value); + return decodedMessage.$type.toObject(decodedMessage, toObjectOptions) as WrrLocality__Output; + } else { + throw new Error(`WRR Locality parsing error: unexpected type URL ${message.type_url}`); + } +} + +function convertToLoadBalancingPolicy(protoPolicy: TypedExtensionConfig__Output, selectChildPolicy: (childPolicy: LoadBalancingPolicy__Output) => LoadBalancingConfig): LoadBalancingConfig | null { + if (protoPolicy.typed_config?.type_url !== WRR_LOCALITY_TYPE_URL) { + throw new Error(`WRR Locality LB policy parsing error: unexpected type URL ${protoPolicy.typed_config?.type_url}`); + } + const wrrLocalityMessage = decodePickFirstConfig(protoPolicy.typed_config); + if (!wrrLocalityMessage.endpoint_picking_policy) { + throw new Error('WRR Locality LB policy parsing error: no endpoint_picking_policy set'); + } + return { + wrr_locality: { + shuffleAddressList: selectChildPolicy(wrrLocalityMessage.endpoint_picking_policy) + } + }; +} + +export function setup() { + registerLbPolicy(WRR_LOCALITY_TYPE_URL, convertToLoadBalancingPolicy); +}