Skip to content

Commit 4b841ef

Browse files
feat: adding samples to start/stop/reset operations (#75)
* feat: Adding start/stop compute samples. Co-authored-by: Anthonios Partheniou <partheniou@google.com>
1 parent dfc06f5 commit 4b841ef

File tree

3 files changed

+271
-0
lines changed

3 files changed

+271
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2021 Google LLC
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
"""
18+
A sample script showing how to start and stop Google Compute Engine instances.
19+
"""
20+
from google.cloud import compute_v1
21+
22+
23+
# [START compute_start_instance]
24+
def start_instance(project_id: str, zone: str, instance_name: str):
25+
"""
26+
Starts a stopped Google Compute Engine instance (with unencrypted disks).
27+
28+
Args:
29+
project_id: project ID or project number of the Cloud project your instance belongs to.
30+
zone: name of the zone your instance belongs to.
31+
instance_name: name of the instance your want to start.
32+
"""
33+
instance_client = compute_v1.InstancesClient()
34+
op_client = compute_v1.ZoneOperationsClient()
35+
36+
op = instance_client.start(project=project_id, zone=zone, instance=instance_name)
37+
38+
op_client.wait(project=project_id, zone=zone, operation=op.name)
39+
return
40+
# [END compute_start_instance]
41+
42+
43+
# [START compute_start_enc_instance]
44+
def start_instance_with_encryption_key(project_id: str, zone: str, instance_name: str, key: bytes):
45+
"""
46+
Starts a stopped Google Compute Engine instance (with encrypted disks).
47+
48+
Args:
49+
project_id: project ID or project number of the Cloud project your instance belongs to.
50+
zone: name of the zone your instance belongs to.
51+
instance_name: name of the instance your want to start.
52+
key: bytes object representing a raw base64 encoded key to your machines boot disk.
53+
For more information about disk encryption see:
54+
https://cloud.google.com/compute/docs/disks/customer-supplied-encryption#specifications
55+
"""
56+
instance_client = compute_v1.InstancesClient()
57+
op_client = compute_v1.ZoneOperationsClient()
58+
59+
instance_data = instance_client.get(project=project_id, zone=zone, instance=instance_name)
60+
61+
# Prepare the information about disk encryption
62+
disk_data = compute_v1.CustomerEncryptionKeyProtectedDisk()
63+
disk_data.source = instance_data.disks[0].source
64+
disk_data.disk_encryption_key = compute_v1.CustomerEncryptionKey()
65+
# Use raw_key to send over the key to unlock the disk
66+
# To use a key stored in KMS, you need to provide `kms_key_name` and `kms_key_service_account`
67+
disk_data.disk_encryption_key.raw_key = key
68+
enc_data = compute_v1.InstancesStartWithEncryptionKeyRequest()
69+
enc_data.disks = [disk_data]
70+
71+
op = instance_client.start_with_encryption_key(project=project_id, zone=zone, instance=instance_name,
72+
instances_start_with_encryption_key_request_resource=enc_data)
73+
74+
op_client.wait(project=project_id, zone=zone, operation=op.name)
75+
return
76+
# [END compute_start_enc_instance]
77+
78+
79+
# [START compute_stop_instance]
80+
def stop_instance(project_id: str, zone: str, instance_name: str):
81+
"""
82+
Stops a stopped Google Compute Engine instance.
83+
84+
Args:
85+
project_id: project ID or project number of the Cloud project your instance belongs to.
86+
zone: name of the zone your instance belongs to.
87+
instance_name: name of the instance your want to stop.
88+
"""
89+
instance_client = compute_v1.InstancesClient()
90+
op_client = compute_v1.ZoneOperationsClient()
91+
92+
op = instance_client.stop(project=project_id, zone=zone, instance=instance_name)
93+
94+
op_client.wait(project=project_id, zone=zone, operation=op.name)
95+
return
96+
# [END compute_stop_instance]
97+
98+
99+
# [START compute_reset_instance]
100+
def reset_instance(project_id: str, zone: str, instance_name: str):
101+
"""
102+
Resets a stopped Google Compute Engine instance (with unencrypted disks).
103+
104+
Args:
105+
project_id: project ID or project number of the Cloud project your instance belongs to.
106+
zone: name of the zone your instance belongs to.
107+
instance_name: name of the instance your want to reset.
108+
"""
109+
instance_client = compute_v1.InstancesClient()
110+
op_client = compute_v1.ZoneOperationsClient()
111+
112+
op = instance_client.reset(project=project_id, zone=zone, instance=instance_name)
113+
114+
op_client.wait(project=project_id, zone=zone, operation=op.name)
115+
return
116+
# [END compute_reset_instance]

packages/google-cloud-compute/samples/snippets/test_sample_default_values.py

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14+
import time
1415
import typing
1516
import uuid
1617

@@ -37,13 +38,15 @@ def temp_bucket():
3738
def test_set_usage_export_bucket_default(capsys: typing.Any,
3839
temp_bucket: storage.Bucket) -> None:
3940
set_usage_export_bucket(project_id=PROJECT, bucket_name=temp_bucket.name)
41+
time.sleep(5) # To make sure the settings are properly updated
4042
uel = get_usage_export_bucket(project_id=PROJECT)
4143
assert(uel.bucket_name == temp_bucket.name)
4244
assert(uel.report_name_prefix == 'usage_gce')
4345
out, _ = capsys.readouterr()
4446
assert('default prefix of `usage_gce`.' in out)
4547

4648
disable_usage_export(project_id=PROJECT)
49+
time.sleep(5) # To make sure the settings are properly updated
4750
uel = get_usage_export_bucket(project_id=PROJECT)
4851
assert(uel.bucket_name == '')
4952
assert(uel.report_name_prefix == '')
@@ -53,13 +56,15 @@ def test_set_usage_export_bucket_custom(capsys: typing.Any,
5356
temp_bucket: storage.Bucket) -> None:
5457
set_usage_export_bucket(project_id=PROJECT, bucket_name=temp_bucket.name,
5558
report_name_prefix=TEST_PREFIX)
59+
time.sleep(5) # To make sure the settings are properly updated
5660
uel = get_usage_export_bucket(project_id=PROJECT)
5761
assert(uel.bucket_name == temp_bucket.name)
5862
assert(uel.report_name_prefix == TEST_PREFIX)
5963
out, _ = capsys.readouterr()
6064
assert('usage_gce' not in out)
6165

6266
disable_usage_export(project_id=PROJECT)
67+
time.sleep(5) # To make sure the settings are properly updated
6368
uel = get_usage_export_bucket(project_id=PROJECT)
6469
assert(uel.bucket_name == '')
6570
assert(uel.report_name_prefix == '')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# Copyright 2021 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
import base64
15+
import random
16+
import string
17+
import time
18+
import uuid
19+
20+
import google.auth
21+
from google.cloud import compute_v1
22+
23+
import pytest
24+
25+
from samples.snippets.sample_start_stop import start_instance, start_instance_with_encryption_key, stop_instance
26+
27+
PROJECT = google.auth.default()[1]
28+
29+
INSTANCE_ZONE = "europe-central2-b"
30+
31+
KEY = "".join(random.sample(string.ascii_letters, 32))
32+
KEY_B64 = base64.b64encode(KEY.encode()) # for example: b'VEdORldtY3NKellPdWRDcUF5YlNVREtJdm5qaFJYSFA='
33+
34+
35+
def _make_disk(raw_key: bytes = None):
36+
disk = compute_v1.AttachedDisk()
37+
initialize_params = compute_v1.AttachedDiskInitializeParams()
38+
initialize_params.source_image = (
39+
"projects/debian-cloud/global/images/family/debian-10"
40+
)
41+
initialize_params.disk_size_gb = "10"
42+
disk.initialize_params = initialize_params
43+
disk.auto_delete = True
44+
disk.boot = True
45+
disk.type_ = compute_v1.AttachedDisk.Type.PERSISTENT
46+
47+
if raw_key:
48+
disk.disk_encryption_key = compute_v1.CustomerEncryptionKey()
49+
disk.disk_encryption_key.raw_key = raw_key
50+
51+
return disk
52+
53+
54+
def _make_request(disk: compute_v1.AttachedDisk):
55+
network_interface = compute_v1.NetworkInterface()
56+
network_interface.name = 'default'
57+
network_interface.access_configs = []
58+
59+
# Collect information into the Instance object.
60+
instance = compute_v1.Instance()
61+
instance.name = "i" + uuid.uuid4().hex[:10]
62+
instance.disks = [disk]
63+
full_machine_type_name = f"zones/{INSTANCE_ZONE}/machineTypes/e2-micro"
64+
instance.machine_type = full_machine_type_name
65+
instance.network_interfaces = [network_interface]
66+
67+
# Prepare the request to insert an instance.
68+
request = compute_v1.InsertInstanceRequest()
69+
request.zone = INSTANCE_ZONE
70+
request.project = PROJECT
71+
request.instance_resource = instance
72+
return request
73+
74+
75+
def _create_instance(request: compute_v1.InsertInstanceRequest):
76+
instance_client = compute_v1.InstancesClient()
77+
operation_client = compute_v1.ZoneOperationsClient()
78+
79+
operation = instance_client.insert(request=request)
80+
operation_client.wait(operation=operation.name, zone=INSTANCE_ZONE, project=PROJECT)
81+
82+
return instance_client.get(project=PROJECT, zone=INSTANCE_ZONE, instance=request.instance_resource.name)
83+
84+
85+
def _delete_instance(instance: compute_v1.Instance):
86+
instance_client = compute_v1.InstancesClient()
87+
operation_client = compute_v1.ZoneOperationsClient()
88+
89+
operation = instance_client.delete(project=PROJECT, zone=INSTANCE_ZONE, instance=instance.name)
90+
operation_client.wait(operation=operation.name, zone=INSTANCE_ZONE, project=PROJECT)
91+
92+
93+
def _get_status(instance: compute_v1.Instance) -> compute_v1.Instance.Status:
94+
instance_client = compute_v1.InstancesClient()
95+
return instance_client.get(project=PROJECT, zone=INSTANCE_ZONE, instance=instance.name).status
96+
97+
98+
@pytest.fixture
99+
def compute_instance():
100+
disk = _make_disk()
101+
request = _make_request(disk)
102+
103+
instance = _create_instance(request)
104+
105+
yield instance
106+
107+
_delete_instance(instance)
108+
109+
110+
@pytest.fixture
111+
def compute_encrypted_instance():
112+
disk = _make_disk(KEY_B64)
113+
request = _make_request(disk)
114+
115+
instance = _create_instance(request)
116+
117+
yield instance
118+
119+
_delete_instance(instance)
120+
121+
122+
def test_instance_operations(compute_instance):
123+
assert _get_status(compute_instance) == compute_v1.Instance.Status.RUNNING
124+
125+
stop_instance(PROJECT, INSTANCE_ZONE, compute_instance.name)
126+
127+
while _get_status(compute_instance) == compute_v1.Instance.Status.STOPPING:
128+
# Since we can't configure timeout parameter for operation wait() (b/188037306)
129+
# We need to do some manual waiting for the stopping to finish...
130+
time.sleep(5)
131+
132+
assert _get_status(compute_instance) == compute_v1.Instance.Status.TERMINATED
133+
134+
start_instance(PROJECT, INSTANCE_ZONE, compute_instance.name)
135+
assert _get_status(compute_instance) == compute_v1.Instance.Status.RUNNING
136+
137+
138+
def test_instance_encrypted(compute_encrypted_instance):
139+
assert _get_status(compute_encrypted_instance) == compute_v1.Instance.Status.RUNNING
140+
141+
stop_instance(PROJECT, INSTANCE_ZONE, compute_encrypted_instance.name)
142+
while _get_status(compute_encrypted_instance) == compute_v1.Instance.Status.STOPPING:
143+
# Since we can't configure timeout parameter for operation wait() (b/188037306)
144+
# We need to do some manual waiting for the stopping to finish...
145+
time.sleep(5)
146+
147+
assert _get_status(compute_encrypted_instance) == compute_v1.Instance.Status.TERMINATED
148+
149+
start_instance_with_encryption_key(PROJECT, INSTANCE_ZONE, compute_encrypted_instance.name, KEY_B64)
150+
assert _get_status(compute_encrypted_instance) == compute_v1.Instance.Status.RUNNING

0 commit comments

Comments
 (0)