1# Copyright 2019 - The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Tests for remote_instance_cf_device_factory.""" 15 16import glob 17import os 18import shutil 19import subprocess 20import tempfile 21import unittest 22import uuid 23 24from unittest import mock 25 26import six 27 28from acloud.create import avd_spec 29from acloud.internal import constants 30from acloud.internal.lib import android_build_client 31from acloud.internal.lib import auth 32from acloud.internal.lib import cvd_compute_client_multi_stage 33from acloud.internal.lib import driver_test_lib 34from acloud.internal.lib import ssh 35from acloud.internal.lib import utils 36from acloud.list import list as list_instances 37from acloud.public.actions import remote_instance_cf_device_factory 38 39 40class RemoteInstanceDeviceFactoryTest(driver_test_lib.BaseDriverTest): 41 """Test RemoteInstanceDeviceFactory method.""" 42 43 def setUp(self): 44 """Set up the test.""" 45 super().setUp() 46 self.Patch(auth, "CreateCredentials", return_value=mock.MagicMock()) 47 self.Patch(android_build_client.AndroidBuildClient, "InitResourceHandle") 48 self.Patch(cvd_compute_client_multi_stage.CvdComputeClient, "InitResourceHandle") 49 self.Patch(list_instances, "GetInstancesFromInstanceNames", return_value=mock.MagicMock()) 50 self.Patch(list_instances, "ChooseOneRemoteInstance", return_value=mock.MagicMock()) 51 self.Patch(utils, "GetBuildEnvironmentVariable", 52 return_value="test_env_cf_arm") 53 self.Patch(glob, "glob", return_vale=["fake.img"]) 54 55 # pylint: disable=protected-access 56 @mock.patch.object(remote_instance_cf_device_factory.RemoteInstanceDeviceFactory, 57 "_FetchBuild") 58 @mock.patch.object(remote_instance_cf_device_factory.RemoteInstanceDeviceFactory, 59 "_UploadLocalImageArtifacts") 60 def testProcessArtifacts(self, mock_upload, mock_download): 61 """test ProcessArtifacts.""" 62 # Test image source type is local. 63 args = mock.MagicMock() 64 args.config_file = "" 65 args.avd_type = constants.TYPE_CF 66 args.flavor = "phone" 67 args.local_image = constants.FIND_IN_BUILD_ENV 68 args.local_system_image = None 69 args.launch_args = None 70 avd_spec_local_img = avd_spec.AVDSpec(args) 71 fake_image_name = "/fake/aosp_cf_x86_phone-img-eng.username.zip" 72 fake_host_package_name = "/fake/host_package.tar.gz" 73 factory_local_img = remote_instance_cf_device_factory.RemoteInstanceDeviceFactory( 74 avd_spec_local_img, 75 fake_image_name, 76 fake_host_package_name) 77 factory_local_img._ProcessArtifacts(constants.IMAGE_SRC_LOCAL) 78 self.assertEqual(mock_upload.call_count, 1) 79 80 # Test image source type is remote. 81 args.local_image = None 82 args.build_id = "1234" 83 args.branch = "fake_branch" 84 args.build_target = "fake_target" 85 args.system_build_id = "2345" 86 args.system_branch = "sys_branch" 87 args.system_build_target = "sys_target" 88 args.kernel_build_id = "3456" 89 args.kernel_branch = "kernel_branch" 90 args.kernel_build_target = "kernel_target" 91 avd_spec_remote_img = avd_spec.AVDSpec(args) 92 self.Patch(cvd_compute_client_multi_stage.CvdComputeClient, "UpdateFetchCvd") 93 factory_remote_img = remote_instance_cf_device_factory.RemoteInstanceDeviceFactory( 94 avd_spec_remote_img) 95 factory_remote_img._ProcessArtifacts(constants.IMAGE_SRC_REMOTE) 96 self.assertEqual(mock_download.call_count, 1) 97 98 # pylint: disable=protected-access 99 @mock.patch.dict(os.environ, {constants.ENV_BUILD_TARGET:'fake-target'}) 100 def testCreateGceInstanceNameMultiStage(self): 101 """test create gce instance.""" 102 # Mock uuid 103 args = mock.MagicMock() 104 args.config_file = "" 105 args.avd_type = constants.TYPE_CF 106 args.flavor = "phone" 107 args.local_image = constants.FIND_IN_BUILD_ENV 108 args.local_system_image = None 109 args.adb_port = None 110 args.launch_args = None 111 fake_avd_spec = avd_spec.AVDSpec(args) 112 fake_avd_spec.cfg.enable_multi_stage = True 113 fake_avd_spec._instance_name_to_reuse = None 114 115 fake_uuid = mock.MagicMock(hex="1234") 116 self.Patch(uuid, "uuid4", return_value=fake_uuid) 117 self.Patch(cvd_compute_client_multi_stage.CvdComputeClient, "CreateInstance") 118 self.Patch(cvd_compute_client_multi_stage.CvdComputeClient, 119 "GetHostImageName", return_value="fake_image") 120 fake_host_package_name = "/fake/host_package.tar.gz" 121 fake_image_name = "/fake/aosp_cf_x86_phone-img-eng.username.zip" 122 123 factory = remote_instance_cf_device_factory.RemoteInstanceDeviceFactory( 124 fake_avd_spec, 125 fake_image_name, 126 fake_host_package_name) 127 self.assertEqual(factory._CreateGceInstance(), "ins-1234-userbuild-aosp-cf-x86-phone") 128 129 # Can't get target name from zip file name. 130 fake_image_name = "/fake/aosp_cf_x86_phone.username.zip" 131 factory = remote_instance_cf_device_factory.RemoteInstanceDeviceFactory( 132 fake_avd_spec, 133 fake_image_name, 134 fake_host_package_name) 135 self.assertEqual(factory._CreateGceInstance(), "ins-1234-userbuild-fake-target") 136 137 # No image zip path, it uses local build images. 138 fake_image_name = "" 139 factory = remote_instance_cf_device_factory.RemoteInstanceDeviceFactory( 140 fake_avd_spec, 141 fake_image_name, 142 fake_host_package_name) 143 self.assertEqual(factory._CreateGceInstance(), "ins-1234-userbuild-fake-target") 144 145 @mock.patch.dict(os.environ, {constants.ENV_BUILD_TARGET:'fake-target'}) 146 def testRemoteHostInstanceName(self): 147 """Test Remote host instance name.""" 148 args = mock.MagicMock() 149 args.config_file = "" 150 args.avd_type = constants.TYPE_CF 151 args.flavor = "phone" 152 args.remote_host = "1.1.1.1" 153 args.local_image = constants.FIND_IN_BUILD_ENV 154 args.local_system_image = None 155 args.adb_port = None 156 args.launch_args = None 157 fake_avd_spec = avd_spec.AVDSpec(args) 158 fake_avd_spec.cfg.enable_multi_stage = True 159 fake_avd_spec._instance_name_to_reuse = None 160 fake_uuid = mock.MagicMock(hex="1234") 161 self.Patch(uuid, "uuid4", return_value=fake_uuid) 162 self.Patch(cvd_compute_client_multi_stage.CvdComputeClient, "CreateInstance") 163 self.Patch(cvd_compute_client_multi_stage.CvdComputeClient, "InitRemoteHost") 164 fake_host_package_name = "/fake/host_package.tar.gz" 165 166 fake_image_name = "/fake/aosp_cf_x86_phone-img-eng.username.zip" 167 factory = remote_instance_cf_device_factory.RemoteInstanceDeviceFactory( 168 fake_avd_spec, 169 fake_image_name, 170 fake_host_package_name) 171 self.assertEqual(factory._InitRemotehost(), "host-1.1.1.1-userbuild-aosp_cf_x86_phone") 172 173 # No image zip path, it uses local build images. 174 fake_image_name = "" 175 factory = remote_instance_cf_device_factory.RemoteInstanceDeviceFactory( 176 fake_avd_spec, 177 fake_image_name, 178 fake_host_package_name) 179 self.assertEqual(factory._InitRemotehost(), "host-1.1.1.1-userbuild-fake-target") 180 181 def testReuseInstanceNameMultiStage(self): 182 """Test reuse instance name.""" 183 args = mock.MagicMock() 184 args.config_file = "" 185 args.avd_type = constants.TYPE_CF 186 args.flavor = "phone" 187 args.local_image = constants.FIND_IN_BUILD_ENV 188 args.local_system_image = None 189 args.adb_port = None 190 args.launch_args = None 191 fake_avd_spec = avd_spec.AVDSpec(args) 192 fake_avd_spec.cfg.enable_multi_stage = True 193 fake_avd_spec._instance_name_to_reuse = "fake-1234-userbuild-fake-target" 194 fake_uuid = mock.MagicMock(hex="1234") 195 self.Patch(uuid, "uuid4", return_value=fake_uuid) 196 self.Patch(cvd_compute_client_multi_stage.CvdComputeClient, "CreateInstance") 197 self.Patch(cvd_compute_client_multi_stage.CvdComputeClient, 198 "GetHostImageName", return_value="fake_image") 199 fake_host_package_name = "/fake/host_package.tar.gz" 200 fake_image_name = "/fake/aosp_cf_x86_phone-img-eng.username.zip" 201 factory = remote_instance_cf_device_factory.RemoteInstanceDeviceFactory( 202 fake_avd_spec, 203 fake_image_name, 204 fake_host_package_name) 205 self.assertEqual(factory._CreateGceInstance(), "fake-1234-userbuild-fake-target") 206 207 def testGetBuildInfoDict(self): 208 """Test GetBuildInfoDict.""" 209 fake_host_package_name = "/fake/host_package.tar.gz" 210 fake_image_name = "/fake/aosp_cf_x86_phone-img-eng.username.zip" 211 args = mock.MagicMock() 212 # Test image source type is local. 213 args.config_file = "" 214 args.avd_type = constants.TYPE_CF 215 args.flavor = "phone" 216 args.local_image = "fake_local_image" 217 args.local_system_image = None 218 args.adb_port = None 219 args.cheeps_betty_image = None 220 args.launch_args = None 221 avd_spec_local_image = avd_spec.AVDSpec(args) 222 factory = remote_instance_cf_device_factory.RemoteInstanceDeviceFactory( 223 avd_spec_local_image, 224 fake_image_name, 225 fake_host_package_name) 226 self.assertEqual(factory.GetBuildInfoDict(), None) 227 228 # Test image source type is remote. 229 args.local_image = None 230 args.build_id = "123" 231 args.branch = "fake_branch" 232 args.build_target = "fake_target" 233 args.system_build_id = "234" 234 args.system_branch = "sys_branch" 235 args.system_build_target = "sys_target" 236 args.kernel_build_id = "345" 237 args.kernel_branch = "kernel_branch" 238 args.kernel_build_target = "kernel_target" 239 args.bootloader_build_id = "456" 240 args.bootloader_branch = "bootloader_branch" 241 args.bootloader_build_target = "bootloader_target" 242 avd_spec_remote_image = avd_spec.AVDSpec(args) 243 factory = remote_instance_cf_device_factory.RemoteInstanceDeviceFactory( 244 avd_spec_remote_image, 245 fake_image_name, 246 fake_host_package_name) 247 expected_build_info = { 248 "build_id": "123", 249 "branch": "fake_branch", 250 "build_target": "fake_target", 251 "system_build_id": "234", 252 "system_branch": "sys_branch", 253 "system_build_target": "sys_target", 254 "kernel_build_id": "345", 255 "kernel_branch": "kernel_branch", 256 "kernel_build_target": "kernel_target", 257 "bootloader_build_id": "456", 258 "bootloader_branch": "bootloader_branch", 259 "bootloader_build_target": "bootloader_target" 260 } 261 self.assertEqual(factory.GetBuildInfoDict(), expected_build_info) 262 263 @mock.patch.object(ssh, "ShellCmdWithRetry") 264 @mock.patch.object(ssh.Ssh, "Run") 265 def testUploadArtifacts(self, mock_ssh_run, mock_shell): 266 """Test UploadArtifacts.""" 267 fake_host_package = "/fake/host_package.tar.gz" 268 fake_image = "/fake/aosp_cf_x86_phone-img-eng.username.zip" 269 fake_local_image_dir = "/fake_image" 270 fake_ip = ssh.IP(external="1.1.1.1", internal="10.1.1.1") 271 args = mock.MagicMock() 272 # Test local image extract from image zip case. 273 args.config_file = "" 274 args.avd_type = constants.TYPE_CF 275 args.flavor = "phone" 276 args.local_image = "fake_local_image" 277 args.local_system_image = None 278 args.adb_port = None 279 args.launch_args = None 280 avd_spec_local_image = avd_spec.AVDSpec(args) 281 factory = remote_instance_cf_device_factory.RemoteInstanceDeviceFactory( 282 avd_spec_local_image, 283 fake_image, 284 fake_host_package) 285 factory._ssh = ssh.Ssh(ip=fake_ip, 286 user=constants.GCE_USER, 287 ssh_private_key_path="/fake/acloud_rea") 288 factory._UploadLocalImageArtifacts(fake_image, 289 fake_host_package, 290 fake_local_image_dir) 291 expected_cmd1 = ("/usr/bin/install_zip.sh . < %s" % fake_image) 292 expected_cmd2 = ("tar -x -z -f - < %s" % fake_host_package) 293 mock_ssh_run.assert_has_calls([ 294 mock.call(expected_cmd1), 295 mock.call(expected_cmd2)]) 296 297 # Test local image get from local folder case. 298 fake_image = None 299 self.Patch(glob, "glob", side_effect=[["fake.img"], ["bootloader"], ["kernel"]]) 300 factory._UploadLocalImageArtifacts(fake_image, 301 fake_host_package, 302 fake_local_image_dir) 303 expected_cmd = ( 304 "tar -cf - --lzop -S -C %s fake.img bootloader kernel | " 305 "%s -- tar -xf - --lzop -S" % 306 (fake_local_image_dir, factory._ssh.GetBaseCmd(constants.SSH_BIN))) 307 mock_shell.assert_called_once_with(expected_cmd) 308 309 mock_shell.reset_mock() 310 required_images = mock.mock_open(read_data=( 311 "boot.img\n" 312 "cache.img\n" 313 "super.img\n" 314 "userdata.img\n" 315 "bootloader\n")) 316 with mock.patch.object(six.moves.builtins, "open", required_images): 317 factory._UploadLocalImageArtifacts(fake_image, 318 fake_host_package, 319 fake_local_image_dir) 320 expected_cmd = ( 321 "tar -cf - --lzop -S -C %s boot.img cache.img super.img userdata.img bootloader | " 322 "%s -- tar -xf - --lzop -S" % 323 (fake_local_image_dir, factory._ssh.GetBaseCmd(constants.SSH_BIN))) 324 mock_shell.assert_called_once_with(expected_cmd) 325 326 @mock.patch.object(ssh, "ShellCmdWithRetry") 327 def testUploadRemoteImageArtifacts(self, mock_shell): 328 """Test UploadRemoteImageArtifacts.""" 329 fake_host_package = "/fake/host_package.tar.gz" 330 fake_image_zip = None 331 fake_local_image_dir = "/fake_image" 332 fake_ip = ssh.IP(external="1.1.1.1", internal="10.1.1.1") 333 args = mock.MagicMock() 334 # Test local image extract from image zip case. 335 args.config_file = "" 336 args.avd_type = constants.TYPE_CF 337 args.flavor = "phone" 338 args.local_image = "fake_local_image" 339 args.local_system_image = None 340 args.adb_port = None 341 args.launch_args = None 342 avd_spec_local_image = avd_spec.AVDSpec(args) 343 factory = remote_instance_cf_device_factory.RemoteInstanceDeviceFactory( 344 avd_spec_local_image, 345 fake_image_zip, 346 fake_host_package) 347 factory._ssh = ssh.Ssh(ip=fake_ip, 348 user=constants.GCE_USER, 349 ssh_private_key_path="/fake/acloud_rea") 350 351 self.Patch(glob, "glob", return_value=["fake.img", "bootloader", "kernel"]) 352 factory._UploadRemoteImageArtifacts(fake_local_image_dir) 353 354 expected_cmd = ( 355 "tar -cf - --lzop -S -C %s fake.img bootloader kernel | " 356 "%s -- tar -xf - --lzop -S" % 357 (fake_local_image_dir, factory._ssh.GetBaseCmd(constants.SSH_BIN))) 358 mock_shell.assert_called_once_with(expected_cmd) 359 360 @mock.patch.object(remote_instance_cf_device_factory.RemoteInstanceDeviceFactory, 361 "_InitRemotehost") 362 @mock.patch.object(remote_instance_cf_device_factory.RemoteInstanceDeviceFactory, 363 "_UploadLocalImageArtifacts") 364 @mock.patch.object(remote_instance_cf_device_factory.RemoteInstanceDeviceFactory, 365 "_LaunchCvd") 366 def testLocalImageRemoteHost(self, mock_launchcvd, mock_upload, mock_init_remote_host): 367 """Test local image with remote host.""" 368 self.Patch( 369 cvd_compute_client_multi_stage, 370 "CvdComputeClient", 371 return_value=mock.MagicMock()) 372 fake_avd_spec = mock.MagicMock() 373 fake_avd_spec.instance_type = constants.INSTANCE_TYPE_HOST 374 fake_avd_spec.image_source = constants.IMAGE_SRC_LOCAL 375 fake_avd_spec._instance_name_to_reuse = None 376 fake_host_package_name = "/fake/host_package.tar.gz" 377 fake_image_name = "" 378 factory = remote_instance_cf_device_factory.RemoteInstanceDeviceFactory( 379 fake_avd_spec, 380 fake_image_name, 381 fake_host_package_name) 382 factory.CreateInstance() 383 self.assertEqual(mock_init_remote_host.call_count, 1) 384 self.assertEqual(mock_upload.call_count, 1) 385 self.assertEqual(mock_launchcvd.call_count, 1) 386 387 @mock.patch.object(remote_instance_cf_device_factory.RemoteInstanceDeviceFactory, 388 "_CreateGceInstance") 389 @mock.patch.object(remote_instance_cf_device_factory.RemoteInstanceDeviceFactory, 390 "_UploadLocalImageArtifacts") 391 @mock.patch.object(remote_instance_cf_device_factory.RemoteInstanceDeviceFactory, 392 "_LaunchCvd") 393 def testLocalImageCreateInstance(self, mock_launchcvd, mock_upload, mock_create_gce_instance): 394 """Test local image with create instance.""" 395 self.Patch( 396 cvd_compute_client_multi_stage, 397 "CvdComputeClient", 398 return_value=mock.MagicMock()) 399 fake_avd_spec = mock.MagicMock() 400 fake_avd_spec.instance_type = constants.INSTANCE_TYPE_REMOTE 401 fake_avd_spec.image_source = constants.IMAGE_SRC_LOCAL 402 fake_avd_spec._instance_name_to_reuse = None 403 fake_host_package_name = "/fake/host_package.tar.gz" 404 fake_image_name = "" 405 factory = remote_instance_cf_device_factory.RemoteInstanceDeviceFactory( 406 fake_avd_spec, 407 fake_image_name, 408 fake_host_package_name) 409 factory.CreateInstance() 410 self.assertEqual(mock_create_gce_instance.call_count, 1) 411 self.assertEqual(mock_upload.call_count, 1) 412 self.assertEqual(mock_launchcvd.call_count, 1) 413 414 # pylint: disable=no-member 415 @mock.patch.object(subprocess, "check_call") 416 def testDownloadArtifacts(self, mock_check_call): 417 """Test process remote cuttlefish image.""" 418 extract_path = "/tmp/1111/" 419 fake_remote_image = {"build_target" : "aosp_cf_x86_phone-userdebug", 420 "branch" : "aosp-master", 421 "build_id": "1234"} 422 self.Patch( 423 cvd_compute_client_multi_stage, 424 "CvdComputeClient", 425 return_value=mock.MagicMock()) 426 self.Patch(tempfile, "mkdtemp", return_value="/tmp/1111/") 427 self.Patch(shutil, "rmtree") 428 fake_avd_spec = mock.MagicMock() 429 fake_avd_spec.cfg = mock.MagicMock() 430 fake_avd_spec.cfg.creds_cache_file = "cache.file" 431 fake_avd_spec.remote_image = fake_remote_image 432 fake_avd_spec.image_download_dir = "/tmp" 433 self.Patch(os.path, "exists", return_value=False) 434 self.Patch(os, "makedirs") 435 factory = remote_instance_cf_device_factory.RemoteInstanceDeviceFactory( 436 fake_avd_spec) 437 438 factory._DownloadArtifacts(extract_path) 439 self.assertEqual(mock_check_call.call_count, 1) 440 441 @mock.patch.object(remote_instance_cf_device_factory.RemoteInstanceDeviceFactory, 442 "_UploadLocalImageArtifacts") 443 @mock.patch.object(remote_instance_cf_device_factory.RemoteInstanceDeviceFactory, 444 "_UploadRemoteImageArtifacts") 445 def testProcessRemoteHostArtifacts(self, 446 mock_upload_remote_image, 447 mock_upload_local_image): 448 """Test process remote host artifacts.""" 449 self.Patch( 450 cvd_compute_client_multi_stage, 451 "CvdComputeClient", 452 return_value=mock.MagicMock()) 453 fake_avd_spec = mock.MagicMock() 454 455 # Test process remote host artifacts with local images. 456 fake_avd_spec.instance_type = constants.INSTANCE_TYPE_HOST 457 fake_avd_spec.image_source = constants.IMAGE_SRC_LOCAL 458 fake_avd_spec._instance_name_to_reuse = None 459 fake_avd_spec.cfg = mock.MagicMock() 460 fake_avd_spec.cfg.creds_cache_file = "cache.file" 461 fake_host_package_name = "/fake/host_package.tar.gz" 462 fake_image_name = "" 463 factory = remote_instance_cf_device_factory.RemoteInstanceDeviceFactory( 464 fake_avd_spec, 465 fake_image_name, 466 fake_host_package_name) 467 factory._ProcessRemoteHostArtifacts() 468 self.assertEqual(mock_upload_local_image.call_count, 1) 469 470 # Test process remote host artifacts with remote images. 471 fake_tmp_folder = "/tmp/1111/" 472 self.Patch(tempfile, "mkdtemp", return_value=fake_tmp_folder) 473 self.Patch(shutil, "rmtree") 474 self.Patch(subprocess, "check_call") 475 fake_avd_spec.instance_type = constants.INSTANCE_TYPE_HOST 476 fake_avd_spec.image_source = constants.IMAGE_SRC_REMOTE 477 fake_avd_spec._instance_name_to_reuse = None 478 factory = remote_instance_cf_device_factory.RemoteInstanceDeviceFactory( 479 fake_avd_spec) 480 factory._ProcessRemoteHostArtifacts() 481 self.assertEqual(mock_upload_remote_image.call_count, 1) 482 shutil.rmtree.assert_called_once_with(fake_tmp_folder) 483 484 485if __name__ == "__main__": 486 unittest.main() 487