diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7723ed4f..89652a35 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.18.0" + ".": "2.19.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 6eccfc1b..16c4f246 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ [1]: https://pypi.org/project/google-cloud-container/#history +## [2.19.0](https://github.com/googleapis/python-container/compare/v2.18.0...v2.19.0) (2023-04-06) + + +### Features + +* Add support for disabling pod IP cidr overprovision. This feature requires special allowlisting for the projects. ([56f65fa](https://github.com/googleapis/python-container/commit/56f65fa23de31317e4cf39e50351ad1e1bb04b57)) +* Add update support for accelerator config ([56f65fa](https://github.com/googleapis/python-container/commit/56f65fa23de31317e4cf39e50351ad1e1bb04b57)) + ## [2.18.0](https://github.com/googleapis/python-container/compare/v2.17.4...v2.18.0) (2023-03-28) diff --git a/google/cloud/container/__init__.py b/google/cloud/container/__init__.py index 61533223..eca392d8 100644 --- a/google/cloud/container/__init__.py +++ b/google/cloud/container/__init__.py @@ -126,6 +126,7 @@ NotificationConfig, Operation, OperationProgress, + PodCIDROverprovisionConfig, PrivateClusterConfig, PrivateClusterMasterGlobalAccessConfig, PrivateIPv6GoogleAccess, @@ -274,6 +275,7 @@ "NotificationConfig", "Operation", "OperationProgress", + "PodCIDROverprovisionConfig", "PrivateClusterConfig", "PrivateClusterMasterGlobalAccessConfig", "RecurringTimeWindow", diff --git a/google/cloud/container/gapic_version.py b/google/cloud/container/gapic_version.py index f09943f6..0f1a446f 100644 --- a/google/cloud/container/gapic_version.py +++ b/google/cloud/container/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.18.0" # {x-release-please-version} +__version__ = "2.19.0" # {x-release-please-version} diff --git a/google/cloud/container_v1/__init__.py b/google/cloud/container_v1/__init__.py index 9797ab28..e22b409f 100644 --- a/google/cloud/container_v1/__init__.py +++ b/google/cloud/container_v1/__init__.py @@ -121,6 +121,7 @@ NotificationConfig, Operation, OperationProgress, + PodCIDROverprovisionConfig, PrivateClusterConfig, PrivateClusterMasterGlobalAccessConfig, PrivateIPv6GoogleAccess, @@ -272,6 +273,7 @@ "NotificationConfig", "Operation", "OperationProgress", + "PodCIDROverprovisionConfig", "PrivateClusterConfig", "PrivateClusterMasterGlobalAccessConfig", "PrivateIPv6GoogleAccess", diff --git a/google/cloud/container_v1/gapic_version.py b/google/cloud/container_v1/gapic_version.py index f09943f6..0f1a446f 100644 --- a/google/cloud/container_v1/gapic_version.py +++ b/google/cloud/container_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.18.0" # {x-release-please-version} +__version__ = "2.19.0" # {x-release-please-version} diff --git a/google/cloud/container_v1/types/__init__.py b/google/cloud/container_v1/types/__init__.py index 27f62df5..01e4f29f 100644 --- a/google/cloud/container_v1/types/__init__.py +++ b/google/cloud/container_v1/types/__init__.py @@ -115,6 +115,7 @@ NotificationConfig, Operation, OperationProgress, + PodCIDROverprovisionConfig, PrivateClusterConfig, PrivateClusterMasterGlobalAccessConfig, PrivateIPv6GoogleAccess, @@ -261,6 +262,7 @@ "NotificationConfig", "Operation", "OperationProgress", + "PodCIDROverprovisionConfig", "PrivateClusterConfig", "PrivateClusterMasterGlobalAccessConfig", "RecurringTimeWindow", diff --git a/google/cloud/container_v1/types/cluster_service.py b/google/cloud/container_v1/types/cluster_service.py index 1c231ebf..d92b7b40 100644 --- a/google/cloud/container_v1/types/cluster_service.py +++ b/google/cloud/container_v1/types/cluster_service.py @@ -68,6 +68,7 @@ "LegacyAbac", "NetworkPolicy", "BinaryAuthorization", + "PodCIDROverprovisionConfig", "IPAllocationPolicy", "Cluster", "NodePoolAutoConfig", @@ -896,6 +897,20 @@ class NodeNetworkConfig(proto.Message): Network bandwidth tier configuration. This field is a member of `oneof`_ ``_network_performance_config``. + pod_cidr_overprovision_config (google.cloud.container_v1.types.PodCIDROverprovisionConfig): + [PRIVATE FIELD] Pod CIDR size overprovisioning config for + the nodepool. + + Pod CIDR size per node depends on max_pods_per_node. By + default, the value of max_pods_per_node is rounded off to + next power of 2 and we then double that to get the size of + pod CIDR block per node. Example: max_pods_per_node of 30 + would result in 64 IPs (/26). + + This config can disable the doubling of IPs (we still round + off to next power of 2) Example: max_pods_per_node of 30 + will result in 32 IPs (/27) when overprovisioning is + disabled. """ class NetworkPerformanceConfig(proto.Message): @@ -954,6 +969,11 @@ class Tier(proto.Enum): optional=True, message=NetworkPerformanceConfig, ) + pod_cidr_overprovision_config: "PodCIDROverprovisionConfig" = proto.Field( + proto.MESSAGE, + number=13, + message="PodCIDROverprovisionConfig", + ) class ShieldedInstanceConfig(proto.Message): @@ -1847,6 +1867,22 @@ class EvaluationMode(proto.Enum): ) +class PodCIDROverprovisionConfig(proto.Message): + r"""[PRIVATE FIELD] Config for pod CIDR size overprovisioning. + + Attributes: + disable (bool): + Whether Pod CIDR overprovisioning is + disabled. Note: Pod CIDR overprovisioning is + enabled by default. + """ + + disable: bool = proto.Field( + proto.BOOL, + number=1, + ) + + class IPAllocationPolicy(proto.Message): r"""Configuration for controlling how IPs are allocated in the cluster. @@ -1970,6 +2006,20 @@ class IPAllocationPolicy(proto.Message): ipv6_access_type (google.cloud.container_v1.types.IPv6AccessType): The ipv6 access type (internal or external) when create_subnetwork is true + pod_cidr_overprovision_config (google.cloud.container_v1.types.PodCIDROverprovisionConfig): + [PRIVATE FIELD] Pod CIDR size overprovisioning config for + the cluster. + + Pod CIDR size per node depends on max_pods_per_node. By + default, the value of max_pods_per_node is doubled and then + rounded off to next power of 2 to get the size of pod CIDR + block per node. Example: max_pods_per_node of 30 would + result in 64 IPs (/26). + + This config can disable the doubling of IPs (we still round + off to next power of 2) Example: max_pods_per_node of 30 + will result in 32 IPs (/27) when overprovisioning is + disabled. subnet_ipv6_cidr_block (str): Output only. [Output only] The subnet's IPv6 CIDR block used by nodes and pods. @@ -2040,6 +2090,11 @@ class IPAllocationPolicy(proto.Message): number=17, enum="IPv6AccessType", ) + pod_cidr_overprovision_config: "PodCIDROverprovisionConfig" = proto.Field( + proto.MESSAGE, + number=21, + message="PodCIDROverprovisionConfig", + ) subnet_ipv6_cidr_block: str = proto.Field( proto.STRING, number=22, diff --git a/google/cloud/container_v1beta1/__init__.py b/google/cloud/container_v1beta1/__init__.py index 9f9b393d..ad1c8db5 100644 --- a/google/cloud/container_v1beta1/__init__.py +++ b/google/cloud/container_v1beta1/__init__.py @@ -128,6 +128,7 @@ NotificationConfig, Operation, OperationProgress, + PodCIDROverprovisionConfig, PodSecurityPolicyConfig, PrivateClusterConfig, PrivateClusterMasterGlobalAccessConfig, @@ -293,6 +294,7 @@ "NotificationConfig", "Operation", "OperationProgress", + "PodCIDROverprovisionConfig", "PodSecurityPolicyConfig", "PrivateClusterConfig", "PrivateClusterMasterGlobalAccessConfig", diff --git a/google/cloud/container_v1beta1/gapic_version.py b/google/cloud/container_v1beta1/gapic_version.py index f09943f6..0f1a446f 100644 --- a/google/cloud/container_v1beta1/gapic_version.py +++ b/google/cloud/container_v1beta1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.18.0" # {x-release-please-version} +__version__ = "2.19.0" # {x-release-please-version} diff --git a/google/cloud/container_v1beta1/types/__init__.py b/google/cloud/container_v1beta1/types/__init__.py index e7cb4f31..a9a8d106 100644 --- a/google/cloud/container_v1beta1/types/__init__.py +++ b/google/cloud/container_v1beta1/types/__init__.py @@ -122,6 +122,7 @@ NotificationConfig, Operation, OperationProgress, + PodCIDROverprovisionConfig, PodSecurityPolicyConfig, PrivateClusterConfig, PrivateClusterMasterGlobalAccessConfig, @@ -283,6 +284,7 @@ "NotificationConfig", "Operation", "OperationProgress", + "PodCIDROverprovisionConfig", "PodSecurityPolicyConfig", "PrivateClusterConfig", "PrivateClusterMasterGlobalAccessConfig", diff --git a/google/cloud/container_v1beta1/types/cluster_service.py b/google/cloud/container_v1beta1/types/cluster_service.py index f5c6e4e3..7b4eaf96 100644 --- a/google/cloud/container_v1beta1/types/cluster_service.py +++ b/google/cloud/container_v1beta1/types/cluster_service.py @@ -71,6 +71,7 @@ "MasterAuthorizedNetworksConfig", "LegacyAbac", "NetworkPolicy", + "PodCIDROverprovisionConfig", "IPAllocationPolicy", "BinaryAuthorization", "PodSecurityPolicyConfig", @@ -902,6 +903,20 @@ class NodeNetworkConfig(proto.Message): Network bandwidth tier configuration. This field is a member of `oneof`_ ``_network_performance_config``. + pod_cidr_overprovision_config (google.cloud.container_v1beta1.types.PodCIDROverprovisionConfig): + [PRIVATE FIELD] Pod CIDR size overprovisioning config for + the nodepool. + + Pod CIDR size per node depends on max_pods_per_node. By + default, the value of max_pods_per_node is rounded off to + next power of 2 and we then double that to get the size of + pod CIDR block per node. Example: max_pods_per_node of 30 + would result in 64 IPs (/26). + + This config can disable the doubling of IPs (we still round + off to next power of 2) Example: max_pods_per_node of 30 + will result in 32 IPs (/27) when overprovisioning is + disabled. """ class NetworkPerformanceConfig(proto.Message): @@ -972,6 +987,11 @@ class Tier(proto.Enum): optional=True, message=NetworkPerformanceConfig, ) + pod_cidr_overprovision_config: "PodCIDROverprovisionConfig" = proto.Field( + proto.MESSAGE, + number=13, + message="PodCIDROverprovisionConfig", + ) class ShieldedInstanceConfig(proto.Message): @@ -1933,6 +1953,22 @@ class Provider(proto.Enum): ) +class PodCIDROverprovisionConfig(proto.Message): + r"""[PRIVATE FIELD] Config for pod CIDR size overprovisioning. + + Attributes: + disable (bool): + Whether Pod CIDR overprovisioning is + disabled. Note: Pod CIDR overprovisioning is + enabled by default. + """ + + disable: bool = proto.Field( + proto.BOOL, + number=1, + ) + + class IPAllocationPolicy(proto.Message): r"""Configuration for controlling how IPs are allocated in the cluster. @@ -2075,6 +2111,20 @@ class IPAllocationPolicy(proto.Message): ipv6_access_type (google.cloud.container_v1beta1.types.IPAllocationPolicy.IPv6AccessType): The ipv6 access type (internal or external) when create_subnetwork is true + pod_cidr_overprovision_config (google.cloud.container_v1beta1.types.PodCIDROverprovisionConfig): + [PRIVATE FIELD] Pod CIDR size overprovisioning config for + the cluster. + + Pod CIDR size per node depends on max_pods_per_node. By + default, the value of max_pods_per_node is doubled and then + rounded off to next power of 2 to get the size of pod CIDR + block per node. Example: max_pods_per_node of 30 would + result in 64 IPs (/26). + + This config can disable the doubling of IPs (we still round + off to next power of 2) Example: max_pods_per_node of 30 + will result in 32 IPs (/27) when overprovisioning is + disabled. subnet_ipv6_cidr_block (str): Output only. [Output only] The subnet's IPv6 CIDR block used by nodes and pods. @@ -2183,6 +2233,11 @@ class IPv6AccessType(proto.Enum): number=17, enum=IPv6AccessType, ) + pod_cidr_overprovision_config: "PodCIDROverprovisionConfig" = proto.Field( + proto.MESSAGE, + number=21, + message="PodCIDROverprovisionConfig", + ) subnet_ipv6_cidr_block: str = proto.Field( proto.STRING, number=22, diff --git a/samples/AUTHORING_GUIDE.md b/samples/AUTHORING_GUIDE.md deleted file mode 100644 index 55c97b32..00000000 --- a/samples/AUTHORING_GUIDE.md +++ /dev/null @@ -1 +0,0 @@ -See https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/AUTHORING_GUIDE.md \ No newline at end of file diff --git a/samples/CONTRIBUTING.md b/samples/CONTRIBUTING.md deleted file mode 100644 index 34c882b6..00000000 --- a/samples/CONTRIBUTING.md +++ /dev/null @@ -1 +0,0 @@ -See https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/CONTRIBUTING.md \ No newline at end of file diff --git a/samples/generated_samples/snippet_metadata_google.container.v1.json b/samples/generated_samples/snippet_metadata_google.container.v1.json index df02e5b5..ecd94663 100644 --- a/samples/generated_samples/snippet_metadata_google.container.v1.json +++ b/samples/generated_samples/snippet_metadata_google.container.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-container", - "version": "2.18.0" + "version": "2.19.0" }, "snippets": [ { diff --git a/samples/generated_samples/snippet_metadata_google.container.v1beta1.json b/samples/generated_samples/snippet_metadata_google.container.v1beta1.json index 0cdcffa1..13e22be3 100644 --- a/samples/generated_samples/snippet_metadata_google.container.v1beta1.json +++ b/samples/generated_samples/snippet_metadata_google.container.v1beta1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-container", - "version": "2.18.0" + "version": "2.19.0" }, "snippets": [ { diff --git a/samples/snippets/README.md b/samples/snippets/README.md deleted file mode 100644 index a1d0691f..00000000 --- a/samples/snippets/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# 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 sample -- [**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 - -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**](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**](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/snippets/create_cluster.py b/samples/snippets/create_cluster.py deleted file mode 100644 index 949c6f6e..00000000 --- a/samples/snippets/create_cluster.py +++ /dev/null @@ -1,112 +0,0 @@ -# -*- 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. - -# [START gke_create_cluster] -import argparse -import sys -from typing import Dict - -import backoff -from google.cloud import container_v1 - - -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. - - 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: Dict[str, str]) -> None: - """ - 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 -) -> 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 - 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 - - -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 = { - "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_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) - - -# [END gke_create_cluster] - -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() - - if len(sys.argv) != 4: - parser.print_usage() - sys.exit(1) - - create_cluster(args.project_id, args.zone, args.cluster_name) diff --git a/samples/snippets/create_cluster_test.py b/samples/snippets/create_cluster_test.py deleted file mode 100644 index 75a281c9..00000000 --- a/samples/snippets/create_cluster_test.py +++ /dev/null @@ -1,72 +0,0 @@ -# -*- 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 - -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(autouse=True) -def setup_and_tear_down() -> None: - - # nohing to setup here - - # run the tests here - yield - - try: - # 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}" - - finally: - # 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_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}) - - 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.py b/samples/snippets/delete_cluster.py deleted file mode 100644 index 405de978..00000000 --- a/samples/snippets/delete_cluster.py +++ /dev/null @@ -1,105 +0,0 @@ -# -*- 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. - -# [START gke_delete_cluster] -import argparse -import sys -from typing import Dict - -import backoff -from google.cloud import container_v1 - - -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. - - 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: Dict[str, str]) -> None: - """ - 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 is 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 -) -> container_v1.Operation.Status: - """ - 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(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}" - # Create the request object with the location identifier. - request = {"name": cluster_name} - delete_response = client.delete_cluster(request) - op_identifier = f"{cluster_location}/operations/{delete_response.name}" - # poll for the operation status until the cluster is deleted - poll_for_op_status(client, op_identifier) - - -# [END gke_delete_cluster] - -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() - - if len(sys.argv) != 4: - parser.print_usage() - sys.exit(1) - - delete_cluster(args.project_id, args.zone, args.cluster_name) diff --git a/samples/snippets/delete_cluster_test.py b/samples/snippets/delete_cluster_test.py deleted file mode 100644 index 5e187d84..00000000 --- a/samples/snippets/delete_cluster_test.py +++ /dev/null @@ -1,94 +0,0 @@ -# -*- 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 - -import backoff -from google.api_core import exceptions as googleEx -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_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}" - - 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() - except googleEx.NotFound: - # if the delete test passed then this is bound to happen - pass - - -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 diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py deleted file mode 100644 index de104dbc..00000000 --- a/samples/snippets/noxfile.py +++ /dev/null @@ -1,292 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import print_function - -import glob -import os -from pathlib import Path -import sys -from typing import Callable, Dict, 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 - -BLACK_VERSION = "black==22.3.0" -ISORT_VERSION = "isort==5.10.1" - -# 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": [], - # 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] - - # Apply user supplied envs. - ret.update(TEST_CONFIG["envs"]) - return ret - - -# DO NOT EDIT - automatically generated. -# All versions used to test samples. -ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11"] - -# 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 = 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 - -# -# Style Checks -# - - -# 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", - "--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") - else: - session.install("flake8", "flake8-annotations") - - args = FLAKE8_COMMON_ARGS + [ - ".", - ] - session.run("flake8", *args) - - -# -# Black -# - - -@nox.session -def blacken(session: nox.sessions.Session) -> None: - """Run black. Format code to uniform standard.""" - session.install(BLACK_VERSION) - python_files = [path for path in os.listdir(".") if path.endswith(".py")] - - session.run("black", *python_files) - - -# -# format = isort + black -# - - -@nox.session -def format(session: nox.sessions.Session) -> None: - """ - Run isort to sort imports. Then run black - to format code to uniform standard. - """ - session.install(BLACK_VERSION, ISORT_VERSION) - python_files = [path for path in os.listdir(".") if path.endswith(".py")] - - # Use the --fss option to sort imports using strict alphabetical order. - # See https://pycqa.github.io/isort/docs/configuration/options.html#force-sort-within-sections - session.run("isort", "--fss", *python_files) - 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", recursive=True) + glob.glob( - "**/test_*.py", recursive=True - ) - test_list.extend(glob.glob("**/tests", recursive=True)) - - if len(test_list) == 0: - print("No tests found, skipping directory.") - return - - 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.""" - concurrent_args = [] - 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") - with open("requirements.txt") as rfile: - packages = rfile.read() - - 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") - with open("requirements-test.txt") as rtfile: - packages += rtfile.read() - - if INSTALL_LIBRARY_FROM_SOURCE: - session.install("-e", _get_repo_root()) - - if post_install: - post_install(session) - - if "pytest-parallel" in packages: - concurrent_args.extend(["--workers", "auto", "--tests-per-worker", "auto"]) - elif "pytest-xdist" in packages: - concurrent_args.extend(["-n", "auto"]) - - session.run( - "pytest", - *(PYTEST_COMMON_ARGS + session.posargs + concurrent_args), - # 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) - # .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.") - - -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 deleted file mode 100644 index 16f326e5..00000000 --- a/samples/snippets/noxfile_config.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright 2022 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# 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/snippets/quickstart.py b/samples/snippets/quickstart.py deleted file mode 100644 index 3299a9a1..00000000 --- a/samples/snippets/quickstart.py +++ /dev/null @@ -1,57 +0,0 @@ -# -*- 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. - -# [START gke_list_cluster] -import argparse -import sys - -from google.cloud import container_v1 - - -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}'. - cluster_location = client.common_location_path(project_id, location) - # Create the request object with the location identifier. - request = {"parent": cluster_location} - list_response = client.list_clusters(request) - - print( - f"There were {len(list_response.clusters)} clusters in {location} for project {project_id}." - ) - for cluster in list_response.clusters: - print(f"- {cluster.name}") - - -# [END gke_list_cluster] - -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() - - if len(sys.argv) != 3: - parser.print_usage() - sys.exit(1) - - list_clusters(args.project_id, args.zone) diff --git a/samples/snippets/quickstart_test.py b/samples/snippets/quickstart_test.py deleted file mode 100644 index 7904fa55..00000000 --- a/samples/snippets/quickstart_test.py +++ /dev/null @@ -1,55 +0,0 @@ -# -*- 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 - -PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] -ZONE = "us-central1-b" - - -def test_list_clusters(capsys: object) -> None: - output_prefix = "There were " - output_suffix = f" clusters in {ZONE} for project {PROJECT_ID}." - - 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. - - cluster1 - - cluster2 - - cluster3 - - Split array by '\n' - [ - "There were 3 clusters in us-central1-b for project test-project.", - "- cluster1", - "- cluster2", - "- 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 - - assert output_suffix in out - assert cluster_count == len(out_lines) - 2 diff --git a/samples/snippets/requirement-test.txt b/samples/snippets/requirement-test.txt deleted file mode 100644 index 2336a123..00000000 --- a/samples/snippets/requirement-test.txt +++ /dev/null @@ -1,4 +0,0 @@ - -backoff==1.11.1 -pytest==7.0.0 -google-api-core=2.5.0 \ No newline at end of file diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt deleted file mode 100644 index b74ce427..00000000 --- a/samples/snippets/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -google-cloud-container==2.17.4 -backoff==2.2.1 -pytest==7.2.2 \ No newline at end of file