| #!/usr/bin/env python2 |
| # Copyright 2015 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import collections |
| import json |
| import os |
| import tempfile |
| import unittest |
| from contextlib import contextmanager |
| |
| import common |
| from autotest_lib.client.bin import utils |
| from autotest_lib.site_utils.lxc import config as lxc_config |
| from autotest_lib.site_utils.lxc import utils as lxc_utils |
| |
| class DeployConfigTest(unittest.TestCase): |
| """Test DeployConfigManager. |
| """ |
| |
| def testValidate(self): |
| """Test ssp_deploy_config.json can be validated. |
| """ |
| global_deploy_config_file = os.path.join( |
| common.autotest_dir, lxc_config.SSP_DEPLOY_CONFIG_FILE) |
| with open(global_deploy_config_file) as f: |
| deploy_configs = json.load(f) |
| for config in deploy_configs: |
| if 'append' in config: |
| lxc_config.DeployConfigManager.validate(config) |
| elif 'mount' in config: |
| # validate_mount checks that the path exists, so we can't call |
| # it from tests. |
| pass |
| else: |
| self.fail('Non-deploy/mount config %s' % config) |
| |
| |
| def testPreStart(self): |
| """Verifies that pre-start works correctly. |
| Checks that mounts are correctly created in the container. |
| """ |
| with lxc_utils.TempDir() as tmpdir: |
| config = [ |
| { |
| 'mount': True, |
| 'source': tempfile.mkdtemp(dir=tmpdir), |
| 'target': '/target0', |
| 'readonly': True, |
| 'force_create': False |
| }, |
| { |
| 'mount': True, |
| 'source': tempfile.mkdtemp(dir=tmpdir), |
| 'target': '/target1', |
| 'readonly': False, |
| 'force_create': False |
| }, |
| ] |
| with ConfigFile(config) as test_cfg, MockContainer() as container: |
| manager = lxc_config.DeployConfigManager(container, test_cfg) |
| manager.deploy_pre_start() |
| self.assertEqual(len(config), len(container.mounts)) |
| for c in config: |
| self.assertTrue(container.has_mount(c)) |
| |
| |
| def testPreStartWithCreate(self): |
| """Verifies that pre-start creates mounted dirs. |
| |
| Checks that missing mount points are created when force_create is |
| enabled. |
| """ |
| with lxc_utils.TempDir() as tmpdir: |
| src_dir = os.path.join(tmpdir, 'foobar') |
| config = [{ |
| 'mount': True, |
| 'source': src_dir, |
| 'target': '/target0', |
| 'readonly': True, |
| 'force_create': True |
| }] |
| with ConfigFile(config) as test_cfg, MockContainer() as container: |
| manager = lxc_config.DeployConfigManager(container, test_cfg) |
| # Pre-condition: the path doesn't exist. |
| self.assertFalse(lxc_utils.path_exists(src_dir)) |
| |
| # After calling deploy_pre_start, the path should exist and the |
| # mount should be created in the container. |
| manager.deploy_pre_start() |
| self.assertTrue(lxc_utils.path_exists(src_dir)) |
| self.assertEqual(len(config), len(container.mounts)) |
| for c in config: |
| self.assertTrue(container.has_mount(c)) |
| |
| |
| class _MockContainer(object): |
| """A test mock for the container class. |
| |
| Don't instantiate this directly, use the MockContainer context manager |
| defined below. |
| """ |
| |
| def __init__(self): |
| self.rootfs = tempfile.mkdtemp() |
| self.mounts = [] |
| self.MountConfig = collections.namedtuple( |
| 'MountConfig', ['source', 'destination', 'readonly']) |
| |
| |
| def cleanup(self): |
| """Clean up tmp dirs created by the container.""" |
| # DeployConfigManager uses sudo to create some directories in the |
| # container, so it's necessary to use sudo to clean up. |
| utils.run('sudo rm -rf %s' % self.rootfs) |
| |
| |
| def mount_dir(self, src, dst, ro): |
| """Stub implementation of mount_dir. |
| |
| Records calls for later verification. |
| |
| @param src: Mount source dir. |
| @param dst: Mount destination dir. |
| @param ro: Read-only flag. |
| """ |
| self.mounts.append(self.MountConfig(src, dst, ro)) |
| |
| |
| def has_mount(self, config): |
| """Verifies whether an earlier call was made to mount_dir. |
| |
| @param config: The config object to verify. |
| |
| @return True if an earlier call was made to mount_dir that matches the |
| given mount configuration; False otherwise. |
| """ |
| mount = self.MountConfig(config['source'], |
| config['target'], |
| config['readonly']) |
| return mount in self.mounts |
| |
| |
| @contextmanager |
| def MockContainer(): |
| """Context manager for creating a _MockContainer for testing.""" |
| container = _MockContainer() |
| try: |
| yield container |
| finally: |
| container.cleanup() |
| |
| |
| @contextmanager |
| def ConfigFile(config): |
| """Context manager for creating a config file. |
| |
| The given configs are translated into json and pushed into a temporary file |
| that the DeployConfigManager can read. |
| |
| @param config: A list of config objects. Each config object is a dictionary |
| which conforms to the format described in config.py. |
| """ |
| with tempfile.NamedTemporaryFile() as tmp: |
| json.dump(config, tmp) |
| tmp.flush() |
| yield tmp.name |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |