Home | History | Annotate | Download | only in lxc
      1 #!/usr/bin/python
      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 
      6 import os
      7 import shutil
      8 import tempfile
      9 import unittest
     10 from contextlib import contextmanager
     11 
     12 import common
     13 from autotest_lib.client.common_lib import error
     14 from autotest_lib.site_utils import lxc
     15 from autotest_lib.site_utils.lxc import BaseImage
     16 from autotest_lib.site_utils.lxc import constants
     17 from autotest_lib.site_utils.lxc import unittest_setup
     18 from autotest_lib.site_utils.lxc import utils as lxc_utils
     19 
     20 
     21 test_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.
     24 reference_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.
     28 cleanup_ref_container = False
     29 
     30 
     31 class 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 
    115 class 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
    160 def 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 
    178 def 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 
    200 def 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 
    208 if __name__ == '__main__':
    209     unittest.main()
    210