Home | History | Annotate | Download | only in lxc
      1 #!/usr/bin/env python
      2 # Copyright 2015 The Chromium 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 collections
      7 import json
      8 import os
      9 import shutil
     10 import tempfile
     11 import unittest
     12 from contextlib import contextmanager
     13 
     14 import common
     15 from autotest_lib.client.bin import utils
     16 from autotest_lib.site_utils.lxc import config as lxc_config
     17 from autotest_lib.site_utils.lxc import utils as lxc_utils
     18 
     19 class DeployConfigTest(unittest.TestCase):
     20     """Test DeployConfigManager.
     21     """
     22 
     23     def testValidate(self):
     24         """Test ssp_deploy_config.json can be validated.
     25         """
     26         global_deploy_config_file = os.path.join(
     27                 common.autotest_dir, lxc_config.SSP_DEPLOY_CONFIG_FILE)
     28         with open(global_deploy_config_file) as f:
     29             deploy_configs = json.load(f)
     30         for config in deploy_configs:
     31             lxc_config.DeployConfigManager.validate(config)
     32 
     33 
     34     def testPreStart(self):
     35         """Verifies that pre-start works correctly.
     36         Checks that mounts are correctly created in the container.
     37         """
     38         with TempDir() as tmpdir:
     39             config = [
     40                 {
     41                     'mount': True,
     42                     'source': tempfile.mkdtemp(dir=tmpdir),
     43                     'target': '/target0',
     44                     'readonly': True,
     45                     'force_create': False
     46                 },
     47                 {
     48                     'mount': True,
     49                     'source': tempfile.mkdtemp(dir=tmpdir),
     50                     'target': '/target1',
     51                     'readonly': False,
     52                     'force_create': False
     53                 },
     54             ]
     55             with ConfigFile(config) as test_cfg, MockContainer() as container:
     56                 manager = lxc_config.DeployConfigManager(container, test_cfg)
     57                 manager.deploy_pre_start()
     58                 self.assertEqual(len(config), len(container.mounts))
     59                 for c in config:
     60                     self.assertTrue(container.has_mount(c))
     61 
     62 
     63     def testPreStartWithCreate(self):
     64         """Verifies that pre-start creates mounted dirs.
     65 
     66         Checks that missing mount points are created when force_create is
     67         enabled.
     68         """
     69         with TempDir() as tmpdir:
     70             src_dir = os.path.join(tmpdir, 'foobar')
     71             config = [{
     72                 'mount': True,
     73                 'source': src_dir,
     74                 'target': '/target0',
     75                 'readonly': True,
     76                 'force_create': True
     77             }]
     78             with ConfigFile(config) as test_cfg, MockContainer() as container:
     79                 manager = lxc_config.DeployConfigManager(container, test_cfg)
     80                 # Pre-condition: the path doesn't exist.
     81                 self.assertFalse(lxc_utils.path_exists(src_dir))
     82 
     83                 # After calling deploy_pre_start, the path should exist and the
     84                 # mount should be created in the container.
     85                 manager.deploy_pre_start()
     86                 self.assertTrue(lxc_utils.path_exists(src_dir))
     87                 self.assertEqual(len(config), len(container.mounts))
     88                 for c in config:
     89                     self.assertTrue(container.has_mount(c))
     90 
     91 
     92 class _MockContainer(object):
     93     """A test mock for the container class.
     94 
     95     Don't instantiate this directly, use the MockContainer context manager
     96     defined below.
     97     """
     98 
     99     def __init__(self):
    100         self.rootfs = tempfile.mkdtemp()
    101         self.mounts = []
    102         self.MountConfig = collections.namedtuple(
    103                 'MountConfig', ['source', 'destination', 'readonly'])
    104 
    105 
    106     def cleanup(self):
    107         """Clean up tmp dirs created by the container."""
    108         # DeployConfigManager uses sudo to create some directories in the
    109         # container, so it's necessary to use sudo to clean up.
    110         utils.run('sudo rm -rf %s' % self.rootfs)
    111 
    112 
    113     def mount_dir(self, src, dst, ro):
    114         """Stub implementation of mount_dir.
    115 
    116         Records calls for later verification.
    117 
    118         @param src: Mount source dir.
    119         @param dst: Mount destination dir.
    120         @param ro: Read-only flag.
    121         """
    122         self.mounts.append(self.MountConfig(src, dst, ro))
    123 
    124 
    125     def has_mount(self, config):
    126         """Verifies whether an earlier call was made to mount_dir.
    127 
    128         @param config: The config object to verify.
    129 
    130         @return True if an earlier call was made to mount_dir that matches the
    131                 given mount configuration; False otherwise.
    132         """
    133         mount = self.MountConfig(config['source'],
    134                                  config['target'],
    135                                  config['readonly'])
    136         return mount in self.mounts
    137 
    138 
    139 @contextmanager
    140 def MockContainer():
    141     """Context manager for creating a _MockContainer for testing."""
    142     container = _MockContainer()
    143     try:
    144         yield container
    145     finally:
    146         container.cleanup()
    147 
    148 
    149 @contextmanager
    150 def ConfigFile(config):
    151     """Context manager for creating a config file.
    152 
    153     The given configs are translated into json and pushed into a temporary file
    154     that the DeployConfigManager can read.
    155 
    156     @param config: A list of config objects.  Each config object is a dictionary
    157                    which conforms to the format described in config.py.
    158     """
    159     with tempfile.NamedTemporaryFile() as tmp:
    160         json.dump(config, tmp)
    161         tmp.flush()
    162         yield tmp.name
    163 
    164 
    165 @contextmanager
    166 def TempDir():
    167     """Context manager for creating a temporary directory.
    168 
    169     We have to mount something.  Make temporary directories to mount.
    170     """
    171     tmpdir = tempfile.mkdtemp()
    172     try:
    173         yield tmpdir
    174     finally:
    175         shutil.rmtree(tmpdir)
    176 
    177 
    178 if __name__ == '__main__':
    179     unittest.main()
    180