• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
6import collections
7import json
8import os
9import tempfile
10import unittest
11from contextlib import contextmanager
12
13import common
14from autotest_lib.client.bin import utils
15from autotest_lib.site_utils.lxc import config as lxc_config
16from autotest_lib.site_utils.lxc import utils as lxc_utils
17
18class 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
98class _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
146def 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
156def 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
171if __name__ == '__main__':
172    unittest.main()
173