From cc1094e281a73fb26e17bdc7d2e55fddcdfb38af Mon Sep 17 00:00:00 2001 From: Maciej Strzelczyk Date: Wed, 13 Apr 2022 16:31:00 +0200 Subject: [PATCH] docs(samples): Add samples for suspending/resuming an instance (#259) * chore(samples): Adding samples for suspend/resume * Fixing lint problems --- .../compute/ingredients/instances/resume.py | 52 ++++++++++++++ .../compute/ingredients/instances/suspend.py | 47 +++++++++++++ compute/compute/recipes/instances/resume.py | 21 ++++++ compute/compute/recipes/instances/suspend.py | 21 ++++++ compute/compute/snippets/instances/resume.py | 60 ++++++++++++++++ compute/compute/snippets/instances/suspend.py | 51 ++++++++++++++ .../tests/test_instance_suspend_resume.py | 69 +++++++++++++++++++ 7 files changed, 321 insertions(+) create mode 100644 compute/compute/ingredients/instances/resume.py create mode 100644 compute/compute/ingredients/instances/suspend.py create mode 100644 compute/compute/recipes/instances/resume.py create mode 100644 compute/compute/recipes/instances/suspend.py create mode 100644 compute/compute/snippets/instances/resume.py create mode 100644 compute/compute/snippets/instances/suspend.py create mode 100644 compute/compute/snippets/tests/test_instance_suspend_resume.py diff --git a/compute/compute/ingredients/instances/resume.py b/compute/compute/ingredients/instances/resume.py new file mode 100644 index 000000000000..5762797b396f --- /dev/null +++ b/compute/compute/ingredients/instances/resume.py @@ -0,0 +1,52 @@ +# 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. + +# This is an ingredient file. It is not meant to be run directly. Check the samples/snippets +# folder for complete code samples that are ready to be used. +# Disabling flake8 for the ingredients file, as it would fail F821 - undefined name check. +# flake8: noqa +import time + +from google.cloud import compute_v1 + + +# +def resume_instance(project_id: str, zone: str, instance_name: str) -> None: + """ + Resume a suspended Google Compute Engine instance (with unencrypted disks). + Args: + project_id: project ID or project number of the Cloud project your instance belongs to. + zone: name of the zone your instance belongs to. + instance_name: name of the instance your want to resume. + """ + instance_client = compute_v1.InstancesClient() + op_client = compute_v1.ZoneOperationsClient() + + instance = instance_client.get(project=project_id, zone=zone, instance=instance_name) + if instance.status != compute_v1.Instance.Status.SUSPENDED.name: + raise RuntimeError(f"Only suspended instances can be resumed. " + f"Instance {instance_name} is in {instance.status} state.") + + op = instance_client.resume_unary( + project=project_id, zone=zone, instance=instance_name + ) + + start = time.time() + while op.status != compute_v1.Operation.Status.DONE: + op = op_client.wait(operation=op.name, zone=zone, project=project_id) + if time.time() - start >= 300: # 5 minutes + raise TimeoutError() + return +# + diff --git a/compute/compute/ingredients/instances/suspend.py b/compute/compute/ingredients/instances/suspend.py new file mode 100644 index 000000000000..50550d112201 --- /dev/null +++ b/compute/compute/ingredients/instances/suspend.py @@ -0,0 +1,47 @@ +# 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. + +# This is an ingredient file. It is not meant to be run directly. Check the samples/snippets +# folder for complete code samples that are ready to be used. +# Disabling flake8 for the ingredients file, as it would fail F821 - undefined name check. +# flake8: noqa +import time + +from google.cloud import compute_v1 + + +# +def suspend_instance(project_id: str, zone: str, instance_name: str) -> None: + """ + Suspend a running Google Compute Engine instance. + Args: + project_id: project ID or project number of the Cloud project your instance belongs to. + zone: name of the zone your instance belongs to. + instance_name: name of the instance your want to suspend. + """ + instance_client = compute_v1.InstancesClient() + op_client = compute_v1.ZoneOperationsClient() + + op = instance_client.suspend_unary( + project=project_id, zone=zone, instance=instance_name + ) + + start = time.time() + while op.status != compute_v1.Operation.Status.DONE: + op = op_client.wait(operation=op.name, zone=zone, project=project_id) + if time.time() - start >= 300: # 5 minutes + raise TimeoutError() + return +# + diff --git a/compute/compute/recipes/instances/resume.py b/compute/compute/recipes/instances/resume.py new file mode 100644 index 000000000000..0165ccb9a381 --- /dev/null +++ b/compute/compute/recipes/instances/resume.py @@ -0,0 +1,21 @@ +# 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. +# flake8: noqa + +# +# + + +# +# diff --git a/compute/compute/recipes/instances/suspend.py b/compute/compute/recipes/instances/suspend.py new file mode 100644 index 000000000000..5f3baa0be14e --- /dev/null +++ b/compute/compute/recipes/instances/suspend.py @@ -0,0 +1,21 @@ +# 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. +# flake8: noqa + +# +# + + +# +# diff --git a/compute/compute/snippets/instances/resume.py b/compute/compute/snippets/instances/resume.py new file mode 100644 index 000000000000..a55b2fafe93e --- /dev/null +++ b/compute/compute/snippets/instances/resume.py @@ -0,0 +1,60 @@ +# 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. +# flake8: noqa + + +# This file is automatically generated. Please do not modify it directly. +# Find the relevant recipe file in the samples/recipes or samples/ingredients +# directory and apply your changes there. + + +# [START compute_resume_instance] +import time + +from google.cloud import compute_v1 + + +def resume_instance(project_id: str, zone: str, instance_name: str) -> None: + """ + Resume a suspended Google Compute Engine instance (with unencrypted disks). + Args: + project_id: project ID or project number of the Cloud project your instance belongs to. + zone: name of the zone your instance belongs to. + instance_name: name of the instance your want to resume. + """ + instance_client = compute_v1.InstancesClient() + op_client = compute_v1.ZoneOperationsClient() + + instance = instance_client.get( + project=project_id, zone=zone, instance=instance_name + ) + if instance.status != compute_v1.Instance.Status.SUSPENDED.name: + raise RuntimeError( + f"Only suspended instances can be resumed. " + f"Instance {instance_name} is in {instance.status} state." + ) + + op = instance_client.resume_unary( + project=project_id, zone=zone, instance=instance_name + ) + + start = time.time() + while op.status != compute_v1.Operation.Status.DONE: + op = op_client.wait(operation=op.name, zone=zone, project=project_id) + if time.time() - start >= 300: # 5 minutes + raise TimeoutError() + return + + +# [END compute_resume_instance] diff --git a/compute/compute/snippets/instances/suspend.py b/compute/compute/snippets/instances/suspend.py new file mode 100644 index 000000000000..4427176a03af --- /dev/null +++ b/compute/compute/snippets/instances/suspend.py @@ -0,0 +1,51 @@ +# 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. +# flake8: noqa + + +# This file is automatically generated. Please do not modify it directly. +# Find the relevant recipe file in the samples/recipes or samples/ingredients +# directory and apply your changes there. + + +# [START compute_suspend_instance] +import time + +from google.cloud import compute_v1 + + +def suspend_instance(project_id: str, zone: str, instance_name: str) -> None: + """ + Suspend a running Google Compute Engine instance. + Args: + project_id: project ID or project number of the Cloud project your instance belongs to. + zone: name of the zone your instance belongs to. + instance_name: name of the instance your want to suspend. + """ + instance_client = compute_v1.InstancesClient() + op_client = compute_v1.ZoneOperationsClient() + + op = instance_client.suspend_unary( + project=project_id, zone=zone, instance=instance_name + ) + + start = time.time() + while op.status != compute_v1.Operation.Status.DONE: + op = op_client.wait(operation=op.name, zone=zone, project=project_id) + if time.time() - start >= 300: # 5 minutes + raise TimeoutError() + return + + +# [END compute_suspend_instance] diff --git a/compute/compute/snippets/tests/test_instance_suspend_resume.py b/compute/compute/snippets/tests/test_instance_suspend_resume.py new file mode 100644 index 000000000000..a6f2ca103f0f --- /dev/null +++ b/compute/compute/snippets/tests/test_instance_suspend_resume.py @@ -0,0 +1,69 @@ +# 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 time +import uuid + +import google.auth +from google.cloud import compute_v1 +import pytest + + +from ..images.get import get_image_from_family +from ..instances.create import create_instance, disk_from_image +from ..instances.delete import delete_instance +from ..instances.resume import resume_instance +from ..instances.suspend import suspend_instance + +PROJECT = google.auth.default()[1] + +INSTANCE_ZONE = "europe-central2-b" + + +def _get_status(instance: compute_v1.Instance) -> compute_v1.Instance.Status: + instance_client = compute_v1.InstancesClient() + return instance_client.get( + project=PROJECT, zone=INSTANCE_ZONE, instance=instance.name + ).status + + +@pytest.fixture +def compute_instance(): + instance_name = "test-instance-" + uuid.uuid4().hex[:10] + newest_debian = get_image_from_family(project="ubuntu-os-cloud", family="ubuntu-2004-lts") + disk_type = f"zones/{INSTANCE_ZONE}/diskTypes/pd-standard" + disks = [disk_from_image(disk_type, 100, True, newest_debian.self_link)] + instance = create_instance( + PROJECT, INSTANCE_ZONE, instance_name, disks + ) + yield instance + + delete_instance(PROJECT, INSTANCE_ZONE, instance_name) + + +def test_instance_suspend_resume(compute_instance): + assert _get_status(compute_instance) == compute_v1.Instance.Status.RUNNING.name + + # Once the machine is running, give it some time to fully start all processes + # before trying to suspend it + time.sleep(45) + + suspend_instance(PROJECT, INSTANCE_ZONE, compute_instance.name) + + while _get_status(compute_instance) == compute_v1.Instance.Status.SUSPENDING.name: + time.sleep(5) + + assert _get_status(compute_instance) == compute_v1.Instance.Status.SUSPENDED.name + + resume_instance(PROJECT, INSTANCE_ZONE, compute_instance.name) + assert _get_status(compute_instance) == compute_v1.Instance.Status.RUNNING.name