1 # Copyright 2016 The Chromium OS Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 import glob 6 import json 7 import logging 8 import os 9 import re 10 import shutil 11 import tempfile 12 13 from autotest_lib.client.bin import test, utils 14 from autotest_lib.client.common_lib import autotemp, error 15 16 CONFIG_JSON_TEMPLATE = ''' 17 { 18 "ociVersion": "1.0.0-rc1", 19 "platform": { 20 "os": "linux", 21 "arch": "all" 22 }, 23 "process": { 24 "terminal": true, 25 "user": { 26 "uid": 10000, 27 "gid": 10000 28 }, 29 "args": [ 30 %s 31 ], 32 "cwd": "/" 33 }, 34 "root": { 35 "path": "rootfs", 36 "readonly": false 37 }, 38 "hostname": "runc", 39 "mounts": [ 40 { 41 "destination": "/proc", 42 "type": "proc", 43 "source": "proc" 44 }, 45 { 46 "destination": "/dev", 47 "type": "tmpfs", 48 "source": "tmpfs", 49 "options": [ 50 "nosuid", 51 "noexec" 52 ] 53 } 54 ], 55 "hooks": {}, 56 "linux": { 57 "namespaces": [ 58 { 59 "type": "cgroup" 60 }, 61 { 62 "type": "pid" 63 }, 64 { 65 "type": "network" 66 }, 67 { 68 "type": "ipc" 69 }, 70 { 71 "type": "user" 72 }, 73 { 74 "type": "uts" 75 }, 76 { 77 "type": "mount" 78 } 79 ], 80 "uidMappings": [ 81 { 82 "hostID": 10000, 83 "containerID": 0, 84 "size": 10 85 } 86 ], 87 "gidMappings": [ 88 { 89 "hostID": 10000, 90 "containerID": 0, 91 "size": 10 92 } 93 ] 94 } 95 } 96 ''' 97 98 class security_RunOci(test.test): 99 version = 1 100 101 preserve_srcdir = True 102 103 def get_test_option(self, handle): 104 """ 105 Gets the test configuration from the json file given in handle. 106 """ 107 data = json.load(handle) 108 return data['run_oci_args'], data['program_argv'], data['expected_result'] 109 110 111 def run_test_in_dir(self, run_oci_args, argv, expected, oci_path): 112 """ 113 Executes the test in the given directory that points to an OCI image. 114 """ 115 ret = 0 116 cmd_output = utils.system_output( 117 '/usr/bin/run_oci %s %s' % (run_oci_args, oci_path), 118 retain_output=True) 119 if cmd_output != expected: 120 ret = 1 121 return ret 122 123 124 def run_test(self, run_oci_args, argv, expected): 125 """ 126 Runs one test from the src directory. Return 0 if the test passes, 127 return 1 on failure. 128 """ 129 td = autotemp.tempdir() 130 os.chown(td.name, 10000, 10000) 131 with open(os.path.join(td.name, 'config.json'), 'w') as config_file: 132 config_file.write(CONFIG_JSON_TEMPLATE % argv) 133 rootfs_path = os.path.join(td.name, 'rootfs') 134 os.mkdir(rootfs_path) 135 os.chown(rootfs_path, 10000, 10000) 136 utils.run(['mount', "--bind", "/", rootfs_path]) 137 ret = self.run_test_in_dir(run_oci_args, argv, expected, td.name) 138 utils.run(['umount', '-f', rootfs_path]) 139 return ret 140 141 142 def run_once(self): 143 """ 144 Runs each of the tests specified in the source directory. 145 This test fails if any subtest fails. Sub tests exercise the run_oci 146 command and check that the correct namespace mappings and mounts are 147 made. If any subtest fails, this test will fail. 148 """ 149 failed = [] 150 ran = 0 151 for p in glob.glob('%s/test-*.json' % self.srcdir): 152 name = os.path.basename(p) 153 logging.info('Running: %s', name) 154 run_oci_args, argv, expected = self.get_test_option(file(p)) 155 if self.run_test(run_oci_args, argv, expected): 156 failed.append(name) 157 ran += 1 158 if ran == 0: 159 failed.append('No tests found to run from %s!' % (self.srcdir)) 160 if failed: 161 logging.error('Failed: %s', failed) 162 raise error.TestFail('Failed: %s' % failed) 163