toil 6.1.0a1__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 +122 -315
- toil/batchSystems/__init__.py +1 -0
- toil/batchSystems/abstractBatchSystem.py +173 -89
- toil/batchSystems/abstractGridEngineBatchSystem.py +272 -148
- toil/batchSystems/awsBatch.py +244 -135
- toil/batchSystems/cleanup_support.py +26 -16
- toil/batchSystems/contained_executor.py +31 -28
- toil/batchSystems/gridengine.py +86 -50
- toil/batchSystems/htcondor.py +166 -89
- toil/batchSystems/kubernetes.py +632 -382
- toil/batchSystems/local_support.py +20 -15
- toil/batchSystems/lsf.py +134 -81
- toil/batchSystems/lsfHelper.py +13 -11
- toil/batchSystems/mesos/__init__.py +41 -29
- toil/batchSystems/mesos/batchSystem.py +290 -151
- toil/batchSystems/mesos/executor.py +79 -50
- toil/batchSystems/mesos/test/__init__.py +31 -23
- toil/batchSystems/options.py +46 -28
- toil/batchSystems/registry.py +53 -19
- toil/batchSystems/singleMachine.py +296 -125
- toil/batchSystems/slurm.py +603 -138
- toil/batchSystems/torque.py +47 -33
- toil/bus.py +186 -76
- toil/common.py +664 -368
- toil/cwl/__init__.py +1 -1
- toil/cwl/cwltoil.py +1136 -483
- toil/cwl/utils.py +17 -22
- toil/deferred.py +63 -42
- toil/exceptions.py +5 -3
- toil/fileStores/__init__.py +5 -5
- toil/fileStores/abstractFileStore.py +140 -60
- toil/fileStores/cachingFileStore.py +717 -269
- toil/fileStores/nonCachingFileStore.py +116 -87
- toil/job.py +1225 -368
- toil/jobStores/abstractJobStore.py +416 -266
- toil/jobStores/aws/jobStore.py +863 -477
- toil/jobStores/aws/utils.py +201 -120
- toil/jobStores/conftest.py +3 -2
- toil/jobStores/fileJobStore.py +292 -154
- toil/jobStores/googleJobStore.py +140 -74
- toil/jobStores/utils.py +36 -15
- toil/leader.py +668 -272
- toil/lib/accelerators.py +115 -18
- toil/lib/aws/__init__.py +74 -31
- 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 +214 -39
- toil/lib/aws/utils.py +287 -231
- toil/lib/bioio.py +13 -5
- toil/lib/compatibility.py +11 -6
- toil/lib/conversions.py +104 -47
- toil/lib/docker.py +131 -103
- toil/lib/ec2.py +361 -199
- 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 +5 -3
- 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 +141 -15
- toil/lib/iterables.py +4 -2
- toil/lib/memoize.py +12 -8
- toil/lib/misc.py +66 -21
- toil/lib/objects.py +2 -2
- toil/lib/resources.py +68 -15
- toil/lib/retry.py +126 -81
- toil/lib/threading.py +299 -82
- toil/lib/throttle.py +16 -15
- toil/options/common.py +843 -409
- toil/options/cwl.py +175 -90
- toil/options/runner.py +50 -0
- toil/options/wdl.py +73 -17
- toil/provisioners/__init__.py +117 -46
- toil/provisioners/abstractProvisioner.py +332 -157
- toil/provisioners/aws/__init__.py +70 -33
- toil/provisioners/aws/awsProvisioner.py +1145 -715
- toil/provisioners/clusterScaler.py +541 -279
- toil/provisioners/gceProvisioner.py +282 -179
- toil/provisioners/node.py +155 -79
- toil/realtimeLogger.py +34 -22
- toil/resource.py +137 -75
- toil/server/app.py +128 -62
- 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 +224 -70
- toil/test/__init__.py +282 -183
- toil/test/batchSystems/batchSystemTest.py +460 -210
- toil/test/batchSystems/batch_system_plugin_test.py +90 -0
- toil/test/batchSystems/test_gridengine.py +173 -0
- toil/test/batchSystems/test_lsf_helper.py +67 -58
- toil/test/batchSystems/test_slurm.py +110 -49
- toil/test/cactus/__init__.py +0 -0
- toil/test/cactus/test_cactus_integration.py +56 -0
- toil/test/cwl/cwlTest.py +496 -287
- 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/cwl/seqtk_seq.cwl +1 -1
- toil/test/docs/scriptsTest.py +69 -46
- toil/test/jobStores/jobStoreTest.py +427 -264
- toil/test/lib/aws/test_iam.py +118 -50
- 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 +58 -50
- 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/__init__.py +13 -0
- toil/test/options/options.py +42 -0
- toil/test/provisioners/aws/awsProvisionerTest.py +320 -150
- toil/test/provisioners/clusterScalerTest.py +440 -250
- toil/test/provisioners/clusterTest.py +166 -44
- 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 +141 -101
- 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 +32 -24
- toil/test/src/environmentTest.py +135 -0
- toil/test/src/fileStoreTest.py +539 -272
- toil/test/src/helloWorldTest.py +7 -4
- toil/test/src/importExportFileTest.py +61 -31
- toil/test/src/jobDescriptionTest.py +46 -21
- 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 +121 -71
- 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 +10 -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 +73 -23
- toil/test/utils/toilDebugTest.py +103 -33
- toil/test/utils/toilKillTest.py +4 -5
- toil/test/utils/utilsTest.py +245 -106
- toil/test/wdl/wdltoil_test.py +818 -149
- toil/test/wdl/wdltoil_test_kubernetes.py +91 -0
- toil/toilState.py +120 -35
- toil/utils/toilConfig.py +13 -4
- toil/utils/toilDebugFile.py +44 -27
- toil/utils/toilDebugJob.py +214 -27
- toil/utils/toilDestroyCluster.py +11 -6
- toil/utils/toilKill.py +8 -3
- toil/utils/toilLaunchCluster.py +256 -140
- toil/utils/toilMain.py +37 -16
- toil/utils/toilRsyncCluster.py +32 -14
- toil/utils/toilSshCluster.py +49 -22
- toil/utils/toilStats.py +356 -273
- toil/utils/toilStatus.py +292 -139
- toil/utils/toilUpdateEC2Instances.py +3 -1
- toil/version.py +12 -12
- toil/wdl/utils.py +5 -5
- toil/wdl/wdltoil.py +3913 -1033
- toil/worker.py +367 -184
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/LICENSE +25 -0
- toil-8.0.0.dist-info/METADATA +173 -0
- toil-8.0.0.dist-info/RECORD +253 -0
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/WHEEL +1 -1
- toil-6.1.0a1.dist-info/METADATA +0 -125
- toil-6.1.0a1.dist-info/RECORD +0 -237
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/entry_points.txt +0 -0
- {toil-6.1.0a1.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,43 +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
|
-
Optional,
|
|
40
|
-
Tuple,
|
|
41
|
-
Type,
|
|
42
|
-
TypeVar,
|
|
43
|
-
Union,
|
|
44
|
-
cast)
|
|
36
|
+
from typing import Any, Callable, Literal, Optional, TypeVar, Union, cast
|
|
45
37
|
from unittest.util import strclass
|
|
46
38
|
from urllib.error import HTTPError, URLError
|
|
47
39
|
from urllib.request import urlopen
|
|
48
40
|
|
|
49
|
-
import pytz
|
|
50
|
-
|
|
51
|
-
if sys.version_info >= (3, 8):
|
|
52
|
-
from typing import Literal
|
|
53
|
-
else:
|
|
54
|
-
from typing_extensions import Literal
|
|
55
|
-
|
|
56
41
|
from toil import ApplianceImageNotFound, applianceSelf, toilPackageDirPath
|
|
57
|
-
from toil.lib.accelerators import (
|
|
58
|
-
|
|
59
|
-
|
|
42
|
+
from toil.lib.accelerators import (
|
|
43
|
+
have_working_nvidia_docker_runtime,
|
|
44
|
+
have_working_nvidia_smi,
|
|
45
|
+
)
|
|
60
46
|
from toil.lib.io import mkdtemp
|
|
61
47
|
from toil.lib.iterables import concat
|
|
62
48
|
from toil.lib.memoize import memoize
|
|
@@ -83,20 +69,24 @@ class ToilTest(unittest.TestCase):
|
|
|
83
69
|
"""
|
|
84
70
|
|
|
85
71
|
_tempBaseDir: Optional[str] = None
|
|
86
|
-
_tempDirs:
|
|
72
|
+
_tempDirs: list[str] = []
|
|
87
73
|
|
|
88
74
|
def setup_method(self, method: Any) -> None:
|
|
89
|
-
western =
|
|
90
|
-
california_time =
|
|
75
|
+
western = zoneinfo.ZoneInfo("America/Los_Angeles")
|
|
76
|
+
california_time = datetime.datetime.now(tz=western)
|
|
91
77
|
timestamp = california_time.strftime("%b %d %Y %H:%M:%S:%f %Z")
|
|
92
|
-
print(
|
|
78
|
+
print(
|
|
79
|
+
f"\n\n[TEST] {strclass(self.__class__)}:{self._testMethodName} ({timestamp})\n\n"
|
|
80
|
+
)
|
|
93
81
|
|
|
94
82
|
@classmethod
|
|
95
83
|
def setUpClass(cls) -> None:
|
|
96
84
|
super().setUpClass()
|
|
97
|
-
tempBaseDir = os.environ.get(
|
|
85
|
+
tempBaseDir = os.environ.get("TOIL_TEST_TEMP", None)
|
|
98
86
|
if tempBaseDir is not None and not os.path.isabs(tempBaseDir):
|
|
99
|
-
tempBaseDir = os.path.abspath(
|
|
87
|
+
tempBaseDir = os.path.abspath(
|
|
88
|
+
os.path.join(cls._projectRootPath(), tempBaseDir)
|
|
89
|
+
)
|
|
100
90
|
os.makedirs(tempBaseDir, exist_ok=True)
|
|
101
91
|
cls._tempBaseDir = tempBaseDir
|
|
102
92
|
|
|
@@ -127,7 +117,9 @@ class ToilTest(unittest.TestCase):
|
|
|
127
117
|
Use us-west-2 unless running on EC2, in which case use the region in which
|
|
128
118
|
the instance is located
|
|
129
119
|
"""
|
|
130
|
-
|
|
120
|
+
from toil.lib.aws import running_on_ec2
|
|
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,50 +349,60 @@ 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
|
-
from
|
|
376
|
-
|
|
378
|
+
from boto3 import Session
|
|
379
|
+
|
|
380
|
+
session = Session()
|
|
381
|
+
boto3_credentials = session.get_credentials()
|
|
377
382
|
except ImportError:
|
|
378
383
|
return unittest.skip("Install Toil with the 'aws' extra to include this test.")(
|
|
379
384
|
test_item
|
|
380
385
|
)
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
386
|
+
from toil.lib.aws import running_on_ec2
|
|
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
|
+
)
|
|
384
396
|
return test_item
|
|
385
397
|
|
|
386
398
|
|
|
387
399
|
def needs_aws_ec2(test_item: MT) -> MT:
|
|
388
400
|
"""Use as a decorator before test classes or methods to run only if AWS EC2 is usable."""
|
|
389
401
|
# Assume we need S3 as well as EC2
|
|
390
|
-
test_item = _mark_test(
|
|
402
|
+
test_item = _mark_test("aws-ec2", needs_aws_s3(test_item))
|
|
391
403
|
# In addition to S3 we also need an SSH key to deploy with.
|
|
392
404
|
# TODO: We assume that if this is set we have EC2 access.
|
|
393
|
-
test_item = needs_env_var(
|
|
405
|
+
test_item = needs_env_var("TOIL_AWS_KEYNAME", "an AWS-stored SSH key")(test_item)
|
|
394
406
|
return test_item
|
|
395
407
|
|
|
396
408
|
|
|
@@ -400,16 +412,23 @@ def needs_aws_batch(test_item: MT) -> MT:
|
|
|
400
412
|
is usable.
|
|
401
413
|
"""
|
|
402
414
|
# Assume we need S3 as well as Batch
|
|
403
|
-
test_item = _mark_test(
|
|
415
|
+
test_item = _mark_test("aws-batch", needs_aws_s3(test_item))
|
|
404
416
|
# Assume we have Batch if the user has set these variables.
|
|
405
|
-
test_item = needs_env_var(
|
|
406
|
-
|
|
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)
|
|
407
423
|
try:
|
|
408
424
|
from toil.lib.aws import get_current_aws_region
|
|
425
|
+
|
|
409
426
|
if get_current_aws_region() is None:
|
|
410
427
|
# We don't know a region so we need one set.
|
|
411
428
|
# TODO: It always won't be set if we get here.
|
|
412
|
-
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)
|
|
413
432
|
|
|
414
433
|
except ImportError:
|
|
415
434
|
return unittest.skip("Install Toil with the 'aws' extra to include this test.")(
|
|
@@ -417,67 +436,79 @@ def needs_aws_batch(test_item: MT) -> MT:
|
|
|
417
436
|
)
|
|
418
437
|
return test_item
|
|
419
438
|
|
|
439
|
+
|
|
420
440
|
def needs_google_storage(test_item: MT) -> MT:
|
|
421
441
|
"""
|
|
422
442
|
Use as a decorator before test classes or methods to run only if Google
|
|
423
443
|
Cloud is installed and we ought to be able to access public Google Storage
|
|
424
444
|
URIs.
|
|
425
445
|
"""
|
|
426
|
-
test_item = _mark_test(
|
|
446
|
+
test_item = _mark_test("google-storage", needs_online(test_item))
|
|
427
447
|
try:
|
|
428
448
|
from google.cloud import storage # noqa
|
|
429
449
|
except ImportError:
|
|
430
|
-
return unittest.skip(
|
|
450
|
+
return unittest.skip(
|
|
451
|
+
"Install Toil with the 'google' extra to include this test."
|
|
452
|
+
)(test_item)
|
|
431
453
|
|
|
432
454
|
return test_item
|
|
433
455
|
|
|
456
|
+
|
|
434
457
|
def needs_google_project(test_item: MT) -> MT:
|
|
435
458
|
"""
|
|
436
459
|
Use as a decorator before test classes or methods to run only if we have a Google Cloud project set.
|
|
437
460
|
"""
|
|
438
|
-
test_item = _mark_test(
|
|
439
|
-
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)
|
|
440
463
|
return test_item
|
|
441
464
|
|
|
442
465
|
|
|
443
466
|
def needs_gridengine(test_item: MT) -> MT:
|
|
444
467
|
"""Use as a decorator before test classes or methods to run only if GridEngine is installed."""
|
|
445
|
-
test_item = _mark_test(
|
|
446
|
-
if which(
|
|
468
|
+
test_item = _mark_test("gridengine", test_item)
|
|
469
|
+
if which("qhost"):
|
|
447
470
|
return test_item
|
|
448
471
|
return unittest.skip("Install GridEngine to include this test.")(test_item)
|
|
449
472
|
|
|
450
473
|
|
|
451
474
|
def needs_torque(test_item: MT) -> MT:
|
|
452
475
|
"""Use as a decorator before test classes or methods to run only if PBS/Torque is installed."""
|
|
453
|
-
test_item = _mark_test(
|
|
454
|
-
if which(
|
|
476
|
+
test_item = _mark_test("torque", test_item)
|
|
477
|
+
if which("pbsnodes"):
|
|
455
478
|
return test_item
|
|
456
479
|
return unittest.skip("Install PBS/Torque to include this test.")(test_item)
|
|
457
480
|
|
|
481
|
+
|
|
458
482
|
def needs_kubernetes_installed(test_item: MT) -> MT:
|
|
459
483
|
"""Use as a decorator before test classes or methods to run only if Kubernetes is installed."""
|
|
460
|
-
test_item = _mark_test(
|
|
484
|
+
test_item = _mark_test("kubernetes", test_item)
|
|
461
485
|
try:
|
|
462
486
|
import kubernetes
|
|
487
|
+
|
|
463
488
|
str(kubernetes) # to prevent removal of this import
|
|
464
489
|
except ImportError:
|
|
465
|
-
return unittest.skip(
|
|
490
|
+
return unittest.skip(
|
|
491
|
+
"Install Toil with the 'kubernetes' extra to include this test."
|
|
492
|
+
)(test_item)
|
|
466
493
|
return test_item
|
|
467
494
|
|
|
495
|
+
|
|
468
496
|
def needs_kubernetes(test_item: MT) -> MT:
|
|
469
497
|
"""Use as a decorator before test classes or methods to run only if Kubernetes is installed and configured."""
|
|
470
498
|
test_item = needs_kubernetes_installed(needs_online(test_item))
|
|
471
499
|
try:
|
|
472
500
|
import kubernetes
|
|
501
|
+
|
|
473
502
|
try:
|
|
474
503
|
kubernetes.config.load_kube_config()
|
|
475
504
|
except kubernetes.config.ConfigException:
|
|
476
505
|
try:
|
|
477
506
|
kubernetes.config.load_incluster_config()
|
|
478
507
|
except kubernetes.config.ConfigException:
|
|
479
|
-
return unittest.skip(
|
|
480
|
-
|
|
508
|
+
return unittest.skip(
|
|
509
|
+
"Configure Kubernetes (~/.kube/config, $KUBECONFIG, "
|
|
510
|
+
"or current pod) to include this test."
|
|
511
|
+
)(test_item)
|
|
481
512
|
except ImportError:
|
|
482
513
|
# We should already be skipping this test
|
|
483
514
|
pass
|
|
@@ -486,37 +517,50 @@ def needs_kubernetes(test_item: MT) -> MT:
|
|
|
486
517
|
|
|
487
518
|
def needs_mesos(test_item: MT) -> MT:
|
|
488
519
|
"""Use as a decorator before test classes or methods to run only if Mesos is installed."""
|
|
489
|
-
test_item = _mark_test(
|
|
490
|
-
if not (which(
|
|
491
|
-
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)
|
|
492
525
|
try:
|
|
493
526
|
import psutil # noqa
|
|
494
527
|
import pymesos # noqa
|
|
495
528
|
except ImportError:
|
|
496
|
-
return unittest.skip(
|
|
529
|
+
return unittest.skip(
|
|
530
|
+
"Install Mesos (and Toil with the 'mesos' extra) to include this test."
|
|
531
|
+
)(test_item)
|
|
497
532
|
return test_item
|
|
498
533
|
|
|
499
534
|
|
|
500
535
|
def needs_slurm(test_item: MT) -> MT:
|
|
501
536
|
"""Use as a decorator before test classes or methods to run only if Slurm is installed."""
|
|
502
|
-
test_item = _mark_test(
|
|
503
|
-
if which(
|
|
537
|
+
test_item = _mark_test("slurm", test_item)
|
|
538
|
+
if which("squeue"):
|
|
504
539
|
return test_item
|
|
505
540
|
return unittest.skip("Install Slurm to include this test.")(test_item)
|
|
506
541
|
|
|
507
542
|
|
|
508
543
|
def needs_htcondor(test_item: MT) -> MT:
|
|
509
544
|
"""Use a decorator before test classes or methods to run only if the HTCondor is installed."""
|
|
510
|
-
test_item = _mark_test(
|
|
545
|
+
test_item = _mark_test("htcondor", test_item)
|
|
511
546
|
try:
|
|
512
547
|
import htcondor
|
|
513
|
-
|
|
548
|
+
|
|
549
|
+
htcondor.Collector(os.getenv("TOIL_HTCONDOR_COLLECTOR")).query(
|
|
550
|
+
constraint="False"
|
|
551
|
+
)
|
|
514
552
|
except ImportError:
|
|
515
|
-
return unittest.skip(
|
|
553
|
+
return unittest.skip(
|
|
554
|
+
"Install the HTCondor Python bindings to include this test."
|
|
555
|
+
)(test_item)
|
|
516
556
|
except OSError:
|
|
517
|
-
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
|
+
)
|
|
518
560
|
except RuntimeError:
|
|
519
|
-
return unittest.skip(
|
|
561
|
+
return unittest.skip(
|
|
562
|
+
"HTCondor must be installed and configured to include this test."
|
|
563
|
+
)(test_item)
|
|
520
564
|
else:
|
|
521
565
|
return test_item
|
|
522
566
|
|
|
@@ -525,8 +569,8 @@ def needs_lsf(test_item: MT) -> MT:
|
|
|
525
569
|
"""
|
|
526
570
|
Use as a decorator before test classes or methods to only run them if LSF is installed.
|
|
527
571
|
"""
|
|
528
|
-
test_item = _mark_test(
|
|
529
|
-
if which(
|
|
572
|
+
test_item = _mark_test("lsf", test_item)
|
|
573
|
+
if which("bsub"):
|
|
530
574
|
return test_item
|
|
531
575
|
else:
|
|
532
576
|
return unittest.skip("Install LSF to include this test.")(test_item)
|
|
@@ -534,8 +578,8 @@ def needs_lsf(test_item: MT) -> MT:
|
|
|
534
578
|
|
|
535
579
|
def needs_java(test_item: MT) -> MT:
|
|
536
580
|
"""Use as a test decorator to run only if java is installed."""
|
|
537
|
-
test_item = _mark_test(
|
|
538
|
-
if which(
|
|
581
|
+
test_item = _mark_test("java", test_item)
|
|
582
|
+
if which("java"):
|
|
539
583
|
return test_item
|
|
540
584
|
else:
|
|
541
585
|
return unittest.skip("Install java to include this test.")(test_item)
|
|
@@ -546,74 +590,84 @@ def needs_docker(test_item: MT) -> MT:
|
|
|
546
590
|
Use as a decorator before test classes or methods to only run them if
|
|
547
591
|
docker is installed and docker-based tests are enabled.
|
|
548
592
|
"""
|
|
549
|
-
test_item = _mark_test(
|
|
550
|
-
if os.getenv(
|
|
551
|
-
return unittest.skip(
|
|
552
|
-
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"):
|
|
553
597
|
return test_item
|
|
554
598
|
else:
|
|
555
599
|
return unittest.skip("Install docker to include this test.")(test_item)
|
|
556
600
|
|
|
601
|
+
|
|
557
602
|
def needs_singularity(test_item: MT) -> MT:
|
|
558
603
|
"""
|
|
559
604
|
Use as a decorator before test classes or methods to only run them if
|
|
560
605
|
singularity is installed.
|
|
561
606
|
"""
|
|
562
|
-
test_item = _mark_test(
|
|
563
|
-
if which(
|
|
607
|
+
test_item = _mark_test("singularity", needs_online(test_item))
|
|
608
|
+
if which("singularity"):
|
|
564
609
|
return test_item
|
|
565
610
|
else:
|
|
566
611
|
return unittest.skip("Install singularity to include this test.")(test_item)
|
|
567
612
|
|
|
613
|
+
|
|
568
614
|
def needs_singularity_or_docker(test_item: MT) -> MT:
|
|
569
615
|
"""
|
|
570
616
|
Use as a decorator before test classes or methods to only run them if
|
|
571
617
|
docker is installed and docker-based tests are enabled, or if Singularity
|
|
572
618
|
is installed.
|
|
573
619
|
"""
|
|
574
|
-
|
|
620
|
+
|
|
575
621
|
# TODO: Is there a good way to OR decorators?
|
|
576
|
-
if which(
|
|
622
|
+
if which("singularity"):
|
|
577
623
|
# Singularity is here, say it's a Singularity test
|
|
578
624
|
return needs_singularity(test_item)
|
|
579
625
|
else:
|
|
580
626
|
# Otherwise say it's a Docker test.
|
|
581
627
|
return needs_docker(test_item)
|
|
582
|
-
|
|
628
|
+
|
|
629
|
+
|
|
583
630
|
def needs_local_cuda(test_item: MT) -> MT:
|
|
584
631
|
"""
|
|
585
632
|
Use as a decorator before test classes or methods to only run them if
|
|
586
633
|
a CUDA setup legible to cwltool (i.e. providing userspace nvidia-smi) is present.
|
|
587
634
|
"""
|
|
588
|
-
test_item = _mark_test(
|
|
635
|
+
test_item = _mark_test("local_cuda", test_item)
|
|
589
636
|
if have_working_nvidia_smi():
|
|
590
637
|
return test_item
|
|
591
638
|
else:
|
|
592
|
-
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
|
+
|
|
593
643
|
|
|
594
644
|
def needs_docker_cuda(test_item: MT) -> MT:
|
|
595
645
|
"""
|
|
596
646
|
Use as a decorator before test classes or methods to only run them if
|
|
597
647
|
a CUDA setup is available through Docker.
|
|
598
648
|
"""
|
|
599
|
-
test_item = _mark_test(
|
|
649
|
+
test_item = _mark_test("docker_cuda", needs_online(test_item))
|
|
600
650
|
if have_working_nvidia_docker_runtime():
|
|
601
651
|
return test_item
|
|
602
652
|
else:
|
|
603
|
-
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
|
+
|
|
604
657
|
|
|
605
658
|
def needs_encryption(test_item: MT) -> MT:
|
|
606
659
|
"""
|
|
607
660
|
Use as a decorator before test classes or methods to only run them if PyNaCl is installed
|
|
608
661
|
and configured.
|
|
609
662
|
"""
|
|
610
|
-
test_item = _mark_test(
|
|
663
|
+
test_item = _mark_test("encryption", test_item)
|
|
611
664
|
try:
|
|
612
665
|
# noinspection PyUnresolvedReferences
|
|
613
666
|
import nacl # noqa
|
|
614
667
|
except ImportError:
|
|
615
668
|
return unittest.skip(
|
|
616
|
-
"Install Toil with the 'encryption' extra to include this test."
|
|
669
|
+
"Install Toil with the 'encryption' extra to include this test."
|
|
670
|
+
)(test_item)
|
|
617
671
|
else:
|
|
618
672
|
return test_item
|
|
619
673
|
|
|
@@ -623,12 +677,31 @@ def needs_cwl(test_item: MT) -> MT:
|
|
|
623
677
|
Use as a decorator before test classes or methods to only run them if CWLTool is installed
|
|
624
678
|
and configured.
|
|
625
679
|
"""
|
|
626
|
-
test_item = _mark_test(
|
|
680
|
+
test_item = _mark_test("cwl", test_item)
|
|
627
681
|
try:
|
|
628
682
|
# noinspection PyUnresolvedReferences
|
|
629
683
|
import cwltool # noqa
|
|
630
684
|
except ImportError:
|
|
631
|
-
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
|
+
)
|
|
688
|
+
else:
|
|
689
|
+
return test_item
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
def needs_wdl(test_item: MT) -> MT:
|
|
693
|
+
"""
|
|
694
|
+
Use as a decorator before test classes or methods to only run them if miniwdl is installed
|
|
695
|
+
and configured.
|
|
696
|
+
"""
|
|
697
|
+
test_item = _mark_test("wdl", test_item)
|
|
698
|
+
try:
|
|
699
|
+
# noinspection PyUnresolvedReferences
|
|
700
|
+
import WDL # noqa
|
|
701
|
+
except ImportError:
|
|
702
|
+
return unittest.skip("Install Toil with the 'wdl' extra to include this test.")(
|
|
703
|
+
test_item
|
|
704
|
+
)
|
|
632
705
|
else:
|
|
633
706
|
return test_item
|
|
634
707
|
|
|
@@ -637,40 +710,48 @@ def needs_server(test_item: MT) -> MT:
|
|
|
637
710
|
"""
|
|
638
711
|
Use as a decorator before test classes or methods to only run them if Connexion is installed.
|
|
639
712
|
"""
|
|
640
|
-
test_item = _mark_test(
|
|
713
|
+
test_item = _mark_test("server_mode", test_item)
|
|
641
714
|
try:
|
|
642
715
|
# noinspection PyUnresolvedReferences
|
|
643
716
|
import connexion
|
|
717
|
+
|
|
644
718
|
print(connexion.__file__) # keep this import from being removed.
|
|
645
719
|
except ImportError:
|
|
646
720
|
return unittest.skip(
|
|
647
|
-
"Install Toil with the 'server' extra to include this test."
|
|
721
|
+
"Install Toil with the 'server' extra to include this test."
|
|
722
|
+
)(test_item)
|
|
648
723
|
else:
|
|
649
724
|
return test_item
|
|
650
725
|
|
|
726
|
+
|
|
651
727
|
def needs_celery_broker(test_item: MT) -> MT:
|
|
652
728
|
"""
|
|
653
729
|
Use as a decorator before test classes or methods to run only if RabbitMQ is set up to take Celery jobs.
|
|
654
730
|
"""
|
|
655
|
-
test_item = _mark_test(
|
|
656
|
-
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)
|
|
657
735
|
return test_item
|
|
658
736
|
|
|
737
|
+
|
|
659
738
|
def needs_wes_server(test_item: MT) -> MT:
|
|
660
739
|
"""
|
|
661
740
|
Use as a decorator before test classes or methods to run only if a WES
|
|
662
741
|
server is available to run against.
|
|
663
742
|
"""
|
|
664
|
-
test_item = _mark_test(
|
|
743
|
+
test_item = _mark_test("wes_server", needs_online(test_item))
|
|
665
744
|
|
|
666
|
-
wes_url = os.environ.get(
|
|
745
|
+
wes_url = os.environ.get("TOIL_WES_ENDPOINT")
|
|
667
746
|
if not wes_url:
|
|
668
747
|
return unittest.skip(f"Set TOIL_WES_ENDPOINT to include this test")(test_item)
|
|
669
748
|
|
|
670
749
|
try:
|
|
671
750
|
urlopen(f"{wes_url}/ga4gh/wes/v1/service-info")
|
|
672
751
|
except (HTTPError, URLError) as e:
|
|
673
|
-
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
|
+
)
|
|
674
755
|
|
|
675
756
|
return test_item
|
|
676
757
|
|
|
@@ -680,10 +761,10 @@ def needs_local_appliance(test_item: MT) -> MT:
|
|
|
680
761
|
Use as a decorator before test classes or methods to only run them if
|
|
681
762
|
the Toil appliance Docker image is downloaded.
|
|
682
763
|
"""
|
|
683
|
-
test_item = _mark_test(
|
|
684
|
-
if os.getenv(
|
|
685
|
-
return unittest.skip(
|
|
686
|
-
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"):
|
|
687
768
|
return unittest.skip("Install docker to include this test.")(test_item)
|
|
688
769
|
|
|
689
770
|
try:
|
|
@@ -719,9 +800,9 @@ def needs_fetchable_appliance(test_item: MT) -> MT:
|
|
|
719
800
|
the Toil appliance Docker image is able to be downloaded from the Internet.
|
|
720
801
|
"""
|
|
721
802
|
|
|
722
|
-
test_item = _mark_test(
|
|
723
|
-
if os.getenv(
|
|
724
|
-
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)
|
|
725
806
|
try:
|
|
726
807
|
applianceSelf()
|
|
727
808
|
except ApplianceImageNotFound:
|
|
@@ -742,12 +823,12 @@ def integrative(test_item: MT) -> MT:
|
|
|
742
823
|
We define integration tests as A) involving other, non-Toil software components
|
|
743
824
|
that we develop and/or B) having a higher cost (time or money).
|
|
744
825
|
"""
|
|
745
|
-
test_item = _mark_test(
|
|
746
|
-
if os.getenv(
|
|
826
|
+
test_item = _mark_test("integrative", test_item)
|
|
827
|
+
if os.getenv("TOIL_TEST_INTEGRATIVE", "").lower() == "true":
|
|
747
828
|
return test_item
|
|
748
829
|
else:
|
|
749
830
|
return unittest.skip(
|
|
750
|
-
|
|
831
|
+
"Set TOIL_TEST_INTEGRATIVE=True to include this integration test, "
|
|
751
832
|
"or run `make integration_test_local` to run all integration tests."
|
|
752
833
|
)(test_item)
|
|
753
834
|
|
|
@@ -757,14 +838,14 @@ def slow(test_item: MT) -> MT:
|
|
|
757
838
|
Use this decorator to identify tests that are slow and not critical.
|
|
758
839
|
Skip if TOIL_TEST_QUICK is true.
|
|
759
840
|
"""
|
|
760
|
-
test_item = _mark_test(
|
|
761
|
-
if os.environ.get(
|
|
841
|
+
test_item = _mark_test("slow", test_item)
|
|
842
|
+
if os.environ.get("TOIL_TEST_QUICK", "").lower() != "true":
|
|
762
843
|
return test_item
|
|
763
844
|
else:
|
|
764
845
|
return unittest.skip('Skipped because TOIL_TEST_QUICK is "True"')(test_item)
|
|
765
846
|
|
|
766
847
|
|
|
767
|
-
methodNamePartRegex = re.compile(
|
|
848
|
+
methodNamePartRegex = re.compile("^[a-zA-Z_0-9]+$")
|
|
768
849
|
|
|
769
850
|
|
|
770
851
|
@contextmanager
|
|
@@ -776,7 +857,7 @@ def timeLimit(seconds: int) -> Generator[None, None, None]:
|
|
|
776
857
|
specified amount of time. See <http://stackoverflow.com/a/601168>.
|
|
777
858
|
|
|
778
859
|
:param seconds: maximum allowable time, in seconds
|
|
779
|
-
|
|
860
|
+
|
|
780
861
|
>>> import time
|
|
781
862
|
>>> with timeLimit(2):
|
|
782
863
|
... time.sleep(1)
|
|
@@ -787,9 +868,10 @@ def timeLimit(seconds: int) -> Generator[None, None, None]:
|
|
|
787
868
|
...
|
|
788
869
|
RuntimeError: Timed out
|
|
789
870
|
"""
|
|
871
|
+
|
|
790
872
|
# noinspection PyUnusedLocal
|
|
791
873
|
def signal_handler(signum: int, frame: Any) -> None:
|
|
792
|
-
raise RuntimeError(
|
|
874
|
+
raise RuntimeError("Timed out")
|
|
793
875
|
|
|
794
876
|
signal.signal(signal.SIGALRM, signal_handler)
|
|
795
877
|
signal.alarm(seconds)
|
|
@@ -854,6 +936,7 @@ def make_tests(generalMethod, targetClass, **kwargs):
|
|
|
854
936
|
False
|
|
855
937
|
|
|
856
938
|
"""
|
|
939
|
+
|
|
857
940
|
def permuteIntoLeft(left, rParamName, right):
|
|
858
941
|
"""
|
|
859
942
|
Permutes values in right dictionary into each parameter: value dict pair in the left
|
|
@@ -879,9 +962,11 @@ def make_tests(generalMethod, targetClass, **kwargs):
|
|
|
879
962
|
"""
|
|
880
963
|
for prmValName, lDict in list(left.items()):
|
|
881
964
|
for rValName, rVal in list(right.items()):
|
|
882
|
-
nextPrmVal =
|
|
965
|
+
nextPrmVal = f"__{rParamName}_{rValName.lower()}"
|
|
883
966
|
if methodNamePartRegex.match(nextPrmVal) is None:
|
|
884
|
-
raise RuntimeError(
|
|
967
|
+
raise RuntimeError(
|
|
968
|
+
"The name '%s' cannot be used in a method name" % pvName
|
|
969
|
+
)
|
|
885
970
|
aggDict = dict(lDict)
|
|
886
971
|
aggDict[rParamName] = rVal
|
|
887
972
|
left[prmValName + nextPrmVal] = aggDict
|
|
@@ -896,7 +981,7 @@ def make_tests(generalMethod, targetClass, **kwargs):
|
|
|
896
981
|
else:
|
|
897
982
|
return generalMethod(self)
|
|
898
983
|
|
|
899
|
-
methodName = f
|
|
984
|
+
methodName = f"test_{generalMethod.__name__}{prmNames}"
|
|
900
985
|
|
|
901
986
|
setattr(targetClass, methodName, fx)
|
|
902
987
|
|
|
@@ -909,9 +994,11 @@ def make_tests(generalMethod, targetClass, **kwargs):
|
|
|
909
994
|
left = {}
|
|
910
995
|
prmName, vals = sortedKwargs.pop()
|
|
911
996
|
for valName, val in list(vals.items()):
|
|
912
|
-
pvName = f
|
|
997
|
+
pvName = f"__{prmName}_{valName.lower()}"
|
|
913
998
|
if methodNamePartRegex.match(pvName) is None:
|
|
914
|
-
raise RuntimeError(
|
|
999
|
+
raise RuntimeError(
|
|
1000
|
+
"The name '%s' cannot be used in a method name" % pvName
|
|
1001
|
+
)
|
|
915
1002
|
left[pvName] = {prmName: val}
|
|
916
1003
|
|
|
917
1004
|
# get cartesian product
|
|
@@ -937,9 +1024,9 @@ class ApplianceTestSupport(ToilTest):
|
|
|
937
1024
|
|
|
938
1025
|
@contextmanager
|
|
939
1026
|
def _applianceCluster(
|
|
940
|
-
self, mounts:
|
|
1027
|
+
self, mounts: dict[str, str], numCores: Optional[int] = None
|
|
941
1028
|
) -> Generator[
|
|
942
|
-
|
|
1029
|
+
tuple["ApplianceTestSupport.LeaderThread", "ApplianceTestSupport.WorkerThread"],
|
|
943
1030
|
None,
|
|
944
1031
|
None,
|
|
945
1032
|
]:
|
|
@@ -969,10 +1056,10 @@ class ApplianceTestSupport(ToilTest):
|
|
|
969
1056
|
class Appliance(ExceptionalThread, metaclass=ABCMeta):
|
|
970
1057
|
@abstractmethod
|
|
971
1058
|
def _getRole(self) -> str:
|
|
972
|
-
return
|
|
1059
|
+
return "leader"
|
|
973
1060
|
|
|
974
1061
|
@abstractmethod
|
|
975
|
-
def _containerCommand(self) ->
|
|
1062
|
+
def _containerCommand(self) -> list[str]:
|
|
976
1063
|
pass
|
|
977
1064
|
|
|
978
1065
|
@abstractmethod
|
|
@@ -985,7 +1072,7 @@ class ApplianceTestSupport(ToilTest):
|
|
|
985
1072
|
def __init__(
|
|
986
1073
|
self,
|
|
987
1074
|
outer: "ApplianceTestSupport",
|
|
988
|
-
mounts:
|
|
1075
|
+
mounts: dict[str, str],
|
|
989
1076
|
cleanMounts: bool = False,
|
|
990
1077
|
) -> None:
|
|
991
1078
|
assert all(
|
|
@@ -1002,15 +1089,20 @@ class ApplianceTestSupport(ToilTest):
|
|
|
1002
1089
|
with self.lock:
|
|
1003
1090
|
image = applianceSelf()
|
|
1004
1091
|
# Omitting --rm, it's unreliable, see https://github.com/docker/docker/issues/16575
|
|
1005
|
-
args = list(
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
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)
|
|
1014
1106
|
self.popen = subprocess.Popen(args)
|
|
1015
1107
|
self.start()
|
|
1016
1108
|
self.__wait_running()
|
|
@@ -1018,17 +1110,17 @@ class ApplianceTestSupport(ToilTest):
|
|
|
1018
1110
|
|
|
1019
1111
|
# noinspection PyUnusedLocal
|
|
1020
1112
|
def __exit__(
|
|
1021
|
-
self, exc_type:
|
|
1113
|
+
self, exc_type: type[BaseException], exc_val: Exception, exc_tb: Any
|
|
1022
1114
|
) -> Literal[False]:
|
|
1023
1115
|
try:
|
|
1024
1116
|
try:
|
|
1025
|
-
self.outer._run(
|
|
1117
|
+
self.outer._run("docker", "stop", self.containerName)
|
|
1026
1118
|
self.join()
|
|
1027
1119
|
finally:
|
|
1028
1120
|
if self.cleanMounts:
|
|
1029
1121
|
self.__cleanMounts()
|
|
1030
1122
|
finally:
|
|
1031
|
-
self.outer._run(
|
|
1123
|
+
self.outer._run("docker", "rm", "-f", self.containerName)
|
|
1032
1124
|
return False # don't swallow exception
|
|
1033
1125
|
|
|
1034
1126
|
def __wait_running(self) -> None:
|
|
@@ -1055,7 +1147,7 @@ class ApplianceTestSupport(ToilTest):
|
|
|
1055
1147
|
except subprocess.CalledProcessError:
|
|
1056
1148
|
pass
|
|
1057
1149
|
else:
|
|
1058
|
-
if
|
|
1150
|
+
if "true" == running:
|
|
1059
1151
|
break
|
|
1060
1152
|
time.sleep(1)
|
|
1061
1153
|
|
|
@@ -1068,30 +1160,33 @@ class ApplianceTestSupport(ToilTest):
|
|
|
1068
1160
|
was stopped, otherwise the running container might still be writing files.
|
|
1069
1161
|
"""
|
|
1070
1162
|
# Delete all files within each mounted directory, but not the directory itself.
|
|
1071
|
-
cmd =
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
self.outer._run(
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
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
|
+
)
|
|
1080
1175
|
|
|
1081
1176
|
def tryRun(self) -> None:
|
|
1082
1177
|
assert self.popen
|
|
1083
1178
|
self.popen.wait()
|
|
1084
|
-
logger.info(
|
|
1179
|
+
logger.info("Exiting %s", self.__class__.__name__)
|
|
1085
1180
|
|
|
1086
1181
|
def runOnAppliance(self, *args: str, **kwargs: Any) -> None:
|
|
1087
1182
|
# Check if thread is still alive. Note that ExceptionalThread.join raises the
|
|
1088
1183
|
# exception that occurred in the thread.
|
|
1089
1184
|
self.join(timeout=0)
|
|
1090
1185
|
# noinspection PyProtectedMember
|
|
1091
|
-
self.outer._run(
|
|
1186
|
+
self.outer._run("docker", "exec", "-i", self.containerName, *args, **kwargs)
|
|
1092
1187
|
|
|
1093
1188
|
def writeToAppliance(self, path: str, contents: Any) -> None:
|
|
1094
|
-
self.runOnAppliance(
|
|
1189
|
+
self.runOnAppliance("tee", path, input=contents)
|
|
1095
1190
|
|
|
1096
1191
|
def deployScript(
|
|
1097
1192
|
self, path: str, packagePath: str, script: Union[str, Callable[..., Any]]
|
|
@@ -1114,42 +1209,46 @@ class ApplianceTestSupport(ToilTest):
|
|
|
1114
1209
|
packagePath_list = packagePath.split(".")
|
|
1115
1210
|
packages, module = packagePath_list[:-1], packagePath_list[-1]
|
|
1116
1211
|
for package in packages:
|
|
1117
|
-
path +=
|
|
1118
|
-
self.runOnAppliance(
|
|
1119
|
-
self.writeToAppliance(path +
|
|
1120
|
-
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)
|
|
1121
1216
|
|
|
1122
1217
|
class LeaderThread(Appliance):
|
|
1123
1218
|
def _entryPoint(self) -> str:
|
|
1124
|
-
return
|
|
1219
|
+
return "mesos-master"
|
|
1125
1220
|
|
|
1126
1221
|
def _getRole(self) -> str:
|
|
1127
|
-
return
|
|
1222
|
+
return "leader"
|
|
1128
1223
|
|
|
1129
|
-
def _containerCommand(self) ->
|
|
1130
|
-
return [
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
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
|
+
]
|
|
1134
1231
|
|
|
1135
1232
|
class WorkerThread(Appliance):
|
|
1136
1233
|
def __init__(
|
|
1137
|
-
self, outer: "ApplianceTestSupport", mounts:
|
|
1234
|
+
self, outer: "ApplianceTestSupport", mounts: dict[str, str], numCores: int
|
|
1138
1235
|
) -> None:
|
|
1139
1236
|
self.numCores = numCores
|
|
1140
1237
|
super().__init__(outer, mounts)
|
|
1141
1238
|
|
|
1142
1239
|
def _entryPoint(self) -> str:
|
|
1143
|
-
return
|
|
1240
|
+
return "mesos-agent"
|
|
1144
1241
|
|
|
1145
1242
|
def _getRole(self) -> str:
|
|
1146
|
-
return
|
|
1147
|
-
|
|
1148
|
-
def _containerCommand(self) ->
|
|
1149
|
-
return [
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
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
|
+
]
|