Skip to content

Commit c8b1160

Browse files
pmkccopybara-github
authored andcommitted
Add regional support for GCE MIGs
PiperOrigin-RevId: 934454503
1 parent 2271f75 commit c8b1160

3 files changed

Lines changed: 70 additions & 10 deletions

File tree

perfkitbenchmarker/managed_vm_group.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,19 @@ def __init__(
5959
self._vms: dict[str, virtual_machine.BaseVirtualMachine] = {}
6060
self._deleted_vms: list[virtual_machine.BaseVirtualMachine] = []
6161

62+
self.region: str | None = None
63+
self.zone: str | None = None
64+
6265
self.last_operation_start_time: float | None = None
6366
self.last_ready_time: float | None = None
6467

6568
def GetResourceMetadata(self) -> dict[Any, Any]:
6669
metadata = super().GetResourceMetadata().copy()
6770
vm = (list(self.vms) + self._deleted_vms + [self.vm_config])[0]
6871
metadata.update(vm.GetResourceMetadata())
72+
metadata['region'] = self.region
73+
metadata['zone'] = self.zone
74+
metadata['is_regional'] = self.zone is None
6975
return metadata
7076

7177
@property

perfkitbenchmarker/providers/gcp/gce_managed_instance_group.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"""GCE Managed Instance Group resource."""
1515

1616
import json
17+
import re
1718
from typing import Any, cast
1819

1920
from perfkitbenchmarker import errors
@@ -101,6 +102,7 @@ class GceManagedInstanceGroup(managed_vm_group.BaseManagedVmGroup):
101102
"""GCE Managed Instance Group."""
102103

103104
CLOUD = provider_info.GCP
105+
instance_template: GceInstanceTemplate
104106

105107
def __init__(
106108
self,
@@ -112,10 +114,18 @@ def __init__(
112114
gce_virtual_machine.GceVirtualMachine, self.vm_config
113115
)
114116
self.project = self.vm_config.project
115-
# TODO(pclay): Add support for regional managed instance groups.
116-
# It's unclear how multiple zones would be plumbed through BaseVmSpec.
117-
self.zone = self.vm_config.zone
118-
self.region = util.GetRegionFromZone(self.zone)
117+
# in theory we could use the users defaults, but this is cleaner.
118+
assert self.vm_config.zone
119+
if util.IsZone(self.vm_config.zone):
120+
self.zone = self.vm_config.zone
121+
self.region = util.GetRegionFromZone(self.zone)
122+
elif util.IsRegion(self.vm_config.zone):
123+
self.region = self.vm_config.zone
124+
self.zone = None
125+
else:
126+
raise ValueError(
127+
f'Unsupported zone: {self.vm_config.zone}. Must be a zone or region.'
128+
)
119129

120130
self.instance_template = GceInstanceTemplate(
121131
self.vm_config, self.region, name=self.name
@@ -126,9 +136,13 @@ def _CreateDependencies(self):
126136
self.instance_template.Create()
127137

128138
def _GcloudCmd(self, *args) -> util.GcloudCommand:
129-
return util.GcloudCommand(
139+
cmd = util.GcloudCommand(
130140
self, 'compute', 'instance-groups', 'managed', *args
131141
)
142+
# GcloudCommand does not have support for regional resources.
143+
if not self.zone:
144+
cmd.flags['region'] = self.region
145+
return cmd
132146

133147
def _Create(self):
134148
cmd = self._GcloudCmd(
@@ -176,7 +190,14 @@ def _GetCurrentVms(self) -> list[VmReference]:
176190
f'{stderr}\n'
177191
)
178192
instances = json.loads(stdout)
179-
return [VmReference(name=instance['name']) for instance in instances]
193+
references = []
194+
for instance in instances:
195+
match = re.search(
196+
r'/zones/([^/]+)/instances/([^/]+)', instance['instance']
197+
)
198+
assert match
199+
references.append(VmReference(name=match.group(2), zone=match.group(1)))
200+
return references
180201

181202
def _AddVms(self, num_vms_to_add: int):
182203
cmd = self._GcloudCmd('create-instance', self.name)

tests/providers/gcp/gce_managed_instance_group_test.py

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from unittest import mock
44

55
from absl import flags
6+
from perfkitbenchmarker import virtual_machine
67
from perfkitbenchmarker import vm_util
78
from perfkitbenchmarker.configs import vm_group_decoders
89
from perfkitbenchmarker.providers.gcp import gce_managed_instance_group
@@ -18,6 +19,7 @@ class GceManagedInstanceGroupTest(pkb_common_test_case.PkbCommonTestCase):
1819
def setUp(self):
1920
super().setUp()
2021
FLAGS.run_uri = 'test_run'
22+
virtual_machine.BaseVirtualMachine._instance_counter = 0
2123
self.mock_cmd = self.MockIssueCommand(
2224
{
2325
'': [('', '', 0)],
@@ -27,13 +29,13 @@ def setUp(self):
2729
@mock.patch.object(gcp_utils, 'GetRegionFromZone', return_value='us-central1')
2830
@mock.patch.object(gce_virtual_machine.gce_network.GceFirewall, 'GetFirewall')
2931
@mock.patch.object(gce_virtual_machine.gce_network.GceNetwork, 'GetNetwork')
30-
def TestMig(self, mock_get_network, *_):
32+
def TestMig(self, mock_get_network, *_, zone='us-central1-c'):
3133
mock_get_network.return_value.placement_group.name = 'test_placement_group'
3234
vm_config = pkb_common_test_case.TestGceVirtualMachine(
3335
gce_virtual_machine.GceVmSpec(
3436
'test_component',
3537
machine_type='n1-standard-4',
36-
zone='us-central1-c',
38+
zone=zone,
3739
)
3840
)
3941
return gce_managed_instance_group.GceManagedInstanceGroup(
@@ -47,7 +49,7 @@ def TestMig(self, mock_get_network, *_):
4749
)
4850

4951
def testCreate(self, *_):
50-
mig = self.TestMig()
52+
mig = self.TestMig(zone='us-central1-c')
5153
mig._Create()
5254
self.assertIn(
5355
'gcloud compute instance-groups managed create pkb-test_run-0'
@@ -58,6 +60,18 @@ def testCreate(self, *_):
5860
self.mock_cmd.all_commands,
5961
)
6062

63+
def testCreateRegional(self, *_):
64+
mig = self.TestMig(zone='us-central1')
65+
mig._Create()
66+
self.assertIn(
67+
'gcloud compute instance-groups managed create pkb-test_run-0'
68+
' --template'
69+
' projects/test_project/regions/us-central1/instanceTemplates/pkb-test_run-0'
70+
' --size 1 --format json --project test_project --quiet --region'
71+
' us-central1',
72+
self.mock_cmd.all_commands,
73+
)
74+
6175
# SSH keys
6276
@mock.patch.object(builtins, 'open')
6377
@mock.patch.object(vm_util, 'NamedTemporaryFile')
@@ -68,7 +82,7 @@ def testCreateDependencies(self, mock_named_temporary_file, _):
6882
)
6983
mig._CreateDependencies()
7084
self.assertIn(
71-
'gcloud compute instance-templates create pkb-test_run-1'
85+
'gcloud compute instance-templates create pkb-test_run-0'
7286
' --instance-template-region us-central1 --format json --labels '
7387
' --machine-type n1-standard-4 --maintenance-policy TERMINATE'
7488
' --metadata enable-oslogin=FALSE,vm_nature=ephemeral'
@@ -78,6 +92,25 @@ def testCreateDependencies(self, mock_named_temporary_file, _):
7892
self.mock_cmd.all_commands,
7993
)
8094

95+
def testGetCurrentVms(self):
96+
list_instances_response = (
97+
'[{"name": "pkb-test_run-0-0", "instance":'
98+
' "https://www.googleapis.com/compute/v1/projects/test_project/'
99+
'zones/us-central1-c/instances/pkb-test_run-0-0"}]'
100+
)
101+
self.MockIssueCommand(
102+
{
103+
'gcloud compute instance-groups managed list-instances': [
104+
(list_instances_response, '', 0)
105+
]
106+
}
107+
)
108+
mig = self.TestMig()
109+
vms = mig._GetCurrentVms()
110+
self.assertEqual(len(vms), 1)
111+
self.assertEqual(vms[0].name, 'pkb-test_run-0-0')
112+
self.assertEqual(vms[0].zone, 'us-central1-c')
113+
81114

82115
if __name__ == '__main__':
83116
unittest.main()

0 commit comments

Comments
 (0)