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