diff --git a/.build/alpine.yaml b/.build/alpine.yaml deleted file mode 100644 index ed5647532..000000000 --- a/.build/alpine.yaml +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2020 Google LLC -# -# 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. - -steps: -- name: gcr.io/cloud-builders/docker - args: - - run - - '--privileged' - - 'linuxkit/binfmt:v0.7' - id: 'initialize-qemu' -- name: gcr.io/cloud-builders/docker - args: - - buildx - - create - - '--name' - - multiarch-builder - id: 'create-builder' -- name: gcr.io/cloud-builders/docker - args: - - buildx - - use - - multiarch-builder - id: 'select-builder' -- name: gcr.io/cloud-builders/docker - args: - - buildx - - inspect - - '--bootstrap' - id: 'show-target-build-platforms' -- name: 'gcr.io/cloud-builders/docker' - args: - - 'buildx' - - 'build' - - '--platform' - - $_DOCKER_BUILDX_PLATFORMS - - '--tag=gcr.io/$PROJECT_ID/cloud-sql-proxy:${_VERSION}-alpine' - - '--tag=us.gcr.io/$PROJECT_ID/cloud-sql-proxy:${_VERSION}-alpine' - - '--tag=eu.gcr.io/$PROJECT_ID/cloud-sql-proxy:${_VERSION}-alpine' - - '--tag=asia.gcr.io/$PROJECT_ID/cloud-sql-proxy:${_VERSION}-alpine' - - '-f=Dockerfile.alpine' - - '--push' - - '.' - id: 'build-multi-architecture-container-image' -options: - env: - - DOCKER_CLI_EXPERIMENTAL=enabled -substitutions: - _DOCKER_BUILDX_PLATFORMS: 'linux/amd64,linux/arm64' diff --git a/.build/bullseye.yaml b/.build/bullseye.yaml deleted file mode 100644 index ad4de2e06..000000000 --- a/.build/bullseye.yaml +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2022 Google LLC -# -# 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. - -steps: -- name: gcr.io/cloud-builders/docker - args: - - run - - '--privileged' - - 'linuxkit/binfmt:v0.7' - id: 'initialize-qemu' -- name: gcr.io/cloud-builders/docker - args: - - buildx - - create - - '--name' - - multiarch-builder - id: 'create-builder' -- name: gcr.io/cloud-builders/docker - args: - - buildx - - use - - multiarch-builder - id: 'select-builder' -- name: gcr.io/cloud-builders/docker - args: - - buildx - - inspect - - '--bootstrap' - id: 'show-target-build-platforms' -- name: 'gcr.io/cloud-builders/docker' - args: - - 'buildx' - - 'build' - - '--platform' - - $_DOCKER_BUILDX_PLATFORMS - - '--tag=gcr.io/$PROJECT_ID/gce-proxy:${_VERSION}-bullseye' - - '--tag=us.gcr.io/$PROJECT_ID/gce-proxy:${_VERSION}-bullseye' - - '--tag=eu.gcr.io/$PROJECT_ID/gce-proxy:${_VERSION}-bullseye' - - '--tag=asia.gcr.io/$PROJECT_ID/gce-proxy:${_VERSION}-bullseye' - - '-f=Dockerfile.bullseye' - - '--push' - - '.' - id: 'build-multi-architecture-container-image' -options: - env: - - DOCKER_CLI_EXPERIMENTAL=enabled -substitutions: - _DOCKER_BUILDX_PLATFORMS: 'linux/amd64' diff --git a/.build/buster.yaml b/.build/buster.yaml deleted file mode 100644 index 505f61d8e..000000000 --- a/.build/buster.yaml +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2020 Google LLC -# -# 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. - -steps: -- name: gcr.io/cloud-builders/docker - args: - - run - - '--privileged' - - 'linuxkit/binfmt:v0.7' - id: 'initialize-qemu' -- name: gcr.io/cloud-builders/docker - args: - - buildx - - create - - '--name' - - multiarch-builder - id: 'create-builder' -- name: gcr.io/cloud-builders/docker - args: - - buildx - - use - - multiarch-builder - id: 'select-builder' -- name: gcr.io/cloud-builders/docker - args: - - buildx - - inspect - - '--bootstrap' - id: 'show-target-build-platforms' -- name: 'gcr.io/cloud-builders/docker' - args: - - 'buildx' - - 'build' - - '--platform' - - $_DOCKER_BUILDX_PLATFORMS - - '--tag=gcr.io/$PROJECT_ID/cloud-sql-proxy:${_VERSION}-buster' - - '--tag=us.gcr.io/$PROJECT_ID/cloud-sql-proxy:${_VERSION}-buster' - - '--tag=eu.gcr.io/$PROJECT_ID/cloud-sql-proxy:${_VERSION}-buster' - - '--tag=asia.gcr.io/$PROJECT_ID/cloud-sql-proxy:${_VERSION}-buster' - - '-f=Dockerfile.buster' - - '--push' - - '.' - id: 'build-multi-architecture-container-image' -options: - env: - - DOCKER_CLI_EXPERIMENTAL=enabled -substitutions: - _DOCKER_BUILDX_PLATFORMS: 'linux/amd64,linux/arm64' diff --git a/.build/default.yaml b/.build/default.yaml deleted file mode 100644 index 5824a9005..000000000 --- a/.build/default.yaml +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright 2020 Google LLC -# -# 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. - -steps: -- name: gcr.io/cloud-builders/docker - args: - - run - - '--privileged' - - 'linuxkit/binfmt:v0.7' - id: 'initialize-qemu' -- name: gcr.io/cloud-builders/docker - args: - - buildx - - create - - '--name' - - multiarch-builder - id: 'create-builder' -- name: gcr.io/cloud-builders/docker - args: - - buildx - - use - - multiarch-builder - id: 'select-builder' -- name: gcr.io/cloud-builders/docker - args: - - buildx - - inspect - - '--bootstrap' - id: 'show-target-build-platforms' -- name: 'gcr.io/cloud-builders/docker' - args: - - 'buildx' - - 'build' - - '--platform' - - $_DOCKER_BUILDX_PLATFORMS - - '--tag=gcr.io/$PROJECT_ID/cloud-sql-proxy:${_VERSION}' - - '--tag=us.gcr.io/$PROJECT_ID/cloud-sql-proxy:${_VERSION}' - - '--tag=eu.gcr.io/$PROJECT_ID/cloud-sql-proxy:${_VERSION}' - - '--tag=asia.gcr.io/$PROJECT_ID/cloud-sql-proxy:${_VERSION}' - - '--push' - - '.' - id: 'build-multi-architecture-container-image' -options: - env: - - DOCKER_CLI_EXPERIMENTAL=enabled -substitutions: - _DOCKER_BUILDX_PLATFORMS: 'linux/amd64,linux/arm64' diff --git a/.build/gcs_upload.yaml b/.build/gcs_upload.yaml deleted file mode 100644 index d9f65e3c8..000000000 --- a/.build/gcs_upload.yaml +++ /dev/null @@ -1,98 +0,0 @@ -# Copyright 2020 Google LLC -# -# 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. - -timeout: 1800s -options: - env: - - "GOPATH=/workspace/GOPATH" - - "CGO_ENABLED=0" - -steps: - - id: linux.amd64 - name: "golang:1.19" - env: - - "GOOS=linux" - - "GOARCH=amd64" - entrypoint: "bash" - args: - - "-c" - - 'go build -ldflags "-X main.versionString=${_VERSION} -X main.metadataString=$$GOOS.$$GOARCH" -o cloud-sql-proxy.$$GOOS.$$GOARCH' - - id: linux.386 - name: "golang:1.19" - env: - - "GOOS=linux" - - "GOARCH=386" - entrypoint: "bash" - args: - - "-c" - - 'go build -ldflags "-X main.versionString=${_VERSION} -X main.metadataString=$$GOOS.$$GOARCH" -o cloud-sql-proxy.$$GOOS.$$GOARCH' - - id: linux.arm64 - name: "golang:1.19" - env: - - "GOOS=linux" - - "GOARCH=arm64" - entrypoint: "bash" - args: - - "-c" - - 'go build -ldflags "-X main.versionString=${_VERSION} -X main.metadataString=$$GOOS.$$GOARCH" -o cloud-sql-proxy.$$GOOS.$$GOARCH' - - id: linux.arm - name: "golang:1.19" - env: - - "GOOS=linux" - - "GOARCH=arm" - entrypoint: "bash" - args: - - "-c" - - 'go build -ldflags "-X main.versionString=${_VERSION} -X main.metadataString=$$GOOS.$$GOARCH" -o cloud-sql-proxy.$$GOOS.$$GOARCH' - - id: darwin.amd64 - name: "golang:1.19" - env: - - "GOOS=darwin" - - "GOARCH=amd64" - entrypoint: "bash" - args: - - "-c" - - 'go build -ldflags "-X main.versionString=${_VERSION} -X main.metadataString=$$GOOS.$$GOARCH" -o cloud-sql-proxy.$$GOOS.$$GOARCH' - - id: darwin.arm64 - name: "golang:1.19" - env: - - "GOOS=darwin" - - "GOARCH=arm64" - entrypoint: "bash" - args: - - "-c" - - 'go build -ldflags "-X main.versionString=${_VERSION} -X main.metadataString=$$GOOS.$$GOARCH" -o cloud-sql-proxy.$$GOOS.$$GOARCH' - - id: windows.amd64 - name: "golang:1.19" - env: - - "GOOS=windows" - - "GOARCH=amd64" - entrypoint: "bash" - args: - - "-c" - - 'go build -ldflags "-X main.versionString=${_VERSION} -X main.metadataString=$$GOOS.$$GOARCH" -o cloud-sql-proxy.x64.exe' - - id: windows.386 - name: "golang:1.19" - env: - - "GOOS=windows" - - "GOARCH=386" - entrypoint: "bash" - args: - - "-c" - - 'go build -ldflags "-X main.versionString=${_VERSION} -X main.metadataString=$$GOOS.$$GOARCH" -o cloud-sql-proxy.x86.exe' -artifacts: - objects: - location: "gs://cloud-sql-connectors/cloud-sql-proxy/v${_VERSION}/" - paths: - - "cloud-sql-proxy*" diff --git a/.build/release_artifacts.sh b/.build/release_artifacts.sh deleted file mode 100755 index bbb2c4ac2..000000000 --- a/.build/release_artifacts.sh +++ /dev/null @@ -1,69 +0,0 @@ -#! /bin/bash -# Copyright 2020 Google LLC -# -# 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. - -# This script distributes the artifacts for the Cloud SQL proxy to their different channels. - -set -e # exit immediatly if any step fails - -PROJ_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/.. >/dev/null 2>&1 && pwd )" -cd $PROJ_ROOT - -# get the current version -export VERSION=$(cat cmd/version.txt) -if [ -z "$VERSION" ]; then - echo "error: No version.txt found in $PROJ_ROOT" - exit 1 -fi - - -read -p "This will release new Cloud SQL proxy artifacts for \"$VERSION\", even if they already exist. Are you sure (y/Y)? " -n 1 -r -echo -if [[ ! $REPLY =~ ^[Yy]$ ]] -then - exit 1 -fi - -# Build and push the container images -gcloud builds submit --async --config .build/default.yaml --substitutions _VERSION=$VERSION -gcloud builds submit --async --config .build/alpine.yaml --substitutions _VERSION=$VERSION -gcloud builds submit --async --config .build/buster.yaml --substitutions _VERSION=$VERSION -gcloud builds submit --async --config .build/bullseye.yaml --substitutions _VERSION=$VERSION - -# Build the binarys and upload to GCS -gcloud builds submit --config .build/gcs_upload.yaml --substitutions _VERSION=$VERSION -# cleam up any artifacts.json left by previous builds -gsutil rm -f gs://cloud-sql-connectors/cloud-sql-proxy/v$VERSION/*.json 2> /dev/null || true - -# Generate sha256 hashes for authentication -echo -e "Add the following table to the release notes on GitHub: \n\n" -echo "| filename | sha256 hash |" -echo "|----------|-------------|" -for f in $(gsutil ls "gs://cloud-sql-connectors/cloud-sql-proxy/v$VERSION/cloud-sql-proxy*"); do - file=$(basename $f) - sha=$(gsutil cat $f | sha256sum --binary | head -c 64) - echo "| [$file](https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v$VERSION/$file) | $sha |" -done - -tag_latest() { - local new_version=$1 - for registry in "gcr.io" "us.gcr.io" "eu.gcr.io" "asia.gcr.io" - do - local base_image="$registry/cloud-sql-connectors/cloud-sql-proxy" - echo "Tagging $new_version as latest in $registry" - gcloud container images add-tag --quiet "$base_image:$new_version" "$base_image:latest" - done -} - -tag_latest $VERSION diff --git a/.ci/cloudbuild.yaml b/.ci/cloudbuild.yaml new file mode 100644 index 000000000..2d94f7213 --- /dev/null +++ b/.ci/cloudbuild.yaml @@ -0,0 +1,114 @@ +# Copyright 2025 Google LLC +# +# 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. + +steps: + + - id: run integration tests + name: golang:${_VERSION} + env: + [ + "IP_TYPE=${_IP_TYPE}", + "GOOGLE_CLOUD_PROJECT=${PROJECT_ID}", + "TMPDIR=/tmp" + ] + secretEnv: + [ + "MYSQL_CONNECTION_NAME", + "MYSQL_USER", + "MYSQL_PASS", + "MYSQL_DB", + "MYSQL_MCP_CONNECTION_NAME", + "MYSQL_MCP_PASS", + "POSTGRES_CONNECTION_NAME", + "POSTGRES_USER", + "POSTGRES_USER_IAM", + "POSTGRES_PASS", + "POSTGRES_DB", + "POSTGRES_CAS_CONNECTION_NAME", + "POSTGRES_CAS_PASS", + "POSTGRES_CUSTOMER_CAS_CONNECTION_NAME", + "POSTGRES_CUSTOMER_CAS_PASS", + "POSTGRES_CUSTOMER_CAS_DOMAIN_NAME", + "POSTGRES_MCP_CONNECTION_NAME", + "POSTGRES_MCP_PASS", + "SQLSERVER_CONNECTION_NAME", + "SQLSERVER_USER", + "SQLSERVER_PASS", + "SQLSERVER_DB", + "IMPERSONATED_USER", + ] + entrypoint: bash + args: + - -c + - | + go test -race -v ./tests/... + +substitutions: + _VERSION: ${_VERSION} + _IP_TYPE: ${_IP_TYPE} + +availableSecrets: + secretManager: + - versionName: "projects/$PROJECT_ID/secrets/MYSQL_CONNECTION_NAME/versions/latest" + env: "MYSQL_CONNECTION_NAME" + - versionName: "projects/$PROJECT_ID/secrets/MYSQL_USER/versions/latest" + env: "MYSQL_USER" + - versionName: "projects/$PROJECT_ID/secrets/MYSQL_PASS/versions/latest" + env: "MYSQL_PASS" + - versionName: "projects/$PROJECT_ID/secrets/MYSQL_DB/versions/latest" + env: "MYSQL_DB" + - versionName: "projects/$PROJECT_ID/secrets/MYSQL_MCP_CONNECTION_NAME/versions/latest" + env: "MYSQL_MCP_CONNECTION_NAME" + - versionName: "projects/$PROJECT_ID/secrets/MYSQL_MCP_PASS/versions/latest" + env: "MYSQL_MCP_PASS" + - versionName: "projects/$PROJECT_ID/secrets/POSTGRES_CONNECTION_NAME/versions/latest" + env: "POSTGRES_CONNECTION_NAME" + - versionName: "projects/$PROJECT_ID/secrets/POSTGRES_USER/versions/latest" + env: "POSTGRES_USER" + - versionName: "projects/$PROJECT_ID/secrets/CLOUD_BUILD_POSTGRES_IAM_USER/versions/latest" + env: "POSTGRES_USER_IAM" + - versionName: "projects/$PROJECT_ID/secrets/POSTGRES_PASS/versions/latest" + env: "POSTGRES_PASS" + - versionName: "projects/$PROJECT_ID/secrets/POSTGRES_DB/versions/latest" + env: "POSTGRES_DB" + - versionName: "projects/$PROJECT_ID/secrets/POSTGRES_CAS_CONNECTION_NAME/versions/latest" + env: "POSTGRES_CAS_CONNECTION_NAME" + - versionName: "projects/$PROJECT_ID/secrets/POSTGRES_CAS_PASS/versions/latest" + env: "POSTGRES_CAS_PASS" + - versionName: "projects/$PROJECT_ID/secrets/POSTGRES_CUSTOMER_CAS_CONNECTION_NAME/versions/latest" + env: "POSTGRES_CUSTOMER_CAS_CONNECTION_NAME" + - versionName: "projects/$PROJECT_ID/secrets/POSTGRES_CUSTOMER_CAS_PASS/versions/latest" + env: "POSTGRES_CUSTOMER_CAS_PASS" + - versionName: "projects/$PROJECT_ID/secrets/POSTGRES_CUSTOMER_CAS_DOMAIN_NAME/versions/latest" + env: "POSTGRES_CUSTOMER_CAS_DOMAIN_NAME" + - versionName: "projects/$PROJECT_ID/secrets/POSTGRES_MCP_CONNECTION_NAME/versions/latest" + env: "POSTGRES_MCP_CONNECTION_NAME" + - versionName: "projects/$PROJECT_ID/secrets/POSTGRES_MCP_PASS/versions/latest" + env: "POSTGRES_MCP_PASS" + - versionName: "projects/$PROJECT_ID/secrets/SQLSERVER_CONNECTION_NAME/versions/latest" + env: "SQLSERVER_CONNECTION_NAME" + - versionName: "projects/$PROJECT_ID/secrets/SQLSERVER_USER/versions/latest" + env: "SQLSERVER_USER" + - versionName: "projects/$PROJECT_ID/secrets/SQLSERVER_PASS/versions/latest" + env: "SQLSERVER_PASS" + - versionName: "projects/$PROJECT_ID/secrets/SQLSERVER_DB/versions/latest" + env: "SQLSERVER_DB" + - versionName: "projects/$PROJECT_ID/secrets/CLOUD_BUILD_SA/versions/latest" + env: "IMPERSONATED_USER" + +options: + dynamicSubstitutions: true + pool: + name: ${_POOL_NAME} + logging: CLOUD_LOGGING_ONLY diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d2262b409..7928cbece 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @GoogleCloudPlatform/infra-db-dpes +* @GoogleCloudPlatform/cloud-sql-connectors diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md deleted file mode 100644 index a7591c122..000000000 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -name: Bug Report -about: Report defective or unintentional behavior you've experienced. -title: "Brief summary of what bug or error was observed" -labels: 'type: bug' - ---- - - - -## Bug Description - -Please enter a detailed description of the bug, and any information about what -behavior you noticed and how it differs from what you expected. - -## Example code (or command) - -``` -// example -``` - -## Stacktrace -``` -Any relevant stacktrace here. Be sure to filter sensitive information. -``` - -## How to reproduce - - 1. ? - 2. ? - -## Environment - -1. OS type and version: -2. Cloud SQL Proxy version (`./cloud-sql-proxy --version`): -3. Proxy invocation (for example, `./cloud-sql-proxy --enable_iam_login --dir /path/to/dir INSTANCE_CONNECTION_NAME`): diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 000000000..353a189b9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,78 @@ +# Copyright 2023 Google LLC +# +# 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 +# +# https://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. + +name: 🐞 Bug Report +description: File a bug report +title: "Brief summary of what bug or error was observed" +labels: ["type: bug"] +body: + - type: markdown + attributes: + value: | + Thanks for stopping by to let us know something could be better! + Please run down the following list and make sure you've tried the usual "quick fixes": + - Search the [current open issues](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues) + - Check for answers on [StackOverflow](https://stackoverflow.com/questions/tagged/google-cloud-sql) (under the 'google-cloud-sql' tag) + + If you are still having issues, please include as much information as possible below! :smile: + - type: textarea + id: bug-description + attributes: + label: Bug Description + description: "Please enter a detailed description of the bug, and any information about what behavior you noticed and why it is defective or unintentional." + validations: + required: true + - type: textarea + id: example-code + attributes: + label: Example code (or command) + description: "Please paste any useful application code related to the bug below. (if your code is in a public repo, feel free to paste a link!)" + value: | + ``` + // paste your code or command here + ``` + - type: textarea + id: stacktrace + attributes: + label: Stacktrace + description: "Paste any relevant stacktrace or error you are running into here. Be sure to filter sensitive information!" + render: bash + - type: textarea + id: repro + attributes: + label: Steps to reproduce? + description: "How do you trigger this bug? Please walk us through it step by step." + value: | + 1. ? + 2. ? + 3. ? + ... + validations: + required: true + - type: textarea + id: environment + attributes: + label: Environment + description: "Let us know some details about the environment in which you are seeing the bug!" + value: | + 1. OS type and version: + 2. Cloud SQL Proxy version (`./cloud-sql-proxy --version`): + 3. Proxy invocation command (for example, `./cloud-sql-proxy --port 5432 INSTANCE_CONNECTION_NAME`): + validations: + required: true + - type: textarea + id: additional-details + attributes: + label: Additional Details + description: "Any other information you want us to know?" diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 94e6589ed..000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,8 +0,0 @@ -blank_issues_enabled: false -contact_links: -- name: Cloud SQL Issue tracker - url: https://issuetracker.google.com/savedsearches/559773 - about: Please use the Cloud SQL Issue tracker for problems with Cloud SQL itself. -- name: StackOverflow - url: https://stackoverflow.com/questions/tagged/google-cloud-sql - about: Please use the `google-cloud-sql` tag for questions on StackOverflow. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/documentation-issue.md b/.github/ISSUE_TEMPLATE/documentation-issue.md deleted file mode 100644 index 3692027d2..000000000 --- a/.github/ISSUE_TEMPLATE/documentation-issue.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: Documentation Issue -about: Report wrong or missing information with the documentation in the repo. -title: "Brief summary of what is missing or incorrect" -labels: 'type: docs' - ---- - - -## Description -Provide a short description of what is missing or incorrect, as well as a link to the specific location of the information. - -## Solution -What would you prefer the documentation say? Why would this information be more accurate or helpful? - -## Additional Context -Please reference any other relevant issues, PRs, descriptions, or screenshots here. diff --git a/.github/ISSUE_TEMPLATE/documentation_issue.yaml b/.github/ISSUE_TEMPLATE/documentation_issue.yaml new file mode 100644 index 000000000..5c3cf5098 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation_issue.yaml @@ -0,0 +1,42 @@ +# Copyright 2023 Google LLC +# +# 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 +# +# https://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. + +name: 📝 Documentation Issue +description: Report wrong or missing information with the documentation in this repo. +title: "Brief summary of what is missing or incorrect" +labels: ["type: docs"] +body: + - type: markdown + attributes: + value: | + Thanks for stopping by to let us know something could be better! :smile: + + Please explain below how we can improve our documentation. + - type: textarea + id: description + attributes: + label: Description + description: "Provide a short description of what is missing or incorrect, as well as a link to the specific location of the issue." + validations: + required: true + - type: textarea + id: potential-solution + attributes: + label: Potential Solution + description: "What would you prefer the documentation say? Why would this information be more accurate or helpful?" + - type: textarea + id: additional-details + attributes: + label: Additional Details + description: "Please reference any other relevant issues, PRs, descriptions, or screenshots here." diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md deleted file mode 100644 index 6849df425..000000000 --- a/.github/ISSUE_TEMPLATE/feature-request.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: Feature Request -about: Suggest an idea for new or improved behavior. -title: "Brief summary of the proposed feature" -labels: 'type: feature request' - ---- - - -## Feature Description -A clear and concise description of what feature you would like to see, and why it would be useful to have added. - -## Alternatives Considered -Are there any workaround or third party tools to replicate this behavior? Why would adding this feature be preferred over them? - -## Additional Context -Please reference any other issues, PRs, descriptions, or screenshots here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml new file mode 100644 index 000000000..c00e4acbf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -0,0 +1,54 @@ +# Copyright 2023 Google LLC +# +# 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 +# +# https://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. + +name: ✨ Feature Request +description: Suggest an idea for new or improved behavior. +title: "Brief summary of the proposed feature" +labels: ["type: feature request"] +body: + - type: markdown + attributes: + value: | + Thanks for stopping by to let us know something could be better! + + Please run down the following list before proceeding with your feature request: + - Search the [current open issues](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues) to prevent creating a duplicate. + + Please include as much information as possible below! :smile: + - type: textarea + id: feature-description + attributes: + label: Feature Description + description: "A clear and concise description of what feature you would like to see, and why it would be useful to have added." + validations: + required: true + - type: textarea + id: sample-code + attributes: + label: Sample code + description: "If you already have an idea of what the implementation of this feature would like in code please provide it. (pseudo code is okay!)" + value: | + ``` + // sample code here + ``` + - type: textarea + id: alternatives-considered + attributes: + label: Alternatives Considered + description: "Are there any workaround or third party tools to replicate this behavior? Why would adding this feature be preferred over them?" + - type: textarea + id: additional-details + attributes: + label: Additional Details + description: "Any additional information we should know? Please reference it here (issues, PRs, descriptions, or screenshots)" diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index 14f5e6bac..000000000 --- a/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -name: Question -about: Questions on how something works or the best way to do something. -title: "Breif summary of your question" -labels: 'type: question' - ---- - - - -## Question -What's your question? Please provide as much relevant information as possible -to reduce turnaround time. - -## Additional Context -Please reference any other relevant issues, PRs, descriptions, or screenshots here. diff --git a/.github/ISSUE_TEMPLATE/question.yaml b/.github/ISSUE_TEMPLATE/question.yaml new file mode 100644 index 000000000..88bf09f0d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.yaml @@ -0,0 +1,48 @@ +# Copyright 2023 Google LLC +# +# 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 +# +# https://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. + +name: 💬 Question +description: Questions on how something works or the best way to do something? +title: "Brief summary of your question" +labels: ["type: question"] +body: + - type: markdown + attributes: + value: | + Thanks for stopping by to let us know something could be better!
+ + Please run down the following list and make sure you've tried the usual "quick fixes": + - Search the [current open issues](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues) for a similar question + - Check for answers on [StackOverflow](https://stackoverflow.com/questions/tagged/google-cloud-sql) (under the 'google-cloud-sql' tag) + + If you still have a question, please include as much information as possible below! :smile: + - type: textarea + id: question + attributes: + label: Question + description: "What's your question? Please provide as much relevant information as possible to reduce turnaround time." + placeholder: "Example: How do I connect using this connector with Private IP from Cloud Run?" + validations: + required: true + - type: textarea + id: code + attributes: + label: Code + description: "Please paste any useful application code that might be relevant to your question. (if your code is in a public repo, feel free to paste a link!)" + render: Go + - type: textarea + id: additional-details + attributes: + label: Additional Details + description: "Any other information you want us to know that might be helpful in answering your question? (link issues, PRs, descriptions, or screenshots)" diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 9208f5ba3..000000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,17 +0,0 @@ -## Change Description - -Please provide a detailed description on what changes your PR will have. - - -## Checklist - -- [ ] Make sure to open an issue as a - [bug/issue](https://github.com/GoogleCloudPlatform/cloudsql-proxy/issues/new/choose) - before writing your code! That way we can discuss the change, evaluate - designs, and agree on the general idea. -- [ ] Ensure the tests and linter pass -- [ ] Appropriate documentation is updated (if necessary) - -## Relevant issues: - -- Fixes # \ No newline at end of file diff --git a/.github/blunderbuss.yml b/.github/blunderbuss.yml index de680bfe1..3867ea389 100644 --- a/.github/blunderbuss.yml +++ b/.github/blunderbuss.yml @@ -13,13 +13,9 @@ # limitations under the License. assign_issues: -# - shubha-rajan - - enocom -# - jackwotherspoon -# - kurtisvg + - hessjcg + - kgala2 assign_prs: -# - shubha-rajan - - enocom -# - jackwotherspoon -# - kurtisvg + - hessjcg + - kgala2 diff --git a/.github/release-please.yml b/.github/release-please.yml index 6a960168c..d6896751a 100644 --- a/.github/release-please.yml +++ b/.github/release-please.yml @@ -15,10 +15,17 @@ handleGHRelease: true packageName: cloud-sql-proxy releaseType: simple -versionFile: 'cmd/version.txt' +versionFile: "cmd/version.txt" branches: -- branch: v1 - versionFile: 'cmd/cloud_sql_proxy/version.txt' - handleGHRelease: true - packageName: cloud-sql-proxy - releaseType: simple + - branch: v1 + versionFile: "proxy/util/version.txt" + handleGHRelease: true + packageName: cloud-sql-proxy + releaseType: simple +extraFiles: + - README.md + - cmd/root.go + - docs/cmd/cloud-sql-proxy.md + - examples/k8s-health-check/README.md + - examples/k8s-service/README.md + - examples/k8s-sidecar/README.md diff --git a/.github/renovate.json b/.github/renovate.json deleted file mode 100644 index f9261508c..000000000 --- a/.github/renovate.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extends": [ - "config:base", - ":semanticCommitTypeAll(chore)" - ], - "ignorePresets": [ - ":semanticPrefixFixDepsChoreOthers" - ], - "prConcurrentLimit": 3, - "rebaseStalePrs": true, - "dependencyDashboard": true, - "semanticCommits": true, - "postUpdateOptions": [ - "gomodTidy" - ] -} diff --git a/.github/renovate.json5 b/.github/renovate.json5 new file mode 100644 index 000000000..691e7a93b --- /dev/null +++ b/.github/renovate.json5 @@ -0,0 +1,26 @@ +{ + "extends": [ + "config:recommended" + ], + "dependencyDashboardLabels": ["type: process"], + "commitMessagePrefix": "deps: ", + "postUpdateOptions": [ + "gomodTidy" + ], + "prConcurrentLimit": 3, + "packageRules": [ + { + "matchManagers": ["github-actions"], + "groupName": "dependencies for github" + }, + { + "matchManagers": ["dockerfile"], + "groupName": "container images" + }, + { + "groupName": "Non-major dependency updates", + "matchManagers": ["gomod"], + "matchUpdateTypes": ["minor", "patch"], + }, + ] +} diff --git a/.github/trusted-contribution.yml b/.github/trusted-contribution.yml index 1a2f61205..244cb6d15 100644 --- a/.github/trusted-contribution.yml +++ b/.github/trusted-contribution.yml @@ -1,10 +1,10 @@ -# Copyright 2022 Google LLC +# Copyright 2025 Google LLC # # 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 # -# https://www.apache.org/licenses/LICENSE-2.0 +# 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, @@ -12,6 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Trigger presubmit tests for trusted contributors +# https://github.com/googleapis/repo-automation-bots/tree/main/packages/trusted-contribution +# Install: https://github.com/apps/trusted-contributions-gcf + +trustedContributors: + - "dependabot[bot]" + - "renovate-bot" + - "renovate[bot]" + - "forking-renovate[bot]" + - "release-please[bot]" annotations: - - type: label - text: "tests: run" + # Trigger Cloud Build tests + - type: comment + text: "/gcbrun" \ No newline at end of file diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000..6104157e5 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,67 @@ +# Copyright 2023 Google LLC +# +# 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. + +name: "CodeQL" + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + paths-ignore: + - "**/*.md" + - "**/*.txt" + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: ["go"] + + steps: + - name: Checkout repository + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + + - name: Setup Go + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 + with: + go-version: "1.25" + if: ${{ matrix.language == 'go' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@4bdb89f48054571735e3792627da6195c57459e2 # v3.31.10 + with: + languages: ${{ matrix.language }} + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). + # If this step fails, then you should remove it and run the build manually + - name: Autobuild + uses: github/codeql-action/autobuild@4bdb89f48054571735e3792627da6195c57459e2 # v3.31.10 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@4bdb89f48054571735e3792627da6195c57459e2 # v3.31.10 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index 17be8ebb2..3328f508d 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -15,17 +15,20 @@ name: code coverage on: [pull_request] +# Declare default permissions as read only. +permissions: read-all + jobs: build: runs-on: ubuntu-latest steps: - name: Setup Go - uses: actions/setup-go@v3 + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 with: - go-version: "1.19" + go-version: "1.25" - name: Checkout base branch - uses: actions/checkout@v3 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: ref: ${{ github.base_ref }} - name: Calculate base code coverage @@ -35,7 +38,7 @@ jobs: echo "CUR_COVER=$CUR_COVER" >> $GITHUB_ENV - name: Checkout PR branch - uses: actions/checkout@v3 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Calculate PR code coverage run: | go test -short -coverprofile pr_cover.out ./... || true diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml new file mode 100644 index 000000000..5ff8c2154 --- /dev/null +++ b/.github/workflows/docs.yaml @@ -0,0 +1,37 @@ +# Copyright 2024 Google LLC +# +# 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. + +name: docs +on: + pull_request: + +# Declare default permissions as read only. +permissions: read-all + +jobs: + docs: + name: Check docs are up to date + runs-on: ubuntu-latest + steps: + - name: Setup Go + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 + with: + go-version: "1.25" + - name: Checkout code + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: Generate docs and fail if there are differences + run: | + go install ./cmd/gendocs + gendocs + git diff --exit-code diff --git a/.github/workflows/govulncheck.yaml b/.github/workflows/govulncheck.yaml new file mode 100644 index 000000000..408c2fb6e --- /dev/null +++ b/.github/workflows/govulncheck.yaml @@ -0,0 +1,51 @@ +# Copyright 2023 Google LLC +# +# 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. + +name: govulncheck + +# Declare default permissions as read only. +permissions: read-all + +on: + push: + branches: + - "main" + - "v1" + pull_request: + branches: + - "main" + - "v1" + schedule: + - cron: "0 2 * * *" + +jobs: + govulncheck_job: + runs-on: ubuntu-latest + name: Run govulncheck + steps: + - name: Setup Go + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 + with: + go-version: "1.25" + check-latest: true + - name: Checkout code + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + ref: ${{ github.event.pull_request.head.sha }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + - id: govulncheck + uses: golang/govulncheck-action@b625fbe08f3bccbe446d94fbf87fcc875a4f50ee # v1 + with: + # Let actions/checkout above check-out the correct SHA + repo-checkout: false diff --git a/.github/workflows/labels.yaml b/.github/workflows/labels.yaml index bf3e60fa1..d6f875f20 100644 --- a/.github/workflows/labels.yaml +++ b/.github/workflows/labels.yaml @@ -18,11 +18,17 @@ on: branches: - main +# Declare default permissions as read only. +permissions: read-all + jobs: build: runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write steps: - - uses: actions/checkout@v3 - - uses: micnncim/action-label-syncer@v1 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - uses: micnncim/action-label-syncer@3abd5ab72fda571e69fffd97bd4e0033dd5f495c # v1.3.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index fe718ed20..4500f38b4 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -15,46 +15,21 @@ name: lint on: pull_request: - pull_request_target: - types: [labeled] + +# Declare default permissions as read only. +permissions: read-all jobs: lint: - if: "${{ github.event.action != 'labeled' || github.event.label.name == 'tests: run' }}" name: run lint runs-on: ubuntu-latest steps: - - name: Remove PR Label - if: "${{ github.event.action == 'labeled' && github.event.label.name == 'tests: run' }}" - uses: actions/github-script@v6 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - try { - await github.rest.issues.removeLabel({ - name: 'tests: run', - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.payload.pull_request.number - }); - } catch (e) { - console.log('Failed to remove label. Another job may have already removed it!'); - } - name: Setup Go - uses: actions/setup-go@v3 + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 with: - go-version: "1.19" + go-version: "1.25" - name: Checkout code - uses: actions/checkout@v3 - with: - ref: ${{ github.event.pull_request.head.sha }} - repository: ${{ github.event.pull_request.head.repo.full_name }} - - name: > - Verify go mod tidy. If you're reading this and the check has - failed, run `goimports -w . && go mod tidy && golangci-lint run` + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: lint run: | - go mod tidy && git diff --exit-code - - name: golangci-lint - uses: golangci/golangci-lint-action@v3 - with: - version: latest + ./build.sh lint_ci diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 000000000..ca8741da5 --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,70 @@ +# Copyright 2023 Google LLC +# +# 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. + +name: OSSF Scorecard +on: + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + schedule: + # weekly on Sunday + - cron: '0 20 * * 0' + push: + branches: [ "main" ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + + steps: + - name: "Checkout code" + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 + with: + results_file: results.sarif + results_format: sarif + + - name: Filter SARIF to skip false positives + # filter out DangerousWorkflow alerts as they do not account for safe use of labels to trigger actions + env: + SCORECARD_SKIPPED_RULE_IDS: "DangerousWorkflowID" + run: | + SCORECARD_SKIPPED_RULE_IDS_JSON=$(echo $SCORECARD_SKIPPED_RULE_IDS | jq -cR 'split(",")') + # Trim the SARIF file to remove false positive detections + cat results.sarif | jq '.runs[].results |= map(select(.ruleId as $id | '$SCORECARD_SKIPPED_RULE_IDS_JSON' | all($id != .)))' > resultsFiltered.sarif + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@4bdb89f48054571735e3792627da6195c57459e2 # v3.31.10 + with: + sarif_file: resultsFiltered.sarif diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index b2940e342..4d82abf2c 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -16,13 +16,14 @@ name: tests on: push: branches: - - 'main' - - 'v1' + - "main" + - "v1" pull_request: - pull_request_target: - types: [labeled] schedule: - - cron: '0 2 * * *' + - cron: "0 2 * * *" + +# Declare default permissions as read only. +permissions: read-all jobs: compilation: @@ -30,85 +31,81 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: 'actions/checkout@v3' + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + + - name: Setup Go + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 + with: + go-version: "1.25" - name: Verify FreeBSD and OpenBSD Builds run: | CGO_ENABLED=0 GOOS=freebsd go build CGO_ENABLED=0 GOOS=openbsd go build - integration: - # run job on proper workflow event triggers (skip job for pull_request event from forks and only run pull_request_target for "tests: run" label) - if: "${{ (github.event.action != 'labeled' && github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name) || github.event.label.name == 'tests: run' }}" name: integration tests + # run integration tests on all builds except pull requests from forks or dependabot + if: | + github.event_name != 'pull_request' || + (github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]') runs-on: ${{ matrix.os }} strategy: matrix: os: [macos-latest, windows-latest, ubuntu-latest] fail-fast: false permissions: - contents: 'read' - id-token: 'write' + contents: read + id-token: write + issues: write + pull-requests: write steps: - - name: Remove PR label - if: "${{ github.event.action == 'labeled' && github.event.label.name == 'tests: run' }}" - uses: actions/github-script@v6 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - try { - await github.rest.issues.removeLabel({ - name: 'tests: run', - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.payload.pull_request.number - }); - } catch (e) { - console.log('Failed to remove label. Another job may have already removed it!'); - } - - name: Checkout code - uses: 'actions/checkout@v3' - with: - ref: ${{ github.event.pull_request.head.sha }} - repository: ${{ github.event.pull_request.head.repo.full_name }} + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Setup Go - uses: actions/setup-go@v3 + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 with: - go-version: 1.19 - - - id: 'auth' - name: 'Authenticate to Google Cloud' - uses: 'google-github-actions/auth@v1.0.0' + go-version: "1.25" + - id: auth + name: Authenticate to Google Cloud + uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # v2.1.13 with: - workload_identity_provider: ${{ secrets.PROVIDER_NAME }} - service_account: ${{ secrets.SERVICE_ACCOUNT }} + workload_identity_provider: ${{ vars.PROVIDER_NAME }} + service_account: ${{ vars.SERVICE_ACCOUNT }} access_token_lifetime: 600s - - name: 'Set up Cloud SDK' - uses: 'google-github-actions/setup-gcloud@v1.0.1' + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@e427ad8a34f8676edf47cf7d7925499adf3eb74f # v2.2.1 - - id: 'secrets' + - id: secrets name: Get secrets - uses: 'google-github-actions/get-secretmanager-secrets@v1.0.0' + uses: google-github-actions/get-secretmanager-secrets@2b5f97c5a4b9c105e64646762ad4fc3f5128e6f5 # v2.2.5 with: secrets: |- - MYSQL_CONNECTION_NAME:${{ secrets.GOOGLE_CLOUD_PROJECT }}/MYSQL_CONNECTION_NAME - MYSQL_USER:${{ secrets.GOOGLE_CLOUD_PROJECT }}/MYSQL_USER - MYSQL_PASS:${{ secrets.GOOGLE_CLOUD_PROJECT }}/MYSQL_PASS - MYSQL_DB:${{ secrets.GOOGLE_CLOUD_PROJECT }}/MYSQL_DB - POSTGRES_CONNECTION_NAME:${{ secrets.GOOGLE_CLOUD_PROJECT }}/POSTGRES_CONNECTION_NAME - POSTGRES_USER:${{ secrets.GOOGLE_CLOUD_PROJECT }}/POSTGRES_USER - POSTGRES_USER_IAM:${{ secrets.GOOGLE_CLOUD_PROJECT }}/POSTGRES_USER_IAM - POSTGRES_PASS:${{ secrets.GOOGLE_CLOUD_PROJECT }}/POSTGRES_PASS - POSTGRES_DB:${{ secrets.GOOGLE_CLOUD_PROJECT }}/POSTGRES_DB - SQLSERVER_CONNECTION_NAME:${{ secrets.GOOGLE_CLOUD_PROJECT }}/SQLSERVER_CONNECTION_NAME - SQLSERVER_USER:${{ secrets.GOOGLE_CLOUD_PROJECT }}/SQLSERVER_USER - SQLSERVER_PASS:${{ secrets.GOOGLE_CLOUD_PROJECT }}/SQLSERVER_PASS - SQLSERVER_DB:${{ secrets.GOOGLE_CLOUD_PROJECT }}/SQLSERVER_DB - IMPERSONATED_USER:${{ secrets.GOOGLE_CLOUD_PROJECT }}/IMPERSONATED_USER + MYSQL_CONNECTION_NAME:${{ vars.GOOGLE_CLOUD_PROJECT }}/MYSQL_CONNECTION_NAME + MYSQL_USER:${{ vars.GOOGLE_CLOUD_PROJECT }}/MYSQL_USER + MYSQL_PASS:${{ vars.GOOGLE_CLOUD_PROJECT }}/MYSQL_PASS + MYSQL_DB:${{ vars.GOOGLE_CLOUD_PROJECT }}/MYSQL_DB + MYSQL_MCP_CONNECTION_NAME:${{ vars.GOOGLE_CLOUD_PROJECT }}/MYSQL_MCP_CONNECTION_NAME + MYSQL_MCP_PASS:${{ vars.GOOGLE_CLOUD_PROJECT }}/MYSQL_MCP_PASS + POSTGRES_CONNECTION_NAME:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_CONNECTION_NAME + POSTGRES_USER:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_USER + POSTGRES_USER_IAM:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_USER_IAM + POSTGRES_PASS:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_PASS + POSTGRES_DB:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_DB + POSTGRES_CAS_CONNECTION_NAME:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_CAS_CONNECTION_NAME + POSTGRES_CAS_PASS:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_CAS_PASS + POSTGRES_CUSTOMER_CAS_CONNECTION_NAME:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_CUSTOMER_CAS_CONNECTION_NAME + POSTGRES_CUSTOMER_CAS_PASS:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_CUSTOMER_CAS_PASS + POSTGRES_CUSTOMER_CAS_DOMAIN_NAME:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_CUSTOMER_CAS_DOMAIN_NAME + POSTGRES_MCP_CONNECTION_NAME:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_MCP_CONNECTION_NAME + POSTGRES_MCP_PASS:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_MCP_PASS + SQLSERVER_CONNECTION_NAME:${{ vars.GOOGLE_CLOUD_PROJECT }}/SQLSERVER_CONNECTION_NAME + SQLSERVER_USER:${{ vars.GOOGLE_CLOUD_PROJECT }}/SQLSERVER_USER + SQLSERVER_PASS:${{ vars.GOOGLE_CLOUD_PROJECT }}/SQLSERVER_PASS + SQLSERVER_DB:${{ vars.GOOGLE_CLOUD_PROJECT }}/SQLSERVER_DB + IMPERSONATED_USER:${{ vars.GOOGLE_CLOUD_PROJECT }}/IMPERSONATED_USER - name: Enable fuse config (Linux) if: runner.os == 'Linux' @@ -117,23 +114,32 @@ jobs: - name: Run tests env: - GOOGLE_CLOUD_PROJECT: '${{ secrets.GOOGLE_CLOUD_PROJECT }}' - MYSQL_CONNECTION_NAME: '${{ steps.secrets.outputs.MYSQL_CONNECTION_NAME }}' - MYSQL_USER: '${{ steps.secrets.outputs.MYSQL_USER }}' - MYSQL_PASS: '${{ steps.secrets.outputs.MYSQL_PASS }}' - MYSQL_DB: '${{ steps.secrets.outputs.MYSQL_DB }}' - POSTGRES_CONNECTION_NAME: '${{ steps.secrets.outputs.POSTGRES_CONNECTION_NAME }}' - POSTGRES_USER: '${{ steps.secrets.outputs.POSTGRES_USER }}' - POSTGRES_USER_IAM: '${{ steps.secrets.outputs.POSTGRES_USER_IAM }}' - POSTGRES_PASS: '${{ steps.secrets.outputs.POSTGRES_PASS }}' - POSTGRES_DB: '${{ steps.secrets.outputs.POSTGRES_DB }}' - SQLSERVER_CONNECTION_NAME: '${{ steps.secrets.outputs.SQLSERVER_CONNECTION_NAME }}' - SQLSERVER_USER: '${{ steps.secrets.outputs.SQLSERVER_USER }}' - SQLSERVER_PASS: '${{ steps.secrets.outputs.SQLSERVER_PASS }}' - SQLSERVER_DB: '${{ steps.secrets.outputs.SQLSERVER_DB }}' - IMPERSONATED_USER: '${{ steps.secrets.outputs.IMPERSONATED_USER }}' + GOOGLE_CLOUD_PROJECT: "${{ vars.GOOGLE_CLOUD_PROJECT }}" + MYSQL_CONNECTION_NAME: "${{ steps.secrets.outputs.MYSQL_CONNECTION_NAME }}" + MYSQL_USER: "${{ steps.secrets.outputs.MYSQL_USER }}" + MYSQL_PASS: "${{ steps.secrets.outputs.MYSQL_PASS }}" + MYSQL_DB: "${{ steps.secrets.outputs.MYSQL_DB }}" + MYSQL_MCP_CONNECTION_NAME: "${{ steps.secrets.outputs.MYSQL_MCP_CONNECTION_NAME }}" + MYSQL_MCP_PASS: "${{ steps.secrets.outputs.MYSQL_MCP_PASS }}" + POSTGRES_CONNECTION_NAME: "${{ steps.secrets.outputs.POSTGRES_CONNECTION_NAME }}" + POSTGRES_USER: "${{ steps.secrets.outputs.POSTGRES_USER }}" + POSTGRES_USER_IAM: "${{ steps.secrets.outputs.POSTGRES_USER_IAM }}" + POSTGRES_PASS: "${{ steps.secrets.outputs.POSTGRES_PASS }}" + POSTGRES_DB: "${{ steps.secrets.outputs.POSTGRES_DB }}" + POSTGRES_CAS_CONNECTION_NAME: "${{ steps.secrets.outputs.POSTGRES_CAS_CONNECTION_NAME }}" + POSTGRES_CAS_PASS: "${{ steps.secrets.outputs.POSTGRES_CAS_PASS }}" + POSTGRES_CUSTOMER_CAS_CONNECTION_NAME: "${{ steps.secrets.outputs.POSTGRES_CUSTOMER_CAS_CONNECTION_NAME }}" + POSTGRES_CUSTOMER_CAS_PASS: "${{ steps.secrets.outputs.POSTGRES_CUSTOMER_CAS_PASS }}" + POSTGRES_CUSTOMER_CAS_DOMAIN_NAME: "${{ steps.secrets.outputs.POSTGRES_CUSTOMER_CAS_DOMAIN_NAME }}" + POSTGRES_MCP_CONNECTION_NAME: "${{ steps.secrets.outputs.POSTGRES_MCP_CONNECTION_NAME }}" + POSTGRES_MCP_PASS: "${{ steps.secrets.outputs.POSTGRES_MCP_PASS }}" + SQLSERVER_CONNECTION_NAME: "${{ steps.secrets.outputs.SQLSERVER_CONNECTION_NAME }}" + SQLSERVER_USER: "${{ steps.secrets.outputs.SQLSERVER_USER }}" + SQLSERVER_PASS: "${{ steps.secrets.outputs.SQLSERVER_PASS }}" + SQLSERVER_DB: "${{ steps.secrets.outputs.SQLSERVER_DB }}" + IMPERSONATED_USER: "${{ steps.secrets.outputs.IMPERSONATED_USER }}" TMPDIR: "/tmp" - TMP: '${{ runner.temp }}' + TMP: "${{ runner.temp }}" # specifying bash shell ensures a failure in a piped process isn't lost by using `set -eo pipefail` shell: bash run: | @@ -167,3 +173,19 @@ jobs: curl https://github.com/googleapis/repo-automation-bots/releases/download/flakybot-1.1.0/flakybot-darwin-amd64 -o flakybot -s -L chmod +x ./flakybot ./flakybot --repo ${{github.repository}} --commit_hash ${{github.sha}} --build_url https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} + + unit: + name: unit tests + runs-on: ubuntu-latest + steps: + - name: Setup Go + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 + with: + go-version: 1.25 + - name: Checkout code + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: Run tests + # specifying bash shell ensures a failure in a piped process isn't lost by using `set -eo pipefail` + shell: bash + run: | + go test -v -race -short ./... diff --git a/.github/workflows/v1-periodic.yaml b/.github/workflows/v1-periodic.yaml index 42cdb44d1..fb57fd294 100644 --- a/.github/workflows/v1-periodic.yaml +++ b/.github/workflows/v1-periodic.yaml @@ -15,7 +15,10 @@ name: v1 periodic on: schedule: - - cron: '0 2 * * *' + - cron: "0 2 * * *" + +# Declare default permissions as read only. +permissions: read-all jobs: integration: @@ -26,30 +29,30 @@ jobs: os: [macos-latest, windows-latest, ubuntu-latest] fail-fast: false permissions: - contents: 'read' - id-token: 'write' + contents: "read" + id-token: "write" steps: - name: Checkout code - uses: 'actions/checkout@v3' + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: ref: v1 - name: Setup Go - uses: actions/setup-go@v3 + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 with: - go-version: 1.19 + go-version: "1.25" - - id: 'auth' - name: 'Authenticate to Google Cloud' - uses: 'google-github-actions/auth@v1.0.0' + - id: auth + name: Authenticate to Google Cloud + uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # v2.1.13 with: workload_identity_provider: ${{ secrets.PROVIDER_NAME }} service_account: ${{ secrets.SERVICE_ACCOUNT }} access_token_lifetime: 600s - - id: 'secrets' + - id: secrets name: Get secrets - uses: 'google-github-actions/get-secretmanager-secrets@v1.0.0' + uses: google-github-actions/get-secretmanager-secrets@2b5f97c5a4b9c105e64646762ad4fc3f5128e6f5 # v2.2.5 with: secrets: |- MYSQL_CONNECTION_NAME:${{ secrets.GOOGLE_CLOUD_PROJECT }}/MYSQL_CONNECTION_NAME @@ -73,22 +76,22 @@ jobs: - name: Run tests env: - GOOGLE_CLOUD_PROJECT: '${{ secrets.GOOGLE_CLOUD_PROJECT }}' - MYSQL_CONNECTION_NAME: '${{ steps.secrets.outputs.MYSQL_CONNECTION_NAME }}' - MYSQL_USER: '${{ steps.secrets.outputs.MYSQL_USER }}' - MYSQL_PASS: '${{ steps.secrets.outputs.MYSQL_PASS }}' - MYSQL_DB: '${{ steps.secrets.outputs.MYSQL_DB }}' - POSTGRES_CONNECTION_NAME: '${{ steps.secrets.outputs.POSTGRES_CONNECTION_NAME }}' - POSTGRES_USER: '${{ steps.secrets.outputs.POSTGRES_USER }}' - POSTGRES_USER_IAM: '${{ steps.secrets.outputs.POSTGRES_USER_IAM }}' - POSTGRES_PASS: '${{ steps.secrets.outputs.POSTGRES_PASS }}' - POSTGRES_DB: '${{ steps.secrets.outputs.POSTGRES_DB }}' - SQLSERVER_CONNECTION_NAME: '${{ steps.secrets.outputs.SQLSERVER_CONNECTION_NAME }}' - SQLSERVER_USER: '${{ steps.secrets.outputs.SQLSERVER_USER }}' - SQLSERVER_PASS: '${{ steps.secrets.outputs.SQLSERVER_PASS }}' - SQLSERVER_DB: '${{ steps.secrets.outputs.SQLSERVER_DB }}' + GOOGLE_CLOUD_PROJECT: "${{ secrets.GOOGLE_CLOUD_PROJECT }}" + MYSQL_CONNECTION_NAME: "${{ steps.secrets.outputs.MYSQL_CONNECTION_NAME }}" + MYSQL_USER: "${{ steps.secrets.outputs.MYSQL_USER }}" + MYSQL_PASS: "${{ steps.secrets.outputs.MYSQL_PASS }}" + MYSQL_DB: "${{ steps.secrets.outputs.MYSQL_DB }}" + POSTGRES_CONNECTION_NAME: "${{ steps.secrets.outputs.POSTGRES_CONNECTION_NAME }}" + POSTGRES_USER: "${{ steps.secrets.outputs.POSTGRES_USER }}" + POSTGRES_USER_IAM: "${{ steps.secrets.outputs.POSTGRES_USER_IAM }}" + POSTGRES_PASS: "${{ steps.secrets.outputs.POSTGRES_PASS }}" + POSTGRES_DB: "${{ steps.secrets.outputs.POSTGRES_DB }}" + SQLSERVER_CONNECTION_NAME: "${{ steps.secrets.outputs.SQLSERVER_CONNECTION_NAME }}" + SQLSERVER_USER: "${{ steps.secrets.outputs.SQLSERVER_USER }}" + SQLSERVER_PASS: "${{ steps.secrets.outputs.SQLSERVER_PASS }}" + SQLSERVER_DB: "${{ steps.secrets.outputs.SQLSERVER_DB }}" TMPDIR: "/tmp" - TMP: '${{ runner.temp }}' + TMP: "${{ runner.temp }}" # specifying bash shell ensures a failure in a piped process isn't lost by using `set -eo pipefail` shell: bash run: | diff --git a/.gitignore b/.gitignore index adbe94a4e..de8224085 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,12 @@ # IDEs .idea/ .vscode/ +*.iml /cloud-sql-proxy +/cloud-sql-proxy.exe /key.json +/logs/ +.tools +test_results.txt diff --git a/.golangci.yml b/.golangci.yml index ded34226d..c6706b8e2 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -12,47 +12,56 @@ # See the License for the specific language governing permissions and # limitations under the License. # .golangci.yml +version: "2" linters: - disable-all: true + default: none enable: - # From https://golangci-lint.run/usage/linters/ - # This is a minor deviation from the default linters - - goimports - - gosimple - govet - ineffassign - revive - staticcheck - unused -issues: - exclude-use-default: false -linters-settings: - revive: - rules: - # From https://revive.run/docs#recommended-configuration - - name: blank-imports - - name: context-as-argument - - name: context-keys-type - - name: dot-imports - - name: empty-block - - name: errorf - - name: error-naming - - name: error-return - - name: error-strings - - name: exported - - name: if-return - - name: import-shadowing - - name: increment-decrement - - name: indent-error-flow - - name: range - - name: range-val-address - - name: range-val-in-closure - - name: receiver-naming - - name: redefines-builtin-id - - name: superfluous-else - - name: time-naming - - name: unexported-return - - name: unreachable-code - - name: unused-parameter - - name: var-declaration - - name: var-naming + settings: + revive: + rules: + - name: blank-imports + - name: context-as-argument + - name: context-keys-type + - name: dot-imports + - name: empty-block + - name: errorf + - name: error-naming + - name: error-return + - name: error-strings + - name: exported + - name: if-return + - name: import-shadowing + - name: increment-decrement + - name: indent-error-flow + - name: range + - name: range-val-address + - name: range-val-in-closure + - name: receiver-naming + - name: redefines-builtin-id + - name: superfluous-else + - name: time-naming + - name: unexported-return + - name: unreachable-code + - name: unused-parameter + - name: var-declaration + - name: var-naming + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - goimports + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/CHANGELOG.md b/CHANGELOG.md index fc9b7f8c4..146e19e28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,398 @@ # Changelog +## [2.21.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.20.0...v2.21.0) (2026-01-16) + + +### Bug Fixes + +* Update deprecated proxy options and update all dependencies ([#2531](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2531)) ([d9ae096](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/d9ae0963767c7e70d446ebd08f1a96acef7a0342)) + + +### Miscellaneous Chores + +* release 2.21.0 ([#2533](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2533)) ([d813423](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/d813423df6eb938e9029c4f47a1bf9a459ac6a9f)) + +## [2.20.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.19.0...v2.20.0) (2025-12-09) + + +### Features + +* Add shutdown command to call the /quitquitquit endpoint. ([#2514](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2514)) ([f6747f9](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/f6747f93af92d235b449851599d1ef31642cfa11)), closes [#2511](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2511) + + +### Bug Fixes + +* Update dependency versions. ([#2520](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2520)) ([119dc23](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/119dc23efaf493411f4c92d0036eee7e843d8aba)) + +## [2.19.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.18.3...v2.19.0) (2025-10-29) + + +### Features + +* Add MySQL caching_sha2_password support for the proxy clients using unix sockets. ([#2489](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2489)) ([6ff8753](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/6ff8753f486e65afcd08ffea9b41be82c7d507f5)), closes [#2317](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2317) + +## [2.18.3](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.18.2...v2.18.3) (2025-10-23) + + +### Bug Fixes + +* bump dependencies to latest ([#2499](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2499)) ([c5fb6ee](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/c5fb6ee1079956c546d2da74c1fe2dfade3ac969)) + +## [2.18.2](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.18.1...v2.18.2) (2025-08-27) + + +### Bug Fixes + +* Update go connector dependency to 1.18.1 to include high priority fix for DNS name configurations. ([#2487](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2487)) ([91efc78](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/91efc78a63866df44b56707684650a86856a0dd9)) + +## [2.18.1](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.18.0...v2.18.1) (2025-08-14) + + +### Bug Fixes + +* bump dependencies to latest ([#2482](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2482)) ([1fc458c](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/1fc458c1e309442efa03cdb824c73f37505aa464)) + +## [2.18.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.17.1...v2.18.0) (2025-07-11) + + +### Features + +* add --skip-failed-instance-config flag, to ignore unix socket connection errors on startup ([#2452](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2452)) ([05b0d60](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/05b0d6067c178fdcd70267ccc2cd78614a292d5b)), closes [#2451](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2451) + +## [2.17.1](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.17.0...v2.17.1) (2025-06-11) + + +### Bug Fixes + +* Fix the release artifacts job. ([#2461](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2461)) ([929b09e](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/929b09e9be30b0995e105c0cc5f4448357f6490d)) + +## [2.17.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.16.0...v2.17.0) (2025-06-11) + + +### Bug Fixes + +* bump dependencies to latest ([#2455](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2455)) ([5035280](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/50352807af4a97797780894dd4fa16607f291582)) + + +### Miscellaneous Chores + +* release 2.17.0 ([#2459](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2459)) ([a78d853](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/a78d853b5ede5305e19a838ef7948c67d9f77083)) + +## [2.16.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.15.3...v2.16.0) (2025-04-28) + +### Features: + +* Update TLS validation to use both SAN and CN fields, from Go Connector v1.17.0 ([#979](https://github.com/GoogleCloudPlatform/cloud-sql-go-connector/issues/979)) ([df60a20](https://github.com/GoogleCloudPlatform/cloud-sql-go-connector/commit/df60a2061dbfd78ce30a87319be8d8e027957d86)) + + +## [2.15.3](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.15.2...v2.15.3) (2025-04-16) + + + +### Bug Fixes + +* bump dependencies to latest ([#2429](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2429)) ([0b1c5f4](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/0b1c5f498f6e1bd8bbb7439030072b09fdb1aacb)) + +## [2.15.2](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.15.1...v2.15.2) (2025-03-20) + + +### Bug Fixes + +* bump dependencies to latest ([#2408](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2408)) ([43119f9](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/43119f9ba6f8c91cfb8d312647a8db55315e1ad4)) + +## [2.15.1](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.15.0...v2.15.1) (2025-02-19) + + +### Bug Fixes + +* bump dependencies to latest ([#2389](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2389)) ([852a16e](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/852a16eb2ab70c5f49c88873abc3e5cfee5875de)) + +## [2.15.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.14.3...v2.15.0) (2025-01-30) + + +### Features + +* enable Proxy to be started with DNS names ([#2363](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2363)) ([d902441](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/d902441dccdb17229c25478f7267a96917cd6a07)) + +## [2.14.3](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.14.2...v2.14.3) (2025-01-16) + + +### Bug Fixes + +* bump dependencies to latest ([#2367](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2367)) ([bab696f](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/bab696f59aaea4f4be47bb068432cabfc50d4777)) + +## [2.14.2](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.14.1...v2.14.2) (2024-12-11) + + +### Bug Fixes + +* bump dependencies to latest ([#2346](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2346)) ([a407a18](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/a407a1801fa1bf4b5afed581d6fe72ad1c45d16b)) + +## [2.14.1](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.14.0...v2.14.1) (2024-11-21) + + +### Bug Fixes + +* bump dependencies to latest ([#2328](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2328)) ([87975c4](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/87975c4cae0877b48eaefedf544d5a80300f2a57)) + +## [2.14.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.13.0...v2.14.0) (2024-10-23) + + +### Features + +* callback notify function when connection is refused ([#2308](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2308)) ([9309b84](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/9309b8461d73d83137943885aad164793a14a875)) + +## [2.13.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.12.0...v2.13.0) (2024-08-14) + + +### Features + +* bump to Go 1.23 ([#2287](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2287)) ([fd6bd49](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/fd6bd49242c884508f641c754eb5cec5d28ac665)) + +## [2.12.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.11.4...v2.12.0) (2024-07-17) + + +### Features + +* Add parameter --min-sigterm-delay allow new connections for a minimum number off seconds before shutting down the proxy. ([#2266](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2266)) ([52cd0d9](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/52cd0d95695d2b8e9456825e7c0bd452234a867b)), closes [#1640](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1640) +* add support for Debian bookworm ([#2267](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2267)) ([fbec17b](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/fbec17bd2c8c0898bdf41eb22669a871e5048ba9)) + + +### Bug Fixes + +* ignore go-fuse ctx in Lookup ([#2268](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2268)) ([ae8ec35](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/ae8ec359b43056fe815ac7c649388232bc1b4171)) +* Make the process exit if there as an error accepting a fuse connection. ([#2257](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2257)) ([bb2a0ae](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/bb2a0ae76d518eaeec69fcc2ac7e930a4bd7e024)) + +## [2.11.4](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.11.3...v2.11.4) (2024-06-12) + + +### Bug Fixes + +* bump dependencies to latest ([#2249](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2249)) ([6501df5](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/6501df5f34fdf82651bea163b9014ea15dc86b81)) + +## [2.11.3](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.11.2...v2.11.3) (2024-05-28) + + +### Bug Fixes + +* bump dependencies to latest ([#2236](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2236)) ([14ff947](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/14ff947fa6c3b9a0023d5be7ad5b165cf6ac153b)) + +## [2.11.2](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.11.1...v2.11.2) (2024-05-16) + + +### Bug Fixes + +* bump dependencies to latest ([#2218](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2218)) ([44dff63](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/44dff63c0d9ec755565ab54f1dd48e9967f6d513)) + +## [2.11.1](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.11.0...v2.11.1) (2024-05-14) + + +### Bug Fixes + +* don't depend on downstream in readiness check ([#2207](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2207)) ([49fa927](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/49fa927ede69bf24f3fd0c56e60b99e4111d58f1)), closes [#2083](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2083) +* ensure proxy shutsdown cleanly on fuse error ([#2205](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2205)) ([54e65d1](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/54e65d14a5d533f44e33b52a2dc88c2a419eae2f)), closes [#2013](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2013) +* use public mirrors for base images ([#2190](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2190)) ([69b4215](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/69b42158291b0ea4f074469dabbe34949af86053)) + +## [2.11.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.10.1...v2.11.0) (2024-04-16) + + +### Features + +* add support for a lazy refresh ([#2184](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2184)) ([fd7ab82](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/fd7ab82796c052ddf12f78989e5d3cab49f26c55)), closes [#2183](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2183) +* use Google managed base images ([#2159](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2159)) ([1103a95](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/1103a95adb0c0751df99704f71a4376ce38613a4)) + +## [2.10.1](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.10.0...v2.10.1) (2024-03-20) + + +### Bug Fixes + +* correct CI/CD build error ([#2155](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2155)) ([3e3b8ed](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/3e3b8ede607a50ce72af8f8d1d86eb6789560aef)) + +## [2.10.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.9.0...v2.10.0) (2024-03-14) + + +### Features + +* add support for config file ([#2106](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2106)) ([c936396](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/c9363966cb017cde7712426c3e9c999e3d7e0973)) +* add TPC support ([#2116](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2116)) ([7d011f8](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/7d011f8f1bb87488f639a3bfde09f57ac350ab8c)) + + +### Bug Fixes + +* use kebab case for config file consistently ([#2130](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2130)) ([ee52f07](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/ee52f0759a84bad9d8cec4a3cd1f8ff536c2e982)) + +## [2.9.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.8.2...v2.9.0) (2024-02-20) + + +### Features + +* add support for debug logging ([#2107](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2107)) ([c8f7a0a](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/c8f7a0abc325a9183b23710e30f5d1c9e619aef5)) + +## [2.8.2](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.8.1...v2.8.2) (2024-01-17) + + +### Bug Fixes + +* update dependencies to latest versions ([#2089](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2089)) ([6d9981a](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/6d9981a757e3c1033954db7b6f834c42c5495e4f)) + +## [2.8.1](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.8.0...v2.8.1) (2023-12-12) + + +### Bug Fixes + +* label container images correctly ([#2061](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2061)) ([f071d38](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/f071d38e152c70113d7102c19ed450c74e8d64f0)) +* Update Go Connector to v1.5.2 to ensure connections work after waking from sleep +([#1788](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1788)) + +## [2.8.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.7.2...v2.8.0) (2023-12-04) + + +### Features + +* add support for wait command ([#2041](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2041)) ([1c00ba4](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/1c00ba475374e6ae46956c4125b91a55fe953751)) + +## [2.7.2](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.7.1...v2.7.2) (2023-11-14) + + +### Bug Fixes + +* use proper error instance to write error log ([#2014](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2014)) ([cc76a54](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/cc76a544a6878ad9f0ef5fb407de314b3f801cbe)) + +## [2.7.1](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.7.0...v2.7.1) (2023-10-17) + + +### Bug Fixes + +* bump dependencies to latest ([#2004](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2004)) ([4953402](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/4953402d9a63613729a2f5b8a33ac0323b7b6bb9)) + +## [2.7.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.6.1...v2.7.0) (2023-09-19) + + +### Features + +* /quitquitquit api now responds to HTTP GET and POST requests. ([#1947](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1947)) ([e5ebb48](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/e5ebb485f7a7a5f9820822bf4e84467da431fc6b)), closes [#1946](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1946) +* Add support for systemd notify ([#1930](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1930)) ([cf23647](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/cf23647f72990fc3e6b4987e3040c6020929b97d)) + +## [2.6.1](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.6.0...v2.6.1) (2023-08-16) + + +### Bug Fixes + +* remove the error message for zero on sigterm ([#1902](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1902)) ([55f0f60](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/55f0f60c9701a22e657ba814c9cfe6221c4840e7)) + +## [2.6.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.5.0...v2.6.0) (2023-07-14) + + +### Features + +* add support for exit zero on sigterm ([#1870](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1870)) ([e0a97dd](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/e0a97ddd5bea94054b1da0c3f0844ab47ad6f126)) + +## [2.5.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.4.0...v2.5.0) (2023-07-11) + + +### Features + +* add PSC support to proxy ([#1863](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1863)) ([496974a](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/496974a31555d64f144d24247507c0ec457d7edd)) + +## [2.4.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.3.0...v2.4.0) (2023-06-14) + + +### Features + +* add connection test for startup ([#1832](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1832)) ([47dae85](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/47dae851a9513bb5e3d98b59a33aef909a2bf125)), closes [#348](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/348) +* allow connections during shutdown ([#1805](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1805)) ([4a456ed](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/4a456ed6a9727f672783aee021b20a208971270d)) + + +### Bug Fixes + +* log info message for quitquitquit ([#1806](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1806)) ([4d36204](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/4d36204cb6c93751e9a7d40be5e3eff94a90847f)) +* Windows service stop ([#1833](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1833)) ([17e66a7](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/17e66a7a73a88d5c29c77133cdb5ad5aebd0a4c1)) + +## [2.3.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.2.0...v2.3.0) (2023-05-16) + + +### Features + +* Add Windows Service support ([#1696](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1696)) ([ec56eba](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/ec56ebab683804885edd95365e099de7a0de578f)), closes [#277](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/277) + + +### Bug Fixes + +* disallow auto-iam-authn with gcloud-auth ([#1762](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1762)) ([8200abe](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/8200abe467bdf9f5b458f108e5f086bdfbfa2dd9)), closes [#1754](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1754) + +## [2.2.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.1.2...v2.2.0) (2023-04-18) + + +### Features + +* add support for Auto IP ([#1735](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1735)) ([83c8a64](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/83c8a6444e9e1305922550bd5b6ac373babb0ffc)) + + +### Bug Fixes + +* allow `--structured-logs` and `--quiet` flags together ([#1750](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1750)) ([0aff60e](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/0aff60e5daf7995890ebc750080032bed543c9ea)) +* limit calls to SQL Admin API on startup ([#1723](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1723)) ([e1a03df](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/e1a03df61120e26c7bffe86a1f971cca8bb77562)) +* pass dial options to FUSE mounts ([#1737](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1737)) ([7ecf6ac](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/7ecf6ac013760a1e775db4a8da6a99a1e1817330)) + +## [2.1.2](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.1.1...v2.1.2) (2023-03-22) + + +### Bug Fixes + +* update dependencies to latest versions ([#1707](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1707)) ([54ea90e](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/54ea90e140873da254a34ea8b4e612b81a46cf13)) + +## [2.1.1](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.1.0...v2.1.1) (2023-02-23) + + +### Bug Fixes + +* build statically linked binaries ([#1680](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1680)) ([49308c5](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/49308c5c3c2372e4cb7e26f58c1e0dba7953f663)) + +## [2.1.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.0.0...v2.1.0) (2023-02-16) + + +### Features + +* add support for Go 1.20 ([#1630](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1630)) ([72df17d](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/72df17d9a1992d51faf3a9f4ecd3960f680b7ef3)) +* add support for quitquitquit endpoint ([#1624](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1624)) ([43f9857](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/43f98574de06211581779a67806b01d5518cdd62)) +* Add unix-socket-path to instance command arguments. ([#1623](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1623)) ([f42f3d1](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/f42f3d1ce9fc81b78d9e8bd68b147cae20516fae)), closes [#1573](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1573) + + +### Bug Fixes + +* ensure separate token source with auto-iam-authn ([#1637](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1637)) ([325a487](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/325a487187c9a9cb1864b8f387b1b06369e1ca25)) +* honor request context in readiness check ([#1657](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1657)) ([0934739](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/09347395eddd8b4942a1cfb77344b014c8bdc90b)) + +## [2.0.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.0.0-preview.4...v2.0.0) (2023-01-17) + + +### Bug Fixes + +* correctly apply metadata to user agent ([#1606](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1606)) ([1ca9902](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/1ca9902fb949ea2a75fcdc5ed9930877db6ff600)) + + +### Miscellaneous Chores + +* release 2.0.0 ([#1615](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1615)) ([4a6283b](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/4a6283b70b49f97a5b60ebb68c9e01d6add2dff0)) + +## [2.0.0-preview.4](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.0.0-preview.3...v2.0.0-preview.4) (2022-12-12) + + +### Features + +* add admin server with pprof ([#1564](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1564)) ([d022c56](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/d022c5683a301722e55692ae3ca1d62cf0e6d017)) + + +### Bug Fixes + +* add runtime version to user agent if present ([#1542](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1542)) ([a6b689b](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/a6b689b05aa6f1e11ede8a1dd6fdec3cfc3c8c8e)) +* use user-agent as flag name ([#1561](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1561)) ([e1b2f7e](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/e1b2f7eb4e9552a1124b7aab3a1bca4366797b53)) + + +### Miscellaneous Chores + +* release 2.0.0-preview.4 ([#1576](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/1576)) ([04fcf88](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/commit/04fcf88d35a1da7e623b763829a02e79431fb74e)) + ## [2.0.0-preview.3](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/compare/v2.0.0-preview.2...v2.0.0-preview.3) (2022-11-15) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..46b2a08ea --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,43 @@ +# Contributor Code of Conduct + +As contributors and maintainers of this project, +and in the interest of fostering an open and welcoming community, +we pledge to respect all people who contribute through reporting issues, +posting feature requests, updating documentation, +submitting pull requests or patches, and other activities. + +We are committed to making participation in this project +a harassment-free experience for everyone, +regardless of level of experience, gender, gender identity and expression, +sexual orientation, disability, personal appearance, +body size, race, ethnicity, age, religion, or nationality. + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery +* Personal attacks +* Trolling or insulting/derogatory comments +* Public or private harassment +* Publishing other's private information, +such as physical or electronic +addresses, without explicit permission +* Other unethical or unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct. +By adopting this Code of Conduct, +project maintainers commit themselves to fairly and consistently +applying these principles to every aspect of managing this project. +Project maintainers who do not follow or enforce the Code of Conduct +may be permanently removed from the project team. + +This code of conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. + +Instances of abusive, harassing, or otherwise unacceptable behavior +may be reported by opening an issue +or contacting one or more of the project maintainers. + +This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, +available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 48cf8e8fc..37ae0a447 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,193 +1,33 @@ -# Contributing +# How to Contribute -1. **Please sign one of the contributor license agreements below!** -1. Fork the repo, develop and test your code changes, add docs. -1. Make sure that your commit messages clearly describe the changes. -1. Send a pull request. +We'd love to accept your patches and contributions to this project. -## Table of contents -* [Opening an issue](#opening-an-issue) -* [How to run tests](#how-to-run-tests) -* [Contributor License Agreements](#contributor-license-agreements) -* [Contributor Code of Conduct](#contributor-code-of-conduct) +## Before you begin -## Opening an issue +### Sign our Contributor License Agreement -If you find a bug in the proxy code or an inaccuracy in the documentation, -please open an issue. GitHub provides a guide, [Mastering -Issues](https://guides.github.com/features/issues/), that is useful if you are -unfamiliar with the process. Here are the specific steps for opening an issue: +Contributions to this project must be accompanied by a +[Contributor License Agreement](https://cla.developers.google.com/about) (CLA). +You (or your employer) retain the copyright to your contribution; this simply +gives us permission to use and redistribute your contributions as part of the +project. -1. Go to the project issues page on GitHub. -1. Click the green `New Issue` button located in the upper right corner. -1. In the title field, write a single phrase that identifies your issue. -1. In the main editor, describe your issue. -1. Click the submit button. +If you or your current employer have already signed the Google CLA (even if it +was for a different project), you probably don't need to do it again. -Thank you. We will do our best to triage your issue within one business day, and -attempt to categorize your issues with an estimate of the priority and issue -type. We will try to respond with regular updates based on its priority: +Visit to see your current agreements or to +sign a new one. -* **Critical** respond and update daily, resolve with a week -* **High** respond and update weekly, resolve within six weeks -* **Medium** respond and update every three months, best effort resolution -* **Low** respond and update every six months, best effort resolution +### Review our Community Guidelines -The priority we assign will be roughly a function of the number of users we -expect to be impacted, as well as its severity. As a rule of thumb: +This project follows +[Google's Open Source Community Guidelines](https://opensource.google/conduct/). - - - - - - - - - - - - - - - - - - - - - - - - - - +## Contribution process - - - - - - - -
SeverityNumber of users
HandfulSomeMostAll
Easy, obvious workaroundLowLowMediumHigh -
Non-obvious workaround availableLowMediumHighCritical
Functionality blockedHighHighCriticalCritical
+### Code Reviews -## How to run tests - -The test suite includes both unit and integration tests. For macOS and Linux, -there is a depenency on [FUSE][] that must be present on the system. - -### Test Dependencies - -When running tests on macOS and Linux, users will need to first install -[FUSE][]. Windows users may skip this step. - -On Debian, use: - -``` -sudo apt-get install fuse -``` - -On macOS, use: - -``` -brew install --cask macfuse -``` - -### How to run just unit tests - -``` -go test -short ./... -``` - -### How to run all tests - -To run all integration tests, users will need a Google Cloud project with a -MySQL, PostgreSQL, and SQL Server database, in addition to installing FUSE -support. Note: Pull Requests will run these tests and as a result may be skipped -locally if necessary. - -A sample `.envrc.example` file is included in the root directory which documents -which environment variables must be set to run the integration tests. Copy this -example file to `.envrc` at the root of the project, supply all the correct -values for each variable, source the file (`source .envrc`, or consider using -[direnv][]), and then run: - -``` -go test ./... -``` - -## Contributor License Agreements - -Open-source software licensing is a wonderful arrangement that benefits -everyone, but in an imperfect world, we all need to exercise some legal -prudence. In order to protect you, Google, and most of all, everyone who comes -to depend on these libraries, we require that all contributors sign our short -and human-readable Contributor License Agreement (CLA). We don't want to open -the door to patent trolls, predatory lawyers, or anyone else who isn't on board -with creating value and making the world a better place. We hope you will agree -that the CLA offers very important protection and is easy to understand. Take a -moment to read it carefully, and if you agree with what you read, please sign it -now. If you believe you've already signed the appropriate CLA already for this -or any other Google open-source project, you shouldn't have to do so again. You -can review your signed CLAs at -[cla.developers.google.com/clas](https://cla.developers.google.com/clas). - -First, check that you are signed in to a [Google -Account](https://accounts.google.com) that matches your [local Git email -address](https://help.github.com/articles/setting-your-email-in-git/). Then -choose one of the following: - -* If you are **an individual writing original source code** and **you own the - intellectual property**, sign the [Individual - CLA](https://developers.google.com/open-source/cla/individual). -* If you work for **a company that wants to allow you to contribute**, sign the - [Corporate CLA](https://developers.google.com/open-source/cla/corporate). - -You (and your authorized signer, if corporate) can sign the CLA -electronically. After that, we'll be able to accept your contributions. - -## Contributor Code of Conduct - -As contributors and maintainers of this project, and in the interest of -fostering an open and welcoming community, we pledge to respect all people who -contribute through reporting issues, posting feature requests, updating -documentation, submitting pull requests or patches, and other activities. - -We are committed to making participation in this project a harassment-free -experience for everyone, regardless of level of experience, gender, gender -identity and expression, sexual orientation, disability, personal appearance, -body size, race, ethnicity, age, religion, or nationality. - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery -* Personal attacks -* Trolling or insulting/derogatory comments -* Public or private harassment -* Publishing other's private information, such as physical or electronic -addresses, without explicit permission -* Other unethical or unprofessional conduct. - -Project maintainers have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct. By adopting this Code of Conduct, project -maintainers commit themselves to fairly and consistently applying these -principles to every aspect of managing this project. Project maintainers who do -not follow or enforce the Code of Conduct may be permanently removed from the -project team. - -This code of conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by opening an issue or contacting one or more of the project -maintainers. - -This Code of Conduct is adapted from the [Contributor -Covenant](http://contributor-covenant.org), version 1.2.0, available at -[http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) - -[FUSE]: https://www.kernel.org/doc/html/latest/filesystems/fuse.html -[direnv]: https://direnv.net/ +All submissions, including submissions by project members, require review. We +use GitHub pull requests for this purpose. Consult +[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more +information on using pull requests. diff --git a/CONTRIBUTORS b/CONTRIBUTORS deleted file mode 100644 index 63a15e2c3..000000000 --- a/CONTRIBUTORS +++ /dev/null @@ -1,40 +0,0 @@ -# This is the official list of people who can contribute -# (and typically have contributed) code to the repository. -# The AUTHORS file lists the copyright holders; this file -# lists people. For example, Google employees are listed here -# but not in AUTHORS, because Google holds the copyright. -# -# The submission process automatically checks to make sure -# that people submitting code are listed in this file (by email address). -# -# Names should be added to this file only after verifying that -# the individual or the individual's organization has agreed to -# the appropriate Contributor License Agreement, found here: -# -# https://cla.developers.google.com/about/google-individual -# https://cla.developers.google.com/about/google-corporate -# -# The CLA can be filled out on the web: -# -# https://cla.developers.google.com/ -# -# When adding J Random Contributor's name to this file, -# either J's name or J's organization's name should be -# added to the AUTHORS file, depending on whether the -# individual or corporate CLA was used. - -# Names should be added to this file like so: -# Name -# -# An entry with two email addresses specifies that the -# first address should be used in the submit logs and -# that the second address should be recognized as the -# same person when interacting with Rietveld. - -# Please keep the list sorted. - -Ben Brown -Frank van Rest -Kevin Malachowski -Mykola Smith - diff --git a/Dockerfile b/Dockerfile index 9acf6a8f7..2670e10d8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,10 +23,13 @@ ARG TARGETARCH RUN go get ./... RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ - go build -ldflags "-X main.metadataString=container" + go build -ldflags "-X github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/cmd.metadataString=container" # Final Stage -FROM gcr.io/distroless/static:nonroot +FROM gcr.io/distroless/static:nonroot@sha256:5d09f5106208a46853a7bebc12c4ce0a2da33f863c45716be11bb4a5b2760e41 + +LABEL org.opencontainers.image.source="https://github.com/GoogleCloudPlatform/cloud-sql-proxy" + COPY --from=build --chown=nonroot /go/src/cloud-sql-proxy/cloud-sql-proxy /cloud-sql-proxy # set the uid as an integer for compatibility with runAsNonRoot in Kubernetes USER 65532 diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 00e38c03d..911742caa 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -13,7 +13,7 @@ # limitations under the License. # Use the latest stable golang 1.x to compile to a binary -FROM --platform=$BUILDPLATFORM golang:1 as build +FROM --platform=$BUILDPLATFORM golang:1-alpine as build WORKDIR /go/src/cloud-sql-proxy COPY . . @@ -22,11 +22,14 @@ ARG TARGETOS ARG TARGETARCH RUN go get ./... -RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ - go build -ldflags "-X main.metadataString=container.alpine" +RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ + go build -ldflags "-X github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/cmd.metadataString=container.alpine" # Final stage -FROM alpine:3 +FROM alpine:3@sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62 + +LABEL org.opencontainers.image.source="https://github.com/GoogleCloudPlatform/cloud-sql-proxy" + RUN apk add --no-cache \ ca-certificates \ libc6-compat diff --git a/Dockerfile.buster b/Dockerfile.bookworm similarity index 75% rename from Dockerfile.buster rename to Dockerfile.bookworm index 924010962..3014f7a53 100644 --- a/Dockerfile.buster +++ b/Dockerfile.bookworm @@ -1,4 +1,4 @@ -# Copyright 2020 Google LLC +# Copyright 2024 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,11 +22,14 @@ ARG TARGETOS ARG TARGETARCH RUN go get ./... -RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ - go build -ldflags "-X main.metadataString=container.buster" +RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ + go build -ldflags "-X github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/cmd.metadataString=container.bookworm" # Final stage -FROM debian:buster +FROM gcr.io/cloud-marketplace-containers/google/debian12@sha256:49793b0935d4a156dcb15c92fc8f3448a2a100cbcbe7f6e0894d5c88fe488219 + +LABEL org.opencontainers.image.source="https://github.com/GoogleCloudPlatform/cloud-sql-proxy" + RUN apt-get update && apt-get install -y ca-certificates # Install fuse and allow enable non-root users to mount RUN apt-get update && apt-get install -y fuse && sed -i 's/^#user_allow_other$/user_allow_other/g' /etc/fuse.conf diff --git a/Dockerfile.bullseye b/Dockerfile.bullseye index af50feb5a..6195faf01 100644 --- a/Dockerfile.bullseye +++ b/Dockerfile.bullseye @@ -22,11 +22,14 @@ ARG TARGETOS ARG TARGETARCH RUN go get ./... -RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ - go build -ldflags "-X main.metadataString=container.bullseye" +RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ + go build -ldflags "-X github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/cmd.metadataString=container.bullseye" # Final stage -FROM debian:bullseye +FROM gcr.io/cloud-marketplace-containers/google/debian11@sha256:519fb462113ba6aa442eafddfae5ba71f6f5e8375c793c8bdaa237ddf733cf86 + +LABEL org.opencontainers.image.source="https://github.com/GoogleCloudPlatform/cloud-sql-proxy" + RUN apt-get update && apt-get install -y ca-certificates # Install fuse and allow enable non-root users to mount RUN apt-get update && apt-get install -y fuse && sed -i 's/^#user_allow_other$/user_allow_other/g' /etc/fuse.conf diff --git a/README.md b/README.md index cd4caed5a..da604b845 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,57 @@ -# Cloud SQL Auth proxy +# Cloud SQL Auth Proxy [![CI][ci-badge]][ci-build] -The Cloud SQL Auth proxy is a utility for ensuring secure connections to your +> [!WARNING] +> **Go versions 1.25.2 and 1.24.8 are NOT compatible with Cloud Auth Proxy.** +> +> An update to the Go version 1.25.2 and Go 1.24.8 breaks SAN verificaton. This is because Cloud SQL includes a trailing dot in the DNS name within the certificate's Subject Alternative Name (SAN), which the above Go versions reject as a malformed DNS name. +> +> For more details, please see the related Go issue: [crypto/x509: quadratic complexity when checking name constraints ](https://github.com/golang/go/issues/75715). + + +> [!IMPORTANT] +> +> The Cloud SQL Auth Proxy does not currently support Unix domain socket +> connections to MySQL 8.4 instances. This is due to a [known issue](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/2317) +> involving the new default `caching_sha2_password` authentication plugin. + +The Cloud SQL Auth Proxy is a utility for ensuring secure connections to your Cloud SQL instances. It provides IAM authorization, allowing you to control who can connect to your instance through IAM permissions, and TLS 1.3 encryption, without having to manage certificates. See the [Connecting Overview][connection-overview] page for more information on -connecting to a Cloud SQL instance, or the [About the proxy][about-proxy] page -for details on how the Cloud SQL proxy works. +connecting to a Cloud SQL instance, or the [About the Proxy][about-proxy] page +for details on how the Cloud SQL Proxy works. -The Cloud SQL Auth proxy has support for: +The Cloud SQL Auth Proxy has support for: -- [Automatic IAM Authentication][iam-auth] (Postgres only) +- [Automatic IAM Authentication][iam-auth] (Postgres and MySQL only) - Metrics ([Cloud Monitoring][], [Cloud Trace][], and [Prometheus][]) - [HTTP Healthchecks][health-check-example] -- Service account impersonation +- [Service account impersonation](#configuring-service-account-impersonation) - Separate Dialer functionality released as the [Cloud SQL Go Connector][go connector] -- Configuration with environment variables +- Configuration with [environment variables](#config-environment-variables) - Fully POSIX-compliant flags -If you're using Go, Java, or Python, consider using the corresponding Cloud SQL -connector which does everything the proxy does, but in process: +If you're using Go, Java, Python, or Node.js, consider using the corresponding Cloud SQL +connector which does everything the Proxy does, but in process: - [Cloud SQL Go connector][go connector] - [Cloud SQL Java connector][java connector] - [Cloud SQL Python connector][python connector] +- [Cloud SQL Node.js connector][node connector] For users migrating from v1, see the [Migration Guide](migration-guide.md). The [v1 README][v1 readme] is still available. -NOTE: The proxy does not configure the network between the VM it's running on -and the Cloud SQL instance. You MUST ensure the proxy can reach your Cloud SQL -instance, either by deploying it in a VPC that has access to your Private IP -instance, or by configuring Public IP. +> [!IMPORTANT] +> +> The Proxy does not configure the network between the VM it's running on +> and the Cloud SQL instance. You MUST ensure the Proxy can reach your Cloud SQL +> instance, either by deploying it in a VPC that has access to your Private IP +> instance, or by configuring Public IP. [cloud monitoring]: https://cloud.google.com/monitoring [cloud trace]: https://cloud.google.com/trace @@ -42,6 +59,7 @@ instance, or by configuring Public IP. [go connector]: https://github.com/GoogleCloudPlatform/cloud-sql-go-connector [java connector]: https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory [python connector]: https://github.com/GoogleCloudPlatform/cloud-sql-python-connector +[node connector]: https://github.com/GoogleCloudPlatform/cloud-sql-nodejs-connector [v1 readme]: https://github.com/GoogleCloudPlatform/cloudsql-proxy/blob/5f5b09b62eb6dfcaa58ce399d0131c1544bf813f/README.md ## Installation @@ -49,14 +67,20 @@ instance, or by configuring Public IP. Check for the latest version on the [releases page][releases] and use the following instructions for your OS and CPU architecture. +> [!NOTE] +> +> Starting with version `v2.17.1`, Windows binaries provided on the +> [releases page][releases] are signed with Google LLC certificates. + +
Linux amd64 ```sh # see Releases for other versions -URL="https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.0.0-preview.3" +URL="https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.21.0" -wget "$URL/cloud-sql-proxy.linux.amd64" -O cloud-sql-proxy +curl "$URL/cloud-sql-proxy.linux.amd64" -o cloud-sql-proxy chmod +x cloud-sql-proxy ``` @@ -68,9 +92,9 @@ chmod +x cloud-sql-proxy ```sh # see Releases for other versions -URL="https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.0.0-preview.3" +URL="https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.21.0" -wget "$URL/cloud-sql-proxy.linux.386" -O cloud-sql-proxy +curl "$URL/cloud-sql-proxy.linux.386" -o cloud-sql-proxy chmod +x cloud-sql-proxy ``` @@ -82,9 +106,9 @@ chmod +x cloud-sql-proxy ```sh # see Releases for other versions -URL="https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.0.0-preview.3" +URL="https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.21.0" -wget "$URL/cloud-sql-proxy.linux.arm64" -O cloud-sql-proxy +curl "$URL/cloud-sql-proxy.linux.arm64" -o cloud-sql-proxy chmod +x cloud-sql-proxy ``` @@ -96,9 +120,9 @@ chmod +x cloud-sql-proxy ```sh # see Releases for other versions -URL="https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.0.0-preview.3" +URL="https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.21.0" -wget "$URL/cloud-sql-proxy.linux.arm" -O cloud-sql-proxy +curl "$URL/cloud-sql-proxy.linux.arm" -o cloud-sql-proxy chmod +x cloud-sql-proxy ``` @@ -110,9 +134,9 @@ chmod +x cloud-sql-proxy ```sh # see Releases for other versions -URL="https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.0.0-preview.3" +URL="https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.21.0" -wget "$URL/cloud-sql-proxy.darwin.amd64" -O cloud-sql-proxy +curl "$URL/cloud-sql-proxy.darwin.amd64" -o cloud-sql-proxy chmod +x cloud-sql-proxy ``` @@ -124,9 +148,9 @@ chmod +x cloud-sql-proxy ```sh # see Releases for other versions -URL="https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.0.0-preview.3" +URL="https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.21.0" -wget "$URL/cloud-sql-proxy.darwin.arm64" -O cloud-sql-proxy +curl "$URL/cloud-sql-proxy.darwin.arm64" -o cloud-sql-proxy chmod +x cloud-sql-proxy ``` @@ -138,7 +162,7 @@ chmod +x cloud-sql-proxy ```sh # see Releases for other versions -wget https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.0.0-preview.3/cloud-sql-proxy-x64.exe -O cloud-sql-proxy.exe +curl https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.21.0/cloud-sql-proxy.x64.exe -o cloud-sql-proxy.exe ```
@@ -148,10 +172,11 @@ wget https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.0.0- ```sh # see Releases for other versions -wget https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.0.0-preview.3/cloud-sql-proxy-x86.exe -O cloud-sql-proxy.exe +curl https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.21.0/cloud-sql-proxy.x86.exe -o cloud-sql-proxy.exe ``` + ### Install from Source @@ -177,12 +202,45 @@ page of your Cloud SQL instance in the console, or use `gcloud` with: gcloud sql instances describe --format='value(connectionName)' ``` +### Credentials + +The Cloud SQL Proxy uses a Cloud IAM principal to authorize connections against +a Cloud SQL instance. The Proxy sources the credentials using +[Application Default Credentials](https://cloud.google.com/docs/authentication/production). + +> [!NOTE] +> +> Any IAM principal connecting to a Cloud SQL database will need one of the +> following IAM roles: +> +> - Cloud SQL Client (preferred) +> - Cloud SQL Editor +> - Cloud SQL Admin +> +> Or one may manually assign the following IAM permissions: +> +> - `cloudsql.instances.connect` +> - `cloudsql.instances.get` +> +> See [Roles and Permissions in Cloud SQL][roles-and-permissions] for details. + +When the Proxy authenticates under the Compute Engine VM's default service +account, the VM must have at least the `sqlservice.admin` API scope (i.e., +"https://www.googleapis.com/auth/sqlservice.admin") and the associated project +must have the SQL Admin API enabled. The default service account must also have +at least writer or editor privileges to any projects of target SQL instances. + +The Proxy also supports two flags related to credentials: + +- `--token` to use an OAuth2 token +- `--credentials-file` to use a service account key file + ### Basic Usage -To start the proxy, use: +To start the Proxy, use: ```shell -# starts the proxy listening on localhost with the default database engine port +# starts the Proxy listening on localhost with the default database engine port # For example: # MySQL localhost:3306 # Postgres localhost:5432 @@ -190,11 +248,11 @@ To start the proxy, use: ./cloud-sql-proxy ``` -The proxy will automatically detect the default database engine's port and start -a corresponding listener. Production deployments should use the --port flag to +The Proxy will automatically detect the default database engine's port and start +a corresponding listener. Production deployments should use the `--port` flag to reduce startup time. -The proxy supports multiple instances: +The Proxy supports multiple instances: ```shell ./cloud-sql-proxy @@ -229,7 +287,7 @@ To configure ports on a per instance basis, use the `port` query param: ### Configuring Listening Address -To overide the choice of `localhost`, use the `--address` flag: +To override the choice of `localhost`, use the `--address` flag: ```shell # Starts a listener on all interfaces at port 5432 @@ -248,7 +306,7 @@ To override address on a per-instance basis, use the `address` query param: ### Configuring Private IP -By default, the proxy attempts to connect to an instance's public IP. To enable +By default, the Proxy attempts to connect to an instance's public IP. To enable private IP, use: ```shell @@ -257,14 +315,16 @@ private IP, use: ./cloud-sql-proxy --private-ip ``` -NOTE: The proxy does not configure the network. You MUST ensure the proxy can -reach your Cloud SQL instance, either by deploying it in a VPC that has access -to your Private IP instance, or by configuring Public IP. +> [!IMPORTANT] +> +> The Proxy does not configure the network. You MUST ensure the Proxy can +> reach your Cloud SQL instance, either by deploying it in a VPC that has access +> to your Private IP instance, or by configuring Public IP. ### Configuring Unix domain sockets -The proxy also supports [Unix domain sockets](https://en.wikipedia.org/wiki/Unix_domain_socket). -To start the proxy with Unix sockets, run: +The Proxy also supports [Unix domain sockets](https://en.wikipedia.org/wiki/Unix_domain_socket). +To start the Proxy with Unix sockets, run: ```shell # Uses the directory "/mycooldir" to create a Unix socket @@ -285,91 +345,319 @@ query param: 'myproject:my-region:mysql?unix-socket=/cloudsql' ``` -NOTE: The proxy supports Unix domain sockets on recent versions of Windows, but -replaces colons with periods: +> [!NOTE] +> +> The Proxy supports Unix domain sockets on recent versions of Windows, but +> replaces colons with periods: +> +> ```shell +> # Starts a Unix domain socket at the path: +> # C:\cloudsql\myproject.my-region.mysql +> ./cloud-sql-proxy --unix-socket C:\cloudsql myproject:my-region:mysql +> ``` + + +### Configuring IAM Database Authentication + +The Proxy supports [Automatic IAM Database Authentication][iam-auth] for MySQL +and Postgres instances, allowing IAM principal's to authenticate and connect +as database users. + +Make sure to configure your [Cloud SQL instance to allow IAM authentication][iam-auth-config] +and to [add your IAM principal as a database user][iam-auth-user]. ```shell -# Starts a Unix domain socket at the path: -# C:\cloudsql\myproject.my-region.mysql -./cloud-sql-proxy --unix-socket C:\cloudsql myproject:my-region:mysql +./cloud-sql-proxy --auto-iam-authn ``` -### Additional flags +> [!IMPORTANT] +> +> Make sure to run the Proxy as the same IAM principal as the database user +> you want to log in as. Only the IAM principal that is attached to the +> [sourced credentials](#credentials) will be able to successfully log in +> via automatic IAM database authentication. +> +> When logging in using an IAM database user, Cloud SQL truncates usernames +> based on the engine type in order to not exceed character limits. +> PostgreSQL's username character limit is 63, while MySQL's is 32. +> +> Cloud SQL IAM database usernames are formatted in the following way: +> +> **Postgres**: +> * For an IAM user account, this is the user's email address. +> * For a service account, it is the service account's email without the +> `.gserviceaccount.com` domain suffix. +> +> **MySQL**: +> * For an IAM user account, this is the user's email address, +> without the `@` or domain name. For example, for `test-user@gmail.com`, +> the database user would be `test-user`. +> * For a service account, this is the service account's email address without +> the `@project-id.iam.gserviceaccount.com` suffix. + + +### Configuring Service Account Impersonation + +The Proxy supports [service account impersonation](https://cloud.google.com/iam/docs/impersonating-service-accounts). +This allows the Proxy to act as a different service account, which can be useful +for granting access to resources that are not accessible to the default IAM +principal. + +To use service account impersonation, you must have the +`iam.serviceAccounts.getAccessToken` permission on the IAM principal +impersonating another service account. You can grant this permission by assigning + the `roles/iam.serviceAccountTokenCreator` role to the IAM principal. + +To impersonate a service account, use the `--impersonate-service-account` flag: + +> [!NOTE] +> +> The impersonated service account must have the `Service Usage Consumer` and +`Cloud SQL Client` permissions. +> Additionally, to use IAM Authenticated users, add the `Cloud SQL Instance User` + permission. -To see a full list of flags, use: ```shell -./cloud-sql-proxy -h +# Starts a listener on localhost:5432 and impersonates the service account +# "my-other-sa@my-project.iam.gserviceaccount.com". +# The Proxy will use the credentials of the principal running the Proxy to +# generate a short-lived access token for the impersonated service account. +./cloud-sql-proxy --impersonate-service-account \ +my-other-sa@my-project.iam.gserviceaccount.com ``` -## Credentials +### Using Advanced Disaster Recovery and DNS domain names to identify instances -The Cloud SQL proxy uses a Cloud IAM principal to authorize connections against -a Cloud SQL instance. The proxy sources the credentials using -[Application Default Credentials](https://cloud.google.com/docs/authentication/production). +The proxy can be configured to use DNS to look up an instance. +Use a DNS name managed by Cloud SQL [Advanced Disaster Recovery](https://docs.cloud.google.com/sql/docs/mysql/use-advanced-disaster-recovery), +or a domain name that you manage. -Note: Any IAM principal connecting to a Cloud SQL database will need one of the -following IAM roles: +#### Using Advanced Recovery Write Endpoint DNS Name -- Cloud SQL Client (preferred) -- Cloud SQL Editor -- Cloud SQL Admin +[Advanced Disaster Recovery](https://docs.cloud.google.com/sql/docs/mysql/use-advanced-disaster-recovery) +creates geographically distributed replicas of your Cloud SQL database instance. When you perform +switchover or failover on the database instance, the proxy will gracefully disconnect from the +old primary instance and reconnect to the new primary instance. -Or one may manually assign the following IAM permissions: +Follow the instructions in [Connect using Write Endpoint](https://docs.cloud.google.com/sql/docs/mysql/connect-to-instance-using-write-endpoint) +to get the write endpoint DNS name for your primary instance. Then, use this write endpoint DNS +name to configure the proxy. -- `cloudsql.instances.connect` -- `cloudsql.instances.get` +#### Configure your DNS Records -See [Roles and Permissions in Cloud SQL][roles-and-permissions] for details. +The proxy may be configured to use DNS that you define as well. -When the proxy authenticates under the Compute Engine VM's default service -account, the VM must have at least the `sqlservice.admin` API scope (i.e., -"https://www.googleapis.com/auth/sqlservice.admin") and the associated project -must have the SQL Admin API enabled. The default service account must also have -at least writer or editor privileges to any projects of target SQL instances. +Add a DNS TXT record for the Cloud SQL instance to a **private** DNS server +or a private Google Cloud DNS Zone used by your application. -The proxy also supports three flags related to credentials: +**Note:** You are strongly discouraged from adding DNS records for your +Cloud SQL instances to a public DNS server. This would allow anyone on the +internet to discover the Cloud SQL instance name. + +For example: suppose you wanted to use the domain name +`prod-db.mycompany.example.com` to connect to your database instance +`my-project:region:my-instance`. You would create the following DNS record: + +- Record type: `TXT` +- Name: `prod-db.mycompany.example.com` – This is the domain name used by the application +- Value: `my-project:region:my-instance` – This is the instance name + +#### Configuring the Proxy + +Configure the Proxy with your DNS domain name instead of an instance connection +name: + +```sh +./cloud-sql-proxy prod-db.mycompany.example.com +``` + +### Automatic fail-over using DNS domain names + +When the Proxy is configured using a domain name, it will +periodically check if the DNS record for an instance changes. When the Proxy +detects that the domain name refers to a different instance, it will +close all open connections to the old instance. Subsequent connection attempts +will be directed to the new instance. + +For example: suppose application is configured to connect using the +domain name `prod-db.mycompany.example.com`. Initially the corporate DNS +zone has a TXT record with the value `my-project:region:my-instance`. The +application establishes connections to the `my-project:region:my-instance` +Cloud SQL instance. + +Then, to reconfigure the application to use a different database +instance, change the value of the `prod-db.mycompany.example.com` DNS record +from `my-project:region:my-instance` to `my-project:other-region:my-instance-2` + +The Proxy detects the change to this DNS record. +Now, when the application connects to its database using the +domain name `prod-db.mycompany.example.com`, it will connect to the +`my-project:other-region:my-instance-2` Cloud SQL instance. + +The Proxy will automatically close all existing connections to +`my-project:region:my-instance`. This will force the connection pools to +establish new connections. Also, it may cause database queries in progress +to fail. + +The Proxy will poll for changes to the DNS name every 30 seconds by default. + +### Testing Connectivity + +The Proxy includes support for a connection test on startup. This test helps +ensure the Proxy can reach the associated instance and is a quick debugging +tool. The test will attempt to connect to the specified instance(s) and fail +if the instance is unreachable. If the test fails, the Proxy will exit with +a non-zero exit code. + +```shell +./cloud-sql-proxy --run-connection-test +``` + +### Config file + +The Proxy supports a configuration file. Supported file types are TOML, JSON, +and YAML. Load the file with the `--config-file` flag: + +```shell +./cloud-sql-proxy --config-file /path/to/config.[toml|json|yaml] +``` + +The configuration file format supports all flags. The key names should match +the flag names. For example: + +``` toml +# use instance-connection-name-0, instance-connection-name-1, etc. +# for multiple instances +instance-connection-name = "proj:region:inst" +auto-iam-authn = true +debug = true +debug-logs = true +``` + +Run `./cloud-sql-proxy --help` for more details. See the full documentation +in [docs/cmd](docs/cmd). + +### Config environment variables + +The proxy supports configuration through environment variables. +Each environment variable uses "CSQL_PROXY" as a prefix and is +the uppercase version of the flag using underscores as word delimiters. + +For example, the `--auto-iam-authn` flag may be set with the environment variable +`CSQL_PROXY_AUTO_IAM_AUTHN`. + +An invocation of the Proxy using environment variables would look like the following: + +```shell +CSQL_PROXY_AUTO_IAM_AUTHN=true \ + ./cloud-sql-proxy +``` + +Run `./cloud-sql-proxy --help` for more details. + +### Configuring a Lazy Refresh + +The `--lazy-refresh` flag configures the Proxy to retrieve connection info +lazily and as-needed. Otherwise, no background refresh cycle runs. This setting +is useful in environments where the CPU may be throttled outside of a request +context, e.g., Cloud Run, Cloud Functions, etc. + +### Additional flags + +To see a full list of flags, use: + +```shell +./cloud-sql-proxy --help +``` -- `--token` to use an OAuth2 token -- `--credentials-file` to use a service account key file -- `--gcloud-auth` to use the Gcloud user's credentials (local development only) ## Container Images -There are containerized versions of the proxy available from the following -Google Cloud Container Registry repositories: +There are containerized versions of the Proxy available from the following +[Artifact Registry](https://cloud.google.com/artifact-registry) repositories: - `gcr.io/cloud-sql-connectors/cloud-sql-proxy` - `us.gcr.io/cloud-sql-connectors/cloud-sql-proxy` - `eu.gcr.io/cloud-sql-connectors/cloud-sql-proxy` - `asia.gcr.io/cloud-sql-connectors/cloud-sql-proxy` -Each image is tagged with the associated proxy version. The following tags are +> [!NOTE] +> +> The above container images were migrated from Google Container Registry (deprecated) +> to Artifact Registry which is why they begin with the old naming pattern (`gcr.io`) + +Each image is tagged with the associated Proxy version. The following tags are currently supported: - `$VERSION` (default) - `$VERSION-alpine` -- `$VERSION-buster` - `$VERSION-bullseye` +- `$VERSION-bookworm` -The `$VERSION` is the proxy version without the leading "v" (e.g., -`2.0.0-preview.3`). + +The `$VERSION` is the Proxy version without the leading "v" (e.g., +`2.21.0`). For example, to pull a particular version, use a command like: ``` shell -# $VERSION is 2.0.0-preview.3 -docker pull gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.0.0-preview.3 +# $VERSION is 2.21.0 +docker pull gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.21.0 ``` + We recommend pinning to a specific version tag and using automation with a CI pipeline to update regularly. The default container image uses [distroless][] with a non-root user. If you -need a shell or related tools, use the Alpine or Buster images listed above. +need a shell or related tools, use the Alpine or Debian-based container images +(bullseye or bookworm) listed above. [distroless]: https://github.com/GoogleContainerTools/distroless +### Working with Docker and the Proxy + +The containers have the proxy as an `ENTRYPOINT` so, to use the proxy from a +container, all you need to do is specify options using the command, and expose +the proxy's internal port to the host. For example, you can use: + +```shell +docker run --publish : \ + gcr.io/cloud-sql-connectors/cloud-sql-proxy:latest \ + --address "0.0.0.0" --port +``` + +You'll need the `--address "0.0.0.0"` so that the proxy doesn't only listen for +connections originating from *within* the container. + +You will need to authenticate using one of the methods outlined in the +[credentials](#credentials) section. If using a credentials file you must mount +the file and ensure that the non-root user that runs the proxy has *read access* +to the file. These alternatives might help: + +1. Change the group of your local file and add read permissions to the group +with `chgrp 65532 key.json && chmod g+r key.json`. +1. If you can't control your file's group, you can directly change the public +permissions of your file by doing `chmod o+r key.json`. + +> [!WARNING] +> +> This can be insecure because it allows any user in the host system to read +> the credential file which they can use to authenticate to services in GCP. + +For example, a full command using a JSON credentials file might look like + +```shell +docker run \ + --publish : \ + --mount type=bind,source="$(pwd)"/sa.json,target=/config/sa.json \ + gcr.io/cloud-sql-connectors/cloud-sql-proxy:latest \ + --address 0.0.0.0 \ + --port \ + --credentials-file /config/sa.json +``` + ## Running as a Kubernetes Sidecar See the [example here][sidecar-example] as well as [Connecting from Google @@ -377,7 +665,7 @@ Kubernetes Engine][connect-to-k8s]. ## Running behind a Socks5 proxy -The Cloud SQL Auth proxy includes support for sending requests through a SOCKS5 +The Cloud SQL Auth Proxy includes support for sending requests through a SOCKS5 proxy. If a SOCKS5 proxy is running on `localhost:8000`, the command to start the Cloud SQL Auth Proxy would look like: @@ -400,17 +688,17 @@ for possible values. ## Support for Metrics and Tracing -The proxy supports [Cloud Monitoring][], [Cloud Trace][], and [Prometheus][]. +The Proxy supports [Cloud Monitoring][], [Cloud Trace][], and [Prometheus][]. Supported metrics include: -- `/cloudsqlconn/dial_latency`: The distribution of dialer latencies (ms) -- `/cloudsqlconn/open_connections`: The current number of open Cloud SQL +- `cloudsqlconn/dial_latency`: The distribution of dialer latencies (ms) +- `cloudsqlconn/open_connections`: The current number of open Cloud SQL connections -- `/cloudsqlconn/dial_failure_count`: The number of failed dial attempts -- `/cloudsqlconn/refresh_success_count`: The number of successful certificate +- `cloudsqlconn/dial_failure_count`: The number of failed dial attempts +- `cloudsqlconn/refresh_success_count`: The number of successful certificate refresh operations -- `/cloudsqlconn/refresh_failure_count`: The number of failed refresh +- `cloudsqlconn/refresh_failure_count`: The number of failed refresh operations. Supported traces include: @@ -426,7 +714,7 @@ Supported traces include: To enable Cloud Monitoring and Cloud Trace, use the `--telemetry-project` flag with the project where you want to view metrics and traces. To configure the metrics prefix used by Cloud Monitoring, use the `--telemetry-prefix` flag. When -enabling telementry, both Cloud Monitoring and Cloud Trace are enabled. To +enabling telemetry, both Cloud Monitoring and Cloud Trace are enabled. To disable Cloud Monitoring, use `--disable-metrics`. To disable Cloud Trace, use `--disable-traces`. @@ -434,30 +722,56 @@ To enable Prometheus, use the `--prometheus` flag. This will start an HTTP server on localhost with a `/metrics` endpoint. The Prometheus namespace may optionally be set with `--prometheus-namespace`. +## Debug logging + +To enable debug logging to report on internal certificate refresh operations, +use the `--debug-logs` flag. Typical use of the Proxy should not require debug +logs, but if you are surprised by the Proxy's behavior, debug logging should +provide insight into internal operations and can help when reporting issues. + +## Localhost Admin Server + +The Proxy includes support for an admin server on localhost. By default, the +the admin server is not enabled. To enable the server, pass the --debug or +--quitquitquit flag. This will start the server on localhost at port 9091. +To change the port, use the --admin-port flag. + +When --debug is set, the admin server enables Go's profiler available at +/debug/pprof/. + +See the [documentation on pprof][pprof] for details on how to use the +profiler. + +When --quitquitquit is set, the admin server adds an endpoint at +/quitquitquit. The admin server exits gracefully when it receives a GET or POST +request at /quitquitquit. + +[pprof]: https://pkg.go.dev/net/http/pprof. + ## Frequently Asked Questions -### Why would I use the proxy? +### Why would I use the Proxy? -The proxy is a convenient way to control access to your database using IAM +The Proxy is a convenient way to control access to your database using IAM permissions while ensuring a secure connection to your Cloud SQL instance. When -using the proxy, you do not have to manage database client certificates, -configured Authorized Networks, or ensure clients connect securely. The proxy +using the Proxy, you do not have to manage database client certificates, +configured Authorized Networks, or ensure clients connect securely. The Proxy handles all of this for you. -### How should I use the proxy? +### How should I use the Proxy? -The proxy is a gateway to your Cloud SQL instance. Clients connect to the proxy +The Proxy is a gateway to your Cloud SQL instance. Clients connect to the Proxy over an unencrypted connection and are authorized using the environment's IAM -principal. The proxy then encrypts the connection to your Cloud SQL instance. +principal. The Proxy then encrypts the connection to your Cloud SQL instance. Because client connections are not encrypted and authorized using the -environment's IAM principal, we recommend running the proxy on the same VM or -Kubernetes pod as your application and using the proxy's default behavior of +environment's IAM principal, we recommend running the Proxy on the same VM or +Kubernetes pod as your application and using the Proxy's default behavior of allowing connections from only the local network interface. This is the most secure configuration: unencrypted traffic does not leave the VM, and only connections from applications on the VM are allowed. -Here are some common examples of how to run the proxy in different environments: +Here are some common examples of how to run the Proxy in different environments: - [Connect to Cloud SQL for MySQL from your local computer][local-quickstart] - [Connect to Cloud SQL for MySQL from Google Kubernetes Engine][gke-quickstart] @@ -465,40 +779,50 @@ Here are some common examples of how to run the proxy in different environments: [local-quickstart]: https://cloud.google.com/sql/docs/mysql/connect-instance-local-computer [gke-quickstart]: https://cloud.google.com/sql/docs/mysql/connect-instance-kubernetes -### Why can't the proxy connect to my private IP instance? +### Why can't the Proxy connect to my private IP instance? -The proxy does not configure the network between the VM it's running on and the -Cloud SQL instance. You MUST ensure the proxy can reach your Cloud SQL +The Proxy does not configure the network between the VM it's running on and the +Cloud SQL instance. You MUST ensure the Proxy can reach your Cloud SQL instance, either by deploying it in a VPC that has access to your Private IP instance, or by configuring Public IP. -### Is there a library version of the proxy that I can use? +### Should I use the Proxy for large deployments? + +We recommend deploying the Proxy on the host machines that are running the +application. However, large deployments may exceed the request quota for the SQL +Admin API . If your Proxy reports request quota errors, we recommend deploying +the Proxy with a connection pooler like [pgbouncer][] or [ProxySQL][]. For +details, see [Running the Cloud SQL Proxy as a Service][service-example]. -Yes. Cloud SQL supports three language connectors: +### Can I share the Proxy across multiple applications? -- [Cloud SQL Go Connector][go connector] -- [Cloud SQL Java Connector](https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory) -- [Cloud SQL Python Connector](https://github.com/GoogleCloudPlatform/cloud-sql-python-connector) +Instead of using a single Proxy across multiple applications, we recommend using +one Proxy instance for every application process. The Proxy uses the context's +IAM principal and so have a 1-to-1 mapping between application and IAM principal +is best. If multiple applications use the same Proxy instance, then it becomes +unclear from an IAM perspective which principal is doing what. -The connectors for Go, Java, and Python offer the best experience when you are -writing an application in those languages. Use the proxy when your application -uses another language. +### How do I verify the shasum of a downloaded Proxy binary? -### Should I use the proxy for large deployments? +After downloading a binary from the releases page, copy the sha256sum value +that corresponds with the binary you chose. -We recommend deploying the proxy on the host machines that are running the -application. However, large deployments may exceed the request quota for the SQL -Admin API . If your proxy reports request quota errors, we recommend deploying -the proxy with a connection pooler like [pgbouncer][] or [ProxySQL][]. For -details, see [Running the Cloud SQL Proxy as a Service][service-example]. +Then run this command (make sure to add the asterisk before the file name): + +``` shell +echo ' *' | shasum -c +``` -### Can I share the proxy across mulitple applications? +For example, after downloading the v2.1.0 release of the Linux AMD64 Proxy, you +would run: + +``` shell +$ echo "547b24faf0dfe5e3d16bbc9f751dfa6b34dfd5e83f618f43a2988283de5208f2 *cloud-sql-proxy" | shasum -c +cloud-sql-proxy: OK +``` + +If you see `OK`, the binary is a verified match. -Instead of using a single proxy across multiple applications, we recommend using -one proxy instance for every application process. The proxy uses the context's -IAM principal and so have a 1-to-1 mapping between application and IAM principal -is best. If multiple applications use the same proxy instance, then it becomes -unclear from an IAM perspective which principal is doing what.\*\*\*\* [pgbouncer]: https://www.pgbouncer.org/ [proxysql]: https://www.proxysql.com/ @@ -506,10 +830,10 @@ unclear from an IAM perspective which principal is doing what.\*\*\*\* ## Reference Documentation - [Cloud SQL][cloud-sql] -- [Cloud SQL Auth proxy Documentation][proxy-page] -- [Cloud SQL Auth proxy Quickstarts][quickstarts] +- [Cloud SQL Auth Proxy Documentation][proxy-page] +- [Cloud SQL Auth Proxy Quickstarts][quickstarts] - [Cloud SQL Code Samples][code-samples] -- [Cloud SQL Auth proxy Package Documentation][pkg-docs] +- [Cloud SQL Auth Proxy Package Documentation][pkg-docs] ## Support policy @@ -518,18 +842,16 @@ unclear from an IAM perspective which principal is doing what.\*\*\*\* This project uses [semantic versioning](https://semver.org/), and uses the following lifecycle regarding support for a major version: -**Active** - Active versions get all new features and security fixes (that +- **Active** - Active versions get all new features and security fixes (that wouldn’t otherwise introduce a breaking change). New major versions are guaranteed to be "active" for a minimum of 1 year. -**Deprecated** - Deprecated versions continue to receive security and critical -bug fixes, but do not receive new features. Deprecated versions will be publicly -supported for 1 year. -**Unsupported** - Any major version that has been deprecated for >=1 year is -considered publicly unsupported. + +- **Maintenance** - Maintenance versions continue to receive security and critical +bug fixes, but do not receive new features. ### Release cadence -The Cloud SQL Auth proxy aims for a minimum monthly release cadence. If no new +The Cloud SQL Auth Proxy aims for a minimum monthly release cadence. If no new features or fixes have been added, a new PATCH version with the latest dependencies is released. @@ -549,12 +871,14 @@ By participating in this project you agree to abide by its terms. See [ci-build]: https://github.com/GoogleCloudPlatform/cloudsql-proxy/actions/workflows/tests.yaml?query=event%3Apush+branch%3Amain [cloud-sql]: https://cloud.google.com/sql [code-samples]: https://cloud.google.com/sql/docs/mysql/samples -[code-of-conduct]: CONTRIBUTING.md#contributor-code-of-conduct +[code-of-conduct]: CODE_OF_CONDUCT.md [connect-to-k8s]: https://cloud.google.com/sql/docs/mysql/connect-kubernetes-engine [connection-overview]: https://cloud.google.com/sql/docs/mysql/connect-overview [contributing]: CONTRIBUTING.md [health-check-example]: https://github.com/GoogleCloudPlatform/cloudsql-proxy/tree/main/examples/k8s-health-check#cloud-sql-proxy-health-checks -[iam-auth]: https://cloud.google.com/sql/docs/postgres/authentication#automatic +[iam-auth]: https://cloud.google.com/sql/docs/postgres/iam-authentication#auto-iam-auth +[iam-auth-config]: https://cloud.google.com/sql/docs/postgres/create-edit-iam-instances#configure-iam-db-instance +[iam-auth-user]: https://cloud.google.com/sql/docs/postgres/add-manage-iam-users#creating-a-database-user [pkg-badge]: https://pkg.go.dev/badge/github.com/GoogleCloudPlatform/cloudsql-proxy.svg [pkg-docs]: https://pkg.go.dev/github.com/GoogleCloudPlatform/cloudsql-proxy [private-ip]: https://cloud.google.com/sql/docs/mysql/private-ip#requirements_for_private_ip diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..8b58ae9c0 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,7 @@ +# Security Policy + +To report a security issue, please use [g.co/vulnz](https://g.co/vulnz). + +The Google Security Team will respond within 5 working days of your report on g.co/vulnz. + +We use g.co/vulnz for our intake, and do coordination and disclosure here using GitHub Security Advisory to privately discuss and fix the issue. diff --git a/build.sh b/build.sh new file mode 100755 index 000000000..3d907c3eb --- /dev/null +++ b/build.sh @@ -0,0 +1,260 @@ +#!/usr/bin/env bash + +# Copyright 2025 Google LLC. +# +# 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 SCRIPT_DIR to the current directory of this file. +SCRIPT_DIR=$(cd -P "$(dirname "$0")" >/dev/null 2>&1 && pwd) +SCRIPT_FILE="${SCRIPT_DIR}/$(basename "$0")" + +## +## Local Development +## +## These functions should be used to run the local development process +## + +## clean - Cleans the build output +function clean() { + if [[ -d '.tools' ]] ; then + rm -rf .tools + fi +} + +## build - Builds the project without running tests. +function build() { + go build -o ./cloud-sql-proxy main.go +} + +## test - Runs local unit tests. +function test() { + go test -v -race -cover -short ./... +} + +## e2e - Runs end-to-end integration tests. +function e2e() { + if [[ ! -f .envrc ]] ; then + write_e2e_env .envrc + fi + source .envrc + e2e_ci +} + +# e2e_ci - Run end-to-end integration tests in the CI system. +# This assumes that the secrets in the env vars are already set. +function e2e_ci() { + go test -race -v ./... | tee test_results.txt +} + +function get_golang_tool() { + name="$1" + github_repo="$2" + package="$3" + + # Download goimports tool + version=$(curl -s "https://api.github.com/repos/$github_repo/tags" | jq -r '.[].name' | head -n 1) + mkdir -p "$SCRIPT_DIR/.tools" + cmd="$SCRIPT_DIR/.tools/$name" + versioned_cmd="$SCRIPT_DIR/.tools/$name-$version" + if [[ ! -f "$versioned_cmd" ]] ; then + GOBIN="$SCRIPT_DIR/.tools" go install "$package@$version" + mv "$cmd" "$versioned_cmd" + if [[ -f "$cmd" ]] ; then + unlink "$cmd" + fi + ln -s "$versioned_cmd" "$cmd" + fi +} + +## fix - Fixes code format. +function fix() { + # run code formatting + get_golang_tool 'goimports' 'golang/tools' 'golang.org/x/tools/cmd/goimports' + ".tools/goimports" -w . + go mod tidy + go fmt ./... + + # Generate CMD docs + go run ./cmd/gendocs/gen_cloud-sql-proxy_docs.go +} + +## lint - runs the linters +function lint() { + # run lint checks + get_golang_tool 'golangci-lint' 'golangci/golangci-lint' 'github.com/golangci/golangci-lint/v2/cmd/golangci-lint' + ".tools/golangci-lint" run --timeout 3m + + # Check the commit includes a go.mod that is fully + # up to date. + fix + if [[ -d "$SCRIPT_DIR/.git" ]] ; then + git diff --exit-code + fi +} + +# lint_ci - runs lint in the CI build job, exiting with an error code if lint fails. +function lint_ci() { + lint # run lint + git diff --exit-code # fail if any files changed +} + +## deps - updates project dependencies to latest +function deps() { + go get -u ./... + go get -t -u ./... + + # Update the image label in the dockerfiles + for n in Dockerfile Dockerfile.* ; do + dockerfile_from_deps "$n" + done +} + +# find +function dockerfile_from_deps() { + # FROM gcr.io/distroless/static:nonroot@sha256:627d6c5a23ad24e6bdff827f16c7b60e0289029b0c79e9f7ccd54ae3279fb45f + # curl -X GET https://gcr.io/v2/distroless/static/manifests/nonroot + file=$1 + + # Get the last FROM statement from the dockerfile + # those ar + fromLine=$(grep "FROM" $1 | tail -n1) + imageUrl="${fromLine#FROM *}" + + # If the image URL does not contain a hash, then don't do anything. + if [[ $imageUrl != *@* ]] ; then + echo "Image does not contain a digest, ignoring" + return + fi + + oldDigest="${imageUrl#*@}" #after the '@' + imageWithoutHash="${imageUrl%%@sha256*}" #before the '@sha256' + imageName="${imageWithoutHash%%:*}" #before the ':' + + imageLabel="${imageWithoutHash#*:}" #after the ':' + # If none found, use "latest" as the label + if [[ "$imageLabel" == "$imageName" ]] ; then + imageLabel=latest + fi + + imageRepo="${imageName%%/*}" #first part of the image name path, may be a repo hostname + if [[ "$imageRepo" == *.* ]]; then + imageName="${imageName#*/}" # trim repo name host from imageName + manifestUrl="https://${imageRepo}/v2/${imageName}/manifests/${imageLabel}" + digest=$(curl -X GET "$manifestUrl" | \ + jq -r '.manifests[] | select(.platform.architecture=="amd64" and .platform.os=="linux") | .digest') + + else + # registry-1.docker.io requires a token + docker_io_token=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/alpine:pull" | jq -r .token) + manifestUrl="https://registry-1.docker.io/v2/${imageName}/manifests/${imageLabel}" + digest=$(curl -s -H "Authorization: Bearer $docker_io_token" \ + -H "Accept: application/vnd.docker.distribution.manifest.list.v2+json" \ + https://registry-1.docker.io/v2/library/alpine/manifests/3 | \ + jq -r '.manifests[] | select(.platform.architecture=="amd64" and .platform.os=="linux") | .digest') + fi + + if [[ "$oldDigest" == "$digest" ]] ; then + echo "No update to image to $file" + else + echo "Updating docker image to $file to $digest" + set -x + sed -i.bak -e "s/$oldDigest/$digest/g" "$file" + fi + if [[ -f "$file.bak" ]] ; then + rm "$file.bak" + fi + +} + +# write_e2e_env - Loads secrets from the gcloud project and writes +# them to target/e2e.env to run e2e tests. +function write_e2e_env(){ + # All secrets used by the e2e tests in the form = + secret_vars=( + MYSQL_CONNECTION_NAME=MYSQL_CONNECTION_NAME + MYSQL_USER=MYSQL_USER + MYSQL_PASS=MYSQL_PASS + MYSQL_DB=MYSQL_DB + MYSQL_MCP_CONNECTION_NAME=MYSQL_MCP_CONNECTION_NAME + MYSQL_MCP_PASS=MYSQL_MCP_PASS + POSTGRES_CONNECTION_NAME=POSTGRES_CONNECTION_NAME + POSTGRES_USER=POSTGRES_USER + POSTGRES_USER_IAM=POSTGRES_USER_IAM + POSTGRES_PASS=POSTGRES_PASS + POSTGRES_DB=POSTGRES_DB + POSTGRES_CAS_CONNECTION_NAME=POSTGRES_CAS_CONNECTION_NAME + POSTGRES_CAS_PASS=POSTGRES_CAS_PASS + POSTGRES_CUSTOMER_CAS_CONNECTION_NAME=POSTGRES_CUSTOMER_CAS_CONNECTION_NAME + POSTGRES_CUSTOMER_CAS_PASS=POSTGRES_CUSTOMER_CAS_PASS + POSTGRES_CUSTOMER_CAS_DOMAIN_NAME=POSTGRES_CUSTOMER_CAS_DOMAIN_NAME + POSTGRES_MCP_CONNECTION_NAME=POSTGRES_MCP_CONNECTION_NAME + POSTGRES_MCP_PASS=POSTGRES_MCP_PASS + SQLSERVER_CONNECTION_NAME=SQLSERVER_CONNECTION_NAME + SQLSERVER_USER=SQLSERVER_USER + SQLSERVER_PASS=SQLSERVER_PASS + SQLSERVER_DB=SQLSERVER_DB + IMPERSONATED_USER=IMPERSONATED_USER + ) + + if [[ -z "$TEST_PROJECT" ]] ; then + echo "Set TEST_PROJECT environment variable to the project containing" + echo "the e2e test suite secrets." + exit 1 + fi + + local_user=$(gcloud auth list --format 'value(account)' | tr -d '\n') + + echo "Getting test secrets from $TEST_PROJECT into $1" + { + for env_name in "${secret_vars[@]}" ; do + env_var_name="${env_name%%=*}" + secret_name="${env_name##*=}" + set -x + val=$(gcloud secrets versions access latest --project "$TEST_PROJECT" --secret="$secret_name") + echo "export $env_var_name='$val'" + done + + # Set IAM User env vars to the local gcloud user + echo "export MYSQL_IAM_USER='${local_user%%@*}'" + echo "export POSTGRES_USER_IAM='$local_user'" + } > "$1" + +} + +## help - prints the help details +## +function help() { + # This will print the comments beginning with ## above each function + # in this file. + + echo "build.sh " + echo + echo "Commands to assist with local development and CI builds." + echo + echo "Commands:" + echo + grep -e '^##' "$SCRIPT_FILE" | sed -e 's/##/ /' +} + +set -euo pipefail + +# Check CLI Arguments +if [[ "$#" -lt 1 ]] ; then + help + exit 1 +fi + +cd "$SCRIPT_DIR" + +"$@" + diff --git a/cmd/config_test.go b/cmd/config_test.go new file mode 100644 index 000000000..8b252dff5 --- /dev/null +++ b/cmd/config_test.go @@ -0,0 +1,142 @@ +// Copyright 2024 Google LLC +// +// 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 +// +// https://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. + +package cmd + +import ( + "testing" +) + +func assert[T comparable](t *testing.T, want, got T) { + t.Helper() + if got != want { + t.Errorf("got %v, want %v", got, want) + } +} + +func TestNewCommandWithConfigFile(t *testing.T) { + tcs := []struct { + desc string + args []string + setup func() + assert func(t *testing.T, c *Command) + }{ + { + desc: "toml config file", + args: []string{"--config-file", "testdata/config-toml.toml"}, + setup: func() {}, + assert: func(t *testing.T, c *Command) { + assert(t, 1, len(c.conf.Instances)) + assert(t, true, c.conf.Debug) + assert(t, 5555, c.conf.Port) + assert(t, true, c.conf.DebugLogs) + assert(t, true, c.conf.IAMAuthN) + }, + }, + { + desc: "yaml config file", + args: []string{"--config-file", "testdata/config-yaml.yaml"}, + setup: func() {}, + assert: func(t *testing.T, c *Command) { + assert(t, 1, len(c.conf.Instances)) + assert(t, true, c.conf.Debug) + }, + }, + { + desc: "json config file", + args: []string{"--config-file", "testdata/config-json.json"}, + setup: func() {}, + assert: func(t *testing.T, c *Command) { + assert(t, 1, len(c.conf.Instances)) + assert(t, true, c.conf.Debug) + }, + }, + { + desc: "config file with two instances", + args: []string{"--config-file", "testdata/two-instances.toml"}, + setup: func() {}, + assert: func(t *testing.T, c *Command) { + assert(t, 2, len(c.conf.Instances)) + }, + }, + { + desc: "instance argument overrides env config precedence", + args: []string{"proj:region:inst"}, + setup: func() { + t.Setenv("CSQL_PROXY_INSTANCE_CONNECTION_NAME", "p:r:i") + }, + assert: func(t *testing.T, c *Command) { + assert(t, "proj:region:inst", c.conf.Instances[0].Name) + }, + }, + { + desc: "instance env overrides config file precedence", + args: []string{"--config-file", "testdata/config.json"}, + setup: func() { + t.Setenv("CSQL_PROXY_INSTANCE_CONNECTION_NAME", "p:r:i") + }, + assert: func(t *testing.T, c *Command) { + assert(t, "p:r:i", c.conf.Instances[0].Name) + }, + }, + { + desc: "flag overrides env config precedence", + args: []string{"proj:region:inst", "--debug"}, + setup: func() { + t.Setenv("CSQL_PROXY_DEBUG", "false") + }, + assert: func(t *testing.T, c *Command) { + assert(t, true, c.conf.Debug) + }, + }, + { + desc: "flag overrides config file precedence", + args: []string{ + "proj:region:inst", + "--config-file", "testdata/config.toml", + "--debug", + }, + setup: func() {}, + assert: func(t *testing.T, c *Command) { + assert(t, true, c.conf.Debug) + }, + }, + { + desc: "env overrides config file precedence", + args: []string{ + "proj:region:inst", + "--config-file", "testdata/config.toml", + }, + setup: func() { + t.Setenv("CSQL_PROXY_DEBUG", "false") + }, + assert: func(t *testing.T, c *Command) { + assert(t, false, c.conf.Debug) + }, + }, + } + + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + tc.setup() + + cmd, err := invokeProxyCommand(tc.args) + if err != nil { + t.Fatalf("want error = nil, got = %v", err) + } + + tc.assert(t, cmd) + }) + } +} diff --git a/cmd/errors.go b/cmd/errors.go index 97f788fb7..fd383fecd 100644 --- a/cmd/errors.go +++ b/cmd/errors.go @@ -28,6 +28,16 @@ var ( Err: errors.New("SIGTERM signal received"), Code: 143, } + + errSigTermZero = &exitError{ + Err: errors.New("SIGTERM signal received"), + Code: 0, + } + + errQuitQuitQuit = &exitError{ + Err: errors.New("/quitquitquit received request"), + Code: 0, // This error guarantees a clean exit. + } ) func newBadCommandError(msg string) error { diff --git a/cmd/gendocs/gen_cloud-sql-proxy_docs.go b/cmd/gendocs/gen_cloud-sql-proxy_docs.go new file mode 100644 index 000000000..1829d54ea --- /dev/null +++ b/cmd/gendocs/gen_cloud-sql-proxy_docs.go @@ -0,0 +1,51 @@ +// Copyright 2024 Google LLC +// +// 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 +// +// https://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. + +package main + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/cmd" + "github.com/spf13/cobra/doc" +) + +func main() { + if len(os.Args) > 2 { + fmt.Fprintf(os.Stderr, "usage: %s [output directory]\n", os.Args[0]) + os.Exit(1) + } + + path := "docs/cmd" + if len(os.Args) == 2 { + path = os.Args[1] + } + + outDir, err := filepath.Abs(path) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to get output directory: %v\n", err) + os.Exit(1) + } + + // Set environment variables used so the output is consistent, + // regardless of where we run. + os.Setenv("TMPDIR", "/tmp") + + cloudSQLProxy := cmd.NewCommand() + cloudSQLProxy.Execute() + cloudSQLProxy.DisableAutoGenTag = true + doc.GenMarkdownTree(cloudSQLProxy.Command, outDir) +} diff --git a/cmd/options.go b/cmd/options.go new file mode 100644 index 000000000..aceb01843 --- /dev/null +++ b/cmd/options.go @@ -0,0 +1,107 @@ +// Copyright 2024 Google LLC +// +// 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 +// +// https://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. + +package cmd + +import "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/cloudsql" + +// Option is a function that configures a Command. +type Option func(*Command) + +// WithLogger overrides the default logger. +func WithLogger(l cloudsql.Logger) Option { + return func(c *Command) { + c.logger = l + } +} + +// WithDialer configures the Command to use the provided dialer to connect to +// Cloud SQL instances. +func WithDialer(d cloudsql.Dialer) Option { + return func(c *Command) { + c.dialer = d + } +} + +// WithFuseDir mounts a directory at the path using FUSE to access Cloud SQL +// instances. +func WithFuseDir(dir string) Option { + return func(c *Command) { + c.conf.FUSEDir = dir + } +} + +// WithFuseTempDir sets the temp directory where Unix sockets are created with +// FUSE +func WithFuseTempDir(dir string) Option { + return func(c *Command) { + c.conf.FUSETempDir = dir + } +} + +// WithMaxConnections sets the maximum allowed number of connections. Default +// is no limit. +func WithMaxConnections(mc uint64) Option { + return func(c *Command) { + c.conf.MaxConnections = mc + } +} + +// WithUserAgent sets additional user agents for Admin API tracking and should +// be a space separated list of additional user agents, e.g. +// cloud-sql-proxy-operator/0.0.1,other-agent/1.0.0 +func WithUserAgent(agent string) Option { + return func(c *Command) { + c.conf.OtherUserAgents = agent + } +} + +// WithAutoIP enables legacy behavior of v1 and will try to connect to first IP +// address returned by the SQL Admin API. In most cases, this flag should not +// be used. Prefer default of public IP or use --private-ip instead.` +func WithAutoIP() Option { + return func(c *Command) { + c.conf.AutoIP = true + } +} + +// WithQuietLogging configures the Proxy to log error messages only. +func WithQuietLogging() Option { + return func(c *Command) { + c.conf.Quiet = true + } +} + +// WithDebugLogging configures the Proxy to log debug level messages. +func WithDebugLogging() Option { + return func(c *Command) { + c.conf.DebugLogs = true + } +} + +// WithLazyRefresh configures the Proxy to refresh connection info on an +// as-needed basis when the cached copy has expired. +func WithLazyRefresh() Option { + return func(c *Command) { + c.conf.LazyRefresh = true + } +} + +// WithConnRefuseNotify configures the Proxy to call the provided function when +// a connection is refused. The notification function is run in a goroutine. +func WithConnRefuseNotify(n func()) Option { + return func(c *Command) { + c.connRefuseNotify = n + } +} diff --git a/cmd/options_test.go b/cmd/options_test.go new file mode 100644 index 000000000..c3dc718f6 --- /dev/null +++ b/cmd/options_test.go @@ -0,0 +1,225 @@ +// Copyright 2024 Google LLC +// +// 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 +// +// https://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. + +package cmd + +import ( + "errors" + "fmt" + "io" + "runtime" + "testing" + + "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/cloudsql" + "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/internal/log" + "github.com/spf13/cobra" +) + +type testDialer struct { + cloudsql.Dialer +} + +func TestCommandOptions(t *testing.T) { + logger := log.NewStdLogger(io.Discard, io.Discard) + dialer := &testDialer{} + tcs := []struct { + desc string + isValid func(*Command) error + option Option + skip bool + }{ + { + desc: "with logger", + isValid: func(c *Command) error { + if c.logger != logger { + return errors.New("loggers do not match") + } + return nil + }, + option: WithLogger(logger), + }, + { + desc: "with dialer", + isValid: func(c *Command) error { + if c.dialer != dialer { + return errors.New("dialers do not match") + } + return nil + }, + option: WithDialer(dialer), + }, + { + desc: "with FUSE dir", + isValid: func(c *Command) error { + if c.conf.FUSEDir != "somedir" { + return fmt.Errorf( + "want = %v, got = %v", "somedir", c.conf.FUSEDir, + ) + } + return nil + }, + option: WithFuseDir("somedir"), + // FUSE isn't available on GitHub macOS runners + // and FUSE isn't supported on Windows, so skip this test. + skip: runtime.GOOS == "darwin" || runtime.GOOS == "windows", + }, + { + desc: "with FUSE temp dir", + isValid: func(c *Command) error { + if c.conf.FUSETempDir != "somedir" { + return fmt.Errorf( + "want = %v, got = %v", "somedir", c.conf.FUSEDir, + ) + } + return nil + }, + option: WithFuseTempDir("somedir"), + // FUSE isn't available on GitHub macOS runners + // and FUSE isn't supported on Windows, so skip this test. + skip: runtime.GOOS == "darwin" || runtime.GOOS == "windows", + }, + { + desc: "with max connections", + isValid: func(c *Command) error { + if c.conf.MaxConnections != 1 { + return fmt.Errorf( + "want = %v, got = %v", 1, c.conf.MaxConnections, + ) + } + return nil + }, + option: WithMaxConnections(1), + }, + { + desc: "with user agent", + isValid: func(c *Command) error { + if c.conf.OtherUserAgents != "agents-go-here" { + return fmt.Errorf( + "want = %v, got = %v", + "agents-go-here", c.conf.OtherUserAgents, + ) + } + return nil + }, + option: WithUserAgent("agents-go-here"), + }, + { + desc: "with auto IP", + isValid: func(c *Command) error { + if !c.conf.AutoIP { + return errors.New("auto IP was false, but should be true") + } + return nil + }, + option: WithAutoIP(), + }, + { + desc: "with quiet logging", + isValid: func(c *Command) error { + if !c.conf.Quiet { + return errors.New("quiet was false, but should be true") + } + return nil + }, + option: WithQuietLogging(), + }, + { + desc: "with lazy refresh", + isValid: func(c *Command) error { + if !c.conf.LazyRefresh { + return errors.New( + "LazyRefresh was false, but should be true", + ) + } + return nil + }, + option: WithLazyRefresh(), + }, + } + + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + if tc.skip { + t.Skip("skipping unsupported test case") + } + got, err := invokeProxyWithOption(nil, tc.option) + if err != nil { + t.Fatal(err) + } + if err := tc.isValid(got); err != nil { + t.Errorf("option did not initialize command correctly: %v", err) + } + }) + } +} + +func TestCommandOptionsOverridesCLI(t *testing.T) { + tcs := []struct { + desc string + isValid func(*Command) error + option Option + args []string + }{ + { + desc: "with duplicate max connections", + isValid: func(c *Command) error { + if c.conf.MaxConnections != 10 { + return errors.New("max connections do not match") + } + return nil + }, + option: WithMaxConnections(10), + args: []string{"--max-connections", "20"}, + }, + { + desc: "with quiet logging", + isValid: func(c *Command) error { + if !c.conf.Quiet { + return errors.New("quiet was false, but should be true") + } + return nil + }, + option: WithQuietLogging(), + args: []string{"--quiet", "false"}, + }, + } + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + got, err := invokeProxyWithOption(tc.args, tc.option) + if err != nil { + t.Fatal(err) + } + if err := tc.isValid(got); err != nil { + t.Errorf("option did not initialize command correctly: %v", err) + } + }) + } +} + +func invokeProxyWithOption(args []string, o Option) (*Command, error) { + c := NewCommand(o) + // Keep the test output quiet + c.SilenceUsage = true + c.SilenceErrors = true + // Disable execute behavior + c.RunE = func(*cobra.Command, []string) error { + return nil + } + args = append(args, "test-project:us-central1:test-instance") + c.SetArgs(args) + + err := c.Execute() + + return c, err +} diff --git a/cmd/root.go b/cmd/root.go index 77259daf5..89792bec2 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -22,12 +22,14 @@ import ( "io" "net" "net/http" + "net/http/pprof" "net/url" "os" "os/signal" "path/filepath" "strconv" "strings" + "sync" "syscall" "time" @@ -37,6 +39,7 @@ import ( "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/internal/healthcheck" "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/internal/log" "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/internal/proxy" + "github.com/coreos/go-systemd/v22/daemon" "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/spf13/viper" @@ -47,20 +50,33 @@ var ( // versionString indicates the version of this library. //go:embed version.txt versionString string - userAgent string + // metadataString indicates additional build or distribution metadata. + metadataString string + userAgent string ) func init() { - versionString = strings.TrimSpace(versionString) + versionString = semanticVersion() userAgent = "cloud-sql-proxy/" + versionString } +// semanticVersion returns the version of the proxy including a compile-time +// metadata. +func semanticVersion() string { + v := strings.TrimSpace(versionString) + if metadataString != "" { + v += "+" + metadataString + } + return v +} + // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { if err := NewCommand().Execute(); err != nil { exit := 1 - if terr, ok := err.(*exitError); ok { + var terr *exitError + if errors.As(err, &terr) { exit = terr.Code } os.Exit(exit) @@ -70,363 +86,694 @@ func Execute() { // Command represents an invocation of the Cloud SQL Auth Proxy. type Command struct { *cobra.Command - conf *proxy.Config - logger cloudsql.Logger - dialer cloudsql.Dialer - - cleanup func() error - disableTraces bool - telemetryTracingSampleRate int - disableMetrics bool - telemetryProject string - telemetryPrefix string - prometheus bool - prometheusNamespace string - healthCheck bool - httpAddress string - httpPort string - quiet bool - runtime string - - // impersonationChain is a comma separated list of one or more service - // accounts. The first entry in the chain is the impersonation target. Any - // additional service accounts after the target are delegates. The - // roles/iam.serviceAccountTokenCreator must be configured for each account - // that will be impersonated. - impersonationChain string -} - -// Option is a function that configures a Command. -type Option func(*Command) - -// WithLogger overrides the default logger. -func WithLogger(l cloudsql.Logger) Option { - return func(c *Command) { - c.logger = l - } -} - -// WithDialer configures the Command to use the provided dialer to connect to -// Cloud SQL instances. -func WithDialer(d cloudsql.Dialer) Option { - return func(c *Command) { - c.dialer = d - } + conf *proxy.Config + logger cloudsql.Logger + dialer cloudsql.Dialer + cleanup func() error + connRefuseNotify func() } var longHelp = ` Overview - The Cloud SQL Auth proxy is a utility for ensuring secure connections to - your Cloud SQL instances. It provides IAM authorization, allowing you to - control who can connect to your instance through IAM permissions, and TLS - 1.3 encryption, without having to manage certificates. + The Cloud SQL Auth Proxy is a utility for ensuring secure connections to + your Cloud SQL instances. It provides IAM authorization, allowing you to + control who can connect to your instance through IAM permissions, and TLS + 1.3 encryption, without having to manage certificates. - NOTE: The proxy does not configure the network. You MUST ensure the proxy - can reach your Cloud SQL instance, either by deploying it in a VPC that has - access to your Private IP instance, or by configuring Public IP. + NOTE: The Proxy does not configure the network. You MUST ensure the Proxy + can reach your Cloud SQL instance, either by deploying it in a VPC that has + access to your Private IP instance, or by configuring Public IP. - For every provided instance connection name, the proxy creates: + For every provided instance connection name, the Proxy creates: - - a socket that mimics a database running locally, and - - an encrypted connection using TLS 1.3 back to your Cloud SQL instance. + - a socket that mimics a database running locally, and + - an encrypted connection using TLS 1.3 back to your Cloud SQL instance. - The proxy uses an ephemeral certificate to establish a secure connection to - your Cloud SQL instance. The proxy will refresh those certificates on an - hourly basis. Existing client connections are unaffected by the refresh - cycle. + The Proxy uses an ephemeral certificate to establish a secure connection to + your Cloud SQL instance. The Proxy will refresh those certificates on an + hourly basis. Existing client connections are unaffected by the refresh + cycle. Starting the Proxy - To start the proxy, you will need your instance connection name, which may - be found in the Cloud SQL instance overview page or by using gcloud with the - following command: + To start the Proxy, you will need your instance connection name, which may + be found in the Cloud SQL instance overview page or by using gcloud with the + following command: - gcloud sql instances describe INSTANCE --format='value(connectionName)' + gcloud sql instances describe INSTANCE --format='value(connectionName)' - For example, if your instance connection name is - "my-project:us-central1:my-db-server", starting the proxy will be: + For example, if your instance connection name is + "my-project:us-central1:my-db-server", starting the Proxy will be: - ./cloud-sql-proxy my-project:us-central1:my-db-server + ./cloud-sql-proxy my-project:us-central1:my-db-server - By default, the proxy will determine the database engine and start a - listener on localhost using the default database engine's port, i.e., MySQL - is 3306, Postgres is 5432, SQL Server is 1433. If multiple instances are - specified which all use the same database engine, the first will be started - on the default database port and subsequent instances will be incremented - from there (e.g., 3306, 3307, 3308, etc). To disable this behavior (and - reduce startup time), use the --port flag. All subsequent listeners will - increment from the provided value. + By default, the Proxy will determine the database engine and start a + listener on localhost using the default database engine's port, i.e., MySQL + is 3306, Postgres is 5432, SQL Server is 1433. If multiple instances are + specified which all use the same database engine, the first will be started + on the default database port and subsequent instances will be incremented + from there (e.g., 3306, 3307, 3308, etc). To disable this behavior (and + reduce startup time), use the --port flag. All subsequent listeners will + increment from the provided value. - All socket listeners use the localhost network interface. To override this - behavior, use the --address flag. + All socket listeners use the localhost network interface. To override this + behavior, use the --address flag. Instance Level Configuration - The proxy supports overriding configuration on an instance-level with an - optional query string syntax using the corresponding full flag name. The - query string takes the form of a URL query string and should be appended to - the INSTANCE_CONNECTION_NAME, e.g., + The Proxy supports overriding configuration on an instance-level with an + optional query string syntax using the corresponding full flag name. The + query string takes the form of a URL query string and should be appended to + the INSTANCE_CONNECTION_NAME, e.g., - 'my-project:us-central1:my-db-server?key1=value1&key2=value2' + 'my-project:us-central1:my-db-server?key1=value1&key2=value2' - When using the optional query string syntax, quotes must wrap the instance - connection name and query string to prevent conflicts with the shell. For - example, to override the address and port for one instance but otherwise use - the default behavior, use: + When using the optional query string syntax, quotes must wrap the instance + connection name and query string to prevent conflicts with the shell. For + example, to override the address and port for one instance but otherwise use + the default behavior, use: - ./cloud-sql-proxy \ - my-project:us-central1:my-db-server \ - 'my-project:us-central1:my-other-server?address=0.0.0.0&port=7000' + ./cloud-sql-proxy \ + my-project:us-central1:my-db-server \ + 'my-project:us-central1:my-other-server?address=0.0.0.0&port=7000' + + When necessary, you may specify the full path to a Unix socket. Set the + unix-socket-path query parameter to the absolute path of the Unix socket for + the database instance. The parent directory of the unix-socket-path must + exist when the Proxy starts or else socket creation will fail. For Postgres + instances, the Proxy will ensure that the last path element is + '.s.PGSQL.5432' appending it if necessary. For example, + + ./cloud-sql-proxy \ + 'my-project:us-central1:my-db-server?unix-socket-path=/path/to/socket' Health checks - When enabling the --health-checks flag, the proxy will start an HTTP server - on localhost with three endpoints: + When enabling the --health-check flag, the Proxy will start an HTTP server + on localhost with three endpoints: - - /startup: Returns 200 status when the proxy has finished starting up. - Otherwise returns 503 status. + - /startup: Returns 200 status when the Proxy has finished starting up. + Otherwise returns 503 status. - - /readiness: Returns 200 status when the proxy has started, has available - connections if max connections have been set with the --max-connections - flag, and when the proxy can connect to all registered instances. Otherwise, - returns a 503 status. Optionally supports a min-ready query param (e.g., - /readiness?min-ready=3) where the proxy will return a 200 status if the - proxy can connect successfully to at least min-ready number of instances. If - min-ready exceeds the number of registered instances, returns a 400. + - /readiness: Returns 200 status when the Proxy has started, has available + connections if max connections have been set with the --max-connections + flag, and when the Proxy can connect to all registered instances. Otherwise, + returns a 503 status. - - /liveness: Always returns 200 status. If this endpoint is not responding, - the proxy is in a bad state and should be restarted. + - /liveness: Always returns 200 status. If this endpoint is not responding, + the Proxy is in a bad state and should be restarted. - To configure the address, use --http-server. + To configure the address, use --http-address. To configure the port, use + --http-port. Service Account Impersonation - The proxy supports service account impersonation with the - --impersonate-service-account flag and matches gcloud's flag. When enabled, - all API requests are made impersonating the supplied service account. The - IAM principal must have the iam.serviceAccounts.getAccessToken permission or - the role roles/iam.serviceAccounts.serviceAccountTokenCreator. + The Proxy supports service account impersonation with the + --impersonate-service-account flag and matches gclouds flag. When enabled, + all API requests are made impersonating the supplied service account. The + IAM principal must have the iam.serviceAccounts.getAccessToken permission or + the role roles/iam.serviceAccounts.serviceAccountTokenCreator. - For example: + For example: - ./cloud-sql-proxy \ - --impersonate-service-account=impersonated@my-project.iam.gserviceaccount.com - my-project:us-central1:my-db-server + ./cloud-sql-proxy \ + --impersonate-service-account=impersonated@my-project.iam.gserviceaccount.com + my-project:us-central1:my-db-server - In addition, the flag supports an impersonation delegation chain where the - value is a comma-separated list of service accounts. The first service - account in the list is the impersonation target. Each subsequent service - account is a delegate to the previous service account. When delegation is - used, each delegate must have the permissions named above on the service - account it is delegating to. + In addition, the flag supports an impersonation delegation chain where the + value is a comma-separated list of service accounts. The first service + account in the list is the impersonation target. Each subsequent service + account is a delegate to the previous service account. When delegation is + used, each delegate must have the permissions named above on the service + account it is delegating to. - For example: + For example: - ./cloud-sql-proxy \ - --impersonate-service-account=SERVICE_ACCOUNT_1,SERVICE_ACCOUNT_2,SERVICE_ACCOUNT_3 - my-project:us-central1:my-db-server + ./cloud-sql-proxy \ + --impersonate-service-account=SERVICE_ACCOUNT_1,SERVICE_ACCOUNT_2,SERVICE_ACCOUNT_3 + my-project:us-central1:my-db-server - In this example, the environment's IAM principal impersonates - SERVICE_ACCOUNT_3 which impersonates SERVICE_ACCOUNT_2 which then - impersonates the target SERVICE_ACCOUNT_1. + In this example, the environment's IAM principal impersonates + SERVICE_ACCOUNT_3 which impersonates SERVICE_ACCOUNT_2 which then + impersonates the target SERVICE_ACCOUNT_1. Configuration using environment variables - Instead of using CLI flags, the proxy may be configured using environment - variables. Each environment variable uses "CSQL_PROXY" as a prefix and is - the uppercase version of the flag using underscores as word delimiters. For - example, the --auto-iam-authn flag may be set with the environment variable - CSQL_PROXY_AUTO_IAM_AUTHN. An invocation of the proxy using environment - variables would look like the following: + Instead of using CLI flags, the Proxy may be configured using environment + variables. Each environment variable uses "CSQL_PROXY" as a prefix and is + the uppercase version of the flag using underscores as word delimiters. For + example, the --auto-iam-authn flag may be set with the environment variable + CSQL_PROXY_AUTO_IAM_AUTHN. An invocation of the Proxy using environment + variables would look like the following: + + CSQL_PROXY_AUTO_IAM_AUTHN=true \ + ./cloud-sql-proxy my-project:us-central1:my-db-server + + In addition to CLI flags, instance connection names may also be specified + with environment variables. If invoking the Proxy with only one instance + connection name, use CSQL_PROXY_INSTANCE_CONNECTION_NAME. For example: + + CSQL_PROXY_INSTANCE_CONNECTION_NAME=my-project:us-central1:my-db-server \ + ./cloud-sql-proxy + + If multiple instance connection names are used, add the index of the + instance connection name as a suffix. For example: + + CSQL_PROXY_INSTANCE_CONNECTION_NAME_0=my-project:us-central1:my-db-server \ + CSQL_PROXY_INSTANCE_CONNECTION_NAME_1=my-other-project:us-central1:my-other-server \ + ./cloud-sql-proxy + +Configuration using a configuration file + + Instead of using CLI flags, the Proxy may be configured using a configuration + file. The configuration file is a TOML, YAML or JSON file with the same keys + as the environment variables. The configuration file is specified with the + --config-file flag. An invocation of the Proxy using a configuration file + would look like the following: + + ./cloud-sql-proxy --config-file=config.toml - CSQL_PROXY_AUTO_IAM_AUTHN=true \ - ./cloud-sql-proxy my-project:us-central1:my-db-server + The configuration file may look like the following: - In addition to CLI flags, instance connection names may also be specified - with environment variables. If invoking the proxy with only one instance - connection name, use CSQL_PROXY_INSTANCE_CONNECTION_NAME. For example: + instance-connection-name = "my-project:us-central1:my-server-instance" + auto-iam-authn = true - CSQL_PROXY_INSTANCE_CONNECTION_NAME=my-project:us-central1:my-db-server \ - ./cloud-sql-proxy + If multiple instance connection names are used, add the index of the + instance connection name as a suffix. For example: - If multiple instance connection names are used, add the index of the - instance connection name as a suffix. For example: + instance-connection-name-0 = "my-project:us-central1:my-db-server" + instance-connection-name-1 = "my-other-project:us-central1:my-other-server" - CSQL_PROXY_INSTANCE_CONNECTION_NAME_0=my-project:us-central1:my-db-server \ - CSQL_PROXY_INSTANCE_CONNECTION_NAME_1=my-other-project:us-central1:my-other-server \ - ./cloud-sql-proxy + The configuration file may also contain the same keys as the environment + variables and flags. For example: + + auto-iam-authn = true + debug = true + max-connections = 5 + +Localhost Admin Server + + The Proxy includes support for an admin server on localhost. By default, + the admin server is not enabled. To enable the server, pass the --debug or + --quitquitquit flag. This will start the server on localhost at port 9091. + To change the port, use the --admin-port flag. + + When --debug is set, the admin server enables Go's profiler available at + /debug/pprof/. + + See the documentation on pprof for details on how to use the + profiler at https://pkg.go.dev/net/http/pprof. + + When --quitquitquit is set, the admin server adds an endpoint at + /quitquitquit. The admin server exits gracefully when it receives a GET or POST + request at /quitquitquit. + +Debug logging + + On occasion, it can help to enable debug logging which will report on + internal certificate refresh operations. To enable debug logging, use: + + ./cloud-sql-proxy --debug-logs + +Waiting for Startup + + See the wait subcommand's help for details. (*) indicates a flag that may be used as a query parameter +Third Party Licenses + + To view all licenses for third party dependencies used within this + distribution please see: + + https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.21.0/third_party/licenses.tar.gz {x-release-please-version} ` -const envPrefix = "CSQL_PROXY" +var waitHelp = ` +Waiting for Proxy Startup -func instanceFromEnv(args []string) []string { - // This supports naming the first instance first with: - // INSTANCE_CONNECTION_NAME - // or if that's not defined, with: - // INSTANCE_CONNECTION_NAME_0 - inst := os.Getenv(fmt.Sprintf("%s_INSTANCE_CONNECTION_NAME", envPrefix)) - if inst == "" { - inst = os.Getenv(fmt.Sprintf("%s_INSTANCE_CONNECTION_NAME_0", envPrefix)) - if inst == "" { - return nil - } + Sometimes it is necessary to wait for the Proxy to start. + + To help ensure the Proxy is up and ready, the Proxy includes a wait + subcommand with an optional --max flag to set the maximum time to wait. + The wait command uses a separate Proxy's startup endpoint to determine + if the other Proxy process is ready. + + Invoke the wait command, like this: + + # waits for another Proxy process' startup endpoint to respond + ./cloud-sql-proxy wait + +Configuration + + By default, the Proxy will wait up to the maximum time for the startup + endpoint to respond. The wait command requires that the Proxy be started in + another process with the HTTP health check or Prometheus enabled. If an + alternate health check port or address is used, as in: + + ./cloud-sql-proxy \ + --http-address 0.0.0.0 \ + --http-port 9191 \ + --health-check + + Then the wait command must also be told to use the same custom values: + + ./cloud-sql-proxy wait \ + --http-address 0.0.0.0 \ + --http-port 9191 + + By default the wait command will wait 30 seconds. To alter this value, + use: + + ./cloud-sql-proxy wait --max 10s +` + +var shutdownHelp = ` +Shutting Down the Proxy + + The shutdown command signals a running Proxy process to gracefully shut + down. This is useful for scripting and for Kubernetes environments. + + The shutdown command requires that the Proxy be started in another process + with the admin server enabled. For example: + + ./cloud-sql-proxy --quitquitquit + + Invoke the shutdown command like this: + + # signals another Proxy process to shut down + ./cloud-sql-proxy shutdown + +Configuration + + If the running Proxy is configured with a non-default admin port, the + shutdown command must also be told to use the same custom value: + + ./cloud-sql-proxy shutdown --admin-port 9192 +` + +const ( + waitMaxFlag = "max" + adminPortFlag = "admin-port" + httpAddressFlag = "http-address" + httpPortFlag = "http-port" +) + +func runWaitCmd(c *cobra.Command, _ []string) error { + a, _ := c.Flags().GetString(httpAddressFlag) + p, _ := c.Flags().GetString(httpPortFlag) + addr := fmt.Sprintf("http://%v:%v/startup", a, p) + + wait, err := c.Flags().GetDuration(waitMaxFlag) + if err != nil { + // This error should always be nil. If the error occurs, it means the + // wait flag name has changed where it was registered. + return err } - args = append(args, inst) + c.SilenceUsage = true - i := 1 + t := time.After(wait) for { - instN := os.Getenv(fmt.Sprintf("%s_INSTANCE_CONNECTION_NAME_%d", envPrefix, i)) - // if the next instance connection name is not defined, stop checking - // environment variables. - if instN == "" { - break + select { + case <-t: + return errors.New("command failed to complete successfully") + default: + resp, err := http.Get(addr) + if err != nil || resp.StatusCode != http.StatusOK { + time.Sleep(time.Second) + break + } + return nil } - args = append(args, instN) - i++ } - return args } +func runShutdownCmd(c *cobra.Command, _ []string) error { + p, _ := c.Flags().GetString(adminPortFlag) + addr := fmt.Sprintf("http://127.0.0.1:%v/quitquitquit", p) + c.SilenceUsage = true + + req, err := http.NewRequestWithContext(c.Context(), "POST", addr, nil) + if err != nil { + return fmt.Errorf("failed to create shutdown request: %w", err) + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return fmt.Errorf("failed to send shutdown request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("shutdown request failed: status code %v, %v", resp.StatusCode, resp.Status) + } + + return nil +} + +const envPrefix = "CSQL_PROXY" + // NewCommand returns a Command object representing an invocation of the proxy. func NewCommand(opts ...Option) *Command { - cmd := &cobra.Command{ + rootCmd := &cobra.Command{ Use: "cloud-sql-proxy INSTANCE_CONNECTION_NAME...", Version: versionString, Short: "cloud-sql-proxy authorizes and encrypts connections to Cloud SQL.", - Long: longHelp, + //remove the inline annotation required by release-please to update version. + Long: strings.ReplaceAll(longHelp, "{x-release-please-version}", ""), } logger := log.NewStdLogger(os.Stdout, os.Stderr) c := &Command{ - Command: cmd, + Command: rootCmd, logger: logger, cleanup: func() error { return nil }, conf: &proxy.Config{ UserAgent: userAgent, }, } - for _, o := range opts { - o(c) + var waitCmd = &cobra.Command{ + Use: "wait", + Short: "Wait for another Proxy process to start", + Long: waitHelp, + RunE: runWaitCmd, } + waitFlags := waitCmd.Flags() + waitFlags.DurationP( + waitMaxFlag, "m", + 30*time.Second, + "maximum amount of time to wait for startup", + ) + rootCmd.AddCommand(waitCmd) - cmd.Args = func(cmd *cobra.Command, args []string) error { - // If args is not already populated, try to read from the environment. - if len(args) == 0 { - args = instanceFromEnv(args) - } - // Handle logger separately from config - if c.conf.StructuredLogs { - c.logger, c.cleanup = log.NewStructuredLogger() - } - if c.quiet { - c.logger = log.NewStdLogger(io.Discard, os.Stderr) - } - err := parseConfig(c, c.conf, args) - if err != nil { - return err - } - // The arguments are parsed. Usage is no longer needed. - cmd.SilenceUsage = true - // Errors will be handled by logging from here on. - cmd.SilenceErrors = true - return nil + var shutdownCmd = &cobra.Command{ + Use: "shutdown", + Short: "Signal a running Proxy process to shut down", + Long: shutdownHelp, + RunE: runShutdownCmd, } + shutdownFlags := shutdownCmd.Flags() + shutdownFlags.String( + adminPortFlag, + "9091", + "port for the admin server", + ) + rootCmd.AddCommand(shutdownCmd) + + rootCmd.Args = func(_ *cobra.Command, args []string) error { + // Load the configuration file before running the command. This should + // ensure that the configuration is loaded in the correct order: + // + // flags > environment variables > configuration files + // + // See https://github.com/carolynvs/stingoftheviper for more info + return loadConfig(c, args, opts) + } + rootCmd.RunE = func(*cobra.Command, []string) error { return runSignalWrapper(c) } - cmd.RunE = func(*cobra.Command, []string) error { return runSignalWrapper(c) } - - pflags := cmd.PersistentFlags() + // Flags that apply only to the root command + localFlags := rootCmd.Flags() + // Flags that apply to all sub-commands + globalFlags := rootCmd.PersistentFlags() - // Override Cobra's default messages. - pflags.BoolP("help", "h", false, "Display help information for cloud-sql-proxy") - pflags.BoolP("version", "v", false, "Print the cloud-sql-proxy version") + localFlags.BoolP("help", "h", false, "Display help information for cloud-sql-proxy") + localFlags.BoolP("version", "v", false, "Print the cloud-sql-proxy version") - // Global-only flags - pflags.StringVar(&c.runtime, "runtime", "", - "(for internal use only) Runtime and version, e.g. cloud-sql-proxy-operator/0.0.1") - pflags.StringVarP(&c.conf.Token, "token", "t", "", + localFlags.StringVar(&c.conf.Filepath, "config-file", c.conf.Filepath, + "Path to a TOML file containing configuration options.") + localFlags.StringVar(&c.conf.OtherUserAgents, "user-agent", "", + "Space separated list of additional user agents, e.g. cloud-sql-proxy-operator/0.0.1") + localFlags.StringVarP(&c.conf.Token, "token", "t", "", "Use bearer token as a source of IAM credentials.") - pflags.StringVarP(&c.conf.CredentialsFile, "credentials-file", "c", "", + localFlags.StringVar(&c.conf.LoginToken, "login-token", "", + "Use bearer token as a database password (used with token and auto-iam-authn only)") + localFlags.StringVarP(&c.conf.CredentialsFile, "credentials-file", "c", "", "Use service account key file as a source of IAM credentials.") - pflags.StringVarP(&c.conf.CredentialsJSON, "json-credentials", "j", "", + localFlags.StringVarP(&c.conf.CredentialsJSON, "json-credentials", "j", "", "Use service account key JSON as a source of IAM credentials.") - pflags.BoolVarP(&c.conf.GcloudAuth, "gcloud-auth", "g", false, - "Use gcloud's user credentials as a source of IAM credentials.") - pflags.BoolVarP(&c.conf.StructuredLogs, "structured-logs", "l", false, + localFlags.BoolVarP(&c.conf.GcloudAuth, "gcloud-auth", "g", false, + `Use gclouds user credentials as a source of IAM credentials. +NOTE: this flag is a legacy feature and generally should not be used. +Instead prefer Application Default Credentials +(enabled with: gcloud auth application-default login) which +the Proxy will then pick-up automatically.`) + localFlags.BoolVarP(&c.conf.StructuredLogs, "structured-logs", "l", false, "Enable structured logging with LogEntry format") - pflags.Uint64Var(&c.conf.MaxConnections, "max-connections", 0, + localFlags.BoolVar(&c.conf.DebugLogs, "debug-logs", false, + "Enable debug logging") + localFlags.Uint64Var(&c.conf.MaxConnections, "max-connections", 0, "Limit the number of connections. Default is no limit.") - pflags.DurationVar(&c.conf.WaitOnClose, "max-sigterm-delay", 0, + localFlags.DurationVar(&c.conf.WaitBeforeClose, "min-sigterm-delay", 0, + "The number of seconds to accept new connections after receiving a TERM signal.") + localFlags.DurationVar(&c.conf.WaitOnClose, "max-sigterm-delay", 0, "Maximum number of seconds to wait for connections to close after receiving a TERM signal.") - pflags.StringVar(&c.telemetryProject, "telemetry-project", "", + localFlags.StringVar(&c.conf.TelemetryProject, "telemetry-project", "", "Enable Cloud Monitoring and Cloud Trace with the provided project ID.") - pflags.BoolVar(&c.disableTraces, "disable-traces", false, + localFlags.BoolVar(&c.conf.DisableTraces, "disable-traces", false, "Disable Cloud Trace integration (used with --telemetry-project)") - pflags.IntVar(&c.telemetryTracingSampleRate, "telemetry-sample-rate", 10_000, + localFlags.IntVar(&c.conf.TelemetryTracingSampleRate, "telemetry-sample-rate", 10_000, "Set the Cloud Trace sample rate. A smaller number means more traces.") - pflags.BoolVar(&c.disableMetrics, "disable-metrics", false, + localFlags.BoolVar(&c.conf.DisableMetrics, "disable-metrics", false, "Disable Cloud Monitoring integration (used with --telemetry-project)") - pflags.StringVar(&c.telemetryPrefix, "telemetry-prefix", "", + localFlags.StringVar(&c.conf.TelemetryPrefix, "telemetry-prefix", "", "Prefix for Cloud Monitoring metrics.") - pflags.BoolVar(&c.prometheus, "prometheus", false, + localFlags.BoolVar(&c.conf.ExitZeroOnSigterm, "exit-zero-on-sigterm", false, + "Exit with 0 exit code when Sigterm received (default is 143)") + localFlags.BoolVar(&c.conf.Prometheus, "prometheus", false, "Enable Prometheus HTTP endpoint /metrics on localhost") - pflags.StringVar(&c.prometheusNamespace, "prometheus-namespace", "", + localFlags.StringVar(&c.conf.PrometheusNamespace, "prometheus-namespace", "", "Use the provided Prometheus namespace for metrics") - pflags.StringVar(&c.httpAddress, "http-address", "localhost", + globalFlags.StringVar(&c.conf.HTTPAddress, httpAddressFlag, "localhost", "Address for Prometheus and health check server") - pflags.StringVar(&c.httpPort, "http-port", "9090", + globalFlags.StringVar(&c.conf.HTTPPort, httpPortFlag, "9090", "Port for Prometheus and health check server") - pflags.BoolVar(&c.healthCheck, "health-check", false, + localFlags.BoolVar(&c.conf.Debug, "debug", false, + "Enable pprof on the localhost admin server") + localFlags.BoolVar(&c.conf.QuitQuitQuit, "quitquitquit", false, + "Enable quitquitquit endpoint on the localhost admin server") + localFlags.StringVar(&c.conf.AdminPort, adminPortFlag, "9091", + "Port for localhost-only admin server") + localFlags.BoolVar(&c.conf.HealthCheck, "health-check", false, "Enables health check endpoints /startup, /liveness, and /readiness on localhost.") - pflags.StringVar(&c.conf.APIEndpointURL, "sqladmin-api-endpoint", "", + localFlags.StringVar(&c.conf.APIEndpointURL, "sqladmin-api-endpoint", "", "API endpoint for all Cloud SQL Admin API requests. (default: https://sqladmin.googleapis.com)") - pflags.StringVar(&c.conf.QuotaProject, "quota-project", "", + localFlags.StringVar(&c.conf.UniverseDomain, "universe-domain", "", + "Universe Domain for non-GDU environments. (default: googleapis.com)") + localFlags.StringVar(&c.conf.QuotaProject, "quota-project", "", `Specifies the project to use for Cloud SQL Admin API quota tracking. The IAM principal must have the "serviceusage.services.use" permission for the given project. See https://cloud.google.com/service-usage/docs/overview and https://cloud.google.com/storage/docs/requester-pays`) - pflags.StringVar(&c.conf.FUSEDir, "fuse", "", + localFlags.StringVar(&c.conf.FUSEDir, "fuse", "", "Mount a directory at the path using FUSE to access Cloud SQL instances.") - pflags.StringVar(&c.conf.FUSETempDir, "fuse-tmp-dir", + localFlags.StringVar(&c.conf.FUSETempDir, "fuse-tmp-dir", filepath.Join(os.TempDir(), "csql-tmp"), "Temp dir for Unix sockets created with FUSE") - pflags.StringVar(&c.impersonationChain, "impersonate-service-account", "", + localFlags.StringVar(&c.conf.ImpersonationChain, "impersonate-service-account", "", `Comma separated list of service accounts to impersonate. Last value is the target account.`) - cmd.PersistentFlags().BoolVar(&c.quiet, "quiet", false, "Log error messages only") + localFlags.BoolVar(&c.conf.Quiet, "quiet", false, "Log error messages only") + localFlags.BoolVar(&c.conf.AutoIP, "auto-ip", false, + `Supports legacy behavior of v1 and will try to connect to first IP +address returned by the SQL Admin API. In most cases, this flag should not be used. +Prefer default of public IP or use --private-ip instead.`) + localFlags.BoolVar(&c.conf.LazyRefresh, "lazy-refresh", false, + `Configure a lazy refresh where connection info is retrieved only if +the cached copy has expired. Use this setting in environments where the +CPU may be throttled and a background refresh cannot run reliably +(e.g., Cloud Run)`, + ) + + localFlags.BoolVar(&c.conf.RunConnectionTest, "run-connection-test", false, `Runs a connection test +against all specified instances. If an instance is unreachable, the Proxy exits with a failure +status code.`) + + localFlags.BoolVar(&c.conf.SkipFailedInstanceConfig, "skip-failed-instance-config", false, + `If set, the Proxy will skip any instances that are invalid/unreachable ( +only applicable to Unix sockets)`) // Global and per instance flags - pflags.StringVarP(&c.conf.Addr, "address", "a", "127.0.0.1", + localFlags.StringVarP(&c.conf.Addr, "address", "a", "127.0.0.1", "(*) Address to bind Cloud SQL instance listeners.") - pflags.IntVarP(&c.conf.Port, "port", "p", 0, + localFlags.IntVarP(&c.conf.Port, "port", "p", 0, "(*) Initial port for listeners. Subsequent listeners increment from this value.") - pflags.StringVarP(&c.conf.UnixSocket, "unix-socket", "u", "", + localFlags.StringVarP(&c.conf.UnixSocket, "unix-socket", "u", "", `(*) Enables Unix sockets for all listeners with the provided directory.`) - pflags.BoolVarP(&c.conf.IAMAuthN, "auto-iam-authn", "i", false, + localFlags.BoolVarP(&c.conf.IAMAuthN, "auto-iam-authn", "i", false, "(*) Enables Automatic IAM Authentication for all instances") - pflags.BoolVar(&c.conf.PrivateIP, "private-ip", false, + localFlags.BoolVar(&c.conf.PrivateIP, "private-ip", false, "(*) Connect to the private ip address for all instances") + localFlags.BoolVar(&c.conf.PSC, "psc", false, + "(*) Connect to the PSC endpoint for all instances") - v := viper.NewWithOptions(viper.EnvKeyReplacer(strings.NewReplacer("-", "_"))) - v.SetEnvPrefix(envPrefix) - v.AutomaticEnv() - // Ignoring the error here since its only occurence is if one of the pflags - // is nil which is never the case here. - _ = v.BindPFlags(pflags) + return c +} - pflags.VisitAll(func(f *pflag.Flag) { +func loadConfig(c *Command, args []string, opts []Option) error { + v, err := initViper(c) + if err != nil { + return err + } + + c.Flags().VisitAll(func(f *pflag.Flag) { // Override any unset flags with Viper values to use the pflags // object as a single source of truth. if !f.Changed && v.IsSet(f.Name) { val := v.Get(f.Name) - pflags.Set(f.Name, fmt.Sprintf("%v", val)) + _ = c.Flags().Set(f.Name, fmt.Sprintf("%v", val)) } }) - return c + // If args is not already populated, try to read from the environment. + if len(args) == 0 { + args = instanceFromEnv(args) + } + + // If no environment args are present, try to read from the config file. + if len(args) == 0 { + args = instanceFromConfigFile(v) + } + + for _, o := range opts { + o(c) + } + + // Handle logger separately from config + if c.conf.StructuredLogs { + c.logger = log.NewStructuredLogger(c.conf.Quiet) + } + + if c.conf.Quiet { + c.logger = log.NewStdLogger(io.Discard, os.Stderr) + } + + err = parseConfig(c, c.conf, args) + if err != nil { + return err + } + + // The arguments are parsed. Usage is no longer needed. + c.SilenceUsage = true + + // Errors will be handled by logging from here on. + c.SilenceErrors = true + + return nil +} + +func initViper(c *Command) (*viper.Viper, error) { + v := viper.New() + + if c.conf.Filepath != "" { + // Setup Viper configuration file. Viper will attempt to load + // configuration from the specified file if it exists. Otherwise, Viper + // will source all configuration from flags and then environment + // variables. + ext := filepath.Ext(c.conf.Filepath) + + badExtErr := newBadCommandError( + fmt.Sprintf("config file %v should have extension of "+ + "toml, yaml, or json", c.conf.Filepath, + )) + + if ext == "" { + return nil, badExtErr + } + + if ext != ".toml" && ext != ".yaml" && ext != ".yml" && ext != ".json" { + return nil, badExtErr + } + + conf := filepath.Base(c.conf.Filepath) + noExt := strings.ReplaceAll(conf, ext, "") + // argument must be the name of config file without extension + v.SetConfigName(noExt) + v.AddConfigPath(filepath.Dir(c.conf.Filepath)) + + // Attempt to load configuration from a file. If no file is found, + // assume configuration is provided by flags or environment variables. + if err := v.ReadInConfig(); err != nil { + // If the error is a ConfigFileNotFoundError, then ignore it. + // Otherwise, report the error to the user. + var cErr viper.ConfigFileNotFoundError + if !errors.As(err, &cErr) { + return nil, newBadCommandError(fmt.Sprintf( + "failed to load configuration from %v: %v", + c.conf.Filepath, err, + )) + } + } + } + + v.SetEnvPrefix(envPrefix) + v.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) + v.AutomaticEnv() + + return v, nil +} + +func instanceFromEnv(args []string) []string { + // This supports naming the first instance first with: + // INSTANCE_CONNECTION_NAME + // or if that's not defined, with: + // INSTANCE_CONNECTION_NAME_0 + inst := os.Getenv(fmt.Sprintf("%s_INSTANCE_CONNECTION_NAME", envPrefix)) + if inst == "" { + inst = os.Getenv(fmt.Sprintf("%s_INSTANCE_CONNECTION_NAME_0", envPrefix)) + if inst == "" { + return nil + } + } + args = append(args, inst) + + i := 1 + for { + instN := os.Getenv(fmt.Sprintf("%s_INSTANCE_CONNECTION_NAME_%d", envPrefix, i)) + // if the next instance connection name is not defined, stop checking + // environment variables. + if instN == "" { + break + } + args = append(args, instN) + i++ + } + return args +} + +func instanceFromConfigFile(v *viper.Viper) []string { + var args []string + inst := v.GetString("instance-connection-name") + + if inst == "" { + inst = v.GetString("instance-connection-name-0") + if inst == "" { + return nil + } + } + args = append(args, inst) + + i := 1 + for { + instN := v.GetString(fmt.Sprintf("instance-connection-name-%d", i)) + // if the next instance connection name is not defined, stop checking + // environment variables. + if instN == "" { + break + } + args = append(args, instN) + i++ + } + + return args +} + +func userHasSetLocal(cmd *Command, f string) bool { + return cmd.LocalFlags().Lookup(f).Changed +} + +func userHasSetGlobal(cmd *Command, f string) bool { + return cmd.PersistentFlags().Lookup(f).Changed } func parseConfig(cmd *Command, conf *proxy.Config, args []string) error { @@ -437,6 +784,10 @@ func parseConfig(cmd *Command, conf *proxy.Config, args []string) error { } if conf.FUSEDir != "" { + if conf.RunConnectionTest { + return newBadCommandError("cannot run connection tests in FUSE mode") + } + if err := proxy.SupportsFUSE(); err != nil { return newBadCommandError( fmt.Sprintf("--fuse is not supported: %v", err), @@ -448,18 +799,23 @@ func parseConfig(cmd *Command, conf *proxy.Config, args []string) error { return newBadCommandError("cannot specify --fuse-tmp-dir without --fuse") } - userHasSet := func(f string) bool { - return cmd.PersistentFlags().Lookup(f).Changed - } - if userHasSet("address") && userHasSet("unix-socket") { + if userHasSetLocal(cmd, "address") && userHasSetLocal(cmd, "unix-socket") { return newBadCommandError("cannot specify --unix-socket and --address together") } - if userHasSet("port") && userHasSet("unix-socket") { + if userHasSetLocal(cmd, "port") && userHasSetLocal(cmd, "unix-socket") { return newBadCommandError("cannot specify --unix-socket and --port together") } if ip := net.ParseIP(conf.Addr); ip == nil { return newBadCommandError(fmt.Sprintf("not a valid IP address: %q", conf.Addr)) } + if userHasSetLocal(cmd, "private-ip") && userHasSetLocal(cmd, "auto-ip") { + return newBadCommandError("cannot specify --private-ip and --auto-ip together") + } + + // If more than one IP type is set, error. + if conf.PrivateIP && conf.PSC { + return newBadCommandError("cannot specify --private-ip and --psc flags at the same time") + } // If more than one auth method is set, error. if conf.Token != "" && conf.CredentialsFile != "" { @@ -481,26 +837,43 @@ func parseConfig(cmd *Command, conf *proxy.Config, args []string) error { return newBadCommandError("cannot specify --json-credentials and --gcloud-auth flags at the same time") } - if userHasSet("http-port") && !userHasSet("prometheus") && !userHasSet("health-check") { + // When using token with auto-iam-authn, login-token must also be set. + // All three are required together. + if conf.IAMAuthN && conf.Token != "" && conf.LoginToken == "" { + return newBadCommandError("cannot specify --auto-iam-authn and --token without --login-token") + } + if conf.IAMAuthN && conf.GcloudAuth { + return newBadCommandError(`cannot use --auto-iam-authn with --gcloud-auth. +Instead use Application Default Credentials (enabled with: gcloud auth application-default login) +and re-try with just --auto-iam-authn`) + } + if conf.LoginToken != "" && (conf.Token == "" || !conf.IAMAuthN) { + return newBadCommandError("cannot specify --login-token without --token and --auto-iam-authn") + } + + if userHasSetGlobal(cmd, "http-port") && !userHasSetLocal(cmd, "prometheus") && !userHasSetLocal(cmd, "health-check") { cmd.logger.Infof("Ignoring --http-port because --prometheus or --health-check was not set") } - if !userHasSet("telemetry-project") && userHasSet("telemetry-prefix") { + if !userHasSetLocal(cmd, "telemetry-project") && userHasSetLocal(cmd, "telemetry-prefix") { cmd.logger.Infof("Ignoring --telementry-prefix because --telemetry-project was not set") } - if !userHasSet("telemetry-project") && userHasSet("disable-metrics") { + if !userHasSetLocal(cmd, "telemetry-project") && userHasSetLocal(cmd, "disable-metrics") { cmd.logger.Infof("Ignoring --disable-metrics because --telemetry-project was not set") } - if !userHasSet("telemetry-project") && userHasSet("disable-traces") { + if !userHasSetLocal(cmd, "telemetry-project") && userHasSetLocal(cmd, "disable-traces") { cmd.logger.Infof("Ignoring --disable-traces because --telemetry-project was not set") } - if userHasSet("runtime") { - userAgent += " " + cmd.runtime + if userHasSetLocal(cmd, "user-agent") { + userAgent += " " + cmd.conf.OtherUserAgents conf.UserAgent = userAgent } - if userHasSet("sqladmin-api-endpoint") && conf.APIEndpointURL != "" { + if userHasSetLocal(cmd, "sqladmin-api-endpoint") && userHasSetLocal(cmd, "universe-domain") { + return newBadCommandError("cannot specify --sqladmin-api-endpoint and --universe-domain at the same time") + } + if userHasSetLocal(cmd, "sqladmin-api-endpoint") && conf.APIEndpointURL != "" { _, err := url.Parse(conf.APIEndpointURL) if err != nil { return newBadCommandError(fmt.Sprintf( @@ -515,19 +888,6 @@ func parseConfig(cmd *Command, conf *proxy.Config, args []string) error { } } - if cmd.impersonationChain != "" { - accts := strings.Split(cmd.impersonationChain, ",") - conf.ImpersonateTarget = accts[0] - // Assign delegates if the chain is more than one account. Delegation - // goes from last back towards target, e.g., With sa1,sa2,sa3, sa3 - // delegates to sa2, which impersonates the target sa1. - if l := len(accts); l > 1 { - for i := l - 1; i > 0; i-- { - conf.ImpersonateDelegates = append(conf.ImpersonateDelegates, accts[i]) - } - } - } - var ics []proxy.InstanceConnConfig for _, a := range args { // Assume no query params initially @@ -545,6 +905,7 @@ func parseConfig(cmd *Command, conf *proxy.Config, args []string) error { a, aok := q["address"] p, pok := q["port"] u, uok := q["unix-socket"] + up, upok := q["unix-socket-path"] if aok && uok { return newBadCommandError("cannot specify both address and unix-socket query params") @@ -552,6 +913,15 @@ func parseConfig(cmd *Command, conf *proxy.Config, args []string) error { if pok && uok { return newBadCommandError("cannot specify both port and unix-socket query params") } + if aok && upok { + return newBadCommandError("cannot specify both address and unix-socket-path query params") + } + if pok && upok { + return newBadCommandError("cannot specify both port and unix-socket-path query params") + } + if uok && upok { + return newBadCommandError("cannot specify both unix-socket-path and unix-socket query params") + } if aok { if len(a) != 1 { @@ -587,6 +957,13 @@ func parseConfig(cmd *Command, conf *proxy.Config, args []string) error { ic.UnixSocket = u[0] } + if upok { + if len(up) != 1 { + return newBadCommandError(fmt.Sprintf("unix-socket-path query param should be only one value: %q", a)) + } + ic.UnixSocketPath = up[0] + } + ic.IAMAuthN, err = parseBoolOpt(q, "auto-iam-authn") if err != nil { return err @@ -596,6 +973,18 @@ func parseConfig(cmd *Command, conf *proxy.Config, args []string) error { if err != nil { return err } + if ic.PrivateIP != nil && *ic.PrivateIP && conf.AutoIP { + return newBadCommandError("cannot use --auto-ip with private-ip") + } + + ic.PSC, err = parseBoolOpt(q, "psc") + if err != nil { + return err + } + + if ic.PrivateIP != nil && ic.PSC != nil { + return newBadCommandError("cannot specify both private-ip and psc query params") + } } ics = append(ics, ic) @@ -610,16 +999,16 @@ func parseConfig(cmd *Command, conf *proxy.Config, args []string) error { // true if the value is "t" or "true" case-insensitive // false if the value is "f" or "false" case-insensitive func parseBoolOpt(q url.Values, name string) (*bool, error) { - iam, ok := q[name] + v, ok := q[name] if !ok { return nil, nil } - if len(iam) != 1 { - return nil, newBadCommandError(fmt.Sprintf("%v param should be only one value: %q", name, iam)) + if len(v) != 1 { + return nil, newBadCommandError(fmt.Sprintf("%v param should be only one value: %q", name, v)) } - switch strings.ToLower(iam[0]) { + switch strings.ToLower(v[0]) { case "true", "t", "": enable := true return &enable, nil @@ -630,26 +1019,26 @@ func parseBoolOpt(q url.Values, name string) (*bool, error) { // value is not recognized return nil, newBadCommandError( fmt.Sprintf("%v query param should be true or false, got: %q", - name, iam[0], + name, v[0], )) } } // runSignalWrapper watches for SIGTERM and SIGINT and interupts execution if necessary. -func runSignalWrapper(cmd *Command) error { - defer cmd.cleanup() +func runSignalWrapper(cmd *Command) (err error) { + defer func() { _ = cmd.cleanup() }() ctx, cancel := context.WithCancel(cmd.Context()) defer cancel() // Configure collectors before the proxy has started to ensure we are // collecting metrics before *ANY* Cloud SQL Admin API calls are made. - enableMetrics := !cmd.disableMetrics - enableTraces := !cmd.disableTraces - if cmd.telemetryProject != "" && (enableMetrics || enableTraces) { + enableMetrics := !cmd.conf.DisableMetrics + enableTraces := !cmd.conf.DisableTraces + if cmd.conf.TelemetryProject != "" && (enableMetrics || enableTraces) { sd, err := stackdriver.NewExporter(stackdriver.Options{ - ProjectID: cmd.telemetryProject, - MetricPrefix: cmd.telemetryPrefix, + ProjectID: cmd.conf.TelemetryProject, + MetricPrefix: cmd.conf.TelemetryPrefix, }) if err != nil { return err @@ -661,7 +1050,7 @@ func runSignalWrapper(cmd *Command) error { } } if enableTraces { - s := trace.ProbabilitySampler(1 / float64(cmd.telemetryTracingSampleRate)) + s := trace.ProbabilitySampler(1 / float64(cmd.conf.TelemetryTracingSampleRate)) trace.ApplyConfig(trace.Config{DefaultSampler: s}) trace.RegisterExporter(sd) } @@ -671,21 +1060,6 @@ func runSignalWrapper(cmd *Command) error { }() } - var ( - needsHTTPServer bool - mux = http.NewServeMux() - ) - if cmd.prometheus { - needsHTTPServer = true - e, err := prometheus.NewExporter(prometheus.Options{ - Namespace: cmd.prometheusNamespace, - }) - if err != nil { - return err - } - mux.Handle("/metrics", e) - } - shutdownCh := make(chan error) // watch for sigterm / sigint signals signals := make(chan os.Signal, 1) @@ -700,9 +1074,15 @@ func runSignalWrapper(cmd *Command) error { } switch s { case syscall.SIGINT: + cmd.logger.Debugf("Sending SIGINT signal for proxy to shutdown.") shutdownCh <- errSigInt case syscall.SIGTERM: - shutdownCh <- errSigTerm + cmd.logger.Debugf("Sending SIGTERM signal for proxy to shutdown.") + if cmd.conf.ExitZeroOnSigterm { + shutdownCh <- errSigTermZero + } else { + shutdownCh <- errSigTerm + } } }() @@ -710,8 +1090,9 @@ func runSignalWrapper(cmd *Command) error { startCh := make(chan *proxy.Client) go func() { defer close(startCh) - p, err := proxy.NewClient(ctx, cmd.dialer, cmd.logger, cmd.conf) + p, err := proxy.NewClient(ctx, cmd.dialer, cmd.logger, cmd.conf, cmd.connRefuseNotify) if err != nil { + cmd.logger.Debugf("Error starting proxy: %v", err) shutdownCh <- fmt.Errorf("unable to start: %v", err) return } @@ -722,66 +1103,168 @@ func runSignalWrapper(cmd *Command) error { select { case err := <-shutdownCh: cmd.logger.Errorf("The proxy has encountered a terminal error: %v", err) + // If running under systemd with Type=notify, it will send a message to the + // service manager that a failure occurred, and it is terminating. + go func() { + if _, err := daemon.SdNotify(false, daemon.SdNotifyStopping); err != nil { + cmd.logger.Errorf("Failed to notify systemd of termination: %v", err) + } + }() return err case p = <-startCh: cmd.logger.Infof("The proxy has started successfully and is ready for new connections!") + // If running under systemd with Type=notify, it will send a message to the + // service manager that it is ready to handle connections now. + go func() { + if _, err := daemon.SdNotify(false, daemon.SdNotifyReady); err != nil { + cmd.logger.Errorf("Failed to notify systemd of readiness: %v", err) + } + }() } defer func() { if cErr := p.Close(); cErr != nil { cmd.logger.Errorf("error during shutdown: %v", cErr) + // Capture error from close to propagate it to the caller. + err = cErr } }() - notify := func() {} - if cmd.healthCheck { + var ( + needsHTTPServer bool + mux = http.NewServeMux() + notifyStarted = func() {} + notifyStopped = func() {} + ) + + if cmd.conf.Prometheus { + needsHTTPServer = true + e, err := prometheus.NewExporter(prometheus.Options{ + Namespace: cmd.conf.PrometheusNamespace, + }) + if err != nil { + return err + } + mux.Handle("/metrics", e) + } + + if cmd.conf.HealthCheck { needsHTTPServer = true cmd.logger.Infof("Starting health check server at %s", - net.JoinHostPort(cmd.httpAddress, cmd.httpPort)) + net.JoinHostPort(cmd.conf.HTTPAddress, cmd.conf.HTTPPort)) hc := healthcheck.NewCheck(p, cmd.logger) mux.HandleFunc("/startup", hc.HandleStartup) mux.HandleFunc("/readiness", hc.HandleReadiness) mux.HandleFunc("/liveness", hc.HandleLiveness) - notify = hc.NotifyStarted + notifyStarted = hc.NotifyStarted + notifyStopped = hc.NotifyStopped } - + defer notifyStopped() // Start the HTTP server if anything requiring HTTP is specified. if needsHTTPServer { - server := &http.Server{ - Addr: net.JoinHostPort(cmd.httpAddress, cmd.httpPort), - Handler: mux, - } - // Start the HTTP server. - go func() { - err := server.ListenAndServe() - if err == http.ErrServerClosed { - return - } - if err != nil { - shutdownCh <- fmt.Errorf("failed to start HTTP server: %v", err) - } - }() - // Handle shutdown of the HTTP server gracefully. - go func() { - <-ctx.Done() - // Give the HTTP server a second to shutdown cleanly. - ctx2, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - if err := server.Shutdown(ctx2); err != nil { - cmd.logger.Errorf("failed to shutdown Prometheus HTTP server: %v\n", err) - } - }() + go startHTTPServer( + ctx, + cmd.logger, + net.JoinHostPort(cmd.conf.HTTPAddress, cmd.conf.HTTPPort), + mux, + shutdownCh, + ) } - go func() { shutdownCh <- p.Serve(ctx, notify) }() + var ( + needsAdminServer bool + m = http.NewServeMux() + ) + if cmd.conf.QuitQuitQuit { + needsAdminServer = true + cmd.logger.Infof("Enabling quitquitquit endpoint at localhost:%v", cmd.conf.AdminPort) + // quitquitquit allows for shutdown on localhost only. + var quitOnce sync.Once + m.HandleFunc("/quitquitquit", quitquitquit(&quitOnce, shutdownCh)) + } + if cmd.conf.Debug { + needsAdminServer = true + cmd.logger.Infof("Enabling pprof endpoints at localhost:%v", cmd.conf.AdminPort) + // pprof standard endpoints + m.HandleFunc("/debug/pprof/", pprof.Index) + m.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) + m.HandleFunc("/debug/pprof/profile", pprof.Profile) + m.HandleFunc("/debug/pprof/symbol", pprof.Symbol) + m.HandleFunc("/debug/pprof/trace", pprof.Trace) + } + if needsAdminServer { + go startHTTPServer( + ctx, + cmd.logger, + net.JoinHostPort("localhost", cmd.conf.AdminPort), + m, + shutdownCh, + ) + } + + go func() { + err := p.Serve(ctx, notifyStarted) + cmd.logger.Debugf("proxy server error: %v", err) + shutdownCh <- err + }() - err := <-shutdownCh + err = <-shutdownCh switch { case errors.Is(err, errSigInt): - cmd.logger.Errorf("SIGINT signal received. Shutting down...") + cmd.logger.Infof("SIGINT signal received. Shutting down...") + time.Sleep(cmd.conf.WaitBeforeClose) case errors.Is(err, errSigTerm): - cmd.logger.Errorf("SIGTERM signal received. Shutting down...") + cmd.logger.Infof("SIGTERM signal received. Shutting down...") + time.Sleep(cmd.conf.WaitBeforeClose) + case errors.Is(err, errSigTermZero): + cmd.logger.Infof("SIGTERM signal received. Shutting down...") + time.Sleep(cmd.conf.WaitBeforeClose) + case errors.Is(err, errQuitQuitQuit): + cmd.logger.Infof("/quitquitquit received request. Shutting down...") + time.Sleep(cmd.conf.WaitBeforeClose) default: cmd.logger.Errorf("The proxy has encountered a terminal error: %v", err) } return err } + +func quitquitquit(quitOnce *sync.Once, shutdownCh chan<- error) http.HandlerFunc { + return func(rw http.ResponseWriter, req *http.Request) { + if req.Method != http.MethodPost && req.Method != http.MethodGet { + rw.WriteHeader(400) + return + } + quitOnce.Do(func() { + select { + case shutdownCh <- errQuitQuitQuit: + default: + // The write attempt to shutdownCh failed and + // the proxy is already exiting. + } + }) + } +} + +func startHTTPServer(ctx context.Context, l cloudsql.Logger, addr string, mux *http.ServeMux, shutdownCh chan<- error) { + server := &http.Server{ + Addr: addr, + Handler: mux, + } + // Start the HTTP server. + go func() { + err := server.ListenAndServe() + if errors.Is(err, http.ErrServerClosed) { + return + } + if err != nil { + shutdownCh <- fmt.Errorf("failed to start HTTP server: %v", err) + } + }() + // Handle shutdown of the HTTP server gracefully. + <-ctx.Done() + // Give the HTTP server a second to shut down cleanly. + ctx2, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + if err := server.Shutdown(ctx2); err != nil { + l.Errorf("failed to shutdown HTTP server: %v\n", err) + } +} diff --git a/cmd/root_linux_test.go b/cmd/root_linux_test.go index 0a2e8e499..121c826ed 100644 --- a/cmd/root_linux_test.go +++ b/cmd/root_linux_test.go @@ -15,10 +15,14 @@ package cmd import ( + "context" + "net" "os" "path/filepath" "testing" + "time" + "github.com/coreos/go-systemd/v22/daemon" "github.com/spf13/cobra" ) @@ -71,3 +75,72 @@ func TestNewCommandArgumentsOnLinux(t *testing.T) { }) } } + +func TestSdNotifyOnLinux(t *testing.T) { + tcs := []struct { + desc string + proxyMustFail bool + notifyState string + }{ + { + desc: "System with systemd Type=notify and proxy started successfully", + proxyMustFail: false, + notifyState: daemon.SdNotifyReady, + }, + { + desc: "System with systemd Type=notify and proxy failed to start", + proxyMustFail: true, + notifyState: daemon.SdNotifyStopping, + }, + } + + // Create a temp dir for the socket file. + testDir, err := os.MkdirTemp("/tmp/", "test-") + if err != nil { + t.Fatalf("Fail to create the temp dir: %v", err) + } + defer os.RemoveAll(testDir) + + //Set up the socket stream to listen for notifications. + socketAddr := filepath.Join(testDir, "notify-socket.sock") + conn, err := net.ListenUnixgram("unixgram", &net.UnixAddr{Name: socketAddr, Net: "unixgram"}) + if err != nil { + t.Fatalf("net.ListenUnixgram error: %v", err) + } + + // To simulate systemd behavior with Type=notify, set NOTIFY_SOCKET + // to the name of the socket that listens for notifications. + os.Setenv("NOTIFY_SOCKET", socketAddr) + defer os.Unsetenv("NOTIFY_SOCKET") + + s := &spyDialer{} + c := NewCommand(WithDialer(s)) + // Keep the test output quiet + c.SilenceUsage = false + c.SilenceErrors = false + c.SetArgs([]string{"my-project:my-region:my-instance"}) + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + + if tc.proxyMustFail { + c.conf.FUSEDir = "invalid" + } + + go c.ExecuteContext(ctx) + + stateReceived := make([]byte, 4096) + length, _, err := conn.ReadFromUnix(stateReceived) + if err != nil { + t.Fatalf("conn.ReadFromUnix error: %s\n", err) + } + if string(stateReceived[0:length]) != tc.notifyState { + t.Fatalf("Expected Notify State %v, got %v", tc.notifyState, string(stateReceived)) + } + + }) + } +} diff --git a/cmd/root_test.go b/cmd/root_test.go index fe327b64a..4a4be698c 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -19,6 +19,7 @@ import ( "errors" "net" "net/http" + "net/url" "os" "path/filepath" "strings" @@ -50,6 +51,18 @@ func withDefaults(c *proxy.Config) *proxy.Config { if c.FUSETempDir == "" { c.FUSETempDir = filepath.Join(os.TempDir(), "csql-tmp") } + if c.HTTPAddress == "" { + c.HTTPAddress = "localhost" + } + if c.HTTPPort == "" { + c.HTTPPort = "9090" + } + if c.AdminPort == "" { + c.AdminPort = "9091" + } + if c.TelemetryTracingSampleRate == 0 { + c.TelemetryTracingSampleRate = 10_000 + } return c } @@ -82,9 +95,9 @@ func invokeProxyCommand(args []string) (*Command, error) { return c, err } -func TestUserAgentWithOperatorVersionEnvVar(t *testing.T) { - os.Setenv("CSQL_PROXY_RUNTIME", "cloud-sql-proxy-operator/0.0.1") - defer os.Unsetenv("CSQL_PROXY_RUNTIME") +func TestUserAgentWithVersionEnvVar(t *testing.T) { + os.Setenv("CSQL_PROXY_USER_AGENT", "cloud-sql-proxy-operator/0.0.1") + defer os.Unsetenv("CSQL_PROXY_USER_AGENT") cmd, err := invokeProxyCommand([]string{"proj:region:inst"}) if err != nil { @@ -94,15 +107,14 @@ func TestUserAgentWithOperatorVersionEnvVar(t *testing.T) { want := "cloud-sql-proxy-operator/0.0.1" got := cmd.conf.UserAgent if !strings.Contains(got, want) { - t.Errorf("expected userAgent to contain: %v; got: %v", want, got) + t.Errorf("expected user agent to contain: %v; got: %v", want, got) } } -func TestUserAgentWithOperatorVersionFlag(t *testing.T) { - +func TestUserAgent(t *testing.T) { cmd, err := invokeProxyCommand( []string{ - "--runtime", + "--user-agent", "cloud-sql-proxy-operator/0.0.1", "proj:region:inst", }, @@ -252,6 +264,13 @@ func TestNewCommandArguments(t *testing.T) { APIEndpointURL: "https://test.googleapis.com/", }), }, + { + desc: "using the universe domain flag", + args: []string{"--universe-domain", "test-universe.test", "proj:region:inst"}, + want: withDefaults(&proxy.Config{ + UniverseDomain: "test-universe.test", + }), + }, { desc: "using the unix socket flag", args: []string{"--unix-socket", "/path/to/dir/", "proj:region:inst"}, @@ -275,6 +294,15 @@ func TestNewCommandArguments(t *testing.T) { }}, }), }, + { + desc: "using the unix socket path query param", + args: []string{"proj:region:inst?unix-socket-path=/path/to/file"}, + want: withDefaults(&proxy.Config{ + Instances: []proxy.InstanceConnConfig{{ + UnixSocketPath: "/path/to/file", + }}, + }), + }, { desc: "using the iam authn login flag", args: []string{"--auto-iam-authn", "proj:region:inst"}, @@ -312,6 +340,13 @@ func TestNewCommandArguments(t *testing.T) { MaxConnections: 1, }), }, + { + desc: "using min-sigterm-delay flag", + args: []string{"--min-sigterm-delay", "10s", "proj:region:inst"}, + want: withDefaults(&proxy.Config{ + WaitBeforeClose: 10 * time.Second, + }), + }, { desc: "using wait after signterm flag", args: []string{"--max-sigterm-delay", "10s", "proj:region:inst"}, @@ -336,141 +371,112 @@ func TestNewCommandArguments(t *testing.T) { }), }, { - desc: "using the quota project flag", - args: []string{"--quota-project", "proj", "proj:region:inst"}, + desc: "using the private-ip flag with query param override", + args: []string{"--private-ip", "proj:region:inst?private-ip=false"}, want: withDefaults(&proxy.Config{ - QuotaProject: "proj", + PrivateIP: true, + Instances: []proxy.InstanceConnConfig{{ + PrivateIP: pointer(false), + }}, }), }, { - desc: "using the impersonate service account flag", - args: []string{"--impersonate-service-account", - "sv1@developer.gserviceaccount.com,sv2@developer.gserviceaccount.com,sv3@developer.gserviceaccount.com", - "proj:region:inst"}, + desc: "using the psc flag", + args: []string{"--psc", "proj:region:inst"}, want: withDefaults(&proxy.Config{ - ImpersonateTarget: "sv1@developer.gserviceaccount.com", - ImpersonateDelegates: []string{ - "sv3@developer.gserviceaccount.com", - "sv2@developer.gserviceaccount.com", - }, + PSC: true, }), }, - } - - for _, tc := range tcs { - t.Run(tc.desc, func(t *testing.T) { - c, err := invokeProxyCommand(tc.args) - if err != nil { - t.Fatalf("want error = nil, got = %v", err) - } - - if got := c.conf; !cmp.Equal(tc.want, got) { - t.Fatalf("want = %#v\ngot = %#v\ndiff = %v", tc.want, got, cmp.Diff(tc.want, got)) - } - }) - } -} - -func TestNewCommandWithEnvironmentConfigPrivateFields(t *testing.T) { - tcs := []struct { - desc string - envName string - envValue string - isValid func(cmd *Command) bool - }{ { - desc: "using the disable traces envvar", - envName: "CSQL_PROXY_DISABLE_TRACES", - envValue: "true", - isValid: func(cmd *Command) bool { - return cmd.disableTraces == true - }, + desc: "using the psc flag query param", + args: []string{"proj:region:inst?psc=true"}, + want: withDefaults(&proxy.Config{ + Instances: []proxy.InstanceConnConfig{{ + PSC: pointer(true), + }}, + }), }, { - desc: "using the telemetry sample rate envvar", - envName: "CSQL_PROXY_TELEMETRY_SAMPLE_RATE", - envValue: "500", - isValid: func(cmd *Command) bool { - return cmd.telemetryTracingSampleRate == 500 - }, + desc: "using the quota project flag", + args: []string{"--quota-project", "proj", "proj:region:inst"}, + want: withDefaults(&proxy.Config{ + QuotaProject: "proj", + }), }, { - desc: "using the disable metrics envvar", - envName: "CSQL_PROXY_DISABLE_METRICS", - envValue: "true", - isValid: func(cmd *Command) bool { - return cmd.disableMetrics == true - }, + desc: "using the impersonate service account flag", + args: []string{"--impersonate-service-account", + "sv1@developer.gserviceaccount.com", + "proj:region:inst"}, + want: withDefaults(&proxy.Config{ + ImpersonationChain: "sv1@developer.gserviceaccount.com", + }), }, { - desc: "using the telemetry project envvar", - envName: "CSQL_PROXY_TELEMETRY_PROJECT", - envValue: "mycoolproject", - isValid: func(cmd *Command) bool { - return cmd.telemetryProject == "mycoolproject" - }, + desc: "using the debug flag", + args: []string{"--debug", "proj:region:inst"}, + want: withDefaults(&proxy.Config{ + Debug: true, + }), }, { - desc: "using the telemetry prefix envvar", - envName: "CSQL_PROXY_TELEMETRY_PREFIX", - envValue: "myprefix", - isValid: func(cmd *Command) bool { - return cmd.telemetryPrefix == "myprefix" - }, + desc: "using the lazy refresh flag", + args: []string{"--lazy-refresh", "proj:region:inst"}, + want: withDefaults(&proxy.Config{ + LazyRefresh: true, + }), }, { - desc: "using the prometheus envvar", - envName: "CSQL_PROXY_PROMETHEUS", - envValue: "true", - isValid: func(cmd *Command) bool { - return cmd.prometheus == true - }, + desc: "using the admin port flag", + args: []string{"--admin-port", "7777", "proj:region:inst"}, + want: withDefaults(&proxy.Config{ + AdminPort: "7777", + }), }, { - desc: "using the prometheus namespace envvar", - envName: "CSQL_PROXY_PROMETHEUS_NAMESPACE", - envValue: "myns", - isValid: func(cmd *Command) bool { - return cmd.prometheusNamespace == "myns" - }, + desc: "using the quitquitquit flag", + args: []string{"--quitquitquit", "proj:region:inst"}, + want: withDefaults(&proxy.Config{ + QuitQuitQuit: true, + }), }, { - desc: "using the health check envvar", - envName: "CSQL_PROXY_HEALTH_CHECK", - envValue: "true", - isValid: func(cmd *Command) bool { - return cmd.healthCheck == true - }, + desc: "using the login-token flag", + args: []string{ + "--auto-iam-authn", + "--token", "MYTOK", + "--login-token", "MYLOGINTOKEN", "proj:region:inst"}, + want: withDefaults(&proxy.Config{ + IAMAuthN: true, + Token: "MYTOK", + LoginToken: "MYLOGINTOKEN", + }), }, { - desc: "using the http address envvar", - envName: "CSQL_PROXY_HTTP_ADDRESS", - envValue: "0.0.0.0", - isValid: func(cmd *Command) bool { - return cmd.httpAddress == "0.0.0.0" - }, + desc: "using the auto-ip flag", + args: []string{"--auto-ip", "proj:region:inst"}, + want: withDefaults(&proxy.Config{ + AutoIP: true, + }), }, { - desc: "using the http port envvar", - envName: "CSQL_PROXY_HTTP_PORT", - envValue: "5555", - isValid: func(cmd *Command) bool { - return cmd.httpPort == "5555" - }, + desc: "using the run-connection-test flag", + args: []string{"--run-connection-test", "proj:region:inst"}, + want: withDefaults(&proxy.Config{ + RunConnectionTest: true, + }), }, } + for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { - os.Setenv(tc.envName, tc.envValue) - defer os.Unsetenv(tc.envName) - - c, err := invokeProxyCommand([]string{"proj:region:inst"}) + c, err := invokeProxyCommand(tc.args) if err != nil { t.Fatalf("want error = nil, got = %v", err) } - if !tc.isValid(c) { - t.Fatal("want valid, got invalid") + if got := c.conf; !cmp.Equal(tc.want, got) { + t.Fatalf("want = %#v\ngot = %#v\ndiff = %v", tc.want, got, cmp.Diff(tc.want, got)) } }) } @@ -679,6 +685,14 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { PrivateIP: true, }), }, + { + desc: "using the psc envvar", + envName: "CSQL_PROXY_PSC", + envValue: "true", + want: withDefaults(&proxy.Config{ + PSC: true, + }), + }, { desc: "using the quota project envvar", envName: "CSQL_PROXY_QUOTA_PROJECT", @@ -688,15 +702,123 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { }), }, { - desc: "using the imopersonate service accounn envvar", + desc: "using the impersonate service account envvar", envName: "CSQL_PROXY_IMPERSONATE_SERVICE_ACCOUNT", - envValue: "sv1@developer.gserviceaccount.com,sv2@developer.gserviceaccount.com,sv3@developer.gserviceaccount.com", + envValue: "sv1@developer.gserviceaccount.com", + want: withDefaults(&proxy.Config{ + ImpersonationChain: "sv1@developer.gserviceaccount.com", + }), + }, + { + desc: "using the disable traces envvar", + envName: "CSQL_PROXY_DISABLE_TRACES", + envValue: "true", + want: withDefaults(&proxy.Config{ + DisableTraces: true, + }), + }, + { + desc: "using the telemetry sample rate envvar", + envName: "CSQL_PROXY_TELEMETRY_SAMPLE_RATE", + envValue: "500", + want: withDefaults(&proxy.Config{ + TelemetryTracingSampleRate: 500, + }), + }, + { + desc: "using the disable metrics envvar", + envName: "CSQL_PROXY_DISABLE_METRICS", + envValue: "true", + want: withDefaults(&proxy.Config{ + DisableMetrics: true, + }), + }, + { + desc: "using the telemetry project envvar", + envName: "CSQL_PROXY_TELEMETRY_PROJECT", + envValue: "mycoolproject", + want: withDefaults(&proxy.Config{ + TelemetryProject: "mycoolproject", + }), + }, + { + desc: "using the telemetry prefix envvar", + envName: "CSQL_PROXY_TELEMETRY_PREFIX", + envValue: "myprefix", + want: withDefaults(&proxy.Config{ + TelemetryPrefix: "myprefix", + }), + }, + { + desc: "using the prometheus envvar", + envName: "CSQL_PROXY_PROMETHEUS", + envValue: "true", + want: withDefaults(&proxy.Config{ + Prometheus: true, + }), + }, + { + desc: "using the prometheus namespace envvar", + envName: "CSQL_PROXY_PROMETHEUS_NAMESPACE", + envValue: "myns", want: withDefaults(&proxy.Config{ - ImpersonateTarget: "sv1@developer.gserviceaccount.com", - ImpersonateDelegates: []string{ - "sv3@developer.gserviceaccount.com", - "sv2@developer.gserviceaccount.com", - }, + PrometheusNamespace: "myns", + }), + }, + { + desc: "using the health check envvar", + envName: "CSQL_PROXY_HEALTH_CHECK", + envValue: "true", + want: withDefaults(&proxy.Config{ + HealthCheck: true, + }), + }, + { + desc: "using the http address envvar", + envName: "CSQL_PROXY_HTTP_ADDRESS", + envValue: "0.0.0.0", + want: withDefaults(&proxy.Config{ + HTTPAddress: "0.0.0.0", + }), + }, + { + desc: "using the http port envvar", + envName: "CSQL_PROXY_HTTP_PORT", + envValue: "5555", + want: withDefaults(&proxy.Config{ + HTTPPort: "5555", + }), + }, + { + desc: "using the debug envvar", + envName: "CSQL_PROXY_DEBUG", + envValue: "true", + want: withDefaults(&proxy.Config{ + Debug: true, + }), + }, + { + desc: "using the admin port envvar", + envName: "CSQL_PROXY_ADMIN_PORT", + envValue: "7777", + want: withDefaults(&proxy.Config{ + AdminPort: "7777", + }), + }, + { + desc: "using the quitquitquit envvar", + envName: "CSQL_PROXY_QUITQUITQUIT", + envValue: "true", + want: withDefaults(&proxy.Config{ + QuitQuitQuit: true, + }), + }, + { + desc: "using the auto-ip envvar", + envName: "CSQL_PROXY_AUTO_IP", + envValue: "true", + want: withDefaults(&proxy.Config{ + AutoIP: true, }), }, } @@ -838,6 +960,79 @@ func TestPrivateIPQueryParams(t *testing.T) { } } +func TestPSCQueryParams(t *testing.T) { + tcs := []struct { + desc string + args []string + want *bool + }{ + { + desc: "when the query string is absent", + args: []string{"proj:region:inst"}, + want: nil, + }, + { + desc: "when the query string has no value", + args: []string{"proj:region:inst?psc"}, + want: pointer(true), + }, + { + desc: "when the query string is true", + args: []string{"proj:region:inst?psc=true"}, + want: pointer(true), + }, + { + desc: "when the query string is True", + args: []string{"proj:region:inst?psc=True"}, + want: pointer(true), + }, + { + desc: "when the query string is (short) T", + args: []string{"proj:region:inst?psc=T"}, + want: pointer(true), + }, + { + desc: "when the query string is (short) t", + args: []string{"proj:region:inst?psc=t"}, + want: pointer(true), + }, + { + desc: "when the query string is false", + args: []string{"proj:region:inst?psc=false"}, + want: pointer(false), + }, + { + desc: "when the query string is (short) f", + args: []string{"proj:region:inst?psc=f"}, + want: pointer(false), + }, + { + desc: "when the query string is False", + args: []string{"proj:region:inst?psc=False"}, + want: pointer(false), + }, + { + desc: "when the query string is (short) F", + args: []string{"proj:region:inst?psc=F"}, + want: pointer(false), + }, + } + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + c, err := invokeProxyCommand(tc.args) + if err != nil { + t.Fatalf("command.Execute: %v", err) + } + if tc.want == nil && c.conf.Instances[0].PSC == nil { + return + } + if got := c.conf.Instances[0].PSC; *got != *tc.want { + t.Errorf("args = %v, want = %v, got = %v", tc.args, *tc.want, *got) + } + }) + } +} + func TestNewCommandWithErrors(t *testing.T) { tcs := []struct { desc string @@ -891,6 +1086,12 @@ func TestNewCommandWithErrors(t *testing.T) { "--token", "my-token", "--gcloud-auth", "proj:region:inst"}, }, + { + desc: "when both gcloud auth and auto-iam-authn are set", + args: []string{ + "--auto-iam-authn", + "--gcloud-auth", "proj:region:inst"}, + }, { desc: "when both gcloud auth and credentials file are set", args: []string{ @@ -927,14 +1128,26 @@ func TestNewCommandWithErrors(t *testing.T) { desc: "using the unix socket flag with port", args: []string{"-u", "/path/to/dir/", "-p", "5432", "proj:region:inst"}, }, + { + desc: "using the unix socket and unix-socket-path", + args: []string{"proj:region:inst?unix-socket=/path&unix-socket-path=/another/path"}, + }, { desc: "using the unix socket and addr query params", args: []string{"proj:region:inst?unix-socket=/path&address=127.0.0.1"}, }, + { + desc: "using the unix socket path and addr query params", + args: []string{"proj:region:inst?unix-socket-path=/path&address=127.0.0.1"}, + }, { desc: "using the unix socket and port query params", args: []string{"proj:region:inst?unix-socket=/path&port=5000"}, }, + { + desc: "using the unix socket path and port query params", + args: []string{"proj:region:inst?unix-socket-path=/path&port=5000"}, + }, { desc: "when the iam authn login query param contains multiple values", args: []string{"proj:region:inst?auto-iam-authn=true&auto-iam-authn=false"}, @@ -951,6 +1164,58 @@ func TestNewCommandWithErrors(t *testing.T) { desc: "using fuse-tmp-dir without fuse", args: []string{"--fuse-tmp-dir", "/mydir"}, }, + { + desc: "using --auto-iam-authn with just token flag", + args: []string{"--auto-iam-authn", "--token", "MYTOKEN", "p:r:i"}, + }, + { + desc: "using the --login-token without --token and --auto-iam-authn", + args: []string{"--login-token", "MYTOKEN", "p:r:i"}, + }, + { + desc: "using --token and --login-token without --auto-iam-authn", + args: []string{ + "--token", "MYTOKEN", + "--login-token", "MYLOGINTOKEN", "p:r:i"}, + }, + { + desc: "using --private-ip with --auto-ip", + args: []string{ + "--private-ip", "--auto-ip", + "p:r:i", + }, + }, + { + desc: "using private-ip query param with --auto-ip", + args: []string{ + "--auto-ip", + "p:r:i?private-ip=true", + }, + }, + { + desc: "using private IP and psc query params", + args: []string{"p:r:i?private-ip=true&psc=true"}, + }, + { + desc: "using --private-ip with --psc", + args: []string{ + "--private-ip", "--psc", + "p:r:i", + }, + }, + { + desc: "run-connection-test with fuse", + args: []string{ + "--run-connection-test", + "--fuse", "myfusedir", + }, + }, + { + desc: "using both --sqladmin-api-endpoint and --universe-domain", + args: []string{ + "--sqladmin-api-endpoint", "https://sqladmin.googleapis.com", + "--universe-domain", "test-universe.test", "proj:region:inst"}, + }, } for _, tc := range tcs { @@ -1002,7 +1267,7 @@ func TestCommandWithCustomDialer(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - if err := c.ExecuteContext(ctx); !errors.As(err, &errSigInt) { + if err := c.ExecuteContext(ctx); !errors.Is(err, errSigInt) { t.Fatalf("want errSigInt, got = %v", err) } @@ -1011,6 +1276,33 @@ func TestCommandWithCustomDialer(t *testing.T) { } } +func tryDial(method, addr string) (*http.Response, error) { + var ( + resp *http.Response + attempts int + err error + ) + u, err := url.Parse(addr) + if err != nil { + return nil, err + } + req := &http.Request{Method: method, URL: u} + // Never wait longer than 30 seconds for an HTTP response. + cl := &http.Client{Timeout: 30 * time.Second} + for { + if attempts > 10 { + return resp, err + } + resp, err = cl.Do(req) + if err != nil { + attempts++ + time.Sleep(time.Second) + continue + } + return resp, err + } +} + func TestPrometheusMetricsEndpoint(t *testing.T) { c := NewCommand(WithDialer(&spyDialer{})) // Keep the test output quiet @@ -1025,26 +1317,7 @@ func TestPrometheusMetricsEndpoint(t *testing.T) { // try to dial metrics server for a max of ~10s to give the proxy time to // start up. - tryDial := func(addr string) (*http.Response, error) { - var ( - resp *http.Response - attempts int - err error - ) - for { - if attempts > 10 { - return resp, err - } - resp, err = http.Get(addr) - if err != nil { - attempts++ - time.Sleep(time.Second) - continue - } - return resp, err - } - } - resp, err := tryDial("http://localhost:9090/metrics") // default port set by http-port flag + resp, err := tryDial("GET", "http://localhost:9090/metrics") // default port set by http-port flag if err != nil { t.Fatalf("failed to dial metrics endpoint: %v", err) } @@ -1052,3 +1325,137 @@ func TestPrometheusMetricsEndpoint(t *testing.T) { t.Fatalf("expected a 200 status, got = %v", resp.StatusCode) } } + +func TestPProfServer(t *testing.T) { + c := NewCommand(WithDialer(&spyDialer{})) + c.SilenceUsage = true + c.SilenceErrors = true + c.SetArgs([]string{"--debug", "--admin-port", "9191", "my-project:my-region:my-instance"}) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + go c.ExecuteContext(ctx) + resp, err := tryDial("GET", "http://localhost:9191/debug/pprof/") + if err != nil { + t.Fatalf("failed to dial endpoint: %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("expected a 200 status, got = %v", resp.StatusCode) + } +} + +func TestQuitQuitQuitHTTPPost(t *testing.T) { + c := NewCommand(WithDialer(&spyDialer{})) + c.SilenceUsage = true + c.SilenceErrors = true + c.SetArgs([]string{"--quitquitquit", "--admin-port", "9192", "my-project:my-region:my-instance"}) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + errCh := make(chan error) + go func() { + err := c.ExecuteContext(ctx) + errCh <- err + }() + resp, err := tryDial("HEAD", "http://localhost:9192/quitquitquit") + if err != nil { + t.Fatalf("failed to dial endpoint: %v", err) + } + if resp.StatusCode != http.StatusBadRequest { + t.Fatalf("expected a 400 status, got = %v", resp.StatusCode) + } + resp, err = tryDial("POST", "http://localhost:9192/quitquitquit") + if err != nil { + t.Fatalf("failed to dial endpoint: %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("expected a 200 status, got = %v", resp.StatusCode) + } + + var gotErr error + select { + case err := <-errCh: + gotErr = err + case <-time.After(30 * time.Second): + t.Fatal("timeout waiting for error") + } + + if !errors.Is(gotErr, errQuitQuitQuit) { + t.Fatalf("want = %v, got = %v", errQuitQuitQuit, gotErr) + } +} + +func TestQuitQuitQuitHTTPGet(t *testing.T) { + c := NewCommand(WithDialer(&spyDialer{})) + c.SilenceUsage = true + c.SilenceErrors = true + c.SetArgs([]string{"--quitquitquit", "--admin-port", "9194", "my-project:my-region:my-instance"}) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + errCh := make(chan error) + go func() { + err := c.ExecuteContext(ctx) + errCh <- err + }() + + resp, err := tryDial("GET", "http://localhost:9194/quitquitquit") + if err != nil { + t.Fatalf("failed to dial endpoint: %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("expected a 200 status, got = %v", resp.StatusCode) + } + + var gotErr error + select { + case err := <-errCh: + gotErr = err + case <-time.After(30 * time.Second): + t.Fatal("timeout waiting for error") + } + + if !errors.Is(gotErr, errQuitQuitQuit) { + t.Fatalf("want = %v, got = %v", errQuitQuitQuit, gotErr) + } +} + +type errorDialer struct { + spyDialer +} + +var errCloseFailed = errors.New("close failed") + +func (*errorDialer) Close() error { + return errCloseFailed +} + +func TestQuitQuitQuitWithErrors(t *testing.T) { + c := NewCommand(WithDialer(&errorDialer{})) + c.SilenceUsage = true + c.SilenceErrors = true + c.SetArgs([]string{ + "--quitquitquit", "--admin-port", "9193", + "my-project:my-region:my-instance", + }) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + errCh := make(chan error) + go func() { + err := c.ExecuteContext(ctx) + errCh <- err + }() + resp, err := tryDial("POST", "http://localhost:9193/quitquitquit") + if err != nil { + t.Fatalf("failed to dial endpoint: %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("expected a 200 status, got = %v", resp.StatusCode) + } + // The returned error is the error from closing the dialer. + got := <-errCh + if !strings.Contains(got.Error(), "close failed") { + t.Fatalf("want = %v, got = %v", errCloseFailed, got) + } +} diff --git a/cmd/shutdown_test.go b/cmd/shutdown_test.go new file mode 100644 index 000000000..03bd44cad --- /dev/null +++ b/cmd/shutdown_test.go @@ -0,0 +1,67 @@ +// Copyright 2025 Google LLC +// +// 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. + +package cmd + +import ( + "net" + "net/http" + "net/http/httptest" + "testing" + "time" +) + +func TestShutdownCommand(t *testing.T) { + shutdownCh := make(chan bool, 1) + handler := func(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + t.Errorf("want = POST, got = %v", r.Method) + } + w.WriteHeader(http.StatusOK) + shutdownCh <- true + } + server := httptest.NewServer(http.HandlerFunc(handler)) + defer server.Close() + + _, port, err := net.SplitHostPort(server.Listener.Addr().String()) + if err != nil { + t.Fatal(err) + } + + _, err = invokeProxyCommand([]string{ + "shutdown", + "--admin-port", port, + }) + if err != nil { + t.Fatalf("invokeProxyCommand failed: %v", err) + } + + select { + case <-shutdownCh: + // success + case <-time.After(1 * time.Second): + t.Fatal("server did not receive shutdown request") + } +} + +func TestShutdownCommandFails(t *testing.T) { + _, err := invokeProxyCommand([]string{ + "shutdown", + // assuming default host and port + "--wait=100ms", + }) + if err == nil { + t.Fatal("shutdown should fail when endpoint does not respond") + } +} diff --git a/cmd/testdata/config-json.json b/cmd/testdata/config-json.json new file mode 100644 index 000000000..6a8ea08dc --- /dev/null +++ b/cmd/testdata/config-json.json @@ -0,0 +1,4 @@ +{ + "instance-connection-name": "proj:region:inst", + "debug": true +} diff --git a/cmd/testdata/config-toml.toml b/cmd/testdata/config-toml.toml new file mode 100644 index 000000000..2cad8bc6b --- /dev/null +++ b/cmd/testdata/config-toml.toml @@ -0,0 +1,5 @@ +instance-connection-name = "proj:region:inst" +debug = true +port = "5555" +debug-logs = true +auto-iam-authn = true diff --git a/cmd/testdata/config-yaml.yaml b/cmd/testdata/config-yaml.yaml new file mode 100644 index 000000000..4ab85f439 --- /dev/null +++ b/cmd/testdata/config-yaml.yaml @@ -0,0 +1,16 @@ +# Copyright 2024 Google LLC +# +# 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. + +instance-connection-name: "proj:region:inst" +debug: true diff --git a/cmd/testdata/two-instances.toml b/cmd/testdata/two-instances.toml new file mode 100644 index 000000000..ddd670f5e --- /dev/null +++ b/cmd/testdata/two-instances.toml @@ -0,0 +1,2 @@ +instance-connection-name-0 = "x:y:z" +instance-connection-name-1 = "a:b:c" diff --git a/cmd/version.txt b/cmd/version.txt index 2accad80e..db65e2167 100644 --- a/cmd/version.txt +++ b/cmd/version.txt @@ -1 +1 @@ -2.0.0-preview.4-dev +2.21.0 diff --git a/cmd/wait_test.go b/cmd/wait_test.go new file mode 100644 index 000000000..fcc332b67 --- /dev/null +++ b/cmd/wait_test.go @@ -0,0 +1,68 @@ +// Copyright 2023 Google LLC +// +// 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. + +package cmd + +import ( + "io" + "net" + "testing" + "time" +) + +func TestWaitCommandFlags(t *testing.T) { + ln, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatal(err) + } + defer ln.Close() + host, port, err := net.SplitHostPort(ln.Addr().String()) + if err != nil { + t.Fatal(err) + } + go func() { + conn, err := ln.Accept() + if err != nil { + return + } + defer conn.Close() + // Use a read deadline to produce read error + conn.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) + // Read client request first. + io.ReadAll(conn) + // Write a generic 200 response back. + conn.Write([]byte("HTTP/1.1 200 OK\r\n\r\n")) + }() + + _, err = invokeProxyCommand([]string{ + "wait", + "--http-address", host, + "--http-port", port, + "--max=1s", + }) + if err != nil { + t.Fatal(err) + } +} + +func TestWaitCommandFails(t *testing.T) { + _, err := invokeProxyCommand([]string{ + "wait", + // assuming default host and port + "--max=100ms", + }) + if err == nil { + t.Fatal("wait should fail when endpoint does not respond") + } +} diff --git a/docs/cmd/cloud-sql-proxy.md b/docs/cmd/cloud-sql-proxy.md new file mode 100644 index 000000000..1cd06278e --- /dev/null +++ b/docs/cmd/cloud-sql-proxy.md @@ -0,0 +1,299 @@ +## cloud-sql-proxy + +cloud-sql-proxy authorizes and encrypts connections to Cloud SQL. + +### Synopsis + + +Overview + + The Cloud SQL Auth Proxy is a utility for ensuring secure connections to + your Cloud SQL instances. It provides IAM authorization, allowing you to + control who can connect to your instance through IAM permissions, and TLS + 1.3 encryption, without having to manage certificates. + + NOTE: The Proxy does not configure the network. You MUST ensure the Proxy + can reach your Cloud SQL instance, either by deploying it in a VPC that has + access to your Private IP instance, or by configuring Public IP. + + For every provided instance connection name, the Proxy creates: + + - a socket that mimics a database running locally, and + - an encrypted connection using TLS 1.3 back to your Cloud SQL instance. + + The Proxy uses an ephemeral certificate to establish a secure connection to + your Cloud SQL instance. The Proxy will refresh those certificates on an + hourly basis. Existing client connections are unaffected by the refresh + cycle. + +Starting the Proxy + + To start the Proxy, you will need your instance connection name, which may + be found in the Cloud SQL instance overview page or by using gcloud with the + following command: + + gcloud sql instances describe INSTANCE --format='value(connectionName)' + + For example, if your instance connection name is + "my-project:us-central1:my-db-server", starting the Proxy will be: + + ./cloud-sql-proxy my-project:us-central1:my-db-server + + By default, the Proxy will determine the database engine and start a + listener on localhost using the default database engine's port, i.e., MySQL + is 3306, Postgres is 5432, SQL Server is 1433. If multiple instances are + specified which all use the same database engine, the first will be started + on the default database port and subsequent instances will be incremented + from there (e.g., 3306, 3307, 3308, etc). To disable this behavior (and + reduce startup time), use the --port flag. All subsequent listeners will + increment from the provided value. + + All socket listeners use the localhost network interface. To override this + behavior, use the --address flag. + +Instance Level Configuration + + The Proxy supports overriding configuration on an instance-level with an + optional query string syntax using the corresponding full flag name. The + query string takes the form of a URL query string and should be appended to + the INSTANCE_CONNECTION_NAME, e.g., + + 'my-project:us-central1:my-db-server?key1=value1&key2=value2' + + When using the optional query string syntax, quotes must wrap the instance + connection name and query string to prevent conflicts with the shell. For + example, to override the address and port for one instance but otherwise use + the default behavior, use: + + ./cloud-sql-proxy \ + my-project:us-central1:my-db-server \ + 'my-project:us-central1:my-other-server?address=0.0.0.0&port=7000' + + When necessary, you may specify the full path to a Unix socket. Set the + unix-socket-path query parameter to the absolute path of the Unix socket for + the database instance. The parent directory of the unix-socket-path must + exist when the Proxy starts or else socket creation will fail. For Postgres + instances, the Proxy will ensure that the last path element is + '.s.PGSQL.5432' appending it if necessary. For example, + + ./cloud-sql-proxy \ + 'my-project:us-central1:my-db-server?unix-socket-path=/path/to/socket' + +Health checks + + When enabling the --health-check flag, the Proxy will start an HTTP server + on localhost with three endpoints: + + - /startup: Returns 200 status when the Proxy has finished starting up. + Otherwise returns 503 status. + + - /readiness: Returns 200 status when the Proxy has started, has available + connections if max connections have been set with the --max-connections + flag, and when the Proxy can connect to all registered instances. Otherwise, + returns a 503 status. + + - /liveness: Always returns 200 status. If this endpoint is not responding, + the Proxy is in a bad state and should be restarted. + + To configure the address, use --http-address. To configure the port, use + --http-port. + +Service Account Impersonation + + The Proxy supports service account impersonation with the + --impersonate-service-account flag and matches gclouds flag. When enabled, + all API requests are made impersonating the supplied service account. The + IAM principal must have the iam.serviceAccounts.getAccessToken permission or + the role roles/iam.serviceAccounts.serviceAccountTokenCreator. + + For example: + + ./cloud-sql-proxy \ + --impersonate-service-account=impersonated@my-project.iam.gserviceaccount.com + my-project:us-central1:my-db-server + + In addition, the flag supports an impersonation delegation chain where the + value is a comma-separated list of service accounts. The first service + account in the list is the impersonation target. Each subsequent service + account is a delegate to the previous service account. When delegation is + used, each delegate must have the permissions named above on the service + account it is delegating to. + + For example: + + ./cloud-sql-proxy \ + --impersonate-service-account=SERVICE_ACCOUNT_1,SERVICE_ACCOUNT_2,SERVICE_ACCOUNT_3 + my-project:us-central1:my-db-server + + In this example, the environment's IAM principal impersonates + SERVICE_ACCOUNT_3 which impersonates SERVICE_ACCOUNT_2 which then + impersonates the target SERVICE_ACCOUNT_1. + +Configuration using environment variables + + Instead of using CLI flags, the Proxy may be configured using environment + variables. Each environment variable uses "CSQL_PROXY" as a prefix and is + the uppercase version of the flag using underscores as word delimiters. For + example, the --auto-iam-authn flag may be set with the environment variable + CSQL_PROXY_AUTO_IAM_AUTHN. An invocation of the Proxy using environment + variables would look like the following: + + CSQL_PROXY_AUTO_IAM_AUTHN=true \ + ./cloud-sql-proxy my-project:us-central1:my-db-server + + In addition to CLI flags, instance connection names may also be specified + with environment variables. If invoking the Proxy with only one instance + connection name, use CSQL_PROXY_INSTANCE_CONNECTION_NAME. For example: + + CSQL_PROXY_INSTANCE_CONNECTION_NAME=my-project:us-central1:my-db-server \ + ./cloud-sql-proxy + + If multiple instance connection names are used, add the index of the + instance connection name as a suffix. For example: + + CSQL_PROXY_INSTANCE_CONNECTION_NAME_0=my-project:us-central1:my-db-server \ + CSQL_PROXY_INSTANCE_CONNECTION_NAME_1=my-other-project:us-central1:my-other-server \ + ./cloud-sql-proxy + +Configuration using a configuration file + + Instead of using CLI flags, the Proxy may be configured using a configuration + file. The configuration file is a TOML, YAML or JSON file with the same keys + as the environment variables. The configuration file is specified with the + --config-file flag. An invocation of the Proxy using a configuration file + would look like the following: + + ./cloud-sql-proxy --config-file=config.toml + + The configuration file may look like the following: + + instance-connection-name = "my-project:us-central1:my-server-instance" + auto-iam-authn = true + + If multiple instance connection names are used, add the index of the + instance connection name as a suffix. For example: + + instance-connection-name-0 = "my-project:us-central1:my-db-server" + instance-connection-name-1 = "my-other-project:us-central1:my-other-server" + + The configuration file may also contain the same keys as the environment + variables and flags. For example: + + auto-iam-authn = true + debug = true + max-connections = 5 + +Localhost Admin Server + + The Proxy includes support for an admin server on localhost. By default, + the admin server is not enabled. To enable the server, pass the --debug or + --quitquitquit flag. This will start the server on localhost at port 9091. + To change the port, use the --admin-port flag. + + When --debug is set, the admin server enables Go's profiler available at + /debug/pprof/. + + See the documentation on pprof for details on how to use the + profiler at https://pkg.go.dev/net/http/pprof. + + When --quitquitquit is set, the admin server adds an endpoint at + /quitquitquit. The admin server exits gracefully when it receives a GET or POST + request at /quitquitquit. + +Debug logging + + On occasion, it can help to enable debug logging which will report on + internal certificate refresh operations. To enable debug logging, use: + + ./cloud-sql-proxy --debug-logs + +Waiting for Startup + + See the wait subcommand's help for details. + +(*) indicates a flag that may be used as a query parameter + +Third Party Licenses + + To view all licenses for third party dependencies used within this + distribution please see: + + https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.21.0/third_party/licenses.tar.gz + + +``` +cloud-sql-proxy INSTANCE_CONNECTION_NAME... [flags] +``` + +### Options + +``` + -a, --address string (*) Address to bind Cloud SQL instance listeners. (default "127.0.0.1") + --admin-port string Port for localhost-only admin server (default "9091") + -i, --auto-iam-authn (*) Enables Automatic IAM Authentication for all instances + --auto-ip Supports legacy behavior of v1 and will try to connect to first IP + address returned by the SQL Admin API. In most cases, this flag should not be used. + Prefer default of public IP or use --private-ip instead. + --config-file string Path to a TOML file containing configuration options. + -c, --credentials-file string Use service account key file as a source of IAM credentials. + --debug Enable pprof on the localhost admin server + --debug-logs Enable debug logging + --disable-metrics Disable Cloud Monitoring integration (used with --telemetry-project) + --disable-traces Disable Cloud Trace integration (used with --telemetry-project) + --exit-zero-on-sigterm Exit with 0 exit code when Sigterm received (default is 143) + --fuse string Mount a directory at the path using FUSE to access Cloud SQL instances. + --fuse-tmp-dir string Temp dir for Unix sockets created with FUSE (default "/tmp/csql-tmp") + -g, --gcloud-auth Use gclouds user credentials as a source of IAM credentials. + NOTE: this flag is a legacy feature and generally should not be used. + Instead prefer Application Default Credentials + (enabled with: gcloud auth application-default login) which + the Proxy will then pick-up automatically. + --health-check Enables health check endpoints /startup, /liveness, and /readiness on localhost. + -h, --help Display help information for cloud-sql-proxy + --http-address string Address for Prometheus and health check server (default "localhost") + --http-port string Port for Prometheus and health check server (default "9090") + --impersonate-service-account string Comma separated list of service accounts to impersonate. Last value + is the target account. + -j, --json-credentials string Use service account key JSON as a source of IAM credentials. + --lazy-refresh Configure a lazy refresh where connection info is retrieved only if + the cached copy has expired. Use this setting in environments where the + CPU may be throttled and a background refresh cannot run reliably + (e.g., Cloud Run) + --login-token string Use bearer token as a database password (used with token and auto-iam-authn only) + --max-connections uint Limit the number of connections. Default is no limit. + --max-sigterm-delay duration Maximum number of seconds to wait for connections to close after receiving a TERM signal. + --min-sigterm-delay duration The number of seconds to accept new connections after receiving a TERM signal. + -p, --port int (*) Initial port for listeners. Subsequent listeners increment from this value. + --private-ip (*) Connect to the private ip address for all instances + --prometheus Enable Prometheus HTTP endpoint /metrics on localhost + --prometheus-namespace string Use the provided Prometheus namespace for metrics + --psc (*) Connect to the PSC endpoint for all instances + --quiet Log error messages only + --quitquitquit Enable quitquitquit endpoint on the localhost admin server + --quota-project string Specifies the project to use for Cloud SQL Admin API quota tracking. + The IAM principal must have the "serviceusage.services.use" permission + for the given project. See https://cloud.google.com/service-usage/docs/overview and + https://cloud.google.com/storage/docs/requester-pays + --run-connection-test Runs a connection test + against all specified instances. If an instance is unreachable, the Proxy exits with a failure + status code. + --skip-failed-instance-config If set, the Proxy will skip any instances that are invalid/unreachable ( + only applicable to Unix sockets) + --sqladmin-api-endpoint string API endpoint for all Cloud SQL Admin API requests. (default: https://sqladmin.googleapis.com) + -l, --structured-logs Enable structured logging with LogEntry format + --telemetry-prefix string Prefix for Cloud Monitoring metrics. + --telemetry-project string Enable Cloud Monitoring and Cloud Trace with the provided project ID. + --telemetry-sample-rate int Set the Cloud Trace sample rate. A smaller number means more traces. (default 10000) + -t, --token string Use bearer token as a source of IAM credentials. + --universe-domain string Universe Domain for non-GDU environments. (default: googleapis.com) + -u, --unix-socket string (*) Enables Unix sockets for all listeners with the provided directory. + --user-agent string Space separated list of additional user agents, e.g. cloud-sql-proxy-operator/0.0.1 + -v, --version Print the cloud-sql-proxy version +``` + +### SEE ALSO + +* [cloud-sql-proxy completion](cloud-sql-proxy_completion.md) - Generate the autocompletion script for the specified shell +* [cloud-sql-proxy shutdown](cloud-sql-proxy_shutdown.md) - Signal a running Proxy process to shut down +* [cloud-sql-proxy wait](cloud-sql-proxy_wait.md) - Wait for another Proxy process to start + diff --git a/docs/cmd/cloud-sql-proxy_completion.md b/docs/cmd/cloud-sql-proxy_completion.md new file mode 100644 index 000000000..8cbe02458 --- /dev/null +++ b/docs/cmd/cloud-sql-proxy_completion.md @@ -0,0 +1,31 @@ +## cloud-sql-proxy completion + +Generate the autocompletion script for the specified shell + +### Synopsis + +Generate the autocompletion script for cloud-sql-proxy for the specified shell. +See each sub-command's help for details on how to use the generated script. + + +### Options + +``` + -h, --help help for completion +``` + +### Options inherited from parent commands + +``` + --http-address string Address for Prometheus and health check server (default "localhost") + --http-port string Port for Prometheus and health check server (default "9090") +``` + +### SEE ALSO + +* [cloud-sql-proxy](cloud-sql-proxy.md) - cloud-sql-proxy authorizes and encrypts connections to Cloud SQL. +* [cloud-sql-proxy completion bash](cloud-sql-proxy_completion_bash.md) - Generate the autocompletion script for bash +* [cloud-sql-proxy completion fish](cloud-sql-proxy_completion_fish.md) - Generate the autocompletion script for fish +* [cloud-sql-proxy completion powershell](cloud-sql-proxy_completion_powershell.md) - Generate the autocompletion script for powershell +* [cloud-sql-proxy completion zsh](cloud-sql-proxy_completion_zsh.md) - Generate the autocompletion script for zsh + diff --git a/docs/cmd/cloud-sql-proxy_completion_bash.md b/docs/cmd/cloud-sql-proxy_completion_bash.md new file mode 100644 index 000000000..18ccb35df --- /dev/null +++ b/docs/cmd/cloud-sql-proxy_completion_bash.md @@ -0,0 +1,50 @@ +## cloud-sql-proxy completion bash + +Generate the autocompletion script for bash + +### Synopsis + +Generate the autocompletion script for the bash shell. + +This script depends on the 'bash-completion' package. +If it is not installed already, you can install it via your OS's package manager. + +To load completions in your current shell session: + + source <(cloud-sql-proxy completion bash) + +To load completions for every new session, execute once: + +#### Linux: + + cloud-sql-proxy completion bash > /etc/bash_completion.d/cloud-sql-proxy + +#### macOS: + + cloud-sql-proxy completion bash > $(brew --prefix)/etc/bash_completion.d/cloud-sql-proxy + +You will need to start a new shell for this setup to take effect. + + +``` +cloud-sql-proxy completion bash +``` + +### Options + +``` + -h, --help help for bash + --no-descriptions disable completion descriptions +``` + +### Options inherited from parent commands + +``` + --http-address string Address for Prometheus and health check server (default "localhost") + --http-port string Port for Prometheus and health check server (default "9090") +``` + +### SEE ALSO + +* [cloud-sql-proxy completion](cloud-sql-proxy_completion.md) - Generate the autocompletion script for the specified shell + diff --git a/docs/cmd/cloud-sql-proxy_completion_fish.md b/docs/cmd/cloud-sql-proxy_completion_fish.md new file mode 100644 index 000000000..bfc1bce6d --- /dev/null +++ b/docs/cmd/cloud-sql-proxy_completion_fish.md @@ -0,0 +1,41 @@ +## cloud-sql-proxy completion fish + +Generate the autocompletion script for fish + +### Synopsis + +Generate the autocompletion script for the fish shell. + +To load completions in your current shell session: + + cloud-sql-proxy completion fish | source + +To load completions for every new session, execute once: + + cloud-sql-proxy completion fish > ~/.config/fish/completions/cloud-sql-proxy.fish + +You will need to start a new shell for this setup to take effect. + + +``` +cloud-sql-proxy completion fish [flags] +``` + +### Options + +``` + -h, --help help for fish + --no-descriptions disable completion descriptions +``` + +### Options inherited from parent commands + +``` + --http-address string Address for Prometheus and health check server (default "localhost") + --http-port string Port for Prometheus and health check server (default "9090") +``` + +### SEE ALSO + +* [cloud-sql-proxy completion](cloud-sql-proxy_completion.md) - Generate the autocompletion script for the specified shell + diff --git a/docs/cmd/cloud-sql-proxy_completion_powershell.md b/docs/cmd/cloud-sql-proxy_completion_powershell.md new file mode 100644 index 000000000..b74a09c4b --- /dev/null +++ b/docs/cmd/cloud-sql-proxy_completion_powershell.md @@ -0,0 +1,38 @@ +## cloud-sql-proxy completion powershell + +Generate the autocompletion script for powershell + +### Synopsis + +Generate the autocompletion script for powershell. + +To load completions in your current shell session: + + cloud-sql-proxy completion powershell | Out-String | Invoke-Expression + +To load completions for every new session, add the output of the above command +to your powershell profile. + + +``` +cloud-sql-proxy completion powershell [flags] +``` + +### Options + +``` + -h, --help help for powershell + --no-descriptions disable completion descriptions +``` + +### Options inherited from parent commands + +``` + --http-address string Address for Prometheus and health check server (default "localhost") + --http-port string Port for Prometheus and health check server (default "9090") +``` + +### SEE ALSO + +* [cloud-sql-proxy completion](cloud-sql-proxy_completion.md) - Generate the autocompletion script for the specified shell + diff --git a/docs/cmd/cloud-sql-proxy_completion_zsh.md b/docs/cmd/cloud-sql-proxy_completion_zsh.md new file mode 100644 index 000000000..486c624a5 --- /dev/null +++ b/docs/cmd/cloud-sql-proxy_completion_zsh.md @@ -0,0 +1,52 @@ +## cloud-sql-proxy completion zsh + +Generate the autocompletion script for zsh + +### Synopsis + +Generate the autocompletion script for the zsh shell. + +If shell completion is not already enabled in your environment you will need +to enable it. You can execute the following once: + + echo "autoload -U compinit; compinit" >> ~/.zshrc + +To load completions in your current shell session: + + source <(cloud-sql-proxy completion zsh) + +To load completions for every new session, execute once: + +#### Linux: + + cloud-sql-proxy completion zsh > "${fpath[1]}/_cloud-sql-proxy" + +#### macOS: + + cloud-sql-proxy completion zsh > $(brew --prefix)/share/zsh/site-functions/_cloud-sql-proxy + +You will need to start a new shell for this setup to take effect. + + +``` +cloud-sql-proxy completion zsh [flags] +``` + +### Options + +``` + -h, --help help for zsh + --no-descriptions disable completion descriptions +``` + +### Options inherited from parent commands + +``` + --http-address string Address for Prometheus and health check server (default "localhost") + --http-port string Port for Prometheus and health check server (default "9090") +``` + +### SEE ALSO + +* [cloud-sql-proxy completion](cloud-sql-proxy_completion.md) - Generate the autocompletion script for the specified shell + diff --git a/docs/cmd/cloud-sql-proxy_shutdown.md b/docs/cmd/cloud-sql-proxy_shutdown.md new file mode 100644 index 000000000..aa5befb24 --- /dev/null +++ b/docs/cmd/cloud-sql-proxy_shutdown.md @@ -0,0 +1,52 @@ +## cloud-sql-proxy shutdown + +Signal a running Proxy process to shut down + +### Synopsis + + +Shutting Down the Proxy + + The shutdown command signals a running Proxy process to gracefully shut + down. This is useful for scripting and for Kubernetes environments. + + The shutdown command requires that the Proxy be started in another process + with the admin server enabled. For example: + + ./cloud-sql-proxy --quitquitquit + + Invoke the shutdown command like this: + + # signals another Proxy process to shut down + ./cloud-sql-proxy shutdown + +Configuration + + If the running Proxy is configured with a non-default admin port, the + shutdown command must also be told to use the same custom value: + + ./cloud-sql-proxy shutdown --admin-port 9192 + + +``` +cloud-sql-proxy shutdown [flags] +``` + +### Options + +``` + --admin-port string port for the admin server (default "9091") + -h, --help help for shutdown +``` + +### Options inherited from parent commands + +``` + --http-address string Address for Prometheus and health check server (default "localhost") + --http-port string Port for Prometheus and health check server (default "9090") +``` + +### SEE ALSO + +* [cloud-sql-proxy](cloud-sql-proxy.md) - cloud-sql-proxy authorizes and encrypts connections to Cloud SQL. + diff --git a/docs/cmd/cloud-sql-proxy_wait.md b/docs/cmd/cloud-sql-proxy_wait.md new file mode 100644 index 000000000..27d90f569 --- /dev/null +++ b/docs/cmd/cloud-sql-proxy_wait.md @@ -0,0 +1,67 @@ +## cloud-sql-proxy wait + +Wait for another Proxy process to start + +### Synopsis + + +Waiting for Proxy Startup + + Sometimes it is necessary to wait for the Proxy to start. + + To help ensure the Proxy is up and ready, the Proxy includes a wait + subcommand with an optional --max flag to set the maximum time to wait. + The wait command uses a separate Proxy's startup endpoint to determine + if the other Proxy process is ready. + + Invoke the wait command, like this: + + # waits for another Proxy process' startup endpoint to respond + ./cloud-sql-proxy wait + +Configuration + + By default, the Proxy will wait up to the maximum time for the startup + endpoint to respond. The wait command requires that the Proxy be started in + another process with the HTTP health check or Prometheus enabled. If an + alternate health check port or address is used, as in: + + ./cloud-sql-proxy \ + --http-address 0.0.0.0 \ + --http-port 9191 \ + --health-check + + Then the wait command must also be told to use the same custom values: + + ./cloud-sql-proxy wait \ + --http-address 0.0.0.0 \ + --http-port 9191 + + By default the wait command will wait 30 seconds. To alter this value, + use: + + ./cloud-sql-proxy wait --max 10s + + +``` +cloud-sql-proxy wait [flags] +``` + +### Options + +``` + -h, --help help for wait + -m, --max duration maximum amount of time to wait for startup (default 30s) +``` + +### Options inherited from parent commands + +``` + --http-address string Address for Prometheus and health check server (default "localhost") + --http-port string Port for Prometheus and health check server (default "9090") +``` + +### SEE ALSO + +* [cloud-sql-proxy](cloud-sql-proxy.md) - cloud-sql-proxy authorizes and encrypts connections to Cloud SQL. + diff --git a/examples/disaster-recovery/README.md b/examples/disaster-recovery/README.md new file mode 100644 index 000000000..2ffc3f501 --- /dev/null +++ b/examples/disaster-recovery/README.md @@ -0,0 +1,51 @@ +# Coordinate disaster recovery with Secret Manager + +## Background + +This document assumes you are already using the following strategy for +detecting and triggering failovers: +1. Using an independent service to detect when the primary is down +2. Trigger a promotion of an existing read replica to become a primary +3. Update a Secret Manager secret with the name of the current primary + +## Restart Auth Proxy when secret changes + +This option uses a wrapper script around the Cloud SQL Auth Proxy to detect +when the secret has changed, and restart the Proxy with the new value. This +could be done in many languages, but here’s an example using bash: + +> [failover.sh](examples/disaster-recovery/failover.sh) +```sh +#! /bin/bash + +SECRET_ID="my-secret-id" # TODO(developer): replace this value +REFRESH_INTERVAL=5 +PORT=5432 + +# Get the latest version of the secret and start the Proxy +INSTANCE=$(gcloud secrets versions access "latest" --secret="$SECRET_ID") +cloud-sql-proxy --port "$PORT" "$INSTANCE" & +PID=$! + +# Every 5s, get the latest version of the secret. If it's changed, restart the +# Proxy with the new value. +while true; do + sleep $REFRESH_INTERVAL + NEW=$(gcloud secrets versions access "latest" --secret="$SECRET_ID") + if [ "$INSTANCE" != "$NEW" ]; then + INSTANCE=$NEW + kill $PID + wait $PID + cloud-sql-proxy --port "$PORT" "$INSTANCE" & + PID=$! + fi +done +``` + +## Benefits of this approach + +Using this approach will help assist with failovers without needing to +reconfigure your application. Instead, by changing the Proxy the application +will always connect to 127.0.0.1 and won’t need to restart to apply +configuration changes. Additionally, it will prevent split brain syndrome by +ensuring that your application can only connect to the current “primary”. diff --git a/examples/disaster-recovery/failover.sh b/examples/disaster-recovery/failover.sh new file mode 100644 index 000000000..66ce3c8f8 --- /dev/null +++ b/examples/disaster-recovery/failover.sh @@ -0,0 +1,40 @@ +# Copyright 2023 Google LLC +# +# 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. + +# [START cloud_sql_proxy_secret_manager_failover] +#! /bin/bash + +SECRET_ID="my-secret-id" # TODO(developer): replace this value +PORT=5432 + +# Get the latest version of the secret and start the proxy +INSTANCE=$(gcloud secrets versions access "latest" --secret="$SECRET_ID") +cloud-sql-proxy --port "$PORT" "$INSTANCE" & +PID=$! + +# Every 5s, get the latest version of the secret. If it's changed, restart the +# proxy with the new value. +while true; do + sleep 5 + NEW=$(gcloud secrets versions access "latest" --secret="$SECRET_ID") + if [ "$INSTANCE" != "$NEW" ]; then + INSTANCE=$NEW + kill $PID + wait $PID + cloud-sql-proxy --port "$PORT" "$INSTANCE" & + PID=$! + fi +done +# [END cloud_sql_proxy_secret_manager_failover] + diff --git a/examples/k8s-health-check/README.md b/examples/k8s-health-check/README.md index ddd6847a2..f8bfabd69 100644 --- a/examples/k8s-health-check/README.md +++ b/examples/k8s-health-check/README.md @@ -1,68 +1,194 @@ -# Cloud SQL proxy health checks +# Cloud SQL Auth Proxy health checks -Kubernetes supports three types of health checks. -1. Startup probes determine whether a container is done starting up. As soon as this probe succeeds, Kubernetes switches over to using liveness and readiness probing. -2. Liveness probes determine whether a container is healthy. When this probe is unsuccessful, the container is restarted. -3. Readiness probes determine whether a container can serve new traffic. When this probe fails, Kubernetes will wait to send requests to the container. +Kubernetes supports [three types of health checks][k8s-docs]: -## Running Cloud SQL proxy with health checks in Kubernetes -1. Configure your Cloud SQL proxy container to include health check probes. +1. Startup probes determine whether a container is done starting up. As soon as + this probe succeeds, Kubernetes switches over to using liveness and readiness + probing. +2. Liveness probes determine whether a container is healthy. When this probe is + unsuccessful, the container is restarted. +3. Readiness probes determine whether a container can serve new traffic. When + this probe fails, Kubernetes will wait to send requests to the container. + +[k8s-docs]: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ + +When enabling the `--health-check` flag, the proxy will start an HTTP server on +localhost with three endpoints: + +- `/startup`: Returns 200 status when the proxy has finished starting up. +Otherwise returns 503 status. + +- `/liveness`: Always returns 200 status. If this endpoint is not responding, +the proxy is in a bad state and should be restarted. + +- `/readiness`: Returns 200 status when the proxy has started, has available + connections if max connections have been set with the `--max-connections` + flag, and when the proxy can connect to all registered instances. Otherwise, + returns a 503 status. + + +To configure the address, use `--http-address`. To configure the port, use +`--http-port`. + +## Running Cloud SQL Auth Proxy with health checks in Kubernetes +1. Configure your Cloud SQL Auth Proxy container to include health check probes. > [proxy_with_http_health_check.yaml](proxy_with_http_health_check.yaml#L77-L111) - ```yaml - # Recommended configurations for health check probes. - # Probe parameters can be adjusted to best fit the requirements of your application. - # For details, see https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ - livenessProbe: - httpGet: - path: /liveness - port: 8090 - # Number of seconds after the container has started before the first probe is scheduled. Defaults to 0. - # Not necessary when the startup probe is in use. - initialDelaySeconds: 0 - # Frequency of the probe. Defaults to 10. - periodSeconds: 10 - # Number of seconds after which the probe times out. Defaults to 1. - timeoutSeconds: 5 - # Number of times the probe is allowed to fail before the transition from healthy to failure state. - # Defaults to 3. - failureThreshold: 1 - readinessProbe: - httpGet: - path: /readiness - port: 8090 - initialDelaySeconds: 0 - periodSeconds: 10 - timeoutSeconds: 5 - # Number of times the probe must report success to transition from failure to healthy state. - # Defaults to 1 for readiness probe. - successThreshold: 1 - failureThreshold: 1 - startupProbe: - httpGet: - path: /startup - port: 8090 - periodSeconds: 1 - timeoutSeconds: 5 - failureThreshold: 20 - ``` - -2. Add `-use_http_health_check` and `-health-check-port` (optional) to your proxy container configuration under `command: `. - > [proxy_with_http_health_check.yaml](proxy_with_http_health_check.yaml#L39-L55) - ```yaml - args: - # Enable structured logging with LogEntry format: - - "--structured-logs" - - # This flag specifies where the service account key can be found - # Remove this argument if you are using workload identity - - "--credentials-file=/secrets/service_account.json" - - # Turn on health check endpoints on port 8090: - - "--health-check" - - "--http-port=8090" - - # Replace DB_PORT with the port the proxy should listen on - - "--port=" - - "" - ``` +```yaml +# Recommended configurations for health check probes. +# Probe parameters can be adjusted to best fit the requirements of your application. +# For details, see https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ +startupProbe: + # We recommend adding a startup probe to the proxy sidecar + # container. This will ensure that service traffic will be routed to + # the pod only after the proxy has successfully started. + httpGet: + path: /startup + port: 9801 + periodSeconds: 1 + timeoutSeconds: 5 + failureThreshold: 20 +livenessProbe: + # We recommend adding a liveness probe to the proxy sidecar container. + httpGet: + path: /liveness + port: 9801 + # Number of seconds after the container has started before the first probe is scheduled. Defaults to 0. + # Not necessary when the startup probe is in use. + initialDelaySeconds: 0 + # Frequency of the probe. + periodSeconds: 60 + # Number of seconds after which the probe times out. + timeoutSeconds: 30 + # Number of times the probe is allowed to fail before the transition + # from healthy to failure state. + # + # If periodSeconds = 60, 5 tries will result in five minutes of + # checks. The proxy starts to refresh a certificate five minutes + # before its expiration. If those five minutes lapse without a + # successful refresh, the liveness probe will fail and the pod will be + # restarted. + failureThreshold: 5 +# We do not recommend adding a readiness probe under most circumstances +``` + +2. Enable the health checks by setting `--http-address` and `--http-port` (optional) to your + proxy container configuration under `command: `. + > [proxy_with_http_health_check.yaml](proxy_with_http_health_check.yaml#L53-L76) + +```yaml +args: +# Replace with the instance connection +# name in the format: "project_name:region:instance_name" +- + +env: +# It can be easier to manage the k8s configuration file when you +# use environment variables instead of CLI flags. This is the +# recommended configuration. This configuration is enabled by default +# when the cloud-sql-proxy-operator configures a proxy image + +# Replace with the port that the proxy should open +# to listen for database connections from the application +- name: CSQL_PROXY_PORT + value: + +# Enable HTTP healthchecks on port 9801. This enables /liveness, +# /readiness and /startup health check endpoints. Allow connections +# listen for connections on any interface (0.0.0.0) so that the +# k8s management components can reach these endpoints. +- name: CSQL_PROXY_HEALTH_CHECK + value: "true" +- name: CSQL_PROXY_HTTP_PORT + value: "9801" +- name: CSQL_PROXY_HTTP_ADDRESS + value: 0.0.0.0 +# Configure the proxy to exit gracefully when sent a k8s configuration +# file. +- name: CSQL_PROXY_EXIT_ZERO_ON_SIGTERM + value: "true" + +``` + +### Readiness Health Check Configuration + +For most common usage, adding a readiness healthcheck to the proxy sidecar +container is unnecessary. An improperly configured readiness check can degrade +the application's availability. + +The proxy readiness probe fails when (1) the proxy used all its available +concurrent connections to a database, (2) the network connection +to the database is interrupted, (3) the database server is unavailable due +to a maintenance operation. These are transient states that usually resolve +within a few seconds. + +Most applications are resilient to transient database connection failures, and +do not need to be restarted. We recommend adding a readiness check to the +application container instead of the proxy container. The application can be +programmed to report whether it is ready to receive requests, and the healthcheck +can be tuned to restart the pod when the application is permanently stuck. + +You should use the proxy container's readiness probe when these circumstances +should cause k8s to terminate the entire pod: + +- The proxy can't connect to the database instances. +- The max number of connections are in use. + +When you do use the proxy pod's readiness probe, be sure to set the +`failureThreshold` and `periodSeconds` to avoid restarting the pod on frequent +transient failures. + +### Readiness Health Check Examples + +The DBA team performs database fail-overs drills without notice. A +batch job should fail if it cannot connect the database for 3 minutes. +Set the readiness check so that the pod will be terminated after 3 minutes +of consecutive readiness check failures. (6 failed readiness checks taken every 30 +seconds, 6 x 30sec = 3 minutes.) + +```yaml +readinessProbe: + httpGet: + path: /readiness + port: 9801 + initialDelaySeconds: 30 + # 30 sec period x 6 failures = 3 min until the pod is terminated + periodSeconds: 30 + failureThreshold: 6 + timeoutSeconds: 10 + successThreshold: 1 +``` + +A web application has a database connection pool leak and the +engineering team can't find the root cause. To keep the system running, +the application should be automatically restarted if it consumes 50 connections +for more than 1 minute. + + +```yaml + containers: + - name: my-application + image: gcr.io/my-container/my-application:1.1 + initContainers: + - name: cloud-sql-proxy + restartPolicy: Always + image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.21.0 + args: + # Set the --max-connections flag to 50 + - "--max-connections" + - "50" + - "--port=" + - "" +# ... + readinessProbe: + httpGet: + path: /readiness + port: 9801 + initialDelaySeconds: 10 + # 5 sec period x 12 failures = 60 sec until the pod is terminated + periodSeconds: 5 + failureThreshold: 12 + timeoutSeconds: 5 + successThreshold: 1 +``` + diff --git a/examples/k8s-health-check/proxy_with_http_health_check.yaml b/examples/k8s-health-check/proxy_with_http_health_check.yaml index c73bf9574..dc6b20f07 100644 --- a/examples/k8s-health-check/proxy_with_http_health_check.yaml +++ b/examples/k8s-health-check/proxy_with_http_health_check.yaml @@ -46,61 +46,109 @@ spec: secretKeyRef: name: key: database + # The proxy should be run as a native sidecar container, available in + # Kubernetes 1.29 and higher. This will ensure that the proxy container + # is ready before the main application container is started, and + # that the proxy container's exit status will not impact the pod's exit + # status. See the Kubernetes documentation: + # https://kubernetes.io/docs/concepts/workloads/pods/sidecar-containers/ + initContainers: - name: cloud-sql-proxy - # It is recommended to use the latest version of the Cloud SQL proxy + restartPolicy: Always + # It is recommended to use the latest version of the Cloud SQL Auth Proxy # Make sure to update on a regular schedule! - image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.0.0.preview.0 # make sure the use the latest version + image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.11.4 + imagePullPolicy: IfNotPresent + args: - # If connecting from a VPC-native GKE cluster, you can use the - # following flag to have the proxy connect over private IP - # - "--private-ip" + # Replace with the instance connection + # name in the format: "project_name:region:instance_name" + - - # Enable structured logging with LogEntry format: - - "--structured-logs" + env: + # Using environment variables instead of CLI arguments to configure the + # proxy k8s configuration can make it easier to read your + # k8s configuration files. + # + # This is the recommended configuration for your proxy pod template. + # It is used by the cloud-sql-proxy-operator when configuring the + # proxy container. - # This flag specifies where the service account key can be found - # Remove this argument if you are using workload identity - - "--credentials-file=/secrets/service_account.json" + # Replace with the port that the proxy should open + # to listen for database connections from the application + - name: CSQL_PROXY_PORT + value: - # Replace DB_PORT with the port the proxy should listen on - - "--port=" - - "" + # If connecting from a VPC-native GKE cluster, you can use the + # following flag to have the proxy connect over private IP + # - name: CSQL_PROXY_PRIVATE_IP + # value: "true" - securityContext: - # The default Cloud SQL proxy image runs as the - # "nonroot" user and group (uid: 65532) by default. - runAsNonRoot: true - volumeMounts: - - name: - mountPath: /secrets/ - readOnly: true - # Resource configuration depends on an application's requirements. You - # should adjust the following values based on what your application - # needs. For details, see https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - resources: - requests: - # The proxy's memory use scales linearly with the number of active - # connections. Fewer open connections will use less memory. Adjust - # this value based on your application's requirements. - memory: "2Gi" - # The proxy's CPU use scales linearly with the amount of IO between - # the database and the application. Adjust this value based on your - # application's requirements. - cpu: "1" - # Recommended configurations for health check probes. - # Probe parameters can be adjusted to best fit the requirements of your application. - # For details, see https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ + # Enable HTTP healthchecks on port 9801. This enables /liveness, + # /readiness and /startup health check endpoints. Allow connections + # listen for connections on any interface (0.0.0.0) so that the + # k8s management components can reach these endpoints. + - name: CSQL_PROXY_HEALTH_CHECK + value: "true" + - name: CSQL_PROXY_HTTP_PORT + value: "9801" + - name: CSQL_PROXY_HTTP_ADDRESS + value: 0.0.0.0 + + # Configure the proxy to exit gracefully when sent a k8s configuration + # file. + - name: CSQL_PROXY_EXIT_ZERO_ON_SIGTERM + value: "true" + + # Enable the admin api server (which only listens for local connections) + # and enable the /quitquitquit endpoint. This allows other pods + # to shut down the proxy gracefully when they are ready to exit. + - name: CSQL_PROXY_QUITQUITQUIT + value: "true" + - name: CSQL_PROXY_ADMIN_PORT + value: "9092" + + # Enable structured logging with LogEntry format + - name: CSQL_PROXY_STRUCTURED_LOGS + value: "true" + + # Configure kubernetes to call run the cloud-sql-proxy shutdown command + # before sending SIGTERM to the proxy when stopping the pod. This will + # give the proxy more time to gracefully exit. + lifecycle: + preStop: + exec: + command: ["/cloud-sql-proxy","shutdown", "--admin-port","9192"] + + # The /startup probe returns OK when the proxy is ready to receive + # connections from the application. In this example, k8s will check + # once a second for 60 seconds. + # + # We strongly recommend adding a startup probe to the proxy sidecar + # container. This will ensure that service traffic will be routed to + # the pod only after the proxy has successfully started. + startupProbe: + failureThreshold: 60 + httpGet: + path: /startup + port: 9801 + scheme: HTTP + periodSeconds: 1 + successThreshold: 1 + timeoutSeconds: 10 + # The /liveness probe returns OK as soon as the proxy application has + # begun its startup process and continues to return OK until the + # process stops. + # + # We recommend adding a liveness probe to the proxy sidecar container. livenessProbe: + failureThreshold: 3 httpGet: path: /liveness - port: 8090 - # Number of seconds after the container has started before the first probe is scheduled. Defaults to 0. - # Not necessary when the startup probe is in use. - initialDelaySeconds: 0 - # Frequency of the probe. - periodSeconds: 60 - # Number of seconds after which the probe times out. - timeoutSeconds: 30 + port: 9801 + scheme: HTTP + # The probe will be checked every 10 seconds. + periodSeconds: 10 # Number of times the probe is allowed to fail before the transition # from healthy to failure state. # @@ -109,25 +157,57 @@ spec: # before its expiration. If those five minutes lapse without a # successful refresh, the liveness probe will fail and the pod will be # restarted. - failureThreshold: 5 + successThreshold: 1 + # The probe will fail if it does not respond in 10 seconds + timeoutSeconds: 10 readinessProbe: + # The /readiness probe returns OK when the proxy can establish + # a new connections to its databases. + # + # Please use the readiness probe to the proxy sidecar with caution. + # An improperly configured readiness probe can cause unnecessary + # interruption to the application. See README.md for more detail. httpGet: path: /readiness - port: 8090 - initialDelaySeconds: 0 + port: 9801 + initialDelaySeconds: 10 periodSeconds: 10 - timeoutSeconds: 5 + timeoutSeconds: 10 # Number of times the probe must report success to transition from failure to healthy state. # Defaults to 1 for readiness probe. successThreshold: 1 - failureThreshold: 1 - startupProbe: - httpGet: - path: /startup - port: 8090 - periodSeconds: 1 - timeoutSeconds: 5 - failureThreshold: 20 + failureThreshold: 6 + + # Declare the HTTP Port so that k8s components can reach the + # metrics and health check endpoints. + ports: + - containerPort: 9801 + protocol: TCP + # You should use resource requests/limits as a best practice to prevent + # pods from consuming too many resources and affecting the execution of + # other pods. You should adjust the following values based on what your + # application needs. For details, see + # https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + resources: + requests: + # The proxy's memory use scales linearly with the number of active + # connections. Fewer open connections will use less memory. Adjust + # this value based on your application's requirements. + memory: "2Gi" + # The proxy's CPU use scales linearly with the amount of IO between + # the database and the application. Adjust this value based on your + # application's requirements. + cpu: "1" + securityContext: + # The default Cloud SQL Auth Proxy image runs as the + # "nonroot" user and group (uid: 65532) by default. + runAsNonRoot: true + # Use a read-only filesystem + readOnlyRootFilesystem: true + # Do not allow privilege escalation + allowPrivilegeEscalation : false + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File volumes: - name: secret: diff --git a/examples/k8s-service/README.md b/examples/k8s-service/README.md index 4ebde8919..c4c7a37e4 100644 --- a/examples/k8s-service/README.md +++ b/examples/k8s-service/README.md @@ -169,9 +169,12 @@ For the PgBouncer deployment, we add the proxy as a sidecar, starting it on port > [`pgbouncer_deployment.yaml`](pgbouncer_deployment.yaml#L70-L76) + ``` yaml +initContainers: - name: cloud-sql-proxy - image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.0.0.preview.0 # make sure the use the latest version + restartPolicy: Always + image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.21.0 # make sure to use the latest version args: # Replace DB_PORT with the port the proxy should listen on - "--port=" @@ -179,6 +182,7 @@ For the PgBouncer deployment, we add the proxy as a sidecar, starting it on port securityContext: runAsNonRoot: true ``` + Next, we create a PgBouncer service, listening on port 5342: diff --git a/examples/k8s-service/pgbouncer_deployment.yaml b/examples/k8s-service/pgbouncer_deployment.yaml index e83eda2a5..cf4eb9df5 100644 --- a/examples/k8s-service/pgbouncer_deployment.yaml +++ b/examples/k8s-service/pgbouncer_deployment.yaml @@ -81,8 +81,12 @@ spec: value: "/etc/server/key.pem" - name: CLIENT_TLS_CERT_FILE value: "/etc/server/cert.pem" + initContainers: - name: cloud-sql-proxy - image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.0.0.preview.0 # make sure to use the latest version + restartPolicy: Always + # It is recommended to use the latest version of the Cloud SQL Auth Proxy + # Make sure to update on a regular schedule! + image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.11.4 args: - "--port=5431" - "" diff --git a/examples/k8s-sidecar/README.md b/examples/k8s-sidecar/README.md index e5865c772..df45d2f6b 100644 --- a/examples/k8s-sidecar/README.md +++ b/examples/k8s-sidecar/README.md @@ -1,6 +1,6 @@ -# Using the Cloud SQL proxy on Kubernetes +# Using the Cloud SQL Auth Proxy on Kubernetes -The Cloud SQL proxy is the recommended way to connect to Cloud SQL, even when +The Cloud SQL Auth Proxy is the recommended way to connect to Cloud SQL, even when using private IP. This is because the proxy provides strong encryption and authentication using IAM, which help keep your database secure. @@ -47,7 +47,7 @@ above, the values will be in the env vars `DB_USER`, `DB_PASS`, and `DB_NAME`. ## Setting up a service account -The first step to running the Cloud SQL proxy in Kubernetes is creating a +The first step to running the Cloud SQL Auth Proxy in Kubernetes is creating a service account to represent your application. It is recommended that you create a service account unique to each application, instead of using the same service account everywhere. This model is more secure since it allows your to limit @@ -70,7 +70,7 @@ The service account for your application needs to meet the following criteria: ## Providing the service account to the proxy Next, you need to configure Kubernetes to provide the service account to the -Cloud SQL Auth proxy. There are two recommended ways to do this. +Cloud SQL Auth Proxy. There are two recommended ways to do this. ### Workload Identity @@ -131,8 +131,8 @@ bind a [Kubernetes Service Account (KSA)][ksa] to a Google Service Account ### Service account key file Alternatively, if your can't use Workload Identity, the recommended pattern is -to mount a service account key file into the Cloud SQL proxy pod and use the -`-credential_file` flag. +to mount a service account key file into the Cloud SQL Auth Proxy pod and use the +`--credentials-file` flag. 1. Create a credential file for your service account key: ```sh @@ -158,7 +158,7 @@ to mount a service account key file into the Cloud SQL proxy pod and use the [k8s-secret]: https://kubernetes.io/docs/concepts/configuration/secret/ -## Run the Cloud SQL proxy as a sidecar +## Run the Cloud SQL Auth Proxy as a sidecar We recommend running the proxy in a "sidecar" pattern (as an additional container sharing a pod with your application). We recommend this over running @@ -176,13 +176,16 @@ as a separate service for several reasons: accurately scope and request resources to match your applications as it scales -1. Add the Cloud SQL proxy to the pod configuration under `containers`: + +1. Add the Cloud SQL Auth Proxy to the pod configuration under `containers`: > [proxy_with_workload-identity.yaml](proxy_with_workload_identity.yaml#L39-L69) ```yaml + initContainers: - name: cloud-sql-proxy - # It is recommended to use the latest version of the Cloud SQL proxy + restartPolicy: Always + # It is recommended to use the latest version of the Cloud SQL Auth Proxy # Make sure to update on a regular schedule! - image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.0.0.preview.0 # make sure the use the latest version + image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.21.0 # make sure to use the latest version args: # If connecting from a VPC-native GKE cluster, you can use the # following flag to have the proxy connect over private IP @@ -192,7 +195,7 @@ as a separate service for several reasons: - "--port=" - "" securityContext: - # The default Cloud SQL proxy image runs as the + # The default Cloud SQL Auth Proxy image runs as the # "nonroot" user and group (uid: 65532) by default. runAsNonRoot: true # Resource configuration depends on an application's requirements. You @@ -209,15 +212,17 @@ as a separate service for several reasons: # application's requirements. cpu: "1" ``` + + If you are using a service account key, specify your secret volume and add - the `-credential_file` flag to the command: + the `--credentials-file` flag to the command: > [proxy_with_sa_key.yaml](proxy_with_sa_key.yaml#L49-L58) ```yaml # This flag specifies where the service account key can be found - - "-credential_file=/secrets/service_account.json" + - "--credentials-file=/secrets/service_account.json" securityContext: - # The default Cloud SQL proxy image runs as the + # The default Cloud SQL Auth Proxy image runs as the # "nonroot" user and group (uid: 65532) by default. runAsNonRoot: true volumeMounts: @@ -230,7 +235,7 @@ as a separate service for several reasons: `` you specified in the command section. -## Connecting without the Cloud SQL proxy +## Connecting without the Cloud SQL Auth Proxy While not as secure, it is possible to connect from a VPC-native GKE cluster to a Cloud SQL instance on the same VPC using private IP without the proxy. diff --git a/examples/k8s-sidecar/job_with_shutdown_hook.yaml b/examples/k8s-sidecar/job_with_shutdown_hook.yaml new file mode 100644 index 000000000..6adb3d9de --- /dev/null +++ b/examples/k8s-sidecar/job_with_shutdown_hook.yaml @@ -0,0 +1,89 @@ +# Copyright 2023 Google LLC. +# +# 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. + +### +# This demonstrates how to configure a batch job so that it shuts down +# the proxy containers when it has finished processing. +# +# The main job container should send a POST request to the proxy's /quitquitquit +# api when the job process finishes. This will cause the proxy side-car +# container to shut down. +# +# In Kubernetes 1.28, side-car containers will be properly supported, and this +# extra step will become unnecessary. +# +# See https://github.com/kubernetes/enhancements/issues/753 +# and https://github.com/GoogleCloudPlatform/cloud-sql-proxy-operator/issues/381 + +apiVersion: batch/v1 +kind: Job +metadata: + name: job + labels: + app: busybox +spec: + template: + metadata: + creationTimestamp: null + labels: + app: busybox + spec: + containers: + - name: my-application + # Note: This demonstrates a way to run the proxy in an older + # kubernetes cluster that does not support native sidecar containers. + # It is better to run the job as a native sidecar container. + # + # See the Kubernetes documentation: + # https://kubernetes.io/docs/concepts/workloads/pods/sidecar-containers/ + # + # Run your batch job command. + # Then, send a HTTTP POST request to the proxy sidecar container's + # /quitquitquit api. This will cause the proxy process to exit. + command: + - sh + - -c + - > + my_batch_job --host=127.0.0.1 --port= --username= --dbname= + curl http://localhost:9091/quitquitquit + image: busybox + imagePullPolicy: IfNotPresent + resources: {} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - name: cloud-sql-proxy + # It is recommended to use the latest version of the Cloud SQL Auth Proxy + # Make sure to update on a regular schedule! + image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.14.1 + args: + # Enable the admin api server on port 9091 + - "--admin-port=9091" + # Enable the /quitquitquit admin api endpoint + - "--quitquitquit" + + # Tell the proxy to exit gracefully if it receives a SIGTERM + - "--exit-zero-on-sigterm" + + # Replace DB_PORT with the port the proxy should listen on + - "--port=" + - "" + + securityContext: + runAsNonRoot: true + resources: + requests: + memory: "2Gi" + cpu: "1" + restartPolicy: Never + terminationGracePeriodSeconds: 30 diff --git a/examples/k8s-sidecar/job_with_sidecar.yaml b/examples/k8s-sidecar/job_with_sidecar.yaml new file mode 100644 index 000000000..a081a3d2d --- /dev/null +++ b/examples/k8s-sidecar/job_with_sidecar.yaml @@ -0,0 +1,77 @@ +# Copyright 2025 Google LLC. +# +# 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. + +### +# This demonstrates how to configure a batch job so that it shuts down +# the proxy containers when it has finished processing. +# +# This works in Kubernetes 1.29 and higher, demonstrating how to run the proxy +# using a native side-car container. +# +# See https://github.com/kubernetes/enhancements/issues/753 +# and https://github.com/GoogleCloudPlatform/cloud-sql-proxy-operator/issues/381 + +apiVersion: batch/v1 +kind: Job +metadata: + name: job + labels: + app: busybox +spec: + template: + metadata: + creationTimestamp: null + labels: + app: busybox + spec: + containers: + - name: my-application + # Run your batch job command. + # Then, send a HTTTP POST request to the proxy sidecar container's + # /quitquitquit api. This will cause the proxy process to exit. + command: + - my_batch_job + - --host=127.0.0.1 + - --port= + - --username= + - --dbname= + image: my-application-image + imagePullPolicy: IfNotPresent + resources: {} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + restartPolicy: Never + terminationGracePeriodSeconds: 30 + initContainers: + - name: cloud-sql-proxy + restartPolicy: Always + # It is recommended to use the latest version of the Cloud SQL Auth Proxy + # Make sure to update on a regular schedule! + image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.14.3 + args: + # Enable the admin api server on port 9091 + - "--admin-port=9091" + + # Tell the proxy to exit gracefully if it receives a SIGTERM + - "--exit-zero-on-sigterm" + + # Replace DB_PORT with the port the proxy should listen on + - "--port=" + - "" + securityContext: + runAsNonRoot: true + resources: + requests: + memory: "2Gi" + cpu: "1" diff --git a/examples/k8s-sidecar/proxy_with_sa_key.yaml b/examples/k8s-sidecar/proxy_with_sa_key.yaml index d5b9f5341..47351c090 100644 --- a/examples/k8s-sidecar/proxy_with_sa_key.yaml +++ b/examples/k8s-sidecar/proxy_with_sa_key.yaml @@ -26,69 +26,74 @@ spec: app: spec: containers: - - name: - # ... other container configuration - env: - - name: DB_USER - valueFrom: - secretKeyRef: - name: - key: username - - name: DB_PASS - valueFrom: - secretKeyRef: - name: - key: password - - name: DB_NAME - valueFrom: - secretKeyRef: - name: - key: database - - name: cloud-sql-proxy - # It is recommended to use the latest version of the Cloud SQL proxy - # Make sure to update on a regular schedule! - image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.0.0.preview.0 # make sure the use the latest version - args: - # If connecting from a VPC-native GKE cluster, you can use the - # following flag to have the proxy connect over private IP - # - "--private-ip" + - name: + # ... other container configuration + env: + - name: DB_USER + valueFrom: + secretKeyRef: + name: + key: username + - name: DB_PASS + valueFrom: + secretKeyRef: + name: + key: password + - name: DB_NAME + valueFrom: + secretKeyRef: + name: + key: database + initContainers: + - name: cloud-sql-proxy + restartPolicy: Always + # It is recommended to use the latest version of the Cloud SQL Auth Proxy + # Make sure to update on a regular schedule! + image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.14.1 + args: + # If connecting from a VPC-native GKE cluster, you can use the + # following flag to have the proxy connect over private IP + # - "--private-ip" - # Enable structured logging with LogEntry format: - - "--structured-logs" + # If you are not connecting with Automatic IAM AuthN, you can delete + # the following flag. + - "--auto-iam-authn" + # Enable structured logging with LogEntry format: + - "--structured-logs" - # Replace DB_PORT with the port the proxy should listen on - - "--port=" - - "" + # Replace DB_PORT with the port the proxy should listen on + - "--port=" + - "" - # [START cloud_sql_proxy_k8s_volume_mount] - # This flag specifies where the service account key can be found - - "--credentials-file=/secrets/service_account.json" - securityContext: - # The default Cloud SQL proxy image runs as the - # "nonroot" user and group (uid: 65532) by default. - runAsNonRoot: true - volumeMounts: - - name: - mountPath: /secrets/ - readOnly: true - # [END cloud_sql_proxy_k8s_volume_mount] - # Resource configuration depends on an application's requirements. You - # should adjust the following values based on what your application - # needs. For details, see https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - resources: - requests: - # The proxy's memory use scales linearly with the number of active - # connections. Fewer open connections will use less memory. Adjust - # this value based on your application's requirements. - memory: "2Gi" - # The proxy's CPU use scales linearly with the amount of IO between - # the database and the application. Adjust this value based on your - # application's requirements. - cpu: "1" + # [START cloud_sql_proxy_k8s_volume_mount] + # This flag specifies where the service account key can be found + - "--credentials-file=/secrets/service_account.json" + securityContext: + # The default Cloud SQL Auth Proxy image runs as the + # "nonroot" user and group (uid: 65532) by default. + runAsNonRoot: true + volumeMounts: + - name: + mountPath: /secrets/ + readOnly: true + # [END cloud_sql_proxy_k8s_volume_mount] + # Resource configuration depends on an application's requirements. You + # should adjust the following values based on what your application + # needs. For details, see https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + resources: + requests: + # The proxy's memory use scales linearly with the number of active + # connections. Fewer open connections will use less memory. Adjust + # this value based on your application's requirements. + memory: "2Gi" + # The proxy's CPU use scales linearly with the amount of IO between + # the database and the application. Adjust this value based on your + # application's requirements. + cpu: "1" # [START cloud_sql_proxy_k8s_volume_secret] volumes: - - name: - secret: - secretName: + - name: + secret: + secretName: # [END cloud_sql_proxy_k8s_volume_secret] diff --git a/examples/k8s-sidecar/proxy_with_workload_identity.yaml b/examples/k8s-sidecar/proxy_with_workload_identity.yaml index f640915c1..7c68e7e68 100644 --- a/examples/k8s-sidecar/proxy_with_workload_identity.yaml +++ b/examples/k8s-sidecar/proxy_with_workload_identity.yaml @@ -30,59 +30,65 @@ spec: # [END cloud_sql_proxy_k8s_sa] # [START cloud_sql_proxy_k8s_secrets] containers: - - name: - # ... other container configuration - env: - - name: DB_USER - valueFrom: - secretKeyRef: - name: - key: username - - name: DB_PASS - valueFrom: - secretKeyRef: - name: - key: password - - name: DB_NAME - valueFrom: - secretKeyRef: - name: - key: database - # [END cloud_sql_proxy_k8s_secrets] - # [START cloud_sql_proxy_k8s_container] - - name: cloud-sql-proxy - # It is recommended to use the latest version of the Cloud SQL proxy - # Make sure to update on a regular schedule! - image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.0.0.preview.0 # make sure the use the latest version - args: - # If connecting from a VPC-native GKE cluster, you can use the - # following flag to have the proxy connect over private IP - # - "--private-ip" + - name: + # ... other container configuration + env: + - name: DB_USER + valueFrom: + secretKeyRef: + name: + key: username + - name: DB_PASS + valueFrom: + secretKeyRef: + name: + key: password + - name: DB_NAME + valueFrom: + secretKeyRef: + name: + key: database + # [END cloud_sql_proxy_k8s_secrets] + # [START cloud_sql_proxy_k8s_container] + initContainers: + - name: cloud-sql-proxy + restartPolicy: Always + # It is recommended to use the latest version of the Cloud SQL Auth Proxy + # Make sure to update on a regular schedule! + image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.14.1 + args: + # If connecting from a VPC-native GKE cluster, you can use the + # following flag to have the proxy connect over private IP + # - "--private-ip" - # Enable structured logging with LogEntry format: - - "--structured-logs" + # If you are not connecting with Automatic IAM, you can delete + # the following flag. + - "--auto-iam-authn" - # Replace DB_PORT with the port the proxy should listen on - - "--port=" - - "" - - securityContext: - # The default Cloud SQL proxy image runs as the - # "nonroot" user and group (uid: 65532) by default. - runAsNonRoot: true - # You should use resource requests/limits as a best practice to prevent - # pods from consuming too many resources and affecting the execution of - # other pods. You should adjust the following values based on what your - # application needs. For details, see - # https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - resources: - requests: - # The proxy's memory use scales linearly with the number of active - # connections. Fewer open connections will use less memory. Adjust - # this value based on your application's requirements. - memory: "2Gi" - # The proxy's CPU use scales linearly with the amount of IO between - # the database and the application. Adjust this value based on your - # application's requirements. - cpu: "1" + # Enable structured logging with LogEntry format: + - "--structured-logs" + + # Replace DB_PORT with the port the proxy should listen on + - "--port=" + - "" + + securityContext: + # The default Cloud SQL Auth Proxy image runs as the + # "nonroot" user and group (uid: 65532) by default. + runAsNonRoot: true + # You should use resource requests/limits as a best practice to prevent + # pods from consuming too many resources and affecting the execution of + # other pods. You should adjust the following values based on what your + # application needs. For details, see + # https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + resources: + requests: + # The proxy's memory use scales linearly with the number of active + # connections. Fewer open connections will use less memory. Adjust + # this value based on your application's requirements. + memory: "2Gi" + # The proxy's CPU use scales linearly with the amount of IO between + # the database and the application. Adjust this value based on your + # application's requirements. + cpu: "1" # [END cloud_sql_proxy_k8s_container] diff --git a/examples/multi-container/ruby/Dockerfile b/examples/multi-container/ruby/Dockerfile new file mode 100644 index 000000000..a69f8e830 --- /dev/null +++ b/examples/multi-container/ruby/Dockerfile @@ -0,0 +1,28 @@ +# Copyright 2023 Google LLC. +# +# 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. + + +FROM ruby:3.2.2 + +# Copy local code to the container image. +ENV APP_HOME /app +WORKDIR $APP_HOME +COPY . ./ + +# Install production dependencies. +RUN bundle install + +EXPOSE 8080 + +CMD ["bundle", "exec", "rackup", "--host", "0.0.0.0", "-p", "8080"] diff --git a/examples/multi-container/ruby/Gemfile b/examples/multi-container/ruby/Gemfile new file mode 100644 index 000000000..e1ff8e1e9 --- /dev/null +++ b/examples/multi-container/ruby/Gemfile @@ -0,0 +1,21 @@ +# Copyright 2023 Google LLC. +# +# 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. + +source 'https://rubygems.org' + +gem 'json' +gem 'pg' +gem 'sequel' +gem 'sinatra' +gem 'thin' diff --git a/examples/multi-container/ruby/README.md b/examples/multi-container/ruby/README.md new file mode 100644 index 000000000..460fbf066 --- /dev/null +++ b/examples/multi-container/ruby/README.md @@ -0,0 +1,134 @@ +# Cloud SQL Auth Proxy Sidecar + +In the following example, we will deploy the Cloud SQL Proxy as a sidecar to an +existing application which connects to a Cloud SQL instance. + +## Before you begin + +1. If you haven't already, [create a project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#creating_a_project). + +1. [Enable the APIs](https://console.cloud.google.com/flows/enableapi?apiid=run.googleapis.com,sqladmin.googleapis.com,run.googleapis.com) that will be used during this tutorial: + + * Cloud SQL Admin + * Cloud Build + * Cloud Run + +1. Create a Cloud SQL Postgres Instance by following these +[instructions](https://cloud.google.com/sql/docs/postgres/create-instance). +Note the connection string and default password that you create. + +1. Create a database for your application by following these +[instructions](https://cloud.google.com/sql/docs/postgres/create-manage-databases). +Note the database name. + +1. Optionally, create a database user for your instance following these +[instructions](https://cloud.google.com/sql/docs/postgres/create-manage-users). +Otherwise, use the username "postgres". + +## Deploying the Application + +The application you will be deploying should connect to the Cloud SQL Proxy using +TCP mode (for example, using the address "127.0.0.1:5432"). Follow the examples +on the [Connect Auth Proxy documentation](https://cloud.google.com/sql/docs/postgres/connect-auth-proxy#expandable-1) +page to correctly configure your application. + +The connection pool is configured in the following sample: + +```ruby +require 'sinatra' +require 'sequel' + +set :bind, '0.0.0.0' +set :port, 8080 + +# Configure a connection pool that connects to the proxy via TCP +def connect_tcp + Sequel.connect( + adapter: 'postgres', + host: ENV["INSTANCE_HOST"], + port: ENV["DB_PORT"], + database: ENV["DB_NAME"], + user: ENV["DB_USER"], + password: ENV["DB_PASS"], + pool_timeout: 5, + max_connections: 5, + ) +end + +DB = connect_tcp() +``` + +Next, build the container image for the main application and deploy it: + +```bash +gcloud builds submit --tag gcr.io//run-cloudsql +``` + +Finally, update the `multicontainer.yaml` file with the correct values for your +deployment for `YOUR_PROJECT_ID`, `DB_USER`, `DB_PASS`, `DB_NAME`, and `INSTANCE_CONNECTION_NAME` +listing the Cloud SQL container image as a sidecar: + +```yaml +apiVersion: serving.knative.dev/v1 +kind: Service +metadata: + annotations: + run.googleapis.com/launch-stage: ALPHA + name: multicontainer-service +spec: + template: + metadata: + annotations: + run.googleapis.com/execution-environment: gen1 #or gen2 + + spec: + containers: + - name: my-app + image: gcr.io//run-cloudsql + ports: + - containerPort: 8080 + env: + - name: DB_USER + value: + - name: DB_PASS + value: + - name: DB_NAME + value: + - name: INSTANCE_HOST + value: "127.0.0.1" + - name: DB_PORT + value: "5432" + initContainers: + - name: cloud-sql-proxy + restartPolicy: Always + image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:latest + args: + # Ensure the port number on the --port argument matches the value of + # the DB_PORT env var on the my-app container. + - "--port=5432" + - "" + +``` + +You can optionally use Secret Manager to store the database password. See +[this documentation](https://cloud.google.com/run/docs/deploying#yaml) for more details. + +Before deploying, you will need to make sure that the service account associated +with the Cloud Run deployment has the Cloud SQL Client role. +See [this documentation](https://cloud.google.com/sql/docs/postgres/roles-and-permissions) +for more details. The default service account will already have these permissions. + +Finally, you can deploy the service using: + +```bash +gcloud run services replace multicontainer.yaml +``` + +Once the service is deployed, the console should print out a URL. You can test +the service by sending a curl request with your gcloud identity token in the headers: + +```bash +curl -H \ +"Authorization: Bearer $(gcloud auth print-identity-token)" \ + +``` diff --git a/examples/multi-container/ruby/app.rb b/examples/multi-container/ruby/app.rb new file mode 100644 index 000000000..5d7757a9f --- /dev/null +++ b/examples/multi-container/ruby/app.rb @@ -0,0 +1,44 @@ +# Copyright 2023 Google LLC. +# +# 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. + +require 'json' +require 'sequel' +require 'sinatra' + + +set :bind, '0.0.0.0' +set :port, 8080 + +# Configure a connection pool that connects to the proxy via TCP +def connect_tcp + Sequel.connect( + adapter: :postgres, + host: ENV.fetch("INSTANCE_HOST") { "127.0.0.1" }, + port: ENV.fetch("DB_PORT") { 5432 }, + database: ENV["DB_NAME"], + user: ENV["DB_USER"], + password: ENV["DB_PASS"], + pool_timeout: 5, + max_connections: 5, + ) +end + +DB = connect_tcp() + + +get '/' do + content_type :json + # Connect to the database and get the current time + return DB["SELECT NOW()"].all.to_json +end diff --git a/examples/multi-container/ruby/config.ru b/examples/multi-container/ruby/config.ru new file mode 100644 index 000000000..427d34717 --- /dev/null +++ b/examples/multi-container/ruby/config.ru @@ -0,0 +1,17 @@ +# Copyright 2023 Google LLC. +# +# 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. + +require './app' + +run Sinatra::Application diff --git a/examples/multi-container/ruby/multicontainer.yaml b/examples/multi-container/ruby/multicontainer.yaml new file mode 100644 index 000000000..68c2b005e --- /dev/null +++ b/examples/multi-container/ruby/multicontainer.yaml @@ -0,0 +1,58 @@ +# Copyright 2023 Google LLC. +# +# 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. + +apiVersion: serving.knative.dev/v1 +kind: Service +metadata: + annotations: + run.googleapis.com/launch-stage: ALPHA + name: multicontainer-service +spec: + template: + metadata: + annotations: + run.googleapis.com/execution-environment: gen1 #or gen2 + # Uncomment the following line if connecting to Cloud SQL using Private IP + # via a VPC access connector + # run.googleapis.com/vpc-access-connector: + spec: + containers: + - name: my-app + image: gcr.io//run-cloudsql:latest + ports: + - containerPort: 8080 + env: + - name: DB_USER + value: + - name: DB_PASS + value: + - name: DB_NAME + value: + - name: INSTANCE_HOST + value: "127.0.0.1" + - name: DB_PORT + value: "5432" + initContainers: + - name: cloud-sql-proxy + restartPolicy: Always + image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:latest + args: + # If connecting to a Cloud SQL instance within a VPC network, you can use the + # following flag to have the proxy connect over private IP + # - "--private-ip" + + # Ensure the port number on the --port argument matches the value of the DB_PORT env var on the my-app container. + - "--port=5432" + # instance connection name takes format "PROJECT:REGION:INSTANCE_NAME" + - "" diff --git a/go.mod b/go.mod index 7505f1cd1..9755231cf 100644 --- a/go.mod +++ b/go.mod @@ -1,83 +1,88 @@ module github.com/GoogleCloudPlatform/cloud-sql-proxy/v2 -go 1.19 +go 1.24.0 require ( - cloud.google.com/go/cloudsqlconn v1.0.1 + cloud.google.com/go/cloudsqlconn v1.20.0 contrib.go.opencensus.io/exporter/prometheus v0.4.2 contrib.go.opencensus.io/exporter/stackdriver v0.13.14 - github.com/go-sql-driver/mysql v1.6.0 - github.com/google/go-cmp v0.5.9 - github.com/hanwen/go-fuse/v2 v2.1.0 - github.com/jackc/pgx/v4 v4.17.2 - github.com/microsoft/go-mssqldb v0.17.0 - github.com/spf13/cobra v1.6.1 - github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.14.0 + github.com/coreos/go-systemd/v22 v22.6.0 + github.com/go-sql-driver/mysql v1.9.3 + github.com/google/go-cmp v0.7.0 + github.com/hanwen/go-fuse/v2 v2.9.0 + github.com/jackc/pgx/v5 v5.8.0 + github.com/microsoft/go-mssqldb v1.9.5 + github.com/spf13/cobra v1.10.2 + github.com/spf13/pflag v1.0.10 + github.com/spf13/viper v1.21.0 go.opencensus.io v0.24.0 - go.uber.org/zap v1.23.0 - golang.org/x/oauth2 v0.2.0 - golang.org/x/sys v0.2.0 - google.golang.org/api v0.103.0 + golang.org/x/oauth2 v0.34.0 + golang.org/x/sys v0.40.0 + google.golang.org/api v0.260.0 + gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) require ( - cloud.google.com/go/compute v1.12.1 // indirect - cloud.google.com/go/compute/metadata v0.2.1 // indirect - cloud.google.com/go/monitoring v1.7.0 // indirect - cloud.google.com/go/trace v1.3.0 // indirect - github.com/aws/aws-sdk-go v1.43.31 // indirect + cloud.google.com/go/auth v0.18.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect + cloud.google.com/go/monitoring v1.24.3 // indirect + cloud.google.com/go/trace v1.11.7 // indirect + filippo.io/edwards25519 v1.1.0 // indirect + github.com/aws/aws-sdk-go v1.55.8 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-kit/log v0.2.1 // indirect - github.com/go-logfmt/logfmt v0.5.1 // indirect + github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect - github.com/googleapis/gax-go/v2 v2.7.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/inconshreveable/mousetrap v1.0.1 // indirect - github.com/jackc/chunkreader/v2 v2.0.1 // indirect - github.com/jackc/pgconn v1.13.0 // indirect - github.com/jackc/pgio v1.0.0 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/s2a-go v0.1.9 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.9 // indirect + github.com/googleapis/gax-go/v2 v2.16.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgproto3/v2 v2.3.1 // indirect - github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect - github.com/jackc/pgtype v1.12.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/lib/pq v1.10.6 // indirect - github.com/magiconair/properties v1.8.6 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.5 // indirect - github.com/prometheus/client_golang v1.13.0 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/prometheus/prometheus v0.35.0 // indirect - github.com/prometheus/statsd_exporter v0.22.7 // indirect - github.com/spf13/afero v1.9.2 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/subosito/gotenv v1.4.1 // indirect - go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.8.0 // indirect - golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be // indirect - golang.org/x/net v0.2.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/text v0.4.0 // indirect - golang.org/x/time v0.1.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c // indirect - google.golang.org/grpc v1.50.1 // indirect - google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/prometheus/client_golang v1.23.2 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.67.4 // indirect + github.com/prometheus/procfs v0.19.2 // indirect + github.com/prometheus/prometheus v0.308.0 // indirect + github.com/prometheus/statsd_exporter v0.28.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/sagikazarmark/locafero v0.12.0 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect + go.opentelemetry.io/otel v1.39.0 // indirect + go.opentelemetry.io/otel/metric v1.39.0 // indirect + go.opentelemetry.io/otel/trace v1.39.0 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/crypto v0.47.0 // indirect + golang.org/x/net v0.49.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/text v0.33.0 // indirect + golang.org/x/time v0.14.0 // indirect + google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect + google.golang.org/grpc v1.78.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index ec985c10f..b30faac3d 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,8 @@ -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= -bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -16,742 +13,142 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= -cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= -cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y= -cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= -cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= -cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= -cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= -cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= -cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= -cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= -cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= -cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= -cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= -cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= -cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= -cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= -cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= -cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= -cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= -cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= -cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= -cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= -cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= -cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= -cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= -cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= -cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= -cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= -cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= -cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= -cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= -cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= +cloud.google.com/go/auth v0.18.0 h1:wnqy5hrv7p3k7cShwAU/Br3nzod7fxoqG+k0VZ+/Pk0= +cloud.google.com/go/auth v0.18.0/go.mod h1:wwkPM1AgE1f2u6dG443MiWoD8C3BtOywNsUMcUTVDRo= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= -cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= -cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= -cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= -cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= -cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= -cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= -cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= -cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= -cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= -cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= -cloud.google.com/go/cloudsqlconn v1.0.1 h1:KiIdyshUgABAAKmKqNNgkCVe/zLxND4QvtYGv1dWZsU= -cloud.google.com/go/cloudsqlconn v1.0.1/go.mod h1:4pm7AbCQz9epIWz4l3C444atSU3TMG71JJOxHngvpPs= -cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= -cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= -cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= -cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.12.1 h1:gKVJMEyqV5c/UnpzjjQbo3Rjvvqpr9B1DFSbJC4OXr0= -cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute/metadata v0.2.1 h1:efOwf5ymceDhK6PKMnnrTHP4pppY5L22mle96M1yP48= -cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= -cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= -cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= -cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= -cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= -cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= -cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= -cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= -cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= -cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= -cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= -cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= -cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= -cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= -cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= -cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= -cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= -cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= -cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= -cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +cloud.google.com/go/cloudsqlconn v1.20.0 h1:5EBr98dktt5QStX6jacFTECTQ4rxfY6qpIUIV9YNRqo= +cloud.google.com/go/cloudsqlconn v1.20.0/go.mod h1:YCoWR0SWYTDf9npeqq8ODFN1WdGMGVC5G74+A3CXXP4= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= -cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= -cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= -cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= -cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= -cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= -cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= -cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= -cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= -cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= -cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= -cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= -cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= -cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= -cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= -cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= -cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= -cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= -cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= -cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= -cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= -cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= -cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= -cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= -cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= -cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= -cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= -cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= -cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= -cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= -cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= -cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= -cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= -cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= -cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= -cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= -cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= -cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= -cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= -cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= -cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= -cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= -cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= -cloud.google.com/go/longrunning v0.1.1 h1:y50CXG4j0+qvEukslYFBCrzaXX0qpFbBzc3PchSu/LE= -cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= -cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= -cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= -cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= -cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= -cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= -cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= -cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= -cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= -cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= -cloud.google.com/go/monitoring v1.7.0 h1:zK12aN/jzLRzrRXopEOUaG6kvuF6WJmlv1mXn1L7a6E= -cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= -cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= -cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= -cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= -cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= -cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= -cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= -cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= -cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= -cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= -cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= -cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= -cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= -cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= -cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= -cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= -cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= -cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= -cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= -cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= -cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= -cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= -cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= -cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +cloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE= +cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= -cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= -cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= -cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= -cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= -cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= -cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= -cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= -cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= -cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= -cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= -cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= -cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= -cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= -cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= -cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= -cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= -cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= -cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= -cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= -cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= -cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= -cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= -cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= -cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= -cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= -cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= -cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= -cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= -cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= -cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= -cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= -cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= -cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= -cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= -cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= -cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= -cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= -cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= -cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= -cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= -cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= -cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= -cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= -cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= -cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= -cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= -cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= -cloud.google.com/go/trace v1.3.0 h1:YmCusGVDe0scOkHKi+y/GgBZlwQjHybtQHV12AfJEgo= -cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= -cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= -cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= -cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= -cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= -cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= -cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= -cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= -cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= -cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= -cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= -cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= -cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= -cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= -cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= -cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= -cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= -cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= -cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= +cloud.google.com/go/trace v1.11.7 h1:kDNDX8JkaAG3R2nq1lIdkb7FCSi1rCmsEtKVsty7p+U= +cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s= contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg= contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ= contrib.go.opencensus.io/exporter/stackdriver v0.13.14 h1:zBakwHardp9Jcb8sQHcHpXy/0+JIb1M8KjigCJzx7+4= contrib.go.opencensus.io/exporter/stackdriver v0.13.14/go.mod h1:5pSSGY0Bhuk7waTHuDf4aQ8D2DrhgETRo9fy6k3Xlzc= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= -github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v63.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0/go.mod h1:+6sju8gk8FRmSajX3Oz4G5Gm7P+mbqE9FVaXXFYTkCM= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest v0.11.25/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= -github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= -github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 h1:5YTBM8QDVIBN3sxBil89WfdAAqDZbyJTgh688DSxX5w= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0 h1:wL5IEG5zb7BVv1Kv0Xm92orq+5hB5Nipn3B5tn4Rqfk= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0/go.mod h1:J7MUC/wtRpfGVbQ5sIItY5/FuVWmvzlY21WAOfQnq/I= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 h1:Wgf5rZba3YZqeTNJPtvqZoBu1sBN/L4sry+u2U3Y75w= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1/go.mod h1:xxCBG/f/4Vbmh2XQJBsOmNdxWUY5j/s27jujKPbQf14= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww= +github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 h1:XkkQbfMyuH2jTSjQjSoihryI8GINRcs4xp8lNawg0FI= +github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= -github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= -github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= -github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= -github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= -github.com/Microsoft/hcsshim v0.8.20/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= -github.com/Microsoft/hcsshim v0.9.2/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= -github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= -github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= -github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.3/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= -github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.43.11/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go v1.43.31 h1:yJZIr8nMV1hXjAvvOLUFqZRJcHV7udPQBfhJqawDzI0= -github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ= +github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0 h1:t/LhUZLVitR1Ow2YOnduCsavhwFUklBMoGVYUCqmCqk= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= -github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= -github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= -github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= -github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= -github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= -github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= -github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= -github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= -github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= -github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= -github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= -github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= -github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8= -github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= -github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= -github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= -github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= -github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= -github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= -github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= -github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= -github.com/containerd/containerd v1.6.1/go.mod h1:1nJz5xCZPusx6jJU8Frfct988y0NpumIq9ODB0kLtoE= -github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= -github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= -github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= -github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= -github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk= -github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= -github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= -github.com/containerd/go-cni v1.1.0/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-cni v1.1.3/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= -github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= -github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= -github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= -github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= -github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue1wb8bP7PQu4= -github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= -github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= -github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= -github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= -github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= -github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= -github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= -github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= -github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= -github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= -github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v1.0.1/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y= -github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= -github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= -github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE= -github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= -github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= -github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= -github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= -github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= -github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= +github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0= +github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4= +github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo= +github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= +github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA= -github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= -github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dgryski/go-sip13 v0.0.0-20200911182023-62edffca9245/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/digitalocean/godo v1.78.0/go.mod h1:GBmu8MkjZmNARE7IXRPmkbbnocNN8+uBm0xbEVw2LCs= -github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= -github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= -github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= -github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.14+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= -github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329 h1:K+fnvUM0VZ7ZFJf0n4L/BRlnsb9pL/GuDG6FqaH+PwM= +github.com/envoyproxy/go-control-plane/envoy v1.35.0 h1:ixjkELDE+ru6idPxcHLj8LBVc2bFP7iBytj353BoHUo= +github.com/envoyproxy/go-control-plane/envoy v1.35.0/go.mod h1:09qwbGVuSWWAyN5t/b3iyVfz5+z8QWGrzkoqm/8SbEs= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= -github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= -github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= -github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= -github.com/go-openapi/runtime v0.23.1/go.mod h1:AKurw9fNre+h3ELZfk6ILsfvPN+bvvlaU/M9q/r9hpk= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= -github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= -github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= -github.com/go-openapi/strfmt v0.21.2/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/validate v0.21.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= +github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= -github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= -github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= -github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= -github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= -github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= -github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= -github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= -github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= -github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= -github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= -github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= -github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= -github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= -github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= -github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= -github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA= -github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= -github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= -github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -759,8 +156,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -776,15 +171,11 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -792,24 +183,15 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -817,714 +199,216 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20220318212150-b2ab0324ddda/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.2.0 h1:y8Yozv7SZtlU//QXbezB6QkpuE6jMD2/gfzk4AftXjs= -github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.9 h1:TOpi/QG8iDcZlkQlGlFUti/ZtyLkliXvHDcyUIMuFrU= +github.com/googleapis/enterprise-certificate-proxy v0.3.9/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= -github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= -github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gophercloud/gophercloud v0.24.0/go.mod h1:Q8fZtyi5zZxPS/j9aj3sSxtvj41AdQMDwyo1myduD5c= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grafana/regexp v0.0.0-20220304095617-2e8d9baf4ac2/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok= -github.com/hanwen/go-fuse/v2 v2.1.0 h1:+32ffteETaLYClUj0a3aHjZ1hOPxxaNEHiZiujuDaek= -github.com/hanwen/go-fuse/v2 v2.1.0/go.mod h1:oRyA5eK+pvJyv5otpO/DgccS8y/RvYMaO00GgRLGryc= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= -github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.12.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.2.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/googleapis/gax-go/v2 v2.16.0 h1:iHbQmKLLZrexmb0OSsNGTeSTS0HO4YvFOG8g5E4Zd0Y= +github.com/googleapis/gax-go/v2 v2.16.0/go.mod h1:o1vfQjjNZn4+dPnRdl/4ZD7S9414Y4xA+a/6Icj6l14= +github.com/hanwen/go-fuse/v2 v2.9.0 h1:0AOGUkHtbOVeyGLr0tXupiid1Vg7QB7M6YUcdmVdC58= +github.com/hanwen/go-fuse/v2 v2.9.0/go.mod h1:yE6D2PqWwm3CbYRxFXV9xUd8Md5d6NG0WBs5spCswmI= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/memberlist v0.3.1/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= -github.com/hetznercloud/hcloud-go v1.33.1/go.mod h1:XX/TQub3ge0yWR2yHWmnDVIrB+MQbda1pHxkUmDlUME= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ= -github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= -github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw= -github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= -github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= -github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= -github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= -github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= -github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= -github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys= -github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI= +github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= +github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= -github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= -github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= -github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= -github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= -github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y= -github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= -github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= -github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= -github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w= -github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= -github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= -github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= -github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= -github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E= -github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw= -github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= -github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= +github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgtype v1.14.4 h1:fKuNiCumbKTAIxQwXfB/nsrnkEI6bPJrrSiMKgbJ2j8= +github.com/jackc/pgtype v1.14.4/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA= +github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= +github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= +github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo= +github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= -github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= +github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= +github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= -github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/linode/linodego v1.4.0/go.mod h1:PVsRxSlOiJyvG4/scTszpmZDTdgS+to3X6eS8pRrWI8= -github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= -github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= -github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE= -github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.48/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= -github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= -github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= -github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= -github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= -github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= +github.com/microsoft/go-mssqldb v1.9.5 h1:orwya0X/5bsL1o+KasupTkk2eNTNFkTQG0BEe/HxCn0= +github.com/microsoft/go-mssqldb v1.9.5/go.mod h1:VCP2a0KEZZtGLRHd1PsLavLFYy/3xX2yJUPycv3Sr2Q= +github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= +github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= -github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= -github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= -github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= -github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= -github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= -github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/prometheus/alertmanager v0.24.0/go.mod h1:r6fy/D7FRuZh5YbnX6J3MBY0eI4Pb5yPYS7/bPSXXqI= -github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.34.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common/assets v0.1.0/go.mod h1:D17UVUE12bHbim7HzwUvtqm6gwBEaDQ0F+hIGbFbccI= -github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= -github.com/prometheus/exporter-toolkit v0.7.1/go.mod h1:ZUBIj498ePooX9t/2xtDjeQYwvRpiPP2lh5u4iblj2g= -github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc= +github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/prometheus v0.35.0 h1:N93oX6BrJ2iP3UuE2Uz4Lt+5BkUpaFer3L9CbADzesc= -github.com/prometheus/prometheus v0.35.0/go.mod h1:7HaLx5kEPKJ0GDgbODG0fZgXbQ8K/XjZNJXQmbmgQlY= -github.com/prometheus/statsd_exporter v0.22.7 h1:7Pji/i2GuhK6Lu7DHrtTkFmNBCudCPT1pX2CziuyQR0= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= +github.com/prometheus/prometheus v0.308.0 h1:kVh/5m1n6m4cSK9HYTDEbMxzuzCWyEdPdKSxFRxXj04= +github.com/prometheus/prometheus v0.308.0/go.mod h1:xXYKzScyqyFHihpS0UsXpC2F3RA/CygOs7wb4mpdusE= github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/prometheus/statsd_exporter v0.28.0 h1:S3ZLyLm/hOKHYZFOF0h4zYmd0EeKyPF9R1pFBYXUgYY= +github.com/prometheus/statsd_exporter v0.28.0/go.mod h1:Lq41vNkMLfiPANmI+uHb5/rpFFUTxPXiiNpmsAYLvDI= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= -github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= -github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= +github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU= -github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= -github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= -github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= -github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= -go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= -go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= -go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= -go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= -go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= -go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= -go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.31.0/go.mod h1:PFmBsWbldL1kiWZk9+0LBZz2brhByaGsvp6pRICMlPE= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= -go.opentelemetry.io/otel v1.6.0/go.mod h1:bfJD2DZVw0LBxghOTlgnlI0CV3hLDu9XF/QKOUXMTQQ= -go.opentelemetry.io/otel v1.6.1/go.mod h1:blzUabWHkX6LJewxvadmzafgh/wnvBSDBdOuwkAtrWQ= -go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.6.1/go.mod h1:NEu79Xo32iVb+0gVNV8PMd7GoWqnyDXRlj04yFjqz40= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.6.1/go.mod h1:YJ/JbY5ag/tSQFXzH3mtDmHqzF3aFn3DI/aB1n7pt4w= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.6.1/go.mod h1:UJJXJj0rltNIemDMwkOJyggsvyMG9QHfJeFH0HS5JjM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.6.1/go.mod h1:DAKwdo06hFLc0U88O10x4xnb5sc7dDRDqRuiN+io8JE= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/metric v0.28.0/go.mod h1:TrzsfQAmQaB1PDcdhBauLMk7nyyg9hm+GoQq/ekE9Iw= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= -go.opentelemetry.io/otel/sdk v1.6.1/go.mod h1:IVYrddmFZ+eJqu2k38qD3WezFR2pymCzm8tdxyh3R4E= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= -go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= -go.opentelemetry.io/otel/trace v1.6.0/go.mod h1:qs7BrU5cZ8dXQHBGxHMOxwME/27YH2qEp4/+tZLLwJE= -go.opentelemetry.io/otel/trace v1.6.1/go.mod h1:RkFRM1m0puWIq10oxImnGEduNBzxiN7TXluRBtE+5j0= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= -go.opentelemetry.io/proto/otlp v0.12.1/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= -go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= -golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 h1:RN3ifU8y4prNWeEnQp2kRRHz8UwonAEYZl8tUzHEXAk= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0/go.mod h1:habDz3tEWiFANTo6oUE99EmaFUrCNYAAg3wiVmusm70= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A= -golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1547,8 +431,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -1557,38 +439,20 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1599,147 +463,57 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.2.0 h1:GtQkldQ9m7yvzCL1V+LrYow3Khe0eJH0w7RbX/VbaIU= -golang.org/x/oauth2 v0.2.0/go.mod h1:Cwn6afJ8jrQwYMxQDTpISoXmXW9I6qF6vDeuuoX3Ibs= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1747,147 +521,53 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1895,7 +575,6 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1908,46 +587,18 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1964,57 +615,19 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= -google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= -google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= -google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= -google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.101.0/go.mod h1:CjxAAWWt3A3VrUE2IGDY2bgK5qhoG/OkyWVlYcP05MY= -google.golang.org/api v0.103.0 h1:9yuVqlu2JCvcLg9p8S3fcFLZij8EPSyvODIY1rkMizQ= -google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= +google.golang.org/api v0.260.0 h1:XbNi5E6bOVEj/uLXQRlt6TKuEzMD7zvW/6tNwltE4P4= +google.golang.org/api v0.260.0/go.mod h1:Shj1j0Phr/9sloYrKomICzdYgsSDImpTxME8rGLaZ/o= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= @@ -2023,7 +636,6 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -2032,104 +644,24 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= -google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= -google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= -google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= -google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221018160656-63c7b68cfc55/go.mod h1:45EK0dUbEZ2NHjCeAd2LXmyjAgGUGrpGROgjhC3ADck= -google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c h1:QgY/XxIAIeccR+Ca/rDdKubLIU9rcJ3xfy1DC/Wd2Oo= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= -google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217 h1:GvESR9BIyHUahIb0NcTum6itIWtdoglGX+rnGxm2934= +google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:yJ2HH4EHEDTd3JiLmhds6NkJ17ITVYOdV3m3VKOnws0= +google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls= +google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -2138,33 +670,9 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -2177,39 +685,21 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/telebot.v3 v3.0.0/go.mod h1:7rExV8/0mDDNu9epSrDm/8j22KLaActH1Tbee6YjzWg= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -2217,14 +707,8 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -2232,69 +716,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= -k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= -k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= -k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8= -k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= -k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= -k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= -k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= -k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= -k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= -k8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ= -k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= -k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= -k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y= -k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4= -k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= -k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= -k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= -k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= -k8s.io/component-base v0.22.5/go.mod h1:VK3I+TjuF9eaa+Ln67dKxhGar5ynVbwnGrUiNF4MqCI= -k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= -k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= -k8s.io/cri-api v0.23.1/go.mod h1:REJE3PSU0h/LOV1APBrupxrEJqnoxZC8KWzkBUHwrK4= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/klog/v2 v2.40.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= -k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= -k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/internal/healthcheck/healthcheck.go b/internal/healthcheck/healthcheck.go index 1491d150c..3c95d5409 100644 --- a/internal/healthcheck/healthcheck.go +++ b/internal/healthcheck/healthcheck.go @@ -12,15 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package healthcheck tests and communicates the health of the Cloud SQL Auth proxy. +// Package healthcheck tests and communicates the health of the Cloud SQL Auth Proxy. package healthcheck import ( - "context" "errors" "fmt" "net/http" - "strconv" "sync" "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/cloudsql" @@ -30,25 +28,34 @@ import ( // Check provides HTTP handlers for use as healthchecks typically in a // Kubernetes context. type Check struct { - once *sync.Once - started chan struct{} - proxy *proxy.Client - logger cloudsql.Logger + startedOnce *sync.Once + started chan struct{} + stoppedOnce *sync.Once + stopped chan struct{} + proxy *proxy.Client + logger cloudsql.Logger } // NewCheck is the initializer for Check. func NewCheck(p *proxy.Client, l cloudsql.Logger) *Check { return &Check{ - once: &sync.Once{}, - started: make(chan struct{}), - proxy: p, - logger: l, + startedOnce: &sync.Once{}, + started: make(chan struct{}), + stoppedOnce: &sync.Once{}, + stopped: make(chan struct{}), + proxy: p, + logger: l, } } // NotifyStarted notifies the check that the proxy has started up successfully. func (c *Check) NotifyStarted() { - c.once.Do(func() { close(c.started) }) + c.startedOnce.Do(func() { close(c.started) }) +} + +// NotifyStopped notifies the check that the proxy has started up successfully. +func (c *Check) NotifyStopped() { + c.stoppedOnce.Do(func() { close(c.stopped) }) } // HandleStartup reports whether the Check has been notified of startup. @@ -63,15 +70,15 @@ func (c *Check) HandleStartup(w http.ResponseWriter, _ *http.Request) { } } -var errNotStarted = errors.New("proxy is not started") +var ( + errNotStarted = errors.New("proxy is not started") + errStopped = errors.New("proxy has stopped") +) // HandleReadiness ensures the Check has been notified of successful startup, -// that the proxy has not reached maximum connections, and that all connections -// are healthy. -func (c *Check) HandleReadiness(w http.ResponseWriter, req *http.Request) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - +// that the proxy has not reached maximum connections, and that the Proxy has +// not started shutting down. +func (c *Check) HandleReadiness(w http.ResponseWriter, _ *http.Request) { select { case <-c.started: default: @@ -81,68 +88,17 @@ func (c *Check) HandleReadiness(w http.ResponseWriter, req *http.Request) { return } - if open, max := c.proxy.ConnCount(); max > 0 && open == max { - err := fmt.Errorf("max connections reached (open = %v, max = %v)", open, max) - c.logger.Errorf("[Health Check] Readiness failed: %v", err) + select { + case <-c.stopped: + c.logger.Errorf("[Health Check] Readiness failed: %v", errStopped) w.WriteHeader(http.StatusServiceUnavailable) - w.Write([]byte(err.Error())) + w.Write([]byte(errStopped.Error())) return + default: } - var minReady *int - q := req.URL.Query() - if v := q.Get("min-ready"); v != "" { - n, err := strconv.Atoi(v) - if err != nil { - c.logger.Errorf("[Health Check] min-ready must be a valid integer, got = %q", v) - w.WriteHeader(http.StatusBadRequest) - fmt.Fprintf(w, "min-query must be a valid integer, got = %q", v) - return - } - if n <= 0 { - c.logger.Errorf("[Health Check] min-ready %q must be greater than zero", v) - w.WriteHeader(http.StatusBadRequest) - fmt.Fprint(w, "min-query must be greater than zero", v) - return - } - minReady = &n - } - - total, err := c.proxy.CheckConnections(ctx) - - switch { - case minReady != nil && *minReady > total: - // When min ready is set and exceeds total instances, 400 status. - mErr := fmt.Errorf( - "min-ready (%v) must be less than or equal to the number of registered instances (%v)", - *minReady, total, - ) - c.logger.Errorf("[Health Check] Readiness failed: %v", mErr) - - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(mErr.Error())) - return - case err != nil && minReady != nil: - // When there's an error and min ready is set, AND min ready instances - // are not ready, 503 status. - c.logger.Errorf("[Health Check] Readiness failed: %v", err) - - mErr, ok := err.(proxy.MultiErr) - if !ok { - // If the err is not a MultiErr, just return it as is. - w.WriteHeader(http.StatusServiceUnavailable) - w.Write([]byte(err.Error())) - return - } - - areReady := total - len(mErr) - if areReady < *minReady { - w.WriteHeader(http.StatusServiceUnavailable) - w.Write([]byte(err.Error())) - return - } - case err != nil: - // When there's just an error without min-ready: 503 status. + if open, maxCount := c.proxy.ConnCount(); maxCount > 0 && open == maxCount { + err := fmt.Errorf("max connections reached (open = %v, max = %v)", open, maxCount) c.logger.Errorf("[Health Check] Readiness failed: %v", err) w.WriteHeader(http.StatusServiceUnavailable) w.Write([]byte(err.Error())) diff --git a/internal/healthcheck/healthcheck_test.go b/internal/healthcheck/healthcheck_test.go index e0ebdd50e..bb86b72ea 100644 --- a/internal/healthcheck/healthcheck_test.go +++ b/internal/healthcheck/healthcheck_test.go @@ -16,7 +16,6 @@ package healthcheck_test import ( "context" - "errors" "fmt" "io" "net" @@ -25,7 +24,6 @@ import ( "net/url" "os" "strings" - "sync/atomic" "testing" "time" @@ -73,29 +71,6 @@ func (*fakeDialer) Close() error { return nil } -type flakeyDialer struct { - dialCount uint64 - fakeDialer -} - -// Dial fails on odd calls and succeeds on even calls. -func (f *flakeyDialer) Dial(_ context.Context, _ string, _ ...cloudsqlconn.DialOption) (net.Conn, error) { - c := atomic.AddUint64(&f.dialCount, 1) - if c%2 == 0 { - conn, _ := net.Pipe() - return conn, nil - } - return nil, errors.New("flakey dialer fails on odd calls") -} - -type errorDialer struct { - fakeDialer -} - -func (*errorDialer) Dial(_ context.Context, _ string, _ ...cloudsqlconn.DialOption) (net.Conn, error) { - return nil, errors.New("errorDialer always errors") -} - func newProxyWithParams(t *testing.T, maxConns uint64, dialer cloudsql.Dialer, instances []proxy.InstanceConnConfig) *proxy.Client { c := &proxy.Config{ Addr: proxyHost, @@ -103,7 +78,7 @@ func newProxyWithParams(t *testing.T, maxConns uint64, dialer cloudsql.Dialer, i Instances: instances, MaxConnections: maxConns, } - p, err := proxy.NewClient(context.Background(), dialer, logger, c) + p, err := proxy.NewClient(context.Background(), dialer, logger, c, nil) if err != nil { t.Fatalf("proxy.NewClient: %v", err) } @@ -116,10 +91,6 @@ func newTestProxyWithMaxConns(t *testing.T, maxConns uint64) *proxy.Client { }) } -func newTestProxyWithDialer(t *testing.T, d cloudsql.Dialer) *proxy.Client { - return newProxyWithParams(t, 0, d, []proxy.InstanceConnConfig{{Name: "proj:region:pg"}}) -} - func newTestProxy(t *testing.T) *proxy.Client { return newProxyWithParams(t, 0, &fakeDialer{}, []proxy.InstanceConnConfig{{Name: "proj:region:pg"}}) } @@ -182,6 +153,27 @@ func TestHandleReadinessWhenNotNotified(t *testing.T) { } } +func TestHandleReadinessWhenStopped(t *testing.T) { + p := newTestProxy(t) + defer func() { + if err := p.Close(); err != nil { + t.Logf("failed to close proxy client: %v", err) + } + }() + check := healthcheck.NewCheck(p, logger) + + check.NotifyStarted() // The Proxy has started. + check.NotifyStopped() // And now the Proxy is shutting down. + + rec := httptest.NewRecorder() + check.HandleReadiness(rec, &http.Request{URL: &url.URL{}}) + + resp := rec.Result() + if got, want := resp.StatusCode, http.StatusServiceUnavailable; got != want { + t.Fatalf("want = %v, got = %v", want, got) + } +} + func TestHandleReadinessForMaxConns(t *testing.T) { p := newTestProxyWithMaxConns(t, 1) defer func() { @@ -230,112 +222,3 @@ func TestHandleReadinessForMaxConns(t *testing.T) { t.Fatalf("want max connections error, got = %v", string(body)) } } - -func TestHandleReadinessWithConnectionProblems(t *testing.T) { - p := newTestProxyWithDialer(t, &errorDialer{}) // error dialer will error on dial - defer func() { - if err := p.Close(); err != nil { - t.Logf("failed to close proxy client: %v", err) - } - }() - check := healthcheck.NewCheck(p, logger) - check.NotifyStarted() - - rec := httptest.NewRecorder() - check.HandleReadiness(rec, &http.Request{URL: &url.URL{}}) - - resp := rec.Result() - if got, want := resp.StatusCode, http.StatusServiceUnavailable; got != want { - t.Fatalf("want = %v, got = %v", want, got) - } - - body, err := io.ReadAll(resp.Body) - if err != nil { - t.Fatalf("failed to read response body: %v", err) - } - if want := "errorDialer"; !strings.Contains(string(body), want) { - t.Fatalf("want substring with = %q, got = %v", want, string(body)) - } -} - -func TestReadinessWithMinReady(t *testing.T) { - tcs := []struct { - desc string - minReady string - wantStatus int - dialer cloudsql.Dialer - }{ - { - desc: "when min ready is zero", - minReady: "0", - wantStatus: http.StatusBadRequest, - dialer: &fakeDialer{}, - }, - { - desc: "when min ready is less than zero", - minReady: "-1", - wantStatus: http.StatusBadRequest, - dialer: &fakeDialer{}, - }, - { - desc: "when only one instance must be ready", - minReady: "1", - wantStatus: http.StatusOK, - dialer: &flakeyDialer{}, // fails on first call, succeeds on second - }, - { - desc: "when all instances must be ready", - minReady: "2", - wantStatus: http.StatusServiceUnavailable, - dialer: &errorDialer{}, - }, - { - desc: "when min ready is greater than the number of instances", - minReady: "3", - wantStatus: http.StatusBadRequest, - dialer: &fakeDialer{}, - }, - { - desc: "when min ready is bogus", - minReady: "bogus", - wantStatus: http.StatusBadRequest, - dialer: &fakeDialer{}, - }, - { - desc: "when min ready is not set", - minReady: "", - wantStatus: http.StatusOK, - dialer: &fakeDialer{}, - }, - } - for _, tc := range tcs { - t.Run(tc.desc, func(t *testing.T) { - p := newProxyWithParams(t, 0, - tc.dialer, - []proxy.InstanceConnConfig{ - {Name: "p:r:instance-1"}, - {Name: "p:r:instance-2"}, - }, - ) - defer func() { - if err := p.Close(); err != nil { - t.Logf("failed to close proxy client: %v", err) - } - }() - - check := healthcheck.NewCheck(p, logger) - check.NotifyStarted() - u, err := url.Parse(fmt.Sprintf("/readiness?min-ready=%s", tc.minReady)) - if err != nil { - t.Fatal(err) - } - rec := httptest.NewRecorder() - check.HandleReadiness(rec, &http.Request{URL: u}) - - resp := rec.Result() - if got, want := resp.StatusCode, tc.wantStatus; got != want { - t.Fatalf("want = %v, got = %v", want, got) - } - }) - } -} diff --git a/internal/log/log.go b/internal/log/log.go index c44306a58..b88a968ca 100644 --- a/internal/log/log.go +++ b/internal/log/log.go @@ -15,34 +15,35 @@ package log import ( + "fmt" "io" llog "log" + "log/slog" "os" + "time" "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/cloudsql" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" ) // StdLogger is the standard logger that distinguishes between info and error // logs. type StdLogger struct { - infoLog *llog.Logger - errLog *llog.Logger + stdLog *llog.Logger + errLog *llog.Logger } // NewStdLogger create a Logger that uses out and err for informational and // error messages. func NewStdLogger(out, err io.Writer) cloudsql.Logger { return &StdLogger{ - infoLog: llog.New(out, "", llog.LstdFlags), - errLog: llog.New(err, "", llog.LstdFlags), + stdLog: llog.New(out, "", llog.LstdFlags), + errLog: llog.New(err, "", llog.LstdFlags), } } // Infof logs informational messages func (l *StdLogger) Infof(format string, v ...interface{}) { - l.infoLog.Printf(format, v...) + l.stdLog.Printf(format, v...) } // Errorf logs error messages @@ -52,53 +53,72 @@ func (l *StdLogger) Errorf(format string, v ...interface{}) { // Debugf logs debug messages func (l *StdLogger) Debugf(format string, v ...interface{}) { - l.infoLog.Printf(format, v...) + l.stdLog.Printf(format, v...) } // StructuredLogger writes log messages in JSON. type StructuredLogger struct { - logger *zap.SugaredLogger + stdLog *slog.Logger + errLog *slog.Logger } // Infof logs informational messages func (l *StructuredLogger) Infof(format string, v ...interface{}) { - l.logger.Infof(format, v...) + l.stdLog.Info(fmt.Sprintf(format, v...)) } // Errorf logs error messages func (l *StructuredLogger) Errorf(format string, v ...interface{}) { - l.logger.Errorf(format, v...) + l.errLog.Error(fmt.Sprintf(format, v...)) } // Debugf logs debug messages func (l *StructuredLogger) Debugf(format string, v ...interface{}) { - l.logger.Infof(format, v...) + l.stdLog.Debug(fmt.Sprintf(format, v...)) } // NewStructuredLogger creates a Logger that logs messages using JSON. -func NewStructuredLogger() (cloudsql.Logger, func() error) { - // Configure structured logs to adhere to LogEntry format - // https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry - c := zap.NewProductionEncoderConfig() - c.LevelKey = "severity" - c.MessageKey = "message" - c.TimeKey = "timestamp" - c.EncodeLevel = zapcore.CapitalLevelEncoder - c.EncodeTime = zapcore.ISO8601TimeEncoder - - enc := zapcore.NewJSONEncoder(c) - core := zapcore.NewTee( - zapcore.NewCore(enc, zapcore.Lock(os.Stdout), zap.LevelEnablerFunc(func(l zapcore.Level) bool { - // Anything below error, goes to the info log. - return l < zapcore.ErrorLevel - })), - zapcore.NewCore(enc, zapcore.Lock(os.Stderr), zap.LevelEnablerFunc(func(l zapcore.Level) bool { - // Anything at error or higher goes to the error log. - return l >= zapcore.ErrorLevel - })), - ) +func NewStructuredLogger(quiet bool) cloudsql.Logger { + var infoHandler, errorHandler slog.Handler + if quiet { + infoHandler = slog.DiscardHandler + } else { + infoHandler = slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ + Level: slog.LevelDebug, + ReplaceAttr: replaceAttr, + }) + } + errorHandler = slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{ + Level: slog.LevelError, + ReplaceAttr: replaceAttr, + }) + l := &StructuredLogger{ - logger: zap.New(core).Sugar(), + stdLog: slog.New(infoHandler), + errLog: slog.New(errorHandler), + } + return l +} + +// replaceAttr remaps default Go logging keys to adhere to LogEntry format +// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry +func replaceAttr(groups []string, a slog.Attr) slog.Attr { + if groups != nil { + return a + } + + switch a.Key { + case slog.LevelKey: + a.Key = "severity" + case slog.MessageKey: + a.Key = "message" + case slog.SourceKey: + a.Key = "sourceLocation" + case slog.TimeKey: + a.Key = "timestamp" + if a.Value.Kind() == slog.KindTime { + a.Value = slog.StringValue(a.Value.Time().Format(time.RFC3339)) + } } - return l, l.logger.Sync + return a } diff --git a/internal/proxy/alignment_test.go b/internal/proxy/alignment_test.go deleted file mode 100644 index a882237aa..000000000 --- a/internal/proxy/alignment_test.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2022 Google LLC -// -// 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. - -package proxy - -import ( - "testing" - "unsafe" -) - -func TestClientUsesSyncAtomicAlignment(t *testing.T) { - // The sync/atomic pkg has a bug that requires the developer to guarantee - // 64-bit alignment when using 64-bit functions on 32-bit systems. - c := &Client{} //nolint:staticcheck - - if a := unsafe.Offsetof(c.connCount); a%64 != 0 { - t.Errorf("Client.connCount is not 64-bit aligned: want 0, got %v", a) - } -} diff --git a/internal/proxy/fuse.go b/internal/proxy/fuse.go index 748b8400b..da9694fc8 100644 --- a/internal/proxy/fuse.go +++ b/internal/proxy/fuse.go @@ -13,7 +13,6 @@ // limitations under the License. //go:build !windows && !openbsd && !freebsd -// +build !windows,!openbsd,!freebsd package proxy diff --git a/internal/proxy/fuse_darwin.go b/internal/proxy/fuse_darwin.go index ceb5db269..8cefe7ce4 100644 --- a/internal/proxy/fuse_darwin.go +++ b/internal/proxy/fuse_darwin.go @@ -34,7 +34,7 @@ func SupportsFUSE() error { if _, err := os.Stat(macfusePath); err != nil { // if that fails, check for osxfuse next if _, err := os.Stat(osxfusePath); err != nil { - return errors.New("failed to find osxfuse or macfuse: verify FUSE installation and try again (see https://osxfuse.github.io).") + return errors.New("failed to find osxfuse or macfuse: verify FUSE installation and try again (see https://osxfuse.github.io)") } } return nil diff --git a/internal/proxy/fuse_test.go b/internal/proxy/fuse_test.go index 2068e4618..02face315 100644 --- a/internal/proxy/fuse_test.go +++ b/internal/proxy/fuse_test.go @@ -13,7 +13,6 @@ // limitations under the License. //go:build !windows && !darwin -// +build !windows,!darwin package proxy_test @@ -43,24 +42,37 @@ func randTmpDir(t interface { // newTestClient is a convenience function for testing that creates a // proxy.Client and starts it. The returned cleanup function is also a // convenience. Callers may choose to ignore it and manually close the client. -func newTestClient(t *testing.T, d cloudsql.Dialer, fuseDir, fuseTempDir string) (*proxy.Client, func()) { +func newTestClient(t *testing.T, d cloudsql.Dialer, fuseDir, fuseTempDir string) (*proxy.Client, chan error, func()) { conf := &proxy.Config{FUSEDir: fuseDir, FUSETempDir: fuseTempDir} - c, err := proxy.NewClient(context.Background(), d, testLogger, conf) + + // This context is only used to call the Cloud SQL API + c, err := proxy.NewClient(context.Background(), d, testLogger, conf, nil) if err != nil { t.Fatalf("want error = nil, got = %v", err) } ready := make(chan struct{}) - go c.Serve(context.Background(), func() { close(ready) }) + servErrCh := make(chan error) + + ctx, cancel := context.WithCancel(context.Background()) + go func() { + + servErr := c.Serve(ctx, func() { close(ready) }) + select { + case servErrCh <- servErr: + case <-ctx.Done(): + } + }() select { case <-ready: case <-time.Tick(5 * time.Second): t.Fatal("failed to Serve") } - return c, func() { + return c, servErrCh, func() { if cErr := c.Close(); cErr != nil { t.Logf("failed to close client: %v", cErr) } + cancel() } } @@ -70,7 +82,7 @@ func TestFUSEREADME(t *testing.T) { } dir := randTmpDir(t) d := &fakeDialer{} - _, cleanup := newTestClient(t, d, dir, randTmpDir(t)) + _, _, cleanup := newTestClient(t, d, dir, randTmpDir(t)) fi, err := os.Stat(dir) if err != nil { @@ -161,7 +173,7 @@ func TestFUSEDialInstance(t *testing.T) { for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { d := &fakeDialer{} - _, cleanup := newTestClient(t, d, fuseDir, tc.fuseTempDir) + _, _, cleanup := newTestClient(t, d, fuseDir, tc.fuseTempDir) defer cleanup() conn := tryDialUnix(t, tc.socketPath) @@ -185,13 +197,66 @@ func TestFUSEDialInstance(t *testing.T) { }) } } +func TestFUSEAcceptErrorReturnedFromServe(t *testing.T) { + if testing.Short() { + t.Skip("skipping fuse tests in short mode.") + } + + fuseDir := randTmpDir(t) + fuseTempDir := randTmpDir(t) + socketPath := filepath.Join(fuseDir, "proj:region:mysql") + + // Create a new client + d := &fakeDialer{} + c, servErrCh, cleanup := newTestClient(t, d, fuseDir, fuseTempDir) + defer cleanup() + + // Attempt a successful connection to the client + conn := tryDialUnix(t, socketPath) + defer conn.Close() + + // Ensure that the client actually fully connected. + // This solves a race condition in the test that is only present on + // the Ubuntu-Latest platform. + var got []string + for i := 0; i < 10; i++ { + got = d.dialedInstances() + if len(got) == 1 { + break + } + time.Sleep(100 * time.Millisecond) + } + if len(got) != 1 { + t.Fatalf("dialed instances len: want = 1, got = %v", got) + } + + // Explicitly close the dialer. This will close all the unix sockets, forcing + // the unix socket accept goroutine to exit with an error + c.Close() + + // Check that Client.Serve() returned a non-nil error + for i := 0; i < 10; i++ { + select { + case servErr := <-servErrCh: + if servErr == nil { + t.Fatal("got nil, want non-nil error returned by Client.Serve()") + } + return + default: + time.Sleep(100 * time.Millisecond) + continue + } + } + t.Fatal("No error thrown by Client.Serve()") + +} func TestFUSEReadDir(t *testing.T) { if testing.Short() { t.Skip("skipping fuse tests in short mode.") } fuseDir := randTmpDir(t) - _, cleanup := newTestClient(t, &fakeDialer{}, fuseDir, randTmpDir(t)) + _, _, cleanup := newTestClient(t, &fakeDialer{}, fuseDir, randTmpDir(t)) defer cleanup() // Initiate a connection so the FUSE server will list it in the dir entries. @@ -215,13 +280,34 @@ func TestFUSEReadDir(t *testing.T) { } } +func TestLookupIgnoresContext(t *testing.T) { + if testing.Short() { + t.Skip("skipping fuse tests in short mode.") + } + // create context and cancel it immediately + ctx, cancel := context.WithCancel(context.Background()) + cancel() + d := &fakeDialer{} + c, _, _ := newTestClient(t, d, randTmpDir(t), randTmpDir(t)) + + // invoke Lookup with cancelled context, should ignore context and succeed + _, err := c.Lookup(ctx, "proj:reg:mysql", nil) + if err != fs.OK { + t.Fatalf("proxy.Client.Lookup(): %v", err) + } + // Close the client to close all open sockets. + if err := c.Close(); err != nil { + t.Fatalf("c.Close(): %v", err) + } +} + func TestFUSEErrors(t *testing.T) { if testing.Short() { t.Skip("skipping fuse tests in short mode.") } ctx := context.Background() d := &fakeDialer{} - c, _ := newTestClient(t, d, randTmpDir(t), randTmpDir(t)) + c, _, _ := newTestClient(t, d, randTmpDir(t), randTmpDir(t)) // Simulate FUSE file access by invoking Lookup directly to control // how the socket cache is populated. @@ -261,7 +347,7 @@ func TestFUSEWithBadInstanceName(t *testing.T) { } fuseDir := randTmpDir(t) d := &fakeDialer{} - _, cleanup := newTestClient(t, d, fuseDir, randTmpDir(t)) + _, _, cleanup := newTestClient(t, d, fuseDir, randTmpDir(t)) defer cleanup() _, dialErr := net.Dial("unix", filepath.Join(fuseDir, "notvalid")) @@ -280,7 +366,7 @@ func TestFUSECheckConnections(t *testing.T) { } fuseDir := randTmpDir(t) d := &fakeDialer{} - c, cleanup := newTestClient(t, d, fuseDir, randTmpDir(t)) + c, _, cleanup := newTestClient(t, d, fuseDir, randTmpDir(t)) defer cleanup() // first establish a connection to "register" it with the proxy @@ -315,7 +401,7 @@ func TestFUSEClose(t *testing.T) { } fuseDir := randTmpDir(t) d := &fakeDialer{} - c, _ := newTestClient(t, d, fuseDir, randTmpDir(t)) + c, _, _ := newTestClient(t, d, fuseDir, randTmpDir(t)) // first establish a connection to "register" it with the proxy conn := tryDialUnix(t, filepath.Join(fuseDir, "proj:reg:mysql")) @@ -337,7 +423,7 @@ func TestFUSEWithBadDir(t *testing.T) { t.Skip("skipping fuse tests in short mode.") } conf := &proxy.Config{FUSEDir: "/not/a/dir", FUSETempDir: randTmpDir(t)} - _, err := proxy.NewClient(context.Background(), &fakeDialer{}, testLogger, conf) + _, err := proxy.NewClient(context.Background(), &fakeDialer{}, testLogger, conf, nil) if err == nil { t.Fatal("proxy client should fail with bad dir") } diff --git a/internal/proxy/internal_test.go b/internal/proxy/internal_test.go new file mode 100644 index 000000000..6523ed472 --- /dev/null +++ b/internal/proxy/internal_test.go @@ -0,0 +1,79 @@ +// Copyright 2022 Google LLC +// +// 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. + +package proxy + +import ( + "testing" + "unsafe" + + "github.com/google/go-cmp/cmp" +) + +func TestClientUsesSyncAtomicAlignment(t *testing.T) { + // The sync/atomic pkg has a bug that requires the developer to guarantee + // 64-bit alignment when using 64-bit functions on 32-bit systems. + c := &Client{} //nolint:staticcheck + + if a := unsafe.Offsetof(c.connCount); a%64 != 0 { + t.Errorf("Client.connCount is not 64-bit aligned: want 0, got %v", a) + } +} + +func TestParseImpersonationChain(t *testing.T) { + tcs := []struct { + desc string + in string + wantTarget string + wantChain []string + }{ + { + desc: "when there is only a target", + in: "sv1@developer.gserviceaccount.com", + wantTarget: "sv1@developer.gserviceaccount.com", + }, + { + desc: "when there are delegates", + in: "sv1@developer.gserviceaccount.com,sv2@developer.gserviceaccount.com,sv3@developer.gserviceaccount.com", + wantTarget: "sv1@developer.gserviceaccount.com", + wantChain: []string{ + "sv3@developer.gserviceaccount.com", + "sv2@developer.gserviceaccount.com", + }, + }, + } + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + gotTarget, gotChain := parseImpersonationChain(tc.in) + if gotTarget != tc.wantTarget { + t.Fatalf("target: want = %v, got = %v", tc.wantTarget, gotTarget) + } + if !equalSlice(tc.wantChain, gotChain) { + t.Fatalf("want chain != got chain: %v", cmp.Diff(tc.wantChain, gotChain)) + } + }) + } +} + +func equalSlice[T comparable](x, y []T) bool { + if len(x) != len(y) { + return false + } + for i := 0; i < len(x); i++ { + if x[i] != y[i] { + return false + } + } + return true +} diff --git a/internal/proxy/proxy.go b/internal/proxy/proxy.go index 7ba1086ce..0b023df3f 100644 --- a/internal/proxy/proxy.go +++ b/internal/proxy/proxy.go @@ -20,6 +20,7 @@ import ( "io" "net" "os" + "path" "regexp" "strings" "sync" @@ -83,33 +84,52 @@ type InstanceConnConfig struct { // Port is the port on which to bind a listener for the instance. Port int // UnixSocket is the directory where a Unix socket will be created, - // connected to the Cloud SQL instance. If set, takes precedence over Addr + // connected to the Cloud SQL instance. The full path to the socket will be + // UnixSocket + os.PathSeparator + Name. If set, takes precedence over Addr // and Port. UnixSocket string + // UnixSocketPath is the path where a Unix socket will be created, + // connected to the Cloud SQL instance. The full path to the socket will be + // UnixSocketPath. If this is a Postgres database, the proxy will ensure that + // the last path element is `.s.PGSQL.5432`, appending this path element if + // necessary. If set, UnixSocketPath takes precedence over UnixSocket, Addr + // and Port. + UnixSocketPath string // IAMAuthN enables automatic IAM DB Authentication for the instance. - // Postgres-only. If it is nil, the value was not specified. + // MySQL and Postgres only. If it is nil, the value was not specified. IAMAuthN *bool // PrivateIP tells the proxy to attempt to connect to the db instance's // private IP address instead of the public IP address PrivateIP *bool + + // PSC tells the proxy to attempt to connect to the db instance's + // private service connect endpoint + PSC *bool } // Config contains all the configuration provided by the caller. type Config struct { + // Filepath is the path to a configuration file. + Filepath string + // UserAgent is the user agent to use when connecting to the cloudsql instance UserAgent string // Token is the Bearer token used for authorization. Token string + // LoginToken is the Bearer token used for Auto IAM AuthN. Used only in + // conjunction with Token. + LoginToken string + // CredentialsFile is the path to a service account key. CredentialsFile string // CredentialsJSON is a JSON representation of the service account key. CredentialsJSON string - // GcloudAuth set whether to use Gcloud's config helper to retrieve a + // GcloudAuth set whether to use gcloud's config helper to retrieve a // token for authentication. GcloudAuth bool @@ -120,10 +140,14 @@ type Config struct { // increments from this value. Port int - // APIEndpointURL is the URL of the google cloud sql api. When left blank, + // APIEndpointURL is the URL of the Google Cloud SQL Admin API. When left blank, // the proxy will use the main public api: https://sqladmin.googleapis.com/ APIEndpointURL string + // UniverseDomain is the universe domain for the TPC environment. When left + // blank, the proxy will use the Google Default Universe (GDU): googleapis.com + UniverseDomain string + // UnixSocket is the directory where Unix sockets will be created, // connected to any Instances. If set, takes precedence over Addr and Port. UnixSocket string @@ -138,7 +162,7 @@ type Config struct { FUSETempDir string // IAMAuthN enables automatic IAM DB Authentication for all instances. - // Postgres-only. + // MySQL and Postgres only. IAMAuthN bool // MaxConnections are the maximum number of connections the Client may @@ -146,6 +170,11 @@ type Config struct { // connections. A zero-value indicates no limit. MaxConnections uint64 + // WaitBeforeClose sets the duration to wait after receiving a shutdown signal + // but before closing the process. Not setting this field means to initiate + // the shutdown process immediately. + WaitBeforeClose time.Duration + // WaitOnClose sets the duration to wait for connections to close before // shutting down. Not setting this field means to close immediately // regardless of any open connections. @@ -155,6 +184,22 @@ type Config struct { // for all instances. PrivateIP bool + // PSC enables connections via the database server's private service connect + // endpoint for all instances + PSC bool + + // AutoIP supports a legacy behavior where the Proxy will connect to + // the first IP address returned from the SQL ADmin API response. This + // setting should be avoided and used only to support legacy Proxy + // users. + AutoIP bool + + // LazyRefresh configures the Go Connector to retrieve connection info + // lazily and as-needed. Otherwise, no background refresh cycle runs. This + // setting is useful in environments where the CPU may be throttled outside + // of a request context, e.g., Cloud Run. + LazyRefresh bool + // Instances are configuration for individual instances. Instance // configuration takes precedence over global configuration. Instances []InstanceConnConfig @@ -163,22 +208,70 @@ type Config struct { // API request quotas. QuotaProject string - // ImpersonateTarget is the service account to impersonate. The IAM - // principal doing the impersonation must have the - // roles/iam.serviceAccountTokenCreator role. - ImpersonateTarget string - // ImpersonateDelegates are the intermediate service accounts through which - // the impersonation is achieved. Each delegate must have the - // roles/iam.serviceAccountTokenCreator role. - ImpersonateDelegates []string + // ImpersonationChain is a comma separated list of one or more service + // accounts. The first entry in the chain is the impersonation target. Any + // additional service accounts after the target are delegates. The + // roles/iam.serviceAccountTokenCreator must be configured for each account + // that will be impersonated. + ImpersonationChain string // StructuredLogs sets all output to use JSON in the LogEntry format. // See https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry StructuredLogs bool - - // Dialer specifies the dialer to use when connecting to Cloud SQL - // instances. - Dialer cloudsql.Dialer + // Quiet controls whether only error messages are logged. + Quiet bool + + // TelemetryProject enables sending metrics and traces to the specified project. + TelemetryProject string + // TelemetryPrefix sets a prefix for all emitted metrics. + TelemetryPrefix string + // TelemetryTracingSampleRate sets the rate at which traces are + // samples. A higher value means fewer traces. + TelemetryTracingSampleRate int + // ExitZeroOnSigterm exits with 0 exit code when Sigterm received + ExitZeroOnSigterm bool + // DisableTraces disables tracing when TelemetryProject is set. + DisableTraces bool + // DisableMetrics disables metrics when TelemetryProject is set. + DisableMetrics bool + + // Prometheus enables a Prometheus endpoint served at the address and + // port specified by HTTPAddress and HTTPPort. + Prometheus bool + // PrometheusNamespace configures the namespace under which metrics are written. + PrometheusNamespace string + + // HealthCheck enables a health check server. It's address and port are + // specified by HTTPAddress and HTTPPort. + HealthCheck bool + + // HTTPAddress sets the address for the health check and prometheus server. + HTTPAddress string + // HTTPPort sets the port for the health check and prometheus server. + HTTPPort string + // AdminPort configures the port for the localhost-only admin server. + AdminPort string + + // Debug enables a debug handler on localhost. + Debug bool + // QuitQuitQuit enables a handler that will shut the Proxy down upon + // receiving a GET or POST request. + QuitQuitQuit bool + // DebugLogs enables debug level logging. + DebugLogs bool + + // OtherUserAgents is a list of space separate user agents that will be + // appended to the default user agent. + OtherUserAgents string + + // RunConnectionTest determines whether the Proxy should attempt a connection + // to all specified instances to verify the network path is valid. + RunConnectionTest bool + + // SkipFailedInstanceConfig determines whether the Proxy should skip failed + // connections to Cloud SQL instances instead of exiting on startup. + // This only applies to Unix sockets. + SkipFailedInstanceConfig bool } // dialOptions interprets appropriate dial options for a particular instance @@ -190,21 +283,64 @@ func dialOptions(c Config, i InstanceConnConfig) []cloudsqlconn.DialOption { opts = append(opts, cloudsqlconn.WithDialIAMAuthN(*i.IAMAuthN)) } - if i.PrivateIP != nil && *i.PrivateIP || i.PrivateIP == nil && c.PrivateIP { + switch { + // If private IP is enabled at the instance level, or private IP is enabled globally + // add the option. + case i.PrivateIP != nil && *i.PrivateIP || i.PrivateIP == nil && c.PrivateIP: opts = append(opts, cloudsqlconn.WithPrivateIP()) + // If PSC is enabled at the instance level, or PSC is enabled globally + // add the option. + case i.PSC != nil && *i.PSC || i.PSC == nil && c.PSC: + opts = append(opts, cloudsqlconn.WithPSC()) + case c.AutoIP: + opts = append(opts, cloudsqlconn.WithAutoIP()) + default: + // assume public IP by default + } + if networkType(&c, i) == "unix" { + opts = append(opts, cloudsqlconn.WithMdxClientProtocolType("uds")) } else { - opts = append(opts, cloudsqlconn.WithPublicIP()) + opts = append(opts, cloudsqlconn.WithMdxClientProtocolType("tcp")) } return opts } +func parseImpersonationChain(chain string) (string, []string) { + accts := strings.Split(chain, ",") + target := accts[0] + // Assign delegates if the chain is more than one account. Delegation + // goes from last back towards target, e.g., With sa1,sa2,sa3, sa3 + // delegates to sa2, which impersonates the target sa1. + var delegates []string + if l := len(accts); l > 1 { + for i := l - 1; i > 0; i-- { + delegates = append(delegates, accts[i]) + } + } + return target, delegates +} + const iamLoginScope = "https://www.googleapis.com/auth/sqlservice.login" +// iamAuthNEnabled returns true if IAM authentication is enabled globally +// or for any instance in the configuration. +func (c *Config) iamAuthNEnabled() bool { + if c.IAMAuthN { + return true + } + for _, inst := range c.Instances { + if inst.IAMAuthN != nil && *inst.IAMAuthN { + return true + } + } + return false +} + func credentialsOpt(c Config, l cloudsql.Logger) (cloudsqlconn.Option, error) { // If service account impersonation is configured, set up an impersonated // credentials token source. - if c.ImpersonateTarget != "" { + if c.ImpersonationChain != "" { var iopts []option.ClientOption switch { case c.Token != "": @@ -214,10 +350,10 @@ func credentialsOpt(c Config, l cloudsql.Logger) (cloudsqlconn.Option, error) { )) case c.CredentialsFile != "": l.Infof("Impersonating service account with the credentials file at %q", c.CredentialsFile) - iopts = append(iopts, option.WithCredentialsFile(c.CredentialsFile)) + iopts = append(iopts, option.WithAuthCredentialsFile(option.ServiceAccount, c.CredentialsFile)) case c.CredentialsJSON != "": l.Infof("Impersonating service account with JSON credentials environment variable") - iopts = append(iopts, option.WithCredentialsJSON([]byte(c.CredentialsJSON))) + iopts = append(iopts, option.WithAuthCredentialsJSON(option.ServiceAccount, []byte(c.CredentialsJSON))) case c.GcloudAuth: l.Infof("Impersonating service account with gcloud user credentials") ts, err := gcloud.TokenSource() @@ -228,11 +364,12 @@ func credentialsOpt(c Config, l cloudsql.Logger) (cloudsqlconn.Option, error) { default: l.Infof("Impersonating service account with Application Default Credentials") } + target, delegates := parseImpersonationChain(c.ImpersonationChain) ts, err := impersonate.CredentialsTokenSource( context.Background(), impersonate.CredentialsConfig{ - TargetPrincipal: c.ImpersonateTarget, - Delegates: c.ImpersonateDelegates, + TargetPrincipal: target, + Delegates: delegates, Scopes: []string{sqladmin.SqlserviceAdminScope}, }, iopts..., @@ -240,12 +377,13 @@ func credentialsOpt(c Config, l cloudsql.Logger) (cloudsqlconn.Option, error) { if err != nil { return nil, err } - if c.IAMAuthN { + + if c.iamAuthNEnabled() { iamLoginTS, err := impersonate.CredentialsTokenSource( context.Background(), impersonate.CredentialsConfig{ - TargetPrincipal: c.ImpersonateTarget, - Delegates: c.ImpersonateDelegates, + TargetPrincipal: target, + Delegates: delegates, Scopes: []string{iamLoginScope}, }, iopts..., @@ -259,36 +397,43 @@ func credentialsOpt(c Config, l cloudsql.Logger) (cloudsqlconn.Option, error) { } // Otherwise, configure credentials as usual. + var opt cloudsqlconn.Option switch { case c.Token != "": l.Infof("Authorizing with OAuth2 token") - return cloudsqlconn.WithTokenSource( - oauth2.StaticTokenSource(&oauth2.Token{AccessToken: c.Token}), - ), nil + ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: c.Token}) + if c.IAMAuthN { + lts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: c.LoginToken}) + opt = cloudsqlconn.WithIAMAuthNTokenSources(ts, lts) + } else { + opt = cloudsqlconn.WithTokenSource(ts) + } case c.CredentialsFile != "": l.Infof("Authorizing with the credentials file at %q", c.CredentialsFile) - return cloudsqlconn.WithCredentialsFile(c.CredentialsFile), nil + opt = cloudsqlconn.WithCredentialsFile(c.CredentialsFile) case c.CredentialsJSON != "": l.Infof("Authorizing with JSON credentials environment variable") - return cloudsqlconn.WithCredentialsJSON([]byte(c.CredentialsJSON)), nil + opt = cloudsqlconn.WithCredentialsJSON([]byte(c.CredentialsJSON)) case c.GcloudAuth: l.Infof("Authorizing with gcloud user credentials") ts, err := gcloud.TokenSource() if err != nil { return nil, err } - return cloudsqlconn.WithTokenSource(ts), nil + opt = cloudsqlconn.WithTokenSource(ts) default: l.Infof("Authorizing with Application Default Credentials") // Return no-op options to avoid having to handle nil in caller code - return cloudsqlconn.WithOptions(), nil + opt = cloudsqlconn.WithOptions() } + return opt, nil } // DialerOptions builds appropriate list of options from the Config // values for use by cloudsqlconn.NewClient() func (c *Config) DialerOptions(l cloudsql.Logger) ([]cloudsqlconn.Option, error) { opts := []cloudsqlconn.Option{ + cloudsqlconn.WithDNSResolver(), cloudsqlconn.WithUserAgent(c.UserAgent), } co, err := credentialsOpt(*c, l) @@ -297,11 +442,19 @@ func (c *Config) DialerOptions(l cloudsql.Logger) ([]cloudsqlconn.Option, error) } opts = append(opts, co) + if c.DebugLogs { + // nolint:staticcheck + opts = append(opts, cloudsqlconn.WithDebugLogger(l)) + } if c.APIEndpointURL != "" { opts = append(opts, cloudsqlconn.WithAdminAPIEndpoint(c.APIEndpointURL)) } - if c.IAMAuthN { + if c.UniverseDomain != "" { + opts = append(opts, cloudsqlconn.WithUniverseDomain(c.UniverseDomain)) + } + + if c.iamAuthNEnabled() { opts = append(opts, cloudsqlconn.WithIAMAuthN()) } @@ -309,6 +462,10 @@ func (c *Config) DialerOptions(l cloudsql.Logger) ([]cloudsqlconn.Option, error) opts = append(opts, cloudsqlconn.WithQuotaProject(c.QuotaProject)) } + if c.LazyRefresh { + opts = append(opts, cloudsqlconn.WithLazyRefresh()) + } + return opts, nil } @@ -362,27 +519,24 @@ type Client struct { // all Cloud SQL instances. connCount uint64 - // maxConns is the maximum number of allowed connections tracked by - // connCount. If not set, there is no limit. - maxConns uint64 + // conf is the configuration used to initialize the Client. + conf *Config dialer cloudsql.Dialer // mnts is a list of all mounted sockets for this client mnts []*socketMount - // waitOnClose is the maximum duration to wait for open connections to close - // when shutting down. - waitOnClose time.Duration - logger cloudsql.Logger + connRefuseNotify func() + fuseMount } // NewClient completes the initial setup required to get the proxy to a "steady" // state. -func NewClient(ctx context.Context, d cloudsql.Dialer, l cloudsql.Logger, conf *Config) (*Client, error) { +func NewClient(ctx context.Context, d cloudsql.Dialer, l cloudsql.Logger, conf *Config, connRefuseNotify func()) (*Client, error) { // Check if the caller has configured a dialer. // Otherwise, initialize a new one. if d == nil { @@ -397,10 +551,10 @@ func NewClient(ctx context.Context, d cloudsql.Dialer, l cloudsql.Logger, conf * } c := &Client{ - logger: l, - dialer: d, - maxConns: conf.MaxConnections, - waitOnClose: conf.WaitOnClose, + logger: l, + dialer: d, + connRefuseNotify: connRefuseNotify, + conf: conf, } if conf.FUSEDir != "" { @@ -409,19 +563,19 @@ func NewClient(ctx context.Context, d cloudsql.Dialer, l cloudsql.Logger, conf * for _, inst := range conf.Instances { // Initiate refresh operation and warm the cache. - go func(name string) { d.EngineVersion(ctx, name) }(inst.Name) + go func(name string) { _, _ = d.EngineVersion(ctx, name) }(inst.Name) } var mnts []*socketMount pc := newPortConfig(conf.Port) for _, inst := range conf.Instances { - version, err := d.EngineVersion(ctx, inst.Name) + m, err := c.newSocketMount(ctx, conf, pc, inst) if err != nil { - return nil, err - } + if conf.SkipFailedInstanceConfig { + l.Errorf("[%v] Unable to mount socket: %v (skipped due to skip-failed-instance-config flag)", inst.Name, err) + continue + } - m, err := newSocketMount(ctx, conf, pc, inst, version) - if err != nil { for _, m := range mnts { mErr := m.Close() if mErr != nil { @@ -488,7 +642,7 @@ func (c *Client) CheckConnections(ctx context.Context) (int, error) { // ConnCount returns the number of open connections and the maximum allowed // connections. Returns 0 when the maximum allowed connections have not been set. func (c *Client) ConnCount() (uint64, uint64) { - return atomic.LoadUint64(&c.connCount), c.maxConns + return atomic.LoadUint64(&c.connCount), c.conf.MaxConnections } // Serve starts proxying connections for all configured instances using the @@ -501,6 +655,15 @@ func (c *Client) Serve(ctx context.Context, notify func()) error { return c.serveFuse(ctx, notify) } + if c.conf.RunConnectionTest { + c.logger.Infof("Connection test started") + if _, err := c.CheckConnections(ctx); err != nil { + c.logger.Errorf("Connection test failed") + return err + } + c.logger.Infof("Connection test passed") + } + exitCh := make(chan error) for _, m := range c.mnts { go func(mnt *socketMount) { @@ -539,12 +702,13 @@ func (m MultiErr) Error() string { return strings.Join(errs, ", ") } -// Close triggers the proxyClient to shutdown. +// Close triggers the proxyClient to shut down. func (c *Client) Close() error { mnts := c.mnts - var mErr MultiErr + // If FUSE is enabled, unmount it and save a reference to any existing + // socket mounts. if c.fuseDir != "" { if err := c.unmountFUSE(); err != nil { mErr = append(mErr, err) @@ -552,28 +716,15 @@ func (c *Client) Close() error { mnts = c.fuseMounts() } - // First, close all open socket listeners to prevent additional connections. - for _, m := range mnts { - err := m.Close() - if err != nil { - mErr = append(mErr, err) - } - } - if c.fuseDir != "" { - c.waitForFUSEMounts() - } - // Next, close the dialer to prevent any additional refreshes. + // Close the dialer to prevent any additional refreshes. cErr := c.dialer.Close() if cErr != nil { mErr = append(mErr, cErr) } - if c.waitOnClose == 0 { - if len(mErr) > 0 { - return mErr - } - return nil - } - timeout := time.After(c.waitOnClose) + + // Start a timer for clean shutdown (where all connections are closed). + // While the timer runs, additional connections will be accepted. + timeout := time.After(c.conf.WaitOnClose) t := time.NewTicker(100 * time.Millisecond) defer t.Stop() for { @@ -586,9 +737,22 @@ func (c *Client) Close() error { } break } + // Close all open socket listeners. Time to complete shutdown. + for _, m := range mnts { + err := m.Close() + if err != nil { + mErr = append(mErr, err) + } + } + if c.fuseDir != "" { + c.waitForFUSEMounts() + } + // Verify that all connections are closed. open := atomic.LoadUint64(&c.connCount) - if open > 0 { - mErr = append(mErr, fmt.Errorf("%d connection(s) still open after waiting %v", open, c.waitOnClose)) + if c.conf.WaitOnClose > 0 && open > 0 { + openErr := fmt.Errorf( + "%d connection(s) still open after waiting %v", open, c.conf.WaitOnClose) + mErr = append(mErr, openErr) } if len(mErr) > 0 { return mErr @@ -612,7 +776,7 @@ func (c *Client) serveSocketMount(_ context.Context, s *socketMount) error { } // handle the connection in a separate goroutine go func() { - c.logger.Infof("[%s] accepted connection from %s", s.inst, cConn.RemoteAddr()) + c.logger.Infof("[%s] Accepted connection from %s", s.inst, cConn.RemoteAddr()) // A client has established a connection to the local socket. Before // we initiate a connection to the Cloud SQL backend, increment the @@ -621,8 +785,11 @@ func (c *Client) serveSocketMount(_ context.Context, s *socketMount) error { count := atomic.AddUint64(&c.connCount, 1) defer atomic.AddUint64(&c.connCount, ^uint64(0)) - if c.maxConns > 0 && count > c.maxConns { - c.logger.Infof("max connections (%v) exceeded, refusing new connection", c.maxConns) + if c.conf.MaxConnections > 0 && count > c.conf.MaxConnections { + c.logger.Infof("max connections (%v) exceeded, refusing new connection", c.conf.MaxConnections) + if c.connRefuseNotify != nil { + go c.connRefuseNotify() + } _ = cConn.Close() return } @@ -634,7 +801,7 @@ func (c *Client) serveSocketMount(_ context.Context, s *socketMount) error { sConn, err := c.dialer.Dial(ctx, s.inst, s.dialOpts...) if err != nil { c.logger.Errorf("[%s] failed to connect to instance: %v", s.inst, err) - cConn.Close() + _ = cConn.Close() return } c.proxyConn(s.inst, cConn, sConn) @@ -649,12 +816,21 @@ type socketMount struct { dialOpts []cloudsqlconn.DialOption } -func newSocketMount(ctx context.Context, conf *Config, pc *portConfig, inst InstanceConnConfig, version string) (*socketMount, error) { +func networkType(conf *Config, inst InstanceConnConfig) string { + if (conf.UnixSocket == "" && inst.UnixSocket == "" && inst.UnixSocketPath == "") || + (inst.Addr != "" || inst.Port != 0) { + return "tcp" + } + return "unix" +} + +func (c *Client) newSocketMount(ctx context.Context, conf *Config, pc *portConfig, inst InstanceConnConfig) (*socketMount, error) { var ( // network is one of "tcp" or "unix" network string // address is either a TCP host port, or a Unix socket address string + err error ) // IF // a global Unix socket directory is NOT set AND @@ -666,8 +842,7 @@ func newSocketMount(ctx context.Context, conf *Config, pc *portConfig, inst Inst // instance) // use a TCP listener. // Otherwise, use a Unix socket. - if (conf.UnixSocket == "" && inst.UnixSocket == "") || - (inst.Addr != "" || inst.Port != 0) { + if networkType(conf, inst) == "tcp" { network = "tcp" a := conf.Addr @@ -682,6 +857,12 @@ func newSocketMount(ctx context.Context, conf *Config, pc *portConfig, inst Inst case conf.Port != 0: np = pc.nextPort() default: + version, err := c.dialer.EngineVersion(ctx, inst.Name) + // Exit if the port is not specified for inactive instance + if err != nil { + c.logger.Errorf("[%v] could not resolve instance version: %v", inst.Name, err) + return nil, err + } np = pc.nextDBPort(version) } @@ -689,34 +870,26 @@ func newSocketMount(ctx context.Context, conf *Config, pc *portConfig, inst Inst } else { network = "unix" - dir := conf.UnixSocket - if dir == "" { - dir = inst.UnixSocket - } - if _, err := os.Stat(dir); err != nil { + version, err := c.dialer.EngineVersion(ctx, inst.Name) + if err != nil { + c.logger.Errorf("[%v] could not resolve instance version: %v", inst.Name, err) return nil, err } - address = UnixAddress(dir, inst.Name) - // When setting up a listener for Postgres, create address as a - // directory, and use the Postgres-specific socket name - // .s.PGSQL.5432. - if strings.HasPrefix(version, "POSTGRES") { - // Make the directory only if it hasn't already been created. - if _, err := os.Stat(address); err != nil { - if err = os.Mkdir(address, 0777); err != nil { - return nil, err - } - } - address = UnixAddress(address, ".s.PGSQL.5432") + + address, err = newUnixSocketMount(inst, conf.UnixSocket, strings.HasPrefix(version, "POSTGRES")) + if err != nil { + c.logger.Errorf("[%v] could not mount unix socket %q: %v", inst.Name, conf.UnixSocket, err) + return nil, err } } lc := net.ListenConfig{KeepAlive: 30 * time.Second} ln, err := lc.Listen(ctx, network, address) if err != nil { + c.logger.Errorf("[%v] could not listen to address %v: %v", inst.Name, address, err) return nil, err } - // Change file permisions to allow access for user, group, and other. + // Change file permissions to allow access for user, group, and other. if network == "unix" { // Best effort. If this call fails, group and other won't have write // access. @@ -727,6 +900,56 @@ func newSocketMount(ctx context.Context, conf *Config, pc *portConfig, inst Inst return m, nil } +// newUnixSocketMount parses the configuration and returns the path to the unix +// socket, or an error if that path is not valid. +func newUnixSocketMount(inst InstanceConnConfig, unixSocketDir string, postgres bool) (string, error) { + var ( + // the path to the unix socket + address string + // the parent directory of the unix socket + dir string + ) + + if inst.UnixSocketPath != "" { + // When UnixSocketPath is set + address = inst.UnixSocketPath + + // If UnixSocketPath ends .s.PGSQL.5432, remove it for consistency + if postgres && path.Base(address) == ".s.PGSQL.5432" { + address = path.Dir(address) + } + + dir = path.Dir(address) + } else { + // When UnixSocket is set + dir = unixSocketDir + if dir == "" { + dir = inst.UnixSocket + } + address = UnixAddress(dir, inst.Name) + } + + // if base directory does not exist, fail + if _, err := os.Stat(dir); err != nil { + return "", err + } + + // When setting up a listener for Postgres, create address as a + // directory, and use the Postgres-specific socket name + // .s.PGSQL.5432. + if postgres { + // Make the directory only if it hasn't already been created. + if _, err := os.Stat(address); err != nil { + if err = os.Mkdir(address, 0777); err != nil { + return "", err + } + } + address = UnixAddress(address, ".s.PGSQL.5432") + } + + return address, nil +} + func (s *socketMount) Addr() net.Addr { return s.listener.Addr() } @@ -735,7 +958,7 @@ func (s *socketMount) Accept() (net.Conn, error) { return s.listener.Accept() } -// close stops the mount from listening for any more connections +// Close stops the mount from listening for any more connections func (s *socketMount) Close() error { return s.listener.Close() } @@ -746,8 +969,8 @@ func (c *Client) proxyConn(inst string, client, server net.Conn) { var o sync.Once cleanup := func(errDesc string, isErr bool) { o.Do(func() { - client.Close() - server.Close() + _ = client.Close() + _ = server.Close() if isErr { c.logger.Errorf(errDesc) } else { @@ -776,7 +999,7 @@ func (c *Client) proxyConn(inst string, client, server net.Conn) { cleanup(fmt.Sprintf("[%s] instance closed the connection", inst), false) return case sErr != nil: - cleanup(fmt.Sprintf("[%s] connection aborted - error writing to instance: %v", inst, cErr), true) + cleanup(fmt.Sprintf("[%s] connection aborted - error writing to instance: %v", inst, sErr), true) return } } @@ -801,7 +1024,7 @@ func (c *Client) proxyConn(inst string, client, server net.Conn) { cleanup(fmt.Sprintf("[%s] client closed the connection", inst), false) return case cErr != nil: - cleanup(fmt.Sprintf("[%s] connection aborted - error writing to client: %v", inst, sErr), true) + cleanup(fmt.Sprintf("[%s] connection aborted - error writing to client: %v", inst, cErr), true) return } } diff --git a/internal/proxy/proxy_other.go b/internal/proxy/proxy_other.go index 71177fee0..b3c7180f3 100644 --- a/internal/proxy/proxy_other.go +++ b/internal/proxy/proxy_other.go @@ -13,7 +13,6 @@ // limitations under the License. //go:build !windows && !openbsd && !freebsd -// +build !windows,!openbsd,!freebsd package proxy @@ -68,6 +67,7 @@ type fuseMount struct { fuseServerMu *sync.Mutex fuseServer *fuse.Server fuseWg *sync.WaitGroup + fuseExitCh chan error // Inode adds support for FUSE operations. fs.Inode @@ -99,30 +99,28 @@ func (c *Client) Readdir(_ context.Context) (fs.DirStream, syscall.Errno) { // socket is connected to the requested Cloud SQL instance. Lookup returns a // symlink (instead of the socket itself) so that multiple callers all use the // same Unix socket. -func (c *Client) Lookup(ctx context.Context, instance string, _ *fuse.EntryOut) (*fs.Inode, syscall.Errno) { +func (c *Client) Lookup(_ context.Context, instance string, _ *fuse.EntryOut) (*fs.Inode, syscall.Errno) { + ctx := context.Background() if instance == "README" { return c.NewInode(ctx, &readme{}, fs.StableAttr{}), fs.OK } if _, err := parseConnName(instance); err != nil { + c.logger.Debugf("could not parse instance connection name for %q: %v", instance, err) return nil, syscall.ENOENT } c.fuseMu.Lock() defer c.fuseMu.Unlock() if l, ok := c.fuseSockets[instance]; ok { + c.logger.Debugf("found existing socket for instance %q", instance) return l.symlink.EmbeddedInode(), fs.OK } - version, err := c.dialer.EngineVersion(ctx, instance) - if err != nil { - c.logger.Errorf("could not resolve version for %q: %v", instance, err) - return nil, syscall.ENOENT - } - - s, err := newSocketMount( - ctx, &Config{UnixSocket: c.fuseTempDir}, - nil, InstanceConnConfig{Name: instance}, version, + c.logger.Debugf("creating new socket for instance %q", instance) + s, err := c.newSocketMount( + ctx, withUnixSocket(*c.conf, c.fuseTempDir), + nil, InstanceConnConfig{Name: instance}, ) if err != nil { c.logger.Errorf("could not create socket for %q: %v", instance, err) @@ -134,9 +132,20 @@ func (c *Client) Lookup(ctx context.Context, instance string, _ *fuse.EntryOut) defer c.fuseWg.Done() sErr := c.serveSocketMount(ctx, s) if sErr != nil { + c.logger.Debugf("could not serve socket for instance %q: %v", instance, sErr) c.fuseMu.Lock() + defer c.fuseMu.Unlock() delete(c.fuseSockets, instance) - c.fuseMu.Unlock() + select { + // Best effort attempt to send error. + // If this send fails, it means the reading goroutine has + // already pulled a value out of the channel and is no longer + // reading any more values. In other words, we report only the + // first error. + case c.fuseExitCh <- sErr: + default: + return + } } }() @@ -153,6 +162,11 @@ func (c *Client) Lookup(ctx context.Context, instance string, _ *fuse.EntryOut) ), fs.OK } +func withUnixSocket(c Config, tmpDir string) *Config { + c.UnixSocket = tmpDir + return &c +} + func (c *Client) serveFuse(ctx context.Context, notify func()) error { srv, err := fs.Mount(c.fuseDir, c, &fs.Options{ MountOptions: fuse.MountOptions{AllowOther: true}, @@ -162,10 +176,16 @@ func (c *Client) serveFuse(ctx context.Context, notify func()) error { } c.fuseServerMu.Lock() c.fuseServer = srv + c.fuseExitCh = make(chan error) + c.fuseServerMu.Unlock() notify() - <-ctx.Done() - return ctx.Err() + select { + case err = <-c.fuseExitCh: + return err + case <-ctx.Done(): + return ctx.Err() + } } func (c *Client) fuseMounts() []*socketMount { @@ -181,6 +201,9 @@ func (c *Client) fuseMounts() []*socketMount { func (c *Client) unmountFUSE() error { c.fuseServerMu.Lock() defer c.fuseServerMu.Unlock() + if c.fuseServer == nil { + return nil + } return c.fuseServer.Unmount() } diff --git a/internal/proxy/proxy_other_test.go b/internal/proxy/proxy_other_test.go index 12cc9db0f..77d7c21bb 100644 --- a/internal/proxy/proxy_other_test.go +++ b/internal/proxy/proxy_other_test.go @@ -13,13 +13,15 @@ // limitations under the License. //go:build !windows -// +build !windows package proxy_test import ( + "context" "os" "testing" + + "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/internal/proxy" ) var ( @@ -40,3 +42,21 @@ func verifySocketPermissions(t *testing.T, addr string) { t.Fatalf("file mode: want = %v, got = %v", 0777|os.ModeSocket, fm) } } + +func TestFuseClosesGracefully(t *testing.T) { + c, err := proxy.NewClient( + context.Background(), nil, testLogger, + &proxy.Config{ + FUSEDir: t.TempDir(), + FUSETempDir: t.TempDir(), + Token: "mytoken", + }, + nil) + if err != nil { + t.Fatal(err) + } + if err := c.Close(); err != nil { + t.Fatal(err) + } + +} diff --git a/internal/proxy/proxy_test.go b/internal/proxy/proxy_test.go index 86ceca99e..68b8e1cf6 100644 --- a/internal/proxy/proxy_test.go +++ b/internal/proxy/proxy_test.go @@ -17,9 +17,11 @@ package proxy_test import ( "context" "errors" + "fmt" "io" "net" "os" + "path" "path/filepath" "strings" "sync" @@ -55,11 +57,10 @@ func (f *fakeDialer) engineVersionAttempts() int { defer f.mu.Unlock() return f.engineVersionCount } - func (f *fakeDialer) dialedInstances() []string { f.mu.Lock() defer f.mu.Unlock() - return append([]string{}, f.instances...) + return f.instances } func (f *fakeDialer) Dial(_ context.Context, inst string, _ ...cloudsqlconn.DialOption) (net.Conn, error) { @@ -82,6 +83,8 @@ func (f *fakeDialer) EngineVersion(_ context.Context, inst string) (string, erro return "MYSQL_8_0", nil case strings.Contains(inst, "sqlserver"): return "SQLSERVER_2019_STANDARD", nil + case strings.Contains(inst, "fakeserver"): + return "", fmt.Errorf("non existing server") default: return "POSTGRES_14", nil } @@ -114,6 +117,9 @@ func createTempDir(t *testing.T) (string, func()) { func TestClientInitialization(t *testing.T) { ctx := context.Background() testDir, cleanup := createTempDir(t) + testUnixSocketPath := path.Join(testDir, "db") + testUnixSocketPathPg := path.Join(testDir, "db", ".s.PGSQL.5432") + defer cleanup() tcs := []struct { @@ -126,63 +132,63 @@ func TestClientInitialization(t *testing.T) { desc: "multiple instances", in: &proxy.Config{ Addr: "127.0.0.1", - Port: 5000, + Port: 50000, Instances: []proxy.InstanceConnConfig{ {Name: pg}, {Name: mysql}, {Name: sqlserver}, }, }, - wantTCPAddrs: []string{"127.0.0.1:5000", "127.0.0.1:5001", "127.0.0.1:5002"}, + wantTCPAddrs: []string{"127.0.0.1:50000", "127.0.0.1:50001", "127.0.0.1:50002"}, }, { desc: "with instance address", in: &proxy.Config{ Addr: "1.1.1.1", // bad address, binding shouldn't happen here. - Port: 5000, + Port: 50003, Instances: []proxy.InstanceConnConfig{ {Addr: "0.0.0.0", Name: pg}, }, }, - wantTCPAddrs: []string{"0.0.0.0:5000"}, + wantTCPAddrs: []string{"0.0.0.0:50003"}, }, { desc: "IPv6 support", in: &proxy.Config{ Addr: "::1", - Port: 5000, + Port: 50004, Instances: []proxy.InstanceConnConfig{ {Name: pg}, }, }, - wantTCPAddrs: []string{"[::1]:5000"}, + wantTCPAddrs: []string{"[::1]:50004"}, }, { desc: "with instance port", in: &proxy.Config{ Addr: "127.0.0.1", - Port: 5000, + Port: 50005, Instances: []proxy.InstanceConnConfig{ - {Name: pg, Port: 6000}, + {Name: pg, Port: 60000}, }, }, - wantTCPAddrs: []string{"127.0.0.1:6000"}, + wantTCPAddrs: []string{"127.0.0.1:60000"}, }, { desc: "with global port and instance port", in: &proxy.Config{ Addr: "127.0.0.1", - Port: 5000, + Port: 50006, Instances: []proxy.InstanceConnConfig{ {Name: pg}, - {Name: mysql, Port: 6000}, + {Name: mysql, Port: 60001}, {Name: sqlserver}, }, }, wantTCPAddrs: []string{ - "127.0.0.1:5000", - "127.0.0.1:6000", - "127.0.0.1:5001", + "127.0.0.1:50006", + "127.0.0.1:60001", + "127.0.0.1:50007", }, }, { @@ -223,7 +229,7 @@ func TestClientInitialization(t *testing.T) { desc: "with a global TCP host port and an instance Unix socket", in: &proxy.Config{ Addr: "127.0.0.1", - Port: 5000, + Port: 50008, Instances: []proxy.InstanceConnConfig{ {Name: mysql, UnixSocket: testDir}, }, @@ -238,11 +244,11 @@ func TestClientInitialization(t *testing.T) { Addr: "127.0.0.1", UnixSocket: testDir, Instances: []proxy.InstanceConnConfig{ - {Name: pg, Port: 5000}, + {Name: pg, Port: 50009}, }, }, wantTCPAddrs: []string{ - "127.0.0.1:5000", + "127.0.0.1:50009", }, }, { @@ -257,11 +263,81 @@ func TestClientInitialization(t *testing.T) { filepath.Join(testDir, pg, ".s.PGSQL.5432"), }, }, + { + desc: "with a Unix socket path per instance", + in: &proxy.Config{ + Instances: []proxy.InstanceConnConfig{ + {Name: mysql, UnixSocketPath: testUnixSocketPath}, + }, + }, + wantUnixAddrs: []string{ + filepath.Join(testUnixSocketPath), + }, + }, + { + desc: "with a Unix socket path overriding Unix socket", + in: &proxy.Config{ + UnixSocket: testDir, + Instances: []proxy.InstanceConnConfig{ + {Name: mysql, UnixSocketPath: testUnixSocketPath}, + }, + }, + wantUnixAddrs: []string{ + filepath.Join(testUnixSocketPath), + }, + }, + { + desc: "with a Unix socket path per pg instance", + in: &proxy.Config{ + Instances: []proxy.InstanceConnConfig{ + {Name: pg, UnixSocketPath: testUnixSocketPath}, + }, + }, + wantUnixAddrs: []string{ + filepath.Join(testUnixSocketPathPg), + }, + }, + { + desc: "with a Unix socket path per pg instance and explicit pg path suffix", + in: &proxy.Config{ + Instances: []proxy.InstanceConnConfig{ + {Name: pg, UnixSocketPath: testUnixSocketPathPg}, + }, + }, + wantUnixAddrs: []string{ + filepath.Join(testUnixSocketPathPg), + }, + }, + { + desc: "with Unix socket and two instances, one invalid but skipped", + in: &proxy.Config{ + UnixSocket: testDir, + Instances: []proxy.InstanceConnConfig{ + {Name: pg}, + {Name: "proj:region:fakeserver"}, + }, + SkipFailedInstanceConfig: true, + }, + wantUnixAddrs: []string{ + filepath.Join(testDir, pg, ".s.PGSQL.5432"), + }, + }, + { + desc: "with TCP port for non functional instance", + in: &proxy.Config{ + Instances: []proxy.InstanceConnConfig{ + {Name: "proj:region:fakeserver", Port: 50010}, + }, + }, + wantTCPAddrs: []string{ + "127.0.0.1:50010", + }, + }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { - c, err := proxy.NewClient(ctx, &fakeDialer{}, testLogger, tc.in) + c, err := proxy.NewClient(ctx, &fakeDialer{}, testLogger, tc.in, nil) if err != nil { t.Fatalf("want error = nil, got = %v", err) } @@ -301,26 +377,32 @@ func TestClientLimitsMaxConnections(t *testing.T) { d := &fakeDialer{} in := &proxy.Config{ Addr: "127.0.0.1", - Port: 5000, + Port: 50011, Instances: []proxy.InstanceConnConfig{ {Name: "proj:region:pg"}, }, MaxConnections: 1, } - c, err := proxy.NewClient(context.Background(), d, testLogger, in) + callbackGot := 0 + connRefuseNotify := func() { + d.mu.Lock() + defer d.mu.Unlock() + callbackGot++ + } + c, err := proxy.NewClient(context.Background(), d, testLogger, in, connRefuseNotify) if err != nil { t.Fatalf("proxy.NewClient error: %v", err) } defer c.Close() go c.Serve(context.Background(), func() {}) - conn1, err1 := net.Dial("tcp", "127.0.0.1:5000") + conn1, err1 := net.Dial("tcp", "127.0.0.1:50011") if err1 != nil { t.Fatalf("net.Dial error: %v", err1) } defer conn1.Close() - conn2, err2 := net.Dial("tcp", "127.0.0.1:5000") + conn2, err2 := net.Dial("tcp", "127.0.0.1:50011") if err2 != nil { t.Fatalf("net.Dial error: %v", err1) } @@ -347,6 +429,10 @@ func TestClientLimitsMaxConnections(t *testing.T) { if got := d.dialAttempts(); got != want { t.Fatalf("dial attempts did not match expected, want = %v, got = %v", want, got) } + + if callbackGot == 0 { + t.Fatal("connRefuseNotifyCallback is not called") + } } func tryTCPDial(t *testing.T, addr string) net.Conn { @@ -361,6 +447,8 @@ func tryTCPDial(t *testing.T, addr string) net.Conn { time.Sleep(100 * time.Millisecond) continue } + // Give the socket some time to finish the connection. + time.Sleep(100 * time.Millisecond) return conn } @@ -371,28 +459,20 @@ func tryTCPDial(t *testing.T, addr string) net.Conn { func TestClientCloseWaitsForActiveConnections(t *testing.T) { in := &proxy.Config{ Addr: "127.0.0.1", - Port: 5000, + Port: 50012, Instances: []proxy.InstanceConnConfig{ {Name: "proj:region:pg"}, }, - WaitOnClose: 5 * time.Second, + WaitOnClose: 1 * time.Second, } - c, err := proxy.NewClient(context.Background(), &fakeDialer{}, testLogger, in) + c, err := proxy.NewClient(context.Background(), &fakeDialer{}, testLogger, in, nil) if err != nil { t.Fatalf("proxy.NewClient error: %v", err) } go c.Serve(context.Background(), func() {}) - var open []net.Conn - for i := 0; i < 5; i++ { - conn := tryTCPDial(t, "127.0.0.1:5000") - open = append(open, conn) - } - defer func() { - for _, o := range open { - o.Close() - } - }() + conn := tryTCPDial(t, "127.0.0.1:50012") + defer conn.Close() if err := c.Close(); err == nil { t.Fatal("c.Close should error, got = nil") @@ -402,18 +482,18 @@ func TestClientCloseWaitsForActiveConnections(t *testing.T) { func TestClientClosesCleanly(t *testing.T) { in := &proxy.Config{ Addr: "127.0.0.1", - Port: 5000, + Port: 50013, Instances: []proxy.InstanceConnConfig{ {Name: "proj:reg:inst"}, }, } - c, err := proxy.NewClient(context.Background(), &fakeDialer{}, testLogger, in) + c, err := proxy.NewClient(context.Background(), &fakeDialer{}, testLogger, in, nil) if err != nil { t.Fatalf("proxy.NewClient error want = nil, got = %v", err) } go c.Serve(context.Background(), func() {}) - conn := tryTCPDial(t, "127.0.0.1:5000") + conn := tryTCPDial(t, "127.0.0.1:50013") _ = conn.Close() if err := c.Close(); err != nil { @@ -424,18 +504,18 @@ func TestClientClosesCleanly(t *testing.T) { func TestClosesWithError(t *testing.T) { in := &proxy.Config{ Addr: "127.0.0.1", - Port: 5000, + Port: 50014, Instances: []proxy.InstanceConnConfig{ {Name: "proj:reg:inst"}, }, } - c, err := proxy.NewClient(context.Background(), &errorDialer{}, testLogger, in) + c, err := proxy.NewClient(context.Background(), &errorDialer{}, testLogger, in, nil) if err != nil { t.Fatalf("proxy.NewClient error want = nil, got = %v", err) } go c.Serve(context.Background(), func() {}) - conn := tryTCPDial(t, "127.0.0.1:5000") + conn := tryTCPDial(t, "127.0.0.1:50014") defer conn.Close() if err = c.Close(); err == nil { @@ -485,13 +565,13 @@ func TestClientInitializationWorksRepeatedly(t *testing.T) { }, } - c, err := proxy.NewClient(ctx, &fakeDialer{}, testLogger, in) + c, err := proxy.NewClient(ctx, &fakeDialer{}, testLogger, in, nil) if err != nil { t.Fatalf("want error = nil, got = %v", err) } c.Close() - c, err = proxy.NewClient(ctx, &fakeDialer{}, testLogger, in) + c, err = proxy.NewClient(ctx, &fakeDialer{}, testLogger, in, nil) if err != nil { t.Fatalf("want error = nil, got = %v", err) } @@ -505,7 +585,7 @@ func TestClientNotifiesCallerOnServe(t *testing.T) { {Name: "proj:region:pg"}, }, } - c, err := proxy.NewClient(ctx, &fakeDialer{}, testLogger, in) + c, err := proxy.NewClient(ctx, &fakeDialer{}, testLogger, in, nil) if err != nil { t.Fatalf("want error = nil, got = %v", err) } @@ -531,14 +611,14 @@ func TestClientNotifiesCallerOnServe(t *testing.T) { func TestClientConnCount(t *testing.T) { in := &proxy.Config{ Addr: "127.0.0.1", - Port: 5000, + Port: 50015, Instances: []proxy.InstanceConnConfig{ {Name: "proj:region:pg"}, }, MaxConnections: 10, } - c, err := proxy.NewClient(context.Background(), &fakeDialer{}, testLogger, in) + c, err := proxy.NewClient(context.Background(), &fakeDialer{}, testLogger, in, nil) if err != nil { t.Fatalf("proxy.NewClient error: %v", err) } @@ -553,7 +633,7 @@ func TestClientConnCount(t *testing.T) { t.Fatalf("want 10 max connections, got = %v", gotMax) } - conn := tryTCPDial(t, "127.0.0.1:5000") + conn := tryTCPDial(t, "127.0.0.1:50015") defer conn.Close() verifyOpen := func(t *testing.T, want uint64) { @@ -573,13 +653,13 @@ func TestClientConnCount(t *testing.T) { func TestCheckConnections(t *testing.T) { in := &proxy.Config{ Addr: "127.0.0.1", - Port: 5000, + Port: 50016, Instances: []proxy.InstanceConnConfig{ {Name: "proj:region:pg"}, }, } d := &fakeDialer{} - c, err := proxy.NewClient(context.Background(), d, testLogger, in) + c, err := proxy.NewClient(context.Background(), d, testLogger, in, nil) if err != nil { t.Fatalf("proxy.NewClient error: %v", err) } @@ -600,14 +680,14 @@ func TestCheckConnections(t *testing.T) { in = &proxy.Config{ Addr: "127.0.0.1", - Port: 6000, + Port: 60002, Instances: []proxy.InstanceConnConfig{ {Name: "proj:region:pg1"}, {Name: "proj:region:pg2"}, }, } ed := &errorDialer{} - c, err = proxy.NewClient(context.Background(), ed, testLogger, in) + c, err = proxy.NewClient(context.Background(), ed, testLogger, in, nil) if err != nil { t.Fatalf("proxy.NewClient error: %v", err) } @@ -622,3 +702,132 @@ func TestCheckConnections(t *testing.T) { t.Fatalf("CheckConnections number of connections: want = %v, got = %v", want, got) } } + +func TestRunConnectionCheck(t *testing.T) { + in := &proxy.Config{ + Addr: "127.0.0.1", + Port: 50017, + Instances: []proxy.InstanceConnConfig{ + {Name: "proj:region:pg"}, + }, + RunConnectionTest: true, + } + d := &fakeDialer{} + c, err := proxy.NewClient(context.Background(), d, testLogger, in, nil) + if err != nil { + t.Fatalf("proxy.NewClient error: %v", err) + } + defer func(c *proxy.Client) { + err := c.Close() + if err != nil { + t.Log(err) + } + }(c) + go func() { + // Serve alone without any connections will still verify that the + // provided instances are reachable. + _ = c.Serve(context.Background(), func() {}) + }() + + verifyDialAttempts := func() error { + var tries int + for { + tries++ + if tries == 10 { + return errors.New("failed to verify dial tries after 10 tries") + } + if got := d.dialAttempts(); got > 0 { + return nil + } + time.Sleep(100 * time.Millisecond) + } + } + + if err := verifyDialAttempts(); err != nil { + t.Fatal(err) + } + +} + +func TestProxyInitializationWithFailedUnixSocket(t *testing.T) { + ctx := context.Background() + testDir, _ := createTempDir(t) + testUnixSocketPath := path.Join(testDir, "db") + + tcs := []struct { + desc string + in *proxy.Config + }{ + { + desc: "with unix socket for non functional instance", + in: &proxy.Config{ + Instances: []proxy.InstanceConnConfig{ + { + Name: "proj:region:fakeserver", + UnixSocketPath: testUnixSocketPath, + }, + }, + }, + }, + { + desc: "without TCP port or unix socket for non functional instance", + in: &proxy.Config{ + Instances: []proxy.InstanceConnConfig{ + {Name: "proj:region:fakeserver"}, + }, + }, + }, + } + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + _, err := proxy.NewClient(ctx, &fakeDialer{}, testLogger, tc.in, nil) + if err == nil { + t.Fatalf("want non nil error, got = %v", err) + } + }) + } +} + +func TestProxyMultiInstances(t *testing.T) { + ctx := context.Background() + testDir, _ := createTempDir(t) + testUnixSocketPath := path.Join(testDir, "db") + + tcs := []struct { + desc string + in *proxy.Config + wantSuccess bool + }{ + { + desc: "with tcp socket and unix for non functional instance", + in: &proxy.Config{ + Instances: []proxy.InstanceConnConfig{ + { + Name: "proj:region:fakeserver", + UnixSocketPath: testUnixSocketPath, + }, + {Name: mysql, Port: 3306}, + }, + }, + wantSuccess: false, + }, + { + desc: "with two tcp socket instances and conflicting ports", + in: &proxy.Config{ + Instances: []proxy.InstanceConnConfig{ + {Name: "proj:region:fakeserver", Port: 60003}, + {Name: mysql, Port: 60003}, + }, + }, + wantSuccess: false, + }, + } + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + _, err := proxy.NewClient(ctx, &fakeDialer{}, testLogger, tc.in, nil) + if tc.wantSuccess != (err == nil) { + t.Fatalf("want return = %v, got = %v", tc.wantSuccess, err == nil) + } + }) + } +} diff --git a/internal/proxy/unix.go b/internal/proxy/unix.go index 523c49451..7ecd59517 100644 --- a/internal/proxy/unix.go +++ b/internal/proxy/unix.go @@ -13,7 +13,6 @@ // limitations under the License. //go:build !windows -// +build !windows package proxy diff --git a/main.go b/main.go index 81401f9b8..4a73ee881 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !windows + package main import ( diff --git a/main_windows.go b/main_windows.go new file mode 100644 index 000000000..1dd1b3454 --- /dev/null +++ b/main_windows.go @@ -0,0 +1,127 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +package main + +import ( + "context" + "errors" + "os" + "path/filepath" + "time" + + "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/cmd" + "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/internal/log" + "golang.org/x/sys/windows/svc" + "gopkg.in/natefinch/lumberjack.v2" +) + +type windowsService struct{} + +func (m *windowsService) Execute(_ []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (bool, uint32) { + // start the service + changes <- svc.Status{State: svc.StartPending} + + // set up the log file + exePath, err := os.Executable() + if err != nil { + changes <- svc.Status{State: svc.StopPending} + return true, 101 // service specific exit code=101 + } + + logFolder := filepath.Join(filepath.Dir(exePath), "logs") + os.Mkdir(logFolder, 0644) // ignore all errors + + logFile := &lumberjack.Logger{ + Filename: filepath.Join(logFolder, "cloud-sql-proxy.log"), + MaxSize: 50, // megabytes + MaxBackups: 10, + MaxAge: 30, //days + } + + logger := log.NewStdLogger(logFile, logFile) + logger.Infof("Starting...") + + // start the main command + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + app := cmd.NewCommand(cmd.WithLogger(logger)) + + cmdErrCh := make(chan error, 1) + go func() { + cmdErrCh <- app.ExecuteContext(ctx) + }() + + // now running + changes <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown} + + var cmdErr error + +loop: + for { + select { + case err := <-cmdErrCh: + cmdErr = err + break loop + + case c := <-r: + switch c.Cmd { + case svc.Interrogate: + changes <- c.CurrentStatus + // testing deadlock from https://code.google.com/archive/p/winsvc/issues/4 + time.Sleep(100 * time.Millisecond) + changes <- c.CurrentStatus + + case svc.Stop, svc.Shutdown: + cancel() + + default: + logger.Errorf("unexpected control request #%d", c) + } + } + } + + // start shutting down + logger.Infof("Stopping...") + + changes <- svc.Status{State: svc.StopPending} + + if cmdErr != nil && errors.Is(cmdErr, context.Canceled) { + logger.Errorf("Unexpected error: %v", cmdErr) + return true, 2 + } + + return false, 0 +} + +func main() { + // determine if running as a windows service + inService, err := svc.IsWindowsService() + if err != nil { + os.Exit(99) // failed to determine service status + } + + // running as service? + if inService { + err := svc.Run("cloud-sql-proxy", &windowsService{}) + if err != nil { + os.Exit(100) // failed to execute service + } + return + } + + // run as commandline + cmd.Execute() +} diff --git a/migration-guide.md b/migration-guide.md index 1010a724e..1bc0dfc9f 100644 --- a/migration-guide.md +++ b/migration-guide.md @@ -1,6 +1,6 @@ # Migrating from v1 to v2 -The Cloud SQL Auth proxy v2 CLI interface maintains a close match to the v1 +The Cloud SQL Auth Proxy v2 CLI interface maintains a close match to the v1 interface. Migrating to v2 will require minimal changes. Below are a number of examples of v1 vs v2 invocations covering the most common uses. See [Flag Changes](#flag-changes) for details. @@ -8,13 +8,59 @@ of examples of v1 vs v2 invocations covering the most common uses. See All the examples below use `` as a placeholder for your instance connection name, e.g., `my-cool-project:us-central1:my-db`. +## Container Image Name Change + +As part of releasing a v2, we have updated the image name to be more descriptive. +Compare: + +``` +# v1 +gcr.io/cloudsql-docker/gce-proxy +``` + +vs + +``` +# v2 +gcr.io/cloud-sql-connectors/cloud-sql-proxy +``` + +To update to the v2 container, make sure to update the image name. + +## Behavior Differences + +In v1, when a client connected, the Proxy would first try to use a public IP +and then attempt to use a private IP. In v2, the Proxy now defaults to public +IP without trying private IP. If you want to use private IP, you must pass +either the `--private-ip` flag or the query parameter. See the README for details. + +In some cases, the v1 behavior may be preferable. Use the `--auto-ip` flag to +mimic v1 behavior. We generally recommend using deterministic IP address selection, +but recognize in some legacy environments `--auto-ip` may be necessary. + +## Executable Name Change + +Note that the name of the executable has changed, using hyphens rather than underscores: + +```shell +# v1 +./cloud_sql_proxy +``` + +vs + +```shell +# v2 +./cloud-sql-proxy +``` + ## Sample Invocations ### Listen on TCP socket ```shell # v1 -./cloud-sql-proxy -instances==tcp:5432 +./cloud_sql_proxy -instances==tcp:5432 # v2 # Using automatic database port selection (MySQL 3306, Postgres 5432, SQL Server 1433) @@ -25,7 +71,7 @@ your instance connection name, e.g., `my-cool-project:us-central1:my-db`. ```shell # v1 -./cloud-sql-proxy -dir /cloudsql -instances= +./cloud_sql_proxy -dir /cloudsql -instances= # v2 ./cloud-sql-proxy --unix-socket /cloudsql @@ -35,7 +81,7 @@ your instance connection name, e.g., `my-cool-project:us-central1:my-db`. ```shell # v1 -./cloud-sql-proxy -instances==tcp:5000,=tcp:5001 +./cloud_sql_proxy -instances==tcp:5000,=tcp:5001 # v2 # starts listener on port 5000, increments for additional listeners @@ -46,7 +92,7 @@ your instance connection name, e.g., `my-cool-project:us-central1:my-db`. ```shell # v1 -./cloud-sql-proxy -instances==tcp:6000,=tcp:7000 +./cloud_sql_proxy -instances==tcp:6000,=tcp:7000 # v2 ./cloud-sql-proxy '?port=6000' '?port=7000' @@ -56,12 +102,40 @@ your instance connection name, e.g., `my-cool-project:us-central1:my-db`. ```shell # v1 -./cloud-sql-proxy -instances==tcp:0.0.0.0:6000 +./cloud_sql_proxy -instances==tcp:0.0.0.0:6000 # v2 ./cloud-sql-proxy --address 0.0.0.0 --port 6000 ``` +## Environment variable changes + +In v1 it was possible to do this: + +``` shell +export INSTANCES="=tcp:3306,=tcp:5432" + +./cloud_sql_proxy +``` + +In v2, we've significantly expanded the support for environment variables. +All flags can be set with an environment variable including instance connection names. + +For example, in v2 this is possible: + +``` shell +export CSQL_PROXY_INSTANCE_CONNECTION_NAME_0="?port=3306" +export CSQL_PROXY_INSTANCE_CONNECTION_NAME_1="?port=5432" + +export CSQL_PROXY_AUTO_IAM_AUTHN=true + +./cloud-sql-proxy +``` + +See the [help message][] for more details. + +[help message]: https://github.com/GoogleCloudPlatform/cloud-sql-proxy/blob/10bec27e4d44c14fe9e68f25fef6c373324e8bab/cmd/root.go#L240-L264 + ## Flag Changes The following table lists in alphabetical order v1 flags and their v2 version. @@ -70,29 +144,29 @@ The following table lists in alphabetical order v1 flags and their v2 version. - ❌: Not supported in V2 - 🤔: Unplanned, but has open feature request -| v1 | v2 | Notes | -| --------------------------- | --------------------- | ------------------------------------------------------------------------------------ | -| check_region | ❌ | | -| credential_file | credentials-file | | -| dir | unix-socket | | -| enable_iam_login | auto-iam-authn | | -| fd_rlimit | 🤔 | [Feature Request](https://github.com/GoogleCloudPlatform/cloudsql-proxy/issues/1258) | -| fuse | fuse | | -| fuse_tmp | fuse-temp-dir | | -| health_check_port | http-port | | -| host | sqladmin-api-endpoint | | -| instances_metadata | 🤔 | [Feature Request](https://github.com/GoogleCloudPlatform/cloudsql-proxy/issues/1259) | -| ip_address_types | private-ip | Defaults to public | -| log_debug_stdout | ❌ | v2 logs to stdout, errors to stderr by default | -| max_connections | max-connections | | -| projects | ❌ | v2 prefers explicit connection configuration to avoid user error | -| quiet | quiet | quiet disables all logging except errors | -| quota_project | quota-project | | -| refresh_config_throttle | ❌ | | -| skip_failed_instance_config | ❌ | | -| structured_logs | structured-logs | | -| term_timeout | max-sigterm-delay | | -| token | token | | -| use_http_health_check | health-check | | -| verbose | ❌ | | -| version | version | | +| v1 | v2 | Notes | +| --------------------------- | --------------------------- | ------------------------------------------------------------------------------------ | +| check_region | ❌ | | +| credential_file | credentials-file | | +| dir | unix-socket | | +| enable_iam_login | auto-iam-authn | | +| fd_rlimit | 🤔 | [Feature Request](https://github.com/GoogleCloudPlatform/cloudsql-proxy/issues/1258) | +| fuse | fuse | | +| fuse_tmp | fuse-temp-dir | | +| health_check_port | http-port | Use --http-address=0.0.0.0 when using a health check in Kubernetes | +| host | sqladmin-api-endpoint | | +| instances_metadata | 🤔 | [Feature Request](https://github.com/GoogleCloudPlatform/cloudsql-proxy/issues/1259) | +| ip_address_types | private-ip | Defaults to public. To connect to a private IP, you must add the --private-ip flag | +| log_debug_stdout | ❌ | v2 logs to stdout, errors to stderr by default | +| max_connections | max-connections | | +| projects | ❌ | v2 prefers explicit connection configuration to avoid user error | +| quiet | quiet | quiet disables all logging except errors | +| quota_project | quota-project | | +| refresh_config_throttle | ❌ | | +| skip_failed_instance_config | skip-failed-instance-config | | +| structured_logs | structured-logs | | +| term_timeout | max-sigterm-delay | | +| token | token | | +| use_http_health_check | health-check | | +| verbose | ❌ | | +| version | version | | diff --git a/tests/common_test.go b/tests/common_test.go index 71c5a2c60..ae5333d97 100644 --- a/tests/common_test.go +++ b/tests/common_test.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package tests contains end to end tests meant to verify the Cloud SQL Auth proxy -// works as expected when executed as a binary. +// Package tests contains end to end tests meant to verify the Cloud SQL Auth +// proxy works as expected when executed as a binary. // // Required flags: // @@ -22,7 +22,6 @@ package tests import ( "bufio" - "bytes" "context" "errors" "flag" @@ -43,7 +42,7 @@ var ( ) ) -// ProxyExec represents an execution of the Cloud SQL proxy. +// ProxyExec represents an execution of the Cloud SQL Auth Proxy. type ProxyExec struct { Out io.ReadCloser @@ -118,49 +117,32 @@ func (p *ProxyExec) Close() { } } -// WaitForServe waits until the proxy ready to serve traffic. Returns any output from -// the proxy while starting or any errors experienced before the proxy was ready to -// server. -func (p *ProxyExec) WaitForServe(ctx context.Context) (output string, err error) { - // Watch for the "Ready for new connections" to indicate the proxy is listening - buf, in, errCh := new(bytes.Buffer), bufio.NewReader(p.Out), make(chan error, 1) - go func() { - defer close(errCh) - for { - // if ctx is finished, stop processing - select { - case <-ctx.Done(): - return - default: - } - s, err := in.ReadString('\n') +// WaitForServe waits until the proxy ready to serve traffic by waiting for a +// known log message (i.e. "ready for new connections"). Returns any output +// from the proxy while starting or any errors experienced before the proxy was +// ready to server. +func (p *ProxyExec) WaitForServe(ctx context.Context) (string, error) { + in := bufio.NewReader(p.Out) + for { + select { + case <-ctx.Done(): + // dump all output and return it as an error + all, err := io.ReadAll(in) if err != nil { - errCh <- err - return - } - if _, err = buf.WriteString(s); err != nil { - errCh <- err - return - } - // Check for an unrecognized flag - if strings.Contains(s, "Error") { - errCh <- errors.New(s) - return - } - if strings.Contains(s, "ready for new connections") { - errCh <- nil - return + return "", err } + return "", errors.New(string(all)) + default: } - }() - // Wait for either the background thread of the context to complete - select { - case <-ctx.Done(): - return buf.String(), ctx.Err() - case err := <-errCh: + s, err := in.ReadString('\n') if err != nil { - return buf.String(), err + return "", err + } + if strings.Contains(s, "Error") || strings.Contains(s, "error") { + return "", errors.New(s) + } + if strings.Contains(s, "ready for new connections") { + return s, nil } } - return buf.String(), nil } diff --git a/tests/connection_test.go b/tests/connection_test.go index de38de4ab..84f7e7bd1 100644 --- a/tests/connection_test.go +++ b/tests/connection_test.go @@ -43,6 +43,9 @@ func removeAuthEnvVar(t *testing.T) (*oauth2.Token, string, func()) { if err != nil { t.Errorf("failed to get token: %v", err) } + if *ipType != "public" { + return tok, "", func() {} + } path, ok := os.LookupEnv("GOOGLE_APPLICATION_CREDENTIALS") if !ok { t.Fatalf("GOOGLE_APPLICATION_CREDENTIALS was not set in the environment") @@ -66,9 +69,7 @@ func keyfile(t *testing.T) string { } return string(creds) } - -// proxyConnTest is a test helper to verify the proxy works with a basic connectivity test. -func proxyConnTest(t *testing.T, args []string, driver, dsn string) { +func proxyConnTestWithReady(t *testing.T, args []string, driver, dsn string, ready func() error) { ctx, cancel := context.WithTimeout(context.Background(), connTestTimeout) defer cancel() // Start the proxy @@ -81,6 +82,9 @@ func proxyConnTest(t *testing.T, args []string, driver, dsn string) { if err != nil { t.Fatalf("unable to verify proxy was serving: %s \n %s", err, output) } + if err := ready(); err != nil { + t.Fatalf("proxy was not ready: %v", err) + } // Connect to the instance db, err := sql.Open(driver, dsn) @@ -94,9 +98,15 @@ func proxyConnTest(t *testing.T, args []string, driver, dsn string) { } } +// proxyConnTest is a test helper to verify the proxy works with a basic connectivity test. +func proxyConnTest(t *testing.T, args []string, driver, dsn string) { + proxyConnTestWithReady(t, args, driver, dsn, func() error { return nil }) +} + // testHealthCheck verifies that when a proxy client serves the given instance, // the readiness endpoint serves http.StatusOK. func testHealthCheck(t *testing.T, connName string) { + t.Helper() ctx, cancel := context.WithTimeout(context.Background(), connTestTimeout) defer cancel() diff --git a/tests/fuse_test.go b/tests/fuse_test.go new file mode 100644 index 000000000..a1b57368f --- /dev/null +++ b/tests/fuse_test.go @@ -0,0 +1,85 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +//go:build !windows && !darwin + +package tests + +import ( + "fmt" + "os" + "testing" + "time" + + "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/internal/proxy" +) + +func TestPostgresFUSEConnect(t *testing.T) { + if v := os.Getenv("IP_TYPE"); v == "private" || v == "psc" { + t.Skipf("skipping test because IP_TYPE is set to %v", v) + } + if testing.Short() { + t.Skip("skipping Postgres integration tests") + } + tmpDir, cleanup := createTempDir(t) + defer cleanup() + + host := proxy.UnixAddress(tmpDir, *postgresConnName) + dsn := fmt.Sprintf( + "host=%s user=%s password=%s database=%s sslmode=disable", + host, *postgresUser, *postgresPass, *postgresDB, + ) + testFUSE(t, tmpDir, host, dsn) +} + +func testFUSE(t *testing.T, tmpDir, host string, dsn string) { + tmpDir2, cleanup2 := createTempDir(t) + defer cleanup2() + + waitForFUSE := func() error { + var err error + for i := 0; i < 10; i++ { + _, err = os.Stat(host) + if err == nil { + return nil + } + time.Sleep(500 * time.Millisecond) + } + return fmt.Errorf("failed to find FUSE mounted Unix socket: %v", err) + } + + tcs := []struct { + desc string + dbUser string + args []string + }{ + { + desc: "using default fuse", + args: []string{fmt.Sprintf("--fuse=%s", tmpDir), fmt.Sprintf("--fuse-tmp-dir=%s", tmpDir2)}, + }, + { + desc: "using fuse with auto-iam-authn", + args: []string{fmt.Sprintf("--fuse=%s", tmpDir), "--auto-iam-authn"}, + }, + } + + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + proxyConnTestWithReady(t, tc.args, "pgx", dsn, waitForFUSE) + // given the kernel some time to unmount the fuse + time.Sleep(100 * time.Millisecond) + }) + } + +} diff --git a/tests/mysql_test.go b/tests/mysql_test.go index d3ec74cbf..8b7f48a31 100644 --- a/tests/mysql_test.go +++ b/tests/mysql_test.go @@ -25,10 +25,18 @@ import ( ) var ( - mysqlConnName = flag.String("mysql_conn_name", os.Getenv("MYSQL_CONNECTION_NAME"), "Cloud SQL MYSQL instance connection name, in the form of 'project:region:instance'.") - mysqlUser = flag.String("mysql_user", os.Getenv("MYSQL_USER"), "Name of database user.") - mysqlPass = flag.String("mysql_pass", os.Getenv("MYSQL_PASS"), "Password for the database user; be careful when entering a password on the command line (it may go into your terminal's history).") - mysqlDB = flag.String("mysql_db", os.Getenv("MYSQL_DB"), "Name of the database to connect to.") + mysqlConnName = flag.String("mysql_conn_name", os.Getenv("MYSQL_CONNECTION_NAME"), "Cloud SQL MYSQL instance connection name, in the form of 'project:region:instance'.") + mysqlUser = flag.String("mysql_user", os.Getenv("MYSQL_USER"), "Name of database user.") + mysqlPass = flag.String("mysql_pass", os.Getenv("MYSQL_PASS"), "Password for the database user; be careful when entering a password on the command line (it may go into your terminal's history).") + mysqlDB = flag.String("mysql_db", os.Getenv("MYSQL_DB"), "Name of the database to connect to.") + mysqlMCPConnName = flag.String("mysql_mcp_conn_name", os.Getenv("MYSQL_MCP_CONNECTION_NAME"), "Cloud SQL MCP MYSQL instance connection name, in the form of 'project:region:instance'.") + mysqlMCPPass = flag.String("mysql_mcp_pass", os.Getenv("MYSQL_MCP_PASS"), "Password for the database user; be careful when entering a password on the command line (it may go into your terminal's history).") + ipType = flag.String("ip_type", func() string { + if v := os.Getenv("IP_TYPE"); v != "" { + return v + } + return "public" + }(), "IP type of the instance to connect to, can be public, private or psc") ) func requireMySQLVars(t *testing.T) { @@ -41,6 +49,10 @@ func requireMySQLVars(t *testing.T) { t.Fatal("'mysql_pass' not set") case *mysqlDB: t.Fatal("'mysql_db' not set") + case *mysqlMCPConnName: + t.Fatal("'mysql_mcp_conn_name' not set") + case *mysqlMCPPass: + t.Fatal("'mysql_mcp_pass' not set") } } @@ -56,12 +68,29 @@ func mysqlDSN() string { return cfg.FormatDSN() } +// AddIPTypeFlag appends the correct flag based on the ipType variable. +func AddIPTypeFlag(args []string) []string { + switch *ipType { + case "private": + return append(args, "--private-ip") + case "psc": + return append(args, "--psc") + default: + return args + } +} + func TestMySQLTCP(t *testing.T) { if testing.Short() { t.Skip("skipping MySQL integration tests") } requireMySQLVars(t) - proxyConnTest(t, []string{*mysqlConnName}, "mysql", mysqlDSN()) + // Prepare the initial arguments + args := []string{*mysqlConnName} + // Add the IP type flag using the helper + args = AddIPTypeFlag(args) + // Run the test + proxyConnTest(t, args, "mysql", mysqlDSN()) } func TestMySQLUnix(t *testing.T) { @@ -82,8 +111,38 @@ func TestMySQLUnix(t *testing.T) { Addr: proxy.UnixAddress(tmpDir, *mysqlConnName), Net: "unix", } - proxyConnTest(t, - []string{"--unix-socket", tmpDir, *mysqlConnName}, "mysql", cfg.FormatDSN()) + // Prepare the initial arguments + args := []string{"--unix-socket", tmpDir, *mysqlConnName} + // Add the IP type flag using the helper + args = AddIPTypeFlag(args) + // Run the test + proxyConnTest(t, args, "mysql", cfg.FormatDSN()) +} + +func TestMySQLMCPUnix(t *testing.T) { + if testing.Short() { + t.Skip("skipping MySQL integration tests") + } + requireMySQLVars(t) + tmpDir, cleanup := createTempDir(t) + defer cleanup() + + cfg := mysql.Config{ + User: *mysqlUser, + Passwd: *mysqlMCPPass, + DBName: *mysqlDB, + AllowNativePasswords: true, + // re-use utility function to determine the Unix address in a + // Windows-friendly way. + Addr: proxy.UnixAddress(tmpDir, *mysqlMCPConnName), + Net: "unix", + } + // Prepare the initial arguments + args := []string{"--unix-socket", tmpDir, *mysqlMCPConnName} + // Add the IP type flag using the helper + args = AddIPTypeFlag(args) + // Run the test + proxyConnTest(t, args, "mysql", cfg.FormatDSN()) } func TestMySQLImpersonation(t *testing.T) { @@ -92,10 +151,15 @@ func TestMySQLImpersonation(t *testing.T) { } requireMySQLVars(t) - proxyConnTest(t, []string{ + // Prepare the initial arguments + args := []string{ "--impersonate-service-account", *impersonatedUser, - *mysqlConnName}, - "mysql", mysqlDSN()) + *mysqlConnName, + } + // Add the IP type flag using the helper + args = AddIPTypeFlag(args) + // Run the test + proxyConnTest(t, args, "mysql", mysqlDSN()) } func TestMySQLAuthentication(t *testing.T) { @@ -104,7 +168,10 @@ func TestMySQLAuthentication(t *testing.T) { } requireMySQLVars(t) - creds := keyfile(t) + var creds string + if *ipType == "public" { + creds = keyfile(t) + } tok, path, cleanup := removeAuthEnvVar(t) defer cleanup() @@ -123,32 +190,42 @@ func TestMySQLAuthentication(t *testing.T) { "--impersonate-service-account", *impersonatedUser, *mysqlConnName}, }, - { - desc: "with credentials file", - args: []string{"--credentials-file", path, *mysqlConnName}, - }, - { - desc: "with credentials file and impersonation", - args: []string{ - "--credentials-file", path, - "--impersonate-service-account", *impersonatedUser, - *mysqlConnName}, - }, - { - desc: "with credentials JSON", - args: []string{"--json-credentials", string(creds), *mysqlConnName}, - }, - { - desc: "with credentials JSON and impersonation", - args: []string{ - "--json-credentials", string(creds), - "--impersonate-service-account", *impersonatedUser, - *mysqlConnName}, - }, + } + if *ipType == "public" { + additionaTcs := []struct { + desc string + args []string + }{ + { + desc: "with credentials file", + args: []string{"--credentials-file", path, *mysqlConnName}, + }, + { + desc: "with credentials file and impersonation", + args: []string{ + "--credentials-file", path, + "--impersonate-service-account", *impersonatedUser, + *mysqlConnName, + }, + }, + { + desc: "with credentials JSON", + args: []string{"--json-credentials", string(creds), *mysqlConnName}, + }, + { + desc: "with credentials JSON and impersonation", + args: []string{ + "--json-credentials", string(creds), + "--impersonate-service-account", *impersonatedUser, + *mysqlConnName, + }, + }, + } + tcs = append(tcs, additionaTcs...) } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { - proxyConnTest(t, tc.args, "mysql", mysqlDSN()) + proxyConnTest(t, AddIPTypeFlag(tc.args), "mysql", mysqlDSN()) }) } } @@ -157,6 +234,9 @@ func TestMySQLGcloudAuth(t *testing.T) { if testing.Short() { t.Skip("skipping MySQL integration tests") } + if v := os.Getenv("IP_TYPE"); v == "private" || v == "psc" { + t.Skipf("skipping test because IP_TYPE is set to %v", v) + } requireMySQLVars(t) tcs := []struct { @@ -177,11 +257,14 @@ func TestMySQLGcloudAuth(t *testing.T) { } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { - proxyConnTest(t, tc.args, "mysql", mysqlDSN()) + proxyConnTest(t, AddIPTypeFlag(tc.args), "mysql", mysqlDSN()) }) } } func TestMySQLHealthCheck(t *testing.T) { + if testing.Short() { + t.Skip("skipping MySQL integration tests") + } testHealthCheck(t, *mysqlConnName) } diff --git a/tests/postgres_test.go b/tests/postgres_test.go index 891e7ef6c..fe6400d02 100644 --- a/tests/postgres_test.go +++ b/tests/postgres_test.go @@ -23,15 +23,20 @@ import ( "testing" "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/internal/proxy" - _ "github.com/jackc/pgx/v4/stdlib" + _ "github.com/jackc/pgx/v5/stdlib" ) var ( - postgresConnName = flag.String("postgres_conn_name", os.Getenv("POSTGRES_CONNECTION_NAME"), "Cloud SQL Postgres instance connection name, in the form of 'project:region:instance'.") - postgresUser = flag.String("postgres_user", os.Getenv("POSTGRES_USER"), "Name of database user.") - postgresPass = flag.String("postgres_pass", os.Getenv("POSTGRES_PASS"), "Password for the database user; be careful when entering a password on the command line (it may go into your terminal's history).") - postgresDB = flag.String("postgres_db", os.Getenv("POSTGRES_DB"), "Name of the database to connect to.") - postgresIAMUser = flag.String("postgres_user_iam", os.Getenv("POSTGRES_USER_IAM"), "Name of database user configured with IAM DB Authentication.") + postgresConnName = flag.String("postgres_conn_name", os.Getenv("POSTGRES_CONNECTION_NAME"), "Cloud SQL Postgres instance connection name, in the form of 'project:region:instance'.") + postgresUser = flag.String("postgres_user", os.Getenv("POSTGRES_USER"), "Name of database user.") + postgresPass = flag.String("postgres_pass", os.Getenv("POSTGRES_PASS"), "Password for the database user; be careful when entering a password on the command line (it may go into your terminal's history).") + postgresDB = flag.String("postgres_db", os.Getenv("POSTGRES_DB"), "Name of the database to connect to.") + postgresIAMUser = flag.String("postgres_user_iam", os.Getenv("POSTGRES_USER_IAM"), "Name of database user configured with IAM DB Authentication.") + postgresMCPConnName = flag.String("postgres_mcp_conn_name", os.Getenv("POSTGRES_MCP_CONNECTION_NAME"), "Cloud SQL Postgres instance connection name for a managed instance, in the form of 'project:region:instance'.") + postgresMCPPass = flag.String("postgres_mcp_pass", os.Getenv("POSTGRES_MCP_PASS"), "Password for the managed instance database user; be careful when entering a password on the command line (it may go into your terminal's history).") + postgresCustomerCASConnName = flag.String("postgres_customer_cas_conn_name", os.Getenv("POSTGRES_CUSTOMER_CAS_CONNECTION_NAME"), "Cloud SQL Postgres instance connection name for a customer CAS enabled instance, in the form of 'project:region:instance'.") + postgresCustomerCASPass = flag.String("postgres_customer_cas_pass", os.Getenv("POSTGRES_CUSTOMER_CAS_PASS"), "Password for the customer CAS instance database user; be careful when entering a password on the command line (it may go into your terminal's history).") + postgresCustomerCASDomain = flag.String("postgres_customer_cas_domain", os.Getenv("POSTGRES_CUSTOMER_CAS_DOMAIN_NAME"), "Valid DNS domain name for the customer CAS instance.") ) func requirePostgresVars(t *testing.T) { @@ -57,8 +62,12 @@ func TestPostgresTCP(t *testing.T) { t.Skip("skipping Postgres integration tests") } requirePostgresVars(t) - - proxyConnTest(t, []string{*postgresConnName}, "pgx", postgresDSN()) + // Prepare the initial arguments + args := []string{*postgresConnName} + // Add the IP type flag using the helper + args = AddIPTypeFlag(args) + // Run the test + proxyConnTest(t, args, "pgx", postgresDSN()) } func TestPostgresUnix(t *testing.T) { @@ -75,8 +84,34 @@ func TestPostgresUnix(t *testing.T) { proxy.UnixAddress(tmpDir, *postgresConnName), *postgresUser, *postgresPass, *postgresDB) - proxyConnTest(t, - []string{"--unix-socket", tmpDir, *postgresConnName}, "pgx", dsn) + // Prepare the initial arguments + args := []string{"--unix-socket", tmpDir, *postgresConnName} + // Add the IP type flag using the helper + args = AddIPTypeFlag(args) + // Run the test + proxyConnTest(t, args, "pgx", dsn) +} + +func TestPostgresMCPUnix(t *testing.T) { + if testing.Short() { + t.Skip("skipping Postgres integration tests") + } + requirePostgresVars(t) + tmpDir, cleanup := createTempDir(t) + defer cleanup() + + dsn := fmt.Sprintf("host=%s user=%s password=%s database=%s sslmode=disable", + // re-use utility function to determine the Unix address in a + // Windows-friendly way. + proxy.UnixAddress(tmpDir, *postgresMCPConnName), + *postgresUser, *postgresMCPPass, *postgresDB) + + // Prepare the initial arguments + args := []string{"--unix-socket", tmpDir, *postgresMCPConnName} + // Add the IP type flag using the helper + args = AddIPTypeFlag(args) + // Run the test + proxyConnTest(t, args, "pgx", dsn) } func createTempDir(t *testing.T) (string, func()) { @@ -96,11 +131,15 @@ func TestPostgresImpersonation(t *testing.T) { t.Skip("skipping Postgres integration tests") } requirePostgresVars(t) - - proxyConnTest(t, []string{ + // Prepare the initial arguments + args := []string{ "--impersonate-service-account", *impersonatedUser, - *postgresConnName}, - "pgx", postgresDSN()) + *postgresConnName, + } + // Add the IP type flag using the helper + args = AddIPTypeFlag(args) + // Run the test + proxyConnTest(t, args, "pgx", postgresDSN()) } func TestPostgresAuthentication(t *testing.T) { @@ -109,7 +148,10 @@ func TestPostgresAuthentication(t *testing.T) { } requirePostgresVars(t) - creds := keyfile(t) + var creds string + if *ipType == "public" { + creds = keyfile(t) + } tok, path, cleanup := removeAuthEnvVar(t) defer cleanup() @@ -128,32 +170,42 @@ func TestPostgresAuthentication(t *testing.T) { "--impersonate-service-account", *impersonatedUser, *postgresConnName}, }, - { - desc: "with credentials file", - args: []string{"--credentials-file", path, *postgresConnName}, - }, - { - desc: "with credentials file and impersonation", - args: []string{ - "--credentials-file", path, - "--impersonate-service-account", *impersonatedUser, - *postgresConnName}, - }, - { - desc: "with credentials JSON", - args: []string{"--json-credentials", string(creds), *postgresConnName}, - }, - { - desc: "with credentials JSON and impersonation", - args: []string{ - "--json-credentials", string(creds), - "--impersonate-service-account", *impersonatedUser, - *postgresConnName}, - }, + } + if *ipType == "public" { + additionalTcs := []struct { + desc string + args []string + }{ + { + desc: "with credentials file", + args: []string{"--credentials-file", path, *postgresConnName}, + }, + { + desc: "with credentials file and impersonation", + args: []string{ + "--credentials-file", path, + "--impersonate-service-account", *impersonatedUser, + *postgresConnName, + }, + }, + { + desc: "with credentials JSON", + args: []string{"--json-credentials", string(creds), *postgresConnName}, + }, + { + desc: "with credentials JSON and impersonation", + args: []string{ + "--json-credentials", string(creds), + "--impersonate-service-account", *impersonatedUser, + *postgresConnName, + }, + }, + } + tcs = append(tcs, additionalTcs...) } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { - proxyConnTest(t, tc.args, "pgx", postgresDSN()) + proxyConnTest(t, AddIPTypeFlag(tc.args), "pgx", postgresDSN()) }) } } @@ -162,6 +214,9 @@ func TestPostgresGcloudAuth(t *testing.T) { if testing.Short() { t.Skip("skipping Postgres integration tests") } + if v := os.Getenv("IP_TYPE"); v == "private" || v == "psc" { + t.Skipf("skipping test because IP_TYPE is set to %v", v) + } requirePostgresVars(t) tcs := []struct { @@ -182,7 +237,7 @@ func TestPostgresGcloudAuth(t *testing.T) { } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { - proxyConnTest(t, tc.args, "pgx", postgresDSN()) + proxyConnTest(t, AddIPTypeFlag(tc.args), "pgx", postgresDSN()) }) } @@ -225,14 +280,66 @@ func TestPostgresIAMDBAuthn(t *testing.T) { dsn: fmt.Sprintf("host=localhost user=%s database=%s sslmode=disable", impersonatedIAMUser, *postgresDB), }, + { + desc: "using impersonation with query param", + args: []string{ + "--impersonate-service-account", *impersonatedUser, + fmt.Sprintf("%s?auto-iam-authn=true", *postgresConnName)}, + dsn: fmt.Sprintf("host=localhost user=%s password=password database=%s sslmode=disable", + impersonatedIAMUser, *postgresDB), + }, + } + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + proxyConnTest(t, AddIPTypeFlag(tc.args), "pgx", tc.dsn) + }) + } +} + +func TestPostgresCustomerCAS(t *testing.T) { + if testing.Short() { + t.Skip("skipping Postgres integration tests") + } + requirePostgresVars(t) + if *postgresCustomerCASConnName == "" { + t.Fatal("'postgres_customer_cas_conn_name' not set") + } + if *postgresCustomerCASPass == "" { + t.Fatal("'postgres_customer_cas_pass' not set") + } + if *postgresCustomerCASDomain == "" { + t.Fatal("'postgres_customer_cas_domain' not set") + } + + defaultDSN := fmt.Sprintf("host=localhost user=%s password=%s database=%s sslmode=disable", + *postgresUser, *postgresCustomerCASPass, *postgresDB) + + tcs := []struct { + desc string + dsn string + args []string + }{ + { + desc: "using customer CAS default", + args: []string{*postgresCustomerCASConnName}, + dsn: defaultDSN, + }, + { + desc: "using valid domain name", + args: []string{*postgresCustomerCASDomain}, + dsn: defaultDSN, + }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { - proxyConnTest(t, tc.args, "pgx", tc.dsn) + proxyConnTest(t, AddIPTypeFlag(tc.args), "pgx", tc.dsn) }) } } func TestPostgresHealthCheck(t *testing.T) { + if testing.Short() { + t.Skip("skipping Postgres integration tests") + } testHealthCheck(t, *postgresConnName) } diff --git a/tests/sqlserver_test.go b/tests/sqlserver_test.go index 6e82f7507..921dff6b1 100644 --- a/tests/sqlserver_test.go +++ b/tests/sqlserver_test.go @@ -54,8 +54,12 @@ func TestSQLServerTCP(t *testing.T) { t.Skip("skipping SQL Server integration tests") } requireSQLServerVars(t) - - proxyConnTest(t, []string{*sqlserverConnName}, "sqlserver", sqlserverDSN()) + // Prepare the initial arguments + args := []string{*sqlserverConnName} + // Add the IP type flag using the helper + args = AddIPTypeFlag(args) + // Run the test + proxyConnTest(t, args, "sqlserver", sqlserverDSN()) } func TestSQLServerImpersonation(t *testing.T) { @@ -63,11 +67,15 @@ func TestSQLServerImpersonation(t *testing.T) { t.Skip("skipping SQL Server integration tests") } requireSQLServerVars(t) - - proxyConnTest(t, []string{ + // Prepare the initial arguments + args := []string{ "--impersonate-service-account", *impersonatedUser, - *sqlserverConnName}, - "sqlserver", sqlserverDSN()) + *sqlserverConnName, + } + // Add the IP type flag using the helper + args = AddIPTypeFlag(args) + // Run the test + proxyConnTest(t, args, "sqlserver", sqlserverDSN()) } func TestSQLServerAuthentication(t *testing.T) { @@ -76,7 +84,10 @@ func TestSQLServerAuthentication(t *testing.T) { } requireSQLServerVars(t) - creds := keyfile(t) + var creds string + if *ipType == "public" { + creds = keyfile(t) + } tok, path, cleanup := removeAuthEnvVar(t) defer cleanup() @@ -95,32 +106,42 @@ func TestSQLServerAuthentication(t *testing.T) { "--impersonate-service-account", *impersonatedUser, *sqlserverConnName}, }, - { - desc: "with credentials file", - args: []string{"--credentials-file", path, *sqlserverConnName}, - }, - { - desc: "with credentials file and impersonation", - args: []string{ - "--credentials-file", path, - "--impersonate-service-account", *impersonatedUser, - *sqlserverConnName}, - }, - { - desc: "with credentials JSON", - args: []string{"--json-credentials", string(creds), *sqlserverConnName}, - }, - { - desc: "with credentials JSON and impersonation", - args: []string{ - "--json-credentials", string(creds), - "--impersonate-service-account", *impersonatedUser, - *sqlserverConnName}, - }, + } + if *ipType == "public" { + additionaTcs := []struct { + desc string + args []string + }{ + { + desc: "with credentials file", + args: []string{"--credentials-file", path, *sqlserverConnName}, + }, + { + desc: "with credentials file and impersonation", + args: []string{ + "--credentials-file", path, + "--impersonate-service-account", *impersonatedUser, + *sqlserverConnName, + }, + }, + { + desc: "with credentials JSON", + args: []string{"--json-credentials", string(creds), *sqlserverConnName}, + }, + { + desc: "with credentials JSON and impersonation", + args: []string{ + "--json-credentials", string(creds), + "--impersonate-service-account", *impersonatedUser, + *sqlserverConnName, + }, + }, + } + tcs = append(tcs, additionaTcs...) } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { - proxyConnTest(t, tc.args, "sqlserver", sqlserverDSN()) + proxyConnTest(t, AddIPTypeFlag(tc.args), "sqlserver", sqlserverDSN()) }) } } @@ -129,6 +150,9 @@ func TestSQLServerGcloudAuth(t *testing.T) { if testing.Short() { t.Skip("skipping SQL Server integration tests") } + if v := os.Getenv("IP_TYPE"); v == "private" || v == "psc" { + t.Skipf("skipping test because IP_TYPE is set to %v", v) + } requireSQLServerVars(t) tcs := []struct { @@ -149,11 +173,14 @@ func TestSQLServerGcloudAuth(t *testing.T) { } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { - proxyConnTest(t, tc.args, "sqlserver", sqlserverDSN()) + proxyConnTest(t, AddIPTypeFlag(tc.args), "sqlserver", sqlserverDSN()) }) } } func TestSQLServerHealthCheck(t *testing.T) { + if testing.Short() { + t.Skip("skipping SQL Server integration tests") + } testHealthCheck(t, *sqlserverConnName) } diff --git a/windows-service-guide.md b/windows-service-guide.md new file mode 100644 index 000000000..454016a13 --- /dev/null +++ b/windows-service-guide.md @@ -0,0 +1,85 @@ +# Cloud SQL Auth Proxy Windows Service Guide + +This document covers running the *Cloud SQL Auth Proxy* as service +on the Windows operating system. + +It was originally built and tested using Go 1.20.2 on Windows Server 2019. + +## Install the Windows Service + +Prerequisites: A built binary for Windows of the Cloud SQL Auth Proxy is required. Either build it from source or [download a release](https://github.com/GoogleCloudPlatform/cloud-sql-proxy/releases) of a Windows pre-built version, e.g. `cloud-sql-proxy.x64.exe`. + +First, install the binary by: + +1. Create a new empty folder, e.g. `C:\Program Files\cloud-sql-proxy` +2. Copy the binary and helper batch files +3. Modify the batch files as needed: + - `SERVICE` is the Windows internal service name (as shown in the Task Manager) + - `DISPLAYNAME` is the service name (as shown in the Windows Administration Console (MMC)) + - `CREDENTIALSFILE` is the *full* path to the credentials file, where `%~dp0` points to the full path of the script file folder. + - `CONNECTIONNAME` is the Google SQL connection name in the format of `project-id:region:db-instance` + - Please note that the `--credentials-file \"%CREDENTIALSFILE%\"` argument is optional and is not needed if the local machine runs within the Google Cloud Compute Engine and "defaults" to the VM instance service account. +4. Grant *read & execute* access to the `Network Service` user +5. Create a `logs` sub-folder, e.g. `C:\Program Files\cloud-sql-proxy\logs` +6. Grant *modify* access to the `Network Service` user +7. Run the `windows_install_service.bat` batch file within an *elevated* command line prompt (read: *Run as Administrator*). + +After that, perform the setup: + +1. Copy the JSON credentials file, if required +2. Modify the `windows_install_service.bat` file to your needs +3. Run the `windows_install_service.bat` file from the commandline + +Please see the FAQ below for common error messages. + +## Uninstall the Windows Service + +To uninstall the Windows Service, perform the following steps: + +1. Modify the `windows_remove_service.bat` file to your needs +2. Run the `windows_remove_service.bat` file from the commandline + +## FAQ + +### Error Message: *Access is denied* + +The error message `Access is denied.` (or `System error 5 has occurred.`) occurs when +trying to start the installed service but the service account does not have access +to the service's file directory. + +Usually this is the *Network Service* built-in user. + +Please note that write access is also required for creating and managing the log files, e.g.: + +- `cloud-sql-proxy.log` +- `cloud-sql-proxy-2016-11-04T18-30-00.000.log` + +### Error Message: *The specified service has been marked for deletion.* + +The error message `The specified service has been marked for deletion.` occurs when +reinstalling the service and the previous deletion request could not be completed +(e.g. because the service was still running or opened in the service manager). + +In this case, the local machine needs to be restarted. + +### Why not running as the *System* user? + +Since the Cloud Proxy does not require and file system access, besides the log files, +extensive operating system access is not required. + +The *Network Service* accounts allow binding ports while not granting +access to file system resources. + +### Why not using *Automatic (Delayed Start)* startup type? + +The service is installed in the *Automatic* startup type, by default. + +The alternative *Automatic (Delayed Start)* startup type was introduced +by Microsoft for services that are not required for operating system operations +like Windows Update and similar services. + +However, if the primary purpose of the local machine is to provide services +which require access to the cloud database, then the start of the service +should not be delayed. + +Delayed services might be started even minutes after operating system startup. diff --git a/windows_install_service.bat b/windows_install_service.bat new file mode 100644 index 000000000..2c4be1506 --- /dev/null +++ b/windows_install_service.bat @@ -0,0 +1,14 @@ +@echo off + +setlocal + +set SERVICE=cloud-sql-proxy +set DISPLAYNAME=Google Cloud SQL Auth Proxy +set CREDENTIALSFILE=%~dp0key.json +set CONNECTIONNAME=project-id:region:db-instance + +sc.exe create "%SERVICE%" binPath= "\"%~dp0cloud-sql-proxy.exe\" --credentials-file \"%CREDENTIALSFILE%\" %CONNECTIONNAME%" obj= "NT AUTHORITY\Network Service" start= auto displayName= "%DISPLAYNAME%" +sc.exe failure "%SERVICE%" reset= 0 actions= restart/0/restart/0/restart/0 +net start "%SERVICE%" + +endlocal diff --git a/windows_remove_service.bat b/windows_remove_service.bat new file mode 100644 index 000000000..387436b47 --- /dev/null +++ b/windows_remove_service.bat @@ -0,0 +1,10 @@ +@echo off + +setlocal + +set SERVICE=cloud-sql-proxy + +net stop "%SERVICE%" +sc.exe delete "%SERVICE%" + +endlocal