Browse code

Fix ansible-test unit test execution. (#45772)

* Fix ansible-test units requirements install.
* Run unit tests as unprivileged user under Docker.

(cherry picked from commit 379a7f4f5a0491964c7896834f3f326412888585)

Matt Clay authored on 2018/09/19 00:37:14
Showing 5 changed files
... ...
@@ -237,6 +237,13 @@ class UnitsConfig(TestConfig):
237 237
 
238 238
         self.collect_only = args.collect_only  # type: bool
239 239
 
240
+        self.requirements_mode = args.requirements_mode if 'requirements_mode' in args else ''
241
+
242
+        if self.requirements_mode == 'only':
243
+            self.requirements = True
244
+        elif self.requirements_mode == 'skip':
245
+            self.requirements = False
246
+
240 247
 
241 248
 class CoverageConfig(EnvironmentConfig):
242 249
     """Configuration for the coverage command."""
... ...
@@ -275,6 +275,29 @@ def delegate_docker(args, exclude, require, integration_targets):
275 275
             if isinstance(args, UnitsConfig) and not args.python:
276 276
                 cmd += ['--python', 'default']
277 277
 
278
+            # run unit tests unprivileged to prevent stray writes to the source tree
279
+            if isinstance(args, UnitsConfig):
280
+                writable_dirs = [
281
+                    '/root/ansible/lib/ansible.egg-info',
282
+                    '/root/ansible/.pytest_cache',
283
+                ]
284
+
285
+                docker_exec(args, test_id, ['mkdir', '-p'] + writable_dirs)
286
+                docker_exec(args, test_id, ['chmod', '777'] + writable_dirs)
287
+
288
+                docker_exec(args, test_id, ['find', '/root/ansible/test/results/', '-type', 'd', '-exec', 'chmod', '777', '{}', '+'])
289
+
290
+                docker_exec(args, test_id, ['chmod', '755', '/root'])
291
+                docker_exec(args, test_id, ['chmod', '644', '/root/ansible/%s' % args.metadata_path])
292
+
293
+                docker_exec(args, test_id, ['useradd', 'pytest', '--create-home'])
294
+
295
+                docker_exec(args, test_id, cmd + ['--requirements-mode', 'only'], options=cmd_options)
296
+
297
+                cmd += ['--requirements-mode', 'skip']
298
+
299
+                cmd_options += ['--user', 'pytest']
300
+
278 301
             try:
279 302
                 docker_exec(args, test_id, cmd, options=cmd_options)
280 303
             finally:
... ...
@@ -12,6 +12,7 @@ import time
12 12
 import textwrap
13 13
 import functools
14 14
 import pipes
15
+import sys
15 16
 import hashlib
16 17
 
17 18
 import lib.pytar
... ...
@@ -49,6 +50,8 @@ from lib.util import (
49 49
     raw_command,
50 50
     get_coverage_path,
51 51
     get_available_port,
52
+    generate_pip_command,
53
+    find_python,
52 54
 )
53 55
 
54 56
 from lib.docker_util import (
... ...
@@ -148,9 +151,10 @@ def create_shell_command(command):
148 148
     return cmd
149 149
 
150 150
 
151
-def install_command_requirements(args):
151
+def install_command_requirements(args, python_version=None):
152 152
     """
153 153
     :type args: EnvironmentConfig
154
+    :type python_version: str | None
154 155
     """
155 156
     generate_egg_info(args)
156 157
 
... ...
@@ -168,7 +172,10 @@ def install_command_requirements(args):
168 168
         if args.junit:
169 169
             packages.append('junit-xml')
170 170
 
171
-    pip = args.pip_command
171
+    if not python_version:
172
+        python_version = args.python_version
173
+
174
+    pip = generate_pip_command(find_python(python_version))
172 175
 
173 176
     commands = [generate_pip_install(pip, args.command, packages=packages)]
174 177
 
... ...
@@ -1133,8 +1140,6 @@ def command_units(args):
1133 1133
     if args.delegate:
1134 1134
         raise Delegate(require=changes)
1135 1135
 
1136
-    install_command_requirements(args)
1137
-
1138 1136
     version_commands = []
1139 1137
 
1140 1138
     for version in SUPPORTED_PYTHON_VERSIONS:
... ...
@@ -1142,6 +1147,9 @@ def command_units(args):
1142 1142
         if args.python and version != args.python_version:
1143 1143
             continue
1144 1144
 
1145
+        if args.requirements_mode != 'skip':
1146
+            install_command_requirements(args, version)
1147
+
1145 1148
         env = ansible_environment(args)
1146 1149
 
1147 1150
         cmd = [
... ...
@@ -1167,6 +1175,9 @@ def command_units(args):
1167 1167
 
1168 1168
         version_commands.append((version, cmd, env))
1169 1169
 
1170
+    if args.requirements_mode == 'only':
1171
+        sys.exit()
1172
+
1170 1173
     for version, command, env in version_commands:
1171 1174
         display.info('Unit test with Python %s' % version)
1172 1175
 
... ...
@@ -335,6 +335,10 @@ def parse_args():
335 335
                        action='store_true',
336 336
                        help='collect tests but do not execute them')
337 337
 
338
+    units.add_argument('--requirements-mode',
339
+                       choices=('only', 'skip'),
340
+                       help=argparse.SUPPRESS)
341
+
338 342
     add_extra_docker_options(units, integration=False)
339 343
 
340 344
     sanity = subparsers.add_parser('sanity',
... ...
@@ -21,6 +21,7 @@ passenv =
21 21
 
22 22
 [pytest]
23 23
 xfail_strict = true
24
+cache_dir = .pytest_cache
24 25
 
25 26
 [flake8]
26 27
 # These are things that the devs don't agree make the code more readable