diff --git a/.flake8 b/.flake8 index 20fe9bda..ed931638 100644 --- a/.flake8 +++ b/.flake8 @@ -21,6 +21,8 @@ exclude = # Exclude generated code. **/proto/** **/gapic/** + **/services/** + **/types/** *_pb2.py # Standard linting exemptions. diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..12a98268 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,8 @@ +# Code owners file. +# This file controls who is tagged for review for any given pull request. +# +# For syntax help see: +# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax + + +/samples/**/*.py @DanSanche @googleapis/python-samples-owners diff --git a/.gitignore b/.gitignore index 3fb06e09..b87e1ed5 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ dist build eggs +.eggs parts bin var @@ -49,6 +50,7 @@ bigquery/docs/generated # Virtual environment env/ coverage.xml +sponge_log.xml # System test environment variables. system_tests/local_test_setup diff --git a/.kokoro/publish-docs.sh b/.kokoro/publish-docs.sh index 3a5dbb78..0d7db6e9 100755 --- a/.kokoro/publish-docs.sh +++ b/.kokoro/publish-docs.sh @@ -13,8 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -#!/bin/bash - set -eo pipefail # Disable buffering, so that the logs stream through. diff --git a/.kokoro/release.sh b/.kokoro/release.sh index e98f62b0..5dae4fdc 100755 --- a/.kokoro/release.sh +++ b/.kokoro/release.sh @@ -13,8 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -#!/bin/bash - set -eo pipefail # Start the releasetool reporter diff --git a/.kokoro/samples/lint/common.cfg b/.kokoro/samples/lint/common.cfg new file mode 100644 index 00000000..6c5ff5c5 --- /dev/null +++ b/.kokoro/samples/lint/common.cfg @@ -0,0 +1,34 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Specify which tests to run +env_vars: { + key: "RUN_TESTS_SESSION" + value: "lint" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-kms/.kokoro/test-samples.sh" +} + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" +} + +# Download secrets for samples +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "python-kms/.kokoro/trampoline.sh" \ No newline at end of file diff --git a/.kokoro/samples/lint/continuous.cfg b/.kokoro/samples/lint/continuous.cfg new file mode 100644 index 00000000..a1c8d975 --- /dev/null +++ b/.kokoro/samples/lint/continuous.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/lint/periodic.cfg b/.kokoro/samples/lint/periodic.cfg new file mode 100644 index 00000000..50fec964 --- /dev/null +++ b/.kokoro/samples/lint/periodic.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "False" +} \ No newline at end of file diff --git a/.kokoro/samples/lint/presubmit.cfg b/.kokoro/samples/lint/presubmit.cfg new file mode 100644 index 00000000..a1c8d975 --- /dev/null +++ b/.kokoro/samples/lint/presubmit.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.6/common.cfg b/.kokoro/samples/python3.6/common.cfg new file mode 100644 index 00000000..7de5927f --- /dev/null +++ b/.kokoro/samples/python3.6/common.cfg @@ -0,0 +1,34 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Specify which tests to run +env_vars: { + key: "RUN_TESTS_SESSION" + value: "py-3.6" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-kms/.kokoro/test-samples.sh" +} + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" +} + +# Download secrets for samples +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "python-kms/.kokoro/trampoline.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.6/continuous.cfg b/.kokoro/samples/python3.6/continuous.cfg new file mode 100644 index 00000000..7218af14 --- /dev/null +++ b/.kokoro/samples/python3.6/continuous.cfg @@ -0,0 +1,7 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} + diff --git a/.kokoro/samples/python3.6/periodic.cfg b/.kokoro/samples/python3.6/periodic.cfg new file mode 100644 index 00000000..50fec964 --- /dev/null +++ b/.kokoro/samples/python3.6/periodic.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "False" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.6/presubmit.cfg b/.kokoro/samples/python3.6/presubmit.cfg new file mode 100644 index 00000000..a1c8d975 --- /dev/null +++ b/.kokoro/samples/python3.6/presubmit.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.7/common.cfg b/.kokoro/samples/python3.7/common.cfg new file mode 100644 index 00000000..91390e2a --- /dev/null +++ b/.kokoro/samples/python3.7/common.cfg @@ -0,0 +1,34 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Specify which tests to run +env_vars: { + key: "RUN_TESTS_SESSION" + value: "py-3.7" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-kms/.kokoro/test-samples.sh" +} + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" +} + +# Download secrets for samples +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "python-kms/.kokoro/trampoline.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.7/continuous.cfg b/.kokoro/samples/python3.7/continuous.cfg new file mode 100644 index 00000000..a1c8d975 --- /dev/null +++ b/.kokoro/samples/python3.7/continuous.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.7/periodic.cfg b/.kokoro/samples/python3.7/periodic.cfg new file mode 100644 index 00000000..50fec964 --- /dev/null +++ b/.kokoro/samples/python3.7/periodic.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "False" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.7/presubmit.cfg b/.kokoro/samples/python3.7/presubmit.cfg new file mode 100644 index 00000000..a1c8d975 --- /dev/null +++ b/.kokoro/samples/python3.7/presubmit.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.8/common.cfg b/.kokoro/samples/python3.8/common.cfg new file mode 100644 index 00000000..50e47fd4 --- /dev/null +++ b/.kokoro/samples/python3.8/common.cfg @@ -0,0 +1,34 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Specify which tests to run +env_vars: { + key: "RUN_TESTS_SESSION" + value: "py-3.8" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-kms/.kokoro/test-samples.sh" +} + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" +} + +# Download secrets for samples +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "python-kms/.kokoro/trampoline.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.8/continuous.cfg b/.kokoro/samples/python3.8/continuous.cfg new file mode 100644 index 00000000..a1c8d975 --- /dev/null +++ b/.kokoro/samples/python3.8/continuous.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.8/periodic.cfg b/.kokoro/samples/python3.8/periodic.cfg new file mode 100644 index 00000000..50fec964 --- /dev/null +++ b/.kokoro/samples/python3.8/periodic.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "False" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.8/presubmit.cfg b/.kokoro/samples/python3.8/presubmit.cfg new file mode 100644 index 00000000..a1c8d975 --- /dev/null +++ b/.kokoro/samples/python3.8/presubmit.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/test-samples.sh b/.kokoro/test-samples.sh new file mode 100755 index 00000000..73bad619 --- /dev/null +++ b/.kokoro/test-samples.sh @@ -0,0 +1,104 @@ +#!/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. + + +# `-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-kms + +# Run periodic samples tests at latest release +if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then + LATEST_RELEASE=$(git describe --abbrev=0 --tags) + git checkout $LATEST_RELEASE +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 Build Cop Bot. + # See https://github.com/googleapis/repo-automation-bots/tree/master/packages/buildcop. + if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then + chmod +x $KOKORO_GFILE_DIR/linux_amd64/buildcop + $KOKORO_GFILE_DIR/linux_amd64/buildcop + 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" \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in index 68855abc..e9e29d12 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -20,3 +20,6 @@ recursive-include google *.json *.proto 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 diff --git a/docs/index.rst b/docs/index.rst index 7ae49b7a..9307e8ff 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,6 @@ .. include:: README.rst +.. include:: multiprocessing.rst API Reference ------------- diff --git a/docs/multiprocessing.rst b/docs/multiprocessing.rst new file mode 100644 index 00000000..1cb29d4c --- /dev/null +++ b/docs/multiprocessing.rst @@ -0,0 +1,7 @@ +.. note:: + + Because this client uses :mod:`grpcio` library, it is safe to + share instances across threads. In multiprocessing scenarios, the best + practice is to create client instances *after* the invocation of + :func:`os.fork` by :class:`multiprocessing.Pool` or + :class:`multiprocessing.Process`. diff --git a/noxfile.py b/noxfile.py index 97b8095a..552de1b0 100644 --- a/noxfile.py +++ b/noxfile.py @@ -26,11 +26,12 @@ BLACK_VERSION = "black==19.3b0" BLACK_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"] -if os.path.exists("samples"): - BLACK_PATHS.append("samples") +DEFAULT_PYTHON_VERSION = "3.7" +SYSTEM_TEST_PYTHON_VERSIONS = ["2.7", "3.7"] +UNIT_TEST_PYTHON_VERSIONS = ["2.7", "3.5", "3.6", "3.7", "3.8"] -@nox.session(python="3.7") +@nox.session(python=DEFAULT_PYTHON_VERSION) def lint(session): """Run linters. @@ -56,7 +57,7 @@ def blacken(session): session.run("black", *BLACK_PATHS) -@nox.session(python="3.7") +@nox.session(python=DEFAULT_PYTHON_VERSION) def lint_setup_py(session): """Verify that setup.py is valid (including RST check).""" session.install("docutils", "pygments") @@ -84,13 +85,13 @@ def default(session): ) -@nox.session(python=["2.7", "3.5", "3.6", "3.7", "3.8"]) +@nox.session(python=UNIT_TEST_PYTHON_VERSIONS) def unit(session): """Run the unit test suite.""" default(session) -@nox.session(python=["2.7", "3.7"]) +@nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS) def system(session): """Run the system test suite.""" system_test_path = os.path.join("tests", "system.py") @@ -120,7 +121,7 @@ def system(session): session.run("py.test", "--quiet", system_test_folder_path, *session.posargs) -@nox.session(python="3.7") +@nox.session(python=DEFAULT_PYTHON_VERSION) def cover(session): """Run the final coverage report. @@ -133,7 +134,7 @@ def cover(session): session.run("coverage", "erase") -@nox.session(python="3.7") +@nox.session(python=DEFAULT_PYTHON_VERSION) def docs(session): """Build the docs for this library.""" diff --git a/samples/AUTHORING_GUIDE.md b/samples/AUTHORING_GUIDE.md new file mode 100644 index 00000000..55c97b32 --- /dev/null +++ b/samples/AUTHORING_GUIDE.md @@ -0,0 +1 @@ +See https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/AUTHORING_GUIDE.md \ No newline at end of file diff --git a/samples/CONTRIBUTING.md b/samples/CONTRIBUTING.md new file mode 100644 index 00000000..34c882b6 --- /dev/null +++ b/samples/CONTRIBUTING.md @@ -0,0 +1 @@ +See https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/CONTRIBUTING.md \ No newline at end of file diff --git a/samples/snippets/README.rst b/samples/snippets/README.rst new file mode 100644 index 00000000..7a52a081 --- /dev/null +++ b/samples/snippets/README.rst @@ -0,0 +1,98 @@ + +.. This file is automatically generated. Do not edit this file directly. + +Google Cloud KMS API Python Samples +=============================================================================== + +.. image:: https://gstatic.com/cloudssh/images/open-btn.png + :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=kms/api-client/README.rst + + +This directory contains samples for Google Cloud KMS API. The `Google Cloud KMS API`_ is a service that allows you to keep encryption keys centrally in the cloud, for direct use by cloud services. + + + + +.. _Google Cloud KMS API: https://cloud.google.com/kms/docs/ + + +Setup +------------------------------------------------------------------------------- + + + +Authentication +++++++++++++++ + +This sample requires you to have authentication setup. Refer to the +`Authentication Getting Started Guide`_ for instructions on setting up +credentials for applications. + +.. _Authentication Getting Started Guide: + https://cloud.google.com/docs/authentication/getting-started + + + + +Install Dependencies +++++++++++++++++++++ + +#. Clone python-docs-samples and change directory to the sample directory you want to use. + + .. code-block:: bash + + $ git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git + +#. Install `pip`_ and `virtualenv`_ if you do not already have them. You may want to refer to the `Python Development Environment Setup Guide`_ for Google Cloud Platform for instructions. + + .. _Python Development Environment Setup Guide: + https://cloud.google.com/python/setup + +#. Create a virtualenv. Samples are compatible with Python 3.6+. + + .. code-block:: bash + + $ virtualenv env + $ source env/bin/activate + +#. Install the dependencies needed to run the samples. + + .. code-block:: bash + + $ pip install -r requirements.txt + +.. _pip: https://pip.pypa.io/ +.. _virtualenv: https://virtualenv.pypa.io/ + + + + + + +Samples +------------------------------------------------------------------------------- + + +Quickstart ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +.. image:: https://gstatic.com/cloudssh/images/open-btn.png + :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=kms/api-client/quickstart.py,kms/api-client/README.rst + + + + +To run this sample: + +.. code-block:: bash + + $ python quickstart.py + + + + + + + + +.. _Google Cloud SDK: https://cloud.google.com/sdk/ diff --git a/samples/snippets/README.rst.in b/samples/snippets/README.rst.in new file mode 100644 index 00000000..cfd81fc8 --- /dev/null +++ b/samples/snippets/README.rst.in @@ -0,0 +1,19 @@ +# This file is used to generate README.rst + +product: + name: Google Cloud KMS API + short_name: Cloud KMS API + url: https://cloud.google.com/kms/docs/ + description: > + The `Google Cloud KMS API`_ is a service that allows you to keep encryption + keys centrally in the cloud, for direct use by cloud services. + +setup: +- auth +- install_deps + +samples: +- name: Quickstart + file: quickstart.py + +folder: kms/api-client diff --git a/samples/snippets/create_key_asymmetric_decrypt.py b/samples/snippets/create_key_asymmetric_decrypt.py new file mode 100644 index 00000000..cac15795 --- /dev/null +++ b/samples/snippets/create_key_asymmetric_decrypt.py @@ -0,0 +1,54 @@ +# 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 + + +# [START kms_create_key_asymmetric_decrypt] +def create_key_asymmetric_decrypt(project_id, location_id, key_ring_id, id): + """ + Creates a new asymmetric decryption key in Cloud KMS. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + id (string): ID of the key to create (e.g. 'my-asymmetric-decrypt-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key ring name. + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + + # Build the key. + purpose = kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_DECRYPT + algorithm = kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_DECRYPT_OAEP_2048_SHA256 + key = { + 'purpose': purpose, + 'version_template': { + 'algorithm': algorithm, + } + } + + # Call the API. + created_key = client.create_crypto_key(key_ring_name, id, key) + print('Created asymmetric decrypt key: {}'.format(created_key.name)) + return created_key +# [END kms_create_key_asymmetric_decrypt] diff --git a/samples/snippets/create_key_asymmetric_sign.py b/samples/snippets/create_key_asymmetric_sign.py new file mode 100644 index 00000000..9bf18a7a --- /dev/null +++ b/samples/snippets/create_key_asymmetric_sign.py @@ -0,0 +1,54 @@ +# 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 + + +# [START kms_create_key_asymmetric_sign] +def create_key_asymmetric_sign(project_id, location_id, key_ring_id, id): + """ + Creates a new asymmetric signing key in Cloud KMS. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + id (string): ID of the key to create (e.g. 'my-asymmetric-signing-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key ring name. + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + + # Build the key. + purpose = kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN + algorithm = kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_SIGN_PKCS1_2048_SHA256 + key = { + 'purpose': purpose, + 'version_template': { + 'algorithm': algorithm, + } + } + + # Call the API. + created_key = client.create_crypto_key(key_ring_name, id, key) + print('Created asymmetric signing key: {}'.format(created_key.name)) + return created_key +# [END kms_create_key_asymmetric_sign] diff --git a/samples/snippets/create_key_hsm.py b/samples/snippets/create_key_hsm.py new file mode 100644 index 00000000..84ba37e5 --- /dev/null +++ b/samples/snippets/create_key_hsm.py @@ -0,0 +1,56 @@ +# 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 + + +# [START kms_create_key_hsm] +def create_key_hsm(project_id, location_id, key_ring_id, id): + """ + Creates a new key in Cloud KMS backed by Cloud HSM. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + id (string): ID of the key to create (e.g. 'my-hsm-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key ring name. + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + + # Build the key. + purpose = kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + algorithm = kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + protection_level = kms.enums.ProtectionLevel.HSM + key = { + 'purpose': purpose, + 'version_template': { + 'algorithm': algorithm, + 'protection_level': protection_level + } + } + + # Call the API. + created_key = client.create_crypto_key(key_ring_name, id, key) + print('Created hsm key: {}'.format(created_key.name)) + return created_key +# [END kms_create_key_hsm] diff --git a/samples/snippets/create_key_labels.py b/samples/snippets/create_key_labels.py new file mode 100644 index 00000000..e64a10cb --- /dev/null +++ b/samples/snippets/create_key_labels.py @@ -0,0 +1,58 @@ +# 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 + + +# [START kms_create_key_labels] +def create_key_labels(project_id, location_id, key_ring_id, id): + """ + Creates a new key in Cloud KMS with labels. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + id (string): ID of the key to create (e.g. 'my-labeled-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key ring name. + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + + # Build the key. + purpose = kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + algorithm = kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + key = { + 'purpose': purpose, + 'version_template': { + 'algorithm': algorithm, + }, + 'labels': { + 'team': 'alpha', + 'cost_center': 'cc1234' + } + } + + # Call the API. + created_key = client.create_crypto_key(key_ring_name, id, key) + print('Created labeled key: {}'.format(created_key.name)) + return created_key +# [END kms_create_key_labels] diff --git a/samples/snippets/create_key_ring.py b/samples/snippets/create_key_ring.py new file mode 100644 index 00000000..c01e8490 --- /dev/null +++ b/samples/snippets/create_key_ring.py @@ -0,0 +1,46 @@ +# 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 + + +# [START kms_create_key_ring] +def create_key_ring(project_id, location_id, id): + """ + Creates a new key ring in Cloud KMS + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + id (string): ID of the key ring to create (e.g. 'my-key-ring'). + + Returns: + KeyRing: Cloud KMS key ring. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent location name. + location_name = client.location_path(project_id, location_id) + + # Build the key ring. + key_ring = {} + + # Call the API. + created_key_ring = client.create_key_ring(location_name, id, key_ring) + print('Created key ring: {}'.format(created_key_ring.name)) + return created_key_ring +# [END kms_create_key_ring] diff --git a/samples/snippets/create_key_rotation_schedule.py b/samples/snippets/create_key_rotation_schedule.py new file mode 100644 index 00000000..e6bbdb62 --- /dev/null +++ b/samples/snippets/create_key_rotation_schedule.py @@ -0,0 +1,67 @@ +# 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 + + +# [START kms_create_key_rotation_schedule] +def create_key_rotation_schedule(project_id, location_id, key_ring_id, id): + """ + Creates a new key in Cloud KMS that automatically rotates. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + id (string): ID of the key to create (e.g. 'my-rotating-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Import time for getting the current time. + import time + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key ring name. + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + + # Build the key. + purpose = kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + algorithm = kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + key = { + 'purpose': purpose, + 'version_template': { + 'algorithm': algorithm, + }, + + # Rotate the key every 30 days. + 'rotation_period': { + 'seconds': 60*60*24*30 + }, + + # Start the first rotation in 24 hours. + 'next_rotation_time': { + 'seconds': int(time.time()) + 60*60*24 + } + } + + # Call the API. + created_key = client.create_crypto_key(key_ring_name, id, key) + print('Created labeled key: {}'.format(created_key.name)) + return created_key +# [END kms_create_key_rotation_schedule] diff --git a/samples/snippets/create_key_symmetric_encrypt_decrypt.py b/samples/snippets/create_key_symmetric_encrypt_decrypt.py new file mode 100644 index 00000000..54b9c5f4 --- /dev/null +++ b/samples/snippets/create_key_symmetric_encrypt_decrypt.py @@ -0,0 +1,54 @@ +# 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 + + +# [START kms_create_key_symmetric_encrypt_decrypt] +def create_key_symmetric_encrypt_decrypt(project_id, location_id, key_ring_id, id): + """ + Creates a new symmetric encryption/decryption key in Cloud KMS. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + id (string): ID of the key to create (e.g. 'my-symmetric-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key ring name. + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + + # Build the key. + purpose = kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + algorithm = kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + key = { + 'purpose': purpose, + 'version_template': { + 'algorithm': algorithm, + } + } + + # Call the API. + created_key = client.create_crypto_key(key_ring_name, id, key) + print('Created symmetric key: {}'.format(created_key.name)) + return created_key +# [END kms_create_key_symmetric_encrypt_decrypt] diff --git a/samples/snippets/create_key_version.py b/samples/snippets/create_key_version.py new file mode 100644 index 00000000..9c84f808 --- /dev/null +++ b/samples/snippets/create_key_version.py @@ -0,0 +1,47 @@ +# 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 + + +# [START kms_create_key_version] +def create_key_version(project_id, location_id, key_ring_id, key_id): + """ + Creates a new version of the given key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key for which to create a new version (e.g. 'my-key'). + + Returns: + CryptoKeyVersion: Cloud KMS key version. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Build the key version. + version = {} + + # Call the API. + created_version = client.create_crypto_key_version(key_name, version) + print('Created key version: {}'.format(created_version.name)) + return created_version +# [END kms_create_key_version] diff --git a/samples/snippets/decrypt_asymmetric.py b/samples/snippets/decrypt_asymmetric.py new file mode 100644 index 00000000..7b040cdd --- /dev/null +++ b/samples/snippets/decrypt_asymmetric.py @@ -0,0 +1,46 @@ +# 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 + + +# [START kms_decrypt_asymmetric] +def decrypt_asymmetric(project_id, location_id, key_ring_id, key_id, version_id, ciphertext): + """ + Decrypt the ciphertext using an asymmetric key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key version to use (e.g. '1'). + ciphertext (bytes): Encrypted bytes to decrypt. + + Returns: + DecryptResponse: Response including plaintext. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Call the API. + decrypt_response = client.asymmetric_decrypt(key_version_name, ciphertext) + print('Plaintext: {}'.format(decrypt_response.plaintext)) + return decrypt_response +# [END kms_decrypt_asymmetric] diff --git a/samples/snippets/decrypt_symmetric.py b/samples/snippets/decrypt_symmetric.py new file mode 100644 index 00000000..a5cbe714 --- /dev/null +++ b/samples/snippets/decrypt_symmetric.py @@ -0,0 +1,45 @@ +# 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 + + +# [START kms_decrypt_symmetric] +def decrypt_symmetric(project_id, location_id, key_ring_id, key_id, ciphertext): + """ + Decrypt the ciphertext using the symmetric key + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + ciphertext (bytes): Encrypted bytes to decrypt. + + Returns: + DecryptResponse: Response including plaintext. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Call the API. + decrypt_response = client.decrypt(key_name, ciphertext) + print('Plaintext: {}'.format(decrypt_response.plaintext)) + return decrypt_response +# [END kms_decrypt_symmetric] diff --git a/samples/snippets/destroy_key_version.py b/samples/snippets/destroy_key_version.py new file mode 100644 index 00000000..7423ca7e --- /dev/null +++ b/samples/snippets/destroy_key_version.py @@ -0,0 +1,45 @@ +# 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 + + +# [START kms_destroy_key_version] +def destroy_key_version(project_id, location_id, key_ring_id, key_id, version_id): + """ + Schedule destruction of the given key version. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key version to destroy (e.g. '1'). + + Returns: + CryptoKeyVersion: The version. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Call the API. + destroyed_version = client.destroy_crypto_key_version(key_version_name) + print('Destroyed key version: {}'.format(destroyed_version.name)) + return destroyed_version +# [END kms_destroy_key_version] diff --git a/samples/snippets/disable_key_version.py b/samples/snippets/disable_key_version.py new file mode 100644 index 00000000..a4a16dd5 --- /dev/null +++ b/samples/snippets/disable_key_version.py @@ -0,0 +1,55 @@ +# 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 + + +# [START kms_disable_key_version] +def disable_key_version(project_id, location_id, key_ring_id, key_id, version_id): + """ + Disable a key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key version to disable (e.g. '1'). + + Returns: + CryptoKeyVersion: The version. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Build the key version. We need to build a full proto instead of a dict due + # to https://github.com/googleapis/gapic-generator-python/issues/364. + from google.cloud.kms_v1.proto import resources_pb2 + key_version = resources_pb2.CryptoKeyVersion() + key_version.name = key_version_name + key_version.state = kms.enums.CryptoKeyVersion.CryptoKeyVersionState.DISABLED + + # Build the update mask. + update_mask = {'paths': ['state']} + + # Call the API. + disabled_version = client.update_crypto_key_version(key_version, update_mask) + print('Disabled key version: {}'.format(disabled_version.name)) + return disabled_version +# [END kms_disable_key_version] diff --git a/samples/snippets/enable_key_version.py b/samples/snippets/enable_key_version.py new file mode 100644 index 00000000..9cb8daad --- /dev/null +++ b/samples/snippets/enable_key_version.py @@ -0,0 +1,55 @@ +# 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 + + +# [START kms_enable_key_version] +def enable_key_version(project_id, location_id, key_ring_id, key_id, version_id): + """ + Enable a key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key version to enable (e.g. '1'). + + Returns: + CryptoKeyVersion: The version. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Build the key version. We need to build a full proto instead of a dict due + # to https://github.com/googleapis/gapic-generator-python/issues/364. + from google.cloud.kms_v1.proto import resources_pb2 + key_version = resources_pb2.CryptoKeyVersion() + key_version.name = key_version_name + key_version.state = kms.enums.CryptoKeyVersion.CryptoKeyVersionState.ENABLED + + # Build the update mask. + update_mask = {'paths': ['state']} + + # Call the API. + enabled_version = client.update_crypto_key_version(key_version, update_mask) + print('Enabled key version: {}'.format(enabled_version.name)) + return enabled_version +# [END kms_enable_key_version] diff --git a/samples/snippets/encrypt_asymmetric.py b/samples/snippets/encrypt_asymmetric.py new file mode 100644 index 00000000..efe40322 --- /dev/null +++ b/samples/snippets/encrypt_asymmetric.py @@ -0,0 +1,69 @@ +# 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 + + +# [START kms_encrypt_asymmetric] +def encrypt_asymmetric(project_id, location_id, key_ring_id, key_id, version_id, plaintext): + """ + Encrypt plaintext using the public key portion of an asymmetric key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key version to use (e.g. '1'). + plaintext (string): message to encrypt + + Returns: + bytes: Encrypted ciphertext. + + """ + + # Import the client library. + from google.cloud import kms + + # Import base64 for printing the ciphertext. + import base64 + + # Import cryptographic helpers from the cryptography package. + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import hashes, serialization + from cryptography.hazmat.primitives.asymmetric import padding + + # Convert the plaintext to bytes. + plaintext_bytes = plaintext.encode('utf-8') + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Get the public key. + public_key = client.get_public_key(key_version_name) + + # Extract and parse the public key as a PEM-encoded RSA key. + pem = public_key.pem.encode('utf-8') + rsa_key = serialization.load_pem_public_key(pem, default_backend()) + + # Construct the padding. Note that the padding differs based on key choice. + sha256 = hashes.SHA256() + mgf = padding.MGF1(algorithm=sha256) + pad = padding.OAEP(mgf=mgf, algorithm=sha256, label=None) + + # Encrypt the data using the public key. + ciphertext = rsa_key.encrypt(plaintext_bytes, pad) + print('Ciphertext: {}'.format(base64.b64encode(ciphertext))) + return ciphertext +# [END kms_encrypt_asymmetric] diff --git a/samples/snippets/encrypt_symmetric.py b/samples/snippets/encrypt_symmetric.py new file mode 100644 index 00000000..b90da358 --- /dev/null +++ b/samples/snippets/encrypt_symmetric.py @@ -0,0 +1,51 @@ +# 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 + + +# [START kms_encrypt_symmetric] +def encrypt_symmetric(project_id, location_id, key_ring_id, key_id, plaintext): + """ + Encrypt plaintext using a symmetric key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + plaintext (string): message to encrypt + + Returns: + bytes: Encrypted ciphertext. + + """ + + # Import the client library. + from google.cloud import kms + + # Import base64 for printing the ciphertext. + import base64 + + # Convert the plaintext to bytes. + plaintext_bytes = plaintext.encode('utf-8') + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Call the API. + encrypt_response = client.encrypt(key_name, plaintext_bytes) + print('Ciphertext: {}'.format(base64.b64encode(encrypt_response.ciphertext))) + return encrypt_response +# [END kms_encrypt_symmetric] diff --git a/samples/snippets/get_key_labels.py b/samples/snippets/get_key_labels.py new file mode 100644 index 00000000..363bcfba --- /dev/null +++ b/samples/snippets/get_key_labels.py @@ -0,0 +1,48 @@ +# 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 + + +# [START kms_get_key_labels] +def get_key_labels(project_id, location_id, key_ring_id, key_id): + """ + Get a key and its labels. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Call the API. + key = client.get_crypto_key(key_name) + + # Example of iterating over labels. + for k, v in key.labels.items(): + print('{} = {}'.format(k, v)) + + return key +# [END kms_get_key_labels] diff --git a/samples/snippets/get_key_version_attestation.py b/samples/snippets/get_key_version_attestation.py new file mode 100644 index 00000000..615d4653 --- /dev/null +++ b/samples/snippets/get_key_version_attestation.py @@ -0,0 +1,56 @@ +# 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 + + +# [START kms_get_key_version_attestation] +def get_key_version_attestation(project_id, location_id, key_ring_id, key_id, version_id): + """ + Get an HSM-backend key's attestation. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the version to use (e.g. '1'). + + Returns: + Attestation: Cloud KMS key attestation. + + """ + + # Import the client library. + from google.cloud import kms + + # Import base64 for printing the attestation. + import base64 + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Call the API. + version = client.get_crypto_key_version(key_version_name) + + # Only HSM keys have an attestation. For other key types, the attestion + # will be None. + attestation = version.attestation + if not attestation: + raise 'no attestation - attestations only exist on HSM keys' + + encoded_attestation = base64.b64encode(attestation.content) + print('Got key attestation: {}'.format(encoded_attestation)) + return attestation +# [END kms_get_key_version_attestation] diff --git a/samples/snippets/get_public_key.py b/samples/snippets/get_public_key.py new file mode 100644 index 00000000..1b810d15 --- /dev/null +++ b/samples/snippets/get_public_key.py @@ -0,0 +1,45 @@ +# 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 + + +# [START kms_get_public_key] +def get_public_key(project_id, location_id, key_ring_id, key_id, version_id): + """ + Get the public key for an asymmetric key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key to use (e.g. '1'). + + Returns: + PublicKey: Cloud KMS public key response. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Call the API. + public_key = client.get_public_key(key_version_name) + print('Public key: {}'.format(public_key.pem)) + return public_key +# [END kms_get_public_key] diff --git a/samples/snippets/iam_add_member.py b/samples/snippets/iam_add_member.py new file mode 100644 index 00000000..442f2483 --- /dev/null +++ b/samples/snippets/iam_add_member.py @@ -0,0 +1,56 @@ +# 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 + + +# [START kms_iam_add_member] +def iam_add_member(project_id, location_id, key_ring_id, key_id, member): + """ + Add an IAM member to a resource. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + member (string): Member to add (e.g. 'user:foo@example.com') + + Returns: + Policy: Updated Cloud IAM policy. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the resource name. + resource_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # The resource name could also be a key ring. + # resource_name = client.key_ring_path(project_id, location_id, key_ring_id); + + # Get the current policy. + policy = client.get_iam_policy(resource_name) + + # Add the member to the policy. + policy.bindings.add( + role='roles/cloudkms.cryptoKeyEncrypterDecrypter', + members=[member]) + + # Save the updated IAM policy. + updated_policy = client.set_iam_policy(resource_name, policy) + print('Added {} to {}'.format(member, resource_name)) + return updated_policy +# [END kms_iam_add_member] diff --git a/samples/snippets/iam_get_policy.py b/samples/snippets/iam_get_policy.py new file mode 100644 index 00000000..c00172e9 --- /dev/null +++ b/samples/snippets/iam_get_policy.py @@ -0,0 +1,54 @@ +# 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 + + +# [START kms_iam_get_policy] +def iam_get_policy(project_id, location_id, key_ring_id, key_id): + """ + Get the IAM policy for a resource. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + + Returns: + Policy: Cloud IAM policy. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the resource name. + resource_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # The resource name could also be a key ring. + # resource_name = client.key_ring_path(project_id, location_id, key_ring_id); + + # Get the current policy. + policy = client.get_iam_policy(resource_name) + + # Print the policy + print('IAM policy for {}'.format(resource_name)) + for binding in policy.bindings: + print(binding.role) + for member in binding.members: + print('- {}'.format(member)) + + return policy +# [END kms_iam_get_policy] diff --git a/samples/snippets/iam_remove_member.py b/samples/snippets/iam_remove_member.py new file mode 100644 index 00000000..ad73fab9 --- /dev/null +++ b/samples/snippets/iam_remove_member.py @@ -0,0 +1,57 @@ +# 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 + + +# [START kms_iam_remove_member] +def iam_remove_member(project_id, location_id, key_ring_id, key_id, member): + """ + Remove an IAM member from a resource. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + member (string): Member to remove (e.g. 'user:foo@example.com') + + Returns: + Policy: Updated Cloud IAM policy. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the resource name. + resource_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # The resource name could also be a key ring. + # resource_name = client.key_ring_path(project_id, location_id, key_ring_id); + + # Get the current policy. + policy = client.get_iam_policy(resource_name) + + # Remove the member from the policy. + for binding in policy.bindings: + if binding.role == 'roles/cloudkms.cryptoKeyEncrypterDecrypter': + if member in binding.members: + binding.members.remove(member) + + # Save the updated IAM policy. + updated_policy = client.set_iam_policy(resource_name, policy) + print('Removed {} from {}'.format(member, resource_name)) + return updated_policy +# [END kms_iam_remove_member] diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py new file mode 100644 index 00000000..b23055f1 --- /dev/null +++ b/samples/snippets/noxfile.py @@ -0,0 +1,225 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import print_function + +import os +from pathlib import Path +import sys + +import nox + + +# WARNING - WARNING - WARNING - WARNING - WARNING +# WARNING - WARNING - WARNING - WARNING - WARNING +# DO NOT EDIT THIS FILE EVER! +# WARNING - WARNING - WARNING - WARNING - WARNING +# WARNING - WARNING - WARNING - WARNING - WARNING + +# Copy `noxfile_config.py` to your directory and modify it instead. + + +# `TEST_CONFIG` dict is a configuration hook that allows users to +# modify the test configurations. The values here should be in sync +# with `noxfile_config.py`. Users will copy `noxfile_config.py` into +# their directory and modify it. + +TEST_CONFIG = { + # You can opt out from the test for specific Python versions. + 'ignored_versions': ["2.7"], + + # An envvar key for determining the project id to use. Change it + # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a + # build specific Cloud project. You can also use your own string + # to use your own Cloud project. + 'gcloud_project_env': 'GCLOUD_PROJECT', + # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT', + + # A dictionary you want to inject into your test. Don't put any + # secrets here. These values will override predefined values. + 'envs': {}, +} + + +try: + # Ensure we can import noxfile_config in the project's directory. + sys.path.append('.') + from noxfile_config import TEST_CONFIG_OVERRIDE +except ImportError as e: + print("No user noxfile_config found: detail: {}".format(e)) + TEST_CONFIG_OVERRIDE = {} + +# Update the TEST_CONFIG with the user supplied values. +TEST_CONFIG.update(TEST_CONFIG_OVERRIDE) + + +def get_pytest_env_vars(): + """Returns a dict for pytest invocation.""" + ret = {} + + # Override the GCLOUD_PROJECT and the alias. + env_key = TEST_CONFIG['gcloud_project_env'] + # This should error out if not set. + ret['GOOGLE_CLOUD_PROJECT'] = os.environ[env_key] + ret['GCLOUD_PROJECT'] = os.environ[env_key] + + # Apply user supplied envs. + ret.update(TEST_CONFIG['envs']) + return ret + + +# DO NOT EDIT - automatically generated. +# All versions used to tested samples. +ALL_VERSIONS = ["2.7", "3.6", "3.7", "3.8"] + +# Any default versions that should be ignored. +IGNORED_VERSIONS = TEST_CONFIG['ignored_versions'] + +TESTED_VERSIONS = sorted([v for v in ALL_VERSIONS if v not in IGNORED_VERSIONS]) + +INSTALL_LIBRARY_FROM_SOURCE = bool(os.environ.get("INSTALL_LIBRARY_FROM_SOURCE", False)) +# +# Style Checks +# + + +def _determine_local_import_names(start_dir): + """Determines all import names that should be considered "local". + + This is used when running the linter to insure that import order is + properly checked. + """ + file_ext_pairs = [os.path.splitext(path) for path in os.listdir(start_dir)] + return [ + basename + for basename, extension in file_ext_pairs + if extension == ".py" + or os.path.isdir(os.path.join(start_dir, basename)) + and basename not in ("__pycache__") + ] + + +# Linting with flake8. +# +# We ignore the following rules: +# E203: whitespace before ‘:’ +# E266: too many leading ‘#’ for block comment +# E501: line too long +# I202: Additional newline in a section of imports +# +# We also need to specify the rules which are ignored by default: +# ['E226', 'W504', 'E126', 'E123', 'W503', 'E24', 'E704', 'E121'] +FLAKE8_COMMON_ARGS = [ + "--show-source", + "--builtin=gettext", + "--max-complexity=20", + "--import-order-style=google", + "--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py", + "--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202", + "--max-line-length=88", +] + + +@nox.session +def lint(session): + session.install("flake8", "flake8-import-order") + + local_names = _determine_local_import_names(".") + args = FLAKE8_COMMON_ARGS + [ + "--application-import-names", + ",".join(local_names), + "." + ] + session.run("flake8", *args) + + +# +# Sample Tests +# + + +PYTEST_COMMON_ARGS = ["--junitxml=sponge_log.xml"] + + +def _session_tests(session, post_install=None): + """Runs py.test for a particular project.""" + if os.path.exists("requirements.txt"): + session.install("-r", "requirements.txt") + + if os.path.exists("requirements-test.txt"): + session.install("-r", "requirements-test.txt") + + if INSTALL_LIBRARY_FROM_SOURCE: + session.install("-e", _get_repo_root()) + + if post_install: + post_install(session) + + session.run( + "pytest", + *(PYTEST_COMMON_ARGS + session.posargs), + # Pytest will return 5 when no tests are collected. This can happen + # on travis where slow and flaky tests are excluded. + # See http://doc.pytest.org/en/latest/_modules/_pytest/main.html + success_codes=[0, 5], + env=get_pytest_env_vars() + ) + + +@nox.session(python=ALL_VERSIONS) +def py(session): + """Runs py.test for a sample using the specified version of Python.""" + if session.python in TESTED_VERSIONS: + _session_tests(session) + else: + session.skip("SKIPPED: {} tests are disabled for this sample.".format( + session.python + )) + + +# +# Readmegen +# + + +def _get_repo_root(): + """ Returns the root folder of the project. """ + # Get root of this repository. Assume we don't have directories nested deeper than 10 items. + p = Path(os.getcwd()) + for i in range(10): + if p is None: + break + if Path(p / ".git").exists(): + return str(p) + p = p.parent + raise Exception("Unable to detect repository root.") + + +GENERATED_READMES = sorted([x for x in Path(".").rglob("*.rst.in")]) + + +@nox.session +@nox.parametrize("path", GENERATED_READMES) +def readmegen(session, path): + """(Re-)generates the readme for a sample.""" + session.install("jinja2", "pyyaml") + dir_ = os.path.dirname(path) + + if os.path.exists(os.path.join(dir_, "requirements.txt")): + session.install("-r", os.path.join(dir_, "requirements.txt")) + + in_file = os.path.join(dir_, "README.rst.in") + session.run( + "python", _get_repo_root() + "/scripts/readme-gen/readme_gen.py", in_file + ) diff --git a/samples/snippets/quickstart.py b/samples/snippets/quickstart.py new file mode 100644 index 00000000..91b5a49a --- /dev/null +++ b/samples/snippets/quickstart.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python + +# Copyright 2017 Google, Inc +# +# 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 + +import argparse + + +# [START kms_quickstart] +def quickstart(project_id, location_id): + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent location name. + location_name = client.location_path(project_id, location_id) + + # Call the API. + key_rings = client.list_key_rings(location_name) + + # Example of iterating over key rings. + for key_ring in key_rings: + print(key_ring.name) + + return key_rings +# [END kms_quickstart] + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument('project_id', help='id of the GCP project') + parser.add_argument('location_id', help='id of the KMS location') + args = parser.parse_args() + + quickstart(args.project_id, args.location_id) diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt new file mode 100644 index 00000000..d3e30fa6 --- /dev/null +++ b/samples/snippets/requirements-test.txt @@ -0,0 +1 @@ +pytest==5.4.1 diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt new file mode 100644 index 00000000..6e2cc2a4 --- /dev/null +++ b/samples/snippets/requirements.txt @@ -0,0 +1,2 @@ +google-cloud-kms==1.4.0 +cryptography==2.9.2 diff --git a/samples/snippets/restore_key_version.py b/samples/snippets/restore_key_version.py new file mode 100644 index 00000000..3c4668d6 --- /dev/null +++ b/samples/snippets/restore_key_version.py @@ -0,0 +1,45 @@ +# 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 + + +# [START kms_restore_key_version] +def restore_key_version(project_id, location_id, key_ring_id, key_id, version_id): + """ + Restore a key version scheduled for destruction. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the version to use (e.g. '1'). + + Returns: + CryptoKeyVersion: Restored Cloud KMS key version. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Call the API. + restored_version = client.restore_crypto_key_version(key_version_name) + print('Restored key version: {}'.format(restored_version.name)) + return restored_version +# [END kms_restore_key_version] diff --git a/samples/snippets/sign_asymmetric.py b/samples/snippets/sign_asymmetric.py new file mode 100644 index 00000000..a92a13ec --- /dev/null +++ b/samples/snippets/sign_asymmetric.py @@ -0,0 +1,64 @@ +# 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 + + +# [START kms_sign_asymmetric] +def sign_asymmetric(project_id, location_id, key_ring_id, key_id, version_id, message): + """ + Sign a message using the public key part of an asymmetric key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): Version to use (e.g. '1'). + message (string): Message to sign. + + Returns: + AsymmetricSignResponse: Signature. + + """ + + # Import the client library. + from google.cloud import kms + + # Import base64 for printing the ciphertext. + import base64 + + # Import hashlib for calculating hashes. + import hashlib + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Convert the message to bytes. + message_bytes = message.encode('utf-8') + + # Calculate the hash. + hash_ = hashlib.sha256(message_bytes).digest() + + # Build the digest. + # + # Note: Key algorithms will require a varying hash function. For + # example, EC_SIGN_P384_SHA384 requires SHA-384. + digest = {'sha256': hash_} + + # Call the API + sign_response = client.asymmetric_sign(key_version_name, digest) + print('Signature: {}'.format(base64.b64encode(sign_response.signature))) + return sign_response +# [END kms_sign_asymmetric] diff --git a/samples/snippets/snippets_test.py b/samples/snippets/snippets_test.py new file mode 100644 index 00000000..795edeb4 --- /dev/null +++ b/samples/snippets/snippets_test.py @@ -0,0 +1,423 @@ +# Copyright 2017 Google, Inc +# +# 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 + +import hashlib +import os +import time +import uuid + +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import padding, utils +from google.cloud import kms +from google.cloud.kms_v1.proto import resources_pb2 +import pytest + +from create_key_asymmetric_decrypt import create_key_asymmetric_decrypt +from create_key_asymmetric_sign import create_key_asymmetric_sign +from create_key_hsm import create_key_hsm +from create_key_labels import create_key_labels +from create_key_ring import create_key_ring +from create_key_rotation_schedule import create_key_rotation_schedule +from create_key_symmetric_encrypt_decrypt import create_key_symmetric_encrypt_decrypt +from create_key_version import create_key_version +from decrypt_asymmetric import decrypt_asymmetric +from decrypt_symmetric import decrypt_symmetric +from destroy_key_version import destroy_key_version +from disable_key_version import disable_key_version +from enable_key_version import enable_key_version +from encrypt_asymmetric import encrypt_asymmetric +from encrypt_symmetric import encrypt_symmetric +from get_key_labels import get_key_labels +from get_key_version_attestation import get_key_version_attestation +from get_public_key import get_public_key +from iam_add_member import iam_add_member +from iam_get_policy import iam_get_policy +from iam_remove_member import iam_remove_member +from quickstart import quickstart +from restore_key_version import restore_key_version +from sign_asymmetric import sign_asymmetric +from update_key_add_rotation import update_key_add_rotation +from update_key_remove_labels import update_key_remove_labels +from update_key_remove_rotation import update_key_remove_rotation +from update_key_set_primary import update_key_set_primary +from update_key_update_labels import update_key_update_labels +from verify_asymmetric_ec import verify_asymmetric_ec +from verify_asymmetric_rsa import verify_asymmetric_rsa + + +@pytest.fixture(scope="module") +def client(): + return kms.KeyManagementServiceClient() + + +@pytest.fixture(scope="module") +def project_id(): + return os.environ['GCLOUD_PROJECT'] + + +@pytest.fixture(scope="module") +def location_id(): + return "us-east1" + + +@pytest.fixture(scope="module") +def key_ring_id(client, project_id, location_id): + location_name = client.location_path(project_id, location_id) + key_ring_id = '{}'.format(uuid.uuid4()) + key_ring = client.create_key_ring(location_name, key_ring_id, {}) + + yield key_ring_id + + for key in client.list_crypto_keys(key_ring.name): + if key.rotation_period.seconds > 0 or key.next_rotation_time.seconds > 0: + # https://github.com/googleapis/gapic-generator-python/issues/364 + updated_key = resources_pb2.CryptoKey() + updated_key.name = key.name + update_mask = {'paths': ['rotation_period', 'next_rotation_time']} + client.update_crypto_key(updated_key, update_mask) + + f = 'state != DESTROYED AND state != DESTROY_SCHEDULED' + for version in client.list_crypto_key_versions(key.name, filter_=f): + client.destroy_crypto_key_version(version.name) + + +@pytest.fixture(scope="module") +def asymmetric_decrypt_key_id(client, project_id, location_id, key_ring_id): + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + key_id = '{}'.format(uuid.uuid4()) + key = client.create_crypto_key(key_ring_name, key_id, { + 'purpose': kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_DECRYPT, + 'version_template': { + 'algorithm': kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_DECRYPT_OAEP_2048_SHA256 + }, + 'labels': {'foo': 'bar', 'zip': 'zap'} + }) + wait_for_ready(client, '{}/cryptoKeyVersions/1'.format(key.name)) + return key_id + + +@pytest.fixture(scope="module") +def asymmetric_sign_ec_key_id(client, project_id, location_id, key_ring_id): + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + key_id = '{}'.format(uuid.uuid4()) + key = client.create_crypto_key(key_ring_name, key_id, { + 'purpose': kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN, + 'version_template': { + 'algorithm': kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.EC_SIGN_P256_SHA256 + }, + 'labels': {'foo': 'bar', 'zip': 'zap'} + }) + wait_for_ready(client, '{}/cryptoKeyVersions/1'.format(key.name)) + return key_id + + +@pytest.fixture(scope="module") +def asymmetric_sign_rsa_key_id(client, project_id, location_id, key_ring_id): + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + key_id = '{}'.format(uuid.uuid4()) + key = client.create_crypto_key(key_ring_name, key_id, { + 'purpose': kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN, + 'version_template': { + 'algorithm': kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_SIGN_PKCS1_2048_SHA256 + }, + 'labels': {'foo': 'bar', 'zip': 'zap'} + }) + wait_for_ready(client, '{}/cryptoKeyVersions/1'.format(key.name)) + return key_id + + +@pytest.fixture(scope="module") +def hsm_key_id(client, project_id, location_id, key_ring_id): + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + key_id = '{}'.format(uuid.uuid4()) + key = client.create_crypto_key(key_ring_name, key_id, { + 'purpose': kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT, + 'version_template': { + 'algorithm': kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION, + 'protection_level': kms.enums.ProtectionLevel.HSM + }, + 'labels': {'foo': 'bar', 'zip': 'zap'} + }) + wait_for_ready(client, '{}/cryptoKeyVersions/1'.format(key.name)) + return key_id + + +@pytest.fixture(scope="module") +def symmetric_key_id(client, project_id, location_id, key_ring_id): + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + key_id = '{}'.format(uuid.uuid4()) + key = client.create_crypto_key(key_ring_name, key_id, { + 'purpose': kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT, + 'version_template': { + 'algorithm': kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + }, + 'labels': {'foo': 'bar', 'zip': 'zap'} + }) + wait_for_ready(client, '{}/cryptoKeyVersions/1'.format(key.name)) + return key_id + + +def wait_for_ready(client, key_version_name): + for i in range(5): + key_version = client.get_crypto_key_version(key_version_name) + if key_version.state == kms.enums.CryptoKeyVersion.CryptoKeyVersionState.ENABLED: + return + time.sleep(0.1*(i**2)) + pytest.fail('{} not ready'.format(key_version_name)) + + +def test_create_key_asymmetric_decrypt(project_id, location_id, key_ring_id): + key_id = '{}'.format(uuid.uuid4()) + key = create_key_asymmetric_decrypt(project_id, location_id, key_ring_id, key_id) + assert key.purpose == kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_DECRYPT + assert key.version_template.algorithm == kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_DECRYPT_OAEP_2048_SHA256 + + +def test_create_key_asymmetric_sign(project_id, location_id, key_ring_id): + key_id = '{}'.format(uuid.uuid4()) + key = create_key_asymmetric_sign(project_id, location_id, key_ring_id, key_id) + assert key.purpose == kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN + assert key.version_template.algorithm == kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_SIGN_PKCS1_2048_SHA256 + + +def test_create_key_hsm(project_id, location_id, key_ring_id): + key_id = '{}'.format(uuid.uuid4()) + key = create_key_hsm(project_id, location_id, key_ring_id, key_id) + assert key.purpose == kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + assert key.version_template.algorithm == kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + assert key.version_template.protection_level == kms.enums.ProtectionLevel.HSM + + +def test_create_key_labels(project_id, location_id, key_ring_id): + key_id = '{}'.format(uuid.uuid4()) + key = create_key_labels(project_id, location_id, key_ring_id, key_id) + assert key.purpose == kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + assert key.version_template.algorithm == kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + assert key.labels == {'team': 'alpha', 'cost_center': 'cc1234'} + + +def test_create_key_ring(project_id, location_id): + key_ring_id = '{}'.format(uuid.uuid4()) + key_ring = create_key_ring(project_id, location_id, key_ring_id) + assert key_ring + + +def test_create_key_rotation_schedule(project_id, location_id, key_ring_id): + key_id = '{}'.format(uuid.uuid4()) + key = create_key_rotation_schedule(project_id, location_id, key_ring_id, key_id) + assert key.rotation_period.seconds == 60*60*24*30 + assert key.next_rotation_time.seconds > 0 + + +def test_create_key_symmetric_encrypt_decrypt(project_id, location_id, key_ring_id): + key_id = '{}'.format(uuid.uuid4()) + key = create_key_symmetric_encrypt_decrypt(project_id, location_id, key_ring_id, key_id) + assert key.purpose == kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + assert key.version_template.algorithm == kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + + +def test_create_key_version(project_id, location_id, key_ring_id, symmetric_key_id): + version = create_key_version(project_id, location_id, key_ring_id, symmetric_key_id) + assert version + + +def test_decrypt_asymmetric(client, project_id, location_id, key_ring_id, asymmetric_decrypt_key_id): + message = 'my message'.encode('utf-8') + + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, '1') + public_key = client.get_public_key(key_version_name) + + pem = public_key.pem.encode('utf-8') + rsa_key = serialization.load_pem_public_key(pem, default_backend()) + + pad = padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None) + ciphertext = rsa_key.encrypt(message, pad) + + response = decrypt_asymmetric(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, '1', ciphertext) + assert response.plaintext == message + + +def test_decrypt_symmetric(client, project_id, location_id, key_ring_id, symmetric_key_id): + plaintext = 'my message'.encode('utf-8') + + key_version_name = client.crypto_key_path(project_id, location_id, key_ring_id, symmetric_key_id) + encrypt_response = client.encrypt(key_version_name, plaintext) + ciphertext = encrypt_response.ciphertext + + decrypt_response = decrypt_symmetric(project_id, location_id, key_ring_id, symmetric_key_id, ciphertext) + assert decrypt_response.plaintext == plaintext + + +def test_destroy_restore_key_version(client, project_id, location_id, key_ring_id, asymmetric_decrypt_key_id): + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id) + version = client.create_crypto_key_version(key_name, {}) + version_id = version.name.split('/')[-1] + + wait_for_ready(client, version.name) + + destroyed_version = destroy_key_version(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, version_id) + assert destroyed_version.state == kms.enums.CryptoKeyVersion.CryptoKeyVersionState.DESTROY_SCHEDULED + + restored_version = restore_key_version(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, version_id) + assert restored_version.state == kms.enums.CryptoKeyVersion.CryptoKeyVersionState.DISABLED + + +def test_disable_enable_key_version(client, project_id, location_id, key_ring_id, asymmetric_decrypt_key_id): + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id) + version = client.create_crypto_key_version(key_name, {}) + version_id = version.name.split('/')[-1] + + wait_for_ready(client, version.name) + + disabled_version = disable_key_version(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, version_id) + assert disabled_version.state == kms.enums.CryptoKeyVersion.CryptoKeyVersionState.DISABLED + + enabled_version = enable_key_version(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, version_id) + assert enabled_version.state == kms.enums.CryptoKeyVersion.CryptoKeyVersionState.ENABLED + + +def test_encrypt_asymmetric(client, project_id, location_id, key_ring_id, asymmetric_decrypt_key_id): + plaintext = 'my message' + ciphertext = encrypt_asymmetric(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, '1', plaintext) + + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, '1') + response = client.asymmetric_decrypt(key_version_name, ciphertext) + assert response.plaintext == plaintext.encode('utf-8') + + +def test_encrypt_symmetric(client, project_id, location_id, key_ring_id, symmetric_key_id): + plaintext = 'my message' + encrypt_response = encrypt_symmetric(project_id, location_id, key_ring_id, symmetric_key_id, plaintext) + + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, symmetric_key_id) + decrypt_response = client.decrypt(key_name, encrypt_response.ciphertext) + assert decrypt_response.plaintext == plaintext.encode('utf-8') + + +def test_get_key_labels(project_id, location_id, key_ring_id, symmetric_key_id): + key = get_key_labels(project_id, location_id, key_ring_id, symmetric_key_id) + assert key.labels == {'foo': 'bar', 'zip': 'zap'} + + +def test_get_key_version_attestation(project_id, location_id, key_ring_id, hsm_key_id): + attestation = get_key_version_attestation(project_id, location_id, key_ring_id, hsm_key_id, '1') + assert attestation.format + assert attestation.content + + +def test_get_public_key(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id): + public_key = get_public_key(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, '1') + assert public_key.pem + + +def test_iam_add_member(project_id, location_id, key_ring_id, symmetric_key_id): + member = 'group:test@google.com' + policy = iam_add_member(project_id, location_id, key_ring_id, symmetric_key_id, member) + assert any(member in b.members for b in policy.bindings) + + +def test_iam_get_policy(project_id, location_id, key_ring_id, symmetric_key_id): + policy = iam_get_policy(project_id, location_id, key_ring_id, symmetric_key_id) + assert policy + + +def test_iam_remove_member(client, project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id): + resource_name = client.crypto_key_path(project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id) + + policy = client.get_iam_policy(resource_name) + policy.bindings.add( + role='roles/cloudkms.cryptoKeyEncrypterDecrypter', + members=['group:test@google.com', 'group:tester@google.com']) + client.set_iam_policy(resource_name, policy) + + policy = iam_remove_member(project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id, 'group:test@google.com') + assert not any('group:test@google.com' in b.members for b in policy.bindings) + assert any('group:tester@google.com' in b.members for b in policy.bindings) + + +def test_sign_asymmetric(client, project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id): + message = 'my message' + + sign_response = sign_asymmetric(project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id, '1', message) + assert sign_response.signature + + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id, '1') + public_key = client.get_public_key(key_version_name) + pem = public_key.pem.encode('utf-8') + rsa_key = serialization.load_pem_public_key(pem, default_backend()) + hash_ = hashlib.sha256(message.encode('utf-8')).digest() + + try: + sha256 = hashes.SHA256() + pad = padding.PKCS1v15() + rsa_key.verify(sign_response.signature, hash_, pad, utils.Prehashed(sha256)) + except InvalidSignature: + pytest.fail('invalid signature') + + +def test_update_key_add_rotation(project_id, location_id, key_ring_id, symmetric_key_id): + key = update_key_add_rotation(project_id, location_id, key_ring_id, symmetric_key_id) + assert key.rotation_period.seconds == 60*60*24*30 + assert key.next_rotation_time.seconds > 0 + + +def test_update_key_remove_labels(project_id, location_id, key_ring_id, symmetric_key_id): + key = update_key_remove_labels(project_id, location_id, key_ring_id, symmetric_key_id) + assert key.labels == {} + + +def test_update_key_remove_rotation(project_id, location_id, key_ring_id, symmetric_key_id): + key = update_key_remove_rotation(project_id, location_id, key_ring_id, symmetric_key_id) + assert key.rotation_period.seconds == 0 + assert key.next_rotation_time.seconds == 0 + + +def test_update_key_set_primary(project_id, location_id, key_ring_id, symmetric_key_id): + key = update_key_set_primary(project_id, location_id, key_ring_id, symmetric_key_id, '1') + assert '1' in key.primary.name + + +def test_update_key_update_labels(project_id, location_id, key_ring_id, symmetric_key_id): + key = update_key_update_labels(project_id, location_id, key_ring_id, symmetric_key_id) + assert key.labels == {'new_label': 'new_value'} + + +def test_verify_asymmetric_ec(client, project_id, location_id, key_ring_id, asymmetric_sign_ec_key_id): + message = 'my message' + + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, asymmetric_sign_ec_key_id, '1') + hash_ = hashlib.sha256(message.encode('utf-8')).digest() + sign_response = client.asymmetric_sign(key_version_name, {'sha256': hash_}) + + verified = verify_asymmetric_ec(project_id, location_id, key_ring_id, asymmetric_sign_ec_key_id, '1', message, sign_response.signature) + assert verified + + +def test_verify_asymmetric_rsa(client, project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id): + message = 'my message' + + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id, '1') + hash_ = hashlib.sha256(message.encode('utf-8')).digest() + sign_response = client.asymmetric_sign(key_version_name, {'sha256': hash_}) + + verified = verify_asymmetric_rsa(project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id, '1', message, sign_response.signature) + assert verified + + +def test_quickstart(project_id, location_id): + key_rings = quickstart(project_id, location_id) + assert key_rings diff --git a/samples/snippets/update_key_add_rotation.py b/samples/snippets/update_key_add_rotation.py new file mode 100644 index 00000000..22dd6b66 --- /dev/null +++ b/samples/snippets/update_key_add_rotation.py @@ -0,0 +1,62 @@ +# 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 + + +# [START kms_update_key_add_rotation_schedule] +def update_key_add_rotation(project_id, location_id, key_ring_id, key_id): + """ + Add a rotation schedule to an existing key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + + Returns: + CryptoKey: Updated Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Import time for getting the current time. + import time + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Build the key. We need to build a full proto instead of a dict due to + # https://github.com/googleapis/gapic-generator-python/issues/364. + from google.cloud.kms_v1.proto import resources_pb2 + key = resources_pb2.CryptoKey() + key.name = key_name + + # Rotate the key every 30 days. + key.rotation_period.seconds = 60*60*24*30 + + # Start the first rotation in 24 hours. + key.next_rotation_time.seconds = int(time.time()) + 60*60*24 + + # Build the update mask. + update_mask = {'paths': ['rotation_period', 'next_rotation_time']} + + # Call the API. + updated_key = client.update_crypto_key(key, update_mask) + print('Updated key: {}'.format(updated_key.name)) + return updated_key +# [END kms_update_key_add_rotation_schedule] diff --git a/samples/snippets/update_key_remove_labels.py b/samples/snippets/update_key_remove_labels.py new file mode 100644 index 00000000..a44ab214 --- /dev/null +++ b/samples/snippets/update_key_remove_labels.py @@ -0,0 +1,54 @@ +# 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 + + +# [START kms_update_key_remove_labels] +def update_key_remove_labels(project_id, location_id, key_ring_id, key_id): + """ + Remove labels from an existing key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + + Returns: + CryptoKey: Updated Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Build the key. We need to build a full proto instead of a dict due to + # https://github.com/googleapis/gapic-generator-python/issues/364. + from google.cloud.kms_v1.proto import resources_pb2 + key = resources_pb2.CryptoKey() + key.name = key_name + key.labels.clear() + + # Build the update mask. + update_mask = {'paths': ['labels']} + + # Call the API. + updated_key = client.update_crypto_key(key, update_mask) + print('Updated key: {}'.format(updated_key.name)) + return updated_key +# [END kms_update_key_remove_labels] diff --git a/samples/snippets/update_key_remove_rotation.py b/samples/snippets/update_key_remove_rotation.py new file mode 100644 index 00000000..7f8707eb --- /dev/null +++ b/samples/snippets/update_key_remove_rotation.py @@ -0,0 +1,53 @@ +# 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 + + +# [START kms_update_key_remove_rotation_schedule] +def update_key_remove_rotation(project_id, location_id, key_ring_id, key_id): + """ + Remove a rotation schedule from an existing key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + + Returns: + CryptoKey: Updated Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Build the key. We need to build a full proto instead of a dict due to + # https://github.com/googleapis/gapic-generator-python/issues/364. + from google.cloud.kms_v1.proto import resources_pb2 + key = resources_pb2.CryptoKey() + key.name = key_name + + # Build the update mask. + update_mask = {'paths': ['rotation_period', 'next_rotation_time']} + + # Call the API. + updated_key = client.update_crypto_key(key, update_mask) + print('Updated key: {}'.format(updated_key.name)) + return updated_key +# [END kms_update_key_remove_rotation_schedule] diff --git a/samples/snippets/update_key_set_primary.py b/samples/snippets/update_key_set_primary.py new file mode 100644 index 00000000..dd889dbd --- /dev/null +++ b/samples/snippets/update_key_set_primary.py @@ -0,0 +1,45 @@ +# 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 + + +# [START kms_update_key_set_primary] +def update_key_set_primary(project_id, location_id, key_ring_id, key_id, version_id): + """ + Update the primary version of a key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key to make primary (e.g. '2'). + + Returns: + CryptoKey: Updated Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Call the API. + updated_key = client.update_crypto_key_primary_version(key_name, version_id) + print('Updated {} primary to {}'.format(updated_key.name, version_id)) + return updated_key +# [END kms_update_key_set_primary] diff --git a/samples/snippets/update_key_update_labels.py b/samples/snippets/update_key_update_labels.py new file mode 100644 index 00000000..21372472 --- /dev/null +++ b/samples/snippets/update_key_update_labels.py @@ -0,0 +1,54 @@ +# 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 + + +# [START kms_update_key_update_labels] +def update_key_update_labels(project_id, location_id, key_ring_id, key_id): + """ + Update labels on an existing key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + + Returns: + CryptoKey: Updated Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Build the key. We need to build a full proto instead of a dict due to + # https://github.com/googleapis/gapic-generator-python/issues/364. + from google.cloud.kms_v1.proto import resources_pb2 + key = resources_pb2.CryptoKey() + key.name = key_name + key.labels.update({'new_label': 'new_value'}) + + # Build the update mask. + update_mask = {'paths': ['labels']} + + # Call the API. + updated_key = client.update_crypto_key(key, update_mask) + print('Updated key: {}'.format(updated_key.name)) + return updated_key +# [END kms_update_key_update_labels] diff --git a/samples/snippets/verify_asymmetric_ec.py b/samples/snippets/verify_asymmetric_ec.py new file mode 100644 index 00000000..ac77a64b --- /dev/null +++ b/samples/snippets/verify_asymmetric_ec.py @@ -0,0 +1,72 @@ +# 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 + + +# [START kms_verify_asymmetric_signature_ec] +def verify_asymmetric_ec(project_id, location_id, key_ring_id, key_id, version_id, message, signature): + """ + Verify the signature of an message signed with an asymmetric EC key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the version to use (e.g. '1'). + message (string): Original message (e.g. 'my message') + signature (bytes): Signature from a sign request. + + Returns: + bool: True if verified, False otherwise + + """ + + # Import the client library. + from google.cloud import kms + + # Import cryptographic helpers from the cryptography package. + from cryptography.exceptions import InvalidSignature + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import hashes, serialization + from cryptography.hazmat.primitives.asymmetric import ec, utils + + # Import hashlib. + import hashlib + + # Convert the message to bytes. + message_bytes = message.encode('utf-8') + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Get the public key. + public_key = client.get_public_key(key_version_name) + + # Extract and parse the public key as a PEM-encoded RSA key. + pem = public_key.pem.encode('utf-8') + ec_key = serialization.load_pem_public_key(pem, default_backend()) + hash_ = hashlib.sha256(message_bytes).digest() + + # Attempt to verify. + try: + sha256 = hashes.SHA256() + ec_key.verify(signature, hash_, ec.ECDSA(utils.Prehashed(sha256))) + print('Signature verified') + return True + except InvalidSignature: + print('Signature failed to verify') + return False +# [END kms_verify_asymmetric_signature_ec] diff --git a/samples/snippets/verify_asymmetric_rsa.py b/samples/snippets/verify_asymmetric_rsa.py new file mode 100644 index 00000000..6df3d862 --- /dev/null +++ b/samples/snippets/verify_asymmetric_rsa.py @@ -0,0 +1,73 @@ +# 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 + + +# [START kms_verify_asymmetric_signature_rsa] +def verify_asymmetric_rsa(project_id, location_id, key_ring_id, key_id, version_id, message, signature): + """ + Verify the signature of an message signed with an asymmetric RSA key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the version to use (e.g. '1'). + message (string): Original message (e.g. 'my message') + signature (bytes): Signature from a sign request. + + Returns: + bool: True if verified, False otherwise + + """ + + # Import the client library. + from google.cloud import kms + + # Import cryptographic helpers from the cryptography package. + from cryptography.exceptions import InvalidSignature + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import hashes, serialization + from cryptography.hazmat.primitives.asymmetric import padding, utils + + # Import hashlib. + import hashlib + + # Convert the message to bytes. + message_bytes = message.encode('utf-8') + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Get the public key. + public_key = client.get_public_key(key_version_name) + + # Extract and parse the public key as a PEM-encoded RSA key. + pem = public_key.pem.encode('utf-8') + rsa_key = serialization.load_pem_public_key(pem, default_backend()) + hash_ = hashlib.sha256(message_bytes).digest() + + # Attempt to verify. + try: + sha256 = hashes.SHA256() + pad = padding.PKCS1v15() + rsa_key.verify(signature, hash_, pad, utils.Prehashed(sha256)) + print('Signature verified') + return True + except InvalidSignature: + print('Signature failed to verify') + return False +# [END kms_verify_asymmetric_signature_rsa] diff --git a/scripts/decrypt-secrets.sh b/scripts/decrypt-secrets.sh new file mode 100755 index 00000000..ff599eb2 --- /dev/null +++ b/scripts/decrypt-secrets.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Copyright 2015 Google Inc. All rights reserved. +# +# 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. + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +ROOT=$( dirname "$DIR" ) + +# Work from the project root. +cd $ROOT + +# Use SECRET_MANAGER_PROJECT if set, fallback to cloud-devrel-kokoro-resources. +PROJECT_ID="${SECRET_MANAGER_PROJECT:-cloud-devrel-kokoro-resources}" + +gcloud secrets versions access latest --secret="python-docs-samples-test-env" \ + > testing/test-env.sh +gcloud secrets versions access latest \ + --secret="python-docs-samples-service-account" \ + > testing/service-account.json +gcloud secrets versions access latest \ + --secret="python-docs-samples-client-secrets" \ + > testing/client-secrets.json \ No newline at end of file diff --git a/scripts/readme-gen/readme_gen.py b/scripts/readme-gen/readme_gen.py new file mode 100644 index 00000000..d309d6e9 --- /dev/null +++ b/scripts/readme-gen/readme_gen.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +# Copyright 2016 Google Inc +# +# 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. + +"""Generates READMEs using configuration defined in yaml.""" + +import argparse +import io +import os +import subprocess + +import jinja2 +import yaml + + +jinja_env = jinja2.Environment( + trim_blocks=True, + loader=jinja2.FileSystemLoader( + os.path.abspath(os.path.join(os.path.dirname(__file__), 'templates')))) + +README_TMPL = jinja_env.get_template('README.tmpl.rst') + + +def get_help(file): + return subprocess.check_output(['python', file, '--help']).decode() + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('source') + parser.add_argument('--destination', default='README.rst') + + args = parser.parse_args() + + source = os.path.abspath(args.source) + root = os.path.dirname(source) + destination = os.path.join(root, args.destination) + + jinja_env.globals['get_help'] = get_help + + with io.open(source, 'r') as f: + config = yaml.load(f) + + # This allows get_help to execute in the right directory. + os.chdir(root) + + output = README_TMPL.render(config) + + with io.open(destination, 'w') as f: + f.write(output) + + +if __name__ == '__main__': + main() diff --git a/scripts/readme-gen/templates/README.tmpl.rst b/scripts/readme-gen/templates/README.tmpl.rst new file mode 100644 index 00000000..4fd23976 --- /dev/null +++ b/scripts/readme-gen/templates/README.tmpl.rst @@ -0,0 +1,87 @@ +{# The following line is a lie. BUT! Once jinja2 is done with it, it will + become truth! #} +.. This file is automatically generated. Do not edit this file directly. + +{{product.name}} Python Samples +=============================================================================== + +.. image:: https://gstatic.com/cloudssh/images/open-btn.png + :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor={{folder}}/README.rst + + +This directory contains samples for {{product.name}}. {{product.description}} + +{{description}} + +.. _{{product.name}}: {{product.url}} + +{% if required_api_url %} +To run the sample, you need to enable the API at: {{required_api_url}} +{% endif %} + +{% if required_role %} +To run the sample, you need to have `{{required_role}}` role. +{% endif %} + +{{other_required_steps}} + +{% if setup %} +Setup +------------------------------------------------------------------------------- + +{% for section in setup %} + +{% include section + '.tmpl.rst' %} + +{% endfor %} +{% endif %} + +{% if samples %} +Samples +------------------------------------------------------------------------------- + +{% for sample in samples %} +{{sample.name}} ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +{% if not sample.hide_cloudshell_button %} +.. image:: https://gstatic.com/cloudssh/images/open-btn.png + :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor={{folder}}/{{sample.file}},{{folder}}/README.rst +{% endif %} + + +{{sample.description}} + +To run this sample: + +.. code-block:: bash + + $ python {{sample.file}} +{% if sample.show_help %} + + {{get_help(sample.file)|indent}} +{% endif %} + + +{% endfor %} +{% endif %} + +{% if cloud_client_library %} + +The client library +------------------------------------------------------------------------------- + +This sample uses the `Google Cloud Client Library for Python`_. +You can read the documentation for more details on API usage and use GitHub +to `browse the source`_ and `report issues`_. + +.. _Google Cloud Client Library for Python: + https://googlecloudplatform.github.io/google-cloud-python/ +.. _browse the source: + https://github.com/GoogleCloudPlatform/google-cloud-python +.. _report issues: + https://github.com/GoogleCloudPlatform/google-cloud-python/issues + +{% endif %} + +.. _Google Cloud SDK: https://cloud.google.com/sdk/ \ No newline at end of file diff --git a/scripts/readme-gen/templates/auth.tmpl.rst b/scripts/readme-gen/templates/auth.tmpl.rst new file mode 100644 index 00000000..1446b94a --- /dev/null +++ b/scripts/readme-gen/templates/auth.tmpl.rst @@ -0,0 +1,9 @@ +Authentication +++++++++++++++ + +This sample requires you to have authentication setup. Refer to the +`Authentication Getting Started Guide`_ for instructions on setting up +credentials for applications. + +.. _Authentication Getting Started Guide: + https://cloud.google.com/docs/authentication/getting-started diff --git a/scripts/readme-gen/templates/auth_api_key.tmpl.rst b/scripts/readme-gen/templates/auth_api_key.tmpl.rst new file mode 100644 index 00000000..11957ce2 --- /dev/null +++ b/scripts/readme-gen/templates/auth_api_key.tmpl.rst @@ -0,0 +1,14 @@ +Authentication +++++++++++++++ + +Authentication for this service is done via an `API Key`_. To obtain an API +Key: + +1. Open the `Cloud Platform Console`_ +2. Make sure that billing is enabled for your project. +3. From the **Credentials** page, create a new **API Key** or use an existing + one for your project. + +.. _API Key: + https://developers.google.com/api-client-library/python/guide/aaa_apikeys +.. _Cloud Console: https://console.cloud.google.com/project?_ diff --git a/scripts/readme-gen/templates/install_deps.tmpl.rst b/scripts/readme-gen/templates/install_deps.tmpl.rst new file mode 100644 index 00000000..a0406dba --- /dev/null +++ b/scripts/readme-gen/templates/install_deps.tmpl.rst @@ -0,0 +1,29 @@ +Install Dependencies +++++++++++++++++++++ + +#. Clone python-docs-samples and change directory to the sample directory you want to use. + + .. code-block:: bash + + $ git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git + +#. Install `pip`_ and `virtualenv`_ if you do not already have them. You may want to refer to the `Python Development Environment Setup Guide`_ for Google Cloud Platform for instructions. + + .. _Python Development Environment Setup Guide: + https://cloud.google.com/python/setup + +#. Create a virtualenv. Samples are compatible with Python 2.7 and 3.4+. + + .. code-block:: bash + + $ virtualenv env + $ source env/bin/activate + +#. Install the dependencies needed to run the samples. + + .. code-block:: bash + + $ pip install -r requirements.txt + +.. _pip: https://pip.pypa.io/ +.. _virtualenv: https://virtualenv.pypa.io/ diff --git a/scripts/readme-gen/templates/install_portaudio.tmpl.rst b/scripts/readme-gen/templates/install_portaudio.tmpl.rst new file mode 100644 index 00000000..5ea33d18 --- /dev/null +++ b/scripts/readme-gen/templates/install_portaudio.tmpl.rst @@ -0,0 +1,35 @@ +Install PortAudio ++++++++++++++++++ + +Install `PortAudio`_. This is required by the `PyAudio`_ library to stream +audio from your computer's microphone. PyAudio depends on PortAudio for cross-platform compatibility, and is installed differently depending on the +platform. + +* For Mac OS X, you can use `Homebrew`_:: + + brew install portaudio + + **Note**: if you encounter an error when running `pip install` that indicates + it can't find `portaudio.h`, try running `pip install` with the following + flags:: + + pip install --global-option='build_ext' \ + --global-option='-I/usr/local/include' \ + --global-option='-L/usr/local/lib' \ + pyaudio + +* For Debian / Ubuntu Linux:: + + apt-get install portaudio19-dev python-all-dev + +* Windows may work without having to install PortAudio explicitly (it will get + installed with PyAudio). + +For more details, see the `PyAudio installation`_ page. + + +.. _PyAudio: https://people.csail.mit.edu/hubert/pyaudio/ +.. _PortAudio: http://www.portaudio.com/ +.. _PyAudio installation: + https://people.csail.mit.edu/hubert/pyaudio/#downloads +.. _Homebrew: http://brew.sh diff --git a/synth.metadata b/synth.metadata index 0289fcb4..29286d25 100644 --- a/synth.metadata +++ b/synth.metadata @@ -1,26 +1,18 @@ { - "updateTime": "2020-04-14T21:47:51.484475Z", "sources": [ { - "generator": { - "name": "artman", - "version": "2.0.0", - "dockerImage": "googleapis/artman@sha256:b3b47805231a305d0f40c4bf069df20f6a2635574e6d4259fac651d3f9f6e098" + "git": { + "name": ".", + "remote": "git@github.com:googleapis/python-kms.git", + "sha": "309319a29b3e0f851d93c58de06b60f50cf0fbfb" } }, { "git": { "name": "googleapis", "remote": "https://github.com/googleapis/googleapis.git", - "sha": "fea22b1d9f27f86ef355c1d0dba00e0791a08a19", - "internalRef": "306508794" - } - }, - { - "git": { - "name": "synthtool", - "remote": "https://github.com/googleapis/synthtool.git", - "sha": "52638600f387deb98efb5f9c85fec39e82aa9052" + "sha": "eafa840ceec23b44a5c21670288107c661252711", + "internalRef": "313488995" } } ], @@ -31,8 +23,7 @@ "apiName": "kms", "apiVersion": "v1", "language": "python", - "generator": "gapic", - "config": "google/cloud/kms/artman_cloudkms.yaml" + "generator": "bazel" } } ] diff --git a/synth.py b/synth.py index cfdf648e..c17cb683 100644 --- a/synth.py +++ b/synth.py @@ -16,6 +16,7 @@ import synthtool as s import synthtool.gcp as gcp +from synthtool.languages import python import logging logging.basicConfig(level=logging.DEBUG) @@ -71,7 +72,13 @@ def crypto_key_path_path(cls, project, location, key_ring, crypto_key_path): # ---------------------------------------------------------------------------- # Add templated files # ---------------------------------------------------------------------------- -templated_files = common.py_library(cov_level=70) +templated_files = common.py_library(cov_level=70, samples=True) s.move(templated_files) +# ---------------------------------------------------------------------------- +# Samples templates +# ---------------------------------------------------------------------------- + +python.py_samples() + s.shell.run(["nox", "-s", "blacken"], hide_output=False) diff --git a/testing/.gitignore b/testing/.gitignore new file mode 100644 index 00000000..b05fbd63 --- /dev/null +++ b/testing/.gitignore @@ -0,0 +1,3 @@ +test-env.sh +service-account.json +client-secrets.json \ No newline at end of file