blob: 7535a72a6e61d46c47400392be08ad65bc060494 [file] [log] [blame]
Tri Vo29ac1822016-10-01 17:06:29 -07001#!/usr/bin/env python
2#
3# Copyright 2016 - The Android Open Source Project
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.
Tri Vo29ac1822016-10-01 17:06:29 -070016"""Tests for acloud.internal.lib.gcompute_client."""
Kevin Cheng5c124ec2018-05-16 13:28:51 -070017# pylint: disable=too-many-lines
Tri Vo29ac1822016-10-01 17:06:29 -070018
Kevin Cheng5c124ec2018-05-16 13:28:51 -070019import copy
Tri Vo29ac1822016-10-01 17:06:29 -070020import os
21
Kevin Cheng5c124ec2018-05-16 13:28:51 -070022import unittest
Tri Vo29ac1822016-10-01 17:06:29 -070023import mock
Kevin Cheng5c124ec2018-05-16 13:28:51 -070024
25# pylint: disable=import-error
26import apiclient.http
Kevin Chengb5963882018-05-09 00:06:27 -070027from absl.testing import parameterized
Tri Vo29ac1822016-10-01 17:06:29 -070028
Tri Vo29ac1822016-10-01 17:06:29 -070029from acloud.internal.lib import driver_test_lib
30from acloud.internal.lib import gcompute_client
31from acloud.internal.lib import utils
32from acloud.public import errors
33
Kevin Chengb5963882018-05-09 00:06:27 -070034GS_IMAGE_SOURCE_URI = "https://storage.googleapis.com/fake-bucket/fake.tar.gz"
35GS_IMAGE_SOURCE_DISK = (
36 "https://www.googleapis.com/compute/v1/projects/fake-project/zones/"
37 "us-east1-d/disks/fake-disk")
38PROJECT = "fake-project"
Tri Vo29ac1822016-10-01 17:06:29 -070039
Kevin Cheng5c124ec2018-05-16 13:28:51 -070040# pylint: disable=protected-access, too-many-public-methods
Kevin Chengb5963882018-05-09 00:06:27 -070041class ComputeClientTest(driver_test_lib.BaseDriverTest, parameterized.TestCase):
Tri Vo29ac1822016-10-01 17:06:29 -070042 """Test ComputeClient."""
43
Kevin Chengb5963882018-05-09 00:06:27 -070044 PROJECT_OTHER = "fake-project-other"
Tri Vo29ac1822016-10-01 17:06:29 -070045 INSTANCE = "fake-instance"
46 IMAGE = "fake-image"
47 IMAGE_URL = "http://fake-image-url"
Kevin Chengb5963882018-05-09 00:06:27 -070048 IMAGE_OTHER = "fake-image-other"
Tri Vo29ac1822016-10-01 17:06:29 -070049 MACHINE_TYPE = "fake-machine-type"
50 MACHINE_TYPE_URL = "http://fake-machine-type-url"
51 METADATA = ("metadata_key", "metadata_value")
Kevin Chengb5963882018-05-09 00:06:27 -070052 ACCELERATOR_URL = "http://speedy-gpu"
Tri Vo29ac1822016-10-01 17:06:29 -070053 NETWORK = "fake-network"
54 NETWORK_URL = "http://fake-network-url"
55 ZONE = "fake-zone"
56 REGION = "fake-region"
57 OPERATION_NAME = "fake-op"
Kevin Chengb5963882018-05-09 00:06:27 -070058 IMAGE_FINGERPRINT = "L_NWHuz7wTY="
59 GPU = "fancy-graphics"
Tri Vo29ac1822016-10-01 17:06:29 -070060
61 def setUp(self):
62 """Set up test."""
63 super(ComputeClientTest, self).setUp()
64 self.Patch(gcompute_client.ComputeClient, "InitResourceHandle")
65 fake_cfg = mock.MagicMock()
Kevin Chengb5963882018-05-09 00:06:27 -070066 fake_cfg.project = PROJECT
67 self.compute_client = gcompute_client.ComputeClient(
68 fake_cfg, mock.MagicMock())
Tri Vo29ac1822016-10-01 17:06:29 -070069 self.compute_client._service = mock.MagicMock()
70
Kevin Cheng5c124ec2018-05-16 13:28:51 -070071 self._disk_args = copy.deepcopy(gcompute_client.BASE_DISK_ARGS)
72 self._disk_args["initializeParams"] = {"diskName": self.INSTANCE,
73 "sourceImage": self.IMAGE_URL}
74
75 # pylint: disable=invalid-name
Tri Vo29ac1822016-10-01 17:06:29 -070076 def _SetupMocksForGetOperationStatus(self, mock_result, operation_scope):
77 """A helper class for setting up mocks for testGetOperationStatus*.
78
Kevin Chengb5963882018-05-09 00:06:27 -070079 Args:
80 mock_result: The result to return by _GetOperationStatus.
81 operation_scope: A value of OperationScope.
Tri Vo29ac1822016-10-01 17:06:29 -070082
Kevin Chengb5963882018-05-09 00:06:27 -070083 Returns:
84 A mock for Resource object.
85 """
Tri Vo29ac1822016-10-01 17:06:29 -070086 resource_mock = mock.MagicMock()
87 mock_api = mock.MagicMock()
88 if operation_scope == gcompute_client.OperationScope.GLOBAL:
89 self.compute_client._service.globalOperations = mock.MagicMock(
90 return_value=resource_mock)
91 elif operation_scope == gcompute_client.OperationScope.ZONE:
92 self.compute_client._service.zoneOperations = mock.MagicMock(
93 return_value=resource_mock)
94 elif operation_scope == gcompute_client.OperationScope.REGION:
95 self.compute_client._service.regionOperations = mock.MagicMock(
96 return_value=resource_mock)
97 resource_mock.get = mock.MagicMock(return_value=mock_api)
98 mock_api.execute = mock.MagicMock(return_value=mock_result)
99 return resource_mock
100
101 def testGetOperationStatusGlobal(self):
102 """Test _GetOperationStatus for global."""
103 resource_mock = self._SetupMocksForGetOperationStatus(
104 {"status": "GOOD"}, gcompute_client.OperationScope.GLOBAL)
105 status = self.compute_client._GetOperationStatus(
106 {"name": self.OPERATION_NAME},
107 gcompute_client.OperationScope.GLOBAL)
108 self.assertEqual(status, "GOOD")
109 resource_mock.get.assert_called_with(
Kevin Chengb5963882018-05-09 00:06:27 -0700110 project=PROJECT, operation=self.OPERATION_NAME)
Tri Vo29ac1822016-10-01 17:06:29 -0700111
112 def testGetOperationStatusZone(self):
113 """Test _GetOperationStatus for zone."""
114 resource_mock = self._SetupMocksForGetOperationStatus(
115 {"status": "GOOD"}, gcompute_client.OperationScope.ZONE)
116 status = self.compute_client._GetOperationStatus(
117 {"name": self.OPERATION_NAME}, gcompute_client.OperationScope.ZONE,
118 self.ZONE)
119 self.assertEqual(status, "GOOD")
120 resource_mock.get.assert_called_with(
Kevin Chengb5963882018-05-09 00:06:27 -0700121 project=PROJECT,
Tri Vo29ac1822016-10-01 17:06:29 -0700122 operation=self.OPERATION_NAME,
123 zone=self.ZONE)
124
125 def testGetOperationStatusRegion(self):
126 """Test _GetOperationStatus for region."""
127 resource_mock = self._SetupMocksForGetOperationStatus(
128 {"status": "GOOD"}, gcompute_client.OperationScope.REGION)
129 self.compute_client._GetOperationStatus(
130 {"name": self.OPERATION_NAME},
131 gcompute_client.OperationScope.REGION, self.REGION)
132 resource_mock.get.assert_called_with(
Kevin Chengb5963882018-05-09 00:06:27 -0700133 project=PROJECT, operation=self.OPERATION_NAME, region=self.REGION)
Tri Vo29ac1822016-10-01 17:06:29 -0700134
135 def testGetOperationStatusError(self):
136 """Test _GetOperationStatus failed."""
137 self._SetupMocksForGetOperationStatus(
138 {"error": {"errors": ["error1", "error2"]}},
139 gcompute_client.OperationScope.GLOBAL)
140 self.assertRaisesRegexp(errors.DriverError,
141 "Get operation state failed.*error1.*error2",
142 self.compute_client._GetOperationStatus,
143 {"name": self.OPERATION_NAME},
144 gcompute_client.OperationScope.GLOBAL)
145
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700146 @mock.patch.object(errors, "GceOperationTimeoutError")
147 @mock.patch.object(utils, "PollAndWait")
148 def testWaitOnOperation(self, mock_poll, mock_gce_operation_timeout_error):
Tri Vo29ac1822016-10-01 17:06:29 -0700149 """Test WaitOnOperation."""
150 mock_error = mock.MagicMock()
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700151 mock_gce_operation_timeout_error.return_value = mock_error
Tri Vo29ac1822016-10-01 17:06:29 -0700152 self.compute_client.WaitOnOperation(
153 operation={"name": self.OPERATION_NAME},
154 operation_scope=gcompute_client.OperationScope.REGION,
155 scope_name=self.REGION)
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700156 mock_poll.assert_called_with(
Tri Vo29ac1822016-10-01 17:06:29 -0700157 func=self.compute_client._GetOperationStatus,
158 expected_return="DONE",
159 timeout_exception=mock_error,
160 timeout_secs=self.compute_client.OPERATION_TIMEOUT_SECS,
Kevin Chengb5963882018-05-09 00:06:27 -0700161 sleep_interval_secs=self.compute_client.OPERATION_POLL_INTERVAL_SECS,
Tri Vo29ac1822016-10-01 17:06:29 -0700162 operation={"name": self.OPERATION_NAME},
163 operation_scope=gcompute_client.OperationScope.REGION,
164 scope_name=self.REGION)
165
Kevin Chengb5963882018-05-09 00:06:27 -0700166 def testGetImage(self):
167 """Test GetImage."""
168 resource_mock = mock.MagicMock()
169 mock_api = mock.MagicMock()
170 self.compute_client._service.images = mock.MagicMock(
171 return_value=resource_mock)
172 resource_mock.get = mock.MagicMock(return_value=mock_api)
173 mock_api.execute = mock.MagicMock(return_value={"name": self.IMAGE})
174 result = self.compute_client.GetImage(self.IMAGE)
175 self.assertEqual(result, {"name": self.IMAGE})
176 resource_mock.get.assert_called_with(project=PROJECT, image=self.IMAGE)
177
178 def testGetImageOther(self):
179 """Test GetImage with other project."""
180 resource_mock = mock.MagicMock()
181 mock_api = mock.MagicMock()
182 self.compute_client._service.images = mock.MagicMock(
183 return_value=resource_mock)
184 resource_mock.get = mock.MagicMock(return_value=mock_api)
185 mock_api.execute = mock.MagicMock(return_value={"name": self.IMAGE_OTHER})
186 result = self.compute_client.GetImage(
187 image_name=self.IMAGE_OTHER,
188 image_project=self.PROJECT_OTHER)
189 self.assertEqual(result, {"name": self.IMAGE_OTHER})
190 resource_mock.get.assert_called_with(
191 project=self.PROJECT_OTHER, image=self.IMAGE_OTHER)
192
193 # pyformat: disable
194 @parameterized.parameters(
195 (GS_IMAGE_SOURCE_URI, None, None,
196 {"name": IMAGE,
197 "rawDisk": {"source": GS_IMAGE_SOURCE_URI}}),
198 (None, GS_IMAGE_SOURCE_DISK, None,
199 {"name": IMAGE,
200 "sourceDisk": GS_IMAGE_SOURCE_DISK}),
201 (None, GS_IMAGE_SOURCE_DISK, {"label1": "xxx"},
202 {"name": IMAGE,
203 "sourceDisk": GS_IMAGE_SOURCE_DISK,
204 "labels": {"label1": "xxx"}}))
205 # pyformat: enable
206 def testCreateImage(self, source_uri, source_disk, labels, expected_body):
Tri Vo29ac1822016-10-01 17:06:29 -0700207 """Test CreateImage."""
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700208 mock_wait = self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
Tri Vo29ac1822016-10-01 17:06:29 -0700209 resource_mock = mock.MagicMock()
210 self.compute_client._service.images = mock.MagicMock(
211 return_value=resource_mock)
212 resource_mock.insert = mock.MagicMock()
Tri Vo29ac1822016-10-01 17:06:29 -0700213 self.compute_client.CreateImage(
Kevin Chengb5963882018-05-09 00:06:27 -0700214 image_name=self.IMAGE, source_uri=source_uri,
215 source_disk=source_disk, labels=labels)
Tri Vo29ac1822016-10-01 17:06:29 -0700216 resource_mock.insert.assert_called_with(
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700217 project=PROJECT, body=expected_body)
218 mock_wait.assert_called_with(
Tri Vo29ac1822016-10-01 17:06:29 -0700219 operation=mock.ANY,
220 operation_scope=gcompute_client.OperationScope.GLOBAL)
221
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700222 @mock.patch.object(gcompute_client.ComputeClient, "GetImage")
223 def testSetImageLabel(self, mock_get_image):
224 """Test SetImageLabel."""
225 with mock.patch.object(self.compute_client._service, "images",
226 return_value=mock.MagicMock(
227 setLabels=mock.MagicMock())) as _:
228 image = {"name": self.IMAGE,
229 "sourceDisk": GS_IMAGE_SOURCE_DISK,
230 "labelFingerprint": self.IMAGE_FINGERPRINT,
231 "labels": {"a": "aaa", "b": "bbb"}}
232 mock_get_image.return_value = image
233 new_labels = {"a": "xxx", "c": "ccc"}
234 # Test
235 self.compute_client.SetImageLabels(
236 self.IMAGE, new_labels)
237 # Check result
238 expected_labels = {"a": "xxx", "b": "bbb", "c": "ccc"}
239 self.compute_client._service.images().setLabels.assert_called_with(
240 project=PROJECT,
241 resource=self.IMAGE,
242 body={
243 "labels": expected_labels,
244 "labelFingerprint": self.IMAGE_FINGERPRINT
245 })
Kevin Chengb5963882018-05-09 00:06:27 -0700246
247 @parameterized.parameters(
248 (GS_IMAGE_SOURCE_URI, GS_IMAGE_SOURCE_DISK),
249 (None, None))
250 def testCreateImageRaiseDriverError(self, source_uri, source_disk):
251 """Test CreateImage."""
252 self.assertRaises(errors.DriverError, self.compute_client.CreateImage,
253 image_name=self.IMAGE, source_uri=source_uri,
254 source_disk=source_disk)
255
Tri Vo29ac1822016-10-01 17:06:29 -0700256
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700257 @mock.patch.object(gcompute_client.ComputeClient, "DeleteImage")
258 @mock.patch.object(gcompute_client.ComputeClient, "CheckImageExists",
259 return_value=True)
260 @mock.patch.object(gcompute_client.ComputeClient, "WaitOnOperation",
261 side_effect=errors.DriverError("Expected fake error"))
262 def testCreateImageFail(self, mock_wait, mock_check, mock_delete):
263 """Test CreateImage fails."""
Tri Vo29ac1822016-10-01 17:06:29 -0700264 resource_mock = mock.MagicMock()
265 self.compute_client._service.images = mock.MagicMock(
266 return_value=resource_mock)
267 resource_mock.insert = mock.MagicMock()
268
269 expected_body = {
270 "name": self.IMAGE,
271 "rawDisk": {
Kevin Chengb5963882018-05-09 00:06:27 -0700272 "source": GS_IMAGE_SOURCE_URI,
Tri Vo29ac1822016-10-01 17:06:29 -0700273 },
274 }
275 self.assertRaisesRegexp(
276 errors.DriverError,
277 "Expected fake error",
278 self.compute_client.CreateImage,
279 image_name=self.IMAGE,
Kevin Chengb5963882018-05-09 00:06:27 -0700280 source_uri=GS_IMAGE_SOURCE_URI)
Tri Vo29ac1822016-10-01 17:06:29 -0700281 resource_mock.insert.assert_called_with(
Kevin Chengb5963882018-05-09 00:06:27 -0700282 project=PROJECT, body=expected_body)
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700283 mock_wait.assert_called_with(
Tri Vo29ac1822016-10-01 17:06:29 -0700284 operation=mock.ANY,
285 operation_scope=gcompute_client.OperationScope.GLOBAL)
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700286 mock_check.assert_called_with(self.IMAGE)
287 mock_delete.assert_called_with(self.IMAGE)
Tri Vo29ac1822016-10-01 17:06:29 -0700288
289 def testCheckImageExistsTrue(self):
290 """Test CheckImageExists return True."""
291 resource_mock = mock.MagicMock()
292 mock_api = mock.MagicMock()
293 self.compute_client._service.images = mock.MagicMock(
294 return_value=resource_mock)
295 resource_mock.get = mock.MagicMock(return_value=mock_api)
296 mock_api.execute = mock.MagicMock(return_value={"name": self.IMAGE})
297 self.assertTrue(self.compute_client.CheckImageExists(self.IMAGE))
298
299 def testCheckImageExistsFalse(self):
300 """Test CheckImageExists return False."""
301 resource_mock = mock.MagicMock()
302 mock_api = mock.MagicMock()
303 self.compute_client._service.images = mock.MagicMock(
304 return_value=resource_mock)
305 resource_mock.get = mock.MagicMock(return_value=mock_api)
306 mock_api.execute = mock.MagicMock(
307 side_effect=errors.ResourceNotFoundError(404, "no image"))
308 self.assertFalse(self.compute_client.CheckImageExists(self.IMAGE))
309
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700310 @mock.patch.object(gcompute_client.ComputeClient, "WaitOnOperation")
311 def testDeleteImage(self, mock_wait):
Tri Vo29ac1822016-10-01 17:06:29 -0700312 """Test DeleteImage."""
Tri Vo29ac1822016-10-01 17:06:29 -0700313 resource_mock = mock.MagicMock()
314 self.compute_client._service.images = mock.MagicMock(
315 return_value=resource_mock)
316 resource_mock.delete = mock.MagicMock()
317 self.compute_client.DeleteImage(self.IMAGE)
318 resource_mock.delete.assert_called_with(
Kevin Chengb5963882018-05-09 00:06:27 -0700319 project=PROJECT, image=self.IMAGE)
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700320 self.assertTrue(mock_wait.called)
Tri Vo29ac1822016-10-01 17:06:29 -0700321
322 def _SetupBatchHttpRequestMock(self):
323 """Setup BatchHttpRequest mock."""
324 requests = {}
325
326 def _Add(request, callback, request_id):
327 requests[request_id] = (request, callback)
328
329 def _Execute():
330 for rid in requests:
331 _, callback = requests[rid]
332 callback(
333 request_id=rid, response=mock.MagicMock(), exception=None)
Tri Vo29ac1822016-10-01 17:06:29 -0700334 mock_batch = mock.MagicMock()
335 mock_batch.add = _Add
336 mock_batch.execute = _Execute
337 self.Patch(apiclient.http, "BatchHttpRequest", return_value=mock_batch)
338
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700339 @mock.patch.object(gcompute_client.ComputeClient, "WaitOnOperation")
340 def testDeleteImages(self, mock_wait):
Tri Vo29ac1822016-10-01 17:06:29 -0700341 """Test DeleteImages."""
342 self._SetupBatchHttpRequestMock()
Tri Vo29ac1822016-10-01 17:06:29 -0700343 fake_images = ["fake_image_1", "fake_image_2"]
344 mock_api = mock.MagicMock()
345 resource_mock = mock.MagicMock()
346 self.compute_client._service.images = mock.MagicMock(
347 return_value=resource_mock)
348 resource_mock.delete = mock.MagicMock(return_value=mock_api)
349 # Call the API.
350 deleted, failed, error_msgs = self.compute_client.DeleteImages(
351 fake_images)
352 # Verify
Kevin Chengb5963882018-05-09 00:06:27 -0700353 calls = [
354 mock.call(project=PROJECT, image="fake_image_1"),
355 mock.call(project=PROJECT, image="fake_image_2")
356 ]
Tri Vo29ac1822016-10-01 17:06:29 -0700357 resource_mock.delete.assert_has_calls(calls, any_order=True)
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700358 self.assertEqual(mock_wait.call_count, 2)
Tri Vo29ac1822016-10-01 17:06:29 -0700359 self.assertEqual(error_msgs, [])
360 self.assertEqual(failed, [])
361 self.assertEqual(set(deleted), set(fake_images))
362
363 def testListImages(self):
364 """Test ListImages."""
365 fake_token = "fake_next_page_token"
366 image_1 = "image_1"
367 image_2 = "image_2"
368 response_1 = {"items": [image_1], "nextPageToken": fake_token}
369 response_2 = {"items": [image_2]}
370 self.Patch(
371 gcompute_client.ComputeClient,
372 "Execute",
373 side_effect=[response_1, response_2])
374 resource_mock = mock.MagicMock()
375 self.compute_client._service.images = mock.MagicMock(
376 return_value=resource_mock)
377 resource_mock.list = mock.MagicMock()
378 images = self.compute_client.ListImages()
379 calls = [
Kevin Chengb5963882018-05-09 00:06:27 -0700380 mock.call(project=PROJECT, filter=None, pageToken=None),
381 mock.call(project=PROJECT, filter=None, pageToken=fake_token)
Tri Vo29ac1822016-10-01 17:06:29 -0700382 ]
383 resource_mock.list.assert_has_calls(calls)
384 self.assertEqual(images, [image_1, image_2])
385
Kevin Chengb5963882018-05-09 00:06:27 -0700386 def testListImagesFromExternalProject(self):
387 """Test ListImages which accepts different project."""
388 image = "image_1"
389 response = {"items": [image]}
390 self.Patch(gcompute_client.ComputeClient, "Execute", side_effect=[response])
391 resource_mock = mock.MagicMock()
392 self.compute_client._service.images = mock.MagicMock(
393 return_value=resource_mock)
394 resource_mock.list = mock.MagicMock()
395 images = self.compute_client.ListImages(
396 image_project="fake-project-2")
397 calls = [
398 mock.call(project="fake-project-2", filter=None, pageToken=None)]
399 resource_mock.list.assert_has_calls(calls)
400 self.assertEqual(images, [image])
401
Tri Vo29ac1822016-10-01 17:06:29 -0700402 def testGetInstance(self):
403 """Test GetInstance."""
404 resource_mock = mock.MagicMock()
405 mock_api = mock.MagicMock()
406 self.compute_client._service.instances = mock.MagicMock(
407 return_value=resource_mock)
408 resource_mock.get = mock.MagicMock(return_value=mock_api)
409 mock_api.execute = mock.MagicMock(return_value={"name": self.INSTANCE})
410 result = self.compute_client.GetInstance(self.INSTANCE, self.ZONE)
411 self.assertEqual(result, {"name": self.INSTANCE})
412 resource_mock.get.assert_called_with(
Kevin Chengb5963882018-05-09 00:06:27 -0700413 project=PROJECT, zone=self.ZONE, instance=self.INSTANCE)
Tri Vo29ac1822016-10-01 17:06:29 -0700414
415 def testListInstances(self):
416 """Test ListInstances."""
417 fake_token = "fake_next_page_token"
418 instance_1 = "instance_1"
419 instance_2 = "instance_2"
420 response_1 = {"items": [instance_1], "nextPageToken": fake_token}
421 response_2 = {"items": [instance_2]}
422 self.Patch(
423 gcompute_client.ComputeClient,
424 "Execute",
425 side_effect=[response_1, response_2])
426 resource_mock = mock.MagicMock()
427 self.compute_client._service.instances = mock.MagicMock(
428 return_value=resource_mock)
429 resource_mock.list = mock.MagicMock()
430 instances = self.compute_client.ListInstances(self.ZONE)
431 calls = [
432 mock.call(
Kevin Chengb5963882018-05-09 00:06:27 -0700433 project=PROJECT,
Tri Vo29ac1822016-10-01 17:06:29 -0700434 zone=self.ZONE,
435 filter=None,
436 pageToken=None),
437 mock.call(
Kevin Chengb5963882018-05-09 00:06:27 -0700438 project=PROJECT,
Tri Vo29ac1822016-10-01 17:06:29 -0700439 zone=self.ZONE,
440 filter=None,
441 pageToken=fake_token),
442 ]
443 resource_mock.list.assert_has_calls(calls)
444 self.assertEqual(instances, [instance_1, instance_2])
445
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700446 @mock.patch.object(gcompute_client.ComputeClient, "GetImage")
447 @mock.patch.object(gcompute_client.ComputeClient, "GetNetworkUrl")
448 @mock.patch.object(gcompute_client.ComputeClient, "GetMachineType")
449 @mock.patch.object(gcompute_client.ComputeClient, "WaitOnOperation")
450 def testCreateInstance(self, mock_wait, mock_get_mach_type,
451 mock_get_network_url, mock_get_image):
Tri Vo29ac1822016-10-01 17:06:29 -0700452 """Test CreateInstance."""
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700453 mock_get_mach_type.return_value = {"selfLink": self.MACHINE_TYPE_URL}
454 mock_get_network_url.return_value = self.NETWORK_URL
455 mock_get_image.return_value = {"selfLink": self.IMAGE_URL}
Tri Vo29ac1822016-10-01 17:06:29 -0700456 resource_mock = mock.MagicMock()
457 self.compute_client._service.instances = mock.MagicMock(
458 return_value=resource_mock)
459 resource_mock.insert = mock.MagicMock()
460
461 expected_body = {
462 "machineType": self.MACHINE_TYPE_URL,
463 "name": self.INSTANCE,
464 "networkInterfaces": [
465 {
466 "network": self.NETWORK_URL,
467 "accessConfigs": [
468 {"name": "External NAT",
469 "type": "ONE_TO_ONE_NAT"}
470 ],
471 }
472 ],
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700473 "disks": [self._disk_args],
Tri Vo29ac1822016-10-01 17:06:29 -0700474 "serviceAccounts": [
475 {"email": "default",
476 "scopes": self.compute_client.DEFAULT_INSTANCE_SCOPE}
477 ],
478 "metadata": {
479 "items": [{"key": self.METADATA[0],
480 "value": self.METADATA[1]}],
481 },
482 }
483
484 self.compute_client.CreateInstance(
485 instance=self.INSTANCE,
486 image_name=self.IMAGE,
487 machine_type=self.MACHINE_TYPE,
488 metadata={self.METADATA[0]: self.METADATA[1]},
489 network=self.NETWORK,
490 zone=self.ZONE)
491
492 resource_mock.insert.assert_called_with(
Kevin Chengb5963882018-05-09 00:06:27 -0700493 project=PROJECT, zone=self.ZONE, body=expected_body)
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700494 mock_wait.assert_called_with(
Tri Vo29ac1822016-10-01 17:06:29 -0700495 mock.ANY,
496 operation_scope=gcompute_client.OperationScope.ZONE,
497 scope_name=self.ZONE)
498
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700499 @mock.patch.object(gcompute_client.ComputeClient, "GetAcceleratorUrl")
500 @mock.patch.object(gcompute_client.ComputeClient, "GetImage")
501 @mock.patch.object(gcompute_client.ComputeClient, "GetNetworkUrl")
502 @mock.patch.object(gcompute_client.ComputeClient, "GetMachineType")
503 @mock.patch.object(gcompute_client.ComputeClient, "WaitOnOperation")
504 def testCreateInstanceWithGpu(self, mock_wait, mock_get_mach,
505 mock_get_network, mock_get_image,
506 mock_get_accel):
Kevin Chengb5963882018-05-09 00:06:27 -0700507 """Test CreateInstance with a GPU parameter not set to None."""
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700508 mock_get_mach.return_value = {"selfLink": self.MACHINE_TYPE_URL}
509 mock_get_network.return_value = self.NETWORK_URL
510 mock_get_accel.return_value = self.ACCELERATOR_URL
511 mock_get_image.return_value = {"selfLink": self.IMAGE_URL}
Kevin Chengb5963882018-05-09 00:06:27 -0700512
513 resource_mock = mock.MagicMock()
514 self.compute_client._service.instances = mock.MagicMock(
515 return_value=resource_mock)
516 resource_mock.insert = mock.MagicMock()
517
518 expected_body = {
519 "machineType":
520 self.MACHINE_TYPE_URL,
521 "name":
522 self.INSTANCE,
523 "networkInterfaces": [{
524 "network":
525 self.NETWORK_URL,
526 "accessConfigs": [{
527 "name": "External NAT",
528 "type": "ONE_TO_ONE_NAT"
529 }],
530 }],
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700531 "disks": [self._disk_args],
Kevin Chengb5963882018-05-09 00:06:27 -0700532 "serviceAccounts": [{
533 "email": "default",
534 "scopes": self.compute_client.DEFAULT_INSTANCE_SCOPE
535 }],
536 "scheduling": {
537 "onHostMaintenance": "terminate"
538 },
539 "guestAccelerators": [{
540 "acceleratorCount": 1,
541 "acceleratorType": "http://speedy-gpu"
542 }],
543 "metadata": {
544 "items": [{
545 "key": self.METADATA[0],
546 "value": self.METADATA[1]
547 }],
548 },
549 }
550
551 self.compute_client.CreateInstance(
552 instance=self.INSTANCE,
553 image_name=self.IMAGE,
554 machine_type=self.MACHINE_TYPE,
555 metadata={self.METADATA[0]: self.METADATA[1]},
556 network=self.NETWORK,
557 zone=self.ZONE,
558 gpu=self.GPU)
559
560 resource_mock.insert.assert_called_with(
561 project=PROJECT, zone=self.ZONE, body=expected_body)
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700562 mock_wait.assert_called_with(
Kevin Chengb5963882018-05-09 00:06:27 -0700563 mock.ANY, operation_scope=gcompute_client.OperationScope.ZONE,
564 scope_name=self.ZONE)
565
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700566 @mock.patch.object(gcompute_client.ComputeClient, "WaitOnOperation")
567 def testDeleteInstance(self, mock_wait):
Tri Vo29ac1822016-10-01 17:06:29 -0700568 """Test DeleteInstance."""
Tri Vo29ac1822016-10-01 17:06:29 -0700569 resource_mock = mock.MagicMock()
570 self.compute_client._service.instances = mock.MagicMock(
571 return_value=resource_mock)
572 resource_mock.delete = mock.MagicMock()
573 self.compute_client.DeleteInstance(
574 instance=self.INSTANCE, zone=self.ZONE)
575 resource_mock.delete.assert_called_with(
Kevin Chengb5963882018-05-09 00:06:27 -0700576 project=PROJECT, zone=self.ZONE, instance=self.INSTANCE)
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700577 mock_wait.assert_called_with(
Tri Vo29ac1822016-10-01 17:06:29 -0700578 mock.ANY,
579 operation_scope=gcompute_client.OperationScope.ZONE,
580 scope_name=self.ZONE)
581
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700582 @mock.patch.object(gcompute_client.ComputeClient, "WaitOnOperation")
583 def testDeleteInstances(self, mock_wait):
Tri Vo29ac1822016-10-01 17:06:29 -0700584 """Test DeleteInstances."""
585 self._SetupBatchHttpRequestMock()
Tri Vo29ac1822016-10-01 17:06:29 -0700586 fake_instances = ["fake_instance_1", "fake_instance_2"]
587 mock_api = mock.MagicMock()
588 resource_mock = mock.MagicMock()
589 self.compute_client._service.instances = mock.MagicMock(
590 return_value=resource_mock)
591 resource_mock.delete = mock.MagicMock(return_value=mock_api)
592 deleted, failed, error_msgs = self.compute_client.DeleteInstances(
593 fake_instances, self.ZONE)
594 calls = [
595 mock.call(
Kevin Chengb5963882018-05-09 00:06:27 -0700596 project=PROJECT,
Tri Vo29ac1822016-10-01 17:06:29 -0700597 instance="fake_instance_1",
Kevin Chengb5963882018-05-09 00:06:27 -0700598 zone=self.ZONE),
599 mock.call(
600 project=PROJECT,
601 instance="fake_instance_2",
602 zone=self.ZONE)
Tri Vo29ac1822016-10-01 17:06:29 -0700603 ]
604 resource_mock.delete.assert_has_calls(calls, any_order=True)
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700605 self.assertEqual(mock_wait.call_count, 2)
Tri Vo29ac1822016-10-01 17:06:29 -0700606 self.assertEqual(error_msgs, [])
607 self.assertEqual(failed, [])
608 self.assertEqual(set(deleted), set(fake_instances))
609
Kevin Chengb5963882018-05-09 00:06:27 -0700610 @parameterized.parameters(("fake-image-project", "fake-image-project"),
611 (None, PROJECT))
612 def testCreateDisk(self, source_project, expected_project_to_use):
613 """Test CreateDisk with images."""
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700614 mock_wait = self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
Kevin Chengb5963882018-05-09 00:06:27 -0700615 resource_mock = mock.MagicMock()
616 self.compute_client._service.disks = mock.MagicMock(
617 return_value=resource_mock)
618 resource_mock.insert = mock.MagicMock()
619 self.compute_client.CreateDisk(
620 "fake_disk", "fake_image", 10, self.ZONE, source_project=source_project)
621 resource_mock.insert.assert_called_with(
622 project=PROJECT,
623 zone=self.ZONE,
624 sourceImage="projects/%s/global/images/fake_image" %
625 expected_project_to_use,
626 body={
627 "name":
628 "fake_disk",
629 "sizeGb":
630 10,
631 "type":
632 "projects/%s/zones/%s/diskTypes/pd-standard" % (PROJECT,
633 self.ZONE)
634 })
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700635 self.assertTrue(mock_wait.called)
Kevin Chengb5963882018-05-09 00:06:27 -0700636
637 @parameterized.parameters(
638 (gcompute_client.PersistentDiskType.STANDARD, "pd-standard"),
639 (gcompute_client.PersistentDiskType.SSD, "pd-ssd"))
640 def testCreateDiskWithType(self, disk_type, expected_disk_type_string):
641 """Test CreateDisk with images."""
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700642 mock_wait = self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
Kevin Chengb5963882018-05-09 00:06:27 -0700643 resource_mock = mock.MagicMock()
644 self.compute_client._service.disks = mock.MagicMock(
645 return_value=resource_mock)
646 resource_mock.insert = mock.MagicMock()
647 self.compute_client.CreateDisk(
648 "fake_disk",
649 "fake_image",
650 10,
651 self.ZONE,
652 source_project="fake-project",
653 disk_type=disk_type)
654 resource_mock.insert.assert_called_with(
655 project=PROJECT,
656 zone=self.ZONE,
657 sourceImage="projects/%s/global/images/fake_image" % "fake-project",
658 body={
659 "name":
660 "fake_disk",
661 "sizeGb":
662 10,
663 "type":
664 "projects/%s/zones/%s/diskTypes/%s" %
665 (PROJECT, self.ZONE, expected_disk_type_string)
666 })
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700667 self.assertTrue(mock_wait.called)
Kevin Chengb5963882018-05-09 00:06:27 -0700668
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700669 @mock.patch.object(gcompute_client.ComputeClient, "WaitOnOperation")
670 def testAttachDisk(self, mock_wait):
Kevin Chengb5963882018-05-09 00:06:27 -0700671 """Test AttachDisk."""
Kevin Chengb5963882018-05-09 00:06:27 -0700672 resource_mock = mock.MagicMock()
673 self.compute_client._service.instances = mock.MagicMock(
674 return_value=resource_mock)
675 resource_mock.attachDisk = mock.MagicMock()
676 self.compute_client.AttachDisk(
677 "fake_instance_1", self.ZONE, deviceName="fake_disk",
678 source="fake-selfLink")
679 resource_mock.attachDisk.assert_called_with(
680 project=PROJECT,
681 zone=self.ZONE,
682 instance="fake_instance_1",
683 body={
684 "deviceName": "fake_disk",
685 "source": "fake-selfLink"
686 })
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700687 self.assertTrue(mock_wait.called)
Kevin Chengb5963882018-05-09 00:06:27 -0700688
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700689 @mock.patch.object(gcompute_client.ComputeClient, "WaitOnOperation")
690 def testDetachDisk(self, mock_wait):
Kevin Chengb5963882018-05-09 00:06:27 -0700691 """Test DetachDisk."""
Kevin Chengb5963882018-05-09 00:06:27 -0700692 resource_mock = mock.MagicMock()
693 self.compute_client._service.instances = mock.MagicMock(
694 return_value=resource_mock)
695 resource_mock.detachDisk = mock.MagicMock()
696 self.compute_client.DetachDisk("fake_instance_1", self.ZONE, "fake_disk")
697 resource_mock.detachDisk.assert_called_with(
698 project=PROJECT,
699 zone=self.ZONE,
700 instance="fake_instance_1",
701 deviceName="fake_disk")
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700702 self.assertTrue(mock_wait.called)
Kevin Chengb5963882018-05-09 00:06:27 -0700703
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700704 @mock.patch.object(gcompute_client.ComputeClient, "GetAcceleratorUrl")
705 @mock.patch.object(gcompute_client.ComputeClient, "WaitOnOperation")
706 def testAttachAccelerator(self, mock_wait, mock_get_accel):
Kevin Chengb5963882018-05-09 00:06:27 -0700707 """Test AttachAccelerator."""
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700708 mock_get_accel.return_value = self.ACCELERATOR_URL
Kevin Chengb5963882018-05-09 00:06:27 -0700709 resource_mock = mock.MagicMock()
710 self.compute_client._service.instances = mock.MagicMock(
711 return_value=resource_mock)
712 resource_mock.attachAccelerator = mock.MagicMock()
713 self.compute_client.AttachAccelerator("fake_instance_1", self.ZONE, 1,
714 "nvidia-tesla-k80")
715 resource_mock.setMachineResources.assert_called_with(
716 project=PROJECT,
717 zone=self.ZONE,
718 instance="fake_instance_1",
719 body={
720 "guestAccelerators": [{
721 "acceleratorType": self.ACCELERATOR_URL,
722 "acceleratorCount": 1
723 }]
724 })
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700725 self.assertTrue(mock_wait.called)
Kevin Chengb5963882018-05-09 00:06:27 -0700726
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700727 @mock.patch.object(gcompute_client.ComputeClient, "WaitOnOperation")
728 def testBatchExecuteOnInstances(self, mock_wait):
729 """Test BatchExecuteOnInstances."""
Tri Vo29ac1822016-10-01 17:06:29 -0700730 self._SetupBatchHttpRequestMock()
Tri Vo29ac1822016-10-01 17:06:29 -0700731 action = mock.MagicMock(return_value=mock.MagicMock())
732 fake_instances = ["fake_instance_1", "fake_instance_2"]
733 done, failed, error_msgs = self.compute_client._BatchExecuteOnInstances(
734 fake_instances, self.ZONE, action)
735 calls = [mock.call(instance="fake_instance_1"),
736 mock.call(instance="fake_instance_2")]
737 action.assert_has_calls(calls, any_order=True)
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700738 self.assertEqual(mock_wait.call_count, 2)
Tri Vo29ac1822016-10-01 17:06:29 -0700739 self.assertEqual(set(done), set(fake_instances))
740 self.assertEqual(error_msgs, [])
741 self.assertEqual(failed, [])
742
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700743 @mock.patch.object(gcompute_client.ComputeClient, "WaitOnOperation")
744 def testResetInstance(self, mock_wait):
Tri Vo29ac1822016-10-01 17:06:29 -0700745 """Test ResetInstance."""
Tri Vo29ac1822016-10-01 17:06:29 -0700746 resource_mock = mock.MagicMock()
747 self.compute_client._service.instances = mock.MagicMock(
748 return_value=resource_mock)
749 resource_mock.reset = mock.MagicMock()
750 self.compute_client.ResetInstance(
751 instance=self.INSTANCE, zone=self.ZONE)
752 resource_mock.reset.assert_called_with(
Kevin Chengb5963882018-05-09 00:06:27 -0700753 project=PROJECT, zone=self.ZONE, instance=self.INSTANCE)
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700754 mock_wait.assert_called_with(
Tri Vo29ac1822016-10-01 17:06:29 -0700755 mock.ANY,
756 operation_scope=gcompute_client.OperationScope.ZONE,
757 scope_name=self.ZONE)
758
759 def _CompareMachineSizeTestHelper(self,
760 machine_info_1,
761 machine_info_2,
762 expected_result=None,
763 expected_error_type=None):
764 """Helper class for testing CompareMachineSize.
765
Kevin Chengb5963882018-05-09 00:06:27 -0700766 Args:
767 machine_info_1: A dictionary representing the first machine size.
768 machine_info_2: A dictionary representing the second machine size.
769 expected_result: An integer, 0, 1 or -1, or None if not set.
770 expected_error_type: An exception type, if set will check for exception.
771 """
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700772 mock_get_mach_type = self.Patch(
Tri Vo29ac1822016-10-01 17:06:29 -0700773 gcompute_client.ComputeClient,
774 "GetMachineType",
775 side_effect=[machine_info_1, machine_info_2])
776 if expected_error_type:
777 self.assertRaises(expected_error_type,
778 self.compute_client.CompareMachineSize, "name1",
779 "name2", self.ZONE)
780 else:
781 result = self.compute_client.CompareMachineSize("name1", "name2",
782 self.ZONE)
783 self.assertEqual(result, expected_result)
784
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700785 mock_get_mach_type.assert_has_calls(
Tri Vo29ac1822016-10-01 17:06:29 -0700786 [mock.call("name1", self.ZONE), mock.call("name2", self.ZONE)])
787
788 def testCompareMachineSizeSmall(self):
789 """Test CompareMachineSize where the first one is smaller."""
790 machine_info_1 = {"guestCpus": 10, "memoryMb": 100}
791 machine_info_2 = {"guestCpus": 10, "memoryMb": 200}
792 self._CompareMachineSizeTestHelper(machine_info_1, machine_info_2, -1)
793
794 def testCompareMachineSizeLarge(self):
795 """Test CompareMachineSize where the first one is larger."""
796 machine_info_1 = {"guestCpus": 10, "memoryMb": 200}
797 machine_info_2 = {"guestCpus": 10, "memoryMb": 100}
798 self._CompareMachineSizeTestHelper(machine_info_1, machine_info_2, 1)
799
800 def testCompareMachineSizeEqual(self):
801 """Test CompareMachineSize where two machine sizes are equal."""
802 machine_info = {"guestCpus": 10, "memoryMb": 100}
803 self._CompareMachineSizeTestHelper(machine_info, machine_info, 0)
804
805 def testCompareMachineSizeBadMetric(self):
806 """Test CompareMachineSize with bad metric."""
Kevin Chengb5963882018-05-09 00:06:27 -0700807 machine_info = {"unknown_metric": 10, "memoryMb": 100}
Tri Vo29ac1822016-10-01 17:06:29 -0700808 self._CompareMachineSizeTestHelper(
809 machine_info, machine_info, expected_error_type=errors.DriverError)
810
811 def testGetMachineType(self):
812 """Test GetMachineType."""
813 resource_mock = mock.MagicMock()
814 mock_api = mock.MagicMock()
815 self.compute_client._service.machineTypes = mock.MagicMock(
816 return_value=resource_mock)
817 resource_mock.get = mock.MagicMock(return_value=mock_api)
818 mock_api.execute = mock.MagicMock(
819 return_value={"name": self.MACHINE_TYPE})
820 result = self.compute_client.GetMachineType(self.MACHINE_TYPE,
821 self.ZONE)
822 self.assertEqual(result, {"name": self.MACHINE_TYPE})
823 resource_mock.get.assert_called_with(
Kevin Chengb5963882018-05-09 00:06:27 -0700824 project=PROJECT,
Tri Vo29ac1822016-10-01 17:06:29 -0700825 zone=self.ZONE,
826 machineType=self.MACHINE_TYPE)
827
828 def _GetSerialPortOutputTestHelper(self, response):
829 """Helper function for testing GetSerialPortOutput.
830
Kevin Chengb5963882018-05-09 00:06:27 -0700831 Args:
832 response: A dictionary representing a fake response.
833 """
Tri Vo29ac1822016-10-01 17:06:29 -0700834 resource_mock = mock.MagicMock()
835 mock_api = mock.MagicMock()
836 self.compute_client._service.instances = mock.MagicMock(
837 return_value=resource_mock)
838 resource_mock.getSerialPortOutput = mock.MagicMock(
839 return_value=mock_api)
840 mock_api.execute = mock.MagicMock(return_value=response)
841
842 if "contents" in response:
843 result = self.compute_client.GetSerialPortOutput(
844 instance=self.INSTANCE, zone=self.ZONE)
845 self.assertEqual(result, "fake contents")
846 else:
847 self.assertRaisesRegexp(
848 errors.DriverError,
849 "Malformed response.*",
850 self.compute_client.GetSerialPortOutput,
851 instance=self.INSTANCE,
852 zone=self.ZONE)
853 resource_mock.getSerialPortOutput.assert_called_with(
Kevin Chengb5963882018-05-09 00:06:27 -0700854 project=PROJECT,
Tri Vo29ac1822016-10-01 17:06:29 -0700855 zone=self.ZONE,
856 instance=self.INSTANCE,
857 port=1)
858
859 def testGetSerialPortOutput(self):
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700860 """Test GetSerialPortOutput."""
Tri Vo29ac1822016-10-01 17:06:29 -0700861 response = {"contents": "fake contents"}
862 self._GetSerialPortOutputTestHelper(response)
863
864 def testGetSerialPortOutputFail(self):
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700865 """Test GetSerialPortOutputFail."""
Tri Vo29ac1822016-10-01 17:06:29 -0700866 response = {"malformed": "fake contents"}
867 self._GetSerialPortOutputTestHelper(response)
868
869 def testGetInstanceNamesByIPs(self):
870 """Test GetInstanceNamesByIPs."""
871 good_instance = {
872 "name": "instance_1",
873 "networkInterfaces": [
874 {
875 "accessConfigs": [
876 {"natIP": "172.22.22.22"},
877 ],
878 },
879 ],
880 }
881 bad_instance = {"name": "instance_2"}
882 self.Patch(
883 gcompute_client.ComputeClient,
884 "ListInstances",
885 return_value=[good_instance, bad_instance])
886 ip_name_map = self.compute_client.GetInstanceNamesByIPs(
887 ips=["172.22.22.22", "172.22.22.23"], zone=self.ZONE)
888 self.assertEqual(ip_name_map, {"172.22.22.22": "instance_1",
889 "172.22.22.23": None})
890
891 def testAddSshRsa(self):
892 """Test AddSshRsa.."""
893 fake_user = "fake_user"
894 sshkey = (
895 "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBkTOTRze9v2VOqkkf7RG"
896 "jSkg6Z2kb9Q9UHsDGatvend3fmjIw1Tugg0O7nnjlPkskmlgyd4a/j99WOeLL"
897 "CPk6xPyoVjrPUVBU/pAk09ORTC4Zqk6YjlW7LOfzvqmXhmIZfYu6Q4Yt50pZzhl"
898 "lllfu26nYjY7Tg12D019nJi/kqPX5+NKgt0LGXTu8T1r2Gav/q4V7QRWQrB8Eiu"
899 "pxXR7I2YhynqovkEt/OXG4qWgvLEXGsWtSQs0CtCzqEVxz0Y9ECr7er4VdjSQxV"
900 "AaeLAsQsK9ROae8hMBFZ3//8zLVapBwpuffCu+fUoql9qeV9xagZcc9zj8XOUOW"
901 "ApiihqNL1111 test@test1.org")
902 project = {
903 "commonInstanceMetadata": {
904 "kind": "compute#metadata",
905 "fingerprint": "a-23icsyx4E=",
906 "items": [
907 {
908 "key": "sshKeys",
909 "value": "user:key"
910 }
911 ]
912 }
913 }
914 expected = {
915 "kind": "compute#metadata",
916 "fingerprint": "a-23icsyx4E=",
917 "items": [
918 {
919 "key": "sshKeys",
920 "value": "user:key\n%s:%s" % (fake_user, sshkey)
921 }
922 ]
923 }
924
925 self.Patch(os.path, "exists", return_value=True)
926 m = mock.mock_open(read_data=sshkey)
927 self.Patch(__builtins__, "open", m, create=True)
928 self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
929 self.Patch(
930 gcompute_client.ComputeClient, "GetProject", return_value=project)
931 resource_mock = mock.MagicMock()
932 self.compute_client._service.projects = mock.MagicMock(
933 return_value=resource_mock)
934 resource_mock.setCommonInstanceMetadata = mock.MagicMock()
935
936 self.compute_client.AddSshRsa(fake_user, "/path/to/test_rsa.pub")
937 resource_mock.setCommonInstanceMetadata.assert_called_with(
Kevin Chengb5963882018-05-09 00:06:27 -0700938 project=PROJECT, body=expected)
Tri Vo29ac1822016-10-01 17:06:29 -0700939
940 def testAddSshRsaInvalidKey(self):
941 """Test AddSshRsa.."""
942 fake_user = "fake_user"
943 sshkey = "ssh-rsa v2VOqkkf7RGL1111 test@test1.org"
944 project = {
945 "commonInstanceMetadata": {
946 "kind": "compute#metadata",
947 "fingerprint": "a-23icsyx4E=",
948 "items": [
949 {
950 "key": "sshKeys",
951 "value": "user:key"
952 }
953 ]
954 }
955 }
956 self.Patch(os.path, "exists", return_value=True)
957 m = mock.mock_open(read_data=sshkey)
958 self.Patch(__builtins__, "open", m, create=True)
959 self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
960 self.Patch(
961 gcompute_client.ComputeClient, "GetProject", return_value=project)
962 self.assertRaisesRegexp(errors.DriverError, "rsa key is invalid:*",
963 self.compute_client.AddSshRsa, fake_user,
964 "/path/to/test_rsa.pub")
965
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700966 @mock.patch.object(gcompute_client.ComputeClient, "WaitOnOperation")
967 def testDeleteDisks(self, mock_wait):
Tri Vo29ac1822016-10-01 17:06:29 -0700968 """Test DeleteDisks."""
969 self._SetupBatchHttpRequestMock()
Tri Vo29ac1822016-10-01 17:06:29 -0700970 fake_disks = ["fake_disk_1", "fake_disk_2"]
971 mock_api = mock.MagicMock()
972 resource_mock = mock.MagicMock()
973 self.compute_client._service.disks = mock.MagicMock(
974 return_value=resource_mock)
975 resource_mock.delete = mock.MagicMock(return_value=mock_api)
976 # Call the API.
977 deleted, failed, error_msgs = self.compute_client.DeleteDisks(
978 fake_disks, zone=self.ZONE)
979 # Verify
Kevin Chengb5963882018-05-09 00:06:27 -0700980 calls = [
981 mock.call(project=PROJECT, disk="fake_disk_1", zone=self.ZONE),
982 mock.call(project=PROJECT, disk="fake_disk_2", zone=self.ZONE)
983 ]
Tri Vo29ac1822016-10-01 17:06:29 -0700984 resource_mock.delete.assert_has_calls(calls, any_order=True)
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700985 self.assertEqual(mock_wait.call_count, 2)
Tri Vo29ac1822016-10-01 17:06:29 -0700986 self.assertEqual(error_msgs, [])
987 self.assertEqual(failed, [])
988 self.assertEqual(set(deleted), set(fake_disks))
989
Kevin Chengb5963882018-05-09 00:06:27 -0700990 def testRetryOnFingerPrintError(self):
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700991 """Test RetryOnFingerPrintError."""
Kevin Chengb5963882018-05-09 00:06:27 -0700992 @utils.RetryOnException(gcompute_client._IsFingerPrintError, 10)
993 def Raise412(sentinel):
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700994 """Raise 412 HTTP exception."""
995 if not sentinel.hitFingerPrintConflict.called:
996 sentinel.hitFingerPrintConflict()
997 raise errors.HttpError(412, "resource labels have changed")
998 return "Passed"
Kevin Chengb5963882018-05-09 00:06:27 -0700999
1000 sentinel = mock.MagicMock()
1001 result = Raise412(sentinel)
1002 self.assertEqual(1, sentinel.hitFingerPrintConflict.call_count)
1003 self.assertEqual("Passed", result)
1004
Tri Vo29ac1822016-10-01 17:06:29 -07001005
1006if __name__ == "__main__":
1007 unittest.main()