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