普通文本  |  173行  |  5.68 KB

#!/usr/bin/env python
# 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()