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/__init__.py
CHANGED
|
@@ -11,21 +11,19 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
-
import errno
|
|
15
14
|
import logging
|
|
16
15
|
import os
|
|
17
16
|
import re
|
|
18
17
|
import socket
|
|
19
18
|
import sys
|
|
20
|
-
import time
|
|
21
19
|
from datetime import datetime
|
|
22
|
-
from typing import TYPE_CHECKING, Optional
|
|
20
|
+
from typing import TYPE_CHECKING, Optional
|
|
23
21
|
|
|
24
22
|
import requests
|
|
25
23
|
|
|
26
24
|
from docker.errors import ImageNotFound
|
|
27
25
|
from toil.lib.memoize import memoize
|
|
28
|
-
from toil.lib.retry import retry
|
|
26
|
+
from toil.lib.retry import retry as retry
|
|
29
27
|
from toil.version import currentCommit
|
|
30
28
|
|
|
31
29
|
if TYPE_CHECKING:
|
|
@@ -43,15 +41,15 @@ def which(cmd, mode=os.F_OK | os.X_OK, path=None) -> Optional[str]:
|
|
|
43
41
|
`mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result
|
|
44
42
|
of os.environ.get("PATH"), or can be overridden with a custom search
|
|
45
43
|
path.
|
|
46
|
-
|
|
44
|
+
|
|
47
45
|
:returns: The path found, or None.
|
|
48
46
|
"""
|
|
47
|
+
|
|
49
48
|
# Check that a given file can be accessed with the correct mode.
|
|
50
49
|
# Additionally check that `file` is not a directory, as on Windows
|
|
51
50
|
# directories pass the os.access check.
|
|
52
51
|
def _access_check(fn, mode):
|
|
53
|
-
return
|
|
54
|
-
and not os.path.isdir(fn))
|
|
52
|
+
return os.path.exists(fn) and os.access(fn, mode) and not os.path.isdir(fn)
|
|
55
53
|
|
|
56
54
|
# If we're given a path with a directory part, look it up directly rather
|
|
57
55
|
# than referring to PATH directories. This includes checking relative to the
|
|
@@ -106,17 +104,19 @@ def toilPackageDirPath() -> str:
|
|
|
106
104
|
The return value is guaranteed to end in '/toil'.
|
|
107
105
|
"""
|
|
108
106
|
result = os.path.dirname(os.path.realpath(__file__))
|
|
109
|
-
if not result.endswith(
|
|
107
|
+
if not result.endswith("/toil"):
|
|
110
108
|
raise RuntimeError("The top-level toil package is not named Toil.")
|
|
111
109
|
return result
|
|
112
110
|
|
|
113
111
|
|
|
114
112
|
def inVirtualEnv() -> bool:
|
|
115
113
|
"""Test if we are inside a virtualenv or Conda virtual environment."""
|
|
116
|
-
return (
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
114
|
+
return (
|
|
115
|
+
"VIRTUAL_ENV" in os.environ
|
|
116
|
+
or "CONDA_DEFAULT_ENV" in os.environ
|
|
117
|
+
or hasattr(sys, "real_prefix")
|
|
118
|
+
or (hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix)
|
|
119
|
+
)
|
|
120
120
|
|
|
121
121
|
|
|
122
122
|
def resolveEntryPoint(entryPoint: str) -> str:
|
|
@@ -125,7 +125,7 @@ def resolveEntryPoint(entryPoint: str) -> str:
|
|
|
125
125
|
|
|
126
126
|
:returns: The path found, which may be an absolute or a relative path.
|
|
127
127
|
"""
|
|
128
|
-
if os.environ.get("TOIL_CHECK_ENV", None) ==
|
|
128
|
+
if os.environ.get("TOIL_CHECK_ENV", None) == "True" and inVirtualEnv():
|
|
129
129
|
path = os.path.join(os.path.dirname(sys.executable), entryPoint)
|
|
130
130
|
# Inside a virtualenv we try to use absolute paths to the entrypoints.
|
|
131
131
|
if os.path.isfile(path):
|
|
@@ -134,7 +134,9 @@ def resolveEntryPoint(entryPoint: str) -> str:
|
|
|
134
134
|
# if Toil is installed in a virtualenv on the leader, it must be installed in
|
|
135
135
|
# a virtualenv located at the same path on each worker as well.
|
|
136
136
|
if not os.access(path, os.X_OK):
|
|
137
|
-
raise RuntimeError(
|
|
137
|
+
raise RuntimeError(
|
|
138
|
+
"Cannot access the Toil virtualenv. If installed in a virtualenv on a cluster, make sure that the virtualenv path is the same for the leader and workers."
|
|
139
|
+
)
|
|
138
140
|
return path
|
|
139
141
|
# Otherwise, we aren't in a virtualenv, or we're in a virtualenv but Toil
|
|
140
142
|
# came in via --system-site-packages, or we think the virtualenv might not
|
|
@@ -154,10 +156,15 @@ def physicalMemory() -> int:
|
|
|
154
156
|
True
|
|
155
157
|
"""
|
|
156
158
|
try:
|
|
157
|
-
return os.sysconf(
|
|
159
|
+
return os.sysconf("SC_PAGE_SIZE") * os.sysconf("SC_PHYS_PAGES")
|
|
158
160
|
except ValueError:
|
|
159
161
|
import subprocess
|
|
160
|
-
|
|
162
|
+
|
|
163
|
+
return int(
|
|
164
|
+
subprocess.check_output(["sysctl", "-n", "hw.memsize"])
|
|
165
|
+
.decode("utf-8")
|
|
166
|
+
.strip()
|
|
167
|
+
)
|
|
161
168
|
|
|
162
169
|
|
|
163
170
|
def physicalDisk(directory: str) -> int:
|
|
@@ -181,15 +188,22 @@ def applianceSelf(forceDockerAppliance: bool = False) -> str:
|
|
|
181
188
|
Setting TOIL_APPLIANCE_SELF will not be necessary in most cases.
|
|
182
189
|
"""
|
|
183
190
|
import toil.version
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
191
|
+
|
|
192
|
+
registry = lookupEnvVar(
|
|
193
|
+
name="docker registry",
|
|
194
|
+
envName="TOIL_DOCKER_REGISTRY",
|
|
195
|
+
defaultValue=toil.version.dockerRegistry,
|
|
196
|
+
)
|
|
197
|
+
name = lookupEnvVar(
|
|
198
|
+
name="docker name",
|
|
199
|
+
envName="TOIL_DOCKER_NAME",
|
|
200
|
+
defaultValue=toil.version.dockerName,
|
|
201
|
+
)
|
|
202
|
+
appliance = lookupEnvVar(
|
|
203
|
+
name="docker appliance",
|
|
204
|
+
envName="TOIL_APPLIANCE_SELF",
|
|
205
|
+
defaultValue=registry + "/" + name + ":" + toil.version.dockerTag,
|
|
206
|
+
)
|
|
193
207
|
|
|
194
208
|
checkDockerSchema(appliance)
|
|
195
209
|
|
|
@@ -211,9 +225,11 @@ def customDockerInitCmd() -> str:
|
|
|
211
225
|
|
|
212
226
|
:returns: The custom command, or an empty string is returned if the environment variable is not set.
|
|
213
227
|
"""
|
|
214
|
-
command = lookupEnvVar(
|
|
215
|
-
|
|
216
|
-
|
|
228
|
+
command = lookupEnvVar(
|
|
229
|
+
name="user-defined custom docker init command",
|
|
230
|
+
envName="TOIL_CUSTOM_DOCKER_INIT_COMMAND",
|
|
231
|
+
defaultValue="",
|
|
232
|
+
)
|
|
217
233
|
_check_custom_bash_cmd(command)
|
|
218
234
|
return command.replace("'", "'\\''") # Ensure any single quotes are escaped.
|
|
219
235
|
|
|
@@ -224,24 +240,28 @@ def customInitCmd() -> str:
|
|
|
224
240
|
|
|
225
241
|
The custom init command is run prior to running Toil appliance itself in workers and/or the
|
|
226
242
|
primary node (i.e. this is run one stage before ``TOIL_CUSTOM_DOCKER_INIT_COMMAND``).
|
|
227
|
-
|
|
243
|
+
|
|
228
244
|
This can be useful for doing any custom initialization on instances (e.g. authenticating to
|
|
229
245
|
private docker registries). Any single quotes are escaped and the command cannot contain a
|
|
230
246
|
set of blacklisted chars (newline or tab).
|
|
231
247
|
|
|
232
248
|
returns: the custom command or n empty string is returned if the environment variable is not set.
|
|
233
249
|
"""
|
|
234
|
-
command = lookupEnvVar(
|
|
235
|
-
|
|
236
|
-
|
|
250
|
+
command = lookupEnvVar(
|
|
251
|
+
name="user-defined custom init command",
|
|
252
|
+
envName="TOIL_CUSTOM_INIT_COMMAND",
|
|
253
|
+
defaultValue="",
|
|
254
|
+
)
|
|
237
255
|
_check_custom_bash_cmd(command)
|
|
238
256
|
return command.replace("'", "'\\''") # Ensure any single quotes are escaped.
|
|
239
257
|
|
|
240
258
|
|
|
241
259
|
def _check_custom_bash_cmd(cmd_str):
|
|
242
260
|
"""Ensure that the Bash command doesn't contain invalid characters."""
|
|
243
|
-
if re.search(r
|
|
244
|
-
raise RuntimeError(
|
|
261
|
+
if re.search(r"[\n\r\t]", cmd_str):
|
|
262
|
+
raise RuntimeError(
|
|
263
|
+
f'"{cmd_str}" contains invalid characters (newline and/or tab).'
|
|
264
|
+
)
|
|
245
265
|
|
|
246
266
|
|
|
247
267
|
def lookupEnvVar(name: str, envName: str, defaultValue: str) -> str:
|
|
@@ -256,10 +276,14 @@ def lookupEnvVar(name: str, envName: str, defaultValue: str) -> str:
|
|
|
256
276
|
try:
|
|
257
277
|
value = os.environ[envName]
|
|
258
278
|
except KeyError:
|
|
259
|
-
log.info(
|
|
279
|
+
log.info(
|
|
280
|
+
"Using default %s of %s as %s is not set.", name, defaultValue, envName
|
|
281
|
+
)
|
|
260
282
|
return defaultValue
|
|
261
283
|
else:
|
|
262
|
-
log.info(
|
|
284
|
+
log.info(
|
|
285
|
+
"Overriding %s of %s with %s from %s.", name, defaultValue, value, envName
|
|
286
|
+
)
|
|
263
287
|
return value
|
|
264
288
|
|
|
265
289
|
|
|
@@ -278,14 +302,20 @@ def checkDockerImageExists(appliance: str) -> str:
|
|
|
278
302
|
return appliance
|
|
279
303
|
registryName, imageName, tag = parseDockerAppliance(appliance)
|
|
280
304
|
|
|
281
|
-
if registryName ==
|
|
282
|
-
return requestCheckDockerIo(
|
|
305
|
+
if registryName == "docker.io":
|
|
306
|
+
return requestCheckDockerIo(
|
|
307
|
+
origAppliance=appliance, imageName=imageName, tag=tag
|
|
308
|
+
)
|
|
283
309
|
else:
|
|
284
|
-
return requestCheckRegularDocker(
|
|
285
|
-
|
|
310
|
+
return requestCheckRegularDocker(
|
|
311
|
+
origAppliance=appliance,
|
|
312
|
+
registryName=registryName,
|
|
313
|
+
imageName=imageName,
|
|
314
|
+
tag=tag,
|
|
315
|
+
)
|
|
286
316
|
|
|
287
317
|
|
|
288
|
-
def parseDockerAppliance(appliance: str) ->
|
|
318
|
+
def parseDockerAppliance(appliance: str) -> tuple[str, str, str]:
|
|
289
319
|
"""
|
|
290
320
|
Derive parsed registry, image reference, and tag from a docker image string.
|
|
291
321
|
|
|
@@ -303,21 +333,21 @@ def parseDockerAppliance(appliance: str) -> Tuple[str, str, str]:
|
|
|
303
333
|
appliance = appliance.lower()
|
|
304
334
|
|
|
305
335
|
# get the tag
|
|
306
|
-
if
|
|
307
|
-
tag = appliance.split(
|
|
308
|
-
appliance = appliance[
|
|
336
|
+
if ":" in appliance:
|
|
337
|
+
tag = appliance.split(":")[-1]
|
|
338
|
+
appliance = appliance[: -(len(":" + tag))] # remove only the tag
|
|
309
339
|
else:
|
|
310
340
|
# default to 'latest' if no tag is specified
|
|
311
|
-
tag =
|
|
341
|
+
tag = "latest"
|
|
312
342
|
|
|
313
343
|
# get the registry and image
|
|
314
|
-
registryName =
|
|
344
|
+
registryName = "docker.io" # default if not specified
|
|
315
345
|
imageName = appliance # will be true if not specified
|
|
316
|
-
if
|
|
317
|
-
registryName = appliance.split(
|
|
318
|
-
imageName = appliance[len(registryName):]
|
|
319
|
-
registryName = registryName.strip(
|
|
320
|
-
imageName = imageName.strip(
|
|
346
|
+
if "/" in appliance and "." in appliance.split("/")[0]:
|
|
347
|
+
registryName = appliance.split("/")[0]
|
|
348
|
+
imageName = appliance[len(registryName) :]
|
|
349
|
+
registryName = registryName.strip("/")
|
|
350
|
+
imageName = imageName.strip("/")
|
|
321
351
|
|
|
322
352
|
return registryName, imageName, tag
|
|
323
353
|
|
|
@@ -325,12 +355,14 @@ def parseDockerAppliance(appliance: str) -> Tuple[str, str, str]:
|
|
|
325
355
|
def checkDockerSchema(appliance):
|
|
326
356
|
if not appliance:
|
|
327
357
|
raise ImageNotFound("No docker image specified.")
|
|
328
|
-
elif
|
|
329
|
-
raise ImageNotFound(
|
|
330
|
-
|
|
358
|
+
elif "://" in appliance:
|
|
359
|
+
raise ImageNotFound(
|
|
360
|
+
"Docker images cannot contain a schema (such as '://'): %s" "" % appliance
|
|
361
|
+
)
|
|
331
362
|
elif len(appliance) > 256:
|
|
332
|
-
raise ImageNotFound(
|
|
333
|
-
|
|
363
|
+
raise ImageNotFound(
|
|
364
|
+
"Docker image must be less than 256 chars: %s" "" % appliance
|
|
365
|
+
)
|
|
334
366
|
|
|
335
367
|
|
|
336
368
|
class ApplianceImageNotFound(ImageNotFound):
|
|
@@ -345,22 +377,28 @@ class ApplianceImageNotFound(ImageNotFound):
|
|
|
345
377
|
"""
|
|
346
378
|
|
|
347
379
|
def __init__(self, origAppliance, url, statusCode):
|
|
348
|
-
msg = (
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
380
|
+
msg = (
|
|
381
|
+
"The docker image that TOIL_APPLIANCE_SELF specifies (%s) produced "
|
|
382
|
+
"a nonfunctional manifest URL (%s). The HTTP status returned was %s. "
|
|
383
|
+
"The specifier is most likely unsupported or malformed. "
|
|
384
|
+
"Please supply a docker image with the format: "
|
|
385
|
+
"'<websitehost>.io/<repo_path>:<tag>' or '<repo_path>:<tag>' "
|
|
386
|
+
"(for official docker.io images). Examples: "
|
|
387
|
+
"'quay.io/ucsc_cgl/toil:latest', 'ubuntu:latest', or "
|
|
388
|
+
"'broadinstitute/genomes-in-the-cloud:2.0.0'."
|
|
389
|
+
"" % (origAppliance, url, str(statusCode))
|
|
390
|
+
)
|
|
357
391
|
super().__init__(msg)
|
|
358
392
|
|
|
393
|
+
|
|
359
394
|
# Cache images we know exist so we don't have to ask the registry about them
|
|
360
395
|
# all the time.
|
|
361
396
|
KNOWN_EXTANT_IMAGES = set()
|
|
362
397
|
|
|
363
|
-
|
|
398
|
+
|
|
399
|
+
def requestCheckRegularDocker(
|
|
400
|
+
origAppliance: str, registryName: str, imageName: str, tag: str
|
|
401
|
+
) -> bool:
|
|
364
402
|
"""
|
|
365
403
|
Check if an image exists using the requests library.
|
|
366
404
|
|
|
@@ -384,8 +422,9 @@ def requestCheckRegularDocker(origAppliance: str, registryName: str, imageName:
|
|
|
384
422
|
# Check the cache first
|
|
385
423
|
return origAppliance
|
|
386
424
|
|
|
387
|
-
ioURL =
|
|
388
|
-
|
|
425
|
+
ioURL = "https://{webhost}/v2/{pathName}/manifests/{tag}" "".format(
|
|
426
|
+
webhost=registryName, pathName=imageName, tag=tag
|
|
427
|
+
)
|
|
389
428
|
response = requests.head(ioURL)
|
|
390
429
|
if not response.ok:
|
|
391
430
|
raise ApplianceImageNotFound(origAppliance, ioURL, response.status_code)
|
|
@@ -412,17 +451,20 @@ def requestCheckDockerIo(origAppliance: str, imageName: str, tag: str) -> bool:
|
|
|
412
451
|
return origAppliance
|
|
413
452
|
|
|
414
453
|
# only official images like 'busybox' or 'ubuntu'
|
|
415
|
-
if
|
|
416
|
-
imageName =
|
|
454
|
+
if "/" not in imageName:
|
|
455
|
+
imageName = "library/" + imageName
|
|
417
456
|
|
|
418
|
-
token_url =
|
|
419
|
-
repo=imageName
|
|
420
|
-
|
|
457
|
+
token_url = "https://auth.docker.io/token?service=registry.docker.io&scope=repository:{repo}:pull".format(
|
|
458
|
+
repo=imageName
|
|
459
|
+
)
|
|
460
|
+
requests_url = f"https://registry-1.docker.io/v2/{imageName}/manifests/{tag}"
|
|
421
461
|
|
|
422
462
|
token = requests.get(token_url)
|
|
423
463
|
jsonToken = token.json()
|
|
424
464
|
bearer = jsonToken["token"]
|
|
425
|
-
response = requests.head(
|
|
465
|
+
response = requests.head(
|
|
466
|
+
requests_url, headers={"Authorization": f"Bearer {bearer}"}
|
|
467
|
+
)
|
|
426
468
|
if not response.ok:
|
|
427
469
|
raise ApplianceImageNotFound(origAppliance, requests_url, response.status_code)
|
|
428
470
|
else:
|
|
@@ -434,21 +476,18 @@ def logProcessContext(config: "Config") -> None:
|
|
|
434
476
|
# toil.version.version (string) cannot be imported at top level because it conflicts with
|
|
435
477
|
# toil.version (module) and Sphinx doesn't like that.
|
|
436
478
|
from toil.version import version
|
|
479
|
+
|
|
437
480
|
log.info("Running Toil version %s on host %s.", version, socket.gethostname())
|
|
438
481
|
log.debug("Configuration: %s", config.__dict__)
|
|
439
482
|
|
|
440
483
|
|
|
441
484
|
try:
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
cache_path = '~/.cache/aws/cached_temporary_credentials'
|
|
448
|
-
datetime_format = "%Y-%m-%dT%H:%M:%SZ" # incidentally the same as the format used by AWS
|
|
485
|
+
cache_path = "~/.cache/aws/cached_temporary_credentials"
|
|
486
|
+
datetime_format = (
|
|
487
|
+
"%Y-%m-%dT%H:%M:%SZ" # incidentally the same as the format used by AWS
|
|
488
|
+
)
|
|
449
489
|
log = logging.getLogger(__name__)
|
|
450
490
|
|
|
451
|
-
|
|
452
491
|
# But in addition to our manual cache, we also are going to turn on boto3's
|
|
453
492
|
# new built-in caching layer.
|
|
454
493
|
|
|
@@ -461,7 +500,6 @@ try:
|
|
|
461
500
|
"""
|
|
462
501
|
return dt.strftime(datetime_format)
|
|
463
502
|
|
|
464
|
-
|
|
465
503
|
def str_to_datetime(s):
|
|
466
504
|
"""
|
|
467
505
|
Convert a string, explicitly UTC into a naive (implicitly UTC) datetime object.
|
toil/batchSystems/__init__.py
CHANGED
|
@@ -18,6 +18,7 @@ class DeadlockException(Exception):
|
|
|
18
18
|
Exception thrown by the Leader or BatchSystem when a deadlock is encountered due to insufficient
|
|
19
19
|
resources to run the workflow
|
|
20
20
|
"""
|
|
21
|
+
|
|
21
22
|
def __init__(self, msg):
|
|
22
23
|
self.msg = f"Deadlock encountered: {msg}"
|
|
23
24
|
super().__init__()
|