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