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/lib/docker.py
CHANGED
|
@@ -17,15 +17,17 @@ import os
|
|
|
17
17
|
import re
|
|
18
18
|
import struct
|
|
19
19
|
from shlex import quote
|
|
20
|
-
from typing import
|
|
20
|
+
from typing import Optional
|
|
21
21
|
|
|
22
22
|
import requests
|
|
23
23
|
|
|
24
24
|
import docker
|
|
25
|
-
from docker.errors import (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
from docker.errors import (
|
|
26
|
+
ContainerError,
|
|
27
|
+
ImageNotFound,
|
|
28
|
+
NotFound,
|
|
29
|
+
create_api_error_from_http_exception,
|
|
30
|
+
)
|
|
29
31
|
from docker.utils.socket import consume_socket_output, demux_adaptor
|
|
30
32
|
from toil.lib.accelerators import get_host_accelerator_numbers
|
|
31
33
|
|
|
@@ -37,42 +39,49 @@ RM = 2
|
|
|
37
39
|
|
|
38
40
|
|
|
39
41
|
def dockerCheckOutput(*args, **kwargs):
|
|
40
|
-
raise RuntimeError(
|
|
41
|
-
|
|
42
|
+
raise RuntimeError(
|
|
43
|
+
"dockerCheckOutput() using subprocess.check_output() has been removed, "
|
|
44
|
+
"please switch to apiDockerCall()."
|
|
45
|
+
)
|
|
42
46
|
|
|
43
47
|
|
|
44
48
|
def dockerCall(*args, **kwargs):
|
|
45
|
-
raise RuntimeError(
|
|
46
|
-
|
|
49
|
+
raise RuntimeError(
|
|
50
|
+
"dockerCall() using subprocess.check_output() has been removed, "
|
|
51
|
+
"please switch to apiDockerCall()."
|
|
52
|
+
)
|
|
47
53
|
|
|
48
54
|
|
|
49
55
|
def subprocessDockerCall(*args, **kwargs):
|
|
50
|
-
raise RuntimeError(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
56
|
+
raise RuntimeError(
|
|
57
|
+
"subprocessDockerCall() has been removed, " "please switch to apiDockerCall()."
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def apiDockerCall(
|
|
62
|
+
job,
|
|
63
|
+
image,
|
|
64
|
+
parameters=None,
|
|
65
|
+
deferParam=None,
|
|
66
|
+
volumes=None,
|
|
67
|
+
working_dir=None,
|
|
68
|
+
containerName=None,
|
|
69
|
+
entrypoint=None,
|
|
70
|
+
detach=False,
|
|
71
|
+
log_config=None,
|
|
72
|
+
auto_remove=None,
|
|
73
|
+
remove=False,
|
|
74
|
+
user=None,
|
|
75
|
+
environment=None,
|
|
76
|
+
stdout=None,
|
|
77
|
+
stderr=False,
|
|
78
|
+
stream=False,
|
|
79
|
+
demux=False,
|
|
80
|
+
streamfile=None,
|
|
81
|
+
accelerators: Optional[list[int]] = None,
|
|
82
|
+
timeout=365 * 24 * 60 * 60,
|
|
83
|
+
**kwargs,
|
|
84
|
+
):
|
|
76
85
|
"""
|
|
77
86
|
A toil wrapper for the python docker API.
|
|
78
87
|
|
|
@@ -184,7 +193,7 @@ def apiDockerCall(job,
|
|
|
184
193
|
working_dir = os.getcwd()
|
|
185
194
|
|
|
186
195
|
if volumes is None:
|
|
187
|
-
volumes = {working_dir: {
|
|
196
|
+
volumes = {working_dir: {"bind": "/data", "mode": "rw"}}
|
|
188
197
|
|
|
189
198
|
for volume in volumes:
|
|
190
199
|
if not os.path.exists(volume):
|
|
@@ -197,11 +206,11 @@ def apiDockerCall(job,
|
|
|
197
206
|
# and chain with pipes.
|
|
198
207
|
if len(parameters) > 0 and type(parameters[0]) is list:
|
|
199
208
|
if entrypoint is None:
|
|
200
|
-
entrypoint = [
|
|
201
|
-
chain_params =
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
command =
|
|
209
|
+
entrypoint = ["/bin/bash", "-c"]
|
|
210
|
+
chain_params = [
|
|
211
|
+
" ".join(quote(arg) for arg in command) for command in parameters
|
|
212
|
+
]
|
|
213
|
+
command = " | ".join(chain_params)
|
|
205
214
|
pipe_prefix = "set -eo pipefail && "
|
|
206
215
|
command = [pipe_prefix + command]
|
|
207
216
|
logger.debug("Calling docker with: " + repr(command))
|
|
@@ -213,7 +222,7 @@ def apiDockerCall(job,
|
|
|
213
222
|
# practice:
|
|
214
223
|
# http://docker-py.readthedocs.io/en/stable/containers.html
|
|
215
224
|
elif len(parameters) > 0 and type(parameters) is list:
|
|
216
|
-
command =
|
|
225
|
+
command = " ".join(quote(arg) for arg in parameters)
|
|
217
226
|
logger.debug("Calling docker with: " + repr(command))
|
|
218
227
|
|
|
219
228
|
# If the 'parameters' lists are empty, they are respecified as None, which
|
|
@@ -226,9 +235,9 @@ def apiDockerCall(job,
|
|
|
226
235
|
|
|
227
236
|
# Ensure the user has passed a valid value for deferParam
|
|
228
237
|
if deferParam not in (None, FORGO, STOP, RM):
|
|
229
|
-
raise RuntimeError(
|
|
238
|
+
raise RuntimeError("Please provide a valid value for deferParam.")
|
|
230
239
|
|
|
231
|
-
client = docker.from_env(version=
|
|
240
|
+
client = docker.from_env(version="auto", timeout=timeout)
|
|
232
241
|
|
|
233
242
|
if deferParam is None:
|
|
234
243
|
deferParam = RM
|
|
@@ -263,8 +272,7 @@ def apiDockerCall(job,
|
|
|
263
272
|
# TODO: Here we assume that the host accelerators are all GPUs
|
|
264
273
|
device_requests.append(
|
|
265
274
|
docker.types.DeviceRequest(
|
|
266
|
-
device_ids=[
|
|
267
|
-
capabilities=[['gpu']]
|
|
275
|
+
device_ids=[",".join(host_accelerators)], capabilities=[["gpu"]]
|
|
268
276
|
)
|
|
269
277
|
)
|
|
270
278
|
|
|
@@ -275,24 +283,26 @@ def apiDockerCall(job,
|
|
|
275
283
|
# 'hello world\n'
|
|
276
284
|
if stdout is None:
|
|
277
285
|
stdout = True
|
|
278
|
-
out = client.containers.run(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
286
|
+
out = client.containers.run(
|
|
287
|
+
image=image,
|
|
288
|
+
command=command,
|
|
289
|
+
working_dir=working_dir,
|
|
290
|
+
entrypoint=entrypoint,
|
|
291
|
+
name=containerName,
|
|
292
|
+
detach=False,
|
|
293
|
+
volumes=volumes,
|
|
294
|
+
auto_remove=auto_remove,
|
|
295
|
+
stdout=stdout,
|
|
296
|
+
stderr=stderr,
|
|
297
|
+
# to get the generator if demux=True
|
|
298
|
+
stream=stream or demux,
|
|
299
|
+
remove=remove,
|
|
300
|
+
log_config=log_config,
|
|
301
|
+
user=user,
|
|
302
|
+
environment=environment,
|
|
303
|
+
device_requests=device_requests,
|
|
304
|
+
**kwargs,
|
|
305
|
+
)
|
|
296
306
|
|
|
297
307
|
if demux is False:
|
|
298
308
|
return out
|
|
@@ -300,7 +310,10 @@ def apiDockerCall(job,
|
|
|
300
310
|
# If demux is True (i.e.: we want STDOUT and STDERR separated), we need to decode
|
|
301
311
|
# the raw response from the docker API and preserve the stream type this time.
|
|
302
312
|
response = out._response
|
|
303
|
-
gen = (
|
|
313
|
+
gen = (
|
|
314
|
+
demux_adaptor(*frame)
|
|
315
|
+
for frame in _multiplexed_response_stream_helper(response)
|
|
316
|
+
)
|
|
304
317
|
|
|
305
318
|
if stream:
|
|
306
319
|
return gen
|
|
@@ -309,9 +322,11 @@ def apiDockerCall(job,
|
|
|
309
322
|
|
|
310
323
|
else:
|
|
311
324
|
if (stdout or stderr) and log_config is None:
|
|
312
|
-
logger.warning(
|
|
313
|
-
|
|
314
|
-
|
|
325
|
+
logger.warning(
|
|
326
|
+
"stdout or stderr specified, but log_config is not set. "
|
|
327
|
+
'Defaulting to "journald".'
|
|
328
|
+
)
|
|
329
|
+
log_config = dict(type="journald")
|
|
315
330
|
|
|
316
331
|
if stdout is None:
|
|
317
332
|
stdout = False
|
|
@@ -319,30 +334,34 @@ def apiDockerCall(job,
|
|
|
319
334
|
# When detach is True, this returns a container object:
|
|
320
335
|
# >>> client.containers.run("bfirsh/reticulate-splines", detach=True)
|
|
321
336
|
# <Container '45e6d2de7c54'>
|
|
322
|
-
container = client.containers.run(
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
337
|
+
container = client.containers.run(
|
|
338
|
+
image=image,
|
|
339
|
+
command=command,
|
|
340
|
+
working_dir=working_dir,
|
|
341
|
+
entrypoint=entrypoint,
|
|
342
|
+
name=containerName,
|
|
343
|
+
detach=True,
|
|
344
|
+
volumes=volumes,
|
|
345
|
+
auto_remove=auto_remove,
|
|
346
|
+
stdout=stdout,
|
|
347
|
+
stderr=stderr,
|
|
348
|
+
stream=stream,
|
|
349
|
+
remove=remove,
|
|
350
|
+
log_config=log_config,
|
|
351
|
+
user=user,
|
|
352
|
+
environment=environment,
|
|
353
|
+
device_requests=device_requests,
|
|
354
|
+
**kwargs,
|
|
355
|
+
)
|
|
339
356
|
if stdout or stderr:
|
|
340
357
|
if streamfile is None:
|
|
341
|
-
streamfile =
|
|
342
|
-
with open(streamfile,
|
|
358
|
+
streamfile = "output.log"
|
|
359
|
+
with open(streamfile, "wb") as f:
|
|
343
360
|
# stream=True makes this loop blocking; we will loop until
|
|
344
361
|
# the container stops and there is no more output.
|
|
345
|
-
for line in container.logs(
|
|
362
|
+
for line in container.logs(
|
|
363
|
+
stdout=stdout, stderr=stderr, stream=True
|
|
364
|
+
):
|
|
346
365
|
f.write(line.encode() if isinstance(line, str) else line)
|
|
347
366
|
|
|
348
367
|
# If we didn't capture output, the caller will need to .wait() on
|
|
@@ -361,10 +380,12 @@ def apiDockerCall(job,
|
|
|
361
380
|
raise create_api_error_from_http_exception(e)
|
|
362
381
|
|
|
363
382
|
|
|
364
|
-
def dockerKill(
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
383
|
+
def dockerKill(
|
|
384
|
+
container_name: str,
|
|
385
|
+
gentleKill: bool = False,
|
|
386
|
+
remove: bool = False,
|
|
387
|
+
timeout: int = 365 * 24 * 60 * 60,
|
|
388
|
+
) -> None:
|
|
368
389
|
"""
|
|
369
390
|
Immediately kills a container. Equivalent to "docker kill":
|
|
370
391
|
https://docs.docker.com/engine/reference/commandline/kill/
|
|
@@ -378,10 +399,10 @@ def dockerKill(container_name: str,
|
|
|
378
399
|
to respect the timeout. Defaults to 1 year (i.e. wait
|
|
379
400
|
essentially indefinitely).
|
|
380
401
|
"""
|
|
381
|
-
client = docker.from_env(version=
|
|
402
|
+
client = docker.from_env(version="auto", timeout=timeout)
|
|
382
403
|
try:
|
|
383
404
|
this_container = client.containers.get(container_name)
|
|
384
|
-
while this_container.status ==
|
|
405
|
+
while this_container.status == "running":
|
|
385
406
|
if gentleKill is False:
|
|
386
407
|
client.containers.get(container_name).kill()
|
|
387
408
|
else:
|
|
@@ -390,9 +411,13 @@ def dockerKill(container_name: str,
|
|
|
390
411
|
if remove:
|
|
391
412
|
this_container.remove()
|
|
392
413
|
except NotFound:
|
|
393
|
-
logger.debug(
|
|
414
|
+
logger.debug(
|
|
415
|
+
f"Attempted to stop container ({container_name}), but container != exist."
|
|
416
|
+
)
|
|
394
417
|
except requests.exceptions.HTTPError as e:
|
|
395
|
-
logger.debug(
|
|
418
|
+
logger.debug(
|
|
419
|
+
f"Attempted to stop container ({container_name}), but server gave an error:"
|
|
420
|
+
)
|
|
396
421
|
raise create_api_error_from_http_exception(e)
|
|
397
422
|
|
|
398
423
|
|
|
@@ -419,10 +444,10 @@ def containerIsRunning(container_name: str, timeout: int = 365 * 24 * 60 * 60):
|
|
|
419
444
|
:returns: True if status is 'running', False if status is anything else,
|
|
420
445
|
and None if the container does not exist.
|
|
421
446
|
"""
|
|
422
|
-
client = docker.from_env(version=
|
|
447
|
+
client = docker.from_env(version="auto", timeout=timeout)
|
|
423
448
|
try:
|
|
424
449
|
this_container = client.containers.get(container_name)
|
|
425
|
-
if this_container.status ==
|
|
450
|
+
if this_container.status == "running":
|
|
426
451
|
return True
|
|
427
452
|
else:
|
|
428
453
|
# this_container.status == 'exited', 'restarting', or 'paused'
|
|
@@ -430,8 +455,7 @@ def containerIsRunning(container_name: str, timeout: int = 365 * 24 * 60 * 60):
|
|
|
430
455
|
except NotFound:
|
|
431
456
|
return None
|
|
432
457
|
except requests.exceptions.HTTPError as e:
|
|
433
|
-
logger.debug("Server error attempting to call container: %s",
|
|
434
|
-
container_name)
|
|
458
|
+
logger.debug("Server error attempting to call container: %s", container_name)
|
|
435
459
|
raise create_api_error_from_http_exception(e)
|
|
436
460
|
|
|
437
461
|
|
|
@@ -440,8 +464,12 @@ def getContainerName(job):
|
|
|
440
464
|
Create a random string including the job name, and return it. Name will
|
|
441
465
|
match ``[a-zA-Z0-9][a-zA-Z0-9_.-]``.
|
|
442
466
|
"""
|
|
443
|
-
parts = [
|
|
444
|
-
|
|
467
|
+
parts = [
|
|
468
|
+
"toil",
|
|
469
|
+
str(job.description),
|
|
470
|
+
base64.b64encode(os.urandom(9), b"-_").decode("utf-8"),
|
|
471
|
+
]
|
|
472
|
+
name = re.sub("[^a-zA-Z0-9_.-]", "", "--".join(parts))
|
|
445
473
|
return name
|
|
446
474
|
|
|
447
475
|
|
|
@@ -459,7 +487,7 @@ def _multiplexed_response_stream_helper(response):
|
|
|
459
487
|
break
|
|
460
488
|
# header is 8 bytes with format: {STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}
|
|
461
489
|
# protocol: https://docs.docker.com/engine/api/v1.24/#attach-to-a-container
|
|
462
|
-
stream_type, length = struct.unpack(
|
|
490
|
+
stream_type, length = struct.unpack(">BxxxL", header)
|
|
463
491
|
if not length:
|
|
464
492
|
continue
|
|
465
493
|
data = response.raw.read(length)
|