diff --git a/.coveragerc b/.coveragerc index 4646abb..c625e5b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,27 +1,11 @@ -# -*- coding: utf-8 -*- -# -# 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 -# -# 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. - -# Generated by synthtool. DO NOT EDIT! [run] branch = True [report] fail_under = 100 show_missing = True -omit = google/cloud/redis/__init__.py +omit = + google/cloud/redis/__init__.py exclude_lines = # Re-enable the standard pragma pragma: NO COVER @@ -31,4 +15,4 @@ exclude_lines = # This is added at the module level as a safeguard for if someone # generates the code and tries to run it without pip installing. This # makes it virtually impossible to test properly. - except pkg_resources.DistributionNotFound \ No newline at end of file + except pkg_resources.DistributionNotFound diff --git a/.github/header-checker-lint.yml b/.github/header-checker-lint.yml new file mode 100644 index 0000000..fc281c0 --- /dev/null +++ b/.github/header-checker-lint.yml @@ -0,0 +1,15 @@ +{"allowedCopyrightHolders": ["Google LLC"], + "allowedLicenses": ["Apache-2.0", "MIT", "BSD-3"], + "ignoreFiles": ["**/requirements.txt", "**/requirements-test.txt"], + "sourceFileExtensions": [ + "ts", + "js", + "java", + "sh", + "Dockerfile", + "yaml", + "py", + "html", + "txt" + ] +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index b9daa52..b4243ce 100644 --- a/.gitignore +++ b/.gitignore @@ -50,8 +50,10 @@ docs.metadata # Virtual environment env/ + +# Test logs coverage.xml -sponge_log.xml +*sponge_log.xml # System test environment variables. system_tests/local_test_setup diff --git a/.kokoro/build.sh b/.kokoro/build.sh index 1c66306..0bf5786 100755 --- a/.kokoro/build.sh +++ b/.kokoro/build.sh @@ -15,7 +15,11 @@ set -eo pipefail -cd github/python-redis +if [[ -z "${PROJECT_ROOT:-}" ]]; then + PROJECT_ROOT="github/python-redis" +fi + +cd "${PROJECT_ROOT}" # Disable buffering, so that the logs stream through. export PYTHONUNBUFFERED=1 @@ -30,16 +34,26 @@ export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/service-account.json export PROJECT_ID=$(cat "${KOKORO_GFILE_DIR}/project-id.json") # Remove old nox -python3.6 -m pip uninstall --yes --quiet nox-automation +python3 -m pip uninstall --yes --quiet nox-automation # Install nox -python3.6 -m pip install --upgrade --quiet nox -python3.6 -m nox --version +python3 -m pip install --upgrade --quiet nox +python3 -m nox --version + +# If this is a continuous build, send the test log to the FlakyBot. +# See https://github.com/googleapis/repo-automation-bots/tree/master/packages/flakybot. +if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"continuous"* ]]; then + cleanup() { + chmod +x $KOKORO_GFILE_DIR/linux_amd64/flakybot + $KOKORO_GFILE_DIR/linux_amd64/flakybot + } + trap cleanup EXIT HUP +fi # If NOX_SESSION is set, it only runs the specified session, # otherwise run all the sessions. if [[ -n "${NOX_SESSION:-}" ]]; then - python3.6 -m nox -s "${NOX_SESSION:-}" + python3 -m nox -s ${NOX_SESSION:-} else - python3.6 -m nox + python3 -m nox fi diff --git a/.kokoro/docs/docs-presubmit.cfg b/.kokoro/docs/docs-presubmit.cfg index 1118107..9d06058 100644 --- a/.kokoro/docs/docs-presubmit.cfg +++ b/.kokoro/docs/docs-presubmit.cfg @@ -15,3 +15,14 @@ env_vars: { key: "TRAMPOLINE_IMAGE_UPLOAD" value: "false" } + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-redis/.kokoro/build.sh" +} + +# Only run this nox session. +env_vars: { + key: "NOX_SESSION" + value: "docs docfx" +} diff --git a/.kokoro/samples/python3.6/periodic-head.cfg b/.kokoro/samples/python3.6/periodic-head.cfg new file mode 100644 index 0000000..f9cfcd3 --- /dev/null +++ b/.kokoro/samples/python3.6/periodic-head.cfg @@ -0,0 +1,11 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-pubsub/.kokoro/test-samples-against-head.sh" +} diff --git a/.kokoro/samples/python3.7/periodic-head.cfg b/.kokoro/samples/python3.7/periodic-head.cfg new file mode 100644 index 0000000..f9cfcd3 --- /dev/null +++ b/.kokoro/samples/python3.7/periodic-head.cfg @@ -0,0 +1,11 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-pubsub/.kokoro/test-samples-against-head.sh" +} diff --git a/.kokoro/samples/python3.8/periodic-head.cfg b/.kokoro/samples/python3.8/periodic-head.cfg new file mode 100644 index 0000000..f9cfcd3 --- /dev/null +++ b/.kokoro/samples/python3.8/periodic-head.cfg @@ -0,0 +1,11 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-pubsub/.kokoro/test-samples-against-head.sh" +} diff --git a/.kokoro/test-samples-against-head.sh b/.kokoro/test-samples-against-head.sh new file mode 100755 index 0000000..369a0f9 --- /dev/null +++ b/.kokoro/test-samples-against-head.sh @@ -0,0 +1,28 @@ +#!/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 +# +# 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. + +# A customized test runner for samples. +# +# For periodic builds, you can specify this file for testing against head. + +# `-e` enables the script to automatically fail when a command fails +# `-o pipefail` sets the exit code to the rightmost comment to exit with a non-zero +set -eo pipefail +# Enables `**` to include files nested inside sub-folders +shopt -s globstar + +cd github/python-redis + +exec .kokoro/test-samples-impl.sh diff --git a/.kokoro/test-samples-impl.sh b/.kokoro/test-samples-impl.sh new file mode 100755 index 0000000..cf5de74 --- /dev/null +++ b/.kokoro/test-samples-impl.sh @@ -0,0 +1,102 @@ +#!/bin/bash +# Copyright 2021 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. + + +# `-e` enables the script to automatically fail when a command fails +# `-o pipefail` sets the exit code to the rightmost comment to exit with a non-zero +set -eo pipefail +# Enables `**` to include files nested inside sub-folders +shopt -s globstar + +# Exit early if samples directory doesn't exist +if [ ! -d "./samples" ]; then + echo "No tests run. `./samples` not found" + exit 0 +fi + +# Disable buffering, so that the logs stream through. +export PYTHONUNBUFFERED=1 + +# Debug: show build environment +env | grep KOKORO + +# Install nox +python3.6 -m pip install --upgrade --quiet nox + +# Use secrets acessor service account to get secrets +if [[ -f "${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" ]]; then + gcloud auth activate-service-account \ + --key-file="${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" \ + --project="cloud-devrel-kokoro-resources" +fi + +# This script will create 3 files: +# - testing/test-env.sh +# - testing/service-account.json +# - testing/client-secrets.json +./scripts/decrypt-secrets.sh + +source ./testing/test-env.sh +export GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/testing/service-account.json + +# For cloud-run session, we activate the service account for gcloud sdk. +gcloud auth activate-service-account \ + --key-file "${GOOGLE_APPLICATION_CREDENTIALS}" + +export GOOGLE_CLIENT_SECRETS=$(pwd)/testing/client-secrets.json + +echo -e "\n******************** TESTING PROJECTS ********************" + +# Switch to 'fail at end' to allow all tests to complete before exiting. +set +e +# Use RTN to return a non-zero value if the test fails. +RTN=0 +ROOT=$(pwd) +# Find all requirements.txt in the samples directory (may break on whitespace). +for file in samples/**/requirements.txt; do + cd "$ROOT" + # Navigate to the project folder. + file=$(dirname "$file") + cd "$file" + + echo "------------------------------------------------------------" + echo "- testing $file" + echo "------------------------------------------------------------" + + # Use nox to execute the tests for the project. + python3.6 -m nox -s "$RUN_TESTS_SESSION" + EXIT=$? + + # If this is a periodic build, send the test log to the FlakyBot. + # See https://github.com/googleapis/repo-automation-bots/tree/master/packages/flakybot. + if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then + chmod +x $KOKORO_GFILE_DIR/linux_amd64/flakybot + $KOKORO_GFILE_DIR/linux_amd64/flakybot + fi + + if [[ $EXIT -ne 0 ]]; then + RTN=1 + echo -e "\n Testing failed: Nox returned a non-zero exit code. \n" + else + echo -e "\n Testing completed.\n" + fi + +done +cd "$ROOT" + +# Workaround for Kokoro permissions issue: delete secrets +rm testing/{test-env.sh,client-secrets.json,service-account.json} + +exit "$RTN" diff --git a/.kokoro/test-samples.sh b/.kokoro/test-samples.sh index e1608c2..dcd9a45 100755 --- a/.kokoro/test-samples.sh +++ b/.kokoro/test-samples.sh @@ -13,6 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +# The default test runner for samples. +# +# For periodic builds, we rewinds the repo to the latest release, and +# run test-samples-impl.sh. # `-e` enables the script to automatically fail when a command fails # `-o pipefail` sets the exit code to the rightmost comment to exit with a non-zero @@ -24,87 +28,19 @@ cd github/python-redis # Run periodic samples tests at latest release if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then + # preserving the test runner implementation. + cp .kokoro/test-samples-impl.sh "${TMPDIR}/test-samples-impl.sh" + echo "--- IMPORTANT IMPORTANT IMPORTANT ---" + echo "Now we rewind the repo back to the latest release..." LATEST_RELEASE=$(git describe --abbrev=0 --tags) git checkout $LATEST_RELEASE -fi - -# Exit early if samples directory doesn't exist -if [ ! -d "./samples" ]; then - echo "No tests run. `./samples` not found" - exit 0 -fi - -# Disable buffering, so that the logs stream through. -export PYTHONUNBUFFERED=1 - -# Debug: show build environment -env | grep KOKORO - -# Install nox -python3.6 -m pip install --upgrade --quiet nox - -# Use secrets acessor service account to get secrets -if [[ -f "${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" ]]; then - gcloud auth activate-service-account \ - --key-file="${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" \ - --project="cloud-devrel-kokoro-resources" -fi - -# This script will create 3 files: -# - testing/test-env.sh -# - testing/service-account.json -# - testing/client-secrets.json -./scripts/decrypt-secrets.sh - -source ./testing/test-env.sh -export GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/testing/service-account.json - -# For cloud-run session, we activate the service account for gcloud sdk. -gcloud auth activate-service-account \ - --key-file "${GOOGLE_APPLICATION_CREDENTIALS}" - -export GOOGLE_CLIENT_SECRETS=$(pwd)/testing/client-secrets.json - -echo -e "\n******************** TESTING PROJECTS ********************" - -# Switch to 'fail at end' to allow all tests to complete before exiting. -set +e -# Use RTN to return a non-zero value if the test fails. -RTN=0 -ROOT=$(pwd) -# Find all requirements.txt in the samples directory (may break on whitespace). -for file in samples/**/requirements.txt; do - cd "$ROOT" - # Navigate to the project folder. - file=$(dirname "$file") - cd "$file" - - echo "------------------------------------------------------------" - echo "- testing $file" - echo "------------------------------------------------------------" - - # Use nox to execute the tests for the project. - python3.6 -m nox -s "$RUN_TESTS_SESSION" - EXIT=$? - - # If this is a periodic build, send the test log to the FlakyBot. - # See https://github.com/googleapis/repo-automation-bots/tree/master/packages/flakybot. - if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then - chmod +x $KOKORO_GFILE_DIR/linux_amd64/flakybot - $KOKORO_GFILE_DIR/linux_amd64/flakybot + echo "The current head is: " + echo $(git rev-parse --verify HEAD) + echo "--- IMPORTANT IMPORTANT IMPORTANT ---" + # move back the test runner implementation if there's no file. + if [ ! -f .kokoro/test-samples-impl.sh ]; then + cp "${TMPDIR}/test-samples-impl.sh" .kokoro/test-samples-impl.sh fi +fi - if [[ $EXIT -ne 0 ]]; then - RTN=1 - echo -e "\n Testing failed: Nox returned a non-zero exit code. \n" - else - echo -e "\n Testing completed.\n" - fi - -done -cd "$ROOT" - -# Workaround for Kokoro permissions issue: delete secrets -rm testing/{test-env.sh,client-secrets.json,service-account.json} - -exit "$RTN" +exec .kokoro/test-samples-impl.sh diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a9024b1..32302e4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,6 +12,6 @@ repos: hooks: - id: black - repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.4 + rev: 3.9.0 hooks: - id: flake8 diff --git a/.trampolinerc b/.trampolinerc index 995ee29..383b6ec 100644 --- a/.trampolinerc +++ b/.trampolinerc @@ -24,6 +24,7 @@ required_envvars+=( pass_down_envvars+=( "STAGING_BUCKET" "V2_STAGING_BUCKET" + "NOX_SESSION" ) # Prevent unintentional override on the default image. diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 7d0f2f0..9573cdf 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -70,9 +70,14 @@ We use `nox `__ to instrument our tests. - To test your changes, run unit tests with ``nox``:: $ nox -s unit-2.7 - $ nox -s unit-3.7 + $ nox -s unit-3.8 $ ... +- Args to pytest can be passed through the nox command separated by a `--`. For + example, to run a single test:: + + $ nox -s unit-3.8 -- -k + .. note:: The unit tests and system tests are described in the @@ -93,8 +98,12 @@ On Debian/Ubuntu:: ************ Coding Style ************ +- We use the automatic code formatter ``black``. You can run it using + the nox session ``blacken``. This will eliminate many lint errors. Run via:: + + $ nox -s blacken -- PEP8 compliance, with exceptions defined in the linter configuration. +- PEP8 compliance is required, with exceptions defined in the linter configuration. If you have ``nox`` installed, you can test that you have not introduced any non-compliant code via:: @@ -133,13 +142,18 @@ Running System Tests - To run system tests, you can execute:: - $ nox -s system-3.7 + # Run all system tests + $ nox -s system-3.8 $ nox -s system-2.7 + # Run a single system test + $ nox -s system-3.8 -- -k + + .. note:: System tests are only configured to run under Python 2.7 and - Python 3.7. For expediency, we do not run them in older versions + Python 3.8. For expediency, we do not run them in older versions of Python 3. This alone will not run the tests. You'll need to change some local diff --git a/LICENSE b/LICENSE index a8ee855..d645695 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ - Apache License + + Apache License Version 2.0, January 2004 - https://www.apache.org/licenses/ + http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -192,7 +193,7 @@ 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, diff --git a/MANIFEST.in b/MANIFEST.in index e9e29d1..e783f4c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -16,10 +16,10 @@ # Generated by synthtool. DO NOT EDIT! include README.rst LICENSE -recursive-include google *.json *.proto +recursive-include google *.json *.proto py.typed recursive-include tests * global-exclude *.py[co] global-exclude __pycache__ # Exclude scripts for samples readmegen -prune scripts/readme-gen \ No newline at end of file +prune scripts/readme-gen diff --git a/UPGRADING.md b/UPGRADING.md index d692c47..3737a80 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -17,10 +17,10 @@ The 2.0.0 release requires Python 3.6+. Methods expect request objects. We provide a script that will convert most common use cases. -* Install the library +* Install the library with `libcst`. ```py -python3 -m pip install google-cloud-redis +python3 -m pip install google-cloud-redis[libcst] ``` * The script `fixup_redis_v1_keywords.py` is shipped with the library. It expects @@ -159,4 +159,4 @@ project = 'my-project' location = 'location' location_path = f'projects/{project}/locations/{location}' -``` \ No newline at end of file +``` diff --git a/docs/_static/custom.css b/docs/_static/custom.css index 0abaf22..bcd37bb 100644 --- a/docs/_static/custom.css +++ b/docs/_static/custom.css @@ -1,4 +1,9 @@ div#python2-eol { border-color: red; border-width: medium; -} \ No newline at end of file +} + +/* Ensure minimum width for 'Parameters' / 'Returns' column */ +dl.field-list > dt { + min-width: 100px +} diff --git a/docs/redis_v1/cloud_redis.rst b/docs/redis_v1/cloud_redis.rst new file mode 100644 index 0000000..3234a55 --- /dev/null +++ b/docs/redis_v1/cloud_redis.rst @@ -0,0 +1,11 @@ +CloudRedis +---------------------------- + +.. automodule:: google.cloud.redis_v1.services.cloud_redis + :members: + :inherited-members: + + +.. automodule:: google.cloud.redis_v1.services.cloud_redis.pagers + :members: + :inherited-members: diff --git a/docs/redis_v1/services.rst b/docs/redis_v1/services.rst index 5b780d7..dba59a3 100644 --- a/docs/redis_v1/services.rst +++ b/docs/redis_v1/services.rst @@ -1,6 +1,6 @@ Services for Google Cloud Redis v1 API ====================================== +.. toctree:: + :maxdepth: 2 -.. automodule:: google.cloud.redis_v1.services.cloud_redis - :members: - :inherited-members: + cloud_redis diff --git a/docs/redis_v1/types.rst b/docs/redis_v1/types.rst index 7eb7c77..38a6d65 100644 --- a/docs/redis_v1/types.rst +++ b/docs/redis_v1/types.rst @@ -3,4 +3,5 @@ Types for Google Cloud Redis v1 API .. automodule:: google.cloud.redis_v1.types :members: + :undoc-members: :show-inheritance: diff --git a/docs/redis_v1beta1/cloud_redis.rst b/docs/redis_v1beta1/cloud_redis.rst new file mode 100644 index 0000000..685854a --- /dev/null +++ b/docs/redis_v1beta1/cloud_redis.rst @@ -0,0 +1,11 @@ +CloudRedis +---------------------------- + +.. automodule:: google.cloud.redis_v1beta1.services.cloud_redis + :members: + :inherited-members: + + +.. automodule:: google.cloud.redis_v1beta1.services.cloud_redis.pagers + :members: + :inherited-members: diff --git a/docs/redis_v1beta1/services.rst b/docs/redis_v1beta1/services.rst index f1a713c..aaa5dcd 100644 --- a/docs/redis_v1beta1/services.rst +++ b/docs/redis_v1beta1/services.rst @@ -1,6 +1,6 @@ Services for Google Cloud Redis v1beta1 API =========================================== +.. toctree:: + :maxdepth: 2 -.. automodule:: google.cloud.redis_v1beta1.services.cloud_redis - :members: - :inherited-members: + cloud_redis diff --git a/docs/redis_v1beta1/types.rst b/docs/redis_v1beta1/types.rst index 4306941..d0ef506 100644 --- a/docs/redis_v1beta1/types.rst +++ b/docs/redis_v1beta1/types.rst @@ -3,4 +3,5 @@ Types for Google Cloud Redis v1beta1 API .. automodule:: google.cloud.redis_v1beta1.types :members: + :undoc-members: :show-inheritance: diff --git a/google/cloud/redis_v1/services/cloud_redis/async_client.py b/google/cloud/redis_v1/services/cloud_redis/async_client.py index 1553c6c..5677201 100644 --- a/google/cloud/redis_v1/services/cloud_redis/async_client.py +++ b/google/cloud/redis_v1/services/cloud_redis/async_client.py @@ -15,7 +15,6 @@ # limitations under the License. # -import builtins from collections import OrderedDict import functools import re @@ -97,7 +96,36 @@ class CloudRedisAsyncClient: CloudRedisClient.parse_common_location_path ) - from_service_account_file = CloudRedisClient.from_service_account_file + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + CloudRedisAsyncClient: The constructed client. + """ + return CloudRedisClient.from_service_account_info.__func__(CloudRedisAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + CloudRedisAsyncClient: The constructed client. + """ + return CloudRedisClient.from_service_account_file.__func__(CloudRedisAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property @@ -182,7 +210,7 @@ async def list_instances( are aggregated. Args: - request (:class:`~.cloud_redis.ListInstancesRequest`): + request (:class:`google.cloud.redis_v1.types.ListInstancesRequest`): The request object. Request for [ListInstances][google.cloud.redis.v1.CloudRedis.ListInstances]. parent (:class:`str`): @@ -190,6 +218,7 @@ async def list_instances( using the form: ``projects/{project_id}/locations/{location_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -201,7 +230,7 @@ async def list_instances( sent along with the request as metadata. Returns: - ~.pagers.ListInstancesAsyncPager: + google.cloud.redis_v1.services.cloud_redis.pagers.ListInstancesAsyncPager: Response for [ListInstances][google.cloud.redis.v1.CloudRedis.ListInstances]. @@ -212,7 +241,7 @@ async def list_instances( # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([parent]) + has_flattened_params = any([parent]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -265,13 +294,14 @@ async def get_instance( r"""Gets the details of a specific Redis instance. Args: - request (:class:`~.cloud_redis.GetInstanceRequest`): + request (:class:`google.cloud.redis_v1.types.GetInstanceRequest`): The request object. Request for [GetInstance][google.cloud.redis.v1.CloudRedis.GetInstance]. name (:class:`str`): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -283,13 +313,13 @@ async def get_instance( sent along with the request as metadata. Returns: - ~.cloud_redis.Instance: + google.cloud.redis_v1.types.Instance: A Google Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name]) + has_flattened_params = any([name]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -351,7 +381,7 @@ async def create_instance( hours, so there is no need to call DeleteOperation. Args: - request (:class:`~.cloud_redis.CreateInstanceRequest`): + request (:class:`google.cloud.redis_v1.types.CreateInstanceRequest`): The request object. Request for [CreateInstance][google.cloud.redis.v1.CloudRedis.CreateInstance]. parent (:class:`str`): @@ -359,6 +389,7 @@ async def create_instance( using the form: ``projects/{project_id}/locations/{location_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -372,10 +403,11 @@ async def create_instance( - Must be between 1-40 characters. - Must end with a number or a letter. - Must be unique within the customer project / location + This corresponds to the ``instance_id`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - instance (:class:`~.cloud_redis.Instance`): + instance (:class:`google.cloud.redis_v1.types.Instance`): Required. A Redis [Instance] resource This corresponds to the ``instance`` field on the ``request`` instance; if ``request`` is provided, this @@ -388,18 +420,18 @@ async def create_instance( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1.types.Instance` A Google + Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([parent, instance_id, instance]) + has_flattened_params = any([parent, instance_id, instance]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -464,10 +496,10 @@ async def update_instance( there is no need to call DeleteOperation. Args: - request (:class:`~.cloud_redis.UpdateInstanceRequest`): + request (:class:`google.cloud.redis_v1.types.UpdateInstanceRequest`): The request object. Request for [UpdateInstance][google.cloud.redis.v1.CloudRedis.UpdateInstance]. - update_mask (:class:`~.field_mask.FieldMask`): + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): Required. Mask of fields to update. At least one path must be supplied in this field. The elements of the repeated paths field may only include these fields from @@ -477,12 +509,14 @@ async def update_instance( - ``labels`` - ``memorySizeGb`` - ``redisConfig`` + This corresponds to the ``update_mask`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - instance (:class:`~.cloud_redis.Instance`): + instance (:class:`google.cloud.redis_v1.types.Instance`): Required. Update description. Only fields specified in update_mask are updated. + This corresponds to the ``instance`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -494,18 +528,18 @@ async def update_instance( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1.types.Instance` A Google + Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([update_mask, instance]) + has_flattened_params = any([update_mask, instance]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -566,19 +600,21 @@ async def upgrade_instance( specified in the request. Args: - request (:class:`~.cloud_redis.UpgradeInstanceRequest`): + request (:class:`google.cloud.redis_v1.types.UpgradeInstanceRequest`): The request object. Request for [UpgradeInstance][google.cloud.redis.v1.CloudRedis.UpgradeInstance]. name (:class:`str`): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. redis_version (:class:`str`): Required. Specifies the target version of Redis software to upgrade to. + This corresponds to the ``redis_version`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -590,18 +626,18 @@ async def upgrade_instance( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1.types.Instance` A Google + Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name, redis_version]) + has_flattened_params = any([name, redis_version]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -667,19 +703,21 @@ async def import_instance( few hours, so there is no need to call DeleteOperation. Args: - request (:class:`~.cloud_redis.ImportInstanceRequest`): + request (:class:`google.cloud.redis_v1.types.ImportInstanceRequest`): The request object. Request for [Import][google.cloud.redis.v1.CloudRedis.ImportInstance]. name (:class:`str`): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - input_config (:class:`~.cloud_redis.InputConfig`): + input_config (:class:`google.cloud.redis_v1.types.InputConfig`): Required. Specify data to be imported. + This corresponds to the ``input_config`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -691,18 +729,18 @@ async def import_instance( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1.types.Instance` A Google + Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name, input_config]) + has_flattened_params = any([name, input_config]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -764,19 +802,21 @@ async def export_instance( few hours, so there is no need to call DeleteOperation. Args: - request (:class:`~.cloud_redis.ExportInstanceRequest`): + request (:class:`google.cloud.redis_v1.types.ExportInstanceRequest`): The request object. Request for [Export][google.cloud.redis.v1.CloudRedis.ExportInstance]. name (:class:`str`): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - output_config (:class:`~.cloud_redis.OutputConfig`): + output_config (:class:`google.cloud.redis_v1.types.OutputConfig`): Required. Specify data to be exported. + This corresponds to the ``output_config`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -788,18 +828,18 @@ async def export_instance( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1.types.Instance` A Google + Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name, output_config]) + has_flattened_params = any([name, output_config]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -859,20 +899,22 @@ async def failover_instance( Memorystore for Redis instance. Args: - request (:class:`~.cloud_redis.FailoverInstanceRequest`): + request (:class:`google.cloud.redis_v1.types.FailoverInstanceRequest`): The request object. Request for [Failover][google.cloud.redis.v1.CloudRedis.FailoverInstance]. name (:class:`str`): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - data_protection_mode (:class:`~.cloud_redis.FailoverInstanceRequest.DataProtectionMode`): + data_protection_mode (:class:`google.cloud.redis_v1.types.FailoverInstanceRequest.DataProtectionMode`): Optional. Available data protection modes that the user can choose. If it's unspecified, data protection mode will be LIMITED_DATA_LOSS by default. + This corresponds to the ``data_protection_mode`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -884,18 +926,18 @@ async def failover_instance( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1.types.Instance` A Google + Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name, data_protection_mode]) + has_flattened_params = any([name, data_protection_mode]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -953,13 +995,14 @@ async def delete_instance( serving and data is deleted. Args: - request (:class:`~.cloud_redis.DeleteInstanceRequest`): + request (:class:`google.cloud.redis_v1.types.DeleteInstanceRequest`): The request object. Request for [DeleteInstance][google.cloud.redis.v1.CloudRedis.DeleteInstance]. name (:class:`str`): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -971,30 +1014,28 @@ async def delete_instance( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. - The result type for the operation will be - :class:``~.empty.Empty``: A generic empty message that - you can re-use to avoid defining duplicated empty - messages in your APIs. A typical example is to use it as - the request or the response type of an API method. For - instance: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name]) + has_flattened_params = any([name]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " diff --git a/google/cloud/redis_v1/services/cloud_redis/client.py b/google/cloud/redis_v1/services/cloud_redis/client.py index 8a5206f..2e5a28e 100644 --- a/google/cloud/redis_v1/services/cloud_redis/client.py +++ b/google/cloud/redis_v1/services/cloud_redis/client.py @@ -15,7 +15,6 @@ # limitations under the License. # -import builtins from collections import OrderedDict from distutils import util import os @@ -135,6 +134,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + CloudRedisClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -147,7 +162,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + CloudRedisClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -255,10 +270,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.CloudRedisTransport]): The + transport (Union[str, CloudRedisTransport]): The transport to use. If set to None, a transport is chosen automatically. - client_options (client_options_lib.ClientOptions): Custom options for the + client_options (google.api_core.client_options.ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -294,21 +309,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -351,7 +362,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -377,14 +388,15 @@ def list_instances( are aggregated. Args: - request (:class:`~.cloud_redis.ListInstancesRequest`): + request (google.cloud.redis_v1.types.ListInstancesRequest): The request object. Request for [ListInstances][google.cloud.redis.v1.CloudRedis.ListInstances]. - parent (:class:`str`): + parent (str): Required. The resource name of the instance location using the form: ``projects/{project_id}/locations/{location_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -396,7 +408,7 @@ def list_instances( sent along with the request as metadata. Returns: - ~.pagers.ListInstancesPager: + google.cloud.redis_v1.services.cloud_redis.pagers.ListInstancesPager: Response for [ListInstances][google.cloud.redis.v1.CloudRedis.ListInstances]. @@ -407,7 +419,7 @@ def list_instances( # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([parent]) + has_flattened_params = any([parent]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -461,13 +473,14 @@ def get_instance( r"""Gets the details of a specific Redis instance. Args: - request (:class:`~.cloud_redis.GetInstanceRequest`): + request (google.cloud.redis_v1.types.GetInstanceRequest): The request object. Request for [GetInstance][google.cloud.redis.v1.CloudRedis.GetInstance]. - name (:class:`str`): + name (str): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -479,13 +492,13 @@ def get_instance( sent along with the request as metadata. Returns: - ~.cloud_redis.Instance: + google.cloud.redis_v1.types.Instance: A Google Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name]) + has_flattened_params = any([name]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -548,18 +561,19 @@ def create_instance( hours, so there is no need to call DeleteOperation. Args: - request (:class:`~.cloud_redis.CreateInstanceRequest`): + request (google.cloud.redis_v1.types.CreateInstanceRequest): The request object. Request for [CreateInstance][google.cloud.redis.v1.CloudRedis.CreateInstance]. - parent (:class:`str`): + parent (str): Required. The resource name of the instance location using the form: ``projects/{project_id}/locations/{location_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - instance_id (:class:`str`): + instance_id (str): Required. The logical name of the Redis instance in the customer project with the following restrictions: @@ -569,10 +583,11 @@ def create_instance( - Must be between 1-40 characters. - Must end with a number or a letter. - Must be unique within the customer project / location + This corresponds to the ``instance_id`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - instance (:class:`~.cloud_redis.Instance`): + instance (google.cloud.redis_v1.types.Instance): Required. A Redis [Instance] resource This corresponds to the ``instance`` field on the ``request`` instance; if ``request`` is provided, this @@ -585,18 +600,18 @@ def create_instance( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1.types.Instance` A Google + Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([parent, instance_id, instance]) + has_flattened_params = any([parent, instance_id, instance]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -662,10 +677,10 @@ def update_instance( there is no need to call DeleteOperation. Args: - request (:class:`~.cloud_redis.UpdateInstanceRequest`): + request (google.cloud.redis_v1.types.UpdateInstanceRequest): The request object. Request for [UpdateInstance][google.cloud.redis.v1.CloudRedis.UpdateInstance]. - update_mask (:class:`~.field_mask.FieldMask`): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Required. Mask of fields to update. At least one path must be supplied in this field. The elements of the repeated paths field may only include these fields from @@ -675,12 +690,14 @@ def update_instance( - ``labels`` - ``memorySizeGb`` - ``redisConfig`` + This corresponds to the ``update_mask`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - instance (:class:`~.cloud_redis.Instance`): + instance (google.cloud.redis_v1.types.Instance): Required. Update description. Only fields specified in update_mask are updated. + This corresponds to the ``instance`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -692,18 +709,18 @@ def update_instance( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1.types.Instance` A Google + Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([update_mask, instance]) + has_flattened_params = any([update_mask, instance]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -765,19 +782,21 @@ def upgrade_instance( specified in the request. Args: - request (:class:`~.cloud_redis.UpgradeInstanceRequest`): + request (google.cloud.redis_v1.types.UpgradeInstanceRequest): The request object. Request for [UpgradeInstance][google.cloud.redis.v1.CloudRedis.UpgradeInstance]. - name (:class:`str`): + name (str): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - redis_version (:class:`str`): + redis_version (str): Required. Specifies the target version of Redis software to upgrade to. + This corresponds to the ``redis_version`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -789,18 +808,18 @@ def upgrade_instance( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1.types.Instance` A Google + Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name, redis_version]) + has_flattened_params = any([name, redis_version]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -867,19 +886,21 @@ def import_instance( few hours, so there is no need to call DeleteOperation. Args: - request (:class:`~.cloud_redis.ImportInstanceRequest`): + request (google.cloud.redis_v1.types.ImportInstanceRequest): The request object. Request for [Import][google.cloud.redis.v1.CloudRedis.ImportInstance]. - name (:class:`str`): + name (str): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - input_config (:class:`~.cloud_redis.InputConfig`): + input_config (google.cloud.redis_v1.types.InputConfig): Required. Specify data to be imported. + This corresponds to the ``input_config`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -891,18 +912,18 @@ def import_instance( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1.types.Instance` A Google + Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name, input_config]) + has_flattened_params = any([name, input_config]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -965,19 +986,21 @@ def export_instance( few hours, so there is no need to call DeleteOperation. Args: - request (:class:`~.cloud_redis.ExportInstanceRequest`): + request (google.cloud.redis_v1.types.ExportInstanceRequest): The request object. Request for [Export][google.cloud.redis.v1.CloudRedis.ExportInstance]. - name (:class:`str`): + name (str): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - output_config (:class:`~.cloud_redis.OutputConfig`): + output_config (google.cloud.redis_v1.types.OutputConfig): Required. Specify data to be exported. + This corresponds to the ``output_config`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -989,18 +1012,18 @@ def export_instance( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1.types.Instance` A Google + Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name, output_config]) + has_flattened_params = any([name, output_config]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1061,20 +1084,22 @@ def failover_instance( Memorystore for Redis instance. Args: - request (:class:`~.cloud_redis.FailoverInstanceRequest`): + request (google.cloud.redis_v1.types.FailoverInstanceRequest): The request object. Request for [Failover][google.cloud.redis.v1.CloudRedis.FailoverInstance]. - name (:class:`str`): + name (str): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - data_protection_mode (:class:`~.cloud_redis.FailoverInstanceRequest.DataProtectionMode`): + data_protection_mode (google.cloud.redis_v1.types.FailoverInstanceRequest.DataProtectionMode): Optional. Available data protection modes that the user can choose. If it's unspecified, data protection mode will be LIMITED_DATA_LOSS by default. + This corresponds to the ``data_protection_mode`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1086,18 +1111,18 @@ def failover_instance( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1.types.Instance` A Google + Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name, data_protection_mode]) + has_flattened_params = any([name, data_protection_mode]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1156,13 +1181,14 @@ def delete_instance( serving and data is deleted. Args: - request (:class:`~.cloud_redis.DeleteInstanceRequest`): + request (google.cloud.redis_v1.types.DeleteInstanceRequest): The request object. Request for [DeleteInstance][google.cloud.redis.v1.CloudRedis.DeleteInstance]. - name (:class:`str`): + name (str): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1174,30 +1200,28 @@ def delete_instance( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. - The result type for the operation will be - :class:``~.empty.Empty``: A generic empty message that - you can re-use to avoid defining duplicated empty - messages in your APIs. A typical example is to use it as - the request or the response type of an API method. For - instance: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name]) + has_flattened_params = any([name]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " diff --git a/google/cloud/redis_v1/services/cloud_redis/pagers.py b/google/cloud/redis_v1/services/cloud_redis/pagers.py index 77af010..a784084 100644 --- a/google/cloud/redis_v1/services/cloud_redis/pagers.py +++ b/google/cloud/redis_v1/services/cloud_redis/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.cloud.redis_v1.types import cloud_redis @@ -24,7 +33,7 @@ class ListInstancesPager: """A pager for iterating through ``list_instances`` requests. This class thinly wraps an initial - :class:`~.cloud_redis.ListInstancesResponse` object, and + :class:`google.cloud.redis_v1.types.ListInstancesResponse` object, and provides an ``__iter__`` method to iterate through its ``instances`` field. @@ -33,7 +42,7 @@ class ListInstancesPager: through the ``instances`` field on the corresponding responses. - All the usual :class:`~.cloud_redis.ListInstancesResponse` + All the usual :class:`google.cloud.redis_v1.types.ListInstancesResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.cloud_redis.ListInstancesRequest`): + request (google.cloud.redis_v1.types.ListInstancesRequest): The initial request object. - response (:class:`~.cloud_redis.ListInstancesResponse`): + response (google.cloud.redis_v1.types.ListInstancesResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ class ListInstancesAsyncPager: """A pager for iterating through ``list_instances`` requests. This class thinly wraps an initial - :class:`~.cloud_redis.ListInstancesResponse` object, and + :class:`google.cloud.redis_v1.types.ListInstancesResponse` object, and provides an ``__aiter__`` method to iterate through its ``instances`` field. @@ -95,7 +104,7 @@ class ListInstancesAsyncPager: through the ``instances`` field on the corresponding responses. - All the usual :class:`~.cloud_redis.ListInstancesResponse` + All the usual :class:`google.cloud.redis_v1.types.ListInstancesResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -113,9 +122,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.cloud_redis.ListInstancesRequest`): + request (google.cloud.redis_v1.types.ListInstancesRequest): The initial request object. - response (:class:`~.cloud_redis.ListInstancesResponse`): + response (google.cloud.redis_v1.types.ListInstancesResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/cloud/redis_v1/services/cloud_redis/transports/base.py b/google/cloud/redis_v1/services/cloud_redis/transports/base.py index f0fc19e..83c64f3 100644 --- a/google/cloud/redis_v1/services/cloud_redis/transports/base.py +++ b/google/cloud/redis_v1/services/cloud_redis/transports/base.py @@ -69,10 +69,10 @@ def __init__( scope (Optional[Sequence[str]]): A list of scopes. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. """ # Save the hostname. Default to port 443 (HTTPS) if none is specified. @@ -80,6 +80,9 @@ def __init__( host += ":443" self._host = host + # Save the scopes. + self._scopes = scopes or self.AUTH_SCOPES + # If no credentials are provided, then determine the appropriate # defaults. if credentials and credentials_file: @@ -89,20 +92,17 @@ def __init__( if credentials_file is not None: credentials, _ = auth.load_credentials_from_file( - credentials_file, scopes=scopes, quota_project_id=quota_project_id + credentials_file, scopes=self._scopes, quota_project_id=quota_project_id ) elif credentials is None: credentials, _ = auth.default( - scopes=scopes, quota_project_id=quota_project_id + scopes=self._scopes, quota_project_id=quota_project_id ) # Save the credentials. self._credentials = credentials - # Lifted into its own function so it can be stubbed out during tests. - self._prep_wrapped_messages(client_info) - def _prep_wrapped_messages(self, client_info): # Precompute the wrapped methods. self._wrapped_methods = { diff --git a/google/cloud/redis_v1/services/cloud_redis/transports/grpc.py b/google/cloud/redis_v1/services/cloud_redis/transports/grpc.py index ae96591..8bfb753 100644 --- a/google/cloud/redis_v1/services/cloud_redis/transports/grpc.py +++ b/google/cloud/redis_v1/services/cloud_redis/transports/grpc.py @@ -79,6 +79,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -109,6 +110,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -123,72 +128,61 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._grpc_channel = None self._ssl_channel_credentials = ssl_channel_credentials + self._stubs: Dict[str, Callable] = {} + self._operations_client = None + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) if channel: - # Sanity check: Ensure that channel and credentials are not both - # provided. + # Ignore credentials if a channel was passed. credentials = False - # If a channel was explicitly provided, set it. self._grpc_channel = channel self._ssl_channel_credentials = None - elif api_mtls_endpoint: - warnings.warn( - "api_mtls_endpoint and client_cert_source are deprecated", - DeprecationWarning, - ) - host = ( - api_mtls_endpoint - if ":" in api_mtls_endpoint - else api_mtls_endpoint + ":443" - ) + else: + if api_mtls_endpoint: + host = api_mtls_endpoint + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + self._ssl_channel_credentials = SslCredentials().ssl_credentials - if credentials is None: - credentials, _ = auth.default( - scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id - ) - - # Create SSL credentials with client_cert_source or application - # default SSL credentials. - if client_cert_source: - cert, key = client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) else: - ssl_credentials = SslCredentials().ssl_credentials + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) - # create a new channel. The provided one is ignored. - self._grpc_channel = type(self).create_channel( - host, - credentials=credentials, - credentials_file=credentials_file, - ssl_credentials=ssl_credentials, - scopes=scopes or self.AUTH_SCOPES, - quota_project_id=quota_project_id, - options=[ - ("grpc.max_send_message_length", -1), - ("grpc.max_receive_message_length", -1), - ], - ) - self._ssl_channel_credentials = ssl_credentials - else: - host = host if ":" in host else host + ":443" - - if credentials is None: - credentials, _ = auth.default( - scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id - ) + # The base transport sets the host, credentials and scopes + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + client_info=client_info, + ) - # create a new channel. The provided one is ignored. + if not self._grpc_channel: self._grpc_channel = type(self).create_channel( - host, - credentials=credentials, + self._host, + credentials=self._credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, - scopes=scopes or self.AUTH_SCOPES, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, quota_project_id=quota_project_id, options=[ ("grpc.max_send_message_length", -1), @@ -196,18 +190,8 @@ def __init__( ], ) - self._stubs = {} # type: Dict[str, Callable] - self._operations_client = None - - # Run the base constructor. - super().__init__( - host=host, - credentials=credentials, - credentials_file=credentials_file, - scopes=scopes or self.AUTH_SCOPES, - quota_project_id=quota_project_id, - client_info=client_info, - ) + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) @classmethod def create_channel( @@ -221,7 +205,7 @@ def create_channel( ) -> grpc.Channel: """Create and return a gRPC channel object. Args: - address (Optional[str]): The host for the channel to use. + host (Optional[str]): The host for the channel to use. credentials (Optional[~.Credentials]): The authorization credentials to attach to requests. These credentials identify this application to the service. If diff --git a/google/cloud/redis_v1/services/cloud_redis/transports/grpc_asyncio.py b/google/cloud/redis_v1/services/cloud_redis/transports/grpc_asyncio.py index b667778..d2568d3 100644 --- a/google/cloud/redis_v1/services/cloud_redis/transports/grpc_asyncio.py +++ b/google/cloud/redis_v1/services/cloud_redis/transports/grpc_asyncio.py @@ -83,7 +83,7 @@ def create_channel( ) -> aio.Channel: """Create and return a gRPC AsyncIO channel object. Args: - address (Optional[str]): The host for the channel to use. + host (Optional[str]): The host for the channel to use. credentials (Optional[~.Credentials]): The authorization credentials to attach to requests. These credentials identify this application to the service. If @@ -123,6 +123,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -154,12 +155,16 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -168,72 +173,61 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._grpc_channel = None self._ssl_channel_credentials = ssl_channel_credentials + self._stubs: Dict[str, Callable] = {} + self._operations_client = None + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) if channel: - # Sanity check: Ensure that channel and credentials are not both - # provided. + # Ignore credentials if a channel was passed. credentials = False - # If a channel was explicitly provided, set it. self._grpc_channel = channel self._ssl_channel_credentials = None - elif api_mtls_endpoint: - warnings.warn( - "api_mtls_endpoint and client_cert_source are deprecated", - DeprecationWarning, - ) - host = ( - api_mtls_endpoint - if ":" in api_mtls_endpoint - else api_mtls_endpoint + ":443" - ) + else: + if api_mtls_endpoint: + host = api_mtls_endpoint + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + self._ssl_channel_credentials = SslCredentials().ssl_credentials - if credentials is None: - credentials, _ = auth.default( - scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id - ) - - # Create SSL credentials with client_cert_source or application - # default SSL credentials. - if client_cert_source: - cert, key = client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) else: - ssl_credentials = SslCredentials().ssl_credentials + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) - # create a new channel. The provided one is ignored. - self._grpc_channel = type(self).create_channel( - host, - credentials=credentials, - credentials_file=credentials_file, - ssl_credentials=ssl_credentials, - scopes=scopes or self.AUTH_SCOPES, - quota_project_id=quota_project_id, - options=[ - ("grpc.max_send_message_length", -1), - ("grpc.max_receive_message_length", -1), - ], - ) - self._ssl_channel_credentials = ssl_credentials - else: - host = host if ":" in host else host + ":443" - - if credentials is None: - credentials, _ = auth.default( - scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id - ) + # The base transport sets the host, credentials and scopes + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + client_info=client_info, + ) - # create a new channel. The provided one is ignored. + if not self._grpc_channel: self._grpc_channel = type(self).create_channel( - host, - credentials=credentials, + self._host, + credentials=self._credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, - scopes=scopes or self.AUTH_SCOPES, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, quota_project_id=quota_project_id, options=[ ("grpc.max_send_message_length", -1), @@ -241,18 +235,8 @@ def __init__( ], ) - # Run the base constructor. - super().__init__( - host=host, - credentials=credentials, - credentials_file=credentials_file, - scopes=scopes or self.AUTH_SCOPES, - quota_project_id=quota_project_id, - client_info=client_info, - ) - - self._stubs = {} - self._operations_client = None + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) @property def grpc_channel(self) -> aio.Channel: diff --git a/google/cloud/redis_v1/types/__init__.py b/google/cloud/redis_v1/types/__init__.py index 1942d36..1f18cb9 100644 --- a/google/cloud/redis_v1/types/__init__.py +++ b/google/cloud/redis_v1/types/__init__.py @@ -16,43 +16,43 @@ # from .cloud_redis import ( - Instance, - ListInstancesRequest, - ListInstancesResponse, - GetInstanceRequest, CreateInstanceRequest, - UpdateInstanceRequest, - UpgradeInstanceRequest, DeleteInstanceRequest, - GcsSource, - InputConfig, - ImportInstanceRequest, - GcsDestination, - OutputConfig, ExportInstanceRequest, FailoverInstanceRequest, - OperationMetadata, + GcsDestination, + GcsSource, + GetInstanceRequest, + ImportInstanceRequest, + InputConfig, + Instance, + ListInstancesRequest, + ListInstancesResponse, LocationMetadata, + OperationMetadata, + OutputConfig, + UpdateInstanceRequest, + UpgradeInstanceRequest, ZoneMetadata, ) __all__ = ( - "Instance", - "ListInstancesRequest", - "ListInstancesResponse", - "GetInstanceRequest", "CreateInstanceRequest", - "UpdateInstanceRequest", - "UpgradeInstanceRequest", "DeleteInstanceRequest", - "GcsSource", - "InputConfig", - "ImportInstanceRequest", - "GcsDestination", - "OutputConfig", "ExportInstanceRequest", "FailoverInstanceRequest", - "OperationMetadata", + "GcsDestination", + "GcsSource", + "GetInstanceRequest", + "ImportInstanceRequest", + "InputConfig", + "Instance", + "ListInstancesRequest", + "ListInstancesResponse", "LocationMetadata", + "OperationMetadata", + "OutputConfig", + "UpdateInstanceRequest", + "UpgradeInstanceRequest", "ZoneMetadata", ) diff --git a/google/cloud/redis_v1/types/cloud_redis.py b/google/cloud/redis_v1/types/cloud_redis.py index 9821f43..aba660c 100644 --- a/google/cloud/redis_v1/types/cloud_redis.py +++ b/google/cloud/redis_v1/types/cloud_redis.py @@ -68,7 +68,7 @@ class Instance(proto.Message): display_name (str): An arbitrary and optional user-provided name for the instance. - labels (Sequence[~.cloud_redis.Instance.LabelsEntry]): + labels (Sequence[google.cloud.redis_v1.types.Instance.LabelsEntry]): Resource labels to represent user provided metadata location_id (str): @@ -118,16 +118,16 @@ class Instance(proto.Message): [location_id][google.cloud.redis.v1.Instance.location_id] or [alternative_location_id][google.cloud.redis.v1.Instance.alternative_location_id] and can change after a failover event. - create_time (~.timestamp.Timestamp): + create_time (google.protobuf.timestamp_pb2.Timestamp): Output only. The time the instance was created. - state (~.cloud_redis.Instance.State): + state (google.cloud.redis_v1.types.Instance.State): Output only. The current state of this instance. status_message (str): Output only. Additional information about the current status of this instance, if available. - redis_configs (Sequence[~.cloud_redis.Instance.RedisConfigsEntry]): + redis_configs (Sequence[google.cloud.redis_v1.types.Instance.RedisConfigsEntry]): Optional. Redis configuration parameters, according to http://redis.io/topics/config. Currently, the only supported parameters are: @@ -148,7 +148,7 @@ class Instance(proto.Message): - stream-node-max-bytes - stream-node-max-entries - tier (~.cloud_redis.Instance.Tier): + tier (google.cloud.redis_v1.types.Instance.Tier): Required. The service tier of the instance. memory_size_gb (int): Required. Redis memory size in GiB. @@ -163,7 +163,7 @@ class Instance(proto.Message): "serviceAccount:". The value may change over time for a given instance so should be checked before each import/export operation. - connect_mode (~.cloud_redis.Instance.ConnectMode): + connect_mode (google.cloud.redis_v1.types.Instance.ConnectMode): Optional. The network connect mode of the Redis instance. If not provided, the connect mode defaults to DIRECT_PEERING. """ @@ -267,7 +267,7 @@ class ListInstancesResponse(proto.Message): [ListInstances][google.cloud.redis.v1.CloudRedis.ListInstances]. Attributes: - instances (Sequence[~.cloud_redis.Instance]): + instances (Sequence[google.cloud.redis_v1.types.Instance]): A list of Redis instances in the project in the specified location, or across all locations. @@ -332,7 +332,7 @@ class CreateInstanceRequest(proto.Message): - Must be between 1-40 characters. - Must end with a number or a letter. - Must be unique within the customer project / location - instance (~.cloud_redis.Instance): + instance (google.cloud.redis_v1.types.Instance): Required. A Redis [Instance] resource """ @@ -348,7 +348,7 @@ class UpdateInstanceRequest(proto.Message): [UpdateInstance][google.cloud.redis.v1.CloudRedis.UpdateInstance]. Attributes: - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Required. Mask of fields to update. At least one path must be supplied in this field. The elements of the repeated paths field may only include these fields from @@ -358,7 +358,7 @@ class UpdateInstanceRequest(proto.Message): - ``labels`` - ``memorySizeGb`` - ``redisConfig`` - instance (~.cloud_redis.Instance): + instance (google.cloud.redis_v1.types.Instance): Required. Update description. Only fields specified in update_mask are updated. """ @@ -417,7 +417,7 @@ class InputConfig(proto.Message): r"""The input content Attributes: - gcs_source (~.cloud_redis.GcsSource): + gcs_source (google.cloud.redis_v1.types.GcsSource): Google Cloud Storage location where input content is located. """ @@ -436,7 +436,7 @@ class ImportInstanceRequest(proto.Message): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. - input_config (~.cloud_redis.InputConfig): + input_config (google.cloud.redis_v1.types.InputConfig): Required. Specify data to be imported. """ @@ -462,7 +462,7 @@ class OutputConfig(proto.Message): r"""The output content Attributes: - gcs_destination (~.cloud_redis.GcsDestination): + gcs_destination (google.cloud.redis_v1.types.GcsDestination): Google Cloud Storage destination for output content. """ @@ -481,7 +481,7 @@ class ExportInstanceRequest(proto.Message): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. - output_config (~.cloud_redis.OutputConfig): + output_config (google.cloud.redis_v1.types.OutputConfig): Required. Specify data to be exported. """ @@ -499,7 +499,7 @@ class FailoverInstanceRequest(proto.Message): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. - data_protection_mode (~.cloud_redis.FailoverInstanceRequest.DataProtectionMode): + data_protection_mode (google.cloud.redis_v1.types.FailoverInstanceRequest.DataProtectionMode): Optional. Available data protection modes that the user can choose. If it's unspecified, data protection mode will be LIMITED_DATA_LOSS by default. @@ -522,9 +522,9 @@ class OperationMetadata(proto.Message): r"""Represents the v1 metadata of the long-running operation. Attributes: - create_time (~.timestamp.Timestamp): + create_time (google.protobuf.timestamp_pb2.Timestamp): Creation timestamp. - end_time (~.timestamp.Timestamp): + end_time (google.protobuf.timestamp_pb2.Timestamp): End timestamp. target (str): Operation target. @@ -561,7 +561,7 @@ class LocationMetadata(proto.Message): ``google.cloud.location.Location.metadata`` field. Attributes: - available_zones (Sequence[~.cloud_redis.LocationMetadata.AvailableZonesEntry]): + available_zones (Sequence[google.cloud.redis_v1.types.LocationMetadata.AvailableZonesEntry]): Output only. The set of available zones in the location. The map is keyed by the lowercase ID of each zone, as defined by GCE. These keys can be specified in ``location_id`` or diff --git a/google/cloud/redis_v1beta1/services/cloud_redis/async_client.py b/google/cloud/redis_v1beta1/services/cloud_redis/async_client.py index fef3d14..4d125eb 100644 --- a/google/cloud/redis_v1beta1/services/cloud_redis/async_client.py +++ b/google/cloud/redis_v1beta1/services/cloud_redis/async_client.py @@ -15,7 +15,6 @@ # limitations under the License. # -import builtins from collections import OrderedDict import functools import re @@ -98,7 +97,36 @@ class CloudRedisAsyncClient: CloudRedisClient.parse_common_location_path ) - from_service_account_file = CloudRedisClient.from_service_account_file + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + CloudRedisAsyncClient: The constructed client. + """ + return CloudRedisClient.from_service_account_info.__func__(CloudRedisAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + CloudRedisAsyncClient: The constructed client. + """ + return CloudRedisClient.from_service_account_file.__func__(CloudRedisAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property @@ -183,7 +211,7 @@ async def list_instances( are aggregated. Args: - request (:class:`~.cloud_redis.ListInstancesRequest`): + request (:class:`google.cloud.redis_v1beta1.types.ListInstancesRequest`): The request object. Request for [ListInstances][google.cloud.redis.v1beta1.CloudRedis.ListInstances]. parent (:class:`str`): @@ -191,6 +219,7 @@ async def list_instances( using the form: ``projects/{project_id}/locations/{location_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -202,7 +231,7 @@ async def list_instances( sent along with the request as metadata. Returns: - ~.pagers.ListInstancesAsyncPager: + google.cloud.redis_v1beta1.services.cloud_redis.pagers.ListInstancesAsyncPager: Response for [ListInstances][google.cloud.redis.v1beta1.CloudRedis.ListInstances]. @@ -213,7 +242,7 @@ async def list_instances( # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([parent]) + has_flattened_params = any([parent]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -266,13 +295,14 @@ async def get_instance( r"""Gets the details of a specific Redis instance. Args: - request (:class:`~.cloud_redis.GetInstanceRequest`): + request (:class:`google.cloud.redis_v1beta1.types.GetInstanceRequest`): The request object. Request for [GetInstance][google.cloud.redis.v1beta1.CloudRedis.GetInstance]. name (:class:`str`): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -284,13 +314,13 @@ async def get_instance( sent along with the request as metadata. Returns: - ~.cloud_redis.Instance: + google.cloud.redis_v1beta1.types.Instance: A Google Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name]) + has_flattened_params = any([name]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -352,7 +382,7 @@ async def create_instance( hours, so there is no need to call DeleteOperation. Args: - request (:class:`~.cloud_redis.CreateInstanceRequest`): + request (:class:`google.cloud.redis_v1beta1.types.CreateInstanceRequest`): The request object. Request for [CreateInstance][google.cloud.redis.v1beta1.CloudRedis.CreateInstance]. parent (:class:`str`): @@ -360,6 +390,7 @@ async def create_instance( using the form: ``projects/{project_id}/locations/{location_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -373,10 +404,11 @@ async def create_instance( - Must be between 1-40 characters. - Must end with a number or a letter. - Must be unique within the customer project / location + This corresponds to the ``instance_id`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - instance (:class:`~.cloud_redis.Instance`): + instance (:class:`google.cloud.redis_v1beta1.types.Instance`): Required. A Redis [Instance] resource This corresponds to the ``instance`` field on the ``request`` instance; if ``request`` is provided, this @@ -389,18 +421,18 @@ async def create_instance( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1beta1.types.Instance` A + Google Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([parent, instance_id, instance]) + has_flattened_params = any([parent, instance_id, instance]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -465,10 +497,10 @@ async def update_instance( there is no need to call DeleteOperation. Args: - request (:class:`~.cloud_redis.UpdateInstanceRequest`): + request (:class:`google.cloud.redis_v1beta1.types.UpdateInstanceRequest`): The request object. Request for [UpdateInstance][google.cloud.redis.v1beta1.CloudRedis.UpdateInstance]. - update_mask (:class:`~.field_mask.FieldMask`): + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): Required. Mask of fields to update. At least one path must be supplied in this field. The elements of the repeated paths field may only include these fields from @@ -478,12 +510,14 @@ async def update_instance( - ``labels`` - ``memorySizeGb`` - ``redisConfig`` + This corresponds to the ``update_mask`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - instance (:class:`~.cloud_redis.Instance`): + instance (:class:`google.cloud.redis_v1beta1.types.Instance`): Required. Update description. Only fields specified in update_mask are updated. + This corresponds to the ``instance`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -495,18 +529,18 @@ async def update_instance( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1beta1.types.Instance` A + Google Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([update_mask, instance]) + has_flattened_params = any([update_mask, instance]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -567,19 +601,21 @@ async def upgrade_instance( specified in the request. Args: - request (:class:`~.cloud_redis.UpgradeInstanceRequest`): + request (:class:`google.cloud.redis_v1beta1.types.UpgradeInstanceRequest`): The request object. Request for [UpgradeInstance][google.cloud.redis.v1beta1.CloudRedis.UpgradeInstance]. name (:class:`str`): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. redis_version (:class:`str`): Required. Specifies the target version of Redis software to upgrade to. + This corresponds to the ``redis_version`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -591,18 +627,18 @@ async def upgrade_instance( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1beta1.types.Instance` A + Google Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name, redis_version]) + has_flattened_params = any([name, redis_version]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -668,19 +704,21 @@ async def import_instance( few hours, so there is no need to call DeleteOperation. Args: - request (:class:`~.cloud_redis.ImportInstanceRequest`): + request (:class:`google.cloud.redis_v1beta1.types.ImportInstanceRequest`): The request object. Request for [Import][google.cloud.redis.v1beta1.CloudRedis.ImportInstance]. name (:class:`str`): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - input_config (:class:`~.cloud_redis.InputConfig`): + input_config (:class:`google.cloud.redis_v1beta1.types.InputConfig`): Required. Specify data to be imported. + This corresponds to the ``input_config`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -692,18 +730,18 @@ async def import_instance( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1beta1.types.Instance` A + Google Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name, input_config]) + has_flattened_params = any([name, input_config]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -765,19 +803,21 @@ async def export_instance( few hours, so there is no need to call DeleteOperation. Args: - request (:class:`~.cloud_redis.ExportInstanceRequest`): + request (:class:`google.cloud.redis_v1beta1.types.ExportInstanceRequest`): The request object. Request for [Export][google.cloud.redis.v1beta1.CloudRedis.ExportInstance]. name (:class:`str`): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - output_config (:class:`~.cloud_redis.OutputConfig`): + output_config (:class:`google.cloud.redis_v1beta1.types.OutputConfig`): Required. Specify data to be exported. + This corresponds to the ``output_config`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -789,18 +829,18 @@ async def export_instance( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1beta1.types.Instance` A + Google Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name, output_config]) + has_flattened_params = any([name, output_config]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -860,20 +900,22 @@ async def failover_instance( Memorystore for Redis instance. Args: - request (:class:`~.cloud_redis.FailoverInstanceRequest`): + request (:class:`google.cloud.redis_v1beta1.types.FailoverInstanceRequest`): The request object. Request for [Failover][google.cloud.redis.v1beta1.CloudRedis.FailoverInstance]. name (:class:`str`): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - data_protection_mode (:class:`~.cloud_redis.FailoverInstanceRequest.DataProtectionMode`): + data_protection_mode (:class:`google.cloud.redis_v1beta1.types.FailoverInstanceRequest.DataProtectionMode`): Optional. Available data protection modes that the user can choose. If it's unspecified, data protection mode will be LIMITED_DATA_LOSS by default. + This corresponds to the ``data_protection_mode`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -885,18 +927,18 @@ async def failover_instance( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1beta1.types.Instance` A + Google Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name, data_protection_mode]) + has_flattened_params = any([name, data_protection_mode]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -954,13 +996,14 @@ async def delete_instance( serving and data is deleted. Args: - request (:class:`~.cloud_redis.DeleteInstanceRequest`): + request (:class:`google.cloud.redis_v1beta1.types.DeleteInstanceRequest`): The request object. Request for [DeleteInstance][google.cloud.redis.v1beta1.CloudRedis.DeleteInstance]. name (:class:`str`): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -972,30 +1015,28 @@ async def delete_instance( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. - The result type for the operation will be - :class:``~.empty.Empty``: A generic empty message that - you can re-use to avoid defining duplicated empty - messages in your APIs. A typical example is to use it as - the request or the response type of an API method. For - instance: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name]) + has_flattened_params = any([name]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " diff --git a/google/cloud/redis_v1beta1/services/cloud_redis/client.py b/google/cloud/redis_v1beta1/services/cloud_redis/client.py index ed583d8..7f0e208 100644 --- a/google/cloud/redis_v1beta1/services/cloud_redis/client.py +++ b/google/cloud/redis_v1beta1/services/cloud_redis/client.py @@ -15,7 +15,6 @@ # limitations under the License. # -import builtins from collections import OrderedDict from distutils import util import os @@ -136,6 +135,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + CloudRedisClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -148,7 +163,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + CloudRedisClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -256,10 +271,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.CloudRedisTransport]): The + transport (Union[str, CloudRedisTransport]): The transport to use. If set to None, a transport is chosen automatically. - client_options (client_options_lib.ClientOptions): Custom options for the + client_options (google.api_core.client_options.ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -295,21 +310,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -352,7 +363,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -378,14 +389,15 @@ def list_instances( are aggregated. Args: - request (:class:`~.cloud_redis.ListInstancesRequest`): + request (google.cloud.redis_v1beta1.types.ListInstancesRequest): The request object. Request for [ListInstances][google.cloud.redis.v1beta1.CloudRedis.ListInstances]. - parent (:class:`str`): + parent (str): Required. The resource name of the instance location using the form: ``projects/{project_id}/locations/{location_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -397,7 +409,7 @@ def list_instances( sent along with the request as metadata. Returns: - ~.pagers.ListInstancesPager: + google.cloud.redis_v1beta1.services.cloud_redis.pagers.ListInstancesPager: Response for [ListInstances][google.cloud.redis.v1beta1.CloudRedis.ListInstances]. @@ -408,7 +420,7 @@ def list_instances( # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([parent]) + has_flattened_params = any([parent]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -462,13 +474,14 @@ def get_instance( r"""Gets the details of a specific Redis instance. Args: - request (:class:`~.cloud_redis.GetInstanceRequest`): + request (google.cloud.redis_v1beta1.types.GetInstanceRequest): The request object. Request for [GetInstance][google.cloud.redis.v1beta1.CloudRedis.GetInstance]. - name (:class:`str`): + name (str): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -480,13 +493,13 @@ def get_instance( sent along with the request as metadata. Returns: - ~.cloud_redis.Instance: + google.cloud.redis_v1beta1.types.Instance: A Google Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name]) + has_flattened_params = any([name]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -549,18 +562,19 @@ def create_instance( hours, so there is no need to call DeleteOperation. Args: - request (:class:`~.cloud_redis.CreateInstanceRequest`): + request (google.cloud.redis_v1beta1.types.CreateInstanceRequest): The request object. Request for [CreateInstance][google.cloud.redis.v1beta1.CloudRedis.CreateInstance]. - parent (:class:`str`): + parent (str): Required. The resource name of the instance location using the form: ``projects/{project_id}/locations/{location_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - instance_id (:class:`str`): + instance_id (str): Required. The logical name of the Redis instance in the customer project with the following restrictions: @@ -570,10 +584,11 @@ def create_instance( - Must be between 1-40 characters. - Must end with a number or a letter. - Must be unique within the customer project / location + This corresponds to the ``instance_id`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - instance (:class:`~.cloud_redis.Instance`): + instance (google.cloud.redis_v1beta1.types.Instance): Required. A Redis [Instance] resource This corresponds to the ``instance`` field on the ``request`` instance; if ``request`` is provided, this @@ -586,18 +601,18 @@ def create_instance( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1beta1.types.Instance` A + Google Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([parent, instance_id, instance]) + has_flattened_params = any([parent, instance_id, instance]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -663,10 +678,10 @@ def update_instance( there is no need to call DeleteOperation. Args: - request (:class:`~.cloud_redis.UpdateInstanceRequest`): + request (google.cloud.redis_v1beta1.types.UpdateInstanceRequest): The request object. Request for [UpdateInstance][google.cloud.redis.v1beta1.CloudRedis.UpdateInstance]. - update_mask (:class:`~.field_mask.FieldMask`): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Required. Mask of fields to update. At least one path must be supplied in this field. The elements of the repeated paths field may only include these fields from @@ -676,12 +691,14 @@ def update_instance( - ``labels`` - ``memorySizeGb`` - ``redisConfig`` + This corresponds to the ``update_mask`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - instance (:class:`~.cloud_redis.Instance`): + instance (google.cloud.redis_v1beta1.types.Instance): Required. Update description. Only fields specified in update_mask are updated. + This corresponds to the ``instance`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -693,18 +710,18 @@ def update_instance( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1beta1.types.Instance` A + Google Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([update_mask, instance]) + has_flattened_params = any([update_mask, instance]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -766,19 +783,21 @@ def upgrade_instance( specified in the request. Args: - request (:class:`~.cloud_redis.UpgradeInstanceRequest`): + request (google.cloud.redis_v1beta1.types.UpgradeInstanceRequest): The request object. Request for [UpgradeInstance][google.cloud.redis.v1beta1.CloudRedis.UpgradeInstance]. - name (:class:`str`): + name (str): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - redis_version (:class:`str`): + redis_version (str): Required. Specifies the target version of Redis software to upgrade to. + This corresponds to the ``redis_version`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -790,18 +809,18 @@ def upgrade_instance( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1beta1.types.Instance` A + Google Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name, redis_version]) + has_flattened_params = any([name, redis_version]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -868,19 +887,21 @@ def import_instance( few hours, so there is no need to call DeleteOperation. Args: - request (:class:`~.cloud_redis.ImportInstanceRequest`): + request (google.cloud.redis_v1beta1.types.ImportInstanceRequest): The request object. Request for [Import][google.cloud.redis.v1beta1.CloudRedis.ImportInstance]. - name (:class:`str`): + name (str): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - input_config (:class:`~.cloud_redis.InputConfig`): + input_config (google.cloud.redis_v1beta1.types.InputConfig): Required. Specify data to be imported. + This corresponds to the ``input_config`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -892,18 +913,18 @@ def import_instance( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1beta1.types.Instance` A + Google Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name, input_config]) + has_flattened_params = any([name, input_config]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -966,19 +987,21 @@ def export_instance( few hours, so there is no need to call DeleteOperation. Args: - request (:class:`~.cloud_redis.ExportInstanceRequest`): + request (google.cloud.redis_v1beta1.types.ExportInstanceRequest): The request object. Request for [Export][google.cloud.redis.v1beta1.CloudRedis.ExportInstance]. - name (:class:`str`): + name (str): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - output_config (:class:`~.cloud_redis.OutputConfig`): + output_config (google.cloud.redis_v1beta1.types.OutputConfig): Required. Specify data to be exported. + This corresponds to the ``output_config`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -990,18 +1013,18 @@ def export_instance( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1beta1.types.Instance` A + Google Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name, output_config]) + has_flattened_params = any([name, output_config]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1062,20 +1085,22 @@ def failover_instance( Memorystore for Redis instance. Args: - request (:class:`~.cloud_redis.FailoverInstanceRequest`): + request (google.cloud.redis_v1beta1.types.FailoverInstanceRequest): The request object. Request for [Failover][google.cloud.redis.v1beta1.CloudRedis.FailoverInstance]. - name (:class:`str`): + name (str): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - data_protection_mode (:class:`~.cloud_redis.FailoverInstanceRequest.DataProtectionMode`): + data_protection_mode (google.cloud.redis_v1beta1.types.FailoverInstanceRequest.DataProtectionMode): Optional. Available data protection modes that the user can choose. If it's unspecified, data protection mode will be LIMITED_DATA_LOSS by default. + This corresponds to the ``data_protection_mode`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1087,18 +1112,18 @@ def failover_instance( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.cloud_redis.Instance``: A Google Cloud Redis - instance. + :class:`google.cloud.redis_v1beta1.types.Instance` A + Google Cloud Redis instance. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name, data_protection_mode]) + has_flattened_params = any([name, data_protection_mode]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1157,13 +1182,14 @@ def delete_instance( serving and data is deleted. Args: - request (:class:`~.cloud_redis.DeleteInstanceRequest`): + request (google.cloud.redis_v1beta1.types.DeleteInstanceRequest): The request object. Request for [DeleteInstance][google.cloud.redis.v1beta1.CloudRedis.DeleteInstance]. - name (:class:`str`): + name (str): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1175,30 +1201,28 @@ def delete_instance( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. - The result type for the operation will be - :class:``~.empty.Empty``: A generic empty message that - you can re-use to avoid defining duplicated empty - messages in your APIs. A typical example is to use it as - the request or the response type of an API method. For - instance: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = builtins.any([name]) + has_flattened_params = any([name]) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " diff --git a/google/cloud/redis_v1beta1/services/cloud_redis/pagers.py b/google/cloud/redis_v1beta1/services/cloud_redis/pagers.py index 16ca1d0..7098490 100644 --- a/google/cloud/redis_v1beta1/services/cloud_redis/pagers.py +++ b/google/cloud/redis_v1beta1/services/cloud_redis/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.cloud.redis_v1beta1.types import cloud_redis @@ -24,7 +33,7 @@ class ListInstancesPager: """A pager for iterating through ``list_instances`` requests. This class thinly wraps an initial - :class:`~.cloud_redis.ListInstancesResponse` object, and + :class:`google.cloud.redis_v1beta1.types.ListInstancesResponse` object, and provides an ``__iter__`` method to iterate through its ``instances`` field. @@ -33,7 +42,7 @@ class ListInstancesPager: through the ``instances`` field on the corresponding responses. - All the usual :class:`~.cloud_redis.ListInstancesResponse` + All the usual :class:`google.cloud.redis_v1beta1.types.ListInstancesResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.cloud_redis.ListInstancesRequest`): + request (google.cloud.redis_v1beta1.types.ListInstancesRequest): The initial request object. - response (:class:`~.cloud_redis.ListInstancesResponse`): + response (google.cloud.redis_v1beta1.types.ListInstancesResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ class ListInstancesAsyncPager: """A pager for iterating through ``list_instances`` requests. This class thinly wraps an initial - :class:`~.cloud_redis.ListInstancesResponse` object, and + :class:`google.cloud.redis_v1beta1.types.ListInstancesResponse` object, and provides an ``__aiter__`` method to iterate through its ``instances`` field. @@ -95,7 +104,7 @@ class ListInstancesAsyncPager: through the ``instances`` field on the corresponding responses. - All the usual :class:`~.cloud_redis.ListInstancesResponse` + All the usual :class:`google.cloud.redis_v1beta1.types.ListInstancesResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -113,9 +122,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.cloud_redis.ListInstancesRequest`): + request (google.cloud.redis_v1beta1.types.ListInstancesRequest): The initial request object. - response (:class:`~.cloud_redis.ListInstancesResponse`): + response (google.cloud.redis_v1beta1.types.ListInstancesResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/cloud/redis_v1beta1/services/cloud_redis/transports/base.py b/google/cloud/redis_v1beta1/services/cloud_redis/transports/base.py index 71337ab..c3704df 100644 --- a/google/cloud/redis_v1beta1/services/cloud_redis/transports/base.py +++ b/google/cloud/redis_v1beta1/services/cloud_redis/transports/base.py @@ -69,10 +69,10 @@ def __init__( scope (Optional[Sequence[str]]): A list of scopes. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. """ # Save the hostname. Default to port 443 (HTTPS) if none is specified. @@ -80,6 +80,9 @@ def __init__( host += ":443" self._host = host + # Save the scopes. + self._scopes = scopes or self.AUTH_SCOPES + # If no credentials are provided, then determine the appropriate # defaults. if credentials and credentials_file: @@ -89,20 +92,17 @@ def __init__( if credentials_file is not None: credentials, _ = auth.load_credentials_from_file( - credentials_file, scopes=scopes, quota_project_id=quota_project_id + credentials_file, scopes=self._scopes, quota_project_id=quota_project_id ) elif credentials is None: credentials, _ = auth.default( - scopes=scopes, quota_project_id=quota_project_id + scopes=self._scopes, quota_project_id=quota_project_id ) # Save the credentials. self._credentials = credentials - # Lifted into its own function so it can be stubbed out during tests. - self._prep_wrapped_messages(client_info) - def _prep_wrapped_messages(self, client_info): # Precompute the wrapped methods. self._wrapped_methods = { diff --git a/google/cloud/redis_v1beta1/services/cloud_redis/transports/grpc.py b/google/cloud/redis_v1beta1/services/cloud_redis/transports/grpc.py index 1354c3d..1f3c559 100644 --- a/google/cloud/redis_v1beta1/services/cloud_redis/transports/grpc.py +++ b/google/cloud/redis_v1beta1/services/cloud_redis/transports/grpc.py @@ -79,6 +79,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -109,6 +110,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -123,72 +128,61 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._grpc_channel = None self._ssl_channel_credentials = ssl_channel_credentials + self._stubs: Dict[str, Callable] = {} + self._operations_client = None + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) if channel: - # Sanity check: Ensure that channel and credentials are not both - # provided. + # Ignore credentials if a channel was passed. credentials = False - # If a channel was explicitly provided, set it. self._grpc_channel = channel self._ssl_channel_credentials = None - elif api_mtls_endpoint: - warnings.warn( - "api_mtls_endpoint and client_cert_source are deprecated", - DeprecationWarning, - ) - host = ( - api_mtls_endpoint - if ":" in api_mtls_endpoint - else api_mtls_endpoint + ":443" - ) + else: + if api_mtls_endpoint: + host = api_mtls_endpoint + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + self._ssl_channel_credentials = SslCredentials().ssl_credentials - if credentials is None: - credentials, _ = auth.default( - scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id - ) - - # Create SSL credentials with client_cert_source or application - # default SSL credentials. - if client_cert_source: - cert, key = client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) else: - ssl_credentials = SslCredentials().ssl_credentials + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) - # create a new channel. The provided one is ignored. - self._grpc_channel = type(self).create_channel( - host, - credentials=credentials, - credentials_file=credentials_file, - ssl_credentials=ssl_credentials, - scopes=scopes or self.AUTH_SCOPES, - quota_project_id=quota_project_id, - options=[ - ("grpc.max_send_message_length", -1), - ("grpc.max_receive_message_length", -1), - ], - ) - self._ssl_channel_credentials = ssl_credentials - else: - host = host if ":" in host else host + ":443" - - if credentials is None: - credentials, _ = auth.default( - scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id - ) + # The base transport sets the host, credentials and scopes + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + client_info=client_info, + ) - # create a new channel. The provided one is ignored. + if not self._grpc_channel: self._grpc_channel = type(self).create_channel( - host, - credentials=credentials, + self._host, + credentials=self._credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, - scopes=scopes or self.AUTH_SCOPES, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, quota_project_id=quota_project_id, options=[ ("grpc.max_send_message_length", -1), @@ -196,18 +190,8 @@ def __init__( ], ) - self._stubs = {} # type: Dict[str, Callable] - self._operations_client = None - - # Run the base constructor. - super().__init__( - host=host, - credentials=credentials, - credentials_file=credentials_file, - scopes=scopes or self.AUTH_SCOPES, - quota_project_id=quota_project_id, - client_info=client_info, - ) + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) @classmethod def create_channel( @@ -221,7 +205,7 @@ def create_channel( ) -> grpc.Channel: """Create and return a gRPC channel object. Args: - address (Optional[str]): The host for the channel to use. + host (Optional[str]): The host for the channel to use. credentials (Optional[~.Credentials]): The authorization credentials to attach to requests. These credentials identify this application to the service. If diff --git a/google/cloud/redis_v1beta1/services/cloud_redis/transports/grpc_asyncio.py b/google/cloud/redis_v1beta1/services/cloud_redis/transports/grpc_asyncio.py index b091718..62c9e0f 100644 --- a/google/cloud/redis_v1beta1/services/cloud_redis/transports/grpc_asyncio.py +++ b/google/cloud/redis_v1beta1/services/cloud_redis/transports/grpc_asyncio.py @@ -83,7 +83,7 @@ def create_channel( ) -> aio.Channel: """Create and return a gRPC AsyncIO channel object. Args: - address (Optional[str]): The host for the channel to use. + host (Optional[str]): The host for the channel to use. credentials (Optional[~.Credentials]): The authorization credentials to attach to requests. These credentials identify this application to the service. If @@ -123,6 +123,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -154,12 +155,16 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -168,72 +173,61 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._grpc_channel = None self._ssl_channel_credentials = ssl_channel_credentials + self._stubs: Dict[str, Callable] = {} + self._operations_client = None + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) if channel: - # Sanity check: Ensure that channel and credentials are not both - # provided. + # Ignore credentials if a channel was passed. credentials = False - # If a channel was explicitly provided, set it. self._grpc_channel = channel self._ssl_channel_credentials = None - elif api_mtls_endpoint: - warnings.warn( - "api_mtls_endpoint and client_cert_source are deprecated", - DeprecationWarning, - ) - host = ( - api_mtls_endpoint - if ":" in api_mtls_endpoint - else api_mtls_endpoint + ":443" - ) + else: + if api_mtls_endpoint: + host = api_mtls_endpoint + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + self._ssl_channel_credentials = SslCredentials().ssl_credentials - if credentials is None: - credentials, _ = auth.default( - scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id - ) - - # Create SSL credentials with client_cert_source or application - # default SSL credentials. - if client_cert_source: - cert, key = client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) else: - ssl_credentials = SslCredentials().ssl_credentials + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) - # create a new channel. The provided one is ignored. - self._grpc_channel = type(self).create_channel( - host, - credentials=credentials, - credentials_file=credentials_file, - ssl_credentials=ssl_credentials, - scopes=scopes or self.AUTH_SCOPES, - quota_project_id=quota_project_id, - options=[ - ("grpc.max_send_message_length", -1), - ("grpc.max_receive_message_length", -1), - ], - ) - self._ssl_channel_credentials = ssl_credentials - else: - host = host if ":" in host else host + ":443" - - if credentials is None: - credentials, _ = auth.default( - scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id - ) + # The base transport sets the host, credentials and scopes + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + client_info=client_info, + ) - # create a new channel. The provided one is ignored. + if not self._grpc_channel: self._grpc_channel = type(self).create_channel( - host, - credentials=credentials, + self._host, + credentials=self._credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, - scopes=scopes or self.AUTH_SCOPES, + scopes=self._scopes, + ssl_credentials=self._ssl_channel_credentials, quota_project_id=quota_project_id, options=[ ("grpc.max_send_message_length", -1), @@ -241,18 +235,8 @@ def __init__( ], ) - # Run the base constructor. - super().__init__( - host=host, - credentials=credentials, - credentials_file=credentials_file, - scopes=scopes or self.AUTH_SCOPES, - quota_project_id=quota_project_id, - client_info=client_info, - ) - - self._stubs = {} - self._operations_client = None + # Wrap messages. This must be done after self._grpc_channel exists + self._prep_wrapped_messages(client_info) @property def grpc_channel(self) -> aio.Channel: diff --git a/google/cloud/redis_v1beta1/types/__init__.py b/google/cloud/redis_v1beta1/types/__init__.py index d2450f1..918e784 100644 --- a/google/cloud/redis_v1beta1/types/__init__.py +++ b/google/cloud/redis_v1beta1/types/__init__.py @@ -16,41 +16,41 @@ # from .cloud_redis import ( - Instance, - ListInstancesRequest, - ListInstancesResponse, - GetInstanceRequest, CreateInstanceRequest, - UpdateInstanceRequest, - UpgradeInstanceRequest, DeleteInstanceRequest, - GcsSource, - InputConfig, - ImportInstanceRequest, - GcsDestination, - OutputConfig, ExportInstanceRequest, FailoverInstanceRequest, + GcsDestination, + GcsSource, + GetInstanceRequest, + ImportInstanceRequest, + InputConfig, + Instance, + ListInstancesRequest, + ListInstancesResponse, LocationMetadata, + OutputConfig, + UpdateInstanceRequest, + UpgradeInstanceRequest, ZoneMetadata, ) __all__ = ( - "Instance", - "ListInstancesRequest", - "ListInstancesResponse", - "GetInstanceRequest", "CreateInstanceRequest", - "UpdateInstanceRequest", - "UpgradeInstanceRequest", "DeleteInstanceRequest", - "GcsSource", - "InputConfig", - "ImportInstanceRequest", - "GcsDestination", - "OutputConfig", "ExportInstanceRequest", "FailoverInstanceRequest", + "GcsDestination", + "GcsSource", + "GetInstanceRequest", + "ImportInstanceRequest", + "InputConfig", + "Instance", + "ListInstancesRequest", + "ListInstancesResponse", "LocationMetadata", + "OutputConfig", + "UpdateInstanceRequest", + "UpgradeInstanceRequest", "ZoneMetadata", ) diff --git a/google/cloud/redis_v1beta1/types/cloud_redis.py b/google/cloud/redis_v1beta1/types/cloud_redis.py index 5b7c71f..8b588ae 100644 --- a/google/cloud/redis_v1beta1/types/cloud_redis.py +++ b/google/cloud/redis_v1beta1/types/cloud_redis.py @@ -67,7 +67,7 @@ class Instance(proto.Message): display_name (str): An arbitrary and optional user-provided name for the instance. - labels (Sequence[~.cloud_redis.Instance.LabelsEntry]): + labels (Sequence[google.cloud.redis_v1beta1.types.Instance.LabelsEntry]): Resource labels to represent user provided metadata location_id (str): @@ -118,16 +118,16 @@ class Instance(proto.Message): or [alternative_location_id][google.cloud.redis.v1beta1.Instance.alternative_location_id] and can change after a failover event. - create_time (~.timestamp.Timestamp): + create_time (google.protobuf.timestamp_pb2.Timestamp): Output only. The time the instance was created. - state (~.cloud_redis.Instance.State): + state (google.cloud.redis_v1beta1.types.Instance.State): Output only. The current state of this instance. status_message (str): Output only. Additional information about the current status of this instance, if available. - redis_configs (Sequence[~.cloud_redis.Instance.RedisConfigsEntry]): + redis_configs (Sequence[google.cloud.redis_v1beta1.types.Instance.RedisConfigsEntry]): Optional. Redis configuration parameters, according to http://redis.io/topics/config. Currently, the only supported parameters are: @@ -148,7 +148,7 @@ class Instance(proto.Message): - stream-node-max-bytes - stream-node-max-entries - tier (~.cloud_redis.Instance.Tier): + tier (google.cloud.redis_v1beta1.types.Instance.Tier): Required. The service tier of the instance. memory_size_gb (int): Required. Redis memory size in GiB. @@ -163,7 +163,7 @@ class Instance(proto.Message): "serviceAccount:". The value may change over time for a given instance so should be checked before each import/export operation. - connect_mode (~.cloud_redis.Instance.ConnectMode): + connect_mode (google.cloud.redis_v1beta1.types.Instance.ConnectMode): Optional. The connect mode of Redis instance. If not provided, default one will be used. Current default: DIRECT_PEERING. @@ -268,7 +268,7 @@ class ListInstancesResponse(proto.Message): [ListInstances][google.cloud.redis.v1beta1.CloudRedis.ListInstances]. Attributes: - instances (Sequence[~.cloud_redis.Instance]): + instances (Sequence[google.cloud.redis_v1beta1.types.Instance]): A list of Redis instances in the project in the specified location, or across all locations. @@ -333,7 +333,7 @@ class CreateInstanceRequest(proto.Message): - Must be between 1-40 characters. - Must end with a number or a letter. - Must be unique within the customer project / location - instance (~.cloud_redis.Instance): + instance (google.cloud.redis_v1beta1.types.Instance): Required. A Redis [Instance] resource """ @@ -349,7 +349,7 @@ class UpdateInstanceRequest(proto.Message): [UpdateInstance][google.cloud.redis.v1beta1.CloudRedis.UpdateInstance]. Attributes: - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Required. Mask of fields to update. At least one path must be supplied in this field. The elements of the repeated paths field may only include these fields from @@ -359,7 +359,7 @@ class UpdateInstanceRequest(proto.Message): - ``labels`` - ``memorySizeGb`` - ``redisConfig`` - instance (~.cloud_redis.Instance): + instance (google.cloud.redis_v1beta1.types.Instance): Required. Update description. Only fields specified in update_mask are updated. """ @@ -418,7 +418,7 @@ class InputConfig(proto.Message): r"""The input content Attributes: - gcs_source (~.cloud_redis.GcsSource): + gcs_source (google.cloud.redis_v1beta1.types.GcsSource): Google Cloud Storage location where input content is located. """ @@ -437,7 +437,7 @@ class ImportInstanceRequest(proto.Message): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. - input_config (~.cloud_redis.InputConfig): + input_config (google.cloud.redis_v1beta1.types.InputConfig): Required. Specify data to be imported. """ @@ -463,7 +463,7 @@ class OutputConfig(proto.Message): r"""The output content Attributes: - gcs_destination (~.cloud_redis.GcsDestination): + gcs_destination (google.cloud.redis_v1beta1.types.GcsDestination): Google Cloud Storage destination for output content. """ @@ -482,7 +482,7 @@ class ExportInstanceRequest(proto.Message): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. - output_config (~.cloud_redis.OutputConfig): + output_config (google.cloud.redis_v1beta1.types.OutputConfig): Required. Specify data to be exported. """ @@ -500,7 +500,7 @@ class FailoverInstanceRequest(proto.Message): Required. Redis instance resource name using the form: ``projects/{project_id}/locations/{location_id}/instances/{instance_id}`` where ``location_id`` refers to a GCP region. - data_protection_mode (~.cloud_redis.FailoverInstanceRequest.DataProtectionMode): + data_protection_mode (google.cloud.redis_v1beta1.types.FailoverInstanceRequest.DataProtectionMode): Optional. Available data protection modes that the user can choose. If it's unspecified, data protection mode will be LIMITED_DATA_LOSS by default. @@ -526,7 +526,7 @@ class LocationMetadata(proto.Message): ``google.cloud.location.Location.metadata`` field. Attributes: - available_zones (Sequence[~.cloud_redis.LocationMetadata.AvailableZonesEntry]): + available_zones (Sequence[google.cloud.redis_v1beta1.types.LocationMetadata.AvailableZonesEntry]): Output only. The set of available zones in the location. The map is keyed by the lowercase ID of each zone, as defined by GCE. These keys can be specified in ``location_id`` or diff --git a/noxfile.py b/noxfile.py index a684a53..43dd302 100644 --- a/noxfile.py +++ b/noxfile.py @@ -18,6 +18,7 @@ from __future__ import absolute_import import os +import pathlib import shutil import nox @@ -30,6 +31,22 @@ SYSTEM_TEST_PYTHON_VERSIONS = ["3.8"] UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8", "3.9"] +CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute() + +# 'docfx' is excluded since it only needs to run in 'docs-presubmit' +nox.options.sessions = [ + "unit", + "system", + "cover", + "lint", + "lint_setup_py", + "blacken", + "docs", +] + +# Error if a python version is missing +nox.options.error_on_missing_interpreters = True + @nox.session(python=DEFAULT_PYTHON_VERSION) def lint(session): @@ -70,17 +87,21 @@ def lint_setup_py(session): def default(session): # Install all test dependencies, then install this package in-place. - session.install("asyncmock", "pytest-asyncio") - session.install( - "mock", "pytest", "pytest-cov", + constraints_path = str( + CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt" ) - session.install("-e", ".") + session.install("asyncmock", "pytest-asyncio", "-c", constraints_path) + + session.install("mock", "pytest", "pytest-cov", "-c", constraints_path) + + session.install("-e", ".", "-c", constraints_path) # Run py.test against the unit tests. session.run( "py.test", "--quiet", + f"--junitxml=unit_{session.python}_sponge_log.xml", "--cov=google/cloud", "--cov=tests/unit", "--cov-append", @@ -101,6 +122,9 @@ def unit(session): @nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS) def system(session): """Run the system test suite.""" + constraints_path = str( + CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt" + ) system_test_path = os.path.join("tests", "system.py") system_test_folder_path = os.path.join("tests", "system") @@ -110,6 +134,9 @@ def system(session): # Sanity check: Only run tests if the environment variable is set. if not os.environ.get("GOOGLE_APPLICATION_CREDENTIALS", ""): session.skip("Credentials must be set via environment variable") + # Install pyopenssl for mTLS testing. + if os.environ.get("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") == "true": + session.install("pyopenssl") system_test_exists = os.path.exists(system_test_path) system_test_folder_exists = os.path.exists(system_test_folder_path) @@ -122,16 +149,26 @@ def system(session): # Install all test dependencies, then install this package into the # virtualenv's dist-packages. - session.install( - "mock", "pytest", "google-cloud-testutils", - ) - session.install("-e", ".") + session.install("mock", "pytest", "google-cloud-testutils", "-c", constraints_path) + session.install("-e", ".", "-c", constraints_path) # Run py.test against the system tests. if system_test_exists: - session.run("py.test", "--quiet", system_test_path, *session.posargs) + session.run( + "py.test", + "--quiet", + f"--junitxml=system_{session.python}_sponge_log.xml", + system_test_path, + *session.posargs, + ) if system_test_folder_exists: - session.run("py.test", "--quiet", system_test_folder_path, *session.posargs) + session.run( + "py.test", + "--quiet", + f"--junitxml=system_{session.python}_sponge_log.xml", + system_test_folder_path, + *session.posargs, + ) @nox.session(python=DEFAULT_PYTHON_VERSION) @@ -142,7 +179,7 @@ def cover(session): test runs (not system test runs), and then erases coverage data. """ session.install("coverage", "pytest-cov") - session.run("coverage", "report", "--show-missing", "--fail-under=100") + session.run("coverage", "report", "--show-missing", "--fail-under=98") session.run("coverage", "erase") @@ -157,7 +194,7 @@ def docs(session): shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) session.run( "sphinx-build", - # "-W", # warnings as errors + "-W", # warnings as errors "-T", # show full traceback on exception "-N", # no colors "-b", diff --git a/renovate.json b/renovate.json index 4fa9493..f08bc22 100644 --- a/renovate.json +++ b/renovate.json @@ -1,5 +1,6 @@ { "extends": [ "config:base", ":preserveSemverRanges" - ] + ], + "ignorePaths": [".pre-commit-config.yaml"] } diff --git a/setup.py b/setup.py index bb46cf0..9f24ef7 100644 --- a/setup.py +++ b/setup.py @@ -31,9 +31,8 @@ dependencies = [ "google-api-core[grpc] >= 1.22.2, < 2.0.0dev", "proto-plus >= 1.4.0", - "libcst >= 0.2.5", ] -extras = {} +extras = {"libcst": "libcst >= 0.2.5"} # Setup boilerplate below this line. diff --git a/synth.metadata b/synth.metadata index a954d58..5971dff 100644 --- a/synth.metadata +++ b/synth.metadata @@ -3,23 +3,23 @@ { "git": { "name": ".", - "remote": "https://github.com/googleapis/python-redis.git", - "sha": "add073580f4734289adc03abe8a44657c8686951" + "remote": "git@github.com:googleapis/python-redis", + "sha": "3d90e1342fd8af0dbbde9fe11ea9a3ec7fd490cc" } }, { "git": { "name": "googleapis", "remote": "https://github.com/googleapis/googleapis.git", - "sha": "dd372aa22ded7a8ba6f0e03a80e06358a3fa0907", - "internalRef": "347055288" + "sha": "95dd24960cf9f794ef583e59ad9f1fabe1c4a924", + "internalRef": "365882072" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "373861061648b5fe5e0ac4f8a38b32d639ee93e4" + "sha": "4dc31ac1ece23dc555f574aa701e5857b5bf2d3f" } } ], @@ -42,114 +42,5 @@ "generator": "bazel" } } - ], - "generatedFiles": [ - ".flake8", - ".github/CONTRIBUTING.md", - ".github/ISSUE_TEMPLATE/bug_report.md", - ".github/ISSUE_TEMPLATE/feature_request.md", - ".github/ISSUE_TEMPLATE/support_request.md", - ".github/PULL_REQUEST_TEMPLATE.md", - ".github/release-please.yml", - ".github/snippet-bot.yml", - ".gitignore", - ".kokoro/build.sh", - ".kokoro/continuous/common.cfg", - ".kokoro/continuous/continuous.cfg", - ".kokoro/docker/docs/Dockerfile", - ".kokoro/docker/docs/fetch_gpg_keys.sh", - ".kokoro/docs/common.cfg", - ".kokoro/docs/docs-presubmit.cfg", - ".kokoro/docs/docs.cfg", - ".kokoro/populate-secrets.sh", - ".kokoro/presubmit/common.cfg", - ".kokoro/presubmit/presubmit.cfg", - ".kokoro/publish-docs.sh", - ".kokoro/release.sh", - ".kokoro/release/common.cfg", - ".kokoro/release/release.cfg", - ".kokoro/samples/lint/common.cfg", - ".kokoro/samples/lint/continuous.cfg", - ".kokoro/samples/lint/periodic.cfg", - ".kokoro/samples/lint/presubmit.cfg", - ".kokoro/samples/python3.6/common.cfg", - ".kokoro/samples/python3.6/continuous.cfg", - ".kokoro/samples/python3.6/periodic.cfg", - ".kokoro/samples/python3.6/presubmit.cfg", - ".kokoro/samples/python3.7/common.cfg", - ".kokoro/samples/python3.7/continuous.cfg", - ".kokoro/samples/python3.7/periodic.cfg", - ".kokoro/samples/python3.7/presubmit.cfg", - ".kokoro/samples/python3.8/common.cfg", - ".kokoro/samples/python3.8/continuous.cfg", - ".kokoro/samples/python3.8/periodic.cfg", - ".kokoro/samples/python3.8/presubmit.cfg", - ".kokoro/test-samples.sh", - ".kokoro/trampoline.sh", - ".kokoro/trampoline_v2.sh", - ".pre-commit-config.yaml", - ".trampolinerc", - "CODE_OF_CONDUCT.md", - "CONTRIBUTING.rst", - "LICENSE", - "MANIFEST.in", - "docs/_static/custom.css", - "docs/_templates/layout.html", - "docs/conf.py", - "docs/multiprocessing.rst", - "docs/redis_v1/services.rst", - "docs/redis_v1/types.rst", - "docs/redis_v1beta1/services.rst", - "docs/redis_v1beta1/types.rst", - "google/cloud/redis/__init__.py", - "google/cloud/redis/py.typed", - "google/cloud/redis_v1/__init__.py", - "google/cloud/redis_v1/proto/cloud_redis.proto", - "google/cloud/redis_v1/py.typed", - "google/cloud/redis_v1/services/__init__.py", - "google/cloud/redis_v1/services/cloud_redis/__init__.py", - "google/cloud/redis_v1/services/cloud_redis/async_client.py", - "google/cloud/redis_v1/services/cloud_redis/client.py", - "google/cloud/redis_v1/services/cloud_redis/pagers.py", - "google/cloud/redis_v1/services/cloud_redis/transports/__init__.py", - "google/cloud/redis_v1/services/cloud_redis/transports/base.py", - "google/cloud/redis_v1/services/cloud_redis/transports/grpc.py", - "google/cloud/redis_v1/services/cloud_redis/transports/grpc_asyncio.py", - "google/cloud/redis_v1/types/__init__.py", - "google/cloud/redis_v1/types/cloud_redis.py", - "google/cloud/redis_v1beta1/__init__.py", - "google/cloud/redis_v1beta1/proto/cloud_redis.proto", - "google/cloud/redis_v1beta1/py.typed", - "google/cloud/redis_v1beta1/services/__init__.py", - "google/cloud/redis_v1beta1/services/cloud_redis/__init__.py", - "google/cloud/redis_v1beta1/services/cloud_redis/async_client.py", - "google/cloud/redis_v1beta1/services/cloud_redis/client.py", - "google/cloud/redis_v1beta1/services/cloud_redis/pagers.py", - "google/cloud/redis_v1beta1/services/cloud_redis/transports/__init__.py", - "google/cloud/redis_v1beta1/services/cloud_redis/transports/base.py", - "google/cloud/redis_v1beta1/services/cloud_redis/transports/grpc.py", - "google/cloud/redis_v1beta1/services/cloud_redis/transports/grpc_asyncio.py", - "google/cloud/redis_v1beta1/types/__init__.py", - "google/cloud/redis_v1beta1/types/cloud_redis.py", - "mypy.ini", - "noxfile.py", - "renovate.json", - "samples/AUTHORING_GUIDE.md", - "samples/CONTRIBUTING.md", - "scripts/decrypt-secrets.sh", - "scripts/fixup_redis_v1_keywords.py", - "scripts/fixup_redis_v1beta1_keywords.py", - "scripts/readme-gen/readme_gen.py", - "scripts/readme-gen/templates/README.tmpl.rst", - "scripts/readme-gen/templates/auth.tmpl.rst", - "scripts/readme-gen/templates/auth_api_key.tmpl.rst", - "scripts/readme-gen/templates/install_deps.tmpl.rst", - "scripts/readme-gen/templates/install_portaudio.tmpl.rst", - "setup.cfg", - "testing/.gitignore", - "tests/unit/gapic/redis_v1/__init__.py", - "tests/unit/gapic/redis_v1/test_cloud_redis.py", - "tests/unit/gapic/redis_v1beta1/__init__.py", - "tests/unit/gapic/redis_v1beta1/test_cloud_redis.py" ] } \ No newline at end of file diff --git a/synth.py b/synth.py index b020dc5..d43dd7c 100644 --- a/synth.py +++ b/synth.py @@ -43,6 +43,7 @@ templated_files = common.py_library( samples=False, microgenerator=True, + cov_level=98, ) s.move(templated_files, excludes=[".coveragerc"]) # microgenerator has a good .coveragerc file diff --git a/tests/unit/gapic/redis_v1/__init__.py b/tests/unit/gapic/redis_v1/__init__.py index 8b13789..42ffdf2 100644 --- a/tests/unit/gapic/redis_v1/__init__.py +++ b/tests/unit/gapic/redis_v1/__init__.py @@ -1 +1,16 @@ +# -*- coding: utf-8 -*- +# 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. +# diff --git a/tests/unit/gapic/redis_v1/test_cloud_redis.py b/tests/unit/gapic/redis_v1/test_cloud_redis.py index 7712657..0527721 100644 --- a/tests/unit/gapic/redis_v1/test_cloud_redis.py +++ b/tests/unit/gapic/redis_v1/test_cloud_redis.py @@ -87,7 +87,22 @@ def test__get_default_mtls_endpoint(): assert CloudRedisClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi -@pytest.mark.parametrize("client_class", [CloudRedisClient, CloudRedisAsyncClient]) +@pytest.mark.parametrize("client_class", [CloudRedisClient, CloudRedisAsyncClient,]) +def test_cloud_redis_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "redis.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [CloudRedisClient, CloudRedisAsyncClient,]) def test_cloud_redis_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() with mock.patch.object( @@ -96,16 +111,21 @@ def test_cloud_redis_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "redis.googleapis.com:443" def test_cloud_redis_client_get_transport_class(): transport = CloudRedisClient.get_transport_class() - assert transport == transports.CloudRedisGrpcTransport + available_transports = [ + transports.CloudRedisGrpcTransport, + ] + assert transport in available_transports transport = CloudRedisClient.get_transport_class("grpc") assert transport == transports.CloudRedisGrpcTransport @@ -154,7 +174,7 @@ def test_cloud_redis_client_client_options( credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -170,7 +190,7 @@ def test_cloud_redis_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -186,7 +206,7 @@ def test_cloud_redis_client_client_options( credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -214,7 +234,7 @@ def test_cloud_redis_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -263,29 +283,25 @@ def test_cloud_redis_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -294,66 +310,53 @@ def test_cloud_redis_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -379,7 +382,7 @@ def test_cloud_redis_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -409,7 +412,7 @@ def test_cloud_redis_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -426,7 +429,7 @@ def test_cloud_redis_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -471,6 +474,22 @@ def test_list_instances_from_dict(): test_list_instances(request_type=dict) +def test_list_instances_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = CloudRedisClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_instances), "__call__") as call: + client.list_instances() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == cloud_redis.ListInstancesRequest() + + @pytest.mark.asyncio async def test_list_instances_async( transport: str = "grpc_asyncio", request_type=cloud_redis.ListInstancesRequest @@ -852,6 +871,22 @@ def test_get_instance_from_dict(): test_get_instance(request_type=dict) +def test_get_instance_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = CloudRedisClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_instance), "__call__") as call: + client.get_instance() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == cloud_redis.GetInstanceRequest() + + @pytest.mark.asyncio async def test_get_instance_async( transport: str = "grpc_asyncio", request_type=cloud_redis.GetInstanceRequest @@ -1087,6 +1122,22 @@ def test_create_instance_from_dict(): test_create_instance(request_type=dict) +def test_create_instance_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = CloudRedisClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_instance), "__call__") as call: + client.create_instance() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == cloud_redis.CreateInstanceRequest() + + @pytest.mark.asyncio async def test_create_instance_async( transport: str = "grpc_asyncio", request_type=cloud_redis.CreateInstanceRequest @@ -1295,6 +1346,22 @@ def test_update_instance_from_dict(): test_update_instance(request_type=dict) +def test_update_instance_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = CloudRedisClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_instance), "__call__") as call: + client.update_instance() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == cloud_redis.UpdateInstanceRequest() + + @pytest.mark.asyncio async def test_update_instance_async( transport: str = "grpc_asyncio", request_type=cloud_redis.UpdateInstanceRequest @@ -1499,6 +1566,22 @@ def test_upgrade_instance_from_dict(): test_upgrade_instance(request_type=dict) +def test_upgrade_instance_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = CloudRedisClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.upgrade_instance), "__call__") as call: + client.upgrade_instance() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == cloud_redis.UpgradeInstanceRequest() + + @pytest.mark.asyncio async def test_upgrade_instance_async( transport: str = "grpc_asyncio", request_type=cloud_redis.UpgradeInstanceRequest @@ -1697,6 +1780,22 @@ def test_import_instance_from_dict(): test_import_instance(request_type=dict) +def test_import_instance_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = CloudRedisClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.import_instance), "__call__") as call: + client.import_instance() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == cloud_redis.ImportInstanceRequest() + + @pytest.mark.asyncio async def test_import_instance_async( transport: str = "grpc_asyncio", request_type=cloud_redis.ImportInstanceRequest @@ -1909,6 +2008,22 @@ def test_export_instance_from_dict(): test_export_instance(request_type=dict) +def test_export_instance_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = CloudRedisClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.export_instance), "__call__") as call: + client.export_instance() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == cloud_redis.ExportInstanceRequest() + + @pytest.mark.asyncio async def test_export_instance_async( transport: str = "grpc_asyncio", request_type=cloud_redis.ExportInstanceRequest @@ -2123,6 +2238,24 @@ def test_failover_instance_from_dict(): test_failover_instance(request_type=dict) +def test_failover_instance_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = CloudRedisClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.failover_instance), "__call__" + ) as call: + client.failover_instance() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == cloud_redis.FailoverInstanceRequest() + + @pytest.mark.asyncio async def test_failover_instance_async( transport: str = "grpc_asyncio", request_type=cloud_redis.FailoverInstanceRequest @@ -2339,6 +2472,22 @@ def test_delete_instance_from_dict(): test_delete_instance(request_type=dict) +def test_delete_instance_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = CloudRedisClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_instance), "__call__") as call: + client.delete_instance() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == cloud_redis.DeleteInstanceRequest() + + @pytest.mark.asyncio async def test_delete_instance_async( transport: str = "grpc_asyncio", request_type=cloud_redis.DeleteInstanceRequest @@ -2549,7 +2698,7 @@ def test_transport_get_channel(): @pytest.mark.parametrize( "transport_class", - [transports.CloudRedisGrpcTransport, transports.CloudRedisGrpcAsyncIOTransport], + [transports.CloudRedisGrpcTransport, transports.CloudRedisGrpcAsyncIOTransport,], ) def test_transport_adc(transport_class): # Test default credentials are used if not provided. @@ -2662,6 +2811,48 @@ def test_cloud_redis_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [transports.CloudRedisGrpcTransport, transports.CloudRedisGrpcAsyncIOTransport], +) +def test_cloud_redis_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=("https://www.googleapis.com/auth/cloud-platform",), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_cloud_redis_host_no_port(): client = CloudRedisClient( credentials=credentials.AnonymousCredentials(), @@ -2683,7 +2874,7 @@ def test_cloud_redis_host_with_port(): def test_cloud_redis_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.CloudRedisGrpcTransport( @@ -2695,7 +2886,7 @@ def test_cloud_redis_grpc_transport_channel(): def test_cloud_redis_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.CloudRedisGrpcAsyncIOTransport( @@ -2706,6 +2897,8 @@ def test_cloud_redis_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.CloudRedisGrpcTransport, transports.CloudRedisGrpcAsyncIOTransport], @@ -2715,7 +2908,7 @@ def test_cloud_redis_transport_channel_mtls_with_client_cert_source(transport_cl "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -2753,6 +2946,8 @@ def test_cloud_redis_transport_channel_mtls_with_client_cert_source(transport_cl assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.CloudRedisGrpcTransport, transports.CloudRedisGrpcAsyncIOTransport], @@ -2765,7 +2960,7 @@ def test_cloud_redis_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel diff --git a/tests/unit/gapic/redis_v1beta1/__init__.py b/tests/unit/gapic/redis_v1beta1/__init__.py index 8b13789..42ffdf2 100644 --- a/tests/unit/gapic/redis_v1beta1/__init__.py +++ b/tests/unit/gapic/redis_v1beta1/__init__.py @@ -1 +1,16 @@ +# -*- coding: utf-8 -*- +# 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. +# diff --git a/tests/unit/gapic/redis_v1beta1/test_cloud_redis.py b/tests/unit/gapic/redis_v1beta1/test_cloud_redis.py index 8254137..4c2b6d5 100644 --- a/tests/unit/gapic/redis_v1beta1/test_cloud_redis.py +++ b/tests/unit/gapic/redis_v1beta1/test_cloud_redis.py @@ -88,7 +88,22 @@ def test__get_default_mtls_endpoint(): assert CloudRedisClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi -@pytest.mark.parametrize("client_class", [CloudRedisClient, CloudRedisAsyncClient]) +@pytest.mark.parametrize("client_class", [CloudRedisClient, CloudRedisAsyncClient,]) +def test_cloud_redis_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "redis.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [CloudRedisClient, CloudRedisAsyncClient,]) def test_cloud_redis_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() with mock.patch.object( @@ -97,16 +112,21 @@ def test_cloud_redis_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "redis.googleapis.com:443" def test_cloud_redis_client_get_transport_class(): transport = CloudRedisClient.get_transport_class() - assert transport == transports.CloudRedisGrpcTransport + available_transports = [ + transports.CloudRedisGrpcTransport, + ] + assert transport in available_transports transport = CloudRedisClient.get_transport_class("grpc") assert transport == transports.CloudRedisGrpcTransport @@ -155,7 +175,7 @@ def test_cloud_redis_client_client_options( credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -171,7 +191,7 @@ def test_cloud_redis_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -187,7 +207,7 @@ def test_cloud_redis_client_client_options( credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -215,7 +235,7 @@ def test_cloud_redis_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -264,29 +284,25 @@ def test_cloud_redis_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -295,66 +311,53 @@ def test_cloud_redis_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -380,7 +383,7 @@ def test_cloud_redis_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -410,7 +413,7 @@ def test_cloud_redis_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -427,7 +430,7 @@ def test_cloud_redis_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -472,6 +475,22 @@ def test_list_instances_from_dict(): test_list_instances(request_type=dict) +def test_list_instances_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = CloudRedisClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_instances), "__call__") as call: + client.list_instances() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == cloud_redis.ListInstancesRequest() + + @pytest.mark.asyncio async def test_list_instances_async( transport: str = "grpc_asyncio", request_type=cloud_redis.ListInstancesRequest @@ -853,6 +872,22 @@ def test_get_instance_from_dict(): test_get_instance(request_type=dict) +def test_get_instance_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = CloudRedisClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_instance), "__call__") as call: + client.get_instance() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == cloud_redis.GetInstanceRequest() + + @pytest.mark.asyncio async def test_get_instance_async( transport: str = "grpc_asyncio", request_type=cloud_redis.GetInstanceRequest @@ -1088,6 +1123,22 @@ def test_create_instance_from_dict(): test_create_instance(request_type=dict) +def test_create_instance_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = CloudRedisClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_instance), "__call__") as call: + client.create_instance() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == cloud_redis.CreateInstanceRequest() + + @pytest.mark.asyncio async def test_create_instance_async( transport: str = "grpc_asyncio", request_type=cloud_redis.CreateInstanceRequest @@ -1296,6 +1347,22 @@ def test_update_instance_from_dict(): test_update_instance(request_type=dict) +def test_update_instance_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = CloudRedisClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_instance), "__call__") as call: + client.update_instance() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == cloud_redis.UpdateInstanceRequest() + + @pytest.mark.asyncio async def test_update_instance_async( transport: str = "grpc_asyncio", request_type=cloud_redis.UpdateInstanceRequest @@ -1500,6 +1567,22 @@ def test_upgrade_instance_from_dict(): test_upgrade_instance(request_type=dict) +def test_upgrade_instance_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = CloudRedisClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.upgrade_instance), "__call__") as call: + client.upgrade_instance() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == cloud_redis.UpgradeInstanceRequest() + + @pytest.mark.asyncio async def test_upgrade_instance_async( transport: str = "grpc_asyncio", request_type=cloud_redis.UpgradeInstanceRequest @@ -1698,6 +1781,22 @@ def test_import_instance_from_dict(): test_import_instance(request_type=dict) +def test_import_instance_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = CloudRedisClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.import_instance), "__call__") as call: + client.import_instance() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == cloud_redis.ImportInstanceRequest() + + @pytest.mark.asyncio async def test_import_instance_async( transport: str = "grpc_asyncio", request_type=cloud_redis.ImportInstanceRequest @@ -1910,6 +2009,22 @@ def test_export_instance_from_dict(): test_export_instance(request_type=dict) +def test_export_instance_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = CloudRedisClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.export_instance), "__call__") as call: + client.export_instance() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == cloud_redis.ExportInstanceRequest() + + @pytest.mark.asyncio async def test_export_instance_async( transport: str = "grpc_asyncio", request_type=cloud_redis.ExportInstanceRequest @@ -2124,6 +2239,24 @@ def test_failover_instance_from_dict(): test_failover_instance(request_type=dict) +def test_failover_instance_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = CloudRedisClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.failover_instance), "__call__" + ) as call: + client.failover_instance() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == cloud_redis.FailoverInstanceRequest() + + @pytest.mark.asyncio async def test_failover_instance_async( transport: str = "grpc_asyncio", request_type=cloud_redis.FailoverInstanceRequest @@ -2340,6 +2473,22 @@ def test_delete_instance_from_dict(): test_delete_instance(request_type=dict) +def test_delete_instance_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = CloudRedisClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_instance), "__call__") as call: + client.delete_instance() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == cloud_redis.DeleteInstanceRequest() + + @pytest.mark.asyncio async def test_delete_instance_async( transport: str = "grpc_asyncio", request_type=cloud_redis.DeleteInstanceRequest @@ -2550,7 +2699,7 @@ def test_transport_get_channel(): @pytest.mark.parametrize( "transport_class", - [transports.CloudRedisGrpcTransport, transports.CloudRedisGrpcAsyncIOTransport], + [transports.CloudRedisGrpcTransport, transports.CloudRedisGrpcAsyncIOTransport,], ) def test_transport_adc(transport_class): # Test default credentials are used if not provided. @@ -2663,6 +2812,48 @@ def test_cloud_redis_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [transports.CloudRedisGrpcTransport, transports.CloudRedisGrpcAsyncIOTransport], +) +def test_cloud_redis_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=("https://www.googleapis.com/auth/cloud-platform",), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_cloud_redis_host_no_port(): client = CloudRedisClient( credentials=credentials.AnonymousCredentials(), @@ -2684,7 +2875,7 @@ def test_cloud_redis_host_with_port(): def test_cloud_redis_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.CloudRedisGrpcTransport( @@ -2696,7 +2887,7 @@ def test_cloud_redis_grpc_transport_channel(): def test_cloud_redis_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.CloudRedisGrpcAsyncIOTransport( @@ -2707,6 +2898,8 @@ def test_cloud_redis_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.CloudRedisGrpcTransport, transports.CloudRedisGrpcAsyncIOTransport], @@ -2716,7 +2909,7 @@ def test_cloud_redis_transport_channel_mtls_with_client_cert_source(transport_cl "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -2754,6 +2947,8 @@ def test_cloud_redis_transport_channel_mtls_with_client_cert_source(transport_cl assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.CloudRedisGrpcTransport, transports.CloudRedisGrpcAsyncIOTransport], @@ -2766,7 +2961,7 @@ def test_cloud_redis_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel