aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTri Vo <trong@google.com>2016-10-01 17:06:29 -0700
committerTri Vo <trong@google.com>2016-10-06 09:03:26 -0700
commit29ac182d42965ac86ba181b9d29899da8e802afa (patch)
tree863efe19a7bd0ce0473743a3954947f277e01818
parentf6a7217f80d4e43c7479e411056e162c28999376 (diff)
downloadacloud-29ac182d42965ac86ba181b9d29899da8e802afa.tar.gz
Add unittests.
Change-Id: I55e7397c44605719bb01aa1b6d81eed2fa743cf4
-rw-r--r--internal/lib/android_compute_client_test.py153
-rw-r--r--internal/lib/base_cloud_client_test.py159
-rw-r--r--internal/lib/gcompute_client_test.py730
-rw-r--r--internal/lib/gstorage_client_test.py148
-rw-r--r--public/acloud_kernel/kernel_swapper_test.py97
-rw-r--r--public/config_test.py160
-rw-r--r--public/device_driver_test.py249
-rw-r--r--public/report_test.py64
8 files changed, 1760 insertions, 0 deletions
diff --git a/internal/lib/android_compute_client_test.py b/internal/lib/android_compute_client_test.py
new file mode 100644
index 00000000..88a2d600
--- /dev/null
+++ b/internal/lib/android_compute_client_test.py
@@ -0,0 +1,153 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 - The Android Open Source Project
+#
+# 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.
+
+"""Tests for android_compute_client."""
+
+import uuid
+
+import mock
+
+import unittest
+from acloud.internal.lib import android_compute_client
+from acloud.internal.lib import driver_test_lib
+from acloud.internal.lib import gcompute_client
+from acloud.public import errors
+
+
+class AndroidComputeClientTest(driver_test_lib.BaseDriverTest):
+ """Test AndroidComputeClient."""
+
+ PROJECT = "fake-project"
+ SERVICE_ACCOUNT_NAME = "fake@fake.com"
+ PRIVATE_KEY_PATH = "/fake/key/path"
+ IMAGE = "fake-image"
+ GS_IMAGE_SOURCE_URI = "https://storage.googleapis.com/fake-bucket/fake.tar.gz"
+ MACHINE_TYPE = "fake-machine-type"
+ MIN_MACHINE_SIZE = "fake-min-machine-size"
+ METADATA = ("metadata_key", "metadata_value")
+ NETWORK = "fake-network"
+ ZONE = "fake-zone"
+ ORIENTATION = "portrait"
+ DEVICE_RESOLUTION = "1200x1200x1200x1200"
+ METADATA = ("metadata_key", "metadata_value")
+ TARGET = "gce_x86-userdebug"
+ BUILD_ID = "2263051"
+
+ def _GetFakeConfig(self):
+ """Create a fake configuration object.
+
+ Returns:
+ A fake configuration mock object.
+ """
+ fake_cfg = mock.MagicMock()
+ fake_cfg.project = self.PROJECT
+ fake_cfg.service_account_name = self.SERVICE_ACCOUNT_NAME
+ fake_cfg.service_account_private_key_path = self.PRIVATE_KEY_PATH
+ fake_cfg.zone = self.ZONE
+ fake_cfg.machine_type = self.MACHINE_TYPE
+ fake_cfg.min_machine_size = self.MIN_MACHINE_SIZE
+ fake_cfg.network = self.NETWORK
+ fake_cfg.orientation = self.ORIENTATION
+ fake_cfg.resolution = self.DEVICE_RESOLUTION
+ fake_cfg.metadata_variable = {self.METADATA[0]: self.METADATA[1]}
+ return fake_cfg
+
+ def setUp(self):
+ """Set up the test."""
+ super(AndroidComputeClientTest, self).setUp()
+ self.Patch(android_compute_client.AndroidComputeClient,
+ "InitResourceHandle")
+ self.android_compute_client = android_compute_client.AndroidComputeClient(
+ self._GetFakeConfig(), mock.MagicMock())
+
+ def testCreateImage(self):
+ """Test CreateImage."""
+ self.Patch(gcompute_client.ComputeClient, "CreateImage")
+ self.Patch(
+ gcompute_client.ComputeClient,
+ "CheckImageExists",
+ return_value=False)
+ unique_id = uuid.uuid4()
+ image_name = "image-gce-x86-userdebug-2345-abcd"
+ self.android_compute_client.CreateImage(image_name,
+ self.GS_IMAGE_SOURCE_URI)
+ super(android_compute_client.AndroidComputeClient,
+ self.android_compute_client).CreateImage.assert_called_with(
+ image_name, self.GS_IMAGE_SOURCE_URI)
+ self.android_compute_client.CheckImageExists.assert_called_with(
+ image_name)
+
+ def testCreateInstance(self):
+ """Test CreateInstance."""
+ self.Patch(
+ gcompute_client.ComputeClient,
+ "CompareMachineSize",
+ return_value=1)
+ self.Patch(gcompute_client.ComputeClient, "CreateInstance")
+ self.Patch(
+ gcompute_client.ComputeClient,
+ "_GetDiskArgs",
+ return_value=[{"fake_arg": "fake_value"}])
+ self.Patch(
+ self.android_compute_client,
+ "_GetExtraDiskArgs",
+ return_value=[{"fake_extra_arg": "fake_extra_value"}])
+ instance_name = "gce-x86-userdebug-2345-abcd"
+ extra_disk_name = "gce-x86-userdebug-2345-abcd-data"
+ expected_metadata = {
+ self.METADATA[0]: self.METADATA[1],
+ "cfg_sta_display_resolution": self.DEVICE_RESOLUTION,
+ "t_force_orientation": self.ORIENTATION,
+ }
+
+ expected_disk_args = [
+ {"fake_arg": "fake_value"}, {"fake_extra_arg": "fake_extra_value"}
+ ]
+
+ self.android_compute_client.CreateInstance(instance_name, self.IMAGE,
+ extra_disk_name)
+ super(android_compute_client.AndroidComputeClient,
+ self.android_compute_client).CreateInstance.assert_called_with(
+ instance_name, self.IMAGE, self.MACHINE_TYPE,
+ expected_metadata, self.NETWORK, self.ZONE,
+ expected_disk_args)
+
+ def testCheckMachineSizeMeetsRequirement(self):
+ """Test CheckMachineSize when machine size meets requirement."""
+ self.Patch(
+ gcompute_client.ComputeClient,
+ "CompareMachineSize",
+ return_value=1)
+ self.android_compute_client._CheckMachineSize()
+ self.android_compute_client.CompareMachineSize.assert_called_with(
+ self.MACHINE_TYPE, self.MIN_MACHINE_SIZE, self.ZONE)
+
+ def testCheckMachineSizeDoesNotMeetRequirement(self):
+ """Test CheckMachineSize when machine size does not meet requirement."""
+ self.Patch(
+ gcompute_client.ComputeClient,
+ "CompareMachineSize",
+ return_value=-1)
+ self.assertRaisesRegexp(
+ errors.DriverError,
+ ".*does not meet the minimum required machine size.*",
+ self.android_compute_client._CheckMachineSize)
+ self.android_compute_client.CompareMachineSize.assert_called_with(
+ self.MACHINE_TYPE, self.MIN_MACHINE_SIZE, self.ZONE)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/internal/lib/base_cloud_client_test.py b/internal/lib/base_cloud_client_test.py
new file mode 100644
index 00000000..96eb3c32
--- /dev/null
+++ b/internal/lib/base_cloud_client_test.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 - The Android Open Source Project
+#
+# 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.
+
+"""Tests for acloud.internal.lib.base_cloud_client."""
+
+import time
+import apiclient
+import mock
+
+import unittest
+from acloud.internal.lib import base_cloud_client
+from acloud.internal.lib import driver_test_lib
+from acloud.public import errors
+
+
+class FakeError(Exception):
+ """Fake Error for testing retries."""
+
+
+class BaseCloudApiClientTest(driver_test_lib.BaseDriverTest):
+ """Test BaseCloudApiClient."""
+
+ def setUp(self):
+ """Set up test."""
+ super(BaseCloudApiClientTest, self).setUp()
+
+ def testInitResourceHandle(self):
+ """Test InitResourceHandle."""
+ # Setup mocks
+ mock_credentials = mock.MagicMock()
+ self.Patch(base_cloud_client, "build")
+ # Call the method
+ base_cloud_client.BaseCloudApiClient(mock.MagicMock())
+ base_cloud_client.build.assert_called_once_with(
+ serviceName=base_cloud_client.BaseCloudApiClient.API_NAME,
+ version=base_cloud_client.BaseCloudApiClient.API_VERSION,
+ http=mock.ANY)
+
+ def _SetupInitMocks(self):
+ """Setup mocks required to initialize a base cloud client.
+
+ Returns:
+ A base_cloud_client.BaseCloudApiClient mock.
+ """
+ self.Patch(
+ base_cloud_client.BaseCloudApiClient,
+ "InitResourceHandle",
+ return_value=mock.MagicMock())
+ return base_cloud_client.BaseCloudApiClient(mock.MagicMock())
+
+ def _SetupBatchHttpRequestMock(self, rid_to_responses, rid_to_exceptions):
+ """Setup BatchHttpRequest mock."""
+
+ rid_to_exceptions = rid_to_exceptions or {}
+ rid_to_responses = rid_to_responses or {}
+
+ def _CreatMockBatchHttpRequest():
+ """Create a mock BatchHttpRequest object."""
+ requests = {}
+
+ def _Add(request, callback, request_id):
+ requests[request_id] = (request, callback)
+
+ def _Execute():
+ for rid in requests:
+ requests[rid][0].execute()
+ _, callback = requests[rid]
+ callback(
+ request_id=rid,
+ response=rid_to_responses.get(rid),
+ exception=rid_to_exceptions.get(rid))
+
+ mock_batch = mock.MagicMock()
+ mock_batch.add = _Add
+ mock_batch.execute = _Execute
+ return mock_batch
+
+ self.Patch(
+ apiclient.http,
+ "BatchHttpRequest",
+ side_effect=_CreatMockBatchHttpRequest)
+
+ def testBatchExecute(self):
+ """Test BatchExecute."""
+ self.Patch(time, "sleep")
+ client = self._SetupInitMocks()
+ requests = {"r1": mock.MagicMock(),
+ "r2": mock.MagicMock(),
+ "r3": mock.MagicMock()}
+ response = {"name": "fake_response"}
+ error_1 = errors.HttpError(503, "fake retriable error.")
+ error_2 = FakeError("fake retriable error.")
+ responses = {"r1": response, "r2": None, "r3": None}
+ exceptions = {"r1": None, "r2": error_1, "r3": error_2}
+ self._SetupBatchHttpRequestMock(responses, exceptions)
+ results = client.BatchExecute(
+ requests, other_retriable_errors=(FakeError, ))
+ expected_results = {
+ "r1": (response, None),
+ "r2": (None, error_1),
+ "r3": (None, error_2)
+ }
+ self.assertEqual(results, expected_results)
+ self.assertEqual(requests["r1"].execute.call_count, 1)
+ self.assertEqual(requests["r2"].execute.call_count,
+ client.RETRY_COUNT + 1)
+ self.assertEqual(requests["r3"].execute.call_count,
+ client.RETRY_COUNT + 1)
+
+ def testListWithMultiPages(self):
+ """Test ListWithMultiPages."""
+ fake_token = "fake_next_page_token"
+ item_1 = "item_1"
+ item_2 = "item_2"
+ response_1 = {"items": [item_1], "nextPageToken": fake_token}
+ response_2 = {"items": [item_2]}
+
+ api_mock = mock.MagicMock()
+ api_mock.execute.side_effect = [response_1, response_2]
+ resource_mock = mock.MagicMock(return_value=api_mock)
+ client = self._SetupInitMocks()
+ items = client.ListWithMultiPages(
+ api_resource=resource_mock, fake_arg="fake_arg")
+ self.assertEqual(items, [item_1, item_2])
+
+ def testExecuteWithRetry(self):
+ """Test Execute is called and retries are triggered."""
+ self.Patch(time, "sleep")
+ client = self._SetupInitMocks()
+ api_mock = mock.MagicMock()
+ error = errors.HttpError(503, "fake retriable error.")
+ api_mock.execute.side_effect = error
+ self.assertRaises(errors.HttpError, client.Execute, api_mock)
+
+ api_mock = mock.MagicMock()
+ api_mock.execute.side_effect = FakeError("fake retriable error.")
+ self.assertRaises(
+ FakeError,
+ client.Execute,
+ api_mock,
+ other_retriable_errors=(FakeError, ))
+ self.assertEqual(api_mock.execute.call_count, client.RETRY_COUNT + 1)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/internal/lib/gcompute_client_test.py b/internal/lib/gcompute_client_test.py
new file mode 100644
index 00000000..2382eb1e
--- /dev/null
+++ b/internal/lib/gcompute_client_test.py
@@ -0,0 +1,730 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 - The Android Open Source Project
+#
+# 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.
+
+"""Tests for acloud.internal.lib.gcompute_client."""
+
+import os
+
+import apiclient.http
+import mock
+
+import unittest
+from acloud.internal.lib import driver_test_lib
+from acloud.internal.lib import gcompute_client
+from acloud.internal.lib import utils
+from acloud.public import errors
+
+
+class ComputeClientTest(driver_test_lib.BaseDriverTest):
+ """Test ComputeClient."""
+
+ PROJECT = "fake-project"
+ INSTANCE = "fake-instance"
+ IMAGE = "fake-image"
+ IMAGE_URL = "http://fake-image-url"
+ GS_IMAGE_SOURCE_URI = "https://storage.googleapis.com/fake-bucket/fake.tar.gz"
+ MACHINE_TYPE = "fake-machine-type"
+ MACHINE_TYPE_URL = "http://fake-machine-type-url"
+ METADATA = ("metadata_key", "metadata_value")
+ NETWORK = "fake-network"
+ NETWORK_URL = "http://fake-network-url"
+ ZONE = "fake-zone"
+ REGION = "fake-region"
+ OPERATION_NAME = "fake-op"
+
+ def setUp(self):
+ """Set up test."""
+ super(ComputeClientTest, self).setUp()
+ self.Patch(gcompute_client.ComputeClient, "InitResourceHandle")
+ fake_cfg = mock.MagicMock()
+ fake_cfg.project = self.PROJECT
+ self.compute_client = gcompute_client.ComputeClient(fake_cfg,
+ mock.MagicMock())
+ self.compute_client._service = mock.MagicMock()
+
+ def _SetupMocksForGetOperationStatus(self, mock_result, operation_scope):
+ """A helper class for setting up mocks for testGetOperationStatus*.
+
+ Args:
+ mock_result: The result to return by _GetOperationStatus.
+ operation_scope: A value of OperationScope.
+
+ Returns:
+ A mock for Resource object.
+ """
+ resource_mock = mock.MagicMock()
+ mock_api = mock.MagicMock()
+ if operation_scope == gcompute_client.OperationScope.GLOBAL:
+ self.compute_client._service.globalOperations = mock.MagicMock(
+ return_value=resource_mock)
+ elif operation_scope == gcompute_client.OperationScope.ZONE:
+ self.compute_client._service.zoneOperations = mock.MagicMock(
+ return_value=resource_mock)
+ elif operation_scope == gcompute_client.OperationScope.REGION:
+ self.compute_client._service.regionOperations = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.get = mock.MagicMock(return_value=mock_api)
+ mock_api.execute = mock.MagicMock(return_value=mock_result)
+ return resource_mock
+
+ def testGetOperationStatusGlobal(self):
+ """Test _GetOperationStatus for global."""
+ resource_mock = self._SetupMocksForGetOperationStatus(
+ {"status": "GOOD"}, gcompute_client.OperationScope.GLOBAL)
+ status = self.compute_client._GetOperationStatus(
+ {"name": self.OPERATION_NAME},
+ gcompute_client.OperationScope.GLOBAL)
+ self.assertEqual(status, "GOOD")
+ resource_mock.get.assert_called_with(
+ project=self.PROJECT, operation=self.OPERATION_NAME)
+
+ def testGetOperationStatusZone(self):
+ """Test _GetOperationStatus for zone."""
+ resource_mock = self._SetupMocksForGetOperationStatus(
+ {"status": "GOOD"}, gcompute_client.OperationScope.ZONE)
+ status = self.compute_client._GetOperationStatus(
+ {"name": self.OPERATION_NAME}, gcompute_client.OperationScope.ZONE,
+ self.ZONE)
+ self.assertEqual(status, "GOOD")
+ resource_mock.get.assert_called_with(
+ project=self.PROJECT,
+ operation=self.OPERATION_NAME,
+ zone=self.ZONE)
+
+ def testGetOperationStatusRegion(self):
+ """Test _GetOperationStatus for region."""
+ resource_mock = self._SetupMocksForGetOperationStatus(
+ {"status": "GOOD"}, gcompute_client.OperationScope.REGION)
+ self.compute_client._GetOperationStatus(
+ {"name": self.OPERATION_NAME},
+ gcompute_client.OperationScope.REGION, self.REGION)
+ resource_mock.get.assert_called_with(
+ project=self.PROJECT,
+ operation=self.OPERATION_NAME,
+ region=self.REGION)
+
+ def testGetOperationStatusError(self):
+ """Test _GetOperationStatus failed."""
+ self._SetupMocksForGetOperationStatus(
+ {"error": {"errors": ["error1", "error2"]}},
+ gcompute_client.OperationScope.GLOBAL)
+ self.assertRaisesRegexp(errors.DriverError,
+ "Get operation state failed.*error1.*error2",
+ self.compute_client._GetOperationStatus,
+ {"name": self.OPERATION_NAME},
+ gcompute_client.OperationScope.GLOBAL)
+
+ def testWaitOnOperation(self):
+ """Test WaitOnOperation."""
+ mock_error = mock.MagicMock()
+ self.Patch(utils, "PollAndWait")
+ self.Patch(errors, "GceOperationTimeoutError", return_value=mock_error)
+ self.compute_client.WaitOnOperation(
+ operation={"name": self.OPERATION_NAME},
+ operation_scope=gcompute_client.OperationScope.REGION,
+ scope_name=self.REGION)
+ utils.PollAndWait.assert_called_with(
+ func=self.compute_client._GetOperationStatus,
+ expected_return="DONE",
+ timeout_exception=mock_error,
+ timeout_secs=self.compute_client.OPERATION_TIMEOUT_SECS,
+ sleep_interval_secs=self.compute_client.
+ OPERATION_POLL_INTERVAL_SECS,
+ operation={"name": self.OPERATION_NAME},
+ operation_scope=gcompute_client.OperationScope.REGION,
+ scope_name=self.REGION)
+
+ def testCreateImage(self):
+ """Test CreateImage."""
+ self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
+ resource_mock = mock.MagicMock()
+ self.compute_client._service.images = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.insert = mock.MagicMock()
+
+ expected_body = {
+ "name": self.IMAGE,
+ "rawDisk": {
+ "source": self.GS_IMAGE_SOURCE_URI,
+ },
+ }
+ self.compute_client.CreateImage(
+ image_name=self.IMAGE, source_uri=self.GS_IMAGE_SOURCE_URI)
+ resource_mock.insert.assert_called_with(
+ project=self.PROJECT, body=expected_body)
+ self.compute_client.WaitOnOperation.assert_called_with(
+ operation=mock.ANY,
+ operation_scope=gcompute_client.OperationScope.GLOBAL)
+
+ def testCreateImageFail(self):
+ """Test CreateImage fails."""
+ self.Patch(
+ gcompute_client.ComputeClient,
+ "WaitOnOperation",
+ side_effect=errors.DriverError("Expected fake error"))
+ self.Patch(
+ gcompute_client.ComputeClient,
+ "CheckImageExists",
+ return_value=True)
+ self.Patch(gcompute_client.ComputeClient, "DeleteImage")
+
+ resource_mock = mock.MagicMock()
+ self.compute_client._service.images = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.insert = mock.MagicMock()
+
+ expected_body = {
+ "name": self.IMAGE,
+ "rawDisk": {
+ "source": self.GS_IMAGE_SOURCE_URI,
+ },
+ }
+ self.assertRaisesRegexp(
+ errors.DriverError,
+ "Expected fake error",
+ self.compute_client.CreateImage,
+ image_name=self.IMAGE,
+ source_uri=self.GS_IMAGE_SOURCE_URI)
+ resource_mock.insert.assert_called_with(
+ project=self.PROJECT, body=expected_body)
+ self.compute_client.WaitOnOperation.assert_called_with(
+ operation=mock.ANY,
+ operation_scope=gcompute_client.OperationScope.GLOBAL)
+ self.compute_client.CheckImageExists.assert_called_with(self.IMAGE)
+ self.compute_client.DeleteImage.assert_called_with(self.IMAGE)
+
+ def testCheckImageExistsTrue(self):
+ """Test CheckImageExists return True."""
+ resource_mock = mock.MagicMock()
+ mock_api = mock.MagicMock()
+ self.compute_client._service.images = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.get = mock.MagicMock(return_value=mock_api)
+ mock_api.execute = mock.MagicMock(return_value={"name": self.IMAGE})
+ self.assertTrue(self.compute_client.CheckImageExists(self.IMAGE))
+
+ def testCheckImageExistsFalse(self):
+ """Test CheckImageExists return False."""
+ resource_mock = mock.MagicMock()
+ mock_api = mock.MagicMock()
+ self.compute_client._service.images = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.get = mock.MagicMock(return_value=mock_api)
+ mock_api.execute = mock.MagicMock(
+ side_effect=errors.ResourceNotFoundError(404, "no image"))
+ self.assertFalse(self.compute_client.CheckImageExists(self.IMAGE))
+
+ def testDeleteImage(self):
+ """Test DeleteImage."""
+ self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
+ resource_mock = mock.MagicMock()
+ self.compute_client._service.images = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.delete = mock.MagicMock()
+ self.compute_client.DeleteImage(self.IMAGE)
+ resource_mock.delete.assert_called_with(
+ project=self.PROJECT, image=self.IMAGE)
+ self.assertTrue(self.compute_client.WaitOnOperation.called)
+
+ def _SetupBatchHttpRequestMock(self):
+ """Setup BatchHttpRequest mock."""
+ requests = {}
+
+ def _Add(request, callback, request_id):
+ requests[request_id] = (request, callback)
+
+ def _Execute():
+ for rid in requests:
+ _, callback = requests[rid]
+ callback(
+ request_id=rid, response=mock.MagicMock(), exception=None)
+
+ mock_batch = mock.MagicMock()
+ mock_batch.add = _Add
+ mock_batch.execute = _Execute
+ self.Patch(apiclient.http, "BatchHttpRequest", return_value=mock_batch)
+
+ def testDeleteImages(self):
+ """Test DeleteImages."""
+ self._SetupBatchHttpRequestMock()
+ self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
+ fake_images = ["fake_image_1", "fake_image_2"]
+ mock_api = mock.MagicMock()
+ resource_mock = mock.MagicMock()
+ self.compute_client._service.images = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.delete = mock.MagicMock(return_value=mock_api)
+ # Call the API.
+ deleted, failed, error_msgs = self.compute_client.DeleteImages(
+ fake_images)
+ # Verify
+ calls = [mock.call(
+ project=self.PROJECT, image="fake_image_1"), mock.call(
+ project=self.PROJECT, image="fake_image_2")]
+ resource_mock.delete.assert_has_calls(calls, any_order=True)
+ self.assertEqual(
+ gcompute_client.ComputeClient.WaitOnOperation.call_count, 2)
+ self.assertEqual(error_msgs, [])
+ self.assertEqual(failed, [])
+ self.assertEqual(set(deleted), set(fake_images))
+
+ def testListImages(self):
+ """Test ListImages."""
+ fake_token = "fake_next_page_token"
+ image_1 = "image_1"
+ image_2 = "image_2"
+ response_1 = {"items": [image_1], "nextPageToken": fake_token}
+ response_2 = {"items": [image_2]}
+ self.Patch(
+ gcompute_client.ComputeClient,
+ "Execute",
+ side_effect=[response_1, response_2])
+ resource_mock = mock.MagicMock()
+ self.compute_client._service.images = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.list = mock.MagicMock()
+ images = self.compute_client.ListImages()
+ calls = [
+ mock.call(
+ project=self.PROJECT, filter=None, pageToken=None), mock.call(
+ project=self.PROJECT, filter=None, pageToken=fake_token)
+ ]
+ resource_mock.list.assert_has_calls(calls)
+ self.assertEqual(images, [image_1, image_2])
+
+ def testGetInstance(self):
+ """Test GetInstance."""
+ resource_mock = mock.MagicMock()
+ mock_api = mock.MagicMock()
+ self.compute_client._service.instances = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.get = mock.MagicMock(return_value=mock_api)
+ mock_api.execute = mock.MagicMock(return_value={"name": self.INSTANCE})
+ result = self.compute_client.GetInstance(self.INSTANCE, self.ZONE)
+ self.assertEqual(result, {"name": self.INSTANCE})
+ resource_mock.get.assert_called_with(
+ project=self.PROJECT, zone=self.ZONE, instance=self.INSTANCE)
+
+ def testListInstances(self):
+ """Test ListInstances."""
+ fake_token = "fake_next_page_token"
+ instance_1 = "instance_1"
+ instance_2 = "instance_2"
+ response_1 = {"items": [instance_1], "nextPageToken": fake_token}
+ response_2 = {"items": [instance_2]}
+ self.Patch(
+ gcompute_client.ComputeClient,
+ "Execute",
+ side_effect=[response_1, response_2])
+ resource_mock = mock.MagicMock()
+ self.compute_client._service.instances = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.list = mock.MagicMock()
+ instances = self.compute_client.ListInstances(self.ZONE)
+ calls = [
+ mock.call(
+ project=self.PROJECT,
+ zone=self.ZONE,
+ filter=None,
+ pageToken=None),
+ mock.call(
+ project=self.PROJECT,
+ zone=self.ZONE,
+ filter=None,
+ pageToken=fake_token),
+ ]
+ resource_mock.list.assert_has_calls(calls)
+ self.assertEqual(instances, [instance_1, instance_2])
+
+ def testCreateInstance(self):
+ """Test CreateInstance."""
+ self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
+ self.Patch(
+ gcompute_client.ComputeClient,
+ "GetMachineType",
+ return_value={"selfLink": self.MACHINE_TYPE_URL})
+ self.Patch(
+ gcompute_client.ComputeClient,
+ "GetNetworkUrl",
+ return_value=self.NETWORK_URL)
+ self.Patch(
+ gcompute_client.ComputeClient,
+ "GetImage",
+ return_value={"selfLink": self.IMAGE_URL})
+
+ resource_mock = mock.MagicMock()
+ self.compute_client._service.instances = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.insert = mock.MagicMock()
+
+ expected_body = {
+ "machineType": self.MACHINE_TYPE_URL,
+ "name": self.INSTANCE,
+ "networkInterfaces": [
+ {
+ "network": self.NETWORK_URL,
+ "accessConfigs": [
+ {"name": "External NAT",
+ "type": "ONE_TO_ONE_NAT"}
+ ],
+ }
+ ],
+ "disks": [
+ {
+ "type": "PERSISTENT",
+ "boot": True,
+ "mode": "READ_WRITE",
+ "autoDelete": True,
+ "initializeParams": {
+ "diskName": self.INSTANCE,
+ "sourceImage": self.IMAGE_URL,
+ },
+ }
+ ],
+ "serviceAccounts": [
+ {"email": "default",
+ "scopes": self.compute_client.DEFAULT_INSTANCE_SCOPE}
+ ],
+ "metadata": {
+ "items": [{"key": self.METADATA[0],
+ "value": self.METADATA[1]}],
+ },
+ }
+
+ self.compute_client.CreateInstance(
+ instance=self.INSTANCE,
+ image_name=self.IMAGE,
+ machine_type=self.MACHINE_TYPE,
+ metadata={self.METADATA[0]: self.METADATA[1]},
+ network=self.NETWORK,
+ zone=self.ZONE)
+
+ resource_mock.insert.assert_called_with(
+ project=self.PROJECT, zone=self.ZONE, body=expected_body)
+ self.compute_client.WaitOnOperation.assert_called_with(
+ mock.ANY,
+ operation_scope=gcompute_client.OperationScope.ZONE,
+ scope_name=self.ZONE)
+
+ def testDeleteInstance(self):
+ """Test DeleteInstance."""
+ self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
+ resource_mock = mock.MagicMock()
+ self.compute_client._service.instances = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.delete = mock.MagicMock()
+ self.compute_client.DeleteInstance(
+ instance=self.INSTANCE, zone=self.ZONE)
+ resource_mock.delete.assert_called_with(
+ project=self.PROJECT, zone=self.ZONE, instance=self.INSTANCE)
+ self.compute_client.WaitOnOperation.assert_called_with(
+ mock.ANY,
+ operation_scope=gcompute_client.OperationScope.ZONE,
+ scope_name=self.ZONE)
+
+ def testDeleteInstances(self):
+ """Test DeleteInstances."""
+ self._SetupBatchHttpRequestMock()
+ self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
+ fake_instances = ["fake_instance_1", "fake_instance_2"]
+ mock_api = mock.MagicMock()
+ resource_mock = mock.MagicMock()
+ self.compute_client._service.instances = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.delete = mock.MagicMock(return_value=mock_api)
+ deleted, failed, error_msgs = self.compute_client.DeleteInstances(
+ fake_instances, self.ZONE)
+ calls = [
+ mock.call(
+ project=self.PROJECT,
+ instance="fake_instance_1",
+ zone=self.ZONE), mock.call(
+ project=self.PROJECT,
+ instance="fake_instance_2",
+ zone=self.ZONE)
+ ]
+ resource_mock.delete.assert_has_calls(calls, any_order=True)
+ self.assertEqual(
+ gcompute_client.ComputeClient.WaitOnOperation.call_count, 2)
+ self.assertEqual(error_msgs, [])
+ self.assertEqual(failed, [])
+ self.assertEqual(set(deleted), set(fake_instances))
+
+ def testBatchExecuteOnInstances(self):
+ self._SetupBatchHttpRequestMock()
+ self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
+ action = mock.MagicMock(return_value=mock.MagicMock())
+ fake_instances = ["fake_instance_1", "fake_instance_2"]
+ done, failed, error_msgs = self.compute_client._BatchExecuteOnInstances(
+ fake_instances, self.ZONE, action)
+ calls = [mock.call(instance="fake_instance_1"),
+ mock.call(instance="fake_instance_2")]
+ action.assert_has_calls(calls, any_order=True)
+ self.assertEqual(
+ gcompute_client.ComputeClient.WaitOnOperation.call_count, 2)
+ self.assertEqual(set(done), set(fake_instances))
+ self.assertEqual(error_msgs, [])
+ self.assertEqual(failed, [])
+
+ def testResetInstance(self):
+ """Test ResetInstance."""
+ self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
+ resource_mock = mock.MagicMock()
+ self.compute_client._service.instances = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.reset = mock.MagicMock()
+ self.compute_client.ResetInstance(
+ instance=self.INSTANCE, zone=self.ZONE)
+ resource_mock.reset.assert_called_with(
+ project=self.PROJECT, zone=self.ZONE, instance=self.INSTANCE)
+ self.compute_client.WaitOnOperation.assert_called_with(
+ mock.ANY,
+ operation_scope=gcompute_client.OperationScope.ZONE,
+ scope_name=self.ZONE)
+
+ def _CompareMachineSizeTestHelper(self,
+ machine_info_1,
+ machine_info_2,
+ expected_result=None,
+ expected_error_type=None):
+ """Helper class for testing CompareMachineSize.
+
+ Args:
+ machine_info_1: A dictionary representing the first machine size.
+ machine_info_2: A dictionary representing the second machine size.
+ expected_result: An integer, 0, 1 or -1, or None if not set.
+ expected_error_type: An exception type, if set will check for exception.
+ """
+ self.Patch(
+ gcompute_client.ComputeClient,
+ "GetMachineType",
+ side_effect=[machine_info_1, machine_info_2])
+ if expected_error_type:
+ self.assertRaises(expected_error_type,
+ self.compute_client.CompareMachineSize, "name1",
+ "name2", self.ZONE)
+ else:
+ result = self.compute_client.CompareMachineSize("name1", "name2",
+ self.ZONE)
+ self.assertEqual(result, expected_result)
+
+ self.compute_client.GetMachineType.assert_has_calls(
+ [mock.call("name1", self.ZONE), mock.call("name2", self.ZONE)])
+
+ def testCompareMachineSizeSmall(self):
+ """Test CompareMachineSize where the first one is smaller."""
+ machine_info_1 = {"guestCpus": 10, "memoryMb": 100}
+ machine_info_2 = {"guestCpus": 10, "memoryMb": 200}
+ self._CompareMachineSizeTestHelper(machine_info_1, machine_info_2, -1)
+
+ def testCompareMachineSizeLarge(self):
+ """Test CompareMachineSize where the first one is larger."""
+ machine_info_1 = {"guestCpus": 10, "memoryMb": 200}
+ machine_info_2 = {"guestCpus": 10, "memoryMb": 100}
+ self._CompareMachineSizeTestHelper(machine_info_1, machine_info_2, 1)
+
+ def testCompareMachineSizeEqual(self):
+ """Test CompareMachineSize where two machine sizes are equal."""
+ machine_info = {"guestCpus": 10, "memoryMb": 100}
+ self._CompareMachineSizeTestHelper(machine_info, machine_info, 0)
+
+ def testCompareMachineSizeBadMetric(self):
+ """Test CompareMachineSize with bad metric."""
+ machine_info = {"unkown_metric": 10, "memoryMb": 100}
+ self._CompareMachineSizeTestHelper(
+ machine_info, machine_info, expected_error_type=errors.DriverError)
+
+ def testGetMachineType(self):
+ """Test GetMachineType."""
+ resource_mock = mock.MagicMock()
+ mock_api = mock.MagicMock()
+ self.compute_client._service.machineTypes = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.get = mock.MagicMock(return_value=mock_api)
+ mock_api.execute = mock.MagicMock(
+ return_value={"name": self.MACHINE_TYPE})
+ result = self.compute_client.GetMachineType(self.MACHINE_TYPE,
+ self.ZONE)
+ self.assertEqual(result, {"name": self.MACHINE_TYPE})
+ resource_mock.get.assert_called_with(
+ project=self.PROJECT,
+ zone=self.ZONE,
+ machineType=self.MACHINE_TYPE)
+
+ def _GetSerialPortOutputTestHelper(self, response):
+ """Helper function for testing GetSerialPortOutput.
+
+ Args:
+ response: A dictionary representing a fake response.
+ """
+ resource_mock = mock.MagicMock()
+ mock_api = mock.MagicMock()
+ self.compute_client._service.instances = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.getSerialPortOutput = mock.MagicMock(
+ return_value=mock_api)
+ mock_api.execute = mock.MagicMock(return_value=response)
+
+ if "contents" in response:
+ result = self.compute_client.GetSerialPortOutput(
+ instance=self.INSTANCE, zone=self.ZONE)
+ self.assertEqual(result, "fake contents")
+ else:
+ self.assertRaisesRegexp(
+ errors.DriverError,
+ "Malformed response.*",
+ self.compute_client.GetSerialPortOutput,
+ instance=self.INSTANCE,
+ zone=self.ZONE)
+ resource_mock.getSerialPortOutput.assert_called_with(
+ project=self.PROJECT,
+ zone=self.ZONE,
+ instance=self.INSTANCE,
+ port=1)
+
+ def testGetSerialPortOutput(self):
+ response = {"contents": "fake contents"}
+ self._GetSerialPortOutputTestHelper(response)
+
+ def testGetSerialPortOutputFail(self):
+ response = {"malformed": "fake contents"}
+ self._GetSerialPortOutputTestHelper(response)
+
+ def testGetInstanceNamesByIPs(self):
+ """Test GetInstanceNamesByIPs."""
+ good_instance = {
+ "name": "instance_1",
+ "networkInterfaces": [
+ {
+ "accessConfigs": [
+ {"natIP": "172.22.22.22"},
+ ],
+ },
+ ],
+ }
+ bad_instance = {"name": "instance_2"}
+ self.Patch(
+ gcompute_client.ComputeClient,
+ "ListInstances",
+ return_value=[good_instance, bad_instance])
+ ip_name_map = self.compute_client.GetInstanceNamesByIPs(
+ ips=["172.22.22.22", "172.22.22.23"], zone=self.ZONE)
+ self.assertEqual(ip_name_map, {"172.22.22.22": "instance_1",
+ "172.22.22.23": None})
+
+ def testAddSshRsa(self):
+ """Test AddSshRsa.."""
+ fake_user = "fake_user"
+ sshkey = (
+ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBkTOTRze9v2VOqkkf7RG"
+ "jSkg6Z2kb9Q9UHsDGatvend3fmjIw1Tugg0O7nnjlPkskmlgyd4a/j99WOeLL"
+ "CPk6xPyoVjrPUVBU/pAk09ORTC4Zqk6YjlW7LOfzvqmXhmIZfYu6Q4Yt50pZzhl"
+ "lllfu26nYjY7Tg12D019nJi/kqPX5+NKgt0LGXTu8T1r2Gav/q4V7QRWQrB8Eiu"
+ "pxXR7I2YhynqovkEt/OXG4qWgvLEXGsWtSQs0CtCzqEVxz0Y9ECr7er4VdjSQxV"
+ "AaeLAsQsK9ROae8hMBFZ3//8zLVapBwpuffCu+fUoql9qeV9xagZcc9zj8XOUOW"
+ "ApiihqNL1111 test@test1.org")
+ project = {
+ "commonInstanceMetadata": {
+ "kind": "compute#metadata",
+ "fingerprint": "a-23icsyx4E=",
+ "items": [
+ {
+ "key": "sshKeys",
+ "value": "user:key"
+ }
+ ]
+ }
+ }
+ expected = {
+ "kind": "compute#metadata",
+ "fingerprint": "a-23icsyx4E=",
+ "items": [
+ {
+ "key": "sshKeys",
+ "value": "user:key\n%s:%s" % (fake_user, sshkey)
+ }
+ ]
+ }
+
+ self.Patch(os.path, "exists", return_value=True)
+ m = mock.mock_open(read_data=sshkey)
+ self.Patch(__builtins__, "open", m, create=True)
+ self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
+ self.Patch(
+ gcompute_client.ComputeClient, "GetProject", return_value=project)
+ resource_mock = mock.MagicMock()
+ self.compute_client._service.projects = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.setCommonInstanceMetadata = mock.MagicMock()
+
+ self.compute_client.AddSshRsa(fake_user, "/path/to/test_rsa.pub")
+ resource_mock.setCommonInstanceMetadata.assert_called_with(
+ project=self.PROJECT, body=expected)
+
+ def testAddSshRsaInvalidKey(self):
+ """Test AddSshRsa.."""
+ fake_user = "fake_user"
+ sshkey = "ssh-rsa v2VOqkkf7RGL1111 test@test1.org"
+ project = {
+ "commonInstanceMetadata": {
+ "kind": "compute#metadata",
+ "fingerprint": "a-23icsyx4E=",
+ "items": [
+ {
+ "key": "sshKeys",
+ "value": "user:key"
+ }
+ ]
+ }
+ }
+ self.Patch(os.path, "exists", return_value=True)
+ m = mock.mock_open(read_data=sshkey)
+ self.Patch(__builtins__, "open", m, create=True)
+ self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
+ self.Patch(
+ gcompute_client.ComputeClient, "GetProject", return_value=project)
+ self.assertRaisesRegexp(errors.DriverError, "rsa key is invalid:*",
+ self.compute_client.AddSshRsa, fake_user,
+ "/path/to/test_rsa.pub")
+
+ def testDeleteDisks(self):
+ """Test DeleteDisks."""
+ self._SetupBatchHttpRequestMock()
+ self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
+ fake_disks = ["fake_disk_1", "fake_disk_2"]
+ mock_api = mock.MagicMock()
+ resource_mock = mock.MagicMock()
+ self.compute_client._service.disks = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.delete = mock.MagicMock(return_value=mock_api)
+ # Call the API.
+ deleted, failed, error_msgs = self.compute_client.DeleteDisks(
+ fake_disks, zone=self.ZONE)
+ # Verify
+ calls = [mock.call(
+ project=self.PROJECT, disk="fake_disk_1", zone=self.ZONE),
+ mock.call(
+ project=self.PROJECT, disk="fake_disk_2", zone=self.ZONE)]
+ resource_mock.delete.assert_has_calls(calls, any_order=True)
+ self.assertEqual(
+ gcompute_client.ComputeClient.WaitOnOperation.call_count, 2)
+ self.assertEqual(error_msgs, [])
+ self.assertEqual(failed, [])
+ self.assertEqual(set(deleted), set(fake_disks))
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/internal/lib/gstorage_client_test.py b/internal/lib/gstorage_client_test.py
new file mode 100644
index 00000000..8577dabd
--- /dev/null
+++ b/internal/lib/gstorage_client_test.py
@@ -0,0 +1,148 @@
+"""Tests for acloud.internal.lib.gstorage_client."""
+
+import io
+import time
+
+import apiclient
+import mock
+
+import unittest
+from acloud.internal.lib import driver_test_lib
+from acloud.internal.lib import gstorage_client
+from acloud.public import errors
+
+
+class StorageClientTest(driver_test_lib.BaseDriverTest):
+ """Test StorageClient."""
+
+ LOCAL_SRC = "/fake/local/path"
+ BUCKET = "fake_bucket"
+ OBJECT = "fake_obj"
+ MIME_TYPE = "fake_mimetype"
+
+ def setUp(self):
+ """Set up test."""
+ super(StorageClientTest, self).setUp()
+ self.Patch(gstorage_client.StorageClient, "InitResourceHandle")
+ self.client = gstorage_client.StorageClient(mock.MagicMock())
+ self.client._service = mock.MagicMock()
+
+ def testGet(self):
+ """Test Get."""
+ mock_api = mock.MagicMock()
+ resource_mock = mock.MagicMock()
+ self.client._service.objects = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.get = mock.MagicMock(return_value=mock_api)
+ self.client.Get(self.BUCKET, self.OBJECT)
+ resource_mock.get.assert_called_with(
+ bucket=self.BUCKET, object=self.OBJECT)
+ self.assertTrue(mock_api.execute.called)
+
+ def testList(self):
+ """Test List."""
+ mock_items = ["fake/return"]
+ self.Patch(
+ gstorage_client.StorageClient,
+ "ListWithMultiPages",
+ return_value=mock_items)
+ resource_mock = mock.MagicMock()
+ self.client._service.objects = mock.MagicMock(
+ return_value=resource_mock)
+ items = self.client.List(self.BUCKET, self.OBJECT)
+ self.client.ListWithMultiPages.assert_called_once_with(
+ api_resource=resource_mock.list,
+ bucket=self.BUCKET,
+ prefix=self.OBJECT)
+ self.assertEqual(mock_items, items)
+
+ def testUpload(self):
+ """Test Upload."""
+ # Create mocks
+ mock_file = mock.MagicMock()
+ mock_file_io = mock.MagicMock()
+ mock_file_io.__enter__.return_value = mock_file
+ mock_media = mock.MagicMock()
+ mock_api = mock.MagicMock()
+ mock_response = mock.MagicMock()
+
+ self.Patch(io, "FileIO", return_value=mock_file_io)
+ self.Patch(
+ apiclient.http, "MediaIoBaseUpload", return_value=mock_media)
+ resource_mock = mock.MagicMock()
+ self.client._service.objects = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.insert = mock.MagicMock(return_value=mock_api)
+ mock_api.execute = mock.MagicMock(return_value=mock_response)
+
+ # Make the call to the api
+ response = self.client.Upload(self.LOCAL_SRC, self.BUCKET, self.OBJECT,
+ self.MIME_TYPE)
+
+ # Verify
+ self.assertEqual(response, mock_response)
+ io.FileIO.assert_called_with(self.LOCAL_SRC, mode="rb")
+ apiclient.http.MediaIoBaseUpload.assert_called_with(mock_file,
+ self.MIME_TYPE)
+ resource_mock.insert.assert_called_with(
+ bucket=self.BUCKET, name=self.OBJECT, media_body=mock_media)
+
+ def testUploadOSError(self):
+ """Test Upload when OSError is raised."""
+ self.Patch(io, "FileIO", side_effect=OSError("fake OSError"))
+ self.assertRaises(errors.DriverError, self.client.Upload,
+ self.LOCAL_SRC, self.BUCKET, self.OBJECT,
+ self.MIME_TYPE)
+
+ def testDelete(self):
+ """Test Delete."""
+ mock_api = mock.MagicMock()
+ resource_mock = mock.MagicMock()
+ self.client._service.objects = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.delete = mock.MagicMock(return_value=mock_api)
+ self.client.Delete(self.BUCKET, self.OBJECT)
+ resource_mock.delete.assert_called_with(
+ bucket=self.BUCKET, object=self.OBJECT)
+ self.assertTrue(mock_api.execute.called)
+
+ def testDeleteMultipleFiles(self):
+ """Test Delete multiple files."""
+ fake_objs = ["fake_obj1", "fake_obj2"]
+ mock_api = mock.MagicMock()
+ resource_mock = mock.MagicMock()
+ self.client._service.objects = mock.MagicMock(
+ return_value=resource_mock)
+ resource_mock.delete = mock.MagicMock(return_value=mock_api)
+ deleted, failed, error_msgs = self.client.DeleteFiles(self.BUCKET,
+ fake_objs)
+ self.assertEqual(deleted, fake_objs)
+ self.assertEqual(failed, [])
+ self.assertEqual(error_msgs, [])
+ calls = [mock.call(
+ bucket=self.BUCKET, object="fake_obj1"), mock.call(
+ bucket=self.BUCKET, object="fake_obj2")]
+ resource_mock.delete.assert_has_calls(calls)
+ self.assertEqual(mock_api.execute.call_count, 2)
+
+ def testGetUrl(self):
+ """Test GetUrl."""
+ fake_item = {"name": "fake-item-1", "selfLink": "link1"}
+ self.Patch(
+ gstorage_client.StorageClient, "Get", return_value=fake_item)
+ self.assertEqual(
+ self.client.GetUrl("fake_bucket", "fake-item-1"), "link1")
+
+ def testGetUrlNotFound(self):
+ """Test GetUrl when object is not found."""
+ self.Patch(
+ gstorage_client.StorageClient,
+ "Get",
+ side_effect=errors.ResourceNotFoundError(404, "expected error"))
+ self.Patch(time, "sleep")
+ self.assertRaises(errors.ResourceNotFoundError, self.client.GetUrl,
+ "fake_bucket", "fake-item-1")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/public/acloud_kernel/kernel_swapper_test.py b/public/acloud_kernel/kernel_swapper_test.py
new file mode 100644
index 00000000..bddb7220
--- /dev/null
+++ b/public/acloud_kernel/kernel_swapper_test.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 - The Android Open Source Project
+#
+# 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.
+"""Tests acloud.public.acloud_kernel.kernel_swapper."""
+
+import subprocess
+import mock
+
+import unittest
+from acloud.internal.lib import android_compute_client
+from acloud.internal.lib import auth
+from acloud.internal.lib import driver_test_lib
+from acloud.public.acloud_kernel import kernel_swapper
+
+
+class KernelSwapperTest(driver_test_lib.BaseDriverTest):
+ """Test kernel_swapper."""
+
+ def setUp(self):
+ """Set up the test."""
+ super(KernelSwapperTest, self).setUp()
+ self.cfg = mock.MagicMock()
+ self.credentials = mock.MagicMock()
+ self.Patch(auth, 'CreateCredentials', return_value=self.credentials)
+ self.compute_client = mock.MagicMock()
+ self.Patch(
+ android_compute_client,
+ 'AndroidComputeClient',
+ return_value=self.compute_client)
+ self.subprocess_call = self.Patch(subprocess, 'check_call')
+
+ self.fake_ip = '123.456.789.000'
+ self.fake_instance = 'fake-instance'
+ self.compute_client.GetInstanceIP.return_value = self.fake_ip
+
+ self.kswapper = kernel_swapper.KernelSwapper(self.cfg,
+ self.fake_instance)
+ self.ssh_cmd_prefix = 'ssh %s root@%s' % (
+ ' '.join(kernel_swapper.SSH_FLAGS), self.fake_ip)
+ self.scp_cmd_prefix = 'scp %s' % ' '.join(kernel_swapper.SSH_FLAGS)
+
+ def testPushFile(self):
+ """Test RebootTarget."""
+ fake_src_path = 'fake-src'
+ fake_dest_path = 'fake-dest'
+ scp_cmd = ' '.join([self.scp_cmd_prefix, '%s root@%s:%s' %
+ (fake_src_path, self.fake_ip, fake_dest_path)])
+
+ self.kswapper.PushFile(fake_src_path, fake_dest_path)
+ self.subprocess_call.assert_called_once_with(scp_cmd, shell=True)
+
+ def testRebootTarget(self):
+ """Test RebootTarget."""
+ self.kswapper.RebootTarget()
+ reboot_cmd = ' '.join([
+ self.ssh_cmd_prefix, '"%s"' % kernel_swapper.REBOOT_CMD
+ ])
+
+ self.subprocess_call.assert_called_once_with(reboot_cmd, shell=True)
+ self.compute_client.WaitForBoot.assert_called_once_with(
+ self.fake_instance)
+
+ def testSwapKernel(self):
+ """Test SwapKernel."""
+ fake_local_kernel_image = 'fake-kernel'
+ mount_cmd = ' '.join([
+ self.ssh_cmd_prefix, '"%s"' % kernel_swapper.MOUNT_CMD
+ ])
+ scp_cmd = ' '.join([self.scp_cmd_prefix, '%s root@%s:%s' %
+ (fake_local_kernel_image, self.fake_ip, '/boot')])
+ reboot_cmd = ' '.join([
+ self.ssh_cmd_prefix, '"%s"' % kernel_swapper.REBOOT_CMD
+ ])
+
+ self.kswapper.SwapKernel(fake_local_kernel_image)
+ self.subprocess_call.assert_has_calls([
+ mock.call(
+ mount_cmd, shell=True), mock.call(
+ scp_cmd, shell=True), mock.call(
+ reboot_cmd, shell=True)
+ ])
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/public/config_test.py b/public/config_test.py
new file mode 100644
index 00000000..554e751f
--- /dev/null
+++ b/public/config_test.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 - The Android Open Source Project
+#
+# 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.
+
+"""Tests for acloud.public.config."""
+
+import mock
+
+import unittest
+from acloud.internal.proto import internal_config_pb2
+from acloud.internal.proto import user_config_pb2
+from acloud.public import config
+from acloud.public import errors
+
+
+class AcloudConfigManagerTest(unittest.TestCase):
+ """Test acloud configuration manager."""
+
+ USER_CONFIG = """
+service_account_name: "fake@developer.gserviceaccount.com"
+service_account_private_key_path: "/path/to/service/account/key"
+project: "fake-project"
+zone: "us-central1-f"
+machine_type: "n1-standard-1"
+network: "default"
+ssh_private_key_path: "/path/to/ssh/key"
+storage_bucket_name: "fake_bucket"
+orientation: "portrait"
+resolution: "1200x1200x1200x1200"
+client_id: "fake_client_id"
+client_secret: "fake_client_secret"
+metadata_variable {
+ key: "metadata_1"
+ value: "metadata_value_1"
+}
+"""
+
+ INTERNAL_CONFIG = """
+min_machine_size: "n1-standard-1"
+disk_image_name: "avd-system.tar.gz"
+disk_image_mime_type: "application/x-tar"
+disk_image_extension: ".tar.gz"
+disk_raw_image_name: "disk.raw"
+disk_raw_image_extension: ".img"
+creds_cache_file: ".fake_oauth2.dat"
+user_agent: "fake_user_agent"
+
+default_usr_cfg {
+ machine_type: "n1-standard-1"
+ network: "default"
+ metadata_variable {
+ key: "metadata_1"
+ value: "metadata_value_1"
+ }
+
+ metadata_variable {
+ key: "metadata_2"
+ value: "metadata_value_2"
+ }
+}
+
+device_resolution_map {
+ key: "nexus5"
+ value: "1080x1920x32x480"
+}
+
+device_default_orientation_map {
+ key: "nexus5"
+ value: "portrait"
+}
+
+valid_branch_and_min_build_id {
+ key: "git_jb-gce-dev"
+ value: 0
+}
+"""
+
+ def setUp(self):
+ self.config_file = mock.MagicMock()
+
+ def testLoadUserConfig(self):
+ """Test loading user config."""
+ self.config_file.read.return_value = self.USER_CONFIG
+ cfg = config.AcloudConfigManager.LoadConfigFromProtocolBuffer(
+ self.config_file, user_config_pb2.UserConfig)
+ self.assertEquals(cfg.service_account_name,
+ "fake@developer.gserviceaccount.com")
+ self.assertEquals(cfg.service_account_private_key_path,
+ "/path/to/service/account/key")
+ self.assertEquals(cfg.project, "fake-project")
+ self.assertEquals(cfg.zone, "us-central1-f")
+ self.assertEquals(cfg.machine_type, "n1-standard-1")
+ self.assertEquals(cfg.network, "default")
+ self.assertEquals(cfg.ssh_private_key_path, "/path/to/ssh/key")
+ self.assertEquals(cfg.storage_bucket_name, "fake_bucket")
+ self.assertEquals(cfg.orientation, "portrait")
+ self.assertEquals(cfg.resolution, "1200x1200x1200x1200")
+ self.assertEquals(cfg.client_id, "fake_client_id")
+ self.assertEquals(cfg.client_secret, "fake_client_secret")
+ self.assertEquals({key: val
+ for key, val in cfg.metadata_variable.iteritems()},
+ {"metadata_1": "metadata_value_1"})
+
+ def testLoadInternalConfig(self):
+ """Test loading internal config."""
+ self.config_file.read.return_value = self.INTERNAL_CONFIG
+ cfg = config.AcloudConfigManager.LoadConfigFromProtocolBuffer(
+ self.config_file, internal_config_pb2.InternalConfig)
+ self.assertEquals(cfg.min_machine_size, "n1-standard-1")
+ self.assertEquals(cfg.disk_image_name, "avd-system.tar.gz")
+ self.assertEquals(cfg.disk_image_mime_type, "application/x-tar")
+ self.assertEquals(cfg.disk_image_extension, ".tar.gz")
+ self.assertEquals(cfg.disk_raw_image_name, "disk.raw")
+ self.assertEquals(cfg.disk_raw_image_extension, ".img")
+ self.assertEquals(cfg.creds_cache_file, ".fake_oauth2.dat")
+ self.assertEquals(cfg.user_agent, "fake_user_agent")
+ self.assertEquals(cfg.default_usr_cfg.machine_type, "n1-standard-1")
+ self.assertEquals(cfg.default_usr_cfg.network, "default")
+ self.assertEquals({
+ key: val
+ for key, val in cfg.default_usr_cfg.metadata_variable.iteritems()
+ }, {"metadata_1": "metadata_value_1",
+ "metadata_2": "metadata_value_2"})
+ self.assertEquals(
+ {key: val
+ for key, val in cfg.device_resolution_map.iteritems()},
+ {"nexus5": "1080x1920x32x480"})
+ device_resolution = {
+ key: val
+ for key, val in cfg.device_default_orientation_map.iteritems()
+ }
+ self.assertEquals(device_resolution, {"nexus5": "portrait"})
+ valid_branch_and_min_build_id = {
+ key: val
+ for key, val in cfg.valid_branch_and_min_build_id.iteritems()
+ }
+ self.assertEquals(valid_branch_and_min_build_id, {"git_jb-gce-dev": 0})
+
+ def testLoadConfigFails(self):
+ """Test loading a bad file."""
+ self.config_file.read.return_value = "malformed text"
+ with self.assertRaises(errors.ConfigError):
+ config.AcloudConfigManager.LoadConfigFromProtocolBuffer(
+ self.config_file, internal_config_pb2.InternalConfig)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/public/device_driver_test.py b/public/device_driver_test.py
new file mode 100644
index 00000000..eb0a64f8
--- /dev/null
+++ b/public/device_driver_test.py
@@ -0,0 +1,249 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 - The Android Open Source Project
+#
+# 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.
+
+"""Tests for acloud.public.device_driver."""
+
+import datetime
+import uuid
+
+import dateutil.parser
+import mock
+
+import unittest
+from acloud.internal.lib import auth
+from acloud.internal.lib import andorid_build_client
+from acloud.internal.lib import android_compute_client
+from acloud.internal.lib import driver_test_lib
+from acloud.internal.lib import gstorage_client
+from acloud.public import device_driver
+
+
+class DeviceDriverTest(driver_test_lib.BaseDriverTest):
+ """Test device_driver."""
+
+ def setUp(self):
+ """Set up the test."""
+ super(DeviceDriverTest, self).setUp()
+ self.build_client = mock.MagicMock()
+ self.Patch(android_build_client, "AndroidBuildClient",
+ return_value=self.build_client)
+ self.storage_client = mock.MagicMock()
+ self.Patch(
+ gstorage_client, "StorageClient", return_value=self.storage_client)
+ self.compute_client = mock.MagicMock()
+ self.Patch(
+ android_compute_client,
+ "AndroidComputeClient",
+ return_value=self.compute_client)
+ self.Patch(auth, "CreateCredentials", return_value=mock.MagicMock())
+
+ def _CreateCfg(self):
+ """A helper method that creates a mock configuration object."""
+ cfg = mock.MagicMock()
+ cfg.service_account_name = "fake@service.com"
+ cfg.service_account_private_key_path = "/fake/path/to/key"
+ cfg.zone = "fake_zone"
+ cfg.disk_image_name = "fake_image.tar.gz"
+ cfg.disk_image_mime_type = "fake/type"
+ cfg.storage_bucket_name = "fake_bucket"
+ cfg.extra_data_disk_size_gb = 4
+ cfg.precreated_data_image_map = {
+ 4: "extradisk-image-4gb",
+ 10: "extradisk-image-10gb"
+ }
+
+ return cfg
+
+ def testCreateAndroidVirtualDevices(self):
+ """Test CreateAndroidVirtualDevices."""
+ cfg = self._CreateCfg()
+ fake_gs_url = "fake_gs_url"
+ fake_ip = "140.1.1.1"
+ fake_instance = "fake-instance"
+ fake_image = "fake-image"
+ fake_build_target = "fake_target"
+ fake_build_id = "12345"
+
+ # Mock uuid
+ fake_uuid = mock.MagicMock(hex="1234")
+ self.Patch(uuid, "uuid4", return_value=fake_uuid)
+ fake_gs_object = fake_uuid.hex + "-" + cfg.disk_image_name
+ self.storage_client.GetUrl.return_value = fake_gs_url
+
+ # Mock compute client methods
+ disk_name = "extradisk-image-4gb"
+ self.compute_client.GetInstanceIP.return_value = fake_ip
+ self.compute_client.GenerateImageName.return_value = fake_image
+ self.compute_client.GenerateInstanceName.return_value = fake_instance
+ self.compute_client.GetDataDiskName.return_value = disk_name
+
+ # Verify
+ r = device_driver.CreateAndroidVirtualDevices(
+ cfg, fake_build_target, fake_build_id)
+ self.build_client.CopyTo.assert_called_with(
+ fake_build_target, fake_build_id, artifact_name=cfg.disk_image_name,
+ destination_bucket=cfg.storage_bucket_name,
+ destination_path=fake_gs_object)
+ self.compute_client.CreateImage.assert_called_with(
+ image_name=fake_image, source_uri=fake_gs_url)
+ self.compute_client.CreateInstance.assert_called_with(
+ fake_instance, fake_image, disk_name)
+ self.compute_client.DeleteImage.assert_called_with(fake_image)
+ self.storage_client.Delete(cfg.storage_bucket_name, fake_gs_object)
+
+ self.assertEquals(
+ r.data,
+ {
+ "devices": [
+ {
+ "instance_name": fake_instance,
+ "ip": fake_ip,
+ },
+ ],
+ }
+ )
+ self.assertEquals(r.command, "create")
+ self.assertEquals(r.status, "SUCCESS")
+
+
+ def testDeleteAndroidVirtualDevices(self):
+ """Test DeleteAndroidVirtualDevices."""
+ instance_names = ["fake-instance-1", "fake-instance-2"]
+ self.compute_client.DeleteInstances.return_value = (instance_names, [],
+ [])
+ cfg = self._CreateCfg()
+ r = device_driver.DeleteAndroidVirtualDevices(cfg, instance_names)
+ self.compute_client.DeleteInstances.assert_called_once_with(
+ instance_names, cfg.zone)
+ self.assertEquals(r.data, {
+ "deleted": [
+ {
+ "name": instance_names[0],
+ "type": "instance",
+ },
+ {
+ "name": instance_names[1],
+ "type": "instance",
+ },
+ ],
+ })
+ self.assertEquals(r.command, "delete")
+ self.assertEquals(r.status, "SUCCESS")
+
+ def testCleanup(self):
+ expiration_mins = 30
+ before_deadline = "2015-10-29T12:00:30.018-07:00"
+ after_deadline = "2015-10-29T12:45:30.018-07:00"
+ now = "2015-10-29T13:00:30.018-07:00"
+ self.Patch(device_driver, "datetime")
+ device_driver.datetime.datetime.now.return_value = dateutil.parser.parse(
+ now)
+ device_driver.datetime.timedelta.return_value = datetime.timedelta(
+ minutes=expiration_mins)
+ fake_instances = [
+ {
+ "name": "fake_instance_1",
+ "creationTimestamp": before_deadline,
+ }, {
+ "name": "fake_instance_2",
+ "creationTimestamp": after_deadline,
+ }
+ ]
+ fake_images = [
+ {
+ "name": "extradisk-image-4gb",
+ "creationTimestamp": before_deadline,
+ }, {
+ "name": "fake_image_1",
+ "creationTimestamp": before_deadline,
+ }, {
+ "name": "fake_image_2",
+ "creationTimestamp": after_deadline,
+ }
+ ]
+ fake_disks = [
+ {
+ "name": "fake_disk_1",
+ "creationTimestamp": before_deadline,
+ }, {
+ "name": "fake_disk_2",
+ "creationTimestamp": before_deadline,
+ "users": ["some-instance-using-the-disk"]
+ }, {
+ "name": "fake_disk_3",
+ "creationTimestamp": after_deadline,
+ }
+ ]
+ fake_objects = [
+ {
+ "name": "fake_object_1",
+ "timeCreated": before_deadline,
+ }, {
+ "name": "fake_object_2",
+ "timeCreated": after_deadline,
+ }
+ ]
+ self.compute_client.ListInstances.return_value = fake_instances
+ self.compute_client.ListImages.return_value = fake_images
+ self.compute_client.ListDisks.return_value = fake_disks
+ self.storage_client.List.return_value = fake_objects
+ self.compute_client.DeleteInstances.return_value = (
+ ["fake_instance_1"], [], [])
+ self.compute_client.DeleteImages.return_value = (["fake_image_1"], [],
+ [])
+ self.compute_client.DeleteDisks.return_value = (["fake_disk_1"], [],
+ [])
+ self.storage_client.DeleteFiles.return_value = (["fake_object_1"], [],
+ [])
+ cfg = self._CreateCfg()
+ r = device_driver.Cleanup(cfg, expiration_mins)
+ self.assertEqual(r.errors, [])
+ expected_report_data = {
+ "deleted": [
+ {"name": "fake_instance_1",
+ "type": "instance"},
+ {"name": "fake_image_1",
+ "type": "image"},
+ {"name": "fake_disk_1",
+ "type": "disk"},
+ {"name": "fake_object_1",
+ "type": "cached_build_artifact"},
+ ]
+ }
+ self.assertEqual(r.data, expected_report_data)
+
+ self.compute_client.ListInstances.assert_called_once_with(
+ zone=cfg.zone)
+ self.compute_client.DeleteInstances.assert_called_once_with(
+ instances=["fake_instance_1"], zone=cfg.zone)
+
+ self.compute_client.ListImages.assert_called_once_with()
+ self.compute_client.DeleteImages.assert_called_once_with(
+ image_names=["fake_image_1"])
+
+ self.compute_client.ListDisks.assert_called_once_with(zone=cfg.zone)
+ self.compute_client.DeleteDisks.assert_called_once_with(
+ disk_names=["fake_disk_1"], zone=cfg.zone)
+
+ self.storage_client.List.assert_called_once_with(
+ bucket_name=cfg.storage_bucket_name)
+ self.storage_client.DeleteFiles.assert_called_once_with(
+ bucket_name=cfg.storage_bucket_name,
+ object_names=["fake_object_1"])
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/public/report_test.py b/public/report_test.py
new file mode 100644
index 00000000..e1fdc554
--- /dev/null
+++ b/public/report_test.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 - The Android Open Source Project
+#
+# 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.
+
+"""Tests for acloud.public.report."""
+
+import unittest
+from acloud.public import report
+
+
+class ReportTest(unittest.TestCase):
+ """Test Report class."""
+
+ def testAddData(self):
+ """test AddData."""
+ r = report.Report("create")
+ r.AddData("devices", {"instance_name": "instance_1"})
+ r.AddData("devices", {"instance_name": "instance_2"})
+ expected = {
+ "devices": [
+ {"instance_name": "instance_1"},
+ {"instance_name": "instance_2"}
+ ]
+ }
+ self.assertEqual(r.data, expected)
+
+ def testAddError(self):
+ """test AddError."""
+ r = report.Report("create")
+ r.errors.append("some errors")
+ r.errors.append("some errors")
+ self.assertEqual(r.errors, ["some errors", "some errors"])
+
+ def testSetStatus(self):
+ """test SetStatus."""
+ r = report.Report("create")
+ r.SetStatus(report.Status.SUCCESS)
+ self.assertEqual(r.status, "SUCCESS")
+
+ r.SetStatus(report.Status.FAIL)
+ self.assertEqual(r.status, "FAIL")
+
+ r.SetStatus(report.Status.BOOT_FAIL)
+ self.assertEqual(r.status, "BOOT_FAIL")
+
+ # Test that more severe status won't get overriden.
+ r.SetStatus(report.Status.FAIL)
+ self.assertEqual(r.status, "BOOT_FAIL")
+
+
+if __name__ == "__main__":
+ unittest.main()