Skip to content

Commit 64efba7

Browse files
m-strzelczykgcf-owl-bot[bot]busunkim96
authored andcommitted
chore(samples): Samples for preemptible instances (#213)
Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com> Co-authored-by: Bu Sun Kim <[email protected]>
1 parent e759ca2 commit 64efba7

File tree

2 files changed

+251
-0
lines changed

2 files changed

+251
-0
lines changed
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# Copyright 2022 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+
# [START compute_preemptible_history]
15+
import datetime
16+
17+
# [END compute_preemptible_history]
18+
# [START compute_preemptible_create]
19+
import sys
20+
21+
# [END compute_preemptible_create]
22+
23+
# [START compute_preemptible_history]
24+
from typing import List, Tuple
25+
26+
# [END compute_preemptible_history]
27+
28+
# [START compute_preemptible_create]
29+
# [START compute_preemptible_check]
30+
# [START compute_preemptible_history]
31+
from google.cloud import compute_v1
32+
33+
# [END compute_preemptible_history]
34+
# [END compute_preemptible_check]
35+
# [END compute_preemptible_create]
36+
37+
# [START compute_preemptible_history]
38+
from google.cloud.compute_v1.services.zone_operations import pagers
39+
40+
# [END compute_preemptible_history]
41+
42+
43+
# [START compute_preemptible_create]
44+
def create_preemptible_instance(
45+
project_id: str, zone: str, instance_name: str,
46+
) -> compute_v1.Instance:
47+
"""
48+
Send an instance creation request to the Compute Engine API and wait for it to complete.
49+
50+
Args:
51+
project_id: project ID or project number of the Cloud project you want to use.
52+
zone: name of the zone you want to use. For example: "us-west3-b"
53+
instance_name: name of the new virtual machine.
54+
Returns:
55+
Instance object.
56+
"""
57+
instance_client = compute_v1.InstancesClient()
58+
operation_client = compute_v1.ZoneOperationsClient()
59+
60+
# Describe the size and source image of the boot disk to attach to the instance.
61+
disk = compute_v1.AttachedDisk()
62+
initialize_params = compute_v1.AttachedDiskInitializeParams()
63+
initialize_params.source_image = (
64+
"projects/debian-cloud/global/images/family/debian-10"
65+
)
66+
initialize_params.disk_size_gb = 10
67+
disk.initialize_params = initialize_params
68+
disk.auto_delete = True
69+
disk.boot = True
70+
disk.type_ = "PERSISTENT"
71+
72+
# Use the default VPC network.
73+
network_interface = compute_v1.NetworkInterface()
74+
network_interface.name = "default"
75+
76+
# Collect information into the Instance object.
77+
instance = compute_v1.Instance()
78+
instance.name = instance_name
79+
instance.disks = [disk]
80+
instance.machine_type = f"zones/{zone}/machineTypes/e2-small"
81+
instance.network_interfaces = [network_interface]
82+
83+
# Set the preemptible setting
84+
instance.scheduling = compute_v1.Scheduling()
85+
instance.scheduling.preemptible = True
86+
87+
# Prepare the request to insert an instance.
88+
request = compute_v1.InsertInstanceRequest()
89+
request.zone = zone
90+
request.project = project_id
91+
request.instance_resource = instance
92+
93+
# Wait for the create operation to complete.
94+
print(f"Creating the {instance_name} instance in {zone}...")
95+
operation = instance_client.insert_unary(request=request)
96+
while operation.status != compute_v1.Operation.Status.DONE:
97+
operation = operation_client.wait(
98+
operation=operation.name, zone=zone, project=project_id
99+
)
100+
if operation.error:
101+
print("Error during creation:", operation.error, file=sys.stderr)
102+
if operation.warnings:
103+
print("Warning during creation:", operation.warnings, file=sys.stderr)
104+
print(f"Instance {instance_name} created.")
105+
return instance_client.get(project=project_id, zone=zone, instance=instance_name)
106+
107+
108+
# [END compute_preemptible_create]
109+
110+
111+
# [START compute_preemptible_check]
112+
def is_preemptible(project_id: str, zone: str, instance_name: str) -> bool:
113+
"""
114+
Check if a given instance is preemptible or not.
115+
116+
Args:
117+
project_id: project ID or project number of the Cloud project you want to use.
118+
zone: name of the zone you want to use. For example: "us-west3-b"
119+
instance_name: name of the virtual machine to check.
120+
Returns:
121+
The preemptible status of the instance.
122+
"""
123+
instance_client = compute_v1.InstancesClient()
124+
instance = instance_client.get(
125+
project=project_id, zone=zone, instance=instance_name
126+
)
127+
return instance.scheduling.preemptible
128+
129+
130+
# [END compute_preemptible_check]
131+
132+
133+
# [START compute_preemptible_history]
134+
def list_zone_operations(
135+
project_id: str, zone: str, filter: str = ""
136+
) -> pagers.ListPager:
137+
"""
138+
List all recent operations the happened in given zone in a project. Optionally filter those
139+
operations by providing a filter. More about using the filter can be found here:
140+
https://cloud.google.com/compute/docs/reference/rest/v1/zoneOperations/list
141+
142+
Args:
143+
project_id: project ID or project number of the Cloud project you want to use.
144+
zone: name of the zone you want to use. For example: "us-west3-b"
145+
instance_name: name of the virtual machine to look for.
146+
Returns:
147+
List of preemption operations in given zone.
148+
"""
149+
operation_client = compute_v1.ZoneOperationsClient()
150+
request = compute_v1.ListZoneOperationsRequest()
151+
request.project = project_id
152+
request.zone = zone
153+
request.filter = filter
154+
155+
return operation_client.list(request)
156+
157+
158+
def preemption_history(
159+
project_id: str, zone: str, instance_name: str = None
160+
) -> List[Tuple[str, datetime.datetime]]:
161+
"""
162+
Get a list of preemption operations from given zone in a project. Optionally limit
163+
the results to instance name.
164+
165+
Args:
166+
project_id: project ID or project number of the Cloud project you want to use.
167+
zone: name of the zone you want to use. For example: "us-west3-b"
168+
instance_name: name of the virtual machine to look for.
169+
Returns:
170+
List of preemption operations in given zone.
171+
"""
172+
if instance_name:
173+
filter = (
174+
f'operationType="compute.instances.preempted" '
175+
f"AND targetLink:instances/{instance_name}"
176+
)
177+
else:
178+
filter = 'operationType="compute.instances.preempted"'
179+
180+
history = []
181+
182+
for operation in list_zone_operations(project_id, zone, filter):
183+
this_instance_name = operation.target_link.rsplit("/", maxsplit=1)[1]
184+
if instance_name and this_instance_name == instance_name:
185+
# The filter used is not 100% accurate, it's `contains` not `equals`
186+
# So we need to check the name to make sure it's the one we want.
187+
moment = datetime.datetime.fromisoformat(operation.insert_time)
188+
history.append((instance_name, moment))
189+
190+
return history
191+
192+
193+
# [END compute_preemptible_history]
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Copyright 2022 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 uuid
15+
16+
import google.auth
17+
import pytest
18+
19+
from quickstart import delete_instance
20+
from sample_preemptible import create_preemptible_instance
21+
from sample_preemptible import is_preemptible
22+
from sample_preemptible import list_zone_operations
23+
24+
PROJECT = google.auth.default()[1]
25+
INSTANCE_ZONE = "europe-central2-c"
26+
27+
28+
@pytest.fixture
29+
def autodelete_instance_name():
30+
instance_name = "i" + uuid.uuid4().hex[:10]
31+
32+
yield instance_name
33+
34+
delete_instance(PROJECT, INSTANCE_ZONE, instance_name)
35+
36+
37+
def test_preemptible_creation(autodelete_instance_name):
38+
instance = create_preemptible_instance(
39+
PROJECT, INSTANCE_ZONE, autodelete_instance_name
40+
)
41+
42+
assert instance.name == autodelete_instance_name
43+
assert is_preemptible(PROJECT, INSTANCE_ZONE, instance.name)
44+
45+
operations = list_zone_operations(
46+
PROJECT,
47+
INSTANCE_ZONE,
48+
f'targetLink="https://www.googleapis.com/compute/v1/projects/'
49+
f'{PROJECT}/zones/{INSTANCE_ZONE}/instances/{instance.name}"',
50+
)
51+
52+
# Since ListPagers don't support len(), we need to check it manually
53+
try:
54+
next(iter(operations))
55+
except StopIteration:
56+
pytest.fail(
57+
"There should be at least one operation for this instance at this point."
58+
)

0 commit comments

Comments
 (0)