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