From 2815f8505072d6a111be1b2df789d46c18679400 Mon Sep 17 00:00:00 2001 From: shabirmean Date: Tue, 8 Feb 2022 21:54:39 -0500 Subject: [PATCH 01/44] improvement: add samples for the using the library --- samples/README.md | 34 ++++++++++++++++ samples/create_cluster.py | 83 +++++++++++++++++++++++++++++++++++++++ samples/delete_cluster.py | 77 ++++++++++++++++++++++++++++++++++++ samples/quickstart.py | 30 ++++++++++++++ 4 files changed, 224 insertions(+) create mode 100644 samples/README.md create mode 100644 samples/create_cluster.py create mode 100644 samples/delete_cluster.py create mode 100644 samples/quickstart.py diff --git a/samples/README.md b/samples/README.md new file mode 100644 index 00000000..a9d70129 --- /dev/null +++ b/samples/README.md @@ -0,0 +1,34 @@ +# Samples + +All the samples are self contained unless they are placed inside their own folders. The samples use [Application Default Credentails (ADC)](https://cloud.google.com/docs/authentication/production#automatically) to authenticate with GCP. So make sure ADC is setup correctly _(i.e. `GOOGLE_APPLICATION_CREDENTIALS` environment variable is set)_ before running the samples. Some sample might require additional python modules to be installed. + +You can run samples as follows: + +```python +python ... +``` + +You can run the following command to find the usage and arguments for the samples: + +```python +python -h +``` +```bash +# example +python quickstart.py -h + +usage: quickstart.py [-h] project_id zone + +positional arguments: + project_id Google Cloud project ID + zone GKE Cluster zone + +optional arguments: + -h, --help show this help message and exit +``` + +- **quickstart.py**: A simple example to list the GKE clusters in a given GCP project and zone. + +- **create_cluster.py**: An example of creating a GKE cluster _(with mostly the defaults)_. This example shows how to handle responses of type [`Operation`](https://cloud.google.com/python/docs/reference/container/latest/google.cloud.container_v1.types.Operation) that reperesents a long running operation. The example uses the python module [`backoff`](https://github.com/litl/backoff) to handle a graceful exponential backoff retry mechanism to check if the `Operation` has completed. + +- **delete_cluster.py**: An example of deleting a GKE cluster. This example shows how to handle responses of type [`Operation`](https://cloud.google.com/python/docs/reference/container/latest/google.cloud.container_v1.types.Operation) that reperesents a long running operation. \ No newline at end of file diff --git a/samples/create_cluster.py b/samples/create_cluster.py new file mode 100644 index 00000000..1709f582 --- /dev/null +++ b/samples/create_cluster.py @@ -0,0 +1,83 @@ +import argparse +import backoff +from google.cloud import container_v1 + + +def on_success(details): + """ + A handler function to pass into the retry backoff algorithm as the function + to be executed upon a successful attempt. + + Read the `Event handlers` section of the backoff python module at: + https://pypi.org/project/backoff/ + """ + print("Successfully created cluster after {elapsed:0.1f} seconds".format(**details)) + + +def on_failure(details): + """ + A handler function to pass into the retry backoff algorithm as the function + to be executed upon a failed attempt. + + Read the `Event handlers` section of the backoff python module at: + https://pypi.org/project/backoff/ + """ + print("Backing off {wait:0.1f} seconds after {tries} tries".format(**details)) + + +@backoff.on_predicate( + # the backoff algorithm to use. we use exponential backoff here + backoff.expo, + # the test function on the return value to determine if a retry is necessary + lambda x: x != container_v1.Operation.Status.DONE, + # maximum number of times to retry before giving up + max_tries=20, + # function to execute upon a failure and when a retry a scheduled + on_backoff=on_failure, + # function to execute upon a successful attempt and no more retries needed + on_success=on_success) +def poll_for_op_status(client: container_v1.ClusterManagerClient, op_id: str): + """ + A simple retry function that fetches the operation and returns it's status. + + The function is annotated with the `backoff` python module to schedule this + function based on a reasonable backoff algorithm + """ + op = client.get_operation({'name': op_id}) + return op.status + + +def create_cluster(client: container_v1.ClusterManagerClient, + project_id: str, location: str, cluster_name: str) -> None: + """Create a new GKE cluster in the given GCP Project and Zone""" + + # Create a fully qualified location identifier of form `projects/{project_id}/location/{zone}'. + cluster_location = client.common_location_path(project_id, location) + cluster_def = { + 'name': cluster_name, + 'initial_node_count': 2, + 'node_config': { + 'machine_type': "e2-standard-2" + }, + } + # Create the request object with the location identifier. + request = {'parent': cluster_location, 'cluster': cluster_def} + # Create the cliuster + create_response = client.create_cluster(request) + op_identifier = f"{cluster_location}/operations/{create_response.name}" + # poll for the operation status and schedule a retry until the cluster is created + poll_for_op_status(client, op_identifier) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, + ) + parser.add_argument("project_id", help="Google Cloud project ID") + parser.add_argument("zone", help="GKE Cluster zone") + parser.add_argument("cluster_name", help="Name to be given to the GKE Cluster") + args = parser.parse_args() + + # Initialize the Cluster management client. + gcp_client = container_v1.ClusterManagerClient() + create_cluster(gcp_client, args.project_id, args.zone, args.cluster_name) diff --git a/samples/delete_cluster.py b/samples/delete_cluster.py new file mode 100644 index 00000000..d69d3112 --- /dev/null +++ b/samples/delete_cluster.py @@ -0,0 +1,77 @@ +import argparse +import backoff +from google.cloud import container_v1 + + +def on_success(details): + """ + A handler function to pass into the retry backoff algorithm as the function + to be executed upon a successful attempt. + + Read the `Event handlers` section of the backoff python module at: + https://pypi.org/project/backoff/ + """ + print("Successfully deleted cluster after {elapsed:0.1f} seconds".format(**details)) + + +def on_failure(details): + """ + A handler function to pass into the retry backoff algorithm as the function + to be executed upon a failed attempt. + + Read the `Event handlers` section of the backoff python module at: + https://pypi.org/project/backoff/ + """ + print("Backing off {wait:0.1f} seconds after {tries} tries".format(**details)) + + +@backoff.on_predicate( + # the backoff algorithm to use. we use exponential backoff here + backoff.expo, + # the test function on the return value to determine if a retry is necessary + lambda x: x != container_v1.Operation.Status.DONE, + # maximum number of times to retry before giving up + max_tries=20, + # function to execute upon a failure and when a retry a scheduled + on_backoff=on_failure, + # function to execute upon a successful attempt and no more retries needed + on_success=on_success) +def poll_for_op_status(client: container_v1.ClusterManagerClient, op_id: str): + """ + A simple retry function that fetches the operation and returns it's status. + + The function is annotated with the `backoff` python module to schedule this + function based on a reasonable backoff algorithm + """ + op = client.get_operation({'name': op_id}) + return op.status + + +def delete_cluster(client: container_v1.ClusterManagerClient, + project_id: str, location: str, cluster_name: str) -> None: + """Delete an existing GKE cluster in the given GCP Project and Zone""" + + # Create a fully qualified location identifier of form `projects/{project_id}/location/{zone}'. + cluster_location = client.common_location_path(project_id, location) + cluster_name = f"{cluster_location}/clusters/{cluster_name}" + # Create the request object with the location identifier. + request = {'name': cluster_name} + # Delete the cliuster + delete_response = client.delete_cluster(request) + op_identifier = f"{cluster_location}/operations/{delete_response.name}" + # poll for the operation status and schedule a retry until the cluster is deleted + poll_for_op_status(client, op_identifier) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, + ) + parser.add_argument("project_id", help="Google Cloud project ID") + parser.add_argument("zone", help="GKE Cluster zone") + parser.add_argument("cluster_name", help="Name to be given to the GKE Cluster") + args = parser.parse_args() + + # Initialize the Cluster management client. + gcp_client = container_v1.ClusterManagerClient() + delete_cluster(gcp_client, args.project_id, args.zone, args.cluster_name) diff --git a/samples/quickstart.py b/samples/quickstart.py new file mode 100644 index 00000000..96e77340 --- /dev/null +++ b/samples/quickstart.py @@ -0,0 +1,30 @@ +import argparse + +from google.cloud import container_v1 + + +def list_clusters(client: container_v1.ClusterManagerClient, project_id: str, location: str) -> None: + """List all the GKE clusters in the given GCP Project and Zone""" + + # Create a fully qualified location identifier of form `projects/{project_id}/location/{zone}'. + clusterLocation = client.common_location_path(project_id, location) + # Create the request object with the location identifier. + request = { 'parent': clusterLocation } + # List all the clusters in the project and location identified by the `clusterLocation` identifier. + listResponse = client.list_clusters(request) + + print(f"There were {len(listResponse.clusters)} clusters in {location} for project {project_id}.") + for cluster in listResponse.clusters: + print(f"- {cluster.name}") + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, + ) + parser.add_argument("project_id", help="Google Cloud project ID") + parser.add_argument("zone", help="GKE Cluster zone") + args = parser.parse_args() + + # Initialize the Cluster management client. + gcp_client = container_v1.ClusterManagerClient() + list_clusters(gcp_client, args.project_id, args.zone) From 6faccea3c468e0e66b5f66571846d6e6e94c5842 Mon Sep 17 00:00:00 2001 From: shabirmean Date: Tue, 8 Feb 2022 21:54:59 -0500 Subject: [PATCH 02/44] cleanup: update the git ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b4243ced..d9f062a8 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,4 @@ system_tests/local_test_setup # Make sure a generated file isn't accidentally committed. pylintrc pylintrc.test +lro-samples \ No newline at end of file From bcf3ecba618be495a48709de06d52a4d6bbf72e2 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Wed, 9 Feb 2022 03:00:49 +0000 Subject: [PATCH 03/44] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index d9f062a8..b4243ced 100644 --- a/.gitignore +++ b/.gitignore @@ -61,4 +61,3 @@ system_tests/local_test_setup # Make sure a generated file isn't accidentally committed. pylintrc pylintrc.test -lro-samples \ No newline at end of file From 1712deedb8de9d400ace603296af6660ce65c093 Mon Sep 17 00:00:00 2001 From: shabirmean Date: Tue, 8 Feb 2022 22:00:50 -0500 Subject: [PATCH 04/44] chore: remove unnecessary comments --- samples/create_cluster.py | 1 - samples/delete_cluster.py | 1 - samples/quickstart.py | 1 - 3 files changed, 3 deletions(-) diff --git a/samples/create_cluster.py b/samples/create_cluster.py index 1709f582..2489f2cc 100644 --- a/samples/create_cluster.py +++ b/samples/create_cluster.py @@ -62,7 +62,6 @@ def create_cluster(client: container_v1.ClusterManagerClient, } # Create the request object with the location identifier. request = {'parent': cluster_location, 'cluster': cluster_def} - # Create the cliuster create_response = client.create_cluster(request) op_identifier = f"{cluster_location}/operations/{create_response.name}" # poll for the operation status and schedule a retry until the cluster is created diff --git a/samples/delete_cluster.py b/samples/delete_cluster.py index d69d3112..81724f43 100644 --- a/samples/delete_cluster.py +++ b/samples/delete_cluster.py @@ -56,7 +56,6 @@ def delete_cluster(client: container_v1.ClusterManagerClient, cluster_name = f"{cluster_location}/clusters/{cluster_name}" # Create the request object with the location identifier. request = {'name': cluster_name} - # Delete the cliuster delete_response = client.delete_cluster(request) op_identifier = f"{cluster_location}/operations/{delete_response.name}" # poll for the operation status and schedule a retry until the cluster is deleted diff --git a/samples/quickstart.py b/samples/quickstart.py index 96e77340..19c865bc 100644 --- a/samples/quickstart.py +++ b/samples/quickstart.py @@ -10,7 +10,6 @@ def list_clusters(client: container_v1.ClusterManagerClient, project_id: str, lo clusterLocation = client.common_location_path(project_id, location) # Create the request object with the location identifier. request = { 'parent': clusterLocation } - # List all the clusters in the project and location identified by the `clusterLocation` identifier. listResponse = client.list_clusters(request) print(f"There were {len(listResponse.clusters)} clusters in {location} for project {project_id}.") From 71e87a21334dec2c3e6a90f639f8f7825f83c05d Mon Sep 17 00:00:00 2001 From: shabirmean Date: Tue, 8 Feb 2022 22:04:54 -0500 Subject: [PATCH 05/44] chore: update git ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b4243ced..d9f062a8 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,4 @@ system_tests/local_test_setup # Make sure a generated file isn't accidentally committed. pylintrc pylintrc.test +lro-samples \ No newline at end of file From dc01c87c8a6b48f8873ceba1fa2c43205fdaa187 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Wed, 9 Feb 2022 03:06:59 +0000 Subject: [PATCH 06/44] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index d9f062a8..b4243ced 100644 --- a/.gitignore +++ b/.gitignore @@ -61,4 +61,3 @@ system_tests/local_test_setup # Make sure a generated file isn't accidentally committed. pylintrc pylintrc.test -lro-samples \ No newline at end of file From f4ebf16f323403ab0e9a1e5be7fe87b1e603ff13 Mon Sep 17 00:00:00 2001 From: Shabir Mohamed Abdul Samadh <7249208+Shabirmean@users.noreply.github.com> Date: Tue, 8 Feb 2022 22:10:10 -0500 Subject: [PATCH 07/44] doc: add simple usage instructions to README --- README.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.rst b/README.rst index 22b2d807..2e06431c 100644 --- a/README.rst +++ b/README.rst @@ -79,6 +79,31 @@ Windows \Scripts\activate \Scripts\pip.exe install google-cloud-container +Once you have the virtual environment setup and activated, you can install the library: + +.. code-block:: console + + pip install google-cloud-container + +Using the client library +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + from google.cloud import container_v1 + + project_id = "GCP_PROJECT_ID" + location = "GCP_ZONE" + + gcp_client = container_v1.ClusterManagerClient() + clusterLocation = client.common_location_path(project_id, location) + + request = { 'parent': clusterLocation } + listResponse = client.list_clusters(request) + + print(f"There were {len(listResponse.clusters)} clusters in {location} for project {project_id}.") + for cluster in listResponse.clusters: + print(f"- {cluster.name}") Next Steps ~~~~~~~~~~ From 4833eb59106a0a150e93b9c10fbdb0bc296c6ac4 Mon Sep 17 00:00:00 2001 From: shabirmean Date: Tue, 8 Feb 2022 22:13:39 -0500 Subject: [PATCH 08/44] cleanup: add copyright headers --- samples/create_cluster.py | 16 ++++++++++++++++ samples/delete_cluster.py | 16 ++++++++++++++++ samples/quickstart.py | 17 ++++++++++++++++- 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/samples/create_cluster.py b/samples/create_cluster.py index 2489f2cc..07d0a412 100644 --- a/samples/create_cluster.py +++ b/samples/create_cluster.py @@ -1,3 +1,19 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + import argparse import backoff from google.cloud import container_v1 diff --git a/samples/delete_cluster.py b/samples/delete_cluster.py index 81724f43..91843942 100644 --- a/samples/delete_cluster.py +++ b/samples/delete_cluster.py @@ -1,3 +1,19 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + import argparse import backoff from google.cloud import container_v1 diff --git a/samples/quickstart.py b/samples/quickstart.py index 19c865bc..f4eb5de6 100644 --- a/samples/quickstart.py +++ b/samples/quickstart.py @@ -1,5 +1,20 @@ -import argparse +# -*- coding: utf-8 -*- +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse from google.cloud import container_v1 From e8e13698d25d51e8a243c529b254f64e21e70742 Mon Sep 17 00:00:00 2001 From: shabirmean Date: Tue, 8 Feb 2022 22:16:47 -0500 Subject: [PATCH 09/44] chore: update git ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b4243ced..d9f062a8 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,4 @@ system_tests/local_test_setup # Make sure a generated file isn't accidentally committed. pylintrc pylintrc.test +lro-samples \ No newline at end of file From bca8a27aa4aa0dccb2f66e1830fb6cc0111252e5 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Wed, 9 Feb 2022 03:18:53 +0000 Subject: [PATCH 10/44] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index d9f062a8..b4243ced 100644 --- a/.gitignore +++ b/.gitignore @@ -61,4 +61,3 @@ system_tests/local_test_setup # Make sure a generated file isn't accidentally committed. pylintrc pylintrc.test -lro-samples \ No newline at end of file From d9a2de0e85dc247210970bab222b45b734dd48aa Mon Sep 17 00:00:00 2001 From: shabirmean Date: Wed, 9 Feb 2022 07:57:01 -0500 Subject: [PATCH 11/44] doc: add more details to retry function doc --- samples/create_cluster.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/samples/create_cluster.py b/samples/create_cluster.py index 07d0a412..c229d1e9 100644 --- a/samples/create_cluster.py +++ b/samples/create_cluster.py @@ -54,10 +54,14 @@ def on_failure(details): on_success=on_success) def poll_for_op_status(client: container_v1.ClusterManagerClient, op_id: str): """ - A simple retry function that fetches the operation and returns it's status. - - The function is annotated with the `backoff` python module to schedule this - function based on a reasonable backoff algorithm + This function calls the Operation API in GCP with the given operation id. It + serves as a simple retry function that fetches the operation and returns + it's status. + + We use the 'backoff' python module to provide us the implementation of the + backoff & retry strategy. The function is annotated with the `backoff` + python module to schedule this function based on a reasonable backoff + algorithm. """ op = client.get_operation({'name': op_id}) return op.status From cbade85aab1d4205509689431f974397082ff60c Mon Sep 17 00:00:00 2001 From: shabirmean Date: Wed, 9 Feb 2022 07:58:02 -0500 Subject: [PATCH 12/44] chore: update git ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b4243ced..51671450 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,4 @@ system_tests/local_test_setup # Make sure a generated file isn't accidentally committed. pylintrc pylintrc.test +lro-samples From 1abec54df7c0cf6a5ac99acf5e9dfe25dee90440 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Wed, 9 Feb 2022 13:00:08 +0000 Subject: [PATCH 13/44] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 51671450..b4243ced 100644 --- a/.gitignore +++ b/.gitignore @@ -61,4 +61,3 @@ system_tests/local_test_setup # Make sure a generated file isn't accidentally committed. pylintrc pylintrc.test -lro-samples From c4ef71dc52d7c5f62442759b0d44e08ba80b66f6 Mon Sep 17 00:00:00 2001 From: shabirmean Date: Wed, 9 Feb 2022 10:16:59 -0500 Subject: [PATCH 14/44] doc: add seperate sections about the samples --- samples/README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/samples/README.md b/samples/README.md index a9d70129..61f2f62c 100644 --- a/samples/README.md +++ b/samples/README.md @@ -27,7 +27,18 @@ optional arguments: -h, --help show this help message and exit ``` -- **quickstart.py**: A simple example to list the GKE clusters in a given GCP project and zone. +### Quickstart sample +- **quickstart.py**: A simple example to list the GKE clusters in a given GCP project and zone. The sample uses the [`list_clusters()`](https://cloud.google.com/python/docs/reference/container/latest/google.cloud.container_v1.services.cluster_manager.ClusterManagerClient#google_cloud_container_v1_services_cluster_manager_ClusterManagerClient_list_clusters) API to fetch the list of cluster. + + +### Long running operation sample + +The following samples are examples of operations that take a while to complete. +For example _creating a cluster_ in GKE can take a while to set up the cluster +nodes, networking and configuring Kubernetes. Thus, calls to such long running +APIs return an object of type [`Operation`](https://cloud.google.com/python/docs/reference/container/latest/google.cloud.container_v1.types.Operation). We can +then use the id of the returned operation to **poll** the [`get_operation()`](https://cloud.google.com/python/docs/reference/container/latest/google.cloud.container_v1.services.cluster_manager.ClusterManagerClient#google_cloud_container_v1_services_cluster_manager_ClusterManagerClient_get_operation) API to check for it's status. You can see the +different statuses it can be in, in [this proto definition](https://github.com/googleapis/googleapis/blob/master/google/container/v1/cluster_service.proto#L1763-L1778). - **create_cluster.py**: An example of creating a GKE cluster _(with mostly the defaults)_. This example shows how to handle responses of type [`Operation`](https://cloud.google.com/python/docs/reference/container/latest/google.cloud.container_v1.types.Operation) that reperesents a long running operation. The example uses the python module [`backoff`](https://github.com/litl/backoff) to handle a graceful exponential backoff retry mechanism to check if the `Operation` has completed. From 4f049ec832a46767592c4bd129803ca840197e90 Mon Sep 17 00:00:00 2001 From: shabirmean Date: Wed, 9 Feb 2022 10:17:56 -0500 Subject: [PATCH 15/44] chore: update git ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b4243ced..51671450 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,4 @@ system_tests/local_test_setup # Make sure a generated file isn't accidentally committed. pylintrc pylintrc.test +lro-samples From a905b5f591816e5a0fbda69bf4eb8408329d0dd2 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Wed, 9 Feb 2022 15:20:02 +0000 Subject: [PATCH 16/44] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 51671450..b4243ced 100644 --- a/.gitignore +++ b/.gitignore @@ -61,4 +61,3 @@ system_tests/local_test_setup # Make sure a generated file isn't accidentally committed. pylintrc pylintrc.test -lro-samples From 1bd9077286c72d17381d1c559f21cccc9d48297a Mon Sep 17 00:00:00 2001 From: shabirmean Date: Wed, 9 Feb 2022 16:25:41 -0500 Subject: [PATCH 17/44] cleanup: add region tags --- samples/create_cluster.py | 15 +++++++-------- samples/delete_cluster.py | 3 ++- samples/quickstart.py | 3 ++- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/samples/create_cluster.py b/samples/create_cluster.py index c229d1e9..d7317262 100644 --- a/samples/create_cluster.py +++ b/samples/create_cluster.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. - +# [START gke_create_cluster] import argparse import backoff from google.cloud import container_v1 @@ -57,7 +57,7 @@ def poll_for_op_status(client: container_v1.ClusterManagerClient, op_id: str): This function calls the Operation API in GCP with the given operation id. It serves as a simple retry function that fetches the operation and returns it's status. - + We use the 'backoff' python module to provide us the implementation of the backoff & retry strategy. The function is annotated with the `backoff` python module to schedule this function based on a reasonable backoff @@ -67,10 +67,10 @@ def poll_for_op_status(client: container_v1.ClusterManagerClient, op_id: str): return op.status -def create_cluster(client: container_v1.ClusterManagerClient, - project_id: str, location: str, cluster_name: str) -> None: +def create_cluster(project_id: str, location: str, cluster_name: str) -> None: """Create a new GKE cluster in the given GCP Project and Zone""" - + # Initialize the Cluster management client. + client = container_v1.ClusterManagerClient() # Create a fully qualified location identifier of form `projects/{project_id}/location/{zone}'. cluster_location = client.common_location_path(project_id, location) cluster_def = { @@ -97,6 +97,5 @@ def create_cluster(client: container_v1.ClusterManagerClient, parser.add_argument("cluster_name", help="Name to be given to the GKE Cluster") args = parser.parse_args() - # Initialize the Cluster management client. - gcp_client = container_v1.ClusterManagerClient() - create_cluster(gcp_client, args.project_id, args.zone, args.cluster_name) + create_cluster(args.project_id, args.zone, args.cluster_name) +# [END gke_create_cluster] diff --git a/samples/delete_cluster.py b/samples/delete_cluster.py index 91843942..5ab6c682 100644 --- a/samples/delete_cluster.py +++ b/samples/delete_cluster.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. - +# [START gke_delete_cluster] import argparse import backoff from google.cloud import container_v1 @@ -90,3 +90,4 @@ def delete_cluster(client: container_v1.ClusterManagerClient, # Initialize the Cluster management client. gcp_client = container_v1.ClusterManagerClient() delete_cluster(gcp_client, args.project_id, args.zone, args.cluster_name) +# [END gke_delete_cluster] diff --git a/samples/quickstart.py b/samples/quickstart.py index f4eb5de6..99fe30b1 100644 --- a/samples/quickstart.py +++ b/samples/quickstart.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. - +# [START gke_list_cluster] import argparse from google.cloud import container_v1 @@ -42,3 +42,4 @@ def list_clusters(client: container_v1.ClusterManagerClient, project_id: str, lo # Initialize the Cluster management client. gcp_client = container_v1.ClusterManagerClient() list_clusters(gcp_client, args.project_id, args.zone) +# [END gke_list_cluster] \ No newline at end of file From 8ef2220689a193c11713fd4c059a4ccf94a16330 Mon Sep 17 00:00:00 2001 From: shabirmean Date: Wed, 9 Feb 2022 16:27:43 -0500 Subject: [PATCH 18/44] doc: update the readme to remove inline code --- README.rst | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/README.rst b/README.rst index 2e06431c..566e7dea 100644 --- a/README.rst +++ b/README.rst @@ -88,22 +88,7 @@ Once you have the virtual environment setup and activated, you can install the l Using the client library ~~~~~~~~~~~~~~~~~~~~~~~~ -.. code-block:: python - - from google.cloud import container_v1 - - project_id = "GCP_PROJECT_ID" - location = "GCP_ZONE" - - gcp_client = container_v1.ClusterManagerClient() - clusterLocation = client.common_location_path(project_id, location) - - request = { 'parent': clusterLocation } - listResponse = client.list_clusters(request) - - print(f"There were {len(listResponse.clusters)} clusters in {location} for project {project_id}.") - for cluster in listResponse.clusters: - print(f"- {cluster.name}") +See the examples in the [samples](/samples) directory. You can start with [quickstart.py](/samples/quickstart.py) Next Steps ~~~~~~~~~~ From 47101eaa0ce043848e1380d240735bad789c8496 Mon Sep 17 00:00:00 2001 From: shabirmean Date: Wed, 9 Feb 2022 16:28:25 -0500 Subject: [PATCH 19/44] chore: update git ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b4243ced..51671450 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,4 @@ system_tests/local_test_setup # Make sure a generated file isn't accidentally committed. pylintrc pylintrc.test +lro-samples From 43c08b2c1fa0adcacd98f127c52330bbbca67405 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Wed, 9 Feb 2022 21:30:37 +0000 Subject: [PATCH 20/44] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 51671450..b4243ced 100644 --- a/.gitignore +++ b/.gitignore @@ -61,4 +61,3 @@ system_tests/local_test_setup # Make sure a generated file isn't accidentally committed. pylintrc pylintrc.test -lro-samples From e2ed73964ba92eb932a8a45f5c3b2383735e4094 Mon Sep 17 00:00:00 2001 From: shabirmean Date: Wed, 9 Feb 2022 16:32:47 -0500 Subject: [PATCH 21/44] doc: update readme --- README.rst | 5 ++++- samples/README.md | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 566e7dea..bd75642d 100644 --- a/README.rst +++ b/README.rst @@ -88,7 +88,10 @@ Once you have the virtual environment setup and activated, you can install the l Using the client library ~~~~~~~~~~~~~~~~~~~~~~~~ -See the examples in the [samples](/samples) directory. You can start with [quickstart.py](/samples/quickstart.py) +See the examples in the `samples`_ directory. You can start with `quickstart.py`_. + +.. _samples: /samples +.. _quickstart.py: /samples/quickstart.py Next Steps ~~~~~~~~~~ diff --git a/samples/README.md b/samples/README.md index 61f2f62c..a1d0691f 100644 --- a/samples/README.md +++ b/samples/README.md @@ -28,7 +28,7 @@ optional arguments: ``` ### Quickstart sample -- **quickstart.py**: A simple example to list the GKE clusters in a given GCP project and zone. The sample uses the [`list_clusters()`](https://cloud.google.com/python/docs/reference/container/latest/google.cloud.container_v1.services.cluster_manager.ClusterManagerClient#google_cloud_container_v1_services_cluster_manager_ClusterManagerClient_list_clusters) API to fetch the list of cluster. +- [**quickstart.py**](quickstart.py): A simple example to list the GKE clusters in a given GCP project and zone. The sample uses the [`list_clusters()`](https://cloud.google.com/python/docs/reference/container/latest/google.cloud.container_v1.services.cluster_manager.ClusterManagerClient#google_cloud_container_v1_services_cluster_manager_ClusterManagerClient_list_clusters) API to fetch the list of cluster. ### Long running operation sample @@ -40,6 +40,6 @@ APIs return an object of type [`Operation`](https://cloud.google.com/python/docs then use the id of the returned operation to **poll** the [`get_operation()`](https://cloud.google.com/python/docs/reference/container/latest/google.cloud.container_v1.services.cluster_manager.ClusterManagerClient#google_cloud_container_v1_services_cluster_manager_ClusterManagerClient_get_operation) API to check for it's status. You can see the different statuses it can be in, in [this proto definition](https://github.com/googleapis/googleapis/blob/master/google/container/v1/cluster_service.proto#L1763-L1778). -- **create_cluster.py**: An example of creating a GKE cluster _(with mostly the defaults)_. This example shows how to handle responses of type [`Operation`](https://cloud.google.com/python/docs/reference/container/latest/google.cloud.container_v1.types.Operation) that reperesents a long running operation. The example uses the python module [`backoff`](https://github.com/litl/backoff) to handle a graceful exponential backoff retry mechanism to check if the `Operation` has completed. +- [**create_cluster.py**](create_cluster.py): An example of creating a GKE cluster _(with mostly the defaults)_. This example shows how to handle responses of type [`Operation`](https://cloud.google.com/python/docs/reference/container/latest/google.cloud.container_v1.types.Operation) that reperesents a long running operation. The example uses the python module [`backoff`](https://github.com/litl/backoff) to handle a graceful exponential backoff retry mechanism to check if the `Operation` has completed. -- **delete_cluster.py**: An example of deleting a GKE cluster. This example shows how to handle responses of type [`Operation`](https://cloud.google.com/python/docs/reference/container/latest/google.cloud.container_v1.types.Operation) that reperesents a long running operation. \ No newline at end of file +- [**delete_cluster.py**](delete_cluster.py): An example of deleting a GKE cluster. This example shows how to handle responses of type [`Operation`](https://cloud.google.com/python/docs/reference/container/latest/google.cloud.container_v1.types.Operation) that reperesents a long running operation. \ No newline at end of file From ff1591d23beafef5fe19ca23bdbdcb3729933cfe Mon Sep 17 00:00:00 2001 From: shabirmean Date: Wed, 9 Feb 2022 16:33:53 -0500 Subject: [PATCH 22/44] chore: update git ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b4243ced..51671450 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,4 @@ system_tests/local_test_setup # Make sure a generated file isn't accidentally committed. pylintrc pylintrc.test +lro-samples From d69c8b66b4b51e046867249b56ea9e56fdb2b1f2 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Wed, 9 Feb 2022 21:36:08 +0000 Subject: [PATCH 23/44] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 51671450..b4243ced 100644 --- a/.gitignore +++ b/.gitignore @@ -61,4 +61,3 @@ system_tests/local_test_setup # Make sure a generated file isn't accidentally committed. pylintrc pylintrc.test -lro-samples From c064e398c3cfa080d2831754bfa48ae3cd70fee6 Mon Sep 17 00:00:00 2001 From: shabirmean Date: Wed, 9 Feb 2022 18:47:41 -0500 Subject: [PATCH 24/44] process: add nox and fix lint errors --- README.rst | 2 +- samples/{ => snippets}/README.md | 0 samples/{ => snippets}/create_cluster.py | 16 +- samples/{ => snippets}/delete_cluster.py | 23 +- samples/snippets/noxfile.py | 270 +++++++++++++++++++++++ samples/snippets/noxfile_config.py | 42 ++++ samples/{ => snippets}/quickstart.py | 20 +- 7 files changed, 354 insertions(+), 19 deletions(-) rename samples/{ => snippets}/README.md (100%) rename samples/{ => snippets}/create_cluster.py (94%) rename samples/{ => snippets}/delete_cluster.py (89%) create mode 100644 samples/snippets/noxfile.py create mode 100644 samples/snippets/noxfile_config.py rename samples/{ => snippets}/quickstart.py (83%) diff --git a/README.rst b/README.rst index bd75642d..ca0b0911 100644 --- a/README.rst +++ b/README.rst @@ -91,7 +91,7 @@ Using the client library See the examples in the `samples`_ directory. You can start with `quickstart.py`_. .. _samples: /samples -.. _quickstart.py: /samples/quickstart.py +.. _quickstart.py: /samples/snippets/quickstart.py Next Steps ~~~~~~~~~~ diff --git a/samples/README.md b/samples/snippets/README.md similarity index 100% rename from samples/README.md rename to samples/snippets/README.md diff --git a/samples/create_cluster.py b/samples/snippets/create_cluster.py similarity index 94% rename from samples/create_cluster.py rename to samples/snippets/create_cluster.py index d7317262..f453dbf2 100644 --- a/samples/create_cluster.py +++ b/samples/snippets/create_cluster.py @@ -15,11 +15,14 @@ # [START gke_create_cluster] import argparse +import sys +from typing import Dict + import backoff from google.cloud import container_v1 -def on_success(details): +def on_success(details: Dict[str, str]) -> None: """ A handler function to pass into the retry backoff algorithm as the function to be executed upon a successful attempt. @@ -30,7 +33,7 @@ def on_success(details): print("Successfully created cluster after {elapsed:0.1f} seconds".format(**details)) -def on_failure(details): +def on_failure(details: Dict[str, str]) -> None: """ A handler function to pass into the retry backoff algorithm as the function to be executed upon a failed attempt. @@ -52,17 +55,18 @@ def on_failure(details): on_backoff=on_failure, # function to execute upon a successful attempt and no more retries needed on_success=on_success) -def poll_for_op_status(client: container_v1.ClusterManagerClient, op_id: str): +def poll_for_op_status(client: container_v1.ClusterManagerClient, op_id: str) -> bool: """ This function calls the Operation API in GCP with the given operation id. It serves as a simple retry function that fetches the operation and returns it's status. We use the 'backoff' python module to provide us the implementation of the - backoff & retry strategy. The function is annotated with the `backoff` + backoff & retry strategy. The function is annotated with the `backoff` python module to schedule this function based on a reasonable backoff algorithm. """ + op = client.get_operation({'name': op_id}) return op.status @@ -97,5 +101,9 @@ def create_cluster(project_id: str, location: str, cluster_name: str) -> None: parser.add_argument("cluster_name", help="Name to be given to the GKE Cluster") args = parser.parse_args() + if len(sys.argv) != 4: + parser.print_usage() + sys.exit(1) + create_cluster(args.project_id, args.zone, args.cluster_name) # [END gke_create_cluster] diff --git a/samples/delete_cluster.py b/samples/snippets/delete_cluster.py similarity index 89% rename from samples/delete_cluster.py rename to samples/snippets/delete_cluster.py index 5ab6c682..60abd1cf 100644 --- a/samples/delete_cluster.py +++ b/samples/snippets/delete_cluster.py @@ -15,11 +15,14 @@ # [START gke_delete_cluster] import argparse +import sys +from typing import Dict + import backoff from google.cloud import container_v1 -def on_success(details): +def on_success(details: Dict[str, str]) -> None: """ A handler function to pass into the retry backoff algorithm as the function to be executed upon a successful attempt. @@ -30,7 +33,7 @@ def on_success(details): print("Successfully deleted cluster after {elapsed:0.1f} seconds".format(**details)) -def on_failure(details): +def on_failure(details: Dict[str, str]) -> None: """ A handler function to pass into the retry backoff algorithm as the function to be executed upon a failed attempt. @@ -52,21 +55,23 @@ def on_failure(details): on_backoff=on_failure, # function to execute upon a successful attempt and no more retries needed on_success=on_success) -def poll_for_op_status(client: container_v1.ClusterManagerClient, op_id: str): +def poll_for_op_status(client: container_v1.ClusterManagerClient, op_id: str) -> bool: """ A simple retry function that fetches the operation and returns it's status. The function is annotated with the `backoff` python module to schedule this function based on a reasonable backoff algorithm """ + op = client.get_operation({'name': op_id}) return op.status -def delete_cluster(client: container_v1.ClusterManagerClient, - project_id: str, location: str, cluster_name: str) -> None: +def delete_cluster(project_id: str, location: str, cluster_name: str) -> None: """Delete an existing GKE cluster in the given GCP Project and Zone""" + # Initialize the Cluster management client. + client = container_v1.ClusterManagerClient() # Create a fully qualified location identifier of form `projects/{project_id}/location/{zone}'. cluster_location = client.common_location_path(project_id, location) cluster_name = f"{cluster_location}/clusters/{cluster_name}" @@ -87,7 +92,9 @@ def delete_cluster(client: container_v1.ClusterManagerClient, parser.add_argument("cluster_name", help="Name to be given to the GKE Cluster") args = parser.parse_args() - # Initialize the Cluster management client. - gcp_client = container_v1.ClusterManagerClient() - delete_cluster(gcp_client, args.project_id, args.zone, args.cluster_name) + if len(sys.argv) != 4: + parser.print_usage() + sys.exit(1) + + delete_cluster(args.project_id, args.zone, args.cluster_name) # [END gke_delete_cluster] diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py new file mode 100644 index 00000000..4dc5fa1a --- /dev/null +++ b/samples/snippets/noxfile.py @@ -0,0 +1,270 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import print_function + +import glob +import os +from pathlib import Path +import sys +from typing import Callable, Dict, List, Optional + +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"], + # Old samples are opted out of enforcing Python type hints + # All new samples should feature them + "enforce_type_hints": False, + # 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": "GOOGLE_CLOUD_PROJECT", + # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT', + # If you need to use a specific version of pip, + # change pip_version_override to the string representation + # of the version number, for example, "20.2.4" + "pip_version_override": None, + # 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() -> Dict[str, str]: + """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] # deprecated + + # 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", "3.9", "3.10"] + +# 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)) + +# Error if a python version is missing +nox.options.error_on_missing_interpreters = True + +# +# Style Checks +# + + +def _determine_local_import_names(start_dir: str) -> List[str]: + """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: nox.sessions.Session) -> None: + if not TEST_CONFIG["enforce_type_hints"]: + session.install("flake8", "flake8-import-order") + else: + session.install("flake8", "flake8-import-order", "flake8-annotations") + + local_names = _determine_local_import_names(".") + args = FLAKE8_COMMON_ARGS + [ + "--application-import-names", + ",".join(local_names), + ".", + ] + session.run("flake8", *args) + + +# +# Black +# + + +@nox.session +def blacken(session: nox.sessions.Session) -> None: + session.install("black") + python_files = [path for path in os.listdir(".") if path.endswith(".py")] + + session.run("black", *python_files) + + +# +# Sample Tests +# + + +PYTEST_COMMON_ARGS = ["--junitxml=sponge_log.xml"] + + +def _session_tests( + session: nox.sessions.Session, post_install: Callable = None +) -> None: + # check for presence of tests + test_list = glob.glob("*_test.py") + glob.glob("test_*.py") + if len(test_list) == 0: + print("No tests found, skipping directory.") + else: + if TEST_CONFIG["pip_version_override"]: + pip_version = TEST_CONFIG["pip_version_override"] + session.install(f"pip=={pip_version}") + """Runs py.test for a particular project.""" + if os.path.exists("requirements.txt"): + if os.path.exists("constraints.txt"): + session.install("-r", "requirements.txt", "-c", "constraints.txt") + else: + session.install("-r", "requirements.txt") + + if os.path.exists("requirements-test.txt"): + if os.path.exists("constraints-test.txt"): + session.install( + "-r", "requirements-test.txt", "-c", "constraints-test.txt" + ) + else: + 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: nox.sessions.Session) -> None: + """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() -> Optional[str]: + """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: nox.sessions.Session, path: str) -> None: + """(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/noxfile_config.py b/samples/snippets/noxfile_config.py new file mode 100644 index 00000000..16f326e5 --- /dev/null +++ b/samples/snippets/noxfile_config.py @@ -0,0 +1,42 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Default TEST_CONFIG_OVERRIDE for python repos. + +# You can copy this file into your directory, then it will be imported from +# the noxfile.py. + +# The source of truth: +# https://github.com/GoogleCloudPlatform/python-docs-samples/blob/main/noxfile_config.py + +TEST_CONFIG_OVERRIDE = { + # You can opt out from the test for specific Python versions. + "ignored_versions": ["2.7"], + # Old samples are opted out of enforcing Python type hints + # All new samples should feature them + "enforce_type_hints": True, + # 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": "GOOGLE_CLOUD_PROJECT", + # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT', + # If you need to use a specific version of pip, + # change pip_version_override to the string representation + # of the version number, for example, "20.2.4" + "pip_version_override": None, + # A dictionary you want to inject into your test. Don't put any + # secrets here. These values will override predefined values. + "envs": {}, +} diff --git a/samples/quickstart.py b/samples/snippets/quickstart.py similarity index 83% rename from samples/quickstart.py rename to samples/snippets/quickstart.py index 99fe30b1..d5a79040 100644 --- a/samples/quickstart.py +++ b/samples/snippets/quickstart.py @@ -15,23 +15,29 @@ # [START gke_list_cluster] import argparse +import sys + from google.cloud import container_v1 -def list_clusters(client: container_v1.ClusterManagerClient, project_id: str, location: str) -> None: +def list_clusters(project_id: str, location: str) -> None: """List all the GKE clusters in the given GCP Project and Zone""" + # Initialize the Cluster management client. + client = container_v1.ClusterManagerClient() # Create a fully qualified location identifier of form `projects/{project_id}/location/{zone}'. clusterLocation = client.common_location_path(project_id, location) # Create the request object with the location identifier. - request = { 'parent': clusterLocation } + request = {'parent': clusterLocation} listResponse = client.list_clusters(request) print(f"There were {len(listResponse.clusters)} clusters in {location} for project {project_id}.") for cluster in listResponse.clusters: print(f"- {cluster.name}") + if __name__ == "__main__": + parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, ) @@ -39,7 +45,9 @@ def list_clusters(client: container_v1.ClusterManagerClient, project_id: str, lo parser.add_argument("zone", help="GKE Cluster zone") args = parser.parse_args() - # Initialize the Cluster management client. - gcp_client = container_v1.ClusterManagerClient() - list_clusters(gcp_client, args.project_id, args.zone) -# [END gke_list_cluster] \ No newline at end of file + if len(sys.argv) != 4: + parser.print_usage() + sys.exit(1) + + list_clusters(args.project_id, args.zone) +# [END gke_list_cluster] From 4916fab69235bc3d4041860ea8279929b93a3aaa Mon Sep 17 00:00:00 2001 From: shabirmean Date: Wed, 9 Feb 2022 18:48:06 -0500 Subject: [PATCH 25/44] chore: update git ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b4243ced..51671450 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,4 @@ system_tests/local_test_setup # Make sure a generated file isn't accidentally committed. pylintrc pylintrc.test +lro-samples From 44020a903cec6abc4dbcb3405225e2558f19c1d2 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Wed, 9 Feb 2022 23:50:17 +0000 Subject: [PATCH 26/44] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- .gitignore | 1 - samples/snippets/create_cluster.py | 18 +++++++++--------- samples/snippets/delete_cluster.py | 10 ++++++---- samples/snippets/quickstart.py | 9 ++++++--- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 51671450..b4243ced 100644 --- a/.gitignore +++ b/.gitignore @@ -61,4 +61,3 @@ system_tests/local_test_setup # Make sure a generated file isn't accidentally committed. pylintrc pylintrc.test -lro-samples diff --git a/samples/snippets/create_cluster.py b/samples/snippets/create_cluster.py index f453dbf2..ff4dee6e 100644 --- a/samples/snippets/create_cluster.py +++ b/samples/snippets/create_cluster.py @@ -54,7 +54,8 @@ def on_failure(details: Dict[str, str]) -> None: # function to execute upon a failure and when a retry a scheduled on_backoff=on_failure, # function to execute upon a successful attempt and no more retries needed - on_success=on_success) + on_success=on_success, +) def poll_for_op_status(client: container_v1.ClusterManagerClient, op_id: str) -> bool: """ This function calls the Operation API in GCP with the given operation id. It @@ -67,7 +68,7 @@ def poll_for_op_status(client: container_v1.ClusterManagerClient, op_id: str) -> algorithm. """ - op = client.get_operation({'name': op_id}) + op = client.get_operation({"name": op_id}) return op.status @@ -78,14 +79,12 @@ def create_cluster(project_id: str, location: str, cluster_name: str) -> None: # Create a fully qualified location identifier of form `projects/{project_id}/location/{zone}'. cluster_location = client.common_location_path(project_id, location) cluster_def = { - 'name': cluster_name, - 'initial_node_count': 2, - 'node_config': { - 'machine_type': "e2-standard-2" - }, + "name": cluster_name, + "initial_node_count": 2, + "node_config": {"machine_type": "e2-standard-2"}, } # Create the request object with the location identifier. - request = {'parent': cluster_location, 'cluster': cluster_def} + request = {"parent": cluster_location, "cluster": cluster_def} create_response = client.create_cluster(request) op_identifier = f"{cluster_location}/operations/{create_response.name}" # poll for the operation status and schedule a retry until the cluster is created @@ -94,7 +93,8 @@ def create_cluster(project_id: str, location: str, cluster_name: str) -> None: if __name__ == "__main__": parser = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument("project_id", help="Google Cloud project ID") parser.add_argument("zone", help="GKE Cluster zone") diff --git a/samples/snippets/delete_cluster.py b/samples/snippets/delete_cluster.py index 60abd1cf..f8482a6f 100644 --- a/samples/snippets/delete_cluster.py +++ b/samples/snippets/delete_cluster.py @@ -54,7 +54,8 @@ def on_failure(details: Dict[str, str]) -> None: # function to execute upon a failure and when a retry a scheduled on_backoff=on_failure, # function to execute upon a successful attempt and no more retries needed - on_success=on_success) + on_success=on_success, +) def poll_for_op_status(client: container_v1.ClusterManagerClient, op_id: str) -> bool: """ A simple retry function that fetches the operation and returns it's status. @@ -63,7 +64,7 @@ def poll_for_op_status(client: container_v1.ClusterManagerClient, op_id: str) -> function based on a reasonable backoff algorithm """ - op = client.get_operation({'name': op_id}) + op = client.get_operation({"name": op_id}) return op.status @@ -76,7 +77,7 @@ def delete_cluster(project_id: str, location: str, cluster_name: str) -> None: cluster_location = client.common_location_path(project_id, location) cluster_name = f"{cluster_location}/clusters/{cluster_name}" # Create the request object with the location identifier. - request = {'name': cluster_name} + request = {"name": cluster_name} delete_response = client.delete_cluster(request) op_identifier = f"{cluster_location}/operations/{delete_response.name}" # poll for the operation status and schedule a retry until the cluster is deleted @@ -85,7 +86,8 @@ def delete_cluster(project_id: str, location: str, cluster_name: str) -> None: if __name__ == "__main__": parser = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument("project_id", help="Google Cloud project ID") parser.add_argument("zone", help="GKE Cluster zone") diff --git a/samples/snippets/quickstart.py b/samples/snippets/quickstart.py index d5a79040..8781f00c 100644 --- a/samples/snippets/quickstart.py +++ b/samples/snippets/quickstart.py @@ -28,10 +28,12 @@ def list_clusters(project_id: str, location: str) -> None: # Create a fully qualified location identifier of form `projects/{project_id}/location/{zone}'. clusterLocation = client.common_location_path(project_id, location) # Create the request object with the location identifier. - request = {'parent': clusterLocation} + request = {"parent": clusterLocation} listResponse = client.list_clusters(request) - print(f"There were {len(listResponse.clusters)} clusters in {location} for project {project_id}.") + print( + f"There were {len(listResponse.clusters)} clusters in {location} for project {project_id}." + ) for cluster in listResponse.clusters: print(f"- {cluster.name}") @@ -39,7 +41,8 @@ def list_clusters(project_id: str, location: str) -> None: if __name__ == "__main__": parser = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument("project_id", help="Google Cloud project ID") parser.add_argument("zone", help="GKE Cluster zone") From f8b5c8d9d432e857cc7769cc6705ad38ce0f8a1f Mon Sep 17 00:00:00 2001 From: shabirmean Date: Wed, 9 Feb 2022 19:10:02 -0500 Subject: [PATCH 27/44] process: add requirements.txt --- samples/snippets/requirement-test.txt | 3 +++ samples/snippets/requirements.txt | 2 ++ 2 files changed, 5 insertions(+) create mode 100644 samples/snippets/requirement-test.txt create mode 100644 samples/snippets/requirements.txt diff --git a/samples/snippets/requirement-test.txt b/samples/snippets/requirement-test.txt new file mode 100644 index 00000000..127f7a7b --- /dev/null +++ b/samples/snippets/requirement-test.txt @@ -0,0 +1,3 @@ + +backoff==1.11.1 +pytest==6.2.5 \ No newline at end of file diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt new file mode 100644 index 00000000..50328fc3 --- /dev/null +++ b/samples/snippets/requirements.txt @@ -0,0 +1,2 @@ +google-cloud-container==2.10.1 +backoff==1.11.1 From 746577a74e132bc917255895f4b567f5495efcfa Mon Sep 17 00:00:00 2001 From: shabirmean Date: Wed, 9 Feb 2022 19:11:53 -0500 Subject: [PATCH 28/44] chore: add lro samples to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b4243ced..51671450 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,4 @@ system_tests/local_test_setup # Make sure a generated file isn't accidentally committed. pylintrc pylintrc.test +lro-samples From 8894f647dfaf3cf0203a7a3964526285cdaa6d36 Mon Sep 17 00:00:00 2001 From: shabirmean Date: Wed, 9 Feb 2022 19:52:23 -0500 Subject: [PATCH 29/44] test: add test for quickstart --- samples/snippets/quickstart.py | 2 +- samples/snippets/quickstart_test.py | 40 +++++++++++++++++++++++++++ samples/snippets/requirement-test.txt | 2 +- samples/snippets/requirements.txt | 1 + 4 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 samples/snippets/quickstart_test.py diff --git a/samples/snippets/quickstart.py b/samples/snippets/quickstart.py index 8781f00c..7c5c1c53 100644 --- a/samples/snippets/quickstart.py +++ b/samples/snippets/quickstart.py @@ -48,7 +48,7 @@ def list_clusters(project_id: str, location: str) -> None: parser.add_argument("zone", help="GKE Cluster zone") args = parser.parse_args() - if len(sys.argv) != 4: + if len(sys.argv) != 3: parser.print_usage() sys.exit(1) diff --git a/samples/snippets/quickstart_test.py b/samples/snippets/quickstart_test.py new file mode 100644 index 00000000..a6049fd4 --- /dev/null +++ b/samples/snippets/quickstart_test.py @@ -0,0 +1,40 @@ +import os + +import quickstart + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] +ZONE = "us-central1-b" + + +def test_list_models(capsys: object) -> None: + outputPrefix = "There were " + outputSuffix = f" clusters in {ZONE} for project {PROJECT_ID}." + + quickstart.list_clusters(PROJECT_ID, ZONE) + out, _ = capsys.readouterr() + + ''' + Typical output looks as follows: + + There were 3 clusters in us-central1-b for project test-project. + - cluster1 + - cluster2 + - cluster3 + + Split array by '\n' + [ + "There were 3 clusters in us-central1-b for project test-project.", + "- cluster1", + "- cluster2", + "- cluster3", + "", + ] + ''' + outLines = out.split("\n") + firstLine = outLines[0] + firstLine = firstLine.replace(outputPrefix, "") + firstLine = firstLine.replace(outputSuffix, "") + clusterCount = int(firstLine) # get the cluster count in the first line + + assert outputSuffix in out + assert clusterCount == len(outLines) - 2 diff --git a/samples/snippets/requirement-test.txt b/samples/snippets/requirement-test.txt index 127f7a7b..1698fe7a 100644 --- a/samples/snippets/requirement-test.txt +++ b/samples/snippets/requirement-test.txt @@ -1,3 +1,3 @@ backoff==1.11.1 -pytest==6.2.5 \ No newline at end of file +pytest==7.0.0 \ No newline at end of file diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 50328fc3..748ddb99 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,2 +1,3 @@ google-cloud-container==2.10.1 backoff==1.11.1 +pytest==7.0.0 \ No newline at end of file From 57b424b24d463b25a846f651a403491981a2ba21 Mon Sep 17 00:00:00 2001 From: shabirmean Date: Wed, 9 Feb 2022 20:34:53 -0500 Subject: [PATCH 30/44] test: add test for create cluster --- samples/snippets/create_cluster.py | 3 +- samples/snippets/create_cluster_test.py | 50 +++++++++++++++++++++++++ samples/snippets/delete_cluster.py | 3 +- samples/snippets/quickstart.py | 10 ++--- samples/snippets/quickstart_test.py | 26 ++++++------- 5 files changed, 72 insertions(+), 20 deletions(-) create mode 100644 samples/snippets/create_cluster_test.py diff --git a/samples/snippets/create_cluster.py b/samples/snippets/create_cluster.py index ff4dee6e..b676cc20 100644 --- a/samples/snippets/create_cluster.py +++ b/samples/snippets/create_cluster.py @@ -56,7 +56,8 @@ def on_failure(details: Dict[str, str]) -> None: # function to execute upon a successful attempt and no more retries needed on_success=on_success, ) -def poll_for_op_status(client: container_v1.ClusterManagerClient, op_id: str) -> bool: +def poll_for_op_status( + client: container_v1.ClusterManagerClient, op_id: str) -> container_v1.Operation.Status: """ This function calls the Operation API in GCP with the given operation id. It serves as a simple retry function that fetches the operation and returns diff --git a/samples/snippets/create_cluster_test.py b/samples/snippets/create_cluster_test.py new file mode 100644 index 00000000..f01c4db2 --- /dev/null +++ b/samples/snippets/create_cluster_test.py @@ -0,0 +1,50 @@ +import os +import uuid + +import backoff + +from google.cloud import container_v1 as gke + +import pytest + +import create_cluster as gke_create + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] +ZONE = "us-central1-b" +CLUSTER_NAME = f"py-container-repo-test-{uuid.uuid4().hex[:10]}" + + +@pytest.fixture +def setup_and_tear_down() -> None: + + # nohing to setup here + + yield + + # delete the cluster + client = gke.ClusterManagerClient() + cluster_location = client.common_location_path(PROJECT_ID, ZONE) + cluster_name = f"{cluster_location}/clusters/{CLUSTER_NAME}" + op = client.delete_cluster({"name": cluster_name}) + op_id = f"{cluster_location}/operations/{op.name}" + + # schedule a retry to ensure the cluster is deleted + @backoff.on_predicate(backoff.fibo, lambda x: x != gke.Operation.Status.DONE, max_tries=20) + def wait_for_delete() -> gke.Operation.Status: + return client.get_operation({"name": op_id}).status + + wait_for_delete() + + +def test_create_clusters(capsys: object) -> None: + gke_create.create_cluster(PROJECT_ID, ZONE, CLUSTER_NAME) + out, _ = capsys.readouterr() + + assert "Backing off " in out + assert "Successfully created cluster after" in out + + client = gke.ClusterManagerClient() + cluster_location = client.common_location_path(PROJECT_ID, ZONE) + list_response = client.list_clusters({"parent": cluster_location}) + + assert CLUSTER_NAME in list_response.clusters diff --git a/samples/snippets/delete_cluster.py b/samples/snippets/delete_cluster.py index f8482a6f..d98592b1 100644 --- a/samples/snippets/delete_cluster.py +++ b/samples/snippets/delete_cluster.py @@ -56,7 +56,8 @@ def on_failure(details: Dict[str, str]) -> None: # function to execute upon a successful attempt and no more retries needed on_success=on_success, ) -def poll_for_op_status(client: container_v1.ClusterManagerClient, op_id: str) -> bool: +def poll_for_op_status( + client: container_v1.ClusterManagerClient, op_id: str) -> container_v1.Operation.Status: """ A simple retry function that fetches the operation and returns it's status. diff --git a/samples/snippets/quickstart.py b/samples/snippets/quickstart.py index 7c5c1c53..b1dbf1f6 100644 --- a/samples/snippets/quickstart.py +++ b/samples/snippets/quickstart.py @@ -26,15 +26,15 @@ def list_clusters(project_id: str, location: str) -> None: # Initialize the Cluster management client. client = container_v1.ClusterManagerClient() # Create a fully qualified location identifier of form `projects/{project_id}/location/{zone}'. - clusterLocation = client.common_location_path(project_id, location) + cluster_location = client.common_location_path(project_id, location) # Create the request object with the location identifier. - request = {"parent": clusterLocation} - listResponse = client.list_clusters(request) + request = {"parent": cluster_location} + list_response = client.list_clusters(request) print( - f"There were {len(listResponse.clusters)} clusters in {location} for project {project_id}." + f"There were {len(list_response.clusters)} clusters in {location} for project {project_id}." ) - for cluster in listResponse.clusters: + for cluster in list_response.clusters: print(f"- {cluster.name}") diff --git a/samples/snippets/quickstart_test.py b/samples/snippets/quickstart_test.py index a6049fd4..b65ef03f 100644 --- a/samples/snippets/quickstart_test.py +++ b/samples/snippets/quickstart_test.py @@ -1,16 +1,16 @@ import os -import quickstart +import quickstart as gke_list PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] ZONE = "us-central1-b" -def test_list_models(capsys: object) -> None: - outputPrefix = "There were " - outputSuffix = f" clusters in {ZONE} for project {PROJECT_ID}." +def test_list_clusters(capsys: object) -> None: + output_prefix = "There were " + output_suffix = f" clusters in {ZONE} for project {PROJECT_ID}." - quickstart.list_clusters(PROJECT_ID, ZONE) + gke_list.list_clusters(PROJECT_ID, ZONE) out, _ = capsys.readouterr() ''' @@ -30,11 +30,11 @@ def test_list_models(capsys: object) -> None: "", ] ''' - outLines = out.split("\n") - firstLine = outLines[0] - firstLine = firstLine.replace(outputPrefix, "") - firstLine = firstLine.replace(outputSuffix, "") - clusterCount = int(firstLine) # get the cluster count in the first line - - assert outputSuffix in out - assert clusterCount == len(outLines) - 2 + out_lines = out.split("\n") + first_line = out_lines[0] + first_line = first_line.replace(output_prefix, "") + first_line = first_line.replace(output_suffix, "") + cluster_count = int(first_line) # get the cluster count in the first line + + assert output_suffix in out + assert cluster_count == len(out_lines) - 2 From 6f8cddadd4b422553553ce34cafc7b9726a9a334 Mon Sep 17 00:00:00 2001 From: shabirmean Date: Wed, 9 Feb 2022 20:57:45 -0500 Subject: [PATCH 31/44] test: add test for delete cluster --- samples/snippets/create_cluster_test.py | 11 ++-- samples/snippets/delete_cluster_test.py | 72 +++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 samples/snippets/delete_cluster_test.py diff --git a/samples/snippets/create_cluster_test.py b/samples/snippets/create_cluster_test.py index f01c4db2..dca1ee81 100644 --- a/samples/snippets/create_cluster_test.py +++ b/samples/snippets/create_cluster_test.py @@ -14,11 +14,12 @@ CLUSTER_NAME = f"py-container-repo-test-{uuid.uuid4().hex[:10]}" -@pytest.fixture +@pytest.fixture(autouse=True) def setup_and_tear_down() -> None: # nohing to setup here + # run the tests here yield # delete the cluster @@ -29,7 +30,7 @@ def setup_and_tear_down() -> None: op_id = f"{cluster_location}/operations/{op.name}" # schedule a retry to ensure the cluster is deleted - @backoff.on_predicate(backoff.fibo, lambda x: x != gke.Operation.Status.DONE, max_tries=20) + @backoff.on_predicate(backoff.expo, lambda x: x != gke.Operation.Status.DONE, max_tries=20) def wait_for_delete() -> gke.Operation.Status: return client.get_operation({"name": op_id}).status @@ -47,4 +48,8 @@ def test_create_clusters(capsys: object) -> None: cluster_location = client.common_location_path(PROJECT_ID, ZONE) list_response = client.list_clusters({"parent": cluster_location}) - assert CLUSTER_NAME in list_response.clusters + list_of_clusters = [] + for cluster in list_response.clusters: + list_of_clusters.append(cluster.name) + + assert CLUSTER_NAME in list_of_clusters diff --git a/samples/snippets/delete_cluster_test.py b/samples/snippets/delete_cluster_test.py new file mode 100644 index 00000000..4790bf9a --- /dev/null +++ b/samples/snippets/delete_cluster_test.py @@ -0,0 +1,72 @@ +import os +import uuid + +import backoff + +from google.cloud import container_v1 as gke + +import pytest + +import delete_cluster as gke_delete + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] +ZONE = "us-central1-b" +CLUSTER_NAME = f"py-container-repo-test-{uuid.uuid4().hex[:10]}" + + +@pytest.fixture(autouse=True) +def setup_and_tear_down() -> None: + + # create a cluster to be deleted + client = gke.ClusterManagerClient() + cluster_location = client.common_location_path(PROJECT_ID, ZONE) + cluster_name = f"{cluster_location}/clusters/{CLUSTER_NAME}" + cluster_def = { + "name": cluster_name, + "initial_node_count": 2, + "node_config": {"machine_type": "e2-standard-2"}, + } + op = client.create_cluster({"parent": cluster_location, "cluster": cluster_def}) + op_id = f"{cluster_location}/operations/{op.name}" + + # schedule a retry to ensure the cluster is created + @backoff.on_predicate(backoff.expo, lambda x: x != gke.Operation.Status.DONE, max_tries=20) + def wait_for_create() -> gke.Operation.Status: + return client.get_operation({"name": op_id}).status + + wait_for_create() + + # run the tests here + yield + + # delete the cluster in case the test itself failed + client = gke.ClusterManagerClient() + cluster_location = client.common_location_path(PROJECT_ID, ZONE) + cluster_name = f"{cluster_location}/clusters/{CLUSTER_NAME}" + op = client.delete_cluster({"name": cluster_name}) + op_id = f"{cluster_location}/operations/{op.name}" + + # schedule a retry to ensure the cluster is deleted + @backoff.on_predicate(backoff.expo, lambda x: x != gke.Operation.Status.DONE, max_tries=20) + def wait_for_delete() -> gke.Operation.Status: + return client.get_operation({"name": op_id}).status + + wait_for_delete() + + +def test_delete_clusters(capsys: object) -> None: + gke_delete.delete_cluster(PROJECT_ID, ZONE, CLUSTER_NAME) + out, _ = capsys.readouterr() + + assert "Backing off " in out + assert "Successfully deleted cluster after" in out + + client = gke.ClusterManagerClient() + cluster_location = client.common_location_path(PROJECT_ID, ZONE) + list_response = client.list_clusters({"parent": cluster_location}) + + list_of_clusters = [] + for cluster in list_response.clusters: + list_of_clusters.append(cluster.name) + + assert CLUSTER_NAME not in list_of_clusters From 74969d724e909a6e64343a8357bc4112f2f88d0b Mon Sep 17 00:00:00 2001 From: shabirmean Date: Wed, 9 Feb 2022 21:08:36 -0500 Subject: [PATCH 32/44] test: fix fixture in test --- samples/snippets/delete_cluster_test.py | 23 ++++++++++++++--------- samples/snippets/requirement-test.txt | 3 ++- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/samples/snippets/delete_cluster_test.py b/samples/snippets/delete_cluster_test.py index 4790bf9a..c9ad3dbc 100644 --- a/samples/snippets/delete_cluster_test.py +++ b/samples/snippets/delete_cluster_test.py @@ -4,6 +4,7 @@ import backoff from google.cloud import container_v1 as gke +from google.api_core import exceptions as googleEx import pytest @@ -20,9 +21,8 @@ def setup_and_tear_down() -> None: # create a cluster to be deleted client = gke.ClusterManagerClient() cluster_location = client.common_location_path(PROJECT_ID, ZONE) - cluster_name = f"{cluster_location}/clusters/{CLUSTER_NAME}" cluster_def = { - "name": cluster_name, + "name": CLUSTER_NAME, "initial_node_count": 2, "node_config": {"machine_type": "e2-standard-2"}, } @@ -43,15 +43,20 @@ def wait_for_create() -> gke.Operation.Status: client = gke.ClusterManagerClient() cluster_location = client.common_location_path(PROJECT_ID, ZONE) cluster_name = f"{cluster_location}/clusters/{CLUSTER_NAME}" - op = client.delete_cluster({"name": cluster_name}) - op_id = f"{cluster_location}/operations/{op.name}" - # schedule a retry to ensure the cluster is deleted - @backoff.on_predicate(backoff.expo, lambda x: x != gke.Operation.Status.DONE, max_tries=20) - def wait_for_delete() -> gke.Operation.Status: - return client.get_operation({"name": op_id}).status + try: + op = client.delete_cluster({"name": cluster_name}) + op_id = f"{cluster_location}/operations/{op.name}" + + # schedule a retry to ensure the cluster is deleted + @backoff.on_predicate(backoff.expo, lambda x: x != gke.Operation.Status.DONE, max_tries=20) + def wait_for_delete() -> gke.Operation.Status: + return client.get_operation({"name": op_id}).status - wait_for_delete() + wait_for_delete() + except googleEx.NotFound as e: + # if the delete test passed this won't be necessary + pass def test_delete_clusters(capsys: object) -> None: diff --git a/samples/snippets/requirement-test.txt b/samples/snippets/requirement-test.txt index 1698fe7a..2336a123 100644 --- a/samples/snippets/requirement-test.txt +++ b/samples/snippets/requirement-test.txt @@ -1,3 +1,4 @@ backoff==1.11.1 -pytest==7.0.0 \ No newline at end of file +pytest==7.0.0 +google-api-core=2.5.0 \ No newline at end of file From e2e4c4ce6c26e8975f85a949d685d297deacd589 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Thu, 10 Feb 2022 02:10:54 +0000 Subject: [PATCH 33/44] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- .gitignore | 1 - samples/snippets/create_cluster.py | 6 ++--- samples/snippets/create_cluster_test.py | 4 +++- samples/snippets/delete_cluster.py | 6 ++--- samples/snippets/delete_cluster_test.py | 8 +++++-- samples/snippets/noxfile.py | 31 ++++++++++++++++--------- samples/snippets/quickstart.py | 3 +-- samples/snippets/quickstart_test.py | 6 ++--- 8 files changed, 39 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 51671450..b4243ced 100644 --- a/.gitignore +++ b/.gitignore @@ -61,4 +61,3 @@ system_tests/local_test_setup # Make sure a generated file isn't accidentally committed. pylintrc pylintrc.test -lro-samples diff --git a/samples/snippets/create_cluster.py b/samples/snippets/create_cluster.py index b676cc20..2cd7cef4 100644 --- a/samples/snippets/create_cluster.py +++ b/samples/snippets/create_cluster.py @@ -57,7 +57,8 @@ def on_failure(details: Dict[str, str]) -> None: on_success=on_success, ) def poll_for_op_status( - client: container_v1.ClusterManagerClient, op_id: str) -> container_v1.Operation.Status: + client: container_v1.ClusterManagerClient, op_id: str +) -> container_v1.Operation.Status: """ This function calls the Operation API in GCP with the given operation id. It serves as a simple retry function that fetches the operation and returns @@ -94,8 +95,7 @@ def create_cluster(project_id: str, location: str, cluster_name: str) -> None: if __name__ == "__main__": parser = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawDescriptionHelpFormatter, + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument("project_id", help="Google Cloud project ID") parser.add_argument("zone", help="GKE Cluster zone") diff --git a/samples/snippets/create_cluster_test.py b/samples/snippets/create_cluster_test.py index dca1ee81..42e3e064 100644 --- a/samples/snippets/create_cluster_test.py +++ b/samples/snippets/create_cluster_test.py @@ -30,7 +30,9 @@ def setup_and_tear_down() -> None: op_id = f"{cluster_location}/operations/{op.name}" # schedule a retry to ensure the cluster is deleted - @backoff.on_predicate(backoff.expo, lambda x: x != gke.Operation.Status.DONE, max_tries=20) + @backoff.on_predicate( + backoff.expo, lambda x: x != gke.Operation.Status.DONE, max_tries=20 + ) def wait_for_delete() -> gke.Operation.Status: return client.get_operation({"name": op_id}).status diff --git a/samples/snippets/delete_cluster.py b/samples/snippets/delete_cluster.py index d98592b1..574c7b4d 100644 --- a/samples/snippets/delete_cluster.py +++ b/samples/snippets/delete_cluster.py @@ -57,7 +57,8 @@ def on_failure(details: Dict[str, str]) -> None: on_success=on_success, ) def poll_for_op_status( - client: container_v1.ClusterManagerClient, op_id: str) -> container_v1.Operation.Status: + client: container_v1.ClusterManagerClient, op_id: str +) -> container_v1.Operation.Status: """ A simple retry function that fetches the operation and returns it's status. @@ -87,8 +88,7 @@ def delete_cluster(project_id: str, location: str, cluster_name: str) -> None: if __name__ == "__main__": parser = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawDescriptionHelpFormatter, + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument("project_id", help="Google Cloud project ID") parser.add_argument("zone", help="GKE Cluster zone") diff --git a/samples/snippets/delete_cluster_test.py b/samples/snippets/delete_cluster_test.py index c9ad3dbc..51c87020 100644 --- a/samples/snippets/delete_cluster_test.py +++ b/samples/snippets/delete_cluster_test.py @@ -30,7 +30,9 @@ def setup_and_tear_down() -> None: op_id = f"{cluster_location}/operations/{op.name}" # schedule a retry to ensure the cluster is created - @backoff.on_predicate(backoff.expo, lambda x: x != gke.Operation.Status.DONE, max_tries=20) + @backoff.on_predicate( + backoff.expo, lambda x: x != gke.Operation.Status.DONE, max_tries=20 + ) def wait_for_create() -> gke.Operation.Status: return client.get_operation({"name": op_id}).status @@ -49,7 +51,9 @@ def wait_for_create() -> gke.Operation.Status: op_id = f"{cluster_location}/operations/{op.name}" # schedule a retry to ensure the cluster is deleted - @backoff.on_predicate(backoff.expo, lambda x: x != gke.Operation.Status.DONE, max_tries=20) + @backoff.on_predicate( + backoff.expo, lambda x: x != gke.Operation.Status.DONE, max_tries=20 + ) def wait_for_delete() -> gke.Operation.Status: return client.get_operation({"name": op_id}).status diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index 4dc5fa1a..20cdfc62 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -1,4 +1,4 @@ -# Copyright 2022 Google LLC +# 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. @@ -29,8 +29,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 @@ -39,7 +40,7 @@ TEST_CONFIG = { # You can opt out from the test for specific Python versions. - "ignored_versions": ["2.7"], + "ignored_versions": [], # Old samples are opted out of enforcing Python type hints # All new samples should feature them "enforce_type_hints": False, @@ -79,7 +80,6 @@ def get_pytest_env_vars() -> Dict[str, str]: 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] # deprecated # Apply user supplied envs. ret.update(TEST_CONFIG["envs"]) @@ -87,15 +87,18 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. -# All versions used to tested samples. -ALL_VERSIONS = ["2.7", "3.6", "3.7", "3.8", "3.9", "3.10"] +# All versions used to test samples. +ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] # 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)) +INSTALL_LIBRARY_FROM_SOURCE = os.environ.get("INSTALL_LIBRARY_FROM_SOURCE", False) in ( + "True", + "true", +) # Error if a python version is missing nox.options.error_on_missing_interpreters = True @@ -107,6 +110,7 @@ def get_pytest_env_vars() -> Dict[str, str]: def _determine_local_import_names(start_dir: str) -> List[str]: """Determines all import names that should be considered "local". + This is used when running the linter to insure that import order is properly checked. """ @@ -164,7 +168,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) @@ -183,6 +187,7 @@ def _session_tests( ) -> None: # check for presence of tests test_list = glob.glob("*_test.py") + glob.glob("test_*.py") + test_list.extend(glob.glob("tests")) if len(test_list) == 0: print("No tests found, skipping directory.") else: @@ -238,15 +243,19 @@ def py(session: nox.sessions.Session) -> None: def _get_repo_root() -> Optional[str]: - """Returns the root folder of the project.""" - # Get root of this repository. - # Assume we don't have directories nested deeper than 10 items. + """ 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) + # .git is not available in repos cloned via Cloud Build + # setup.py is always in the library's root, so use that instead + # https://github.com/googleapis/synthtool/issues/792 + if Path(p / "setup.py").exists(): + return str(p) p = p.parent raise Exception("Unable to detect repository root.") diff --git a/samples/snippets/quickstart.py b/samples/snippets/quickstart.py index b1dbf1f6..11e53179 100644 --- a/samples/snippets/quickstart.py +++ b/samples/snippets/quickstart.py @@ -41,8 +41,7 @@ def list_clusters(project_id: str, location: str) -> None: if __name__ == "__main__": parser = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawDescriptionHelpFormatter, + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument("project_id", help="Google Cloud project ID") parser.add_argument("zone", help="GKE Cluster zone") diff --git a/samples/snippets/quickstart_test.py b/samples/snippets/quickstart_test.py index b65ef03f..c212d6b9 100644 --- a/samples/snippets/quickstart_test.py +++ b/samples/snippets/quickstart_test.py @@ -13,7 +13,7 @@ def test_list_clusters(capsys: object) -> None: gke_list.list_clusters(PROJECT_ID, ZONE) out, _ = capsys.readouterr() - ''' + """ Typical output looks as follows: There were 3 clusters in us-central1-b for project test-project. @@ -29,12 +29,12 @@ def test_list_clusters(capsys: object) -> None: "- cluster3", "", ] - ''' + """ out_lines = out.split("\n") first_line = out_lines[0] first_line = first_line.replace(output_prefix, "") first_line = first_line.replace(output_suffix, "") - cluster_count = int(first_line) # get the cluster count in the first line + cluster_count = int(first_line) # get the cluster count in the first line assert output_suffix in out assert cluster_count == len(out_lines) - 2 From dc8a9a0d4bf0d605a7980b1353fc715625307e0a Mon Sep 17 00:00:00 2001 From: shabirmean Date: Wed, 9 Feb 2022 21:24:38 -0500 Subject: [PATCH 34/44] lint: finx linting errors --- samples/snippets/delete_cluster_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/snippets/delete_cluster_test.py b/samples/snippets/delete_cluster_test.py index 51c87020..e6364724 100644 --- a/samples/snippets/delete_cluster_test.py +++ b/samples/snippets/delete_cluster_test.py @@ -3,8 +3,8 @@ import backoff -from google.cloud import container_v1 as gke from google.api_core import exceptions as googleEx +from google.cloud import container_v1 as gke import pytest @@ -58,8 +58,8 @@ def wait_for_delete() -> gke.Operation.Status: return client.get_operation({"name": op_id}).status wait_for_delete() - except googleEx.NotFound as e: - # if the delete test passed this won't be necessary + except googleEx.NotFound: + # if the delete test passed then this is bound to happen pass From 54c84e2e8907d6c37363e79fa0d99d7260c455d2 Mon Sep 17 00:00:00 2001 From: Shabir Mohamed Abdul Samadh <7249208+Shabirmean@users.noreply.github.com> Date: Fri, 11 Feb 2022 14:08:56 -0500 Subject: [PATCH 35/44] cleanup: remove the main from being inside the region tags --- samples/snippets/quickstart.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/samples/snippets/quickstart.py b/samples/snippets/quickstart.py index 11e53179..112fc294 100644 --- a/samples/snippets/quickstart.py +++ b/samples/snippets/quickstart.py @@ -36,7 +36,7 @@ def list_clusters(project_id: str, location: str) -> None: ) for cluster in list_response.clusters: print(f"- {cluster.name}") - +# [END gke_list_cluster] if __name__ == "__main__": @@ -52,4 +52,3 @@ def list_clusters(project_id: str, location: str) -> None: sys.exit(1) list_clusters(args.project_id, args.zone) -# [END gke_list_cluster] From 2ec423f19874a82e7221123218393a7a5cc8b297 Mon Sep 17 00:00:00 2001 From: Shabir Mohamed Abdul Samadh <7249208+Shabirmean@users.noreply.github.com> Date: Fri, 11 Feb 2022 14:09:35 -0500 Subject: [PATCH 36/44] cleanup: remove the main from being inside the region tags --- samples/snippets/create_cluster.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/samples/snippets/create_cluster.py b/samples/snippets/create_cluster.py index 2cd7cef4..5ebbe1e6 100644 --- a/samples/snippets/create_cluster.py +++ b/samples/snippets/create_cluster.py @@ -91,7 +91,7 @@ def create_cluster(project_id: str, location: str, cluster_name: str) -> None: op_identifier = f"{cluster_location}/operations/{create_response.name}" # poll for the operation status and schedule a retry until the cluster is created poll_for_op_status(client, op_identifier) - +# [END gke_create_cluster] if __name__ == "__main__": parser = argparse.ArgumentParser( @@ -107,4 +107,3 @@ def create_cluster(project_id: str, location: str, cluster_name: str) -> None: sys.exit(1) create_cluster(args.project_id, args.zone, args.cluster_name) -# [END gke_create_cluster] From c86fa1137d011211d5db74fbb76d7abb4b7c0d07 Mon Sep 17 00:00:00 2001 From: Shabir Mohamed Abdul Samadh <7249208+Shabirmean@users.noreply.github.com> Date: Fri, 11 Feb 2022 14:10:15 -0500 Subject: [PATCH 37/44] cleanup: remove the main from being inside the region tags --- samples/snippets/delete_cluster.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/samples/snippets/delete_cluster.py b/samples/snippets/delete_cluster.py index 574c7b4d..ed18e92a 100644 --- a/samples/snippets/delete_cluster.py +++ b/samples/snippets/delete_cluster.py @@ -84,7 +84,7 @@ def delete_cluster(project_id: str, location: str, cluster_name: str) -> None: op_identifier = f"{cluster_location}/operations/{delete_response.name}" # poll for the operation status and schedule a retry until the cluster is deleted poll_for_op_status(client, op_identifier) - +# [END gke_delete_cluster] if __name__ == "__main__": parser = argparse.ArgumentParser( @@ -100,4 +100,3 @@ def delete_cluster(project_id: str, location: str, cluster_name: str) -> None: sys.exit(1) delete_cluster(args.project_id, args.zone, args.cluster_name) -# [END gke_delete_cluster] From 2187059da62eb13ad200a22f203204fb3a61347a Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Fri, 11 Feb 2022 19:11:26 +0000 Subject: [PATCH 38/44] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- samples/snippets/quickstart.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/samples/snippets/quickstart.py b/samples/snippets/quickstart.py index 112fc294..b7cecb52 100644 --- a/samples/snippets/quickstart.py +++ b/samples/snippets/quickstart.py @@ -36,6 +36,8 @@ def list_clusters(project_id: str, location: str) -> None: ) for cluster in list_response.clusters: print(f"- {cluster.name}") + + # [END gke_list_cluster] if __name__ == "__main__": From 93bcff055bb103d2b26f45292b5df1b99ac8e486 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Fri, 11 Feb 2022 19:13:36 +0000 Subject: [PATCH 39/44] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- samples/snippets/create_cluster.py | 2 ++ samples/snippets/delete_cluster.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/samples/snippets/create_cluster.py b/samples/snippets/create_cluster.py index 5ebbe1e6..ad5ddfc3 100644 --- a/samples/snippets/create_cluster.py +++ b/samples/snippets/create_cluster.py @@ -91,6 +91,8 @@ def create_cluster(project_id: str, location: str, cluster_name: str) -> None: op_identifier = f"{cluster_location}/operations/{create_response.name}" # poll for the operation status and schedule a retry until the cluster is created poll_for_op_status(client, op_identifier) + + # [END gke_create_cluster] if __name__ == "__main__": diff --git a/samples/snippets/delete_cluster.py b/samples/snippets/delete_cluster.py index ed18e92a..1fb91d87 100644 --- a/samples/snippets/delete_cluster.py +++ b/samples/snippets/delete_cluster.py @@ -84,6 +84,8 @@ def delete_cluster(project_id: str, location: str, cluster_name: str) -> None: op_identifier = f"{cluster_location}/operations/{delete_response.name}" # poll for the operation status and schedule a retry until the cluster is deleted poll_for_op_status(client, op_identifier) + + # [END gke_delete_cluster] if __name__ == "__main__": From 59240f0b4925dce2fd25ab4c5da79698aa842125 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Fri, 11 Feb 2022 19:13:37 +0000 Subject: [PATCH 40/44] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- samples/snippets/create_cluster.py | 2 ++ samples/snippets/delete_cluster.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/samples/snippets/create_cluster.py b/samples/snippets/create_cluster.py index 5ebbe1e6..ad5ddfc3 100644 --- a/samples/snippets/create_cluster.py +++ b/samples/snippets/create_cluster.py @@ -91,6 +91,8 @@ def create_cluster(project_id: str, location: str, cluster_name: str) -> None: op_identifier = f"{cluster_location}/operations/{create_response.name}" # poll for the operation status and schedule a retry until the cluster is created poll_for_op_status(client, op_identifier) + + # [END gke_create_cluster] if __name__ == "__main__": diff --git a/samples/snippets/delete_cluster.py b/samples/snippets/delete_cluster.py index ed18e92a..1fb91d87 100644 --- a/samples/snippets/delete_cluster.py +++ b/samples/snippets/delete_cluster.py @@ -84,6 +84,8 @@ def delete_cluster(project_id: str, location: str, cluster_name: str) -> None: op_identifier = f"{cluster_location}/operations/{delete_response.name}" # poll for the operation status and schedule a retry until the cluster is deleted poll_for_op_status(client, op_identifier) + + # [END gke_delete_cluster] if __name__ == "__main__": From 00da1e6fb01dcf2caec81702b348cee3ff8ce226 Mon Sep 17 00:00:00 2001 From: Shabir Mohamed Abdul Samadh <7249208+Shabirmean@users.noreply.github.com> Date: Sun, 13 Feb 2022 21:32:51 -0500 Subject: [PATCH 41/44] doc: fix typo Co-authored-by: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> --- samples/snippets/delete_cluster.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/delete_cluster.py b/samples/snippets/delete_cluster.py index 1fb91d87..eb2a4dd7 100644 --- a/samples/snippets/delete_cluster.py +++ b/samples/snippets/delete_cluster.py @@ -51,7 +51,7 @@ def on_failure(details: Dict[str, str]) -> None: lambda x: x != container_v1.Operation.Status.DONE, # maximum number of times to retry before giving up max_tries=20, - # function to execute upon a failure and when a retry a scheduled + # function to execute upon a failure and when a retry is scheduled on_backoff=on_failure, # function to execute upon a successful attempt and no more retries needed on_success=on_success, From 176fa2259578982385e813bbe95206a3b7c54989 Mon Sep 17 00:00:00 2001 From: Shabir Mohamed Abdul Samadh <7249208+Shabirmean@users.noreply.github.com> Date: Sun, 13 Feb 2022 21:33:11 -0500 Subject: [PATCH 42/44] doc: pr comment doc update Co-authored-by: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> --- samples/snippets/delete_cluster.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/delete_cluster.py b/samples/snippets/delete_cluster.py index eb2a4dd7..0290766e 100644 --- a/samples/snippets/delete_cluster.py +++ b/samples/snippets/delete_cluster.py @@ -82,7 +82,7 @@ def delete_cluster(project_id: str, location: str, cluster_name: str) -> None: request = {"name": cluster_name} delete_response = client.delete_cluster(request) op_identifier = f"{cluster_location}/operations/{delete_response.name}" - # poll for the operation status and schedule a retry until the cluster is deleted + # poll for the operation status until the cluster is deleted poll_for_op_status(client, op_identifier) From 588e02c518855f97059c919446d66157d9fb0202 Mon Sep 17 00:00:00 2001 From: shabirmean Date: Sun, 13 Feb 2022 21:33:38 -0500 Subject: [PATCH 43/44] doc: add license headers to missing files --- samples/snippets/create_cluster_test.py | 15 +++++++++++++++ samples/snippets/delete_cluster_test.py | 15 +++++++++++++++ samples/snippets/noxfile.py | 2 +- samples/snippets/quickstart_test.py | 15 +++++++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/samples/snippets/create_cluster_test.py b/samples/snippets/create_cluster_test.py index 42e3e064..a06ab2c5 100644 --- a/samples/snippets/create_cluster_test.py +++ b/samples/snippets/create_cluster_test.py @@ -1,3 +1,18 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + import os import uuid diff --git a/samples/snippets/delete_cluster_test.py b/samples/snippets/delete_cluster_test.py index e6364724..fc7845d3 100644 --- a/samples/snippets/delete_cluster_test.py +++ b/samples/snippets/delete_cluster_test.py @@ -1,3 +1,18 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + import os import uuid diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index 20cdfc62..a2f115aa 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -1,4 +1,4 @@ -# Copyright 2019 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/snippets/quickstart_test.py b/samples/snippets/quickstart_test.py index c212d6b9..7904fa55 100644 --- a/samples/snippets/quickstart_test.py +++ b/samples/snippets/quickstart_test.py @@ -1,3 +1,18 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + import os import quickstart as gke_list From c3338a594625c66bc4d9d5aa0618d1059bcd7b4a Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Mon, 14 Feb 2022 02:37:12 +0000 Subject: [PATCH 44/44] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- samples/snippets/noxfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index a2f115aa..20cdfc62 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -1,4 +1,4 @@ -# Copyright 2022 Google LLC +# 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.