From 63763a40003b17e5e8f3f9c8d7f4aeb6592569f6 Mon Sep 17 00:00:00 2001 From: Michael Lumish Date: Mon, 8 Apr 2024 17:47:26 -0700 Subject: [PATCH 01/11] Merge pull request #2712 from sergiitk/psm-interop-pkg-dev PSM Interop: Migrate to Artifact Registry --- packages/grpc-js-xds/scripts/xds_k8s_lb.sh | 7 ++++--- packages/grpc-js-xds/scripts/xds_k8s_url_map.sh | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/grpc-js-xds/scripts/xds_k8s_lb.sh b/packages/grpc-js-xds/scripts/xds_k8s_lb.sh index ed7c77fe2..2c5684ea3 100755 --- a/packages/grpc-js-xds/scripts/xds_k8s_lb.sh +++ b/packages/grpc-js-xds/scripts/xds_k8s_lb.sh @@ -19,8 +19,9 @@ set -eo pipefail readonly GITHUB_REPOSITORY_NAME="grpc-node" readonly TEST_DRIVER_INSTALL_SCRIPT_URL="https://raw.githubusercontent.com/${TEST_DRIVER_REPO_OWNER:-grpc}/psm-interop/${TEST_DRIVER_BRANCH:-main}/.kokoro/psm_interop_kokoro_lib.sh" ## xDS test client Docker images -readonly SERVER_IMAGE_NAME="gcr.io/grpc-testing/xds-interop/java-server:558b5b0bfac8e21755c223063274a779b3898afe" -readonly CLIENT_IMAGE_NAME="gcr.io/grpc-testing/xds-interop/node-client" +readonly DOCKER_REGISTRY="us-docker.pkg.dev" +readonly SERVER_IMAGE_NAME="us-docker.pkg.dev/grpc-testing/psm-interop/java-server:canonical" +readonly CLIENT_IMAGE_NAME="us-docker.pkg.dev/grpc-testing/psm-interop/node-client" readonly FORCE_IMAGE_BUILD="${FORCE_IMAGE_BUILD:-0}" readonly BUILD_APP_PATH="packages/grpc-js-xds/interop/Dockerfile" readonly LANGUAGE_NAME="Node" @@ -46,7 +47,7 @@ build_test_app_docker_images() { -t "${CLIENT_IMAGE_NAME}:${GIT_COMMIT}" \ . - gcloud -q auth configure-docker + gcloud -q auth configure-docker "${DOCKER_REGISTRY}" docker push "${CLIENT_IMAGE_NAME}:${GIT_COMMIT}" if is_version_branch "${TESTING_VERSION}"; then tag_and_push_docker_image "${CLIENT_IMAGE_NAME}" "${GIT_COMMIT}" "${TESTING_VERSION}" diff --git a/packages/grpc-js-xds/scripts/xds_k8s_url_map.sh b/packages/grpc-js-xds/scripts/xds_k8s_url_map.sh index 9344d054b..d6e2c7ed4 100644 --- a/packages/grpc-js-xds/scripts/xds_k8s_url_map.sh +++ b/packages/grpc-js-xds/scripts/xds_k8s_url_map.sh @@ -19,7 +19,8 @@ set -eo pipefail readonly GITHUB_REPOSITORY_NAME="grpc-node" readonly TEST_DRIVER_INSTALL_SCRIPT_URL="https://raw.githubusercontent.com/${TEST_DRIVER_REPO_OWNER:-grpc}/psm-interop/${TEST_DRIVER_BRANCH:-main}/.kokoro/psm_interop_kokoro_lib.sh" ## xDS test client Docker images -readonly CLIENT_IMAGE_NAME="gcr.io/grpc-testing/xds-interop/node-client" +readonly DOCKER_REGISTRY="us-docker.pkg.dev" +readonly CLIENT_IMAGE_NAME="us-docker.pkg.dev/grpc-testing/psm-interop/node-client" readonly FORCE_IMAGE_BUILD="${FORCE_IMAGE_BUILD:-0}" readonly BUILD_APP_PATH="packages/grpc-js-xds/interop/Dockerfile" readonly LANGUAGE_NAME="Node" @@ -45,7 +46,7 @@ build_test_app_docker_images() { -t "${CLIENT_IMAGE_NAME}:${GIT_COMMIT}" \ . - gcloud -q auth configure-docker + gcloud -q auth configure-docker "${DOCKER_REGISTRY}" docker push "${CLIENT_IMAGE_NAME}:${GIT_COMMIT}" if is_version_branch "${TESTING_VERSION}"; then tag_and_push_docker_image "${CLIENT_IMAGE_NAME}" "${GIT_COMMIT}" "${TESTING_VERSION}" From 5ae7c8c84518fa49ec639cd36051d65e50db5a6c Mon Sep 17 00:00:00 2001 From: Michael Lumish Date: Fri, 3 May 2024 14:24:44 -0700 Subject: [PATCH 02/11] Merge pull request #2735 from murgatroid99/grpc-js_linkify-it_fix root: Update dependency on jsdoc to avoid linkify-it compilation error --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 70a15fbbf..49087fb05 100644 --- a/package.json +++ b/package.json @@ -20,14 +20,13 @@ "del": "^3.0.0", "execa": "^0.8.0", "gulp": "^4.0.1", - "gulp-jsdoc3": "^1.0.1", "gulp-jshint": "^2.0.4", "gulp-mocha": "^4.3.1", "gulp-sourcemaps": "^2.6.1", "gulp-tslint": "^8.1.1", "gulp-typescript": "^3.2.2", "gulp-util": "^3.0.8", - "jsdoc": "^3.3.2", + "jsdoc": "^4.0.3", "jshint": "^2.9.5", "make-dir": "^1.1.0", "merge2": "^1.1.0", From cf14020643472af7ec56c3591c73f91d74c4aa73 Mon Sep 17 00:00:00 2001 From: Michael Lumish Date: Mon, 6 May 2024 15:20:11 -0700 Subject: [PATCH 03/11] Merge pull request #2729 from sergiitk/psm-interop-common-prod-tests PSM Interop: simplify Kokoro buildscripts --- .../scripts/psm-interop-build-node.sh | 38 ++++ .../scripts/psm-interop-test-node.sh | 40 ++++ packages/grpc-js-xds/scripts/xds_k8s_lb.sh | 176 ------------------ .../grpc-js-xds/scripts/xds_k8s_url_map.sh | 163 ---------------- test/kokoro/xds-interop.cfg | 24 --- test/kokoro/xds_k8s_lb.cfg | 6 +- test/kokoro/xds_k8s_url_map.cfg | 6 +- 7 files changed, 88 insertions(+), 365 deletions(-) create mode 100755 packages/grpc-js-xds/scripts/psm-interop-build-node.sh create mode 100755 packages/grpc-js-xds/scripts/psm-interop-test-node.sh delete mode 100755 packages/grpc-js-xds/scripts/xds_k8s_lb.sh delete mode 100644 packages/grpc-js-xds/scripts/xds_k8s_url_map.sh delete mode 100644 test/kokoro/xds-interop.cfg diff --git a/packages/grpc-js-xds/scripts/psm-interop-build-node.sh b/packages/grpc-js-xds/scripts/psm-interop-build-node.sh new file mode 100755 index 000000000..d52206f0e --- /dev/null +++ b/packages/grpc-js-xds/scripts/psm-interop-build-node.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# Copyright 2024 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. +set -eo pipefail + +####################################### +# Builds test app Docker images and pushes them to GCR. +# Called from psm_interop_kokoro_lib.sh. +# +# Globals: +# SRC_DIR: Absolute path to the source repo on Kokoro VM +# SERVER_IMAGE_NAME: Test server Docker image name +# CLIENT_IMAGE_NAME: Test client Docker image name +# GIT_COMMIT: SHA-1 of git commit being built +# DOCKER_REGISTRY: Docker registry to push to +# Outputs: +# Writes the output of docker image build stdout, stderr +####################################### +psm::lang::build_docker_images() { + local client_dockerfile="packages/grpc-js-xds/interop/Dockerfile" + + cd "${SRC_DIR}" + psm::tools::run_verbose git submodule update --init --recursive + psm::tools::run_verbose git submodule status + + psm::build::docker_images_generic "${client_dockerfile}" +} diff --git a/packages/grpc-js-xds/scripts/psm-interop-test-node.sh b/packages/grpc-js-xds/scripts/psm-interop-test-node.sh new file mode 100755 index 000000000..169cf06f2 --- /dev/null +++ b/packages/grpc-js-xds/scripts/psm-interop-test-node.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# Copyright 2024 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. +set -eo pipefail + +# Input parameters to psm:: methods of the install script. +readonly GRPC_LANGUAGE="node" +readonly BUILD_SCRIPT_DIR="$(dirname "$0")" + +# Used locally. +readonly TEST_DRIVER_INSTALL_SCRIPT_URL="https://raw.githubusercontent.com/${TEST_DRIVER_REPO_OWNER:-grpc}/psm-interop/${TEST_DRIVER_BRANCH:-main}/.kokoro/psm_interop_kokoro_lib.sh" + +psm::lang::source_install_lib() { + echo "Sourcing test driver install script from: ${TEST_DRIVER_INSTALL_SCRIPT_URL}" + local install_lib + # Download to a tmp file. + install_lib="$(mktemp -d)/psm_interop_kokoro_lib.sh" + curl -s --retry-connrefused --retry 5 -o "${install_lib}" "${TEST_DRIVER_INSTALL_SCRIPT_URL}" + # Checksum. + if command -v sha256sum &> /dev/null; then + echo "Install script checksum:" + sha256sum "${install_lib}" + fi + source "${install_lib}" +} + +psm::lang::source_install_lib +source "${BUILD_SCRIPT_DIR}/psm-interop-build-${GRPC_LANGUAGE}.sh" +psm::run "${PSM_TEST_SUITE}" diff --git a/packages/grpc-js-xds/scripts/xds_k8s_lb.sh b/packages/grpc-js-xds/scripts/xds_k8s_lb.sh deleted file mode 100755 index 2c5684ea3..000000000 --- a/packages/grpc-js-xds/scripts/xds_k8s_lb.sh +++ /dev/null @@ -1,176 +0,0 @@ -#!/usr/bin/env bash -# Copyright 2022 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. - -set -eo pipefail - -# Constants -readonly GITHUB_REPOSITORY_NAME="grpc-node" -readonly TEST_DRIVER_INSTALL_SCRIPT_URL="https://raw.githubusercontent.com/${TEST_DRIVER_REPO_OWNER:-grpc}/psm-interop/${TEST_DRIVER_BRANCH:-main}/.kokoro/psm_interop_kokoro_lib.sh" -## xDS test client Docker images -readonly DOCKER_REGISTRY="us-docker.pkg.dev" -readonly SERVER_IMAGE_NAME="us-docker.pkg.dev/grpc-testing/psm-interop/java-server:canonical" -readonly CLIENT_IMAGE_NAME="us-docker.pkg.dev/grpc-testing/psm-interop/node-client" -readonly FORCE_IMAGE_BUILD="${FORCE_IMAGE_BUILD:-0}" -readonly BUILD_APP_PATH="packages/grpc-js-xds/interop/Dockerfile" -readonly LANGUAGE_NAME="Node" - -####################################### -# Builds test app Docker images and pushes them to GCR -# Globals: -# BUILD_APP_PATH -# CLIENT_IMAGE_NAME: Test client Docker image name -# GIT_COMMIT: SHA-1 of git commit being built -# TESTING_VERSION: version branch under test, f.e. v1.42.x, master -# Arguments: -# None -# Outputs: -# Writes the output of `gcloud builds submit` to stdout, stderr -####################################### -build_test_app_docker_images() { - echo "Building ${LANGUAGE_NAME} xDS interop test app Docker images" - - pushd "${SRC_DIR}" - docker build \ - -f "${BUILD_APP_PATH}" \ - -t "${CLIENT_IMAGE_NAME}:${GIT_COMMIT}" \ - . - - gcloud -q auth configure-docker "${DOCKER_REGISTRY}" - docker push "${CLIENT_IMAGE_NAME}:${GIT_COMMIT}" - if is_version_branch "${TESTING_VERSION}"; then - tag_and_push_docker_image "${CLIENT_IMAGE_NAME}" "${GIT_COMMIT}" "${TESTING_VERSION}" - fi - popd -} - -####################################### -# Builds test app and its docker images unless they already exist -# Globals: -# CLIENT_IMAGE_NAME: Test client Docker image name -# GIT_COMMIT: SHA-1 of git commit being built -# FORCE_IMAGE_BUILD -# Arguments: -# None -# Outputs: -# Writes the output to stdout, stderr -####################################### -build_docker_images_if_needed() { - # Check if images already exist - client_tags="$(gcloud_gcr_list_image_tags "${CLIENT_IMAGE_NAME}" "${GIT_COMMIT}")" - printf "Client image: %s:%s\n" "${CLIENT_IMAGE_NAME}" "${GIT_COMMIT}" - echo "${client_tags:-Client image not found}" - - # Build if any of the images are missing, or FORCE_IMAGE_BUILD=1 - if [[ "${FORCE_IMAGE_BUILD}" == "1" || -z "${client_tags}" ]]; then - build_test_app_docker_images - else - echo "Skipping ${LANGUAGE_NAME} test app build" - fi -} - -####################################### -# Executes the test case -# Globals: -# TEST_DRIVER_FLAGFILE: Relative path to test driver flagfile -# KUBE_CONTEXT: The name of kubectl context with GKE cluster access -# SECONDARY_KUBE_CONTEXT: The name of kubectl context with secondary GKE cluster access, if any -# TEST_XML_OUTPUT_DIR: Output directory for the test xUnit XML report -# CLIENT_IMAGE_NAME: Test client Docker image name -# GIT_COMMIT: SHA-1 of git commit being built -# Arguments: -# Test case name -# Outputs: -# Writes the output of test execution to stdout, stderr -# Test xUnit report to ${TEST_XML_OUTPUT_DIR}/${test_name}/sponge_log.xml -####################################### -run_test() { - # Test driver usage: - # https://github.com/grpc/grpc/tree/master/tools/run_tests/xds_k8s_test_driver#basic-usage - local test_name="${1:?Usage: run_test test_name}" - local out_dir="${TEST_XML_OUTPUT_DIR}/${test_name}" - mkdir -pv "${out_dir}" - # testing_version is used by the framework to determine the supported PSM - # features. It's captured from Kokoro job name of the Node repo, which takes - # the form: - # grpc/node// - python3 -m "tests.${test_name}" \ - --flagfile="${TEST_DRIVER_FLAGFILE}" \ - --kube_context="${KUBE_CONTEXT}" \ - --secondary_kube_context="${SECONDARY_KUBE_CONTEXT}" \ - --client_image="${CLIENT_IMAGE_NAME}:${GIT_COMMIT}" \ - --server_image="${SERVER_IMAGE_NAME}" \ - --testing_version="${TESTING_VERSION}" \ - --force_cleanup \ - --collect_app_logs \ - --log_dir="${out_dir}" \ - --xml_output_file="${out_dir}/sponge_log.xml" \ - |& tee "${out_dir}/sponge_log.log" -} - -####################################### -# Main function: provision software necessary to execute tests, and run them -# Globals: -# KOKORO_ARTIFACTS_DIR -# GITHUB_REPOSITORY_NAME -# SRC_DIR: Populated with absolute path to the source repo -# TEST_DRIVER_REPO_DIR: Populated with the path to the repo containing -# the test driver -# TEST_DRIVER_FULL_DIR: Populated with the path to the test driver source code -# TEST_DRIVER_FLAGFILE: Populated with relative path to test driver flagfile -# TEST_XML_OUTPUT_DIR: Populated with the path to test xUnit XML report -# GIT_ORIGIN_URL: Populated with the origin URL of git repo used for the build -# GIT_COMMIT: Populated with the SHA-1 of git commit being built -# GIT_COMMIT_SHORT: Populated with the short SHA-1 of git commit being built -# KUBE_CONTEXT: Populated with name of kubectl context with GKE cluster access -# SECONDARY_KUBE_CONTEXT: Populated with name of kubectl context with secondary GKE cluster access, if any -# Arguments: -# None -# Outputs: -# Writes the output of test execution to stdout, stderr -####################################### -main() { - local script_dir - script_dir="$(dirname "$0")" - - cd "${script_dir}" - - git submodule update --init --recursive - - # Source the test driver from the master branch. - echo "Sourcing test driver install script from: ${TEST_DRIVER_INSTALL_SCRIPT_URL}" - source /dev/stdin <<< "$(curl -s "${TEST_DRIVER_INSTALL_SCRIPT_URL}")" - - activate_gke_cluster GKE_CLUSTER_PSM_LB - activate_secondary_gke_cluster GKE_CLUSTER_PSM_LB - - set -x - if [[ -n "${KOKORO_ARTIFACTS_DIR}" ]]; then - kokoro_setup_test_driver "${GITHUB_REPOSITORY_NAME}" - else - local_setup_test_driver "${script_dir}" - fi - build_docker_images_if_needed - - # Run tests - cd "${TEST_DRIVER_FULL_DIR}" - local failed_tests=0 - test_suites=("baseline_test" "api_listener_test" "change_backend_service_test" "failover_test" "remove_neg_test" "round_robin_test" "outlier_detection_test") - for test in "${test_suites[@]}"; do - run_test $test || (( ++failed_tests )) - done - echo "Failed test suites: ${failed_tests}" -} - -main "$@" diff --git a/packages/grpc-js-xds/scripts/xds_k8s_url_map.sh b/packages/grpc-js-xds/scripts/xds_k8s_url_map.sh deleted file mode 100644 index d6e2c7ed4..000000000 --- a/packages/grpc-js-xds/scripts/xds_k8s_url_map.sh +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/env bash -# Copyright 2022 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. - -set -eo pipefail - -# Constants -readonly GITHUB_REPOSITORY_NAME="grpc-node" -readonly TEST_DRIVER_INSTALL_SCRIPT_URL="https://raw.githubusercontent.com/${TEST_DRIVER_REPO_OWNER:-grpc}/psm-interop/${TEST_DRIVER_BRANCH:-main}/.kokoro/psm_interop_kokoro_lib.sh" -## xDS test client Docker images -readonly DOCKER_REGISTRY="us-docker.pkg.dev" -readonly CLIENT_IMAGE_NAME="us-docker.pkg.dev/grpc-testing/psm-interop/node-client" -readonly FORCE_IMAGE_BUILD="${FORCE_IMAGE_BUILD:-0}" -readonly BUILD_APP_PATH="packages/grpc-js-xds/interop/Dockerfile" -readonly LANGUAGE_NAME="Node" - -####################################### -# Builds test app Docker images and pushes them to GCR -# Globals: -# BUILD_APP_PATH -# CLIENT_IMAGE_NAME: Test client Docker image name -# GIT_COMMIT: SHA-1 of git commit being built -# TESTING_VERSION: version branch under test, f.e. v1.42.x, master -# Arguments: -# None -# Outputs: -# Writes the output of `gcloud builds submit` to stdout, stderr -####################################### -build_test_app_docker_images() { - echo "Building ${LANGUAGE_NAME} xDS interop test app Docker images" - - pushd "${SRC_DIR}" - docker build \ - -f "${BUILD_APP_PATH}" \ - -t "${CLIENT_IMAGE_NAME}:${GIT_COMMIT}" \ - . - - gcloud -q auth configure-docker "${DOCKER_REGISTRY}" - docker push "${CLIENT_IMAGE_NAME}:${GIT_COMMIT}" - if is_version_branch "${TESTING_VERSION}"; then - tag_and_push_docker_image "${CLIENT_IMAGE_NAME}" "${GIT_COMMIT}" "${TESTING_VERSION}" - fi - popd -} - -####################################### -# Builds test app and its docker images unless they already exist -# Globals: -# CLIENT_IMAGE_NAME: Test client Docker image name -# GIT_COMMIT: SHA-1 of git commit being built -# FORCE_IMAGE_BUILD -# Arguments: -# None -# Outputs: -# Writes the output to stdout, stderr -####################################### -build_docker_images_if_needed() { - # Check if images already exist - client_tags="$(gcloud_gcr_list_image_tags "${CLIENT_IMAGE_NAME}" "${GIT_COMMIT}")" - printf "Client image: %s:%s\n" "${CLIENT_IMAGE_NAME}" "${GIT_COMMIT}" - echo "${client_tags:-Client image not found}" - - # Build if any of the images are missing, or FORCE_IMAGE_BUILD=1 - if [[ "${FORCE_IMAGE_BUILD}" == "1" || -z "${client_tags}" ]]; then - build_test_app_docker_images - else - echo "Skipping ${LANGUAGE_NAME} test app build" - fi -} - -####################################### -# Executes the test case -# Globals: -# TEST_DRIVER_FLAGFILE: Relative path to test driver flagfile -# KUBE_CONTEXT: The name of kubectl context with GKE cluster access -# TEST_XML_OUTPUT_DIR: Output directory for the test xUnit XML report -# CLIENT_IMAGE_NAME: Test client Docker image name -# GIT_COMMIT: SHA-1 of git commit being built -# TESTING_VERSION: version branch under test: used by the framework to determine the supported PSM -# features. -# Arguments: -# Test case name -# Outputs: -# Writes the output of test execution to stdout, stderr -# Test xUnit report to ${TEST_XML_OUTPUT_DIR}/${test_name}/sponge_log.xml -####################################### -run_test() { - # Test driver usage: - # https://github.com/grpc/grpc/tree/master/tools/run_tests/xds_k8s_test_driver#basic-usage - local test_name="${1:?Usage: run_test test_name}" - local out_dir="${TEST_XML_OUTPUT_DIR}/${test_name}" - mkdir -pv "${out_dir}" - set -x - python3 -m "tests.${test_name}" \ - --flagfile="${TEST_DRIVER_FLAGFILE}" \ - --flagfile="config/url-map.cfg" \ - --kube_context="${KUBE_CONTEXT}" \ - --client_image="${CLIENT_IMAGE_NAME}:${GIT_COMMIT}" \ - --testing_version="${TESTING_VERSION}" \ - --collect_app_logs \ - --log_dir="${out_dir}" \ - --xml_output_file="${out_dir}/sponge_log.xml" \ - |& tee "${out_dir}/sponge_log.log" -} - -####################################### -# Main function: provision software necessary to execute tests, and run them -# Globals: -# KOKORO_ARTIFACTS_DIR -# GITHUB_REPOSITORY_NAME -# SRC_DIR: Populated with absolute path to the source repo -# TEST_DRIVER_REPO_DIR: Populated with the path to the repo containing -# the test driver -# TEST_DRIVER_FULL_DIR: Populated with the path to the test driver source code -# TEST_DRIVER_FLAGFILE: Populated with relative path to test driver flagfile -# TEST_XML_OUTPUT_DIR: Populated with the path to test xUnit XML report -# GIT_ORIGIN_URL: Populated with the origin URL of git repo used for the build -# GIT_COMMIT: Populated with the SHA-1 of git commit being built -# GIT_COMMIT_SHORT: Populated with the short SHA-1 of git commit being built -# KUBE_CONTEXT: Populated with name of kubectl context with GKE cluster access -# Arguments: -# None -# Outputs: -# Writes the output of test execution to stdout, stderr -####################################### -main() { - local script_dir - script_dir="$(dirname "$0")" - - cd "${script_dir}" - - git submodule update --init --recursive - - # Source the test driver from the master branch. - echo "Sourcing test driver install script from: ${TEST_DRIVER_INSTALL_SCRIPT_URL}" - source /dev/stdin <<< "$(curl -s "${TEST_DRIVER_INSTALL_SCRIPT_URL}")" - - activate_gke_cluster GKE_CLUSTER_PSM_BASIC - - set -x - if [[ -n "${KOKORO_ARTIFACTS_DIR}" ]]; then - kokoro_setup_test_driver "${GITHUB_REPOSITORY_NAME}" - else - local_setup_test_driver "${script_dir}" - fi - build_docker_images_if_needed - # Run tests - cd "${TEST_DRIVER_FULL_DIR}" - run_test url_map || echo "Failed url_map test" -} - -main "$@" diff --git a/test/kokoro/xds-interop.cfg b/test/kokoro/xds-interop.cfg deleted file mode 100644 index 866cb4b58..000000000 --- a/test/kokoro/xds-interop.cfg +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2017 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. - -# Config file for Kokoro (in protobuf text format) - -# Location of the continuous shell script in repository. -build_file: "grpc-node/packages/grpc-js-xds/scripts/xds.sh" -timeout_mins: 360 -action { - define_artifacts { - regex: "github/grpc/reports/**" - } -} diff --git a/test/kokoro/xds_k8s_lb.cfg b/test/kokoro/xds_k8s_lb.cfg index 09aa3d17d..3efb62f29 100644 --- a/test/kokoro/xds_k8s_lb.cfg +++ b/test/kokoro/xds_k8s_lb.cfg @@ -15,7 +15,7 @@ # Config file for Kokoro (in protobuf text format) # Location of the continuous shell script in repository. -build_file: "grpc-node/packages/grpc-js-xds/scripts/xds_k8s_lb.sh" +build_file: "grpc-node/packages/grpc-js-xds/scripts/psm-interop-test-node.sh" timeout_mins: 180 action { define_artifacts { @@ -24,3 +24,7 @@ action { strip_prefix: "artifacts" } } +env_vars { + key: "PSM_TEST_SUITE" + value: "lb" +} diff --git a/test/kokoro/xds_k8s_url_map.cfg b/test/kokoro/xds_k8s_url_map.cfg index 50d523b66..bb6e6baf1 100644 --- a/test/kokoro/xds_k8s_url_map.cfg +++ b/test/kokoro/xds_k8s_url_map.cfg @@ -15,7 +15,7 @@ # Config file for Kokoro (in protobuf text format) # Location of the continuous shell script in repository. -build_file: "grpc-node/packages/grpc-js-xds/scripts/xds_k8s_url_map.sh" +build_file: "grpc-node/packages/grpc-js-xds/scripts/psm-interop-test-node.sh" timeout_mins: 180 action { define_artifacts { @@ -24,3 +24,7 @@ action { strip_prefix: "artifacts" } } +env_vars { + key: "PSM_TEST_SUITE" + value: "url_map" +} From d5d62b4d94acf05d4335122efa9e36b07955eb2d Mon Sep 17 00:00:00 2001 From: Michael Lumish Date: Fri, 7 Jun 2024 10:11:06 -0700 Subject: [PATCH 04/11] grpc-js: Avoid buffering significantly more than max_receive_message_size per received message (1.9.x) --- packages/grpc-js/src/compression-filter.ts | 67 ++++++++++---- packages/grpc-js/src/internal-channel.ts | 2 - .../grpc-js/src/max-message-size-filter.ts | 88 ------------------- packages/grpc-js/src/server-call.ts | 87 +++++++++++------- packages/grpc-js/src/stream-decoder.ts | 5 ++ packages/grpc-js/src/subchannel-call.ts | 14 ++- packages/grpc-js/src/transport.ts | 7 +- .../grpc-js/test/fixtures/test_service.proto | 1 + packages/grpc-js/test/test-server-errors.ts | 49 ++++++++++- 9 files changed, 175 insertions(+), 145 deletions(-) delete mode 100644 packages/grpc-js/src/max-message-size-filter.ts diff --git a/packages/grpc-js/src/compression-filter.ts b/packages/grpc-js/src/compression-filter.ts index 136311ad5..f1600b36d 100644 --- a/packages/grpc-js/src/compression-filter.ts +++ b/packages/grpc-js/src/compression-filter.ts @@ -21,7 +21,7 @@ import { WriteObject, WriteFlags } from './call-interface'; import { Channel } from './channel'; import { ChannelOptions } from './channel-options'; import { CompressionAlgorithms } from './compression-algorithms'; -import { LogVerbosity } from './constants'; +import { DEFAULT_MAX_RECEIVE_MESSAGE_LENGTH, LogVerbosity, Status } from './constants'; import { BaseFilter, Filter, FilterFactory } from './filter'; import * as logging from './logging'; import { Metadata, MetadataValue } from './metadata'; @@ -98,6 +98,10 @@ class IdentityHandler extends CompressionHandler { } class DeflateHandler extends CompressionHandler { + constructor(private maxRecvMessageLength: number) { + super(); + } + compressMessage(message: Buffer) { return new Promise((resolve, reject) => { zlib.deflate(message, (err, output) => { @@ -112,18 +116,34 @@ class DeflateHandler extends CompressionHandler { decompressMessage(message: Buffer) { return new Promise((resolve, reject) => { - zlib.inflate(message, (err, output) => { - if (err) { - reject(err); - } else { - resolve(output); + let totalLength = 0; + const messageParts: Buffer[] = []; + const decompresser = zlib.createInflate(); + decompresser.on('data', (chunk: Buffer) => { + messageParts.push(chunk); + totalLength += chunk.byteLength; + if (this.maxRecvMessageLength !== -1 && totalLength > this.maxRecvMessageLength) { + decompresser.destroy(); + reject({ + code: Status.RESOURCE_EXHAUSTED, + details: `Received message that decompresses to a size larger than ${this.maxRecvMessageLength}` + }); } }); + decompresser.on('end', () => { + resolve(Buffer.concat(messageParts)); + }); + decompresser.write(message); + decompresser.end(); }); } } class GzipHandler extends CompressionHandler { + constructor(private maxRecvMessageLength: number) { + super(); + } + compressMessage(message: Buffer) { return new Promise((resolve, reject) => { zlib.gzip(message, (err, output) => { @@ -138,13 +158,25 @@ class GzipHandler extends CompressionHandler { decompressMessage(message: Buffer) { return new Promise((resolve, reject) => { - zlib.unzip(message, (err, output) => { - if (err) { - reject(err); - } else { - resolve(output); + let totalLength = 0; + const messageParts: Buffer[] = []; + const decompresser = zlib.createGunzip(); + decompresser.on('data', (chunk: Buffer) => { + messageParts.push(chunk); + totalLength += chunk.byteLength; + if (this.maxRecvMessageLength !== -1 && totalLength > this.maxRecvMessageLength) { + decompresser.destroy(); + reject({ + code: Status.RESOURCE_EXHAUSTED, + details: `Received message that decompresses to a size larger than ${this.maxRecvMessageLength}` + }); } }); + decompresser.on('end', () => { + resolve(Buffer.concat(messageParts)); + }); + decompresser.write(message); + decompresser.end(); }); } } @@ -169,14 +201,14 @@ class UnknownHandler extends CompressionHandler { } } -function getCompressionHandler(compressionName: string): CompressionHandler { +function getCompressionHandler(compressionName: string, maxReceiveMessageSize: number): CompressionHandler { switch (compressionName) { case 'identity': return new IdentityHandler(); case 'deflate': - return new DeflateHandler(); + return new DeflateHandler(maxReceiveMessageSize); case 'gzip': - return new GzipHandler(); + return new GzipHandler(maxReceiveMessageSize); default: return new UnknownHandler(compressionName); } @@ -186,6 +218,7 @@ export class CompressionFilter extends BaseFilter implements Filter { private sendCompression: CompressionHandler = new IdentityHandler(); private receiveCompression: CompressionHandler = new IdentityHandler(); private currentCompressionAlgorithm: CompressionAlgorithm = 'identity'; + private maxReceiveMessageLength: number; constructor( channelOptions: ChannelOptions, @@ -195,6 +228,7 @@ export class CompressionFilter extends BaseFilter implements Filter { const compressionAlgorithmKey = channelOptions['grpc.default_compression_algorithm']; + this.maxReceiveMessageLength = channelOptions['grpc.max_receive_message_length'] ?? DEFAULT_MAX_RECEIVE_MESSAGE_LENGTH if (compressionAlgorithmKey !== undefined) { if (isCompressionAlgorithmKey(compressionAlgorithmKey)) { const clientSelectedEncoding = CompressionAlgorithms[ @@ -215,7 +249,8 @@ export class CompressionFilter extends BaseFilter implements Filter { ) { this.currentCompressionAlgorithm = clientSelectedEncoding; this.sendCompression = getCompressionHandler( - this.currentCompressionAlgorithm + this.currentCompressionAlgorithm, + -1 ); } } else { @@ -247,7 +282,7 @@ export class CompressionFilter extends BaseFilter implements Filter { if (receiveEncoding.length > 0) { const encoding: MetadataValue = receiveEncoding[0]; if (typeof encoding === 'string') { - this.receiveCompression = getCompressionHandler(encoding); + this.receiveCompression = getCompressionHandler(encoding, this.maxReceiveMessageLength); } } metadata.remove('grpc-encoding'); diff --git a/packages/grpc-js/src/internal-channel.ts b/packages/grpc-js/src/internal-channel.ts index 6a65b712f..5c8c91c41 100644 --- a/packages/grpc-js/src/internal-channel.ts +++ b/packages/grpc-js/src/internal-channel.ts @@ -33,7 +33,6 @@ import { } from './resolver'; import { trace } from './logging'; import { SubchannelAddress } from './subchannel-address'; -import { MaxMessageSizeFilterFactory } from './max-message-size-filter'; import { mapProxyName } from './http_proxy'; import { GrpcUri, parseUri, uriToString } from './uri-parser'; import { ServerSurfaceCall } from './server-call'; @@ -393,7 +392,6 @@ export class InternalChannel { } ); this.filterStackFactory = new FilterStackFactory([ - new MaxMessageSizeFilterFactory(this.options), new CompressionFilterFactory(this, this.options), ]); this.trace( diff --git a/packages/grpc-js/src/max-message-size-filter.ts b/packages/grpc-js/src/max-message-size-filter.ts deleted file mode 100644 index b6df374b2..000000000 --- a/packages/grpc-js/src/max-message-size-filter.ts +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2020 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 { BaseFilter, Filter, FilterFactory } from './filter'; -import { WriteObject } from './call-interface'; -import { - Status, - DEFAULT_MAX_SEND_MESSAGE_LENGTH, - DEFAULT_MAX_RECEIVE_MESSAGE_LENGTH, -} from './constants'; -import { ChannelOptions } from './channel-options'; -import { Metadata } from './metadata'; - -export class MaxMessageSizeFilter extends BaseFilter implements Filter { - private maxSendMessageSize: number = DEFAULT_MAX_SEND_MESSAGE_LENGTH; - private maxReceiveMessageSize: number = DEFAULT_MAX_RECEIVE_MESSAGE_LENGTH; - constructor(options: ChannelOptions) { - super(); - if ('grpc.max_send_message_length' in options) { - this.maxSendMessageSize = options['grpc.max_send_message_length']!; - } - if ('grpc.max_receive_message_length' in options) { - this.maxReceiveMessageSize = options['grpc.max_receive_message_length']!; - } - } - - async sendMessage(message: Promise): Promise { - /* A configured size of -1 means that there is no limit, so skip the check - * entirely */ - if (this.maxSendMessageSize === -1) { - return message; - } else { - const concreteMessage = await message; - if (concreteMessage.message.length > this.maxSendMessageSize) { - throw { - code: Status.RESOURCE_EXHAUSTED, - details: `Sent message larger than max (${concreteMessage.message.length} vs. ${this.maxSendMessageSize})`, - metadata: new Metadata(), - }; - } else { - return concreteMessage; - } - } - } - - async receiveMessage(message: Promise): Promise { - /* A configured size of -1 means that there is no limit, so skip the check - * entirely */ - if (this.maxReceiveMessageSize === -1) { - return message; - } else { - const concreteMessage = await message; - if (concreteMessage.length > this.maxReceiveMessageSize) { - throw { - code: Status.RESOURCE_EXHAUSTED, - details: `Received message larger than max (${concreteMessage.length} vs. ${this.maxReceiveMessageSize})`, - metadata: new Metadata(), - }; - } else { - return concreteMessage; - } - } - } -} - -export class MaxMessageSizeFilterFactory - implements FilterFactory -{ - constructor(private readonly options: ChannelOptions) {} - - createFilter(): MaxMessageSizeFilter { - return new MaxMessageSizeFilter(this.options); - } -} diff --git a/packages/grpc-js/src/server-call.ts b/packages/grpc-js/src/server-call.ts index 107c2e3ef..3032c6e4e 100644 --- a/packages/grpc-js/src/server-call.ts +++ b/packages/grpc-js/src/server-call.ts @@ -19,7 +19,6 @@ import { EventEmitter } from 'events'; import * as http2 from 'http2'; import { Duplex, Readable, Writable } from 'stream'; import * as zlib from 'zlib'; -import { promisify } from 'util'; import { Status, @@ -38,8 +37,6 @@ import { Deadline } from './deadline'; import { getErrorCode, getErrorMessage } from './error'; const TRACER_NAME = 'server_call'; -const unzip = promisify(zlib.unzip); -const inflate = promisify(zlib.inflate); function trace(text: string): void { logging.trace(LogVerbosity.DEBUG, TRACER_NAME, text); @@ -480,19 +477,42 @@ export class Http2ServerCallStream< private getDecompressedMessage( message: Buffer, encoding: string - ): Buffer | Promise { - if (encoding === 'deflate') { - return inflate(message.subarray(5)); - } else if (encoding === 'gzip') { - return unzip(message.subarray(5)); - } else if (encoding === 'identity') { - return message.subarray(5); + ): Buffer | Promise { const messageContents = message.subarray(5); + if (encoding === 'identity') { + return messageContents; + } else if (encoding === 'deflate' || encoding === 'gzip') { + let decompresser: zlib.Gunzip | zlib.Deflate; + if (encoding === 'deflate') { + decompresser = zlib.createInflate(); + } else { + decompresser = zlib.createGunzip(); + } + return new Promise((resolve, reject) => { + let totalLength = 0 + const messageParts: Buffer[] = []; + decompresser.on('data', (chunk: Buffer) => { + messageParts.push(chunk); + totalLength += chunk.byteLength; + if (this.maxReceiveMessageSize !== -1 && totalLength > this.maxReceiveMessageSize) { + decompresser.destroy(); + reject({ + code: Status.RESOURCE_EXHAUSTED, + details: `Received message that decompresses to a size larger than ${this.maxReceiveMessageSize}` + }); + } + }); + decompresser.on('end', () => { + resolve(Buffer.concat(messageParts)); + }); + decompresser.write(messageContents); + decompresser.end(); + }); + } else { + return Promise.reject({ + code: Status.UNIMPLEMENTED, + details: `Received message compressed with unsupported encoding "${encoding}"`, + }); } - - return Promise.reject({ - code: Status.UNIMPLEMENTED, - details: `Received message compressed with unsupported encoding "${encoding}"`, - }); } sendMetadata(customMetadata?: Metadata) { @@ -816,7 +836,7 @@ export class Http2ServerCallStream< | ServerDuplexStream, encoding: string ) { - const decoder = new StreamDecoder(); + const decoder = new StreamDecoder(this.maxReceiveMessageSize); let readsDone = false; @@ -832,29 +852,34 @@ export class Http2ServerCallStream< }; this.stream.on('data', async (data: Buffer) => { - const messages = decoder.write(data); + let messages: Buffer[]; + try { + messages = decoder.write(data); + } catch (e) { + this.sendError({ + code: Status.RESOURCE_EXHAUSTED, + details: (e as Error).message + }); + return; + } pendingMessageProcessing = true; this.stream.pause(); for (const message of messages) { - if ( - this.maxReceiveMessageSize !== -1 && - message.length > this.maxReceiveMessageSize - ) { - this.sendError({ - code: Status.RESOURCE_EXHAUSTED, - details: `Received message larger than max (${message.length} vs. ${this.maxReceiveMessageSize})`, - }); - return; - } this.emit('receiveMessage'); const compressed = message.readUInt8(0) === 1; const compressedMessageEncoding = compressed ? encoding : 'identity'; - const decompressedMessage = await this.getDecompressedMessage( - message, - compressedMessageEncoding - ); + let decompressedMessage: Buffer; + try { + decompressedMessage = await this.getDecompressedMessage( + message, + compressedMessageEncoding + ); + } catch (e) { + this.sendError(e as Partial); + return; + } // Encountered an error with decompression; it'll already have been propogated back // Just return early diff --git a/packages/grpc-js/src/stream-decoder.ts b/packages/grpc-js/src/stream-decoder.ts index 671ad41ae..ea669d14c 100644 --- a/packages/grpc-js/src/stream-decoder.ts +++ b/packages/grpc-js/src/stream-decoder.ts @@ -30,6 +30,8 @@ export class StreamDecoder { private readPartialMessage: Buffer[] = []; private readMessageRemaining = 0; + constructor(private maxReadMessageLength: number) {} + write(data: Buffer): Buffer[] { let readHead = 0; let toRead: number; @@ -60,6 +62,9 @@ export class StreamDecoder { // readSizeRemaining >=0 here if (this.readSizeRemaining === 0) { this.readMessageSize = this.readPartialSize.readUInt32BE(0); + if (this.maxReadMessageLength !== -1 && this.readMessageSize > this.maxReadMessageLength) { + throw new Error(`Received message larger than max (${this.readMessageSize} vs ${this.maxReadMessageLength})`); + } this.readMessageRemaining = this.readMessageSize; if (this.readMessageRemaining > 0) { this.readState = ReadState.READING_MESSAGE; diff --git a/packages/grpc-js/src/subchannel-call.ts b/packages/grpc-js/src/subchannel-call.ts index 3b9b6152f..b9f3191cc 100644 --- a/packages/grpc-js/src/subchannel-call.ts +++ b/packages/grpc-js/src/subchannel-call.ts @@ -18,7 +18,7 @@ import * as http2 from 'http2'; import * as os from 'os'; -import { Status } from './constants'; +import { DEFAULT_MAX_RECEIVE_MESSAGE_LENGTH, Status } from './constants'; import { Metadata } from './metadata'; import { StreamDecoder } from './stream-decoder'; import * as logging from './logging'; @@ -82,7 +82,7 @@ export interface SubchannelCallInterceptingListener } export class Http2SubchannelCall implements SubchannelCall { - private decoder = new StreamDecoder(); + private decoder: StreamDecoder; private isReadFilterPending = false; private isPushPending = false; @@ -112,6 +112,8 @@ export class Http2SubchannelCall implements SubchannelCall { private readonly transport: Transport, private readonly callId: number ) { + const maxReceiveMessageLength = transport.getOptions()['grpc.max_receive_message_length'] ?? DEFAULT_MAX_RECEIVE_MESSAGE_LENGTH; + this.decoder = new StreamDecoder(maxReceiveMessageLength); http2Stream.on('response', (headers, flags) => { let headersString = ''; for (const header of Object.keys(headers)) { @@ -169,7 +171,13 @@ export class Http2SubchannelCall implements SubchannelCall { return; } this.trace('receive HTTP/2 data frame of length ' + data.length); - const messages = this.decoder.write(data); + let messages: Buffer[]; + try { + messages = this.decoder.write(data); + } catch (e) { + this.cancelWithStatus(Status.RESOURCE_EXHAUSTED, (e as Error).message); + return; + } for (const message of messages) { this.trace('parsed message of length ' + message.length); diff --git a/packages/grpc-js/src/transport.ts b/packages/grpc-js/src/transport.ts index 39ca69383..fe9a81352 100644 --- a/packages/grpc-js/src/transport.ts +++ b/packages/grpc-js/src/transport.ts @@ -83,6 +83,7 @@ export interface TransportDisconnectListener { export interface Transport { getChannelzRef(): SocketRef; getPeerName(): string; + getOptions(): ChannelOptions; createCall( metadata: Metadata, host: string, @@ -146,7 +147,7 @@ class Http2Transport implements Transport { constructor( private session: http2.ClientHttp2Session, subchannelAddress: SubchannelAddress, - options: ChannelOptions, + private options: ChannelOptions, /** * Name of the remote server, if it is not the same as the subchannel * address, i.e. if connecting through an HTTP CONNECT proxy. @@ -601,6 +602,10 @@ class Http2Transport implements Transport { return this.subchannelAddressString; } + getOptions() { + return this.options; + } + shutdown() { this.session.close(); unregisterChannelzRef(this.channelzRef); diff --git a/packages/grpc-js/test/fixtures/test_service.proto b/packages/grpc-js/test/fixtures/test_service.proto index 64ce0d378..2a7a303f3 100644 --- a/packages/grpc-js/test/fixtures/test_service.proto +++ b/packages/grpc-js/test/fixtures/test_service.proto @@ -21,6 +21,7 @@ message Request { bool error = 1; string message = 2; int32 errorAfter = 3; + int32 responseLength = 4; } message Response { diff --git a/packages/grpc-js/test/test-server-errors.ts b/packages/grpc-js/test/test-server-errors.ts index 24ccfeef3..243e10918 100644 --- a/packages/grpc-js/test/test-server-errors.ts +++ b/packages/grpc-js/test/test-server-errors.ts @@ -33,6 +33,7 @@ import { } from '../src/server-call'; import { loadProtoFile } from './common'; +import { CompressionAlgorithms } from '../src/compression-algorithms'; const protoFile = join(__dirname, 'fixtures', 'test_service.proto'); const testServiceDef = loadProtoFile(protoFile); @@ -310,7 +311,7 @@ describe('Other conditions', () => { trailerMetadata ); } else { - cb(null, { count: 1 }, trailerMetadata); + cb(null, { count: 1, message: 'a'.repeat(req.responseLength) }, trailerMetadata); } }, @@ -320,6 +321,7 @@ describe('Other conditions', () => { ) { let count = 0; let errored = false; + let responseLength = 0; stream.on('data', (data: any) => { if (data.error) { @@ -327,13 +329,14 @@ describe('Other conditions', () => { errored = true; cb(new Error(message) as ServiceError, null, trailerMetadata); } else { + responseLength += data.responseLength; count++; } }); stream.on('end', () => { if (!errored) { - cb(null, { count }, trailerMetadata); + cb(null, { count, message: 'a'.repeat(responseLength) }, trailerMetadata); } }); }, @@ -349,7 +352,7 @@ describe('Other conditions', () => { }); } else { for (let i = 1; i <= 5; i++) { - stream.write({ count: i }); + stream.write({ count: i, message: 'a'.repeat(req.responseLength) }); if (req.errorAfter && req.errorAfter === i) { stream.emit('error', { code: grpc.status.UNKNOWN, @@ -376,7 +379,7 @@ describe('Other conditions', () => { err.metadata.add('count', '' + count); stream.emit('error', err); } else { - stream.write({ count }); + stream.write({ count, message: 'a'.repeat(data.responseLength) }); count++; } }); @@ -740,6 +743,44 @@ describe('Other conditions', () => { }); }); }); + + describe('Max message size', () => { + const largeMessage = 'a'.repeat(10_000_000); + it('Should be enforced on the server', done => { + client.unary({ message: largeMessage }, (error?: ServiceError) => { + assert(error); + assert.strictEqual(error.code, grpc.status.RESOURCE_EXHAUSTED); + done(); + }); + }); + it('Should be enforced on the client', done => { + client.unary({ responseLength: 10_000_000 }, (error?: ServiceError) => { + assert(error); + assert.strictEqual(error.code, grpc.status.RESOURCE_EXHAUSTED); + done(); + }); + }); + describe('Compressed messages', () => { + it('Should be enforced with gzip', done => { + const compressingClient = new testServiceClient(`localhost:${port}`, clientInsecureCreds, {'grpc.default_compression_algorithm': CompressionAlgorithms.gzip}); + compressingClient.unary({ message: largeMessage }, (error?: ServiceError) => { + assert(error); + assert.strictEqual(error.code, grpc.status.RESOURCE_EXHAUSTED); + assert.match(error.details, /Received message that decompresses to a size larger/); + done(); + }); + }); + it('Should be enforced with deflate', done => { + const compressingClient = new testServiceClient(`localhost:${port}`, clientInsecureCreds, {'grpc.default_compression_algorithm': CompressionAlgorithms.deflate}); + compressingClient.unary({ message: largeMessage }, (error?: ServiceError) => { + assert(error); + assert.strictEqual(error.code, grpc.status.RESOURCE_EXHAUSTED); + assert.match(error.details, /Received message that decompresses to a size larger/); + done(); + }); + }); + }); + }); }); function identity(arg: any): any { From c75e04894829ff5c0eac83a3eea96724ec7cd118 Mon Sep 17 00:00:00 2001 From: Michael Lumish Date: Fri, 7 Jun 2024 10:54:20 -0700 Subject: [PATCH 05/11] grpc-js: Bump to 1.9.15 --- packages/grpc-js/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grpc-js/package.json b/packages/grpc-js/package.json index 8d8f4fd90..0473c21ed 100644 --- a/packages/grpc-js/package.json +++ b/packages/grpc-js/package.json @@ -1,6 +1,6 @@ { "name": "@grpc/grpc-js", - "version": "1.9.14", + "version": "1.9.15", "description": "gRPC Library for Node - pure JS implementation", "homepage": "https://grpc.io/", "repository": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js", From 436baa2c8d919ba79ab7d8b8d58e97f4663ce749 Mon Sep 17 00:00:00 2001 From: Michael Lumish Date: Fri, 22 Nov 2024 14:35:40 -0800 Subject: [PATCH 06/11] grpc-js-xds: Update @types/node and update code for compatibility --- packages/grpc-js-xds/package.json | 2 +- packages/grpc-js-xds/src/load-balancer-priority.ts | 4 ++-- packages/grpc-js-xds/src/load-balancer-weighted-target.ts | 2 +- packages/grpc-js-xds/src/xds-client.ts | 4 ++-- packages/grpc-js-xds/test/client.ts | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/grpc-js-xds/package.json b/packages/grpc-js-xds/package.json index a6f0fcf1d..859e5b51a 100644 --- a/packages/grpc-js-xds/package.json +++ b/packages/grpc-js-xds/package.json @@ -36,7 +36,7 @@ "@types/gulp": "^4.0.6", "@types/gulp-mocha": "0.0.32", "@types/mocha": "^5.2.6", - "@types/node": "^13.11.1", + "@types/node": ">=20.11.20", "@types/yargs": "^15.0.5", "gts": "^2.0.2", "typescript": "^3.8.3", diff --git a/packages/grpc-js-xds/src/load-balancer-priority.ts b/packages/grpc-js-xds/src/load-balancer-priority.ts index a9d03d0a6..bb1fa3229 100644 --- a/packages/grpc-js-xds/src/load-balancer-priority.ts +++ b/packages/grpc-js-xds/src/load-balancer-priority.ts @@ -141,8 +141,8 @@ export class PriorityLoadBalancer implements LoadBalancer { private connectivityState: ConnectivityState = ConnectivityState.IDLE; private picker: Picker; private childBalancer: ChildLoadBalancerHandler; - private failoverTimer: NodeJS.Timer | null = null; - private deactivationTimer: NodeJS.Timer | null = null; + private failoverTimer: NodeJS.Timeout | null = null; + private deactivationTimer: NodeJS.Timeout | null = null; private seenReadyOrIdleSinceTransientFailure = false; constructor(private parent: PriorityLoadBalancer, private name: string, ignoreReresolutionRequests: boolean) { this.childBalancer = new ChildLoadBalancerHandler(experimental.createChildChannelControlHelper(this.parent.channelControlHelper, { diff --git a/packages/grpc-js-xds/src/load-balancer-weighted-target.ts b/packages/grpc-js-xds/src/load-balancer-weighted-target.ts index 7cd92d98b..1634575f6 100644 --- a/packages/grpc-js-xds/src/load-balancer-weighted-target.ts +++ b/packages/grpc-js-xds/src/load-balancer-weighted-target.ts @@ -149,7 +149,7 @@ export class WeightedTargetLoadBalancer implements LoadBalancer { private connectivityState: ConnectivityState = ConnectivityState.IDLE; private picker: Picker; private childBalancer: ChildLoadBalancerHandler; - private deactivationTimer: NodeJS.Timer | null = null; + private deactivationTimer: NodeJS.Timeout | null = null; private weight: number = 0; constructor(private parent: WeightedTargetLoadBalancer, private name: string) { diff --git a/packages/grpc-js-xds/src/xds-client.ts b/packages/grpc-js-xds/src/xds-client.ts index 020f767b5..28249b28f 100644 --- a/packages/grpc-js-xds/src/xds-client.ts +++ b/packages/grpc-js-xds/src/xds-client.ts @@ -102,7 +102,7 @@ export class Watcher implements ResourceWatcherInterface { const RESOURCE_TIMEOUT_MS = 15_000; class ResourceTimer { - private timer: NodeJS.Timer | null = null; + private timer: NodeJS.Timeout | null = null; private resourceSeen = false; constructor(private callState: AdsCallState, private type: XdsResourceType, private name: XdsResourceName) {} @@ -666,7 +666,7 @@ class ClusterLoadReportMap { } class LrsCallState { - private statsTimer: NodeJS.Timer | null = null; + private statsTimer: NodeJS.Timeout | null = null; private sentInitialMessage = false; constructor(private client: XdsSingleServerClient, private call: LrsCall, private node: Node) { call.on('data', (message: LoadStatsResponse__Output) => { diff --git a/packages/grpc-js-xds/test/client.ts b/packages/grpc-js-xds/test/client.ts index 0779702bb..ac04a2c48 100644 --- a/packages/grpc-js-xds/test/client.ts +++ b/packages/grpc-js-xds/test/client.ts @@ -42,7 +42,7 @@ const BOOTSTRAP_CONFIG_KEY = 'grpc.TEST_ONLY_DO_NOT_USE_IN_PROD.xds_bootstrap_co export class XdsTestClient { private client: EchoTestServiceClient; - private callInterval: NodeJS.Timer; + private callInterval: NodeJS.Timeout; constructor(target: string, bootstrapInfo: string, options?: ChannelOptions) { this.client = new loadedProtos.grpc.testing.EchoTestService(target, credentials.createInsecure(), {...options, [BOOTSTRAP_CONFIG_KEY]: bootstrapInfo}); From b5480496a03b05dacf006c28602309673925087f Mon Sep 17 00:00:00 2001 From: Michael Lumish Date: Mon, 2 Dec 2024 12:18:59 -0500 Subject: [PATCH 07/11] grpc-js-xds: Update to TypeScript 5 --- packages/grpc-js-xds/package.json | 2 +- packages/grpc-js-xds/src/http-filter.ts | 4 ++-- packages/grpc-js-xds/src/load-balancer-cds.ts | 6 +++--- packages/grpc-js-xds/src/matcher.ts | 4 ++-- packages/grpc-js-xds/src/resolver-xds.ts | 4 ++-- packages/grpc-js-xds/src/xds-bootstrap.ts | 8 ++++---- packages/grpc-js-xds/src/xds-client.ts | 4 ++-- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/grpc-js-xds/package.json b/packages/grpc-js-xds/package.json index 859e5b51a..fb8f17003 100644 --- a/packages/grpc-js-xds/package.json +++ b/packages/grpc-js-xds/package.json @@ -39,7 +39,7 @@ "@types/node": ">=20.11.20", "@types/yargs": "^15.0.5", "gts": "^2.0.2", - "typescript": "^3.8.3", + "typescript": "^5.1.3", "yargs": "^15.4.1" }, "dependencies": { diff --git a/packages/grpc-js-xds/src/http-filter.ts b/packages/grpc-js-xds/src/http-filter.ts index 29ce5958f..f8da5b828 100644 --- a/packages/grpc-js-xds/src/http-filter.ts +++ b/packages/grpc-js-xds/src/http-filter.ts @@ -116,7 +116,7 @@ export function validateTopLevelFilter(httpFilter: HttpFilter__Output): boolean try { typeUrl = getTopLevelFilterUrl(encodedConfig); } catch (e) { - trace(httpFilter.name + ' validation failed with error ' + e.message); + trace(httpFilter.name + ' validation failed with error ' + (e as Error).message); return false; } const registryEntry = FILTER_REGISTRY.get(typeUrl); @@ -243,4 +243,4 @@ export function createHttpFilter(config: HttpFilterConfig, overrideConfig?: Http } else { return null; } -} \ No newline at end of file +} diff --git a/packages/grpc-js-xds/src/load-balancer-cds.ts b/packages/grpc-js-xds/src/load-balancer-cds.ts index 6f791299c..ce4e2553e 100644 --- a/packages/grpc-js-xds/src/load-balancer-cds.ts +++ b/packages/grpc-js-xds/src/load-balancer-cds.ts @@ -141,8 +141,8 @@ const RECURSION_DEPTH_LIMIT = 15; /** * Prerequisite: isClusterTreeFullyUpdated(tree, root) - * @param tree - * @param root + * @param tree + * @param root */ function getDiscoveryMechanismList(tree: ClusterTree, root: string): DiscoveryMechanism[] { const visited = new Set(); @@ -208,7 +208,7 @@ export class CdsLoadBalancer implements LoadBalancer { try { discoveryMechanismList = getDiscoveryMechanismList(this.clusterTree, this.latestConfig!.getCluster()); } catch (e) { - this.channelControlHelper.updateState(connectivityState.TRANSIENT_FAILURE, new UnavailablePicker({code: status.UNAVAILABLE, details: e.message, metadata: new Metadata()})); + this.channelControlHelper.updateState(connectivityState.TRANSIENT_FAILURE, new UnavailablePicker({code: status.UNAVAILABLE, details: (e as Error).message, metadata: new Metadata()})); return; } const clusterResolverConfig = new XdsClusterResolverLoadBalancingConfig( diff --git a/packages/grpc-js-xds/src/matcher.ts b/packages/grpc-js-xds/src/matcher.ts index 148df7f85..b657d32c9 100644 --- a/packages/grpc-js-xds/src/matcher.ts +++ b/packages/grpc-js-xds/src/matcher.ts @@ -71,7 +71,7 @@ export class SafeRegexValueMatcher implements ValueMatcher { const numberRegex = new RE2(/^-?\d+$/u); export class RangeValueMatcher implements ValueMatcher { - constructor(private start: BigInt, private end: BigInt) {} + constructor(private start: bigint, private end: bigint) {} apply(value: string) { if (!numberRegex.test(value)) { @@ -264,4 +264,4 @@ export class FullMatcher implements Matcher { headers: ${this.headerMatchers.map(matcher => matcher.toString()).join('\n\t')} fraction: ${this.fraction ? fractionToString(this.fraction): 'none'}`; } -} \ No newline at end of file +} diff --git a/packages/grpc-js-xds/src/resolver-xds.ts b/packages/grpc-js-xds/src/resolver-xds.ts index 17d3c6305..e6a0f2efd 100644 --- a/packages/grpc-js-xds/src/resolver-xds.ts +++ b/packages/grpc-js-xds/src/resolver-xds.ts @@ -634,7 +634,7 @@ class XdsResolver implements Resolver { this.isLdsWatcherActive = true; } catch (e) { - this.reportResolutionError(e.message); + this.reportResolutionError((e as Error).message); } } } @@ -647,7 +647,7 @@ class XdsResolver implements Resolver { try { this.bootstrapInfo = loadBootstrapInfo(); } catch (e) { - this.reportResolutionError(e.message); + this.reportResolutionError((e as Error).message); } this.startResolution(); } diff --git a/packages/grpc-js-xds/src/xds-bootstrap.ts b/packages/grpc-js-xds/src/xds-bootstrap.ts index 327ee1b06..1ef94aa74 100644 --- a/packages/grpc-js-xds/src/xds-bootstrap.ts +++ b/packages/grpc-js-xds/src/xds-bootstrap.ts @@ -354,14 +354,14 @@ export function loadBootstrapInfo(): BootstrapInfo { try { rawBootstrap = fs.readFileSync(bootstrapPath, { encoding: 'utf8'}); } catch (e) { - throw new Error(`Failed to read xDS bootstrap file from path ${bootstrapPath} with error ${e.message}`); + throw new Error(`Failed to read xDS bootstrap file from path ${bootstrapPath} with error ${(e as Error).message}`); } try { const parsedFile = JSON.parse(rawBootstrap); loadedBootstrapInfo = validateBootstrapConfig(parsedFile); return loadedBootstrapInfo; } catch (e) { - throw new Error(`Failed to parse xDS bootstrap file at path ${bootstrapPath} with error ${e.message}`) + throw new Error(`Failed to parse xDS bootstrap file at path ${bootstrapPath} with error ${(e as Error).message}`) } } @@ -380,14 +380,14 @@ export function loadBootstrapInfo(): BootstrapInfo { loadedBootstrapInfo = validateBootstrapConfig(parsedConfig); } catch (e) { throw new Error( - `Failed to parse xDS bootstrap config from environment variable GRPC_XDS_BOOTSTRAP_CONFIG with error ${e.message}` + `Failed to parse xDS bootstrap config from environment variable GRPC_XDS_BOOTSTRAP_CONFIG with error ${(e as Error).message}` ); } return loadedBootstrapInfo; } - + throw new Error( 'The GRPC_XDS_BOOTSTRAP or GRPC_XDS_BOOTSTRAP_CONFIG environment variables need to be set to the path to the bootstrap file to use xDS' ); diff --git a/packages/grpc-js-xds/src/xds-client.ts b/packages/grpc-js-xds/src/xds-client.ts index 28249b28f..8d354f1f7 100644 --- a/packages/grpc-js-xds/src/xds-client.ts +++ b/packages/grpc-js-xds/src/xds-client.ts @@ -208,14 +208,14 @@ class AdsResponseParser { try { decodeResult = this.result.type.decode(decodeContext, resource); } catch (e) { - this.result.errors.push(`${errorPrefix} ${e.message}`); + this.result.errors.push(`${errorPrefix} ${(e as Error).message}`); return; } let parsedName: XdsResourceName; try { parsedName = parseXdsResourceName(decodeResult.name, this.result.type!.getTypeUrl()); } catch (e) { - this.result.errors.push(`${errorPrefix} ${e.message}`); + this.result.errors.push(`${errorPrefix} ${(e as Error).message}`); return; } this.adsCallState.typeStates.get(this.result.type!)?.subscribedResources.get(parsedName.authority)?.get(parsedName.key)?.markSeen(); From adda74732dde4bfb453080179b611fd630cb0759 Mon Sep 17 00:00:00 2001 From: Michael Lumish Date: Wed, 13 Nov 2024 17:37:33 -0800 Subject: [PATCH 08/11] grpc-js{,-xds}: Renew self-signed certificates used in tests --- packages/grpc-js/test/fixtures/ca.pem | 28 +++++++++++---------- packages/grpc-js/test/fixtures/server1.pem | 29 +++++++++++----------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/packages/grpc-js/test/fixtures/ca.pem b/packages/grpc-js/test/fixtures/ca.pem index 6c8511a73..9eb75fa0f 100644 --- a/packages/grpc-js/test/fixtures/ca.pem +++ b/packages/grpc-js/test/fixtures/ca.pem @@ -1,15 +1,17 @@ -----BEGIN CERTIFICATE----- -MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla -Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 -YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT -BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7 -+L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu -g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd -Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV -HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau -sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m -oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG -Dfcog5wrJytaQ6UA0wE= +MIICzDCCAjWgAwIBAgIBBzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJVUzER +MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xFTATBgNVBAoTDEV4 +YW1wbGUsIENvLjEaMBgGA1UEAxQRKi50ZXN0Lmdvb2dsZS5jb20wHhcNMjQxMTE0 +MDEzMjM3WhcNNDQxMTA5MDEzMjM3WjBlMQswCQYDVQQGEwJVUzERMA8GA1UECBMI +SWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xFTATBgNVBAoTDEV4YW1wbGUsIENv +LjEaMBgGA1UEAxQRKi50ZXN0Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD +gY0AMIGJAoGBAOHDFScoLCVJpYDDM4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlu +mN+fm+AjPEK5GHhGn1BgzkWF+slf3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wH +NVX77fBZOgp9VlSMVfyd9N8YwbBYAckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAGj +gYswgYgwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0 +Lmdvb2dsZS5mcoIYd2F0ZXJ6b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91 +dHViZS5jb22HBMCoAQMwHQYDVR0OBBYEFI50QV/hcKkD2qyS/Melvq16+zY8MA0G +CSqGSIb3DQEBCwUAA4GBAIxvZ4kg9HAvzM31p4J04h1MFfnDx8O4Hwogzpaqk+0M +qz8L1ojJy5jus1g8+RBguT1rv5TmRFpMWrp50XQ0bMFHoOcNyL0htxoOhmoKHoX9 +dM0KWtgUheeBcEm83UzFFlGKlna22+pdUHLEnuX+i25s+Lbi4/LGf6KwUlgPL/Vk -----END CERTIFICATE----- diff --git a/packages/grpc-js/test/fixtures/server1.pem b/packages/grpc-js/test/fixtures/server1.pem index f3d43fcc5..9eb75fa0f 100644 --- a/packages/grpc-js/test/fixtures/server1.pem +++ b/packages/grpc-js/test/fixtures/server1.pem @@ -1,16 +1,17 @@ -----BEGIN CERTIFICATE----- -MIICnDCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJBVTET -MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ -dHkgTHRkMQ8wDQYDVQQDEwZ0ZXN0Y2EwHhcNMTUxMTA0MDIyMDI0WhcNMjUxMTAx -MDIyMDI0WjBlMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV -BAcTB0NoaWNhZ28xFTATBgNVBAoTDEV4YW1wbGUsIENvLjEaMBgGA1UEAxQRKi50 -ZXN0Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOHDFSco -LCVJpYDDM4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1Bg -zkWF+slf3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd -9N8YwbBYAckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAGjazBpMAkGA1UdEwQCMAAw -CwYDVR0PBAQDAgXgME8GA1UdEQRIMEaCECoudGVzdC5nb29nbGUuZnKCGHdhdGVy -em9vaS50ZXN0Lmdvb2dsZS5iZYISKi50ZXN0LnlvdXR1YmUuY29thwTAqAEDMA0G -CSqGSIb3DQEBCwUAA4GBAJFXVifQNub1LUP4JlnX5lXNlo8FxZ2a12AFQs+bzoJ6 -hM044EDjqyxUqSbVePK0ni3w1fHQB5rY9yYC5f8G7aqqTY1QOhoUk8ZTSTRpnkTh -y4jjdvTZeLDVBlueZUTDRmy2feY5aZIU18vFDK08dTG0A87pppuv1LNIR3loveU8 +MIICzDCCAjWgAwIBAgIBBzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJVUzER +MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xFTATBgNVBAoTDEV4 +YW1wbGUsIENvLjEaMBgGA1UEAxQRKi50ZXN0Lmdvb2dsZS5jb20wHhcNMjQxMTE0 +MDEzMjM3WhcNNDQxMTA5MDEzMjM3WjBlMQswCQYDVQQGEwJVUzERMA8GA1UECBMI +SWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xFTATBgNVBAoTDEV4YW1wbGUsIENv +LjEaMBgGA1UEAxQRKi50ZXN0Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD +gY0AMIGJAoGBAOHDFScoLCVJpYDDM4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlu +mN+fm+AjPEK5GHhGn1BgzkWF+slf3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wH +NVX77fBZOgp9VlSMVfyd9N8YwbBYAckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAGj +gYswgYgwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0 +Lmdvb2dsZS5mcoIYd2F0ZXJ6b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91 +dHViZS5jb22HBMCoAQMwHQYDVR0OBBYEFI50QV/hcKkD2qyS/Melvq16+zY8MA0G +CSqGSIb3DQEBCwUAA4GBAIxvZ4kg9HAvzM31p4J04h1MFfnDx8O4Hwogzpaqk+0M +qz8L1ojJy5jus1g8+RBguT1rv5TmRFpMWrp50XQ0bMFHoOcNyL0htxoOhmoKHoX9 +dM0KWtgUheeBcEm83UzFFlGKlna22+pdUHLEnuX+i25s+Lbi4/LGf6KwUlgPL/Vk -----END CERTIFICATE----- From b34ba40afa0d4bab5a6976fcbc11e96efed192ee Mon Sep 17 00:00:00 2001 From: Michael Lumish Date: Mon, 9 Dec 2024 15:16:43 -0500 Subject: [PATCH 09/11] grpc-js-xds: Update gts to be compatible with typescript version --- packages/grpc-js-xds/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grpc-js-xds/package.json b/packages/grpc-js-xds/package.json index fb8f17003..06cbacfe5 100644 --- a/packages/grpc-js-xds/package.json +++ b/packages/grpc-js-xds/package.json @@ -38,7 +38,7 @@ "@types/mocha": "^5.2.6", "@types/node": ">=20.11.20", "@types/yargs": "^15.0.5", - "gts": "^2.0.2", + "gts": "^5.0.1", "typescript": "^5.1.3", "yargs": "^15.4.1" }, From 760fc4252ed97fe25e37bdc45fcac15eca66e32a Mon Sep 17 00:00:00 2001 From: Michael Lumish Date: Tue, 7 Jan 2025 14:24:25 -0800 Subject: [PATCH 10/11] grpc-js-xds: Preserve resource type version and nonce when unsubscribing --- packages/grpc-js-xds/src/xds-client.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/grpc-js-xds/src/xds-client.ts b/packages/grpc-js-xds/src/xds-client.ts index 8d354f1f7..b1f028628 100644 --- a/packages/grpc-js-xds/src/xds-client.ts +++ b/packages/grpc-js-xds/src/xds-client.ts @@ -451,9 +451,6 @@ class AdsCallState { if (authorityMap.size === 0) { typeState.subscribedResources.delete(name.authority); } - if (typeState.subscribedResources.size === 0) { - this.typeStates.delete(type); - } this.updateNames(type); } From b9797cf489b28b2ab68bc4b3d3143178d8de7a1a Mon Sep 17 00:00:00 2001 From: Michael Lumish Date: Fri, 1 Aug 2025 09:50:14 -0700 Subject: [PATCH 11/11] grpc-js: Declare buffer type to avoid build error --- packages/grpc-js/src/compression-filter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grpc-js/src/compression-filter.ts b/packages/grpc-js/src/compression-filter.ts index f1600b36d..a0aa6daf8 100644 --- a/packages/grpc-js/src/compression-filter.ts +++ b/packages/grpc-js/src/compression-filter.ts @@ -65,7 +65,7 @@ abstract class CompressionHandler { */ async readMessage(data: Buffer): Promise { const compressed = data.readUInt8(0) === 1; - let messageBuffer = data.slice(5); + let messageBuffer: Buffer = data.slice(5); if (compressed) { messageBuffer = await this.decompressMessage(messageBuffer); }