diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ddbdfe696..c9c9806c1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,8 +4,8 @@ # For syntax help see: # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax -# The @googleapis/yoshi-python is the default owner for changes in this repo -* @googleapis/yoshi-python +# The @googleapis/yoshi-python and @googleapis/actools are the default code owners for changes in this repo +* @googleapis/yoshi-python @googleapis/actools /samples/ @m-strzelczyk @googleapis/python-samples-owners diff --git a/.kokoro/docker/docs/Dockerfile b/.kokoro/docker/docs/Dockerfile index 412b0b56a..4e1b1fb8b 100644 --- a/.kokoro/docker/docs/Dockerfile +++ b/.kokoro/docker/docs/Dockerfile @@ -40,6 +40,7 @@ RUN apt-get update \ libssl-dev \ libsqlite3-dev \ portaudio19-dev \ + python3-distutils \ redis-server \ software-properties-common \ ssh \ @@ -59,40 +60,8 @@ RUN apt-get update \ && rm -rf /var/lib/apt/lists/* \ && rm -f /var/cache/apt/archives/*.deb - -COPY fetch_gpg_keys.sh /tmp -# Install the desired versions of Python. -RUN set -ex \ - && export GNUPGHOME="$(mktemp -d)" \ - && echo "disable-ipv6" >> "${GNUPGHOME}/dirmngr.conf" \ - && /tmp/fetch_gpg_keys.sh \ - && for PYTHON_VERSION in 3.7.8 3.8.5; do \ - wget --no-check-certificate -O python-${PYTHON_VERSION}.tar.xz "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz" \ - && wget --no-check-certificate -O python-${PYTHON_VERSION}.tar.xz.asc "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc" \ - && gpg --batch --verify python-${PYTHON_VERSION}.tar.xz.asc python-${PYTHON_VERSION}.tar.xz \ - && rm -r python-${PYTHON_VERSION}.tar.xz.asc \ - && mkdir -p /usr/src/python-${PYTHON_VERSION} \ - && tar -xJC /usr/src/python-${PYTHON_VERSION} --strip-components=1 -f python-${PYTHON_VERSION}.tar.xz \ - && rm python-${PYTHON_VERSION}.tar.xz \ - && cd /usr/src/python-${PYTHON_VERSION} \ - && ./configure \ - --enable-shared \ - # This works only on Python 2.7 and throws a warning on every other - # version, but seems otherwise harmless. - --enable-unicode=ucs4 \ - --with-system-ffi \ - --without-ensurepip \ - && make -j$(nproc) \ - && make install \ - && ldconfig \ - ; done \ - && rm -rf "${GNUPGHOME}" \ - && rm -rf /usr/src/python* \ - && rm -rf ~/.cache/ - RUN wget -O /tmp/get-pip.py 'https://bootstrap.pypa.io/get-pip.py' \ - && python3.7 /tmp/get-pip.py \ && python3.8 /tmp/get-pip.py \ && rm /tmp/get-pip.py -CMD ["python3.7"] +CMD ["python3.8"] diff --git a/.kokoro/docker/docs/fetch_gpg_keys.sh b/.kokoro/docker/docs/fetch_gpg_keys.sh deleted file mode 100755 index d653dd868..000000000 --- a/.kokoro/docker/docs/fetch_gpg_keys.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash -# Copyright 2020 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# A script to fetch gpg keys with retry. -# Avoid jinja parsing the file. -# - -function retry { - if [[ "${#}" -le 1 ]]; then - echo "Usage: ${0} retry_count commands.." - exit 1 - fi - local retries=${1} - local command="${@:2}" - until [[ "${retries}" -le 0 ]]; do - $command && return 0 - if [[ $? -ne 0 ]]; then - echo "command failed, retrying" - ((retries--)) - fi - done - return 1 -} - -# 3.6.9, 3.7.5 (Ned Deily) -retry 3 gpg --keyserver ha.pool.sks-keyservers.net --recv-keys \ - 0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D - -# 3.8.0 (Łukasz Langa) -retry 3 gpg --keyserver ha.pool.sks-keyservers.net --recv-keys \ - E3FF2839C048B25C084DEBE9B26995E310250568 - -# diff --git a/.kokoro/samples/python3.6/periodic-head.cfg b/.kokoro/samples/python3.6/periodic-head.cfg index f9cfcd33e..343d23602 100644 --- a/.kokoro/samples/python3.6/periodic-head.cfg +++ b/.kokoro/samples/python3.6/periodic-head.cfg @@ -7,5 +7,5 @@ env_vars: { env_vars: { key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-pubsub/.kokoro/test-samples-against-head.sh" + value: "github/python-compute/.kokoro/test-samples-against-head.sh" } diff --git a/.kokoro/samples/python3.7/periodic-head.cfg b/.kokoro/samples/python3.7/periodic-head.cfg index f9cfcd33e..343d23602 100644 --- a/.kokoro/samples/python3.7/periodic-head.cfg +++ b/.kokoro/samples/python3.7/periodic-head.cfg @@ -7,5 +7,5 @@ env_vars: { env_vars: { key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-pubsub/.kokoro/test-samples-against-head.sh" + value: "github/python-compute/.kokoro/test-samples-against-head.sh" } diff --git a/.kokoro/samples/python3.8/periodic-head.cfg b/.kokoro/samples/python3.8/periodic-head.cfg index f9cfcd33e..343d23602 100644 --- a/.kokoro/samples/python3.8/periodic-head.cfg +++ b/.kokoro/samples/python3.8/periodic-head.cfg @@ -7,5 +7,5 @@ env_vars: { env_vars: { key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-pubsub/.kokoro/test-samples-against-head.sh" + value: "github/python-compute/.kokoro/test-samples-against-head.sh" } diff --git a/.kokoro/samples/python3.9/common.cfg b/.kokoro/samples/python3.9/common.cfg new file mode 100644 index 000000000..52e4c47c0 --- /dev/null +++ b/.kokoro/samples/python3.9/common.cfg @@ -0,0 +1,40 @@ +# 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.9" +} + +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-py39" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-compute/.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-compute/.kokoro/trampoline.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.9/continuous.cfg b/.kokoro/samples/python3.9/continuous.cfg new file mode 100644 index 000000000..a1c8d9759 --- /dev/null +++ b/.kokoro/samples/python3.9/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.9/periodic-head.cfg b/.kokoro/samples/python3.9/periodic-head.cfg new file mode 100644 index 000000000..343d23602 --- /dev/null +++ b/.kokoro/samples/python3.9/periodic-head.cfg @@ -0,0 +1,11 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-compute/.kokoro/test-samples-against-head.sh" +} diff --git a/.kokoro/samples/python3.9/periodic.cfg b/.kokoro/samples/python3.9/periodic.cfg new file mode 100644 index 000000000..50fec9649 --- /dev/null +++ b/.kokoro/samples/python3.9/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.9/presubmit.cfg b/.kokoro/samples/python3.9/presubmit.cfg new file mode 100644 index 000000000..a1c8d9759 --- /dev/null +++ b/.kokoro/samples/python3.9/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-impl.sh b/.kokoro/test-samples-impl.sh index cf5de74c1..311a8d54b 100755 --- a/.kokoro/test-samples-impl.sh +++ b/.kokoro/test-samples-impl.sh @@ -20,9 +20,9 @@ set -eo pipefail # Enables `**` to include files nested inside sub-folders shopt -s globstar -# Exit early if samples directory doesn't exist -if [ ! -d "./samples" ]; then - echo "No tests run. `./samples` not found" +# Exit early if samples don't exist +if ! find samples -name 'requirements.txt' | grep -q .; then + echo "No tests run. './samples/**/requirements.txt' not found" exit 0 fi diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4f00c7cff..62eb5a77d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.4.0 + rev: v4.0.1 hooks: - id: trailing-whitespace - id: end-of-file-fixer diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e18799b1..933333020 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +### [0.4.2](https://www.github.com/googleapis/python-compute/compare/v0.4.1...v0.4.2) (2021-07-26) + +### Bug Fixes + +* **deps:** pin 'google-{api,cloud}-core', 'google-auth' to allow 2.x versions ([#81](https://www.github.com/googleapis/python-compute/issues/81)) ([1163f2b](https://www.github.com/googleapis/python-compute/commit/1163f2bb15f0e042bdd9562a31ad2ac7f39e3536)) + + +### Documentation + +* samples docstring and comments update ([#69](https://www.github.com/googleapis/python-compute/issues/69)) ([2d53eb1](https://www.github.com/googleapis/python-compute/commit/2d53eb1839a8751945ba7cdf12991f2970893df2)) +* adding samples for default values ([#64](https://www.github.com/googleapis/python-compute/issues/64)) ([553e389](https://www.github.com/googleapis/python-compute/commit/553e3891179c2719a57d398ffc26c1459945bf4d)) + + +### Miscellaneous Chores + +* release as 0.4.2 ([#84](https://www.github.com/googleapis/python-compute/issues/84)) ([dc77c0c](https://www.github.com/googleapis/python-compute/commit/dc77c0c55eef9cc1c2e515137fe66b4d4afca77e)) +* Kokoro uses separate project for concurent tests. ([#83](https://www.github.com/googleapis/python-compute/issues/83)) ([40afb33](https://www.github.com/googleapis/python-compute/commit/40afb333a963717853f70410a7a08eda5492418c)) + + ### [0.4.1](https://www.github.com/googleapis/python-compute/compare/v0.4.0...v0.4.1) (2021-06-16) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index cefabb377..6d765f117 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -68,15 +68,12 @@ Using ``nox`` We use `nox `__ to instrument our tests. - To test your changes, run unit tests with ``nox``:: + $ nox -s unit - $ nox -s unit-2.7 - $ nox -s unit-3.8 - $ ... +- To run a single unit test:: -- Args to pytest can be passed through the nox command separated by a `--`. For - example, to run a single test:: + $ nox -s unit-3.9 -- -k - $ nox -s unit-3.8 -- -k .. note:: @@ -143,8 +140,7 @@ Running System Tests - To run system tests, you can execute:: # Run all system tests - $ nox -s system-3.8 - $ nox -s system-2.7 + $ nox -s system # Run a single system test $ nox -s system-3.8 -- -k @@ -152,9 +148,8 @@ Running System Tests .. note:: - System tests are only configured to run under Python 2.7 and - Python 3.8. For expediency, we do not run them in older versions - of Python 3. + System tests are only configured to run under Python 3.8. + For expediency, we do not run them in older versions of Python 3. This alone will not run the tests. You'll need to change some local auth settings and change some configuration in your project to @@ -242,8 +237,8 @@ Supported versions can be found in our ``noxfile.py`` `config`_. .. _config: https://github.com/googleapis/python-compute/blob/master/noxfile.py -We also explicitly decided to support Python 3 beginning with version -3.6. Reasons for this include: +We also explicitly decided to support Python 3 beginning with version 3.6. +Reasons for this include: - Encouraging use of newest versions of Python 3 - Taking the lead of `prominent`_ open-source `projects`_ diff --git a/docs/conf.py b/docs/conf.py index 5774aeb8b..3eda874a8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -80,9 +80,9 @@ master_doc = "index" # General information about the project. -project = u"google-cloud-compute" -copyright = u"2019, Google" -author = u"Google APIs" +project = "google-cloud-compute" +copyright = "2019, Google" +author = "Google APIs" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -281,7 +281,7 @@ ( master_doc, "google-cloud-compute.tex", - u"google-cloud-compute Documentation", + "google-cloud-compute Documentation", author, "manual", ) @@ -316,7 +316,7 @@ ( master_doc, "google-cloud-compute", - u"google-cloud-compute Documentation", + "google-cloud-compute Documentation", [author], 1, ) @@ -335,7 +335,7 @@ ( master_doc, "google-cloud-compute", - u"google-cloud-compute Documentation", + "google-cloud-compute Documentation", author, "google-cloud-compute", "google-cloud-compute Library", diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index 5ff9e1db5..6a8ccdae2 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -28,8 +28,9 @@ # WARNING - WARNING - WARNING - WARNING - WARNING # WARNING - WARNING - WARNING - WARNING - WARNING -# Copy `noxfile_config.py` to your directory and modify it instead. +BLACK_VERSION = "black==19.10b0" +# 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 @@ -159,7 +160,7 @@ def lint(session: nox.sessions.Session) -> None: @nox.session def blacken(session: nox.sessions.Session) -> None: - session.install("black") + session.install(BLACK_VERSION) python_files = [path for path in os.listdir(".") if path.endswith(".py")] session.run("black", *python_files) diff --git a/samples/snippets/noxfile_config.py b/samples/snippets/noxfile_config.py new file mode 100644 index 000000000..16c4c694e --- /dev/null +++ b/samples/snippets/noxfile_config.py @@ -0,0 +1,18 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. + +TEST_CONFIG_OVERRIDE = { + # Tests in test_sample_default_values.py require separate projects to not interfere with each other. + 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT', +} diff --git a/samples/snippets/quickstart.py b/samples/snippets/quickstart.py index 478a5f707..fb67bfab0 100644 --- a/samples/snippets/quickstart.py +++ b/samples/snippets/quickstart.py @@ -40,13 +40,11 @@ # [START compute_instances_list] def list_instances(project_id: str, zone: str) -> typing.Iterable[compute_v1.Instance]: """ - Gets a list of instances created in given project in given zone. - Returns an iterable collection of Instance objects. + List all instances in the given zone in the specified project. Args: - project_id: ID or number of the project you want to use. - zone: Name of the zone you want to check, for example: us-west3-b - + project_id: project ID or project number of the Cloud project you want to use. + zone: name of the zone you want to use. For example: “us-west3-b” Returns: An iterable collection of Instance objects. """ @@ -58,20 +56,18 @@ def list_instances(project_id: str, zone: str) -> typing.Iterable[compute_v1.Ins print(f" - {instance.name} ({instance.machine_type})") return instance_list - - # [END compute_instances_list] + # [START compute_instances_list_all] def list_all_instances( project_id: str, ) -> typing.Dict[str, typing.Iterable[compute_v1.Instance]]: """ - Returns a dictionary of all instances present in a project, grouped by their zone. + Return a dictionary of all instances present in a project, grouped by their zone. Args: - project_id: ID or number of the project you want to use. - + project_id: project ID or project number of the Cloud project you want to use. Returns: A dictionary with zone names as keys (in form of "zones/{zone_name}") and iterable collections of Instance objects as values. @@ -87,8 +83,6 @@ def list_all_instances( for instance in response.instances: print(f" - {instance.name} ({instance.machine_type})") return all_instances - - # [END compute_instances_list_all] @@ -102,33 +96,28 @@ def create_instance( network_name: str = "global/networks/default", ) -> compute_v1.Instance: """ - Sends an instance creation request to GCP and waits for it to complete. + Send an instance creation request to the Compute Engine API and wait for it to complete. Args: - project_id: ID or number of the project you want to use. - zone: Name of the zone you want to use, for example: us-west3-b - instance_name: Name of the new machine. - machine_type: Machine type you want to create in following format: - "zones/{zone}/machineTypes/{type_name}". For example: - "zones/europe-west3-c/machineTypes/f1-micro" - You can find the list of available machine types using: - https://cloud.google.com/sdk/gcloud/reference/compute/machine-types/list - source_image: Path the the disk image you want to use for your boot + project_id: project ID or project number of the Cloud project you want to use. + zone: name of the zone you want to use. For example: “us-west3-b” + instance_name: name of the new virtual machine. + machine_type: machine type of the VM being created. This value uses the + following format: "zones/{zone}/machineTypes/{type_name}". + For example: "zones/europe-west3-c/machineTypes/f1-micro" + source_image: path to the operating system image to mount on your boot disk. This can be one of the public images - (e.g. "projects/debian-cloud/global/images/family/debian-10") + (like "projects/debian-cloud/global/images/family/debian-10") or a private image you have access to. - You can check the list of available public images using: - $ gcloud compute images list - network_name: Name of the network you want the new instance to use. - For example: global/networks/default - if you want to use the - default network. - + network_name: name of the network you want the new instance to use. + For example: "global/networks/default" represents the `default` + network interface, which is created automatically for each project. Returns: Instance object. """ instance_client = compute_v1.InstancesClient() - # Every machine requires at least one persistent disk + # Describe the size and source image of the boot disk to attach to the instance. disk = compute_v1.AttachedDisk() initialize_params = compute_v1.AttachedDiskInitializeParams() initialize_params.source_image = ( @@ -140,12 +129,11 @@ def create_instance( disk.boot = True disk.type_ = compute_v1.AttachedDisk.Type.PERSISTENT - # Every machine needs to be connected to a VPC network. - # The 'default' network is created automatically in every project. + # Use the network interface provided in the network_name argument. network_interface = compute_v1.NetworkInterface() network_interface.name = network_name - # Collecting all the information into the Instance object + # Collect information into the Instance object. instance = compute_v1.Instance() instance.name = instance_name instance.disks = [disk] @@ -153,12 +141,13 @@ def create_instance( instance.machine_type = full_machine_type_name instance.network_interfaces = [network_interface] - # Preparing the InsertInstanceRequest + # Prepare the request to insert an instance. request = compute_v1.InsertInstanceRequest() request.zone = zone request.project = project_id request.instance_resource = instance + # Wait for the create operation to complete. print(f"Creating the {instance_name} instance in {zone}...") operation = instance_client.insert(request=request) if operation.status == compute_v1.Operation.Status.RUNNING: @@ -172,20 +161,18 @@ def create_instance( print("Warning during creation:", operation.warnings, file=sys.stderr) print(f"Instance {instance_name} created.") return instance - - # [END compute_instances_create] # [START compute_instances_delete] def delete_instance(project_id: str, zone: str, machine_name: str) -> None: """ - Sends a delete request to GCP and waits for it to complete. + Send an instance deletion request to the Compute Engine API and wait for it to complete. Args: - project_id: ID or number of the project you want to use. - zone: Name of the zone you want to use, for example: us-west3-b - machine_name: Name of the machine you want to delete. + project_id: project ID or project number of the Cloud project you want to use. + zone: name of the zone you want to use. For example: “us-west3-b” + machine_name: name of the machine you want to delete. """ instance_client = compute_v1.InstancesClient() @@ -204,8 +191,6 @@ def delete_instance(project_id: str, zone: str, machine_name: str) -> None: print("Warning during deletion:", operation.warnings, file=sys.stderr) print(f"Instance {machine_name} deleted.") return - - # [END compute_instances_delete] @@ -220,7 +205,7 @@ def wait_for_operation( Args: operation: The Operation object representing the operation you want to wait on. - project_id: ID or number of the project owning the operation. + project_id: project ID or project number of the Cloud project you want to use. Returns: Finished Operation object. @@ -232,13 +217,11 @@ def wait_for_operation( kwargs["zone"] = operation.zone.rsplit("/", maxsplit=1)[1] elif operation.region: client = compute_v1.RegionOperationsClient() - # Operation.region is a full URL address of a zone, so we need to extract just the name + # Operation.region is a full URL address of a region, so we need to extract just the name kwargs["region"] = operation.region.rsplit("/", maxsplit=1)[1] else: client = compute_v1.GlobalOperationsClient() return client.wait(**kwargs) - - # [END compute_instances_operation_check] diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index 11b890fae..444dff8b3 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -1 +1,2 @@ -pytest==6.2.4 \ No newline at end of file +pytest==6.2.4 +google-cloud-storage==1.41.1 \ No newline at end of file diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index d562c687b..c7973f448 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1 +1 @@ -google-cloud-compute==0.4.0 \ No newline at end of file +google-cloud-compute==0.4.1 \ No newline at end of file diff --git a/samples/snippets/sample_default_values.py b/samples/snippets/sample_default_values.py new file mode 100644 index 000000000..5f14e2a77 --- /dev/null +++ b/samples/snippets/sample_default_values.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. +""" +A sample script showing how to handle default values when communicating +with the Compute Engine API. +""" +# [START compute_instances_verify_default_value] +# [START compute_usage_report_set] +# [START compute_usage_report_get] +# [START compute_usage_report_disable] +from google.cloud import compute_v1 +# [END compute_usage_report_disable] +# [END compute_usage_report_get] +# [END compute_usage_report_set] + + +# [START compute_usage_report_set] +def set_usage_export_bucket(project_id: str, bucket_name: str, + report_name_prefix: str = "") -> None: + """ + Set Compute Engine usage export bucket for the Cloud project. + This sample presents how to interpret the default value for the + report name prefix parameter. + + Args: + project_id: project ID or project number of the project to update. + bucket_name: Google Cloud Storage bucket used to store Compute Engine + usage reports. An existing Google Cloud Storage bucket is required. + report_name_prefix: Prefix of the usage report name which defaults to an empty string + to showcase default values behaviour. + """ + usage_export_location = compute_v1.UsageExportLocation( + bucket_name=bucket_name, + report_name_prefix=report_name_prefix + ) + + if not report_name_prefix: + # Sending an empty value for report_name_prefix results in the + # next usage report being generated with the default prefix value + # "usage_gce". (ref: https://cloud.google.com/compute/docs/reference/rest/v1/projects/setUsageExportBucket) + print("Setting report_name_prefix to empty value causes the report " + "to have the default prefix of `usage_gce`.") + + projects_client = compute_v1.ProjectsClient() + operation = projects_client.set_usage_export_bucket( + project=project_id, usage_export_location_resource=usage_export_location) + + op_client = compute_v1.GlobalOperationsClient() + op_client.wait(project=project_id, operation=operation.name) +# [END compute_usage_report_set] + + +# [START compute_usage_report_get] +def get_usage_export_bucket(project_id: str) -> compute_v1.UsageExportLocation: + """ + Retrieve Compute Engine usage export bucket for the Cloud project. + Replaces the empty value returned by the API with the default value used + to generate report file names. + + Args: + project_id: project ID or project number of the project to update. + Returns: + UsageExportLocation object describing the current usage export settings + for project project_id. + """ + projects_client = compute_v1.ProjectsClient() + project_data = projects_client.get(project=project_id) + + uel = project_data.usage_export_location + + if not uel.bucket_name: + # The usage reports are disabled. + return uel + + if not uel.report_name_prefix: + # Although the server sent the empty string value, the next usage report + # generated with these settings still has the default prefix value + # "usage_gce". (see https://cloud.google.com/compute/docs/reference/rest/v1/projects/get) + print('Report name prefix not set, replacing with default value of ' + '`usage_gce`.') + uel.report_name_prefix = 'usage_gce' + return uel +# [END compute_usage_report_get] +# [END compute_instances_verify_default_value] + + +# [START compute_usage_report_disable] +def disable_usage_export(project_id: str) -> None: + """ + Disable Compute Engine usage export bucket for the Cloud Project. + + Args: + project_id: project ID or project number of the project to update. + """ + projects_client = compute_v1.ProjectsClient() + + # Updating the setting with None will disable the + # usage report generation. + operation = projects_client.set_usage_export_bucket( + project=project_id, usage_export_location_resource=None) + + op_client = compute_v1.GlobalOperationsClient() + op_client.wait(project=project_id, operation=operation.name) +# [END compute_usage_report_disable] diff --git a/samples/snippets/test_sample_default_values.py b/samples/snippets/test_sample_default_values.py new file mode 100644 index 000000000..b6d2f0acc --- /dev/null +++ b/samples/snippets/test_sample_default_values.py @@ -0,0 +1,65 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import typing +import uuid + +import google.auth +import google.cloud.storage as storage +import pytest + +from sample_default_values import \ + disable_usage_export, get_usage_export_bucket, set_usage_export_bucket + +PROJECT = google.auth.default()[1] +BUCKET_NAME = "test" + uuid.uuid4().hex[:10] +TEST_PREFIX = 'some-prefix' + + +@pytest.fixture +def temp_bucket(): + storage_client = storage.Client() + bucket = storage_client.create_bucket(BUCKET_NAME) + yield bucket + bucket.delete(force=True) + + +def test_set_usage_export_bucket_default(capsys: typing.Any, + temp_bucket: storage.Bucket) -> None: + set_usage_export_bucket(project_id=PROJECT, bucket_name=temp_bucket.name) + uel = get_usage_export_bucket(project_id=PROJECT) + assert(uel.bucket_name == temp_bucket.name) + assert(uel.report_name_prefix == 'usage_gce') + out, _ = capsys.readouterr() + assert('default prefix of `usage_gce`.' in out) + + disable_usage_export(project_id=PROJECT) + uel = get_usage_export_bucket(project_id=PROJECT) + assert(uel.bucket_name == '') + assert(uel.report_name_prefix == '') + + +def test_set_usage_export_bucket_custom(capsys: typing.Any, + temp_bucket: storage.Bucket) -> None: + set_usage_export_bucket(project_id=PROJECT, bucket_name=temp_bucket.name, + report_name_prefix=TEST_PREFIX) + uel = get_usage_export_bucket(project_id=PROJECT) + assert(uel.bucket_name == temp_bucket.name) + assert(uel.report_name_prefix == TEST_PREFIX) + out, _ = capsys.readouterr() + assert('usage_gce' not in out) + + disable_usage_export(project_id=PROJECT) + uel = get_usage_export_bucket(project_id=PROJECT) + assert(uel.bucket_name == '') + assert(uel.report_name_prefix == '') diff --git a/setup.py b/setup.py index cfbf5591c..942c3d5b8 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ import os import setuptools # type: ignore -version = "0.4.1" +version = "0.4.2" package_root = os.path.abspath(os.path.dirname(__file__)) @@ -44,7 +44,10 @@ platforms="Posix; MacOS X; Windows", include_package_data=True, install_requires=( - "google-api-core[grpc] >= 1.28.0, < 2.0.0dev", + # NOTE: Maintainers, please do not require google-api-core>=2.x.x + # Until this issue is closed + # https://github.com/googleapis/google-cloud-python/issues/10566 + "google-api-core[grpc] >= 1.28.0, <3.0.0dev", "proto-plus >= 1.13.0", "packaging >= 14.3", ), diff --git a/synth.metadata b/synth.metadata index 8798701ce..06ab16328 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,7 +4,7 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/python-compute.git", - "sha": "ce057e7b80baca7ef577ee1495ba034cdb3a491a" + "sha": "40afb333a963717853f70410a7a08eda5492418c" } }, { @@ -42,7 +42,6 @@ ".kokoro/continuous/common.cfg", ".kokoro/continuous/continuous.cfg", ".kokoro/docker/docs/Dockerfile", - ".kokoro/docker/docs/fetch_gpg_keys.sh", ".kokoro/docs/common.cfg", ".kokoro/docs/docs-presubmit.cfg", ".kokoro/docs/docs.cfg", @@ -72,6 +71,11 @@ ".kokoro/samples/python3.8/periodic-head.cfg", ".kokoro/samples/python3.8/periodic.cfg", ".kokoro/samples/python3.8/presubmit.cfg", + ".kokoro/samples/python3.9/common.cfg", + ".kokoro/samples/python3.9/continuous.cfg", + ".kokoro/samples/python3.9/periodic-head.cfg", + ".kokoro/samples/python3.9/periodic.cfg", + ".kokoro/samples/python3.9/presubmit.cfg", ".kokoro/test-samples-against-head.sh", ".kokoro/test-samples-impl.sh", ".kokoro/test-samples.sh", @@ -83,6 +87,7 @@ "CONTRIBUTING.rst", "LICENSE", "MANIFEST.in", + "SECURITY.md", "docs/_static/custom.css", "docs/_templates/layout.html", "docs/compute_v1/accelerator_types.rst", @@ -644,6 +649,7 @@ "mypy.ini", "noxfile.py", "renovate.json", + "samples/snippets/noxfile.py", "scripts/decrypt-secrets.sh", "scripts/readme-gen/readme_gen.py", "scripts/readme-gen/templates/README.tmpl.rst",