toil 7.0.0__py3-none-any.whl → 8.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- toil/__init__.py +121 -83
- toil/batchSystems/__init__.py +1 -0
- toil/batchSystems/abstractBatchSystem.py +137 -77
- toil/batchSystems/abstractGridEngineBatchSystem.py +211 -101
- toil/batchSystems/awsBatch.py +237 -128
- toil/batchSystems/cleanup_support.py +22 -16
- toil/batchSystems/contained_executor.py +30 -26
- toil/batchSystems/gridengine.py +85 -49
- toil/batchSystems/htcondor.py +164 -87
- toil/batchSystems/kubernetes.py +622 -386
- toil/batchSystems/local_support.py +17 -12
- toil/batchSystems/lsf.py +132 -79
- toil/batchSystems/lsfHelper.py +13 -11
- toil/batchSystems/mesos/__init__.py +41 -29
- toil/batchSystems/mesos/batchSystem.py +288 -149
- toil/batchSystems/mesos/executor.py +77 -49
- toil/batchSystems/mesos/test/__init__.py +31 -23
- toil/batchSystems/options.py +38 -29
- toil/batchSystems/registry.py +53 -19
- toil/batchSystems/singleMachine.py +293 -123
- toil/batchSystems/slurm.py +489 -137
- toil/batchSystems/torque.py +46 -32
- toil/bus.py +141 -73
- toil/common.py +630 -359
- toil/cwl/__init__.py +1 -1
- toil/cwl/cwltoil.py +1114 -532
- toil/cwl/utils.py +17 -22
- toil/deferred.py +62 -41
- toil/exceptions.py +5 -3
- toil/fileStores/__init__.py +5 -5
- toil/fileStores/abstractFileStore.py +88 -57
- toil/fileStores/cachingFileStore.py +711 -247
- toil/fileStores/nonCachingFileStore.py +113 -75
- toil/job.py +988 -315
- toil/jobStores/abstractJobStore.py +387 -243
- toil/jobStores/aws/jobStore.py +727 -403
- toil/jobStores/aws/utils.py +161 -109
- toil/jobStores/conftest.py +1 -0
- toil/jobStores/fileJobStore.py +289 -151
- toil/jobStores/googleJobStore.py +137 -70
- toil/jobStores/utils.py +36 -15
- toil/leader.py +614 -269
- toil/lib/accelerators.py +115 -18
- toil/lib/aws/__init__.py +55 -28
- toil/lib/aws/ami.py +122 -87
- toil/lib/aws/iam.py +284 -108
- toil/lib/aws/s3.py +31 -0
- toil/lib/aws/session.py +193 -58
- toil/lib/aws/utils.py +238 -218
- toil/lib/bioio.py +13 -5
- toil/lib/compatibility.py +11 -6
- toil/lib/conversions.py +83 -49
- toil/lib/docker.py +131 -103
- toil/lib/ec2.py +322 -209
- toil/lib/ec2nodes.py +174 -106
- toil/lib/encryption/_dummy.py +5 -3
- toil/lib/encryption/_nacl.py +10 -6
- toil/lib/encryption/conftest.py +1 -0
- toil/lib/exceptions.py +26 -7
- toil/lib/expando.py +4 -2
- toil/lib/ftp_utils.py +217 -0
- toil/lib/generatedEC2Lists.py +127 -19
- toil/lib/humanize.py +6 -2
- toil/lib/integration.py +341 -0
- toil/lib/io.py +99 -11
- toil/lib/iterables.py +4 -2
- toil/lib/memoize.py +12 -8
- toil/lib/misc.py +65 -18
- toil/lib/objects.py +2 -2
- toil/lib/resources.py +19 -7
- toil/lib/retry.py +115 -77
- toil/lib/threading.py +282 -80
- toil/lib/throttle.py +15 -14
- toil/options/common.py +834 -401
- toil/options/cwl.py +175 -90
- toil/options/runner.py +50 -0
- toil/options/wdl.py +70 -19
- toil/provisioners/__init__.py +111 -46
- toil/provisioners/abstractProvisioner.py +322 -157
- toil/provisioners/aws/__init__.py +62 -30
- toil/provisioners/aws/awsProvisioner.py +980 -627
- toil/provisioners/clusterScaler.py +541 -279
- toil/provisioners/gceProvisioner.py +282 -179
- toil/provisioners/node.py +147 -79
- toil/realtimeLogger.py +34 -22
- toil/resource.py +137 -75
- toil/server/app.py +127 -61
- toil/server/celery_app.py +3 -1
- toil/server/cli/wes_cwl_runner.py +82 -53
- toil/server/utils.py +54 -28
- toil/server/wes/abstract_backend.py +64 -26
- toil/server/wes/amazon_wes_utils.py +21 -15
- toil/server/wes/tasks.py +121 -63
- toil/server/wes/toil_backend.py +142 -107
- toil/server/wsgi_app.py +4 -3
- toil/serviceManager.py +58 -22
- toil/statsAndLogging.py +148 -64
- toil/test/__init__.py +263 -179
- toil/test/batchSystems/batchSystemTest.py +438 -195
- toil/test/batchSystems/batch_system_plugin_test.py +18 -7
- toil/test/batchSystems/test_gridengine.py +173 -0
- toil/test/batchSystems/test_lsf_helper.py +67 -58
- toil/test/batchSystems/test_slurm.py +93 -47
- toil/test/cactus/test_cactus_integration.py +20 -22
- toil/test/cwl/cwlTest.py +271 -71
- toil/test/cwl/measure_default_memory.cwl +12 -0
- toil/test/cwl/not_run_required_input.cwl +29 -0
- toil/test/cwl/scatter_duplicate_outputs.cwl +40 -0
- toil/test/docs/scriptsTest.py +60 -34
- toil/test/jobStores/jobStoreTest.py +412 -235
- toil/test/lib/aws/test_iam.py +116 -48
- toil/test/lib/aws/test_s3.py +16 -9
- toil/test/lib/aws/test_utils.py +5 -6
- toil/test/lib/dockerTest.py +118 -141
- toil/test/lib/test_conversions.py +113 -115
- toil/test/lib/test_ec2.py +57 -49
- toil/test/lib/test_integration.py +104 -0
- toil/test/lib/test_misc.py +12 -5
- toil/test/mesos/MesosDataStructuresTest.py +23 -10
- toil/test/mesos/helloWorld.py +7 -6
- toil/test/mesos/stress.py +25 -20
- toil/test/options/options.py +7 -2
- toil/test/provisioners/aws/awsProvisionerTest.py +293 -140
- toil/test/provisioners/clusterScalerTest.py +440 -250
- toil/test/provisioners/clusterTest.py +81 -42
- toil/test/provisioners/gceProvisionerTest.py +174 -100
- toil/test/provisioners/provisionerTest.py +25 -13
- toil/test/provisioners/restartScript.py +5 -4
- toil/test/server/serverTest.py +188 -141
- toil/test/sort/restart_sort.py +137 -68
- toil/test/sort/sort.py +134 -66
- toil/test/sort/sortTest.py +91 -49
- toil/test/src/autoDeploymentTest.py +140 -100
- toil/test/src/busTest.py +20 -18
- toil/test/src/checkpointTest.py +8 -2
- toil/test/src/deferredFunctionTest.py +49 -35
- toil/test/src/dockerCheckTest.py +33 -26
- toil/test/src/environmentTest.py +20 -10
- toil/test/src/fileStoreTest.py +538 -271
- toil/test/src/helloWorldTest.py +7 -4
- toil/test/src/importExportFileTest.py +61 -31
- toil/test/src/jobDescriptionTest.py +32 -17
- toil/test/src/jobEncapsulationTest.py +2 -0
- toil/test/src/jobFileStoreTest.py +74 -50
- toil/test/src/jobServiceTest.py +187 -73
- toil/test/src/jobTest.py +120 -70
- toil/test/src/miscTests.py +19 -18
- toil/test/src/promisedRequirementTest.py +82 -36
- toil/test/src/promisesTest.py +7 -6
- toil/test/src/realtimeLoggerTest.py +6 -6
- toil/test/src/regularLogTest.py +71 -37
- toil/test/src/resourceTest.py +80 -49
- toil/test/src/restartDAGTest.py +36 -22
- toil/test/src/resumabilityTest.py +9 -2
- toil/test/src/retainTempDirTest.py +45 -14
- toil/test/src/systemTest.py +12 -8
- toil/test/src/threadingTest.py +44 -25
- toil/test/src/toilContextManagerTest.py +10 -7
- toil/test/src/userDefinedJobArgTypeTest.py +8 -5
- toil/test/src/workerTest.py +33 -16
- toil/test/utils/toilDebugTest.py +70 -58
- toil/test/utils/toilKillTest.py +4 -5
- toil/test/utils/utilsTest.py +239 -102
- toil/test/wdl/wdltoil_test.py +789 -148
- toil/test/wdl/wdltoil_test_kubernetes.py +37 -23
- toil/toilState.py +52 -26
- toil/utils/toilConfig.py +13 -4
- toil/utils/toilDebugFile.py +44 -27
- toil/utils/toilDebugJob.py +85 -25
- toil/utils/toilDestroyCluster.py +11 -6
- toil/utils/toilKill.py +8 -3
- toil/utils/toilLaunchCluster.py +251 -145
- toil/utils/toilMain.py +37 -16
- toil/utils/toilRsyncCluster.py +27 -14
- toil/utils/toilSshCluster.py +45 -22
- toil/utils/toilStats.py +75 -36
- toil/utils/toilStatus.py +226 -119
- toil/utils/toilUpdateEC2Instances.py +3 -1
- toil/version.py +11 -11
- toil/wdl/utils.py +5 -5
- toil/wdl/wdltoil.py +3513 -1052
- toil/worker.py +269 -128
- toil-8.0.0.dist-info/METADATA +173 -0
- toil-8.0.0.dist-info/RECORD +253 -0
- {toil-7.0.0.dist-info → toil-8.0.0.dist-info}/WHEEL +1 -1
- toil-7.0.0.dist-info/METADATA +0 -158
- toil-7.0.0.dist-info/RECORD +0 -244
- {toil-7.0.0.dist-info → toil-8.0.0.dist-info}/LICENSE +0 -0
- {toil-7.0.0.dist-info → toil-8.0.0.dist-info}/entry_points.txt +0 -0
- {toil-7.0.0.dist-info → toil-8.0.0.dist-info}/top_level.txt +0 -0
toil/test/__init__.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Base testing class for Toil."""
|
|
2
|
+
|
|
2
3
|
# Copyright (C) 2015-2021 Regents of the University of California
|
|
3
4
|
#
|
|
4
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -20,42 +21,28 @@ import re
|
|
|
20
21
|
import shutil
|
|
21
22
|
import signal
|
|
22
23
|
import subprocess
|
|
23
|
-
import sys
|
|
24
24
|
import threading
|
|
25
25
|
import time
|
|
26
26
|
import unittest
|
|
27
27
|
import uuid
|
|
28
|
+
import zoneinfo
|
|
28
29
|
from abc import ABCMeta, abstractmethod
|
|
30
|
+
from collections.abc import Generator
|
|
29
31
|
from contextlib import contextmanager
|
|
30
32
|
from inspect import getsource
|
|
31
33
|
from shutil import which
|
|
32
34
|
from tempfile import mkstemp
|
|
33
35
|
from textwrap import dedent
|
|
34
|
-
from typing import
|
|
35
|
-
Callable,
|
|
36
|
-
Dict,
|
|
37
|
-
Generator,
|
|
38
|
-
List,
|
|
39
|
-
Literal,
|
|
40
|
-
Optional,
|
|
41
|
-
Tuple,
|
|
42
|
-
Type,
|
|
43
|
-
TypeVar,
|
|
44
|
-
Union,
|
|
45
|
-
cast)
|
|
36
|
+
from typing import Any, Callable, Literal, Optional, TypeVar, Union, cast
|
|
46
37
|
from unittest.util import strclass
|
|
47
38
|
from urllib.error import HTTPError, URLError
|
|
48
39
|
from urllib.request import urlopen
|
|
49
40
|
|
|
50
|
-
|
|
51
|
-
if sys.version_info >= (3, 9):
|
|
52
|
-
import zoneinfo
|
|
53
|
-
else:
|
|
54
|
-
from backports import zoneinfo
|
|
55
|
-
|
|
56
41
|
from toil import ApplianceImageNotFound, applianceSelf, toilPackageDirPath
|
|
57
|
-
from toil.lib.accelerators import (
|
|
58
|
-
|
|
42
|
+
from toil.lib.accelerators import (
|
|
43
|
+
have_working_nvidia_docker_runtime,
|
|
44
|
+
have_working_nvidia_smi,
|
|
45
|
+
)
|
|
59
46
|
from toil.lib.io import mkdtemp
|
|
60
47
|
from toil.lib.iterables import concat
|
|
61
48
|
from toil.lib.memoize import memoize
|
|
@@ -82,20 +69,24 @@ class ToilTest(unittest.TestCase):
|
|
|
82
69
|
"""
|
|
83
70
|
|
|
84
71
|
_tempBaseDir: Optional[str] = None
|
|
85
|
-
_tempDirs:
|
|
72
|
+
_tempDirs: list[str] = []
|
|
86
73
|
|
|
87
74
|
def setup_method(self, method: Any) -> None:
|
|
88
75
|
western = zoneinfo.ZoneInfo("America/Los_Angeles")
|
|
89
76
|
california_time = datetime.datetime.now(tz=western)
|
|
90
77
|
timestamp = california_time.strftime("%b %d %Y %H:%M:%S:%f %Z")
|
|
91
|
-
print(
|
|
78
|
+
print(
|
|
79
|
+
f"\n\n[TEST] {strclass(self.__class__)}:{self._testMethodName} ({timestamp})\n\n"
|
|
80
|
+
)
|
|
92
81
|
|
|
93
82
|
@classmethod
|
|
94
83
|
def setUpClass(cls) -> None:
|
|
95
84
|
super().setUpClass()
|
|
96
|
-
tempBaseDir = os.environ.get(
|
|
85
|
+
tempBaseDir = os.environ.get("TOIL_TEST_TEMP", None)
|
|
97
86
|
if tempBaseDir is not None and not os.path.isabs(tempBaseDir):
|
|
98
|
-
tempBaseDir = os.path.abspath(
|
|
87
|
+
tempBaseDir = os.path.abspath(
|
|
88
|
+
os.path.join(cls._projectRootPath(), tempBaseDir)
|
|
89
|
+
)
|
|
99
90
|
os.makedirs(tempBaseDir, exist_ok=True)
|
|
100
91
|
cls._tempBaseDir = tempBaseDir
|
|
101
92
|
|
|
@@ -127,7 +118,8 @@ class ToilTest(unittest.TestCase):
|
|
|
127
118
|
the instance is located
|
|
128
119
|
"""
|
|
129
120
|
from toil.lib.aws import running_on_ec2
|
|
130
|
-
|
|
121
|
+
|
|
122
|
+
return cls._region() if running_on_ec2() else "us-west-2"
|
|
131
123
|
|
|
132
124
|
@classmethod
|
|
133
125
|
def _availabilityZone(cls) -> str:
|
|
@@ -151,13 +143,15 @@ class ToilTest(unittest.TestCase):
|
|
|
151
143
|
The region will not change over the life of the instance so the result
|
|
152
144
|
is memoized to avoid unnecessary work.
|
|
153
145
|
"""
|
|
154
|
-
region = re.match(
|
|
146
|
+
region = re.match(
|
|
147
|
+
r"^([a-z]{2}-[a-z]+-[1-9][0-9]*)([a-z])$", cls._availabilityZone()
|
|
148
|
+
)
|
|
155
149
|
assert region
|
|
156
150
|
return region.group(1)
|
|
157
151
|
|
|
158
152
|
@classmethod
|
|
159
153
|
def _getUtilScriptPath(cls, script_name: str) -> str:
|
|
160
|
-
return os.path.join(toilPackageDirPath(),
|
|
154
|
+
return os.path.join(toilPackageDirPath(), "utils", script_name + ".py")
|
|
161
155
|
|
|
162
156
|
@classmethod
|
|
163
157
|
def _projectRootPath(cls) -> str:
|
|
@@ -169,12 +163,12 @@ class ToilTest(unittest.TestCase):
|
|
|
169
163
|
mode, since it assumes the existence of a src subdirectory which, in a regular install
|
|
170
164
|
wouldn't exist. Then again, in that mode project root has no meaning anyways.
|
|
171
165
|
"""
|
|
172
|
-
assert re.search(r
|
|
166
|
+
assert re.search(r"__init__\.pyc?$", __file__)
|
|
173
167
|
projectRootPath = os.path.dirname(os.path.abspath(__file__))
|
|
174
|
-
packageComponents = __name__.split(
|
|
175
|
-
expectedSuffix = os.path.join(
|
|
168
|
+
packageComponents = __name__.split(".")
|
|
169
|
+
expectedSuffix = os.path.join("src", *packageComponents)
|
|
176
170
|
assert projectRootPath.endswith(expectedSuffix)
|
|
177
|
-
projectRootPath = projectRootPath[
|
|
171
|
+
projectRootPath = projectRootPath[: -len(expectedSuffix)]
|
|
178
172
|
return projectRootPath
|
|
179
173
|
|
|
180
174
|
def _createTempDir(self, purpose: Optional[str] = None) -> str:
|
|
@@ -187,7 +181,7 @@ class ToilTest(unittest.TestCase):
|
|
|
187
181
|
classname = classname[len("toil.test.") :]
|
|
188
182
|
prefix = ["toil", "test", classname]
|
|
189
183
|
prefix.extend([_f for _f in names if _f])
|
|
190
|
-
prefix.append(
|
|
184
|
+
prefix.append("")
|
|
191
185
|
temp_dir_path = os.path.realpath(
|
|
192
186
|
mkdtemp(dir=cls._tempBaseDir, prefix="-".join(prefix))
|
|
193
187
|
)
|
|
@@ -277,9 +271,9 @@ class ToilTest(unittest.TestCase):
|
|
|
277
271
|
capture = kwargs.pop("capture", False)
|
|
278
272
|
_input = kwargs.pop("input", None)
|
|
279
273
|
if capture:
|
|
280
|
-
kwargs[
|
|
274
|
+
kwargs["stdout"] = subprocess.PIPE
|
|
281
275
|
if _input is not None:
|
|
282
|
-
kwargs[
|
|
276
|
+
kwargs["stdin"] = subprocess.PIPE
|
|
283
277
|
popen = subprocess.Popen(args, universal_newlines=True, **kwargs)
|
|
284
278
|
stdout, stderr = popen.communicate(input=_input)
|
|
285
279
|
assert stderr is None
|
|
@@ -295,7 +289,8 @@ class ToilTest(unittest.TestCase):
|
|
|
295
289
|
This is a naughty but incredibly useful trick that lets you embed user scripts
|
|
296
290
|
as nested functions and expose them to the syntax checker of your IDE.
|
|
297
291
|
"""
|
|
298
|
-
return dedent(
|
|
292
|
+
return dedent("\n".join(getsource(callable_).split("\n")[1:]))
|
|
293
|
+
|
|
299
294
|
|
|
300
295
|
MT = TypeVar("MT", bound=Callable[..., Any])
|
|
301
296
|
|
|
@@ -306,6 +301,7 @@ except ImportError:
|
|
|
306
301
|
# noinspection PyUnusedLocal
|
|
307
302
|
def _mark_test(name: str, test_item: MT) -> MT:
|
|
308
303
|
return test_item
|
|
304
|
+
|
|
309
305
|
else:
|
|
310
306
|
|
|
311
307
|
def _mark_test(name: str, test_item: MT) -> MT:
|
|
@@ -319,7 +315,7 @@ def get_temp_file(suffix: str = "", rootDir: Optional[str] = None) -> str:
|
|
|
319
315
|
os.close(handle)
|
|
320
316
|
return tmp_file
|
|
321
317
|
else:
|
|
322
|
-
alphanumerics =
|
|
318
|
+
alphanumerics = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
|
323
319
|
tmp_file = os.path.join(
|
|
324
320
|
rootDir,
|
|
325
321
|
f"tmp_{''.join([random.choice(alphanumerics) for _ in range(0, 10)])}{suffix}",
|
|
@@ -328,18 +324,24 @@ def get_temp_file(suffix: str = "", rootDir: Optional[str] = None) -> str:
|
|
|
328
324
|
os.chmod(tmp_file, 0o777) # Ensure everyone has access to the file.
|
|
329
325
|
return tmp_file
|
|
330
326
|
|
|
327
|
+
|
|
331
328
|
def needs_env_var(var_name: str, comment: Optional[str] = None) -> Callable[[MT], MT]:
|
|
332
329
|
"""
|
|
333
330
|
Use as a decorator before test classes or methods to run only if the given
|
|
334
331
|
environment variable is set.
|
|
335
332
|
Can include a comment saying what the variable should be set to.
|
|
336
333
|
"""
|
|
334
|
+
|
|
337
335
|
def decorator(test_item: MT) -> MT:
|
|
338
336
|
if not os.getenv(var_name):
|
|
339
|
-
return unittest.skip(
|
|
337
|
+
return unittest.skip(
|
|
338
|
+
f"Set {var_name}{' to ' + comment if comment else ''} to include this test."
|
|
339
|
+
)(test_item)
|
|
340
340
|
return test_item
|
|
341
|
+
|
|
341
342
|
return decorator
|
|
342
343
|
|
|
344
|
+
|
|
343
345
|
def needs_rsync3(test_item: MT) -> MT:
|
|
344
346
|
"""
|
|
345
347
|
Decorate classes or methods that depend on any features from rsync version 3.0.0+.
|
|
@@ -347,32 +349,34 @@ def needs_rsync3(test_item: MT) -> MT:
|
|
|
347
349
|
Necessary because :meth:`utilsTest.testAWSProvisionerUtils` uses option `--protect-args`
|
|
348
350
|
which is only available in rsync 3
|
|
349
351
|
"""
|
|
350
|
-
test_item = _mark_test(
|
|
352
|
+
test_item = _mark_test("rsync", test_item)
|
|
351
353
|
try:
|
|
352
|
-
versionInfo = subprocess.check_output([
|
|
354
|
+
versionInfo = subprocess.check_output(["rsync", "--version"]).decode("utf-8")
|
|
353
355
|
# output looks like: 'rsync version 2.6.9 ...'
|
|
354
356
|
if int(versionInfo.split()[2].split(".")[0]) < 3:
|
|
355
357
|
return unittest.skip("This test depends on rsync version 3.0.0+.")(
|
|
356
358
|
test_item
|
|
357
359
|
)
|
|
358
360
|
except subprocess.CalledProcessError:
|
|
359
|
-
return unittest.skip(
|
|
361
|
+
return unittest.skip("rsync needs to be installed to run this test.")(test_item)
|
|
360
362
|
return test_item
|
|
361
363
|
|
|
362
364
|
|
|
363
365
|
def needs_online(test_item: MT) -> MT:
|
|
364
366
|
"""Use as a decorator before test classes or methods to run only if we are meant to talk to the Internet."""
|
|
365
|
-
test_item = _mark_test(
|
|
366
|
-
if os.getenv(
|
|
367
|
-
return unittest.skip(
|
|
367
|
+
test_item = _mark_test("online", test_item)
|
|
368
|
+
if os.getenv("TOIL_SKIP_ONLINE", "").lower() == "true":
|
|
369
|
+
return unittest.skip("Skipping online test.")(test_item)
|
|
368
370
|
return test_item
|
|
369
371
|
|
|
372
|
+
|
|
370
373
|
def needs_aws_s3(test_item: MT) -> MT:
|
|
371
374
|
"""Use as a decorator before test classes or methods to run only if AWS S3 is usable."""
|
|
372
375
|
# TODO: we just check for generic access to the AWS account
|
|
373
|
-
test_item = _mark_test(
|
|
376
|
+
test_item = _mark_test("aws-s3", needs_online(test_item))
|
|
374
377
|
try:
|
|
375
378
|
from boto3 import Session
|
|
379
|
+
|
|
376
380
|
session = Session()
|
|
377
381
|
boto3_credentials = session.get_credentials()
|
|
378
382
|
except ImportError:
|
|
@@ -380,18 +384,25 @@ def needs_aws_s3(test_item: MT) -> MT:
|
|
|
380
384
|
test_item
|
|
381
385
|
)
|
|
382
386
|
from toil.lib.aws import running_on_ec2
|
|
383
|
-
|
|
384
|
-
|
|
387
|
+
|
|
388
|
+
if not (
|
|
389
|
+
boto3_credentials
|
|
390
|
+
or os.path.exists(os.path.expanduser("~/.aws/credentials"))
|
|
391
|
+
or running_on_ec2()
|
|
392
|
+
):
|
|
393
|
+
return unittest.skip("Configure AWS credentials to include this test.")(
|
|
394
|
+
test_item
|
|
395
|
+
)
|
|
385
396
|
return test_item
|
|
386
397
|
|
|
387
398
|
|
|
388
399
|
def needs_aws_ec2(test_item: MT) -> MT:
|
|
389
400
|
"""Use as a decorator before test classes or methods to run only if AWS EC2 is usable."""
|
|
390
401
|
# Assume we need S3 as well as EC2
|
|
391
|
-
test_item = _mark_test(
|
|
402
|
+
test_item = _mark_test("aws-ec2", needs_aws_s3(test_item))
|
|
392
403
|
# In addition to S3 we also need an SSH key to deploy with.
|
|
393
404
|
# TODO: We assume that if this is set we have EC2 access.
|
|
394
|
-
test_item = needs_env_var(
|
|
405
|
+
test_item = needs_env_var("TOIL_AWS_KEYNAME", "an AWS-stored SSH key")(test_item)
|
|
395
406
|
return test_item
|
|
396
407
|
|
|
397
408
|
|
|
@@ -401,16 +412,23 @@ def needs_aws_batch(test_item: MT) -> MT:
|
|
|
401
412
|
is usable.
|
|
402
413
|
"""
|
|
403
414
|
# Assume we need S3 as well as Batch
|
|
404
|
-
test_item = _mark_test(
|
|
415
|
+
test_item = _mark_test("aws-batch", needs_aws_s3(test_item))
|
|
405
416
|
# Assume we have Batch if the user has set these variables.
|
|
406
|
-
test_item = needs_env_var(
|
|
407
|
-
|
|
417
|
+
test_item = needs_env_var("TOIL_AWS_BATCH_QUEUE", "an AWS Batch queue name or ARN")(
|
|
418
|
+
test_item
|
|
419
|
+
)
|
|
420
|
+
test_item = needs_env_var(
|
|
421
|
+
"TOIL_AWS_BATCH_JOB_ROLE_ARN", "an IAM role ARN that grants S3 and SDB access"
|
|
422
|
+
)(test_item)
|
|
408
423
|
try:
|
|
409
424
|
from toil.lib.aws import get_current_aws_region
|
|
425
|
+
|
|
410
426
|
if get_current_aws_region() is None:
|
|
411
427
|
# We don't know a region so we need one set.
|
|
412
428
|
# TODO: It always won't be set if we get here.
|
|
413
|
-
test_item = needs_env_var(
|
|
429
|
+
test_item = needs_env_var(
|
|
430
|
+
"TOIL_AWS_REGION", "an AWS region to use with AWS batch"
|
|
431
|
+
)(test_item)
|
|
414
432
|
|
|
415
433
|
except ImportError:
|
|
416
434
|
return unittest.skip("Install Toil with the 'aws' extra to include this test.")(
|
|
@@ -418,67 +436,79 @@ def needs_aws_batch(test_item: MT) -> MT:
|
|
|
418
436
|
)
|
|
419
437
|
return test_item
|
|
420
438
|
|
|
439
|
+
|
|
421
440
|
def needs_google_storage(test_item: MT) -> MT:
|
|
422
441
|
"""
|
|
423
442
|
Use as a decorator before test classes or methods to run only if Google
|
|
424
443
|
Cloud is installed and we ought to be able to access public Google Storage
|
|
425
444
|
URIs.
|
|
426
445
|
"""
|
|
427
|
-
test_item = _mark_test(
|
|
446
|
+
test_item = _mark_test("google-storage", needs_online(test_item))
|
|
428
447
|
try:
|
|
429
448
|
from google.cloud import storage # noqa
|
|
430
449
|
except ImportError:
|
|
431
|
-
return unittest.skip(
|
|
450
|
+
return unittest.skip(
|
|
451
|
+
"Install Toil with the 'google' extra to include this test."
|
|
452
|
+
)(test_item)
|
|
432
453
|
|
|
433
454
|
return test_item
|
|
434
455
|
|
|
456
|
+
|
|
435
457
|
def needs_google_project(test_item: MT) -> MT:
|
|
436
458
|
"""
|
|
437
459
|
Use as a decorator before test classes or methods to run only if we have a Google Cloud project set.
|
|
438
460
|
"""
|
|
439
|
-
test_item = _mark_test(
|
|
440
|
-
test_item = needs_env_var(
|
|
461
|
+
test_item = _mark_test("google-project", needs_online(test_item))
|
|
462
|
+
test_item = needs_env_var("TOIL_GOOGLE_PROJECTID", "a Google project ID")(test_item)
|
|
441
463
|
return test_item
|
|
442
464
|
|
|
443
465
|
|
|
444
466
|
def needs_gridengine(test_item: MT) -> MT:
|
|
445
467
|
"""Use as a decorator before test classes or methods to run only if GridEngine is installed."""
|
|
446
|
-
test_item = _mark_test(
|
|
447
|
-
if which(
|
|
468
|
+
test_item = _mark_test("gridengine", test_item)
|
|
469
|
+
if which("qhost"):
|
|
448
470
|
return test_item
|
|
449
471
|
return unittest.skip("Install GridEngine to include this test.")(test_item)
|
|
450
472
|
|
|
451
473
|
|
|
452
474
|
def needs_torque(test_item: MT) -> MT:
|
|
453
475
|
"""Use as a decorator before test classes or methods to run only if PBS/Torque is installed."""
|
|
454
|
-
test_item = _mark_test(
|
|
455
|
-
if which(
|
|
476
|
+
test_item = _mark_test("torque", test_item)
|
|
477
|
+
if which("pbsnodes"):
|
|
456
478
|
return test_item
|
|
457
479
|
return unittest.skip("Install PBS/Torque to include this test.")(test_item)
|
|
458
480
|
|
|
481
|
+
|
|
459
482
|
def needs_kubernetes_installed(test_item: MT) -> MT:
|
|
460
483
|
"""Use as a decorator before test classes or methods to run only if Kubernetes is installed."""
|
|
461
|
-
test_item = _mark_test(
|
|
484
|
+
test_item = _mark_test("kubernetes", test_item)
|
|
462
485
|
try:
|
|
463
486
|
import kubernetes
|
|
487
|
+
|
|
464
488
|
str(kubernetes) # to prevent removal of this import
|
|
465
489
|
except ImportError:
|
|
466
|
-
return unittest.skip(
|
|
490
|
+
return unittest.skip(
|
|
491
|
+
"Install Toil with the 'kubernetes' extra to include this test."
|
|
492
|
+
)(test_item)
|
|
467
493
|
return test_item
|
|
468
494
|
|
|
495
|
+
|
|
469
496
|
def needs_kubernetes(test_item: MT) -> MT:
|
|
470
497
|
"""Use as a decorator before test classes or methods to run only if Kubernetes is installed and configured."""
|
|
471
498
|
test_item = needs_kubernetes_installed(needs_online(test_item))
|
|
472
499
|
try:
|
|
473
500
|
import kubernetes
|
|
501
|
+
|
|
474
502
|
try:
|
|
475
503
|
kubernetes.config.load_kube_config()
|
|
476
504
|
except kubernetes.config.ConfigException:
|
|
477
505
|
try:
|
|
478
506
|
kubernetes.config.load_incluster_config()
|
|
479
507
|
except kubernetes.config.ConfigException:
|
|
480
|
-
return unittest.skip(
|
|
481
|
-
|
|
508
|
+
return unittest.skip(
|
|
509
|
+
"Configure Kubernetes (~/.kube/config, $KUBECONFIG, "
|
|
510
|
+
"or current pod) to include this test."
|
|
511
|
+
)(test_item)
|
|
482
512
|
except ImportError:
|
|
483
513
|
# We should already be skipping this test
|
|
484
514
|
pass
|
|
@@ -487,37 +517,50 @@ def needs_kubernetes(test_item: MT) -> MT:
|
|
|
487
517
|
|
|
488
518
|
def needs_mesos(test_item: MT) -> MT:
|
|
489
519
|
"""Use as a decorator before test classes or methods to run only if Mesos is installed."""
|
|
490
|
-
test_item = _mark_test(
|
|
491
|
-
if not (which(
|
|
492
|
-
return unittest.skip(
|
|
520
|
+
test_item = _mark_test("mesos", test_item)
|
|
521
|
+
if not (which("mesos-master") or which("mesos-agent")):
|
|
522
|
+
return unittest.skip(
|
|
523
|
+
"Install Mesos (and Toil with the 'mesos' extra) to include this test."
|
|
524
|
+
)(test_item)
|
|
493
525
|
try:
|
|
494
526
|
import psutil # noqa
|
|
495
527
|
import pymesos # noqa
|
|
496
528
|
except ImportError:
|
|
497
|
-
return unittest.skip(
|
|
529
|
+
return unittest.skip(
|
|
530
|
+
"Install Mesos (and Toil with the 'mesos' extra) to include this test."
|
|
531
|
+
)(test_item)
|
|
498
532
|
return test_item
|
|
499
533
|
|
|
500
534
|
|
|
501
535
|
def needs_slurm(test_item: MT) -> MT:
|
|
502
536
|
"""Use as a decorator before test classes or methods to run only if Slurm is installed."""
|
|
503
|
-
test_item = _mark_test(
|
|
504
|
-
if which(
|
|
537
|
+
test_item = _mark_test("slurm", test_item)
|
|
538
|
+
if which("squeue"):
|
|
505
539
|
return test_item
|
|
506
540
|
return unittest.skip("Install Slurm to include this test.")(test_item)
|
|
507
541
|
|
|
508
542
|
|
|
509
543
|
def needs_htcondor(test_item: MT) -> MT:
|
|
510
544
|
"""Use a decorator before test classes or methods to run only if the HTCondor is installed."""
|
|
511
|
-
test_item = _mark_test(
|
|
545
|
+
test_item = _mark_test("htcondor", test_item)
|
|
512
546
|
try:
|
|
513
547
|
import htcondor
|
|
514
|
-
|
|
548
|
+
|
|
549
|
+
htcondor.Collector(os.getenv("TOIL_HTCONDOR_COLLECTOR")).query(
|
|
550
|
+
constraint="False"
|
|
551
|
+
)
|
|
515
552
|
except ImportError:
|
|
516
|
-
return unittest.skip(
|
|
553
|
+
return unittest.skip(
|
|
554
|
+
"Install the HTCondor Python bindings to include this test."
|
|
555
|
+
)(test_item)
|
|
517
556
|
except OSError:
|
|
518
|
-
return unittest.skip("HTCondor must be running to include this test.")(
|
|
557
|
+
return unittest.skip("HTCondor must be running to include this test.")(
|
|
558
|
+
test_item
|
|
559
|
+
)
|
|
519
560
|
except RuntimeError:
|
|
520
|
-
return unittest.skip(
|
|
561
|
+
return unittest.skip(
|
|
562
|
+
"HTCondor must be installed and configured to include this test."
|
|
563
|
+
)(test_item)
|
|
521
564
|
else:
|
|
522
565
|
return test_item
|
|
523
566
|
|
|
@@ -526,8 +569,8 @@ def needs_lsf(test_item: MT) -> MT:
|
|
|
526
569
|
"""
|
|
527
570
|
Use as a decorator before test classes or methods to only run them if LSF is installed.
|
|
528
571
|
"""
|
|
529
|
-
test_item = _mark_test(
|
|
530
|
-
if which(
|
|
572
|
+
test_item = _mark_test("lsf", test_item)
|
|
573
|
+
if which("bsub"):
|
|
531
574
|
return test_item
|
|
532
575
|
else:
|
|
533
576
|
return unittest.skip("Install LSF to include this test.")(test_item)
|
|
@@ -535,8 +578,8 @@ def needs_lsf(test_item: MT) -> MT:
|
|
|
535
578
|
|
|
536
579
|
def needs_java(test_item: MT) -> MT:
|
|
537
580
|
"""Use as a test decorator to run only if java is installed."""
|
|
538
|
-
test_item = _mark_test(
|
|
539
|
-
if which(
|
|
581
|
+
test_item = _mark_test("java", test_item)
|
|
582
|
+
if which("java"):
|
|
540
583
|
return test_item
|
|
541
584
|
else:
|
|
542
585
|
return unittest.skip("Install java to include this test.")(test_item)
|
|
@@ -547,74 +590,84 @@ def needs_docker(test_item: MT) -> MT:
|
|
|
547
590
|
Use as a decorator before test classes or methods to only run them if
|
|
548
591
|
docker is installed and docker-based tests are enabled.
|
|
549
592
|
"""
|
|
550
|
-
test_item = _mark_test(
|
|
551
|
-
if os.getenv(
|
|
552
|
-
return unittest.skip(
|
|
553
|
-
if which(
|
|
593
|
+
test_item = _mark_test("docker", needs_online(test_item))
|
|
594
|
+
if os.getenv("TOIL_SKIP_DOCKER", "").lower() == "true":
|
|
595
|
+
return unittest.skip("Skipping docker test.")(test_item)
|
|
596
|
+
if which("docker"):
|
|
554
597
|
return test_item
|
|
555
598
|
else:
|
|
556
599
|
return unittest.skip("Install docker to include this test.")(test_item)
|
|
557
600
|
|
|
601
|
+
|
|
558
602
|
def needs_singularity(test_item: MT) -> MT:
|
|
559
603
|
"""
|
|
560
604
|
Use as a decorator before test classes or methods to only run them if
|
|
561
605
|
singularity is installed.
|
|
562
606
|
"""
|
|
563
|
-
test_item = _mark_test(
|
|
564
|
-
if which(
|
|
607
|
+
test_item = _mark_test("singularity", needs_online(test_item))
|
|
608
|
+
if which("singularity"):
|
|
565
609
|
return test_item
|
|
566
610
|
else:
|
|
567
611
|
return unittest.skip("Install singularity to include this test.")(test_item)
|
|
568
612
|
|
|
613
|
+
|
|
569
614
|
def needs_singularity_or_docker(test_item: MT) -> MT:
|
|
570
615
|
"""
|
|
571
616
|
Use as a decorator before test classes or methods to only run them if
|
|
572
617
|
docker is installed and docker-based tests are enabled, or if Singularity
|
|
573
618
|
is installed.
|
|
574
619
|
"""
|
|
575
|
-
|
|
620
|
+
|
|
576
621
|
# TODO: Is there a good way to OR decorators?
|
|
577
|
-
if which(
|
|
622
|
+
if which("singularity"):
|
|
578
623
|
# Singularity is here, say it's a Singularity test
|
|
579
624
|
return needs_singularity(test_item)
|
|
580
625
|
else:
|
|
581
626
|
# Otherwise say it's a Docker test.
|
|
582
627
|
return needs_docker(test_item)
|
|
583
|
-
|
|
628
|
+
|
|
629
|
+
|
|
584
630
|
def needs_local_cuda(test_item: MT) -> MT:
|
|
585
631
|
"""
|
|
586
632
|
Use as a decorator before test classes or methods to only run them if
|
|
587
633
|
a CUDA setup legible to cwltool (i.e. providing userspace nvidia-smi) is present.
|
|
588
634
|
"""
|
|
589
|
-
test_item = _mark_test(
|
|
635
|
+
test_item = _mark_test("local_cuda", test_item)
|
|
590
636
|
if have_working_nvidia_smi():
|
|
591
637
|
return test_item
|
|
592
638
|
else:
|
|
593
|
-
return unittest.skip(
|
|
639
|
+
return unittest.skip(
|
|
640
|
+
"Install nvidia-smi, an nvidia proprietary driver, and a CUDA-capable nvidia GPU to include this test."
|
|
641
|
+
)(test_item)
|
|
642
|
+
|
|
594
643
|
|
|
595
644
|
def needs_docker_cuda(test_item: MT) -> MT:
|
|
596
645
|
"""
|
|
597
646
|
Use as a decorator before test classes or methods to only run them if
|
|
598
647
|
a CUDA setup is available through Docker.
|
|
599
648
|
"""
|
|
600
|
-
test_item = _mark_test(
|
|
649
|
+
test_item = _mark_test("docker_cuda", needs_online(test_item))
|
|
601
650
|
if have_working_nvidia_docker_runtime():
|
|
602
651
|
return test_item
|
|
603
652
|
else:
|
|
604
|
-
return unittest.skip(
|
|
653
|
+
return unittest.skip(
|
|
654
|
+
"Install nvidia-container-runtime on your Docker server and configure an 'nvidia' runtime to include this test."
|
|
655
|
+
)(test_item)
|
|
656
|
+
|
|
605
657
|
|
|
606
658
|
def needs_encryption(test_item: MT) -> MT:
|
|
607
659
|
"""
|
|
608
660
|
Use as a decorator before test classes or methods to only run them if PyNaCl is installed
|
|
609
661
|
and configured.
|
|
610
662
|
"""
|
|
611
|
-
test_item = _mark_test(
|
|
663
|
+
test_item = _mark_test("encryption", test_item)
|
|
612
664
|
try:
|
|
613
665
|
# noinspection PyUnresolvedReferences
|
|
614
666
|
import nacl # noqa
|
|
615
667
|
except ImportError:
|
|
616
668
|
return unittest.skip(
|
|
617
|
-
"Install Toil with the 'encryption' extra to include this test."
|
|
669
|
+
"Install Toil with the 'encryption' extra to include this test."
|
|
670
|
+
)(test_item)
|
|
618
671
|
else:
|
|
619
672
|
return test_item
|
|
620
673
|
|
|
@@ -624,26 +677,31 @@ def needs_cwl(test_item: MT) -> MT:
|
|
|
624
677
|
Use as a decorator before test classes or methods to only run them if CWLTool is installed
|
|
625
678
|
and configured.
|
|
626
679
|
"""
|
|
627
|
-
test_item = _mark_test(
|
|
680
|
+
test_item = _mark_test("cwl", test_item)
|
|
628
681
|
try:
|
|
629
682
|
# noinspection PyUnresolvedReferences
|
|
630
683
|
import cwltool # noqa
|
|
631
684
|
except ImportError:
|
|
632
|
-
return unittest.skip("Install Toil with the 'cwl' extra to include this test.")(
|
|
685
|
+
return unittest.skip("Install Toil with the 'cwl' extra to include this test.")(
|
|
686
|
+
test_item
|
|
687
|
+
)
|
|
633
688
|
else:
|
|
634
689
|
return test_item
|
|
635
690
|
|
|
691
|
+
|
|
636
692
|
def needs_wdl(test_item: MT) -> MT:
|
|
637
693
|
"""
|
|
638
694
|
Use as a decorator before test classes or methods to only run them if miniwdl is installed
|
|
639
695
|
and configured.
|
|
640
696
|
"""
|
|
641
|
-
test_item = _mark_test(
|
|
697
|
+
test_item = _mark_test("wdl", test_item)
|
|
642
698
|
try:
|
|
643
699
|
# noinspection PyUnresolvedReferences
|
|
644
700
|
import WDL # noqa
|
|
645
701
|
except ImportError:
|
|
646
|
-
return unittest.skip("Install Toil with the 'wdl' extra to include this test.")(
|
|
702
|
+
return unittest.skip("Install Toil with the 'wdl' extra to include this test.")(
|
|
703
|
+
test_item
|
|
704
|
+
)
|
|
647
705
|
else:
|
|
648
706
|
return test_item
|
|
649
707
|
|
|
@@ -652,40 +710,48 @@ def needs_server(test_item: MT) -> MT:
|
|
|
652
710
|
"""
|
|
653
711
|
Use as a decorator before test classes or methods to only run them if Connexion is installed.
|
|
654
712
|
"""
|
|
655
|
-
test_item = _mark_test(
|
|
713
|
+
test_item = _mark_test("server_mode", test_item)
|
|
656
714
|
try:
|
|
657
715
|
# noinspection PyUnresolvedReferences
|
|
658
716
|
import connexion
|
|
717
|
+
|
|
659
718
|
print(connexion.__file__) # keep this import from being removed.
|
|
660
719
|
except ImportError:
|
|
661
720
|
return unittest.skip(
|
|
662
|
-
"Install Toil with the 'server' extra to include this test."
|
|
721
|
+
"Install Toil with the 'server' extra to include this test."
|
|
722
|
+
)(test_item)
|
|
663
723
|
else:
|
|
664
724
|
return test_item
|
|
665
725
|
|
|
726
|
+
|
|
666
727
|
def needs_celery_broker(test_item: MT) -> MT:
|
|
667
728
|
"""
|
|
668
729
|
Use as a decorator before test classes or methods to run only if RabbitMQ is set up to take Celery jobs.
|
|
669
730
|
"""
|
|
670
|
-
test_item = _mark_test(
|
|
671
|
-
test_item = needs_env_var(
|
|
731
|
+
test_item = _mark_test("celery", needs_online(test_item))
|
|
732
|
+
test_item = needs_env_var(
|
|
733
|
+
"TOIL_WES_BROKER_URL", "a URL to a RabbitMQ broker for Celery"
|
|
734
|
+
)(test_item)
|
|
672
735
|
return test_item
|
|
673
736
|
|
|
737
|
+
|
|
674
738
|
def needs_wes_server(test_item: MT) -> MT:
|
|
675
739
|
"""
|
|
676
740
|
Use as a decorator before test classes or methods to run only if a WES
|
|
677
741
|
server is available to run against.
|
|
678
742
|
"""
|
|
679
|
-
test_item = _mark_test(
|
|
743
|
+
test_item = _mark_test("wes_server", needs_online(test_item))
|
|
680
744
|
|
|
681
|
-
wes_url = os.environ.get(
|
|
745
|
+
wes_url = os.environ.get("TOIL_WES_ENDPOINT")
|
|
682
746
|
if not wes_url:
|
|
683
747
|
return unittest.skip(f"Set TOIL_WES_ENDPOINT to include this test")(test_item)
|
|
684
748
|
|
|
685
749
|
try:
|
|
686
750
|
urlopen(f"{wes_url}/ga4gh/wes/v1/service-info")
|
|
687
751
|
except (HTTPError, URLError) as e:
|
|
688
|
-
return unittest.skip(f"Run a WES server on {wes_url} to include this test")(
|
|
752
|
+
return unittest.skip(f"Run a WES server on {wes_url} to include this test")(
|
|
753
|
+
test_item
|
|
754
|
+
)
|
|
689
755
|
|
|
690
756
|
return test_item
|
|
691
757
|
|
|
@@ -695,10 +761,10 @@ def needs_local_appliance(test_item: MT) -> MT:
|
|
|
695
761
|
Use as a decorator before test classes or methods to only run them if
|
|
696
762
|
the Toil appliance Docker image is downloaded.
|
|
697
763
|
"""
|
|
698
|
-
test_item = _mark_test(
|
|
699
|
-
if os.getenv(
|
|
700
|
-
return unittest.skip(
|
|
701
|
-
if not which(
|
|
764
|
+
test_item = _mark_test("appliance", test_item)
|
|
765
|
+
if os.getenv("TOIL_SKIP_DOCKER", "").lower() == "true":
|
|
766
|
+
return unittest.skip("Skipping docker test.")(test_item)
|
|
767
|
+
if not which("docker"):
|
|
702
768
|
return unittest.skip("Install docker to include this test.")(test_item)
|
|
703
769
|
|
|
704
770
|
try:
|
|
@@ -734,9 +800,9 @@ def needs_fetchable_appliance(test_item: MT) -> MT:
|
|
|
734
800
|
the Toil appliance Docker image is able to be downloaded from the Internet.
|
|
735
801
|
"""
|
|
736
802
|
|
|
737
|
-
test_item = _mark_test(
|
|
738
|
-
if os.getenv(
|
|
739
|
-
return unittest.skip(
|
|
803
|
+
test_item = _mark_test("fetchable_appliance", needs_online(test_item))
|
|
804
|
+
if os.getenv("TOIL_SKIP_DOCKER", "").lower() == "true":
|
|
805
|
+
return unittest.skip("Skipping docker test.")(test_item)
|
|
740
806
|
try:
|
|
741
807
|
applianceSelf()
|
|
742
808
|
except ApplianceImageNotFound:
|
|
@@ -757,12 +823,12 @@ def integrative(test_item: MT) -> MT:
|
|
|
757
823
|
We define integration tests as A) involving other, non-Toil software components
|
|
758
824
|
that we develop and/or B) having a higher cost (time or money).
|
|
759
825
|
"""
|
|
760
|
-
test_item = _mark_test(
|
|
761
|
-
if os.getenv(
|
|
826
|
+
test_item = _mark_test("integrative", test_item)
|
|
827
|
+
if os.getenv("TOIL_TEST_INTEGRATIVE", "").lower() == "true":
|
|
762
828
|
return test_item
|
|
763
829
|
else:
|
|
764
830
|
return unittest.skip(
|
|
765
|
-
|
|
831
|
+
"Set TOIL_TEST_INTEGRATIVE=True to include this integration test, "
|
|
766
832
|
"or run `make integration_test_local` to run all integration tests."
|
|
767
833
|
)(test_item)
|
|
768
834
|
|
|
@@ -772,14 +838,14 @@ def slow(test_item: MT) -> MT:
|
|
|
772
838
|
Use this decorator to identify tests that are slow and not critical.
|
|
773
839
|
Skip if TOIL_TEST_QUICK is true.
|
|
774
840
|
"""
|
|
775
|
-
test_item = _mark_test(
|
|
776
|
-
if os.environ.get(
|
|
841
|
+
test_item = _mark_test("slow", test_item)
|
|
842
|
+
if os.environ.get("TOIL_TEST_QUICK", "").lower() != "true":
|
|
777
843
|
return test_item
|
|
778
844
|
else:
|
|
779
845
|
return unittest.skip('Skipped because TOIL_TEST_QUICK is "True"')(test_item)
|
|
780
846
|
|
|
781
847
|
|
|
782
|
-
methodNamePartRegex = re.compile(
|
|
848
|
+
methodNamePartRegex = re.compile("^[a-zA-Z_0-9]+$")
|
|
783
849
|
|
|
784
850
|
|
|
785
851
|
@contextmanager
|
|
@@ -791,7 +857,7 @@ def timeLimit(seconds: int) -> Generator[None, None, None]:
|
|
|
791
857
|
specified amount of time. See <http://stackoverflow.com/a/601168>.
|
|
792
858
|
|
|
793
859
|
:param seconds: maximum allowable time, in seconds
|
|
794
|
-
|
|
860
|
+
|
|
795
861
|
>>> import time
|
|
796
862
|
>>> with timeLimit(2):
|
|
797
863
|
... time.sleep(1)
|
|
@@ -802,9 +868,10 @@ def timeLimit(seconds: int) -> Generator[None, None, None]:
|
|
|
802
868
|
...
|
|
803
869
|
RuntimeError: Timed out
|
|
804
870
|
"""
|
|
871
|
+
|
|
805
872
|
# noinspection PyUnusedLocal
|
|
806
873
|
def signal_handler(signum: int, frame: Any) -> None:
|
|
807
|
-
raise RuntimeError(
|
|
874
|
+
raise RuntimeError("Timed out")
|
|
808
875
|
|
|
809
876
|
signal.signal(signal.SIGALRM, signal_handler)
|
|
810
877
|
signal.alarm(seconds)
|
|
@@ -869,6 +936,7 @@ def make_tests(generalMethod, targetClass, **kwargs):
|
|
|
869
936
|
False
|
|
870
937
|
|
|
871
938
|
"""
|
|
939
|
+
|
|
872
940
|
def permuteIntoLeft(left, rParamName, right):
|
|
873
941
|
"""
|
|
874
942
|
Permutes values in right dictionary into each parameter: value dict pair in the left
|
|
@@ -894,9 +962,11 @@ def make_tests(generalMethod, targetClass, **kwargs):
|
|
|
894
962
|
"""
|
|
895
963
|
for prmValName, lDict in list(left.items()):
|
|
896
964
|
for rValName, rVal in list(right.items()):
|
|
897
|
-
nextPrmVal =
|
|
965
|
+
nextPrmVal = f"__{rParamName}_{rValName.lower()}"
|
|
898
966
|
if methodNamePartRegex.match(nextPrmVal) is None:
|
|
899
|
-
raise RuntimeError(
|
|
967
|
+
raise RuntimeError(
|
|
968
|
+
"The name '%s' cannot be used in a method name" % pvName
|
|
969
|
+
)
|
|
900
970
|
aggDict = dict(lDict)
|
|
901
971
|
aggDict[rParamName] = rVal
|
|
902
972
|
left[prmValName + nextPrmVal] = aggDict
|
|
@@ -911,7 +981,7 @@ def make_tests(generalMethod, targetClass, **kwargs):
|
|
|
911
981
|
else:
|
|
912
982
|
return generalMethod(self)
|
|
913
983
|
|
|
914
|
-
methodName = f
|
|
984
|
+
methodName = f"test_{generalMethod.__name__}{prmNames}"
|
|
915
985
|
|
|
916
986
|
setattr(targetClass, methodName, fx)
|
|
917
987
|
|
|
@@ -924,9 +994,11 @@ def make_tests(generalMethod, targetClass, **kwargs):
|
|
|
924
994
|
left = {}
|
|
925
995
|
prmName, vals = sortedKwargs.pop()
|
|
926
996
|
for valName, val in list(vals.items()):
|
|
927
|
-
pvName = f
|
|
997
|
+
pvName = f"__{prmName}_{valName.lower()}"
|
|
928
998
|
if methodNamePartRegex.match(pvName) is None:
|
|
929
|
-
raise RuntimeError(
|
|
999
|
+
raise RuntimeError(
|
|
1000
|
+
"The name '%s' cannot be used in a method name" % pvName
|
|
1001
|
+
)
|
|
930
1002
|
left[pvName] = {prmName: val}
|
|
931
1003
|
|
|
932
1004
|
# get cartesian product
|
|
@@ -952,9 +1024,9 @@ class ApplianceTestSupport(ToilTest):
|
|
|
952
1024
|
|
|
953
1025
|
@contextmanager
|
|
954
1026
|
def _applianceCluster(
|
|
955
|
-
self, mounts:
|
|
1027
|
+
self, mounts: dict[str, str], numCores: Optional[int] = None
|
|
956
1028
|
) -> Generator[
|
|
957
|
-
|
|
1029
|
+
tuple["ApplianceTestSupport.LeaderThread", "ApplianceTestSupport.WorkerThread"],
|
|
958
1030
|
None,
|
|
959
1031
|
None,
|
|
960
1032
|
]:
|
|
@@ -984,10 +1056,10 @@ class ApplianceTestSupport(ToilTest):
|
|
|
984
1056
|
class Appliance(ExceptionalThread, metaclass=ABCMeta):
|
|
985
1057
|
@abstractmethod
|
|
986
1058
|
def _getRole(self) -> str:
|
|
987
|
-
return
|
|
1059
|
+
return "leader"
|
|
988
1060
|
|
|
989
1061
|
@abstractmethod
|
|
990
|
-
def _containerCommand(self) ->
|
|
1062
|
+
def _containerCommand(self) -> list[str]:
|
|
991
1063
|
pass
|
|
992
1064
|
|
|
993
1065
|
@abstractmethod
|
|
@@ -1000,7 +1072,7 @@ class ApplianceTestSupport(ToilTest):
|
|
|
1000
1072
|
def __init__(
|
|
1001
1073
|
self,
|
|
1002
1074
|
outer: "ApplianceTestSupport",
|
|
1003
|
-
mounts:
|
|
1075
|
+
mounts: dict[str, str],
|
|
1004
1076
|
cleanMounts: bool = False,
|
|
1005
1077
|
) -> None:
|
|
1006
1078
|
assert all(
|
|
@@ -1017,15 +1089,20 @@ class ApplianceTestSupport(ToilTest):
|
|
|
1017
1089
|
with self.lock:
|
|
1018
1090
|
image = applianceSelf()
|
|
1019
1091
|
# Omitting --rm, it's unreliable, see https://github.com/docker/docker/issues/16575
|
|
1020
|
-
args = list(
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1092
|
+
args = list(
|
|
1093
|
+
concat(
|
|
1094
|
+
"docker",
|
|
1095
|
+
"run",
|
|
1096
|
+
"--entrypoint=" + self._entryPoint(),
|
|
1097
|
+
"--net=host",
|
|
1098
|
+
"-i",
|
|
1099
|
+
"--name=" + self.containerName,
|
|
1100
|
+
["--volume=%s:%s" % mount for mount in self.mounts.items()],
|
|
1101
|
+
image,
|
|
1102
|
+
self._containerCommand(),
|
|
1103
|
+
)
|
|
1104
|
+
)
|
|
1105
|
+
logger.info("Running %r", args)
|
|
1029
1106
|
self.popen = subprocess.Popen(args)
|
|
1030
1107
|
self.start()
|
|
1031
1108
|
self.__wait_running()
|
|
@@ -1033,17 +1110,17 @@ class ApplianceTestSupport(ToilTest):
|
|
|
1033
1110
|
|
|
1034
1111
|
# noinspection PyUnusedLocal
|
|
1035
1112
|
def __exit__(
|
|
1036
|
-
self, exc_type:
|
|
1113
|
+
self, exc_type: type[BaseException], exc_val: Exception, exc_tb: Any
|
|
1037
1114
|
) -> Literal[False]:
|
|
1038
1115
|
try:
|
|
1039
1116
|
try:
|
|
1040
|
-
self.outer._run(
|
|
1117
|
+
self.outer._run("docker", "stop", self.containerName)
|
|
1041
1118
|
self.join()
|
|
1042
1119
|
finally:
|
|
1043
1120
|
if self.cleanMounts:
|
|
1044
1121
|
self.__cleanMounts()
|
|
1045
1122
|
finally:
|
|
1046
|
-
self.outer._run(
|
|
1123
|
+
self.outer._run("docker", "rm", "-f", self.containerName)
|
|
1047
1124
|
return False # don't swallow exception
|
|
1048
1125
|
|
|
1049
1126
|
def __wait_running(self) -> None:
|
|
@@ -1070,7 +1147,7 @@ class ApplianceTestSupport(ToilTest):
|
|
|
1070
1147
|
except subprocess.CalledProcessError:
|
|
1071
1148
|
pass
|
|
1072
1149
|
else:
|
|
1073
|
-
if
|
|
1150
|
+
if "true" == running:
|
|
1074
1151
|
break
|
|
1075
1152
|
time.sleep(1)
|
|
1076
1153
|
|
|
@@ -1083,30 +1160,33 @@ class ApplianceTestSupport(ToilTest):
|
|
|
1083
1160
|
was stopped, otherwise the running container might still be writing files.
|
|
1084
1161
|
"""
|
|
1085
1162
|
# Delete all files within each mounted directory, but not the directory itself.
|
|
1086
|
-
cmd =
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
self.outer._run(
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1163
|
+
cmd = "shopt -s dotglob && rm -rf " + " ".join(
|
|
1164
|
+
v + "/*" for k, v in self.mounts.items() if os.path.isdir(k)
|
|
1165
|
+
)
|
|
1166
|
+
self.outer._run(
|
|
1167
|
+
"docker",
|
|
1168
|
+
"run",
|
|
1169
|
+
"--rm",
|
|
1170
|
+
"--entrypoint=/bin/bash",
|
|
1171
|
+
applianceSelf(),
|
|
1172
|
+
"-c",
|
|
1173
|
+
cmd,
|
|
1174
|
+
)
|
|
1095
1175
|
|
|
1096
1176
|
def tryRun(self) -> None:
|
|
1097
1177
|
assert self.popen
|
|
1098
1178
|
self.popen.wait()
|
|
1099
|
-
logger.info(
|
|
1179
|
+
logger.info("Exiting %s", self.__class__.__name__)
|
|
1100
1180
|
|
|
1101
1181
|
def runOnAppliance(self, *args: str, **kwargs: Any) -> None:
|
|
1102
1182
|
# Check if thread is still alive. Note that ExceptionalThread.join raises the
|
|
1103
1183
|
# exception that occurred in the thread.
|
|
1104
1184
|
self.join(timeout=0)
|
|
1105
1185
|
# noinspection PyProtectedMember
|
|
1106
|
-
self.outer._run(
|
|
1186
|
+
self.outer._run("docker", "exec", "-i", self.containerName, *args, **kwargs)
|
|
1107
1187
|
|
|
1108
1188
|
def writeToAppliance(self, path: str, contents: Any) -> None:
|
|
1109
|
-
self.runOnAppliance(
|
|
1189
|
+
self.runOnAppliance("tee", path, input=contents)
|
|
1110
1190
|
|
|
1111
1191
|
def deployScript(
|
|
1112
1192
|
self, path: str, packagePath: str, script: Union[str, Callable[..., Any]]
|
|
@@ -1129,42 +1209,46 @@ class ApplianceTestSupport(ToilTest):
|
|
|
1129
1209
|
packagePath_list = packagePath.split(".")
|
|
1130
1210
|
packages, module = packagePath_list[:-1], packagePath_list[-1]
|
|
1131
1211
|
for package in packages:
|
|
1132
|
-
path +=
|
|
1133
|
-
self.runOnAppliance(
|
|
1134
|
-
self.writeToAppliance(path +
|
|
1135
|
-
self.writeToAppliance(path +
|
|
1212
|
+
path += "/" + package
|
|
1213
|
+
self.runOnAppliance("mkdir", "-p", path)
|
|
1214
|
+
self.writeToAppliance(path + "/__init__.py", "")
|
|
1215
|
+
self.writeToAppliance(path + "/" + module + ".py", script)
|
|
1136
1216
|
|
|
1137
1217
|
class LeaderThread(Appliance):
|
|
1138
1218
|
def _entryPoint(self) -> str:
|
|
1139
|
-
return
|
|
1219
|
+
return "mesos-master"
|
|
1140
1220
|
|
|
1141
1221
|
def _getRole(self) -> str:
|
|
1142
|
-
return
|
|
1222
|
+
return "leader"
|
|
1143
1223
|
|
|
1144
|
-
def _containerCommand(self) ->
|
|
1145
|
-
return [
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1224
|
+
def _containerCommand(self) -> list[str]:
|
|
1225
|
+
return [
|
|
1226
|
+
"--registry=in_memory",
|
|
1227
|
+
"--ip=127.0.0.1",
|
|
1228
|
+
"--port=5050",
|
|
1229
|
+
"--allocation_interval=500ms",
|
|
1230
|
+
]
|
|
1149
1231
|
|
|
1150
1232
|
class WorkerThread(Appliance):
|
|
1151
1233
|
def __init__(
|
|
1152
|
-
self, outer: "ApplianceTestSupport", mounts:
|
|
1234
|
+
self, outer: "ApplianceTestSupport", mounts: dict[str, str], numCores: int
|
|
1153
1235
|
) -> None:
|
|
1154
1236
|
self.numCores = numCores
|
|
1155
1237
|
super().__init__(outer, mounts)
|
|
1156
1238
|
|
|
1157
1239
|
def _entryPoint(self) -> str:
|
|
1158
|
-
return
|
|
1240
|
+
return "mesos-agent"
|
|
1159
1241
|
|
|
1160
1242
|
def _getRole(self) -> str:
|
|
1161
|
-
return
|
|
1162
|
-
|
|
1163
|
-
def _containerCommand(self) ->
|
|
1164
|
-
return [
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1243
|
+
return "worker"
|
|
1244
|
+
|
|
1245
|
+
def _containerCommand(self) -> list[str]:
|
|
1246
|
+
return [
|
|
1247
|
+
"--work_dir=/var/lib/mesos",
|
|
1248
|
+
"--ip=127.0.0.1",
|
|
1249
|
+
"--master=127.0.0.1:5050",
|
|
1250
|
+
"--attributes=preemptible:False",
|
|
1251
|
+
"--resources=cpus(*):%i" % self.numCores,
|
|
1252
|
+
"--no-hostname_lookup",
|
|
1253
|
+
"--no-systemd_enable_support",
|
|
1254
|
+
]
|