blob: 9f4a252f1d87ee781a0f9c4fb5aec40c748ec87b [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
245 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_NeedProjectSetup")
246 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_SetupStorageBucket")
247 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_SetupClientIDSecret")
248 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_UpdateProject")
249 def testSetupProjectNoChange(self, mock_setproj, mock_setid,
250 mock_setstorage, mock_chkproj):
251 """test setup project and project not be changed."""
252 # Test project didn't change, and no need to setup client id/secret
253 mock_chkproj.return_value = False
254 self.gcp_env_runner.client_id = "test_client_id"
255 self.gcp_env_runner._SetupProject(self.gcloud_runner)
256 self.assertEqual(mock_setproj.call_count, 0)
257 self.assertEqual(mock_setid.call_count, 0)
258 mock_setstorage.assert_called_once()
259 # Test project didn't change, but client_id is empty
260 self.gcp_env_runner.client_id = ""
261 self.gcp_env_runner._SetupProject(self.gcloud_runner)
262 self.assertEqual(mock_setproj.call_count, 0)
263 mock_setid.assert_called_once()
264
265 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_NeedProjectSetup")
266 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_SetupStorageBucket")
267 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_SetupClientIDSecret")
268 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_UpdateProject")
269 def testSetupProjectChanged(self, mock_setproj, mock_setid,
270 mock_setstorage, mock_chkproj):
271 """test setup project when project changed."""
272 mock_chkproj.return_value = True
273 mock_setproj.return_value = True
274 self.gcp_env_runner._SetupProject(self.gcloud_runner)
275 mock_setproj.assert_called_once()
276 mock_setid.assert_called_once()
277 mock_setstorage.assert_called_once()
278
279 @mock.patch.object(utils, "GetUserAnswerYes")
280 def testNeedProjectSetup(self, mock_ans):
281 """test need project setup."""
282 # Test need project setup.
283 self.gcp_env_runner.project = ""
284 self.gcp_env_runner.zone = ""
285 self.assertTrue(self.gcp_env_runner._NeedProjectSetup())
286 # Test no need project setup and get user's answer.
287 self.gcp_env_runner.project = "test_project"
288 self.gcp_env_runner.zone = "test_zone"
289 self.gcp_env_runner._NeedProjectSetup()
290 mock_ans.assert_called_once()
291
292 def testNeedClientIDSetup(self):
293 """test need client_id setup."""
294 # Test project changed.
295 self.assertTrue(self.gcp_env_runner._NeedClientIDSetup(True))
296 # Test project is not changed but client_id or client_secret is empty.
297 self.gcp_env_runner.client_id = ""
298 self.gcp_env_runner.client_secret = ""
299 self.assertTrue(self.gcp_env_runner._NeedClientIDSetup(False))
300 # Test no need client_id setup.
301 self.gcp_env_runner.client_id = "test_client_id"
302 self.gcp_env_runner.client_secret = "test_client_secret"
303 self.assertFalse(self.gcp_env_runner._NeedClientIDSetup(False))
304
305 @mock.patch("subprocess.check_output")
306 @mock.patch.object(gcp_setup_runner.GcpTaskRunner, "_CreateDefaultBucket")
307 @mock.patch.object(gcp_setup_runner, "UpdateConfigFile")
308 def testSetupStorageBucket(self, mock_update, mock_create, mock_check):
309 """test setup storage bucket."""
310 self.gcp_env_runner.storage_bucket_name = ""
311 mock_create.return_value = "fake_create_default_bucket"
312 self.gcp_env_runner._SetupStorageBucket(self.gcloud_runner)
313 mock_check.assert_has_calls([
314 mock.call(["gsutil", "acl", "ch", "-u",
315 "android-build-prod@system.gserviceaccount.com:W",
316 "gs://fake_create_default_bucket"], stderr=-2)])
317 mock_update.assert_called_once()
318
319 @mock.patch("subprocess.check_output")
320 def testEnableGcloudServices(self, mock_run):
321 """test enable Gcloud services."""
322 self.gcp_env_runner._EnableGcloudServices(self.gcloud_runner)
323 mock_run.assert_has_calls([
324 mock.call(["gcloud", "services", "enable",
325 "storage-component.googleapis.com"], stderr=-2),
326 mock.call(["gcloud", "services", "enable",
327 "androidbuildinternal.googleapis.com"], stderr=-2),
328 mock.call(["gcloud", "services", "enable",
329 "compute.googleapis.com"], stderr=-2)])
herbertxue34776bb2018-07-03 21:57:48 +0800330
331
332if __name__ == "__main__":
333 unittest.main()