From 664e81a669538918e5ebac16197850dd03b91e92 Mon Sep 17 00:00:00 2001 From: Maciej Strzelczyk Date: Wed, 10 Aug 2022 15:47:37 +0200 Subject: [PATCH] docs(samples): Various ways of creating a new disk (#308) * docs(samples): Samples for various ways to create disks Co-authored-by: Anthonios Partheniou Co-authored-by: Savija Vijayaraghavan --- .../ingredients/disks/clone_encrypted_disk.py | 64 +++++++++ .../disks/clone_encrypted_disk_managed_key.py | 65 +++++++++ .../ingredients/disks/create_from_snapshot.py | 1 - .../ingredients/disks/create_from_source.py | 55 ++++++++ .../disks/create_kms_encrypted_disk.py | 70 ++++++++++ .../disks/regional_create_from_source.py | 68 +++++++++ .../ingredients/disks/regional_delete.py | 39 ++++++ .../recipes/disks/clone_encrypted_disk.py | 23 ++++ .../disks/clone_encrypted_disk_managed_key.py | 23 ++++ .../recipes/disks/create_from_source.py | 23 ++++ .../disks/create_kms_encrypted_disk.py | 24 ++++ .../disks/regional_create_from_source.py | 23 ++++ .../compute/recipes/disks/regional_delete.py | 24 ++++ compute/compute/requirements-test.txt | 1 + .../snippets/disks/clone_encrypted_disk.py | 124 +++++++++++++++++ .../disks/clone_encrypted_disk_managed_key.py | 125 +++++++++++++++++ .../snippets/disks/create_from_source.py | 116 ++++++++++++++++ .../disks/create_kms_encrypted_disk.py | 130 ++++++++++++++++++ .../disks/regional_create_from_source.py | 129 +++++++++++++++++ .../compute/snippets/disks/regional_delete.py | 92 +++++++++++++ compute/compute/snippets/tests/test_disks.py | 116 ++++++++++++++++ .../tests/test_instance_from_template.py | 1 + .../tests/test_instance_start_stop.py | 21 +++ 23 files changed, 1356 insertions(+), 1 deletion(-) create mode 100644 compute/compute/ingredients/disks/clone_encrypted_disk.py create mode 100644 compute/compute/ingredients/disks/clone_encrypted_disk_managed_key.py create mode 100644 compute/compute/ingredients/disks/create_from_source.py create mode 100644 compute/compute/ingredients/disks/create_kms_encrypted_disk.py create mode 100644 compute/compute/ingredients/disks/regional_create_from_source.py create mode 100644 compute/compute/ingredients/disks/regional_delete.py create mode 100644 compute/compute/recipes/disks/clone_encrypted_disk.py create mode 100644 compute/compute/recipes/disks/clone_encrypted_disk_managed_key.py create mode 100644 compute/compute/recipes/disks/create_from_source.py create mode 100644 compute/compute/recipes/disks/create_kms_encrypted_disk.py create mode 100644 compute/compute/recipes/disks/regional_create_from_source.py create mode 100644 compute/compute/recipes/disks/regional_delete.py create mode 100644 compute/compute/snippets/disks/clone_encrypted_disk.py create mode 100644 compute/compute/snippets/disks/clone_encrypted_disk_managed_key.py create mode 100644 compute/compute/snippets/disks/create_from_source.py create mode 100644 compute/compute/snippets/disks/create_kms_encrypted_disk.py create mode 100644 compute/compute/snippets/disks/regional_create_from_source.py create mode 100644 compute/compute/snippets/disks/regional_delete.py diff --git a/compute/compute/ingredients/disks/clone_encrypted_disk.py b/compute/compute/ingredients/disks/clone_encrypted_disk.py new file mode 100644 index 000000000000..2df729de6932 --- /dev/null +++ b/compute/compute/ingredients/disks/clone_encrypted_disk.py @@ -0,0 +1,64 @@ +# 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 + +from google.cloud import compute_v1 + + +# +def create_disk_from_customer_encrypted_disk( + project_id: str, zone: str, disk_name: str, disk_type: str, + disk_size_gb: int, disk_link: str, + encryption_key: bytes) -> compute_v1.Disk: + """ + Creates a zonal non-boot persistent disk in a project with the copy of data from an existing disk. + + The encryption key must be the same for the source disk and the new disk. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + zone: name of the zone in which you want to create the disk. + disk_name: name of the disk you want to create. + disk_type: the type of disk you want to create. This value uses the following format: + "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)". + For example: "zones/us-west3-b/diskTypes/pd-ssd" + disk_size_gb: size of the new disk in gigabytes + disk_link: a link to the disk you want to use as a source for the new disk. + This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}" + encryption_key: customer-supplied encryption key used for encrypting + data in the source disk. The data will be encrypted with the same key + in the new disk. + + Returns: + An attachable copy of an existing disk. + """ + disk_client = compute_v1.DisksClient() + disk = compute_v1.Disk() + disk.zone = zone + disk.size_gb = disk_size_gb + disk.source_disk = disk_link + disk.type_ = disk_type + disk.name = disk_name + disk.disk_encryption_key = compute_v1.CustomerEncryptionKey() + disk.disk_encryption_key.raw_key = encryption_key + operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk) + + wait_for_extended_operation(operation, "disk creation") + + return disk_client.get(project=project_id, zone=zone, disk=disk_name) +# diff --git a/compute/compute/ingredients/disks/clone_encrypted_disk_managed_key.py b/compute/compute/ingredients/disks/clone_encrypted_disk_managed_key.py new file mode 100644 index 000000000000..53ab7baa5a1e --- /dev/null +++ b/compute/compute/ingredients/disks/clone_encrypted_disk_managed_key.py @@ -0,0 +1,65 @@ +# 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 + +from google.cloud import compute_v1 + + +# +def create_disk_from_kms_encrypted_disk( + project_id: str, zone: str, disk_name: str, disk_type: str, + disk_size_gb: int, disk_link: str, + kms_key_name: str) -> compute_v1.Disk: + """ + Creates a zonal non-boot disk in a project with the copy of data from an existing disk. + + The encryption key must be the same for the source disk and the new disk. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + zone: name of the zone in which you want to create the disk. + disk_name: name of the disk you want to create. + disk_type: the type of disk you want to create. This value uses the following format: + "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)". + For example: "zones/us-west3-b/diskTypes/pd-ssd" + disk_size_gb: size of the new disk in gigabytes + disk_link: a link to the disk you want to use as a source for the new disk. + This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}" + kms_key_name: URL of the key from KMS. The key might be from another project, as + long as you have access to it. The data will be encrypted with the same key + in the new disk. This value uses following format: + "projects/{kms_project_id}/locations/{region}/keyRings/{key_ring}/cryptoKeys/{key}" + + Returns: + An attachable copy of an existing disk. + """ + disk_client = compute_v1.DisksClient() + disk = compute_v1.Disk() + disk.zone = zone + disk.size_gb = disk_size_gb + disk.source_disk = disk_link + disk.type_ = disk_type + disk.name = disk_name + disk.disk_encryption_key = compute_v1.CustomerEncryptionKey() + disk.disk_encryption_key.kms_key_name = kms_key_name + operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk) + + wait_for_extended_operation(operation, "disk creation") + + return disk_client.get(project=project_id, zone=zone, disk=disk_name) +# diff --git a/compute/compute/ingredients/disks/create_from_snapshot.py b/compute/compute/ingredients/disks/create_from_snapshot.py index 88a79751204e..a2dd1f49595a 100644 --- a/compute/compute/ingredients/disks/create_from_snapshot.py +++ b/compute/compute/ingredients/disks/create_from_snapshot.py @@ -16,7 +16,6 @@ # 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 sys from google.cloud import compute_v1 diff --git a/compute/compute/ingredients/disks/create_from_source.py b/compute/compute/ingredients/disks/create_from_source.py new file mode 100644 index 000000000000..a7ed3006ceb9 --- /dev/null +++ b/compute/compute/ingredients/disks/create_from_source.py @@ -0,0 +1,55 @@ +# 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 + +from google.cloud import compute_v1 + + +# +def create_disk_from_disk(project_id: str, zone: str, disk_name: str, disk_type: str, + disk_size_gb: int, disk_link: str) -> compute_v1.Disk: + """ + Creates a disk in a project in a given zone. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + zone: name of the zone in which you want to create the disk. + disk_name: name of the disk you want to create. + disk_type: the type of disk you want to create. This value uses the following format: + "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)". + For example: "zones/us-west3-b/diskTypes/pd-ssd" + disk_size_gb: size of the new disk in gigabytes + disk_link: a link to the disk you want to use as a source for the new disk. + This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}" + + Returns: + An attachable disk. + """ + disk_client = compute_v1.DisksClient() + disk = compute_v1.Disk() + disk.zone = zone + disk.size_gb = disk_size_gb + disk.source_disk = disk_link + disk.type_ = disk_type + disk.name = disk_name + operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk) + + wait_for_extended_operation(operation, "disk creation") + + return disk_client.get(project=project_id, zone=zone, disk=disk_name) +# diff --git a/compute/compute/ingredients/disks/create_kms_encrypted_disk.py b/compute/compute/ingredients/disks/create_kms_encrypted_disk.py new file mode 100644 index 000000000000..7078ea6b6b62 --- /dev/null +++ b/compute/compute/ingredients/disks/create_kms_encrypted_disk.py @@ -0,0 +1,70 @@ +# 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 +from typing import Optional + +from google.cloud import compute_v1 + + +# +def create_kms_encrypted_disk(project_id: str, zone: str, disk_name: str, disk_type: str, + disk_size_gb: int, kms_key_name: str, + disk_link: Optional[str] = None, image_link: Optional[str] = None) -> compute_v1.Disk: + """ + Creates a zonal disk in a project. If you do not provide values for disk_link or image_link, + an empty disk will be created. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + zone: name of the zone in which you want to create the disk. + disk_name: name of the disk you want to create. + disk_type: the type of disk you want to create. This value uses the following format: + "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)". + For example: "zones/us-west3-b/diskTypes/pd-ssd" + disk_size_gb: size of the new disk in gigabytes + kms_key_name: URL of the key from KMS. The key might be from another project, as + long as you have access to it. The data will be encrypted with the same key + in the new disk. This value uses following format: + "projects/{kms_project_id}/locations/{region}/keyRings/{key_ring}/cryptoKeys/{key}" + disk_link: a link to the disk you want to use as a source for the new disk. + This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}" + image_link: a link to the image you want to use as a source for the new disk. + This value uses the following format: "projects/{project_name}/global/images/{image_name}" + + Returns: + An attachable disk. + """ + disk_client = compute_v1.DisksClient() + disk = compute_v1.Disk() + disk.zone = zone + disk.size_gb = disk_size_gb + if disk_link: + disk.source_disk = disk_link + if image_link: + disk.source_image = image_link + disk.type_ = disk_type + disk.name = disk_name + disk.disk_encryption_key = compute_v1.CustomerEncryptionKey() + disk.disk_encryption_key.kms_key_name = kms_key_name + operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk) + + wait_for_extended_operation(operation, "disk creation") + + return disk_client.get(project=project_id, zone=zone, disk=disk_name) + +# diff --git a/compute/compute/ingredients/disks/regional_create_from_source.py b/compute/compute/ingredients/disks/regional_create_from_source.py new file mode 100644 index 000000000000..af6757d7efbd --- /dev/null +++ b/compute/compute/ingredients/disks/regional_create_from_source.py @@ -0,0 +1,68 @@ +# 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 +from typing import Iterable, Optional + +from google.cloud import compute_v1 + + +# +def create_regional_disk(project_id: str, region: str, replica_zones: Iterable[str], + disk_name: str, disk_type: str, + disk_size_gb: int, + disk_link: Optional[str] = None, + snapshot_link: Optional[str] = None) -> compute_v1.Disk: + """ + Creates a regional disk from an existing zonal disk in a given project. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + region: name of the region in which you want to create the disk. + replica_zones: an iterable collection of zone names in which you want to keep + the new disks' replicas. One of the replica zones of the clone must match + the zone of the source disk. + disk_name: name of the disk you want to create. + disk_type: the type of disk you want to create. This value uses the following format: + "regions/{region}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)". + For example: "regions/us-west3/diskTypes/pd-ssd" + disk_size_gb: size of the new disk in gigabytes + disk_link: a link to the disk you want to use as a source for the new disk. + This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}" + snapshot_link: a link to the snapshot you want to use as a source for the new disk. + This value uses the following format: "projects/{project_name}/global/snapshots/{snapshot_name}" + + Returns: + An attachable regional disk. + """ + disk_client = compute_v1.RegionDisksClient() + disk = compute_v1.Disk() + disk.replica_zones = replica_zones + disk.size_gb = disk_size_gb + if disk_link: + disk.source_disk = disk_link + if snapshot_link: + disk.source_snapshot = snapshot_link + disk.type_ = disk_type + disk.region = region + disk.name = disk_name + operation = disk_client.insert(project=project_id, region=region, disk_resource=disk) + + wait_for_extended_operation(operation, "disk creation") + + return disk_client.get(project=project_id, region=region, disk=disk_name) +# diff --git a/compute/compute/ingredients/disks/regional_delete.py b/compute/compute/ingredients/disks/regional_delete.py new file mode 100644 index 000000000000..0bc2f59d87a6 --- /dev/null +++ b/compute/compute/ingredients/disks/regional_delete.py @@ -0,0 +1,39 @@ +# 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 sys +from typing import NoReturn + +from google.cloud import compute_v1 + + +# +def delete_regional_disk(project_id: str, region: str, disk_name: str) -> NoReturn: + """ + Deletes a disk from a project. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + region:name of the region where the disk is located. + disk_name: name of the disk that you want to delete. + """ + disk_client = compute_v1.RegionDisksClient() + operation = disk_client.delete(project=project_id, region=region, disk=disk_name) + wait_for_extended_operation(operation, "regional disk deletion") + return +# diff --git a/compute/compute/recipes/disks/clone_encrypted_disk.py b/compute/compute/recipes/disks/clone_encrypted_disk.py new file mode 100644 index 000000000000..9ce0ddf795de --- /dev/null +++ b/compute/compute/recipes/disks/clone_encrypted_disk.py @@ -0,0 +1,23 @@ +# 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/disks/clone_encrypted_disk_managed_key.py b/compute/compute/recipes/disks/clone_encrypted_disk_managed_key.py new file mode 100644 index 000000000000..d4b100af644f --- /dev/null +++ b/compute/compute/recipes/disks/clone_encrypted_disk_managed_key.py @@ -0,0 +1,23 @@ +# 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/disks/create_from_source.py b/compute/compute/recipes/disks/create_from_source.py new file mode 100644 index 000000000000..f40f561ec3f9 --- /dev/null +++ b/compute/compute/recipes/disks/create_from_source.py @@ -0,0 +1,23 @@ +# 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/disks/create_kms_encrypted_disk.py b/compute/compute/recipes/disks/create_kms_encrypted_disk.py new file mode 100644 index 000000000000..06cc0d049d75 --- /dev/null +++ b/compute/compute/recipes/disks/create_kms_encrypted_disk.py @@ -0,0 +1,24 @@ +# 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/disks/regional_create_from_source.py b/compute/compute/recipes/disks/regional_create_from_source.py new file mode 100644 index 000000000000..aceeaa99c9f4 --- /dev/null +++ b/compute/compute/recipes/disks/regional_create_from_source.py @@ -0,0 +1,23 @@ +# 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/disks/regional_delete.py b/compute/compute/recipes/disks/regional_delete.py new file mode 100644 index 000000000000..986f7dfcda63 --- /dev/null +++ b/compute/compute/recipes/disks/regional_delete.py @@ -0,0 +1,24 @@ +# 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/requirements-test.txt b/compute/compute/requirements-test.txt index d666116a4c03..d7c6a629cf09 100644 --- a/compute/compute/requirements-test.txt +++ b/compute/compute/requirements-test.txt @@ -2,3 +2,4 @@ pytest==7.1.2 pytest-parallel==0.1.1 flaky==3.7.0 google-cloud-storage==2.5.0 +google-cloud-kms==2.12.0 diff --git a/compute/compute/snippets/disks/clone_encrypted_disk.py b/compute/compute/snippets/disks/clone_encrypted_disk.py new file mode 100644 index 000000000000..5850656e2b83 --- /dev/null +++ b/compute/compute/snippets/disks/clone_encrypted_disk.py @@ -0,0 +1,124 @@ +# 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_disk_clone_encrypted_disk] +import sys +from typing import Any + +from google.api_core.extended_operation import ExtendedOperation +from google.cloud import compute_v1 + + +def wait_for_extended_operation( + operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300 +) -> Any: + """ + This method will wait for the extended (long-running) operation to + complete. If the operation is successful, it will return its result. + If the operation ends with an error, an exception will be raised. + If there were any warnings during the execution of the operation + they will be printed to sys.stderr. + + Args: + operation: a long-running operation you want to wait on. + verbose_name: (optional) a more verbose name of the operation, + used only during error and warning reporting. + timeout: how long (in seconds) to wait for operation to finish. + If None, wait indefinitely. + + Returns: + Whatever the operation.result() returns. + + Raises: + This method will raise the exception received from `operation.exception()` + or RuntimeError if there is no exception set, but there is an `error_code` + set for the `operation`. + + In case of an operation taking longer than `timeout` seconds to complete, + a `concurrent.futures.TimeoutError` will be raised. + """ + result = operation.result(timeout=timeout) + + if operation.error_code: + print( + f"Error during {verbose_name}: [Code: {operation.error_code}]: {operation.error_message}", + file=sys.stderr, + flush=True, + ) + print(f"Operation ID: {operation.name}", file=sys.stderr, flush=True) + raise operation.exception() or RuntimeError(operation.error_message) + + if operation.warnings: + print(f"Warnings during {verbose_name}:\n", file=sys.stderr, flush=True) + for warning in operation.warnings: + print(f" - {warning.code}: {warning.message}", file=sys.stderr, flush=True) + + return result + + +def create_disk_from_customer_encrypted_disk( + project_id: str, + zone: str, + disk_name: str, + disk_type: str, + disk_size_gb: int, + disk_link: str, + encryption_key: bytes, +) -> compute_v1.Disk: + """ + Creates a zonal non-boot persistent disk in a project with the copy of data from an existing disk. + + The encryption key must be the same for the source disk and the new disk. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + zone: name of the zone in which you want to create the disk. + disk_name: name of the disk you want to create. + disk_type: the type of disk you want to create. This value uses the following format: + "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)". + For example: "zones/us-west3-b/diskTypes/pd-ssd" + disk_size_gb: size of the new disk in gigabytes + disk_link: a link to the disk you want to use as a source for the new disk. + This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}" + encryption_key: customer-supplied encryption key used for encrypting + data in the source disk. The data will be encrypted with the same key + in the new disk. + + Returns: + An attachable copy of an existing disk. + """ + disk_client = compute_v1.DisksClient() + disk = compute_v1.Disk() + disk.zone = zone + disk.size_gb = disk_size_gb + disk.source_disk = disk_link + disk.type_ = disk_type + disk.name = disk_name + disk.disk_encryption_key = compute_v1.CustomerEncryptionKey() + disk.disk_encryption_key.raw_key = encryption_key + operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk) + + wait_for_extended_operation(operation, "disk creation") + + return disk_client.get(project=project_id, zone=zone, disk=disk_name) + + +# [END compute_disk_clone_encrypted_disk] diff --git a/compute/compute/snippets/disks/clone_encrypted_disk_managed_key.py b/compute/compute/snippets/disks/clone_encrypted_disk_managed_key.py new file mode 100644 index 000000000000..8f89d191be51 --- /dev/null +++ b/compute/compute/snippets/disks/clone_encrypted_disk_managed_key.py @@ -0,0 +1,125 @@ +# 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_disk_clone_encrypted_disk_kms] +import sys +from typing import Any + +from google.api_core.extended_operation import ExtendedOperation +from google.cloud import compute_v1 + + +def wait_for_extended_operation( + operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300 +) -> Any: + """ + This method will wait for the extended (long-running) operation to + complete. If the operation is successful, it will return its result. + If the operation ends with an error, an exception will be raised. + If there were any warnings during the execution of the operation + they will be printed to sys.stderr. + + Args: + operation: a long-running operation you want to wait on. + verbose_name: (optional) a more verbose name of the operation, + used only during error and warning reporting. + timeout: how long (in seconds) to wait for operation to finish. + If None, wait indefinitely. + + Returns: + Whatever the operation.result() returns. + + Raises: + This method will raise the exception received from `operation.exception()` + or RuntimeError if there is no exception set, but there is an `error_code` + set for the `operation`. + + In case of an operation taking longer than `timeout` seconds to complete, + a `concurrent.futures.TimeoutError` will be raised. + """ + result = operation.result(timeout=timeout) + + if operation.error_code: + print( + f"Error during {verbose_name}: [Code: {operation.error_code}]: {operation.error_message}", + file=sys.stderr, + flush=True, + ) + print(f"Operation ID: {operation.name}", file=sys.stderr, flush=True) + raise operation.exception() or RuntimeError(operation.error_message) + + if operation.warnings: + print(f"Warnings during {verbose_name}:\n", file=sys.stderr, flush=True) + for warning in operation.warnings: + print(f" - {warning.code}: {warning.message}", file=sys.stderr, flush=True) + + return result + + +def create_disk_from_kms_encrypted_disk( + project_id: str, + zone: str, + disk_name: str, + disk_type: str, + disk_size_gb: int, + disk_link: str, + kms_key_name: str, +) -> compute_v1.Disk: + """ + Creates a zonal non-boot disk in a project with the copy of data from an existing disk. + + The encryption key must be the same for the source disk and the new disk. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + zone: name of the zone in which you want to create the disk. + disk_name: name of the disk you want to create. + disk_type: the type of disk you want to create. This value uses the following format: + "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)". + For example: "zones/us-west3-b/diskTypes/pd-ssd" + disk_size_gb: size of the new disk in gigabytes + disk_link: a link to the disk you want to use as a source for the new disk. + This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}" + kms_key_name: URL of the key from KMS. The key might be from another project, as + long as you have access to it. The data will be encrypted with the same key + in the new disk. This value uses following format: + "projects/{kms_project_id}/locations/{region}/keyRings/{key_ring}/cryptoKeys/{key}" + + Returns: + An attachable copy of an existing disk. + """ + disk_client = compute_v1.DisksClient() + disk = compute_v1.Disk() + disk.zone = zone + disk.size_gb = disk_size_gb + disk.source_disk = disk_link + disk.type_ = disk_type + disk.name = disk_name + disk.disk_encryption_key = compute_v1.CustomerEncryptionKey() + disk.disk_encryption_key.kms_key_name = kms_key_name + operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk) + + wait_for_extended_operation(operation, "disk creation") + + return disk_client.get(project=project_id, zone=zone, disk=disk_name) + + +# [END compute_disk_clone_encrypted_disk_kms] diff --git a/compute/compute/snippets/disks/create_from_source.py b/compute/compute/snippets/disks/create_from_source.py new file mode 100644 index 000000000000..85a1d38254e5 --- /dev/null +++ b/compute/compute/snippets/disks/create_from_source.py @@ -0,0 +1,116 @@ +# 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_disk_create_from_disk] +import sys +from typing import Any + +from google.api_core.extended_operation import ExtendedOperation +from google.cloud import compute_v1 + + +def wait_for_extended_operation( + operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300 +) -> Any: + """ + This method will wait for the extended (long-running) operation to + complete. If the operation is successful, it will return its result. + If the operation ends with an error, an exception will be raised. + If there were any warnings during the execution of the operation + they will be printed to sys.stderr. + + Args: + operation: a long-running operation you want to wait on. + verbose_name: (optional) a more verbose name of the operation, + used only during error and warning reporting. + timeout: how long (in seconds) to wait for operation to finish. + If None, wait indefinitely. + + Returns: + Whatever the operation.result() returns. + + Raises: + This method will raise the exception received from `operation.exception()` + or RuntimeError if there is no exception set, but there is an `error_code` + set for the `operation`. + + In case of an operation taking longer than `timeout` seconds to complete, + a `concurrent.futures.TimeoutError` will be raised. + """ + result = operation.result(timeout=timeout) + + if operation.error_code: + print( + f"Error during {verbose_name}: [Code: {operation.error_code}]: {operation.error_message}", + file=sys.stderr, + flush=True, + ) + print(f"Operation ID: {operation.name}", file=sys.stderr, flush=True) + raise operation.exception() or RuntimeError(operation.error_message) + + if operation.warnings: + print(f"Warnings during {verbose_name}:\n", file=sys.stderr, flush=True) + for warning in operation.warnings: + print(f" - {warning.code}: {warning.message}", file=sys.stderr, flush=True) + + return result + + +def create_disk_from_disk( + project_id: str, + zone: str, + disk_name: str, + disk_type: str, + disk_size_gb: int, + disk_link: str, +) -> compute_v1.Disk: + """ + Creates a disk in a project in a given zone. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + zone: name of the zone in which you want to create the disk. + disk_name: name of the disk you want to create. + disk_type: the type of disk you want to create. This value uses the following format: + "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)". + For example: "zones/us-west3-b/diskTypes/pd-ssd" + disk_size_gb: size of the new disk in gigabytes + disk_link: a link to the disk you want to use as a source for the new disk. + This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}" + + Returns: + An attachable disk. + """ + disk_client = compute_v1.DisksClient() + disk = compute_v1.Disk() + disk.zone = zone + disk.size_gb = disk_size_gb + disk.source_disk = disk_link + disk.type_ = disk_type + disk.name = disk_name + operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk) + + wait_for_extended_operation(operation, "disk creation") + + return disk_client.get(project=project_id, zone=zone, disk=disk_name) + + +# [END compute_disk_create_from_disk] diff --git a/compute/compute/snippets/disks/create_kms_encrypted_disk.py b/compute/compute/snippets/disks/create_kms_encrypted_disk.py new file mode 100644 index 000000000000..d3f64377a164 --- /dev/null +++ b/compute/compute/snippets/disks/create_kms_encrypted_disk.py @@ -0,0 +1,130 @@ +# 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_create_kms_encrypted_disk] +import sys +from typing import Any, Optional + +from google.api_core.extended_operation import ExtendedOperation +from google.cloud import compute_v1 + + +def wait_for_extended_operation( + operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300 +) -> Any: + """ + This method will wait for the extended (long-running) operation to + complete. If the operation is successful, it will return its result. + If the operation ends with an error, an exception will be raised. + If there were any warnings during the execution of the operation + they will be printed to sys.stderr. + + Args: + operation: a long-running operation you want to wait on. + verbose_name: (optional) a more verbose name of the operation, + used only during error and warning reporting. + timeout: how long (in seconds) to wait for operation to finish. + If None, wait indefinitely. + + Returns: + Whatever the operation.result() returns. + + Raises: + This method will raise the exception received from `operation.exception()` + or RuntimeError if there is no exception set, but there is an `error_code` + set for the `operation`. + + In case of an operation taking longer than `timeout` seconds to complete, + a `concurrent.futures.TimeoutError` will be raised. + """ + result = operation.result(timeout=timeout) + + if operation.error_code: + print( + f"Error during {verbose_name}: [Code: {operation.error_code}]: {operation.error_message}", + file=sys.stderr, + flush=True, + ) + print(f"Operation ID: {operation.name}", file=sys.stderr, flush=True) + raise operation.exception() or RuntimeError(operation.error_message) + + if operation.warnings: + print(f"Warnings during {verbose_name}:\n", file=sys.stderr, flush=True) + for warning in operation.warnings: + print(f" - {warning.code}: {warning.message}", file=sys.stderr, flush=True) + + return result + + +def create_kms_encrypted_disk( + project_id: str, + zone: str, + disk_name: str, + disk_type: str, + disk_size_gb: int, + kms_key_name: str, + disk_link: Optional[str] = None, + image_link: Optional[str] = None, +) -> compute_v1.Disk: + """ + Creates a zonal disk in a project. If you do not provide values for disk_link or image_link, + an empty disk will be created. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + zone: name of the zone in which you want to create the disk. + disk_name: name of the disk you want to create. + disk_type: the type of disk you want to create. This value uses the following format: + "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)". + For example: "zones/us-west3-b/diskTypes/pd-ssd" + disk_size_gb: size of the new disk in gigabytes + kms_key_name: URL of the key from KMS. The key might be from another project, as + long as you have access to it. The data will be encrypted with the same key + in the new disk. This value uses following format: + "projects/{kms_project_id}/locations/{region}/keyRings/{key_ring}/cryptoKeys/{key}" + disk_link: a link to the disk you want to use as a source for the new disk. + This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}" + image_link: a link to the image you want to use as a source for the new disk. + This value uses the following format: "projects/{project_name}/global/images/{image_name}" + + Returns: + An attachable disk. + """ + disk_client = compute_v1.DisksClient() + disk = compute_v1.Disk() + disk.zone = zone + disk.size_gb = disk_size_gb + if disk_link: + disk.source_disk = disk_link + if image_link: + disk.source_image = image_link + disk.type_ = disk_type + disk.name = disk_name + disk.disk_encryption_key = compute_v1.CustomerEncryptionKey() + disk.disk_encryption_key.kms_key_name = kms_key_name + operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk) + + wait_for_extended_operation(operation, "disk creation") + + return disk_client.get(project=project_id, zone=zone, disk=disk_name) + + +# [END compute_create_kms_encrypted_disk] diff --git a/compute/compute/snippets/disks/regional_create_from_source.py b/compute/compute/snippets/disks/regional_create_from_source.py new file mode 100644 index 000000000000..25a682b391c8 --- /dev/null +++ b/compute/compute/snippets/disks/regional_create_from_source.py @@ -0,0 +1,129 @@ +# 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_regional_disk_create_from_disk] +import sys +from typing import Any, Iterable, Optional + +from google.api_core.extended_operation import ExtendedOperation +from google.cloud import compute_v1 + + +def wait_for_extended_operation( + operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300 +) -> Any: + """ + This method will wait for the extended (long-running) operation to + complete. If the operation is successful, it will return its result. + If the operation ends with an error, an exception will be raised. + If there were any warnings during the execution of the operation + they will be printed to sys.stderr. + + Args: + operation: a long-running operation you want to wait on. + verbose_name: (optional) a more verbose name of the operation, + used only during error and warning reporting. + timeout: how long (in seconds) to wait for operation to finish. + If None, wait indefinitely. + + Returns: + Whatever the operation.result() returns. + + Raises: + This method will raise the exception received from `operation.exception()` + or RuntimeError if there is no exception set, but there is an `error_code` + set for the `operation`. + + In case of an operation taking longer than `timeout` seconds to complete, + a `concurrent.futures.TimeoutError` will be raised. + """ + result = operation.result(timeout=timeout) + + if operation.error_code: + print( + f"Error during {verbose_name}: [Code: {operation.error_code}]: {operation.error_message}", + file=sys.stderr, + flush=True, + ) + print(f"Operation ID: {operation.name}", file=sys.stderr, flush=True) + raise operation.exception() or RuntimeError(operation.error_message) + + if operation.warnings: + print(f"Warnings during {verbose_name}:\n", file=sys.stderr, flush=True) + for warning in operation.warnings: + print(f" - {warning.code}: {warning.message}", file=sys.stderr, flush=True) + + return result + + +def create_regional_disk( + project_id: str, + region: str, + replica_zones: Iterable[str], + disk_name: str, + disk_type: str, + disk_size_gb: int, + disk_link: Optional[str] = None, + snapshot_link: Optional[str] = None, +) -> compute_v1.Disk: + """ + Creates a regional disk from an existing zonal disk in a given project. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + region: name of the region in which you want to create the disk. + replica_zones: an iterable collection of zone names in which you want to keep + the new disks' replicas. One of the replica zones of the clone must match + the zone of the source disk. + disk_name: name of the disk you want to create. + disk_type: the type of disk you want to create. This value uses the following format: + "regions/{region}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)". + For example: "regions/us-west3/diskTypes/pd-ssd" + disk_size_gb: size of the new disk in gigabytes + disk_link: a link to the disk you want to use as a source for the new disk. + This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}" + snapshot_link: a link to the snapshot you want to use as a source for the new disk. + This value uses the following format: "projects/{project_name}/global/snapshots/{snapshot_name}" + + Returns: + An attachable regional disk. + """ + disk_client = compute_v1.RegionDisksClient() + disk = compute_v1.Disk() + disk.replica_zones = replica_zones + disk.size_gb = disk_size_gb + if disk_link: + disk.source_disk = disk_link + if snapshot_link: + disk.source_snapshot = snapshot_link + disk.type_ = disk_type + disk.region = region + disk.name = disk_name + operation = disk_client.insert( + project=project_id, region=region, disk_resource=disk + ) + + wait_for_extended_operation(operation, "disk creation") + + return disk_client.get(project=project_id, region=region, disk=disk_name) + + +# [END compute_regional_disk_create_from_disk] diff --git a/compute/compute/snippets/disks/regional_delete.py b/compute/compute/snippets/disks/regional_delete.py new file mode 100644 index 000000000000..b68bbd96981e --- /dev/null +++ b/compute/compute/snippets/disks/regional_delete.py @@ -0,0 +1,92 @@ +# 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_regional_disk_delete] +import sys +from typing import Any, NoReturn + +from google.api_core.extended_operation import ExtendedOperation +from google.cloud import compute_v1 + + +def wait_for_extended_operation( + operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300 +) -> Any: + """ + This method will wait for the extended (long-running) operation to + complete. If the operation is successful, it will return its result. + If the operation ends with an error, an exception will be raised. + If there were any warnings during the execution of the operation + they will be printed to sys.stderr. + + Args: + operation: a long-running operation you want to wait on. + verbose_name: (optional) a more verbose name of the operation, + used only during error and warning reporting. + timeout: how long (in seconds) to wait for operation to finish. + If None, wait indefinitely. + + Returns: + Whatever the operation.result() returns. + + Raises: + This method will raise the exception received from `operation.exception()` + or RuntimeError if there is no exception set, but there is an `error_code` + set for the `operation`. + + In case of an operation taking longer than `timeout` seconds to complete, + a `concurrent.futures.TimeoutError` will be raised. + """ + result = operation.result(timeout=timeout) + + if operation.error_code: + print( + f"Error during {verbose_name}: [Code: {operation.error_code}]: {operation.error_message}", + file=sys.stderr, + flush=True, + ) + print(f"Operation ID: {operation.name}", file=sys.stderr, flush=True) + raise operation.exception() or RuntimeError(operation.error_message) + + if operation.warnings: + print(f"Warnings during {verbose_name}:\n", file=sys.stderr, flush=True) + for warning in operation.warnings: + print(f" - {warning.code}: {warning.message}", file=sys.stderr, flush=True) + + return result + + +def delete_regional_disk(project_id: str, region: str, disk_name: str) -> NoReturn: + """ + Deletes a disk from a project. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + region:name of the region where the disk is located. + disk_name: name of the disk that you want to delete. + """ + disk_client = compute_v1.RegionDisksClient() + operation = disk_client.delete(project=project_id, region=region, disk=disk_name) + wait_for_extended_operation(operation, "regional disk deletion") + return + + +# [END compute_regional_disk_delete] diff --git a/compute/compute/snippets/tests/test_disks.py b/compute/compute/snippets/tests/test_disks.py index b847bc206eff..75fa2e9867bb 100644 --- a/compute/compute/snippets/tests/test_disks.py +++ b/compute/compute/snippets/tests/test_disks.py @@ -15,15 +15,76 @@ from google.api_core.exceptions import NotFound import google.auth +from google.cloud import kms_v1 import pytest +from ..disks.clone_encrypted_disk_managed_key import \ + create_disk_from_kms_encrypted_disk from ..disks.create_from_image import create_disk_from_image +from ..disks.create_from_source import create_disk_from_disk +from ..disks.create_kms_encrypted_disk import create_kms_encrypted_disk from ..disks.delete import delete_disk from ..disks.list import list_disks +from ..disks.regional_create_from_source import create_regional_disk +from ..disks.regional_delete import delete_regional_disk from ..images.get import get_image_from_family +from ..snapshots.create import create_snapshot +from ..snapshots.delete import delete_snapshot PROJECT = google.auth.default()[1] ZONE = 'europe-north1-c' +REGION = 'europe-central2' +KMS_KEYRING_NAME = 'compute-test-keyring' +KMS_KEY_NAME = 'compute-test-key' + + +@pytest.fixture() +def kms_key(): + client = kms_v1.KeyManagementServiceClient() + location = f"projects/{PROJECT}/locations/global" + keyring_link = f"projects/{PROJECT}/locations/global/keyRings/{KMS_KEYRING_NAME}" + key_name = f"{keyring_link}/cryptoKeys/{KMS_KEY_NAME}" + + for ring in client.list_key_rings(parent=location): + if ring.name == keyring_link: + break + else: + client.create_key_ring(parent=location, key_ring_id=KMS_KEYRING_NAME) + + for key in client.list_crypto_keys(parent=keyring_link): + if key.name == key_name: + break + else: + key = kms_v1.CryptoKey() + key.purpose = key.CryptoKeyPurpose.ENCRYPT_DECRYPT + client.create_crypto_key(parent=keyring_link, crypto_key_id=KMS_KEY_NAME, crypto_key=key) + + yield client.get_crypto_key(name=key_name) + + +@pytest.fixture +def test_disk(): + """ + Get the newest version of debian 11 and make a disk from it. + """ + new_debian = get_image_from_family('debian-cloud', 'debian-11') + test_disk_name = "test-disk-" + uuid.uuid4().hex[:10] + disk = create_disk_from_image(PROJECT, ZONE, test_disk_name, + f"zones/{ZONE}/diskTypes/pd-standard", + 20, new_debian.self_link) + yield disk + delete_disk(PROJECT, ZONE, test_disk_name) + + +@pytest.fixture +def test_snapshot(test_disk): + """ + Make a snapshot that will be deleted when tests are done. + """ + test_snap_name = "test-snap-" + uuid.uuid4().hex[:10] + snap = create_snapshot(PROJECT, test_disk.name, test_snap_name, zone=test_disk.zone.rsplit('/')[-1]) + yield snap + delete_snapshot(PROJECT, snap.name) @pytest.fixture() @@ -37,6 +98,19 @@ def autodelete_disk_name(): pass +# To use the fixture 2 times in one test: +# https://stackoverflow.com/questions/36100624/pytest-use-same-fixture-twice-in-one-function +autodelete_disk_name2 = autodelete_disk_name + + +@pytest.fixture() +def autodelete_src_disk(autodelete_disk_name): + disk_type = f"zones/{ZONE}/diskTypes/pd-standard" + debian_image = get_image_from_family('debian-cloud', 'debian-11') + disk = create_disk_from_image(PROJECT, ZONE, autodelete_disk_name, disk_type, 24, debian_image.self_link) + yield disk + + def test_disk_create_delete(autodelete_disk_name): disk_type = f"zones/{ZONE}/diskTypes/pd-standard" debian_image = get_image_from_family('debian-cloud', 'debian-11') @@ -57,3 +131,45 @@ def test_disk_create_delete(autodelete_disk_name): for i_disk in list_disks(PROJECT, ZONE): if i_disk.name == autodelete_disk_name: pytest.fail("Found a disk that should be deleted on the disk list.") + + +def test_create_and_clone_encrypted_disk(autodelete_disk_name, kms_key, autodelete_disk_name2): + # The service account service-{PROJECT_ID}@compute-system.iam.gserviceaccount.com needs to have the + # cloudkms.cryptoKeyVersions.useToEncrypt permission to execute this test. + disk_type = f"zones/{ZONE}/diskTypes/pd-standard" + debian_image = get_image_from_family('debian-cloud', 'debian-11') + + disk = create_kms_encrypted_disk(PROJECT, ZONE, autodelete_disk_name, disk_type, 25, kms_key.name, + image_link=debian_image.self_link) + assert disk.name == autodelete_disk_name + assert disk.type_.endswith(disk_type) + + disk2 = create_disk_from_kms_encrypted_disk(PROJECT, ZONE, autodelete_disk_name2, disk_type, + 25, disk_link=disk.self_link, kms_key_name=kms_key.name) + assert disk2.name == autodelete_disk_name2 + assert disk2.type_.endswith(disk_type) + + +def test_create_disk_from_disk(autodelete_src_disk, autodelete_disk_name2): + disk_type = f"zones/{ZONE}/diskTypes/pd-standard" + new_disk = create_disk_from_disk(PROJECT, ZONE, autodelete_disk_name2, disk_type, 24, autodelete_src_disk.self_link) + + assert new_disk.type_.endswith(disk_type) + assert new_disk.name == autodelete_disk_name2 + + +def test_create_and_delete_regional_disk(test_snapshot): + disk_name = "test-rdisk-" + uuid.uuid4().hex[:10] + disk_type = f"regions/{REGION}/diskTypes/pd-balanced" + replica_zones = [ + f"projects/diregapic-mestiv/zones/{REGION}-a", + f"projects/diregapic-mestiv/zones/{REGION}-b", + ] + + try: + regional_disk = create_regional_disk(PROJECT, REGION, replica_zones, disk_name, + disk_type, 25, snapshot_link=test_snapshot.self_link) + assert regional_disk.name == disk_name + assert regional_disk.type_.endswith(disk_type) + finally: + delete_regional_disk(PROJECT, REGION, disk_name) diff --git a/compute/compute/snippets/tests/test_instance_from_template.py b/compute/compute/snippets/tests/test_instance_from_template.py index e322082581b9..e8a20f372d81 100644 --- a/compute/compute/snippets/tests/test_instance_from_template.py +++ b/compute/compute/snippets/tests/test_instance_from_template.py @@ -37,6 +37,7 @@ def instance_template(): "projects/debian-cloud/global/images/family/debian-11" ) initialize_params.disk_size_gb = 25 + initialize_params.disk_type = 'pd-balanced' disk.initialize_params = initialize_params disk.auto_delete = True disk.boot = True diff --git a/compute/compute/snippets/tests/test_instance_start_stop.py b/compute/compute/snippets/tests/test_instance_start_stop.py index 737400f8a649..8c6efc802de7 100644 --- a/compute/compute/snippets/tests/test_instance_start_stop.py +++ b/compute/compute/snippets/tests/test_instance_start_stop.py @@ -21,6 +21,8 @@ from google.cloud import compute_v1 import pytest +from ..disks.clone_encrypted_disk import create_disk_from_customer_encrypted_disk +from ..disks.delete import delete_disk from ..instances.start import start_instance from ..instances.start_encrypted import start_instance_with_encryption_key from ..instances.stop import stop_instance @@ -135,6 +137,13 @@ def compute_encrypted_instance(): _delete_instance(instance) +@pytest.fixture +def autodelete_disk_name(): + name = "d" + uuid.uuid4().hex[:10] + yield name + delete_disk(PROJECT, INSTANCE_ZONE, name) + + def test_instance_operations(compute_instance): assert _get_status(compute_instance) == "RUNNING" @@ -166,3 +175,15 @@ def test_instance_encrypted(compute_encrypted_instance): PROJECT, INSTANCE_ZONE, compute_encrypted_instance.name, KEY_B64 ) assert _get_status(compute_encrypted_instance) == "RUNNING" + + +def test_clone_encrypted_disk(autodelete_disk_name, compute_encrypted_instance): + assert _get_status(compute_encrypted_instance) == "RUNNING" + + new_disk = create_disk_from_customer_encrypted_disk( + PROJECT, INSTANCE_ZONE, autodelete_disk_name, + f"zones/{INSTANCE_ZONE}/diskTypes/pd-standard", + 10, compute_encrypted_instance.disks[0].source, + encryption_key=KEY_B64) + + assert new_disk.name == autodelete_disk_name