1#!/usr/bin/python3 2# Copyright 2017 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6from __future__ import absolute_import 7from __future__ import division 8from __future__ import print_function 9 10import os 11import shutil 12import tempfile 13import unittest 14from contextlib import contextmanager 15 16import common 17from autotest_lib.client.common_lib import error 18from autotest_lib.site_utils import lxc 19from autotest_lib.site_utils.lxc import constants 20from autotest_lib.site_utils.lxc import unittest_setup 21from autotest_lib.site_utils.lxc import utils as lxc_utils 22from autotest_lib.site_utils.lxc.base_image import BaseImage 23from six.moves import range 24 25 26test_dir = None 27# A reference to an existing base container that can be copied for tests that 28# need a base container. This is an optimization. 29reference_container = None 30# The reference container can either be a reference to an existing container, or 31# to a container that was downloaded by this test. If the latter, then it needs 32# to be cleaned up when the tests are complete. 33cleanup_ref_container = False 34 35 36class BaseImageTests(lxc_utils.LXCTests): 37 """Unit tests to verify the BaseImage class.""" 38 39 def testCreate_existing(self): 40 """Verifies that BaseImage works with existing base containers.""" 41 with TestBaseContainer() as control: 42 manager = BaseImage(control.container_path, control.name) 43 self.assertIsNotNone(manager.base_container) 44 self.assertEquals(control.container_path, 45 manager.base_container.container_path) 46 self.assertEquals(control.name, manager.base_container.name) 47 try: 48 manager.base_container.refresh_status() 49 except error.ContainerError: 50 self.fail('Base container was not valid.\n%s' % 51 error.format_error()) 52 53 def testCleanup_noClones(self): 54 """Verifies that cleanup cleans up the base image.""" 55 base = lxc.Container.clone(src=reference_container, 56 new_name=constants.BASE, 57 new_path=test_dir, 58 snapshot=True) 59 60 manager = BaseImage(base.container_path, base.name) 61 # Precondition: ensure base exists and is a valid container. 62 base.refresh_status() 63 64 manager.cleanup() 65 66 # Verify that the base container was cleaned up. 67 self.assertFalse(lxc_utils.path_exists( 68 os.path.join(base.container_path, base.name))) 69 70 def testCleanup_withClones(self): 71 """Verifies that cleanup cleans up the base image. 72 73 Ensure that it works even when clones of the base image exist. 74 """ 75 # Do not snapshot, as snapshots of snapshots behave differently than 76 # snapshots of full container clones. BaseImage cleanup code assumes 77 # that the base container is not a snapshot. 78 base = lxc.Container.clone(src=reference_container, 79 new_name=constants.BASE, 80 new_path=test_dir, 81 snapshot=False) 82 manager = BaseImage(base.container_path, base.name) 83 clones = [] 84 for i in range(3): 85 clones.append(lxc.Container.clone(src=base, 86 new_name='clone_%d' % i, 87 snapshot=True)) 88 89 # Precondition: all containers are valid. 90 base.refresh_status() 91 for container in clones: 92 container.refresh_status() 93 94 manager.cleanup() 95 96 # Verify that all containers were cleaned up 97 self.assertFalse(lxc_utils.path_exists( 98 os.path.join(base.container_path, base.name))) 99 for container in clones: 100 if constants.SUPPORT_SNAPSHOT_CLONE: 101 # Snapshot clones should get deleted along with the base 102 # container. 103 self.assertFalse(lxc_utils.path_exists( 104 os.path.join(container.container_path, container.name))) 105 else: 106 # If snapshot clones aren't supported (e.g. on moblab), the 107 # clones should not be affected by the destruction of the base 108 # container. 109 try: 110 container.refresh_status() 111 except error.ContainerError: 112 self.fail(error.format_error()) 113 114 115class BaseImageSetupTests(lxc_utils.LXCTests): 116 """Unit tests to verify the setup of specific images. 117 118 Some images differ in layout from others. These tests test specific images 119 to make sure the setup code works for all of them. 120 """ 121 122 def setUp(self): 123 self.manager = BaseImage(test_dir, lxc.BASE) 124 125 def tearDown(self): 126 self.manager.cleanup() 127 128 def testSetupBase05(self): 129 """Verifies that setup works for moblab base container. 130 131 Verifies that the code for installing the rootfs location into the 132 lxc config, is working correctly. 133 """ 134 # Set up the bucket, then start the base container, and verify it works. 135 self.manager.setup('base_05') 136 container = self.manager.base_container 137 138 container.start(wait_for_network=False) 139 self.assertTrue(container.is_running()) 140 141 @unittest.skipIf(constants.IS_MOBLAB, 142 "Moblab does not support the regular base container.") 143 def testSetupBase09(self): 144 """Verifies that setup works for base container. 145 146 Verifies that the code for installing the rootfs location into the 147 lxc config, is working correctly. 148 """ 149 self.manager.setup('base_09') 150 container = self.manager.base_container 151 152 container.start(wait_for_network=False) 153 self.assertTrue(container.is_running()) 154 155 156@contextmanager 157def TestBaseContainer(name=constants.BASE): 158 """Context manager for creating a scoped base container for testing. 159 160 @param name: (optional) Name of the base container. If this is not 161 provided, the default base container name is used. 162 """ 163 container = lxc.Container.clone(src=reference_container, 164 new_name=name, 165 new_path=test_dir, 166 snapshot=True, 167 cleanup=False) 168 try: 169 yield container 170 finally: 171 if not unittest_setup.config.skip_cleanup: 172 container.destroy() 173 174 175def setUpModule(): 176 """Module setup for base image unittests. 177 178 Sets up a test directory along with a reference container that is used by 179 tests that need an existing base container. 180 """ 181 global test_dir 182 global reference_container 183 global cleanup_ref_container 184 185 test_dir = tempfile.mkdtemp(dir=lxc.DEFAULT_CONTAINER_PATH, 186 prefix='base_container_manager_unittest_') 187 # Unfortunately, aside from duping the BaseImage code completely, there 188 # isn't an easy way to download and configure a base container. So even 189 # though this is the BaseImage unittest, we use a BaseImage to set it up. 190 bcm = BaseImage(lxc.DEFAULT_CONTAINER_PATH, lxc.BASE) 191 if bcm.base_container is None: 192 bcm.setup() 193 cleanup_ref_container = True 194 reference_container = bcm.base_container 195 196 197def tearDownModule(): 198 """Deletes the test dir and reference container.""" 199 if not unittest_setup.config.skip_cleanup: 200 if cleanup_ref_container: 201 reference_container.destroy() 202 shutil.rmtree(test_dir) 203 204 205if __name__ == '__main__': 206 unittest.main() 207