Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: migrate code from googleapis/python-container #8451

Merged
merged 42 commits into from
Nov 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
f432083
feat!: migrate to microgenerator (#33)
arithmetic1728 Jul 16, 2020
91547b0
docs(samples): add usage samples to show handling of LRO response Ope…
Shabirmean Feb 16, 2022
4da083a
chore: Adding support for pytest-xdist and pytest-parallel (#204)
gcf-owl-bot[bot] Mar 4, 2022
4dd369c
chore(deps): update all dependencies (#206)
renovate-bot Mar 7, 2022
8caf13e
chore(deps): update dependency google-cloud-container to v2.10.6 (#207)
renovate-bot Mar 7, 2022
f9687c6
chore(deps): update dependency pytest to v7.1.0 (#209)
renovate-bot Mar 13, 2022
cae7f4d
chore(deps): update dependency pytest to v7.1.1 (#211)
renovate-bot Mar 19, 2022
72c72db
fix: test cleanup stages with try finally (#212)
Shabirmean Mar 22, 2022
f9080ce
chore(deps): update dependency google-cloud-container to v2.10.7 (#215)
renovate-bot Mar 22, 2022
c93f8df
chore(python): use black==22.3.0 (#217)
gcf-owl-bot[bot] Mar 28, 2022
1127deb
chore(python): add nox session to sort python imports (#229)
gcf-owl-bot[bot] Apr 21, 2022
bdc99b0
chore(deps): update dependency pytest to v7.1.2 (#232)
renovate-bot Apr 25, 2022
7920f17
chore(deps): update dependency backoff to v2 (#233)
renovate-bot Apr 26, 2022
48b42ae
chore(deps): update dependency backoff to v2.0.1 (#235)
renovate-bot Apr 27, 2022
0d6ae6c
fix: require python 3.7+ (#266)
gcf-owl-bot[bot] Jul 10, 2022
e79b44e
chore(deps): update all dependencies (#258)
renovate-bot Jul 15, 2022
73bc1bd
chore(deps): update all dependencies (#271)
renovate-bot Aug 2, 2022
40614e6
chore(deps): update all dependencies to v2.11.1 (#276)
renovate-bot Aug 10, 2022
ccf1085
chore(deps): update dependency google-cloud-container to v2.11.2 (#280)
renovate-bot Aug 16, 2022
bf9b246
chore(deps): update dependency pytest to v7.1.3 (#291)
renovate-bot Sep 6, 2022
490f770
chore: detect samples tests in nested directories (#295)
gcf-owl-bot[bot] Sep 13, 2022
3e78978
chore(deps): update dependency google-cloud-container to v2.12.0 (#299)
renovate-bot Sep 20, 2022
8f1a926
chore(deps): update dependency google-cloud-container to v2.12.1 (#303)
renovate-bot Oct 4, 2022
566162e
chore(deps): update dependency backoff to v2.2.1 (#304)
renovate-bot Oct 6, 2022
fa3cac8
chore(deps): update dependency google-cloud-container to v2.12.2 (#308)
renovate-bot Oct 11, 2022
857a2ad
chore(deps): update dependency pytest to v7.2.0 (#310)
renovate-bot Oct 26, 2022
b2d82b8
chore(deps): update dependency google-cloud-container to v2.13.0 (#311)
renovate-bot Oct 28, 2022
5a680f1
Merge remote-tracking branch 'migration/main' into python-container-m…
donmccasland Nov 2, 2022
ac3c355
Updating license headers
donmccasland Nov 2, 2022
07e128e
Merge branch 'main' into python-container-migration
nicain Nov 3, 2022
f282fe2
Update container/snippets/noxfile.py
donmccasland Nov 3, 2022
53e3bce
lint fix
donmccasland Nov 3, 2022
10625cf
Adding CODEOWNERS definition
donmccasland Nov 3, 2022
583de91
Update container/CONTRIBUTING.md
donmccasland Nov 3, 2022
df1d2c0
Update container/AUTHORING_GUIDE.md
donmccasland Nov 3, 2022
0621d06
Removing noxfile
donmccasland Nov 3, 2022
0adad32
Adding blunderbuss
donmccasland Nov 3, 2022
933dd05
Merge branch 'main' into python-container-migration
donmccasland Nov 3, 2022
d9de181
Merge branch 'main' into python-container-migration
kweinmeister Nov 4, 2022
444c340
Extending test timeout to 1 hour because cluster creation takes forever
donmccasland Nov 4, 2022
526fcc0
Merge branch 'main' into python-container-migration
dandhlee Nov 5, 2022
14ecc9f
Merge branch 'main' into python-container-migration
dandhlee Nov 7, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
/codelabs/**/* @GoogleCloudPlatform/python-samples-reviewers
/composer/**/* @leahecole @rachael-ds @rafalbiegacz @GoogleCloudPlatform/python-samples-reviewers
/compute/**/* @m-strzelczyk @GoogleCloudPlatform/python-samples-reviewers
/container/**/* @GoogleCloudPlatform/dee-platform-ops @GoogleCloudPlatform/python-samples-reviewers
donmccasland marked this conversation as resolved.
Show resolved Hide resolved
/data-science-onramp/ @leahecole @bradmiro @GoogleCloudPlatform/python-samples-reviewers
/dataflow/**/* @davidcavazos @GoogleCloudPlatform/python-samples-reviewers
/datastore/**/* @GoogleCloudPlatform/cloud-native-db-dpes @GoogleCloudPlatform/python-samples-reviewers
Expand Down
4 changes: 4 additions & 0 deletions .github/blunderbuss.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ assign_issues_by:
- 'api: compute'
to:
- m-strzelczyk
- labels:
- 'api: container'
to:
- GoogleCloudPlatform/dee-platform-ops
- labels:
- 'api: datascienceonramp'
to:
Expand Down
2 changes: 1 addition & 1 deletion .kokoro/tests/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ test_prog="${PROJECT_ROOT}/.kokoro/tests/run_single_test.sh"

btlr_args=(
"run"
"--max-cmd-duration=30m"
"--max-cmd-duration=60m"
"**/requirements.txt"
)

Expand Down
1 change: 1 addition & 0 deletions container/AUTHORING_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
See https://github.com/GoogleCloudPlatform/python-docs-samples/blob/main/AUTHORING_GUIDE.md
1 change: 1 addition & 0 deletions container/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
See https://github.com/GoogleCloudPlatform/python-docs-samples/blob/main/CONTRIBUTING.md
45 changes: 45 additions & 0 deletions container/snippets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# 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 <sample_name.py> <arg1> <arg2> ...
```

You can run the following command to find the usage and arguments for the samples:

```python
python <sample_name.py> -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.
112 changes: 112 additions & 0 deletions container/snippets/create_cluster.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# -*- 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)
72 changes: 72 additions & 0 deletions container/snippets/create_cluster_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# -*- 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
105 changes: 105 additions & 0 deletions container/snippets/delete_cluster.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# -*- 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)
Loading