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
5import glob
6import json
7import logging
8import os
9import re
10import shutil
11import tempfile
12
13from autotest_lib.client.bin import test, utils
14from autotest_lib.client.common_lib import autotemp, error
15
16CONFIG_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
98class 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