blob: 1b51c87666b23a25af934a9b41043d5914537902 [file] [log] [blame]
herbertxue34776bb2018-07-03 21:57:48 +08001#!/usr/bin/env python
2#
3# Copyright 2018 - 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.
16"""Tests for acloud.setup.gcp_setup_runner."""
17
18import unittest
19import os
20import mock
21
chojoyce13cb0c52019-07-01 17:24:52 +080022# pylint: disable=no-name-in-module,import-error,no-member
23from acloud import errors
24from acloud.internal.lib import utils
herbertxue34776bb2018-07-03 21:57:48 +080025from acloud.internal.proto import user_config_pb2
26from acloud.public import config
27from acloud.setup import gcp_setup_runner
28
29_GCP_USER_CONFIG = """
30[compute]
31region = new_region
32zone = new_zone
33[core]
34account = new@google.com
35disable_usage_reporting = False
36project = new_project
37"""
38
39
40def _CreateCfgFile():
41 """A helper method that creates a mock configuration object."""
42 default_cfg = """
43project: "fake_project"
44zone: "fake_zone"
45storage_bucket_name: "fake_bucket"
46client_id: "fake_client_id"
47client_secret: "fake_client_secret"
48"""
49 return default_cfg
50
51
52# pylint: disable=protected-access
53class AcloudGCPSetupTest(unittest.TestCase):
54 """Test GCP Setup steps."""
55
56 def setUp(self):
57 """Create config and gcp_env_runner."""
58 self.cfg_path = "acloud_unittest.config"
59 file_write = open(self.cfg_path, 'w')
60 file_write.write(_CreateCfgFile().strip())
61 file_write.close()
62 self.gcp_env_runner = gcp_setup_runner.GcpTaskRunner(self.cfg_path)
63 self.gcloud_runner = gcp_setup_runner.GoogleSDKBins("")
64
65 def tearDown(self):
66 """Remove temp file."""
67 if os.path.isfile(self.cfg_path):
68 os.remove(self.cfg_path)
69
70 def testUpdateConfigFile(self):
71 """Test update config file."""
72 # Test update project field.
73 gcp_setup_runner.UpdateConfigFile(self.cfg_path, "project",
74 "test_project")
75 cfg = config.AcloudConfigManager.LoadConfigFromProtocolBuffer(
76 open(self.cfg_path, "r"), user_config_pb2.UserConfig)
77 self.assertEqual(cfg.project, "test_project")
78 self.assertEqual(cfg.ssh_private_key_path, "")
chojoyce13cb0c52019-07-01 17:24:52 +080079 # Test add ssh key path in config.
herbertxue34776bb2018-07-03 21:57:48 +080080 gcp_setup_runner.UpdateConfigFile(self.cfg_path,
81 "ssh_private_key_path", "test_path")
82 cfg = config.AcloudConfigManager.LoadConfigFromProtocolBuffer(
83 open(self.cfg_path, "r"), user_config_pb2.UserConfig)
84 self.assertEqual(cfg.project, "test_project")
85 self.assertEqual(cfg.ssh_private_key_path, "test_path")
chojoyce13cb0c52019-07-01 17:24:52 +080086 # Test config is not a file
87 with mock.patch("os.path.isfile") as chkfile:
88 chkfile.return_value = False
89 gcp_setup_runner.UpdateConfigFile(self.cfg_path, "project",
90 "test_project")
91 cfg = config.AcloudConfigManager.LoadConfigFromProtocolBuffer(
92 open(self.cfg_path, "r"), user_config_pb2.UserConfig)
93 self.assertEqual(cfg.project, "test_project")
herbertxue34776bb2018-07-03 21:57:48 +080094
chojoyce13cb0c52019-07-01 17:24:52 +080095 @mock.patch("subprocess.check_output")
herbertxue34776bb2018-07-03 21:57:48 +080096 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_BucketExists")
97 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_BucketInDefaultRegion")
chojoyce13cb0c52019-07-01 17:24:52 +080098 def testCreateDefaultBucket(self, mock_valid, mock_exist, mock_out):
herbertxue34776bb2018-07-03 21:57:48 +080099 """Test default bucket name.
100
101 Default bucket name is "acloud-{project}".
102 If default bucket exist but region is not in default region,
103 bucket name changes to "acloud-{project}-us".
104 """
105 self.gcp_env_runner.project = "fake_project"
106 mock_exist.return_value = False
107 mock_valid.return_value = False
herbertxue34776bb2018-07-03 21:57:48 +0800108 self.assertEqual(
109 "acloud-fake_project",
110 self.gcp_env_runner._CreateDefaultBucket(self.gcloud_runner))
chojoyce13cb0c52019-07-01 17:24:52 +0800111 self.gcloud_runner.gsutil_command_path = "gsutil"
112 mock_out.assert_called_once_with(
113 ["gsutil", "mb", "gs://acloud-fake_project"])
114
herbertxue34776bb2018-07-03 21:57:48 +0800115 mock_exist.return_value = True
116 mock_valid.return_value = False
117 self.assertEqual(
118 "acloud-fake_project-%s" %
119 gcp_setup_runner._DEFAULT_BUCKET_REGION.lower(),
120 self.gcp_env_runner._CreateDefaultBucket(self.gcloud_runner))
121
122 @mock.patch("os.path.dirname", return_value="")
123 @mock.patch("subprocess.check_output")
124 def testSeupProjectZone(self, mock_runner, mock_path):
125 """Test setup project and zone."""
126 gcloud_runner = gcp_setup_runner.GoogleSDKBins(mock_path)
127 self.gcp_env_runner.project = "fake_project"
128 self.gcp_env_runner.zone = "fake_zone"
129 mock_runner.side_effect = [0, _GCP_USER_CONFIG]
130 self.gcp_env_runner._UpdateProject(gcloud_runner)
131 self.assertEqual(self.gcp_env_runner.project, "new_project")
132 self.assertEqual(self.gcp_env_runner.zone, "new_zone")
133
134 @mock.patch("__builtin__.raw_input")
135 def testSetupClientIDSecret(self, mock_id):
136 """Test setup client ID and client secret."""
137 self.gcp_env_runner.client_id = "fake_client_id"
138 self.gcp_env_runner.client_secret = "fake_client_secret"
139 mock_id.side_effect = ["new_id", "new_secret"]
140 self.gcp_env_runner._SetupClientIDSecret()
141 self.assertEqual(self.gcp_env_runner.client_id, "new_id")
142 self.assertEqual(self.gcp_env_runner.client_secret, "new_secret")
143
herbertxueefb02a82018-10-08 12:02:54 +0800144 def testGenerateBucketName(self):
145 """Test generate default bucket name."""
146 # Filter out organization name for project name.
147 bucket_name = self.gcp_env_runner._GenerateBucketName(
148 "AOSP.com:fake_project")
149 self.assertEqual(bucket_name, "acloud-fake_project")
150
151 # A bucket name can contain lowercase alphanumeric characters,
152 # hyphens and underscores.
153 bucket_name = self.gcp_env_runner._GenerateBucketName(
154 "@.fake_*Project.#")
155 self.assertEqual(bucket_name, "acloud-fake_project")
156
157 # Bucket names must limit to 63 characters.
158 bucket_name = self.gcp_env_runner._GenerateBucketName(
159 "fake_project-fake_project-fake_project-fake_project-fake_project")
160 self.assertEqual(bucket_name,
161 "acloud-fake_project-fake_project-fake_project-fake_project-fake")
162
163 # Rule 3: Bucket names must end with a number or letter.
164 bucket_name = self.gcp_env_runner._GenerateBucketName("fake_project__--")
165 self.assertEqual(bucket_name, "acloud-fake_project")
166
herbertxue34776bb2018-07-03 21:57:48 +0800167 @mock.patch.object(gcp_setup_runner.GoogleSDKBins, "RunGsutil")
168 def testBucketExists(self, mock_bucket_name):
169 """Test bucket name exist or not."""
170 mock_bucket_name.return_value = "gs://acloud-fake_project/"
171 self.assertTrue(
172 self.gcp_env_runner._BucketExists("acloud-fake_project",
173 self.gcloud_runner))
174 self.assertFalse(
175 self.gcp_env_runner._BucketExists("wrong_project",
176 self.gcloud_runner))
177
178 @mock.patch.object(gcp_setup_runner.GoogleSDKBins, "RunGsutil")
179 def testBucketNotInDefaultRegion(self, mock_region):
180 """Test bucket region is in default region or not."""
181 mock_region.return_value = "Location constraint:ASIA"
182 self.assertFalse(
183 self.gcp_env_runner._BucketInDefaultRegion("test-bucket",
184 self.gcloud_runner))
185 mock_region.return_value = "Location constraint:US"
186 self.assertTrue(
187 self.gcp_env_runner._BucketInDefaultRegion("test-bucket",
188 self.gcloud_runner))
chojoyce13cb0c52019-07-01 17:24:52 +0800189 # Test parsing error.
190 mock_region.return_value = "Wrong Lable:ASIA"
191 self.assertRaises(errors.ParseBucketRegionError,
192 self.gcp_env_runner._BucketInDefaultRegion,
193 "test-bucket", self.gcloud_runner)
194
195 @mock.patch.object(gcp_setup_runner, "UpdateConfigFile")
196 @mock.patch.object(utils, "CreateSshKeyPairIfNotExist")
197 def testSetupSSHKeys(self, mock_check, mock_update):
198 """Test setup the pair of the ssh key for acloud.config."""
199 # Test ssh key has already setup
200 gcp_setup_runner.SetupSSHKeys(self.cfg_path,
201 "fake_private_key_path",
202 "fake_public_key_path")
203 self.assertEqual(mock_update.call_count, 0)
204 # Test if private_key_path is empty string
205 with mock.patch('os.path.expanduser') as ssh_path:
206 ssh_path.return_value = ""
207 gcp_setup_runner.SetupSSHKeys(self.cfg_path,
208 "fake_private_key_path",
209 "fake_public_key_path")
210 mock_check.assert_called_once_with(
211 gcp_setup_runner._DEFAULT_SSH_PRIVATE_KEY,
212 gcp_setup_runner._DEFAULT_SSH_PUBLIC_KEY)
213
214 mock_update.assert_has_calls([
215 mock.call(self.cfg_path, "ssh_private_key_path",
216 gcp_setup_runner._DEFAULT_SSH_PRIVATE_KEY),
217 mock.call(self.cfg_path, "ssh_public_key_path",
218 gcp_setup_runner._DEFAULT_SSH_PUBLIC_KEY)])
219
220 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_CreateStableHostImage")
221 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_EnableGcloudServices")
222 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_SetupProject")
223 @mock.patch.object(gcp_setup_runner, "GoogleSDKBins")
224 def testSetupGcloudInfo(self, mock_sdk, mock_set, mock_run, mock_create):
225 """test setup gcloud info"""
226 with mock.patch("google_sdk.GoogleSDK"):
227 self.gcp_env_runner._SetupGcloudInfo()
228 mock_sdk.assert_called_once()
229 mock_set.assert_called_once()
230 mock_run.assert_called_once()
231 mock_create.assert_called_once()
232
233 @mock.patch.object(gcp_setup_runner, "UpdateConfigFile")
234 def testCreateStableHostImage(self, mock_update):
235 """test create stable hostimage."""
236 # Test no need to create stable hose image name.
237 self.gcp_env_runner.stable_host_image_name = "fake_host_image_name"
238 self.gcp_env_runner._CreateStableHostImage()
239 self.assertEqual(mock_update.call_count, 0)
240 # Test need to reset stable hose image name.
241 self.gcp_env_runner.stable_host_image_name = ""
242 self.gcp_env_runner._CreateStableHostImage()
243 self.assertEqual(mock_update.call_count, 1)
244
herbertxueed577e82019-08-26 18:19:09 +0800245 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_CheckBillingEnable")
chojoyce13cb0c52019-07-01 17:24:52 +0800246 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_NeedProjectSetup")
247 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_SetupStorageBucket")
248 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_SetupClientIDSecret")
249 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_UpdateProject")
250 def testSetupProjectNoChange(self, mock_setproj, mock_setid,
herbertxueed577e82019-08-26 18:19:09 +0800251 mock_setstorage, mock_chkproj,
252 mock_check_billing):
chojoyce13cb0c52019-07-01 17:24:52 +0800253 """test setup project and project not be changed."""
254 # Test project didn't change, and no need to setup client id/secret
255 mock_chkproj.return_value = False
256 self.gcp_env_runner.client_id = "test_client_id"
257 self.gcp_env_runner._SetupProject(self.gcloud_runner)
258 self.assertEqual(mock_setproj.call_count, 0)
259 self.assertEqual(mock_setid.call_count, 0)
260 mock_setstorage.assert_called_once()
herbertxueed577e82019-08-26 18:19:09 +0800261 mock_check_billing.assert_called_once()
chojoyce13cb0c52019-07-01 17:24:52 +0800262 # Test project didn't change, but client_id is empty
263 self.gcp_env_runner.client_id = ""
264 self.gcp_env_runner._SetupProject(self.gcloud_runner)
265 self.assertEqual(mock_setproj.call_count, 0)
266 mock_setid.assert_called_once()
herbertxueed577e82019-08-26 18:19:09 +0800267 self.assertEqual(mock_check_billing.call_count, 2)
chojoyce13cb0c52019-07-01 17:24:52 +0800268
herbertxueed577e82019-08-26 18:19:09 +0800269 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_CheckBillingEnable")
chojoyce13cb0c52019-07-01 17:24:52 +0800270 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_NeedProjectSetup")
271 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_SetupStorageBucket")
272 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_SetupClientIDSecret")
273 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_UpdateProject")
274 def testSetupProjectChanged(self, mock_setproj, mock_setid,
herbertxueed577e82019-08-26 18:19:09 +0800275 mock_setstorage, mock_chkproj,
276 mock_check_billing):
chojoyce13cb0c52019-07-01 17:24:52 +0800277 """test setup project when project changed."""
278 mock_chkproj.return_value = True
279 mock_setproj.return_value = True
280 self.gcp_env_runner._SetupProject(self.gcloud_runner)
281 mock_setproj.assert_called_once()
282 mock_setid.assert_called_once()
283 mock_setstorage.assert_called_once()
herbertxueed577e82019-08-26 18:19:09 +0800284 mock_check_billing.assert_called_once()
chojoyce13cb0c52019-07-01 17:24:52 +0800285
286 @mock.patch.object(utils, "GetUserAnswerYes")
287 def testNeedProjectSetup(self, mock_ans):
288 """test need project setup."""
289 # Test need project setup.
290 self.gcp_env_runner.project = ""
291 self.gcp_env_runner.zone = ""
292 self.assertTrue(self.gcp_env_runner._NeedProjectSetup())
293 # Test no need project setup and get user's answer.
294 self.gcp_env_runner.project = "test_project"
295 self.gcp_env_runner.zone = "test_zone"
296 self.gcp_env_runner._NeedProjectSetup()
297 mock_ans.assert_called_once()
298
299 def testNeedClientIDSetup(self):
300 """test need client_id setup."""
301 # Test project changed.
302 self.assertTrue(self.gcp_env_runner._NeedClientIDSetup(True))
303 # Test project is not changed but client_id or client_secret is empty.
304 self.gcp_env_runner.client_id = ""
305 self.gcp_env_runner.client_secret = ""
306 self.assertTrue(self.gcp_env_runner._NeedClientIDSetup(False))
307 # Test no need client_id setup.
308 self.gcp_env_runner.client_id = "test_client_id"
309 self.gcp_env_runner.client_secret = "test_client_secret"
310 self.assertFalse(self.gcp_env_runner._NeedClientIDSetup(False))
311
312 @mock.patch("subprocess.check_output")
313 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_CreateDefaultBucket")
314 @mock.patch.object(gcp_setup_runner, "UpdateConfigFile")
315 def testSetupStorageBucket(self, mock_update, mock_create, mock_check):
316 """test setup storage bucket."""
317 self.gcp_env_runner.storage_bucket_name = ""
318 mock_create.return_value = "fake_create_default_bucket"
319 self.gcp_env_runner._SetupStorageBucket(self.gcloud_runner)
320 mock_check.assert_has_calls([
321 mock.call(["gsutil", "acl", "ch", "-u",
322 "android-build-prod@system.gserviceaccount.com:W",
323 "gs://fake_create_default_bucket"], stderr=-2)])
324 mock_update.assert_called_once()
325
326 @mock.patch("subprocess.check_output")
327 def testEnableGcloudServices(self, mock_run):
328 """test enable Gcloud services."""
herbertxue2160dd92019-10-07 17:34:37 +0800329 mock_run.return_value = ""
chojoyce13cb0c52019-07-01 17:24:52 +0800330 self.gcp_env_runner._EnableGcloudServices(self.gcloud_runner)
331 mock_run.assert_has_calls([
332 mock.call(["gcloud", "services", "enable",
herbertxue2160dd92019-10-07 17:34:37 +0800333 gcp_setup_runner._GOOGLE_CLOUD_STORAGE_SERVICE], stderr=-2),
chojoyce13cb0c52019-07-01 17:24:52 +0800334 mock.call(["gcloud", "services", "enable",
herbertxue2160dd92019-10-07 17:34:37 +0800335 gcp_setup_runner._ANDROID_BUILD_SERVICE], stderr=-2),
chojoyce13cb0c52019-07-01 17:24:52 +0800336 mock.call(["gcloud", "services", "enable",
herbertxue2160dd92019-10-07 17:34:37 +0800337 gcp_setup_runner._COMPUTE_ENGINE_SERVICE], stderr=-2)])
338
339 @mock.patch("subprocess.check_output")
340 def testGoogleAPIService(self, mock_run):
341 """Test GoogleAPIService"""
342 api_service = gcp_setup_runner.GoogleAPIService("service_name",
343 "error_message")
344 api_service.EnableService(self.gcloud_runner)
345 mock_run.assert_has_calls([
346 mock.call(["gcloud", "services", "enable", "service_name"], stderr=-2)])
347
348 @mock.patch("subprocess.check_output")
349 def testCheckBillingEnable(self, mock_run):
350 """Test CheckBillingEnable"""
351 # Test billing account in gcp project already enabled.
352 mock_run.return_value = "billingEnabled: true"
353 self.gcp_env_runner._CheckBillingEnable(self.gcloud_runner)
354 mock_run.assert_has_calls([
355 mock.call(["gcloud", "alpha", "billing", "projects", "describe",
356 self.gcp_env_runner.project])])
357
358 # Test billing account in gcp project was not enabled.
359 mock_run.return_value = "billingEnabled: false"
360 with self.assertRaises(errors.NoBillingError):
361 self.gcp_env_runner._CheckBillingEnable(self.gcloud_runner)
herbertxue34776bb2018-07-03 21:57:48 +0800362
363
364if __name__ == "__main__":
365 unittest.main()