Skip to content

Commit 31e409f

Browse files
DailyDreamingmr-c
authored andcommitted
Add bones of idea without actually running the script.
1 parent abac575 commit 31e409f

File tree

3 files changed

+85
-10
lines changed

3 files changed

+85
-10
lines changed

cwltool/docker.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import shutil
88
import subprocess # nosec
99
import sys
10+
import tempfile
1011
import threading
1112
from distutils import spawn
1213
from io import StringIO, open # pylint: disable=redefined-builtin
@@ -102,6 +103,9 @@ def __init__(
102103
super(DockerCommandLineJob, self).__init__(
103104
builder, joborder, make_path_mapper, requirements, hints, name
104105
)
106+
# TODO: Unused; Implement for docker as well.
107+
self.universal_file_bindmount_dir = tempfile.mkdtemp(suffix='-cwl-docker-mnt')
108+
self.bindings_map = []
105109

106110
@staticmethod
107111
def get_image(

cwltool/job.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
import logging
55
import os
66
import re
7+
import resource
78
import shutil
89
import subprocess # nosec
910
import sys
1011
import tempfile
12+
import textwrap
1113
import threading
1214
import time
1315
import uuid
@@ -529,6 +531,15 @@ def run(
529531
tmpdir_lock: Optional[threading.Lock] = None,
530532
) -> None:
531533

534+
# attempt to set an "unlimited" (-1) heap size for this process
535+
# (& thus commandline size) on any system that supports it
536+
# TODO: Do containers inherit the processes's limits?
537+
# Can they be configured from outside of the container?
538+
try:
539+
resource.setrlimit(resource.RLIMIT_DATA, (-1, -1))
540+
except Exception:
541+
pass
542+
532543
if tmpdir_lock:
533544
with tmpdir_lock:
534545
if not os.path.exists(self.tmpdir):
@@ -589,6 +600,20 @@ def run(
589600

590601
class ContainerCommandLineJob(JobBase, metaclass=ABCMeta):
591602
"""Commandline job using containers."""
603+
def __init__(
604+
self,
605+
builder: Builder,
606+
joborder: CWLObjectType,
607+
make_path_mapper: Callable[..., PathMapper],
608+
requirements: List[CWLObjectType],
609+
hints: List[CWLObjectType],
610+
name: str,
611+
) -> None:
612+
super(JobBase, self).__init__(
613+
builder, joborder, make_path_mapper, requirements, hints, name
614+
)
615+
self.universal_file_bindmount_dir = None
616+
self.bindings_map = None
592617

593618
@abstractmethod
594619
def get_from_requirements(
@@ -710,6 +735,41 @@ def add_volumes(
710735
)
711736
pathmapper.update(key, new_path, vol.target, vol.type, vol.staged)
712737

738+
# Dir of individual file inputs for the job (all named as uuid4).
739+
# This creates the same dir inside of the container as exists outside of it,
740+
# Overlayfs must be supported/enabled (which should always be true for CWL).
741+
src = dst = self.universal_file_bindmount_dir
742+
runtime.append(f"--bind={src}:{dst}:rw")
743+
744+
# Make a TSV of the file mappings.
745+
mapping_tsv = os.path.join(self.universal_file_bindmount_dir, 'mapping.tsv')
746+
with open(mapping_tsv, 'w') as f:
747+
# 1. Sort by the destination path, which should sort alphabetically
748+
# and by shortest path first.
749+
# 2. Then, when we go to hardlink the files, we
750+
# should then just be able to hardlink them in order.
751+
for (src, dst, writable) in sorted(self.bindings_map, key=lambda x: len(x[1])):
752+
f.write('\t'.join((src, dst, writable)) + '\n')
753+
754+
# Make the script that uses the TSV file mappings to hardlink everything
755+
# inside of the container to where the job expects to find them.
756+
# This script needs to be the first thing run inside of the container.
757+
linking_script = os.path.join(self.universal_file_bindmount_dir, 'hard_linking_script.py')
758+
# TODO: Write in bash instead. All images might not have python.
759+
with open(linking_script, 'w') as f:
760+
f.write(textwrap.dedent(f"""
761+
import os
762+
763+
with open('{mapping_tsv}', 'r') as f:
764+
for line in f:
765+
src, dst, writable = line.split('\\t')
766+
os.makedirs(os.path.dirname(dst), exist_ok=True)
767+
os.link(src, dst)
768+
# TODO: set the permissions on the file here after linking
769+
770+
"""[1:]))
771+
os.chmod(linking_script, 0o777)
772+
713773
def run(
714774
self,
715775
runtimeContext: RuntimeContext,

cwltool/singularity.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import re
66
import shutil
77
import sys
8+
import tempfile
9+
from uuid import uuid4
810
from distutils import spawn
911
from subprocess import ( # nosec
1012
DEVNULL,
@@ -102,6 +104,8 @@ def __init__(
102104
super(SingularityCommandLineJob, self).__init__(
103105
builder, joborder, make_path_mapper, requirements, hints, name
104106
)
107+
self.universal_file_bindmount_dir = tempfile.mkdtemp(suffix='-cwl-singularity-mnt')
108+
self.bindings_map = []
105109

106110
@staticmethod
107111
def get_image(
@@ -278,18 +282,25 @@ def get_from_requirements(
278282

279283
return os.path.abspath(cast(str, r["dockerImageId"]))
280284

281-
@staticmethod
282285
def append_volume(
283-
runtime: List[str], source: str, target: str, writable: bool = False
286+
self, runtime: List[str], source: str, target: str, writable: bool = False
284287
) -> None:
285-
runtime.append("--bind")
286-
runtime.append(
287-
"{}:{}:{}".format(
288-
docker_windows_path_adjust(source),
289-
docker_windows_path_adjust(target),
290-
"rw" if writable else "ro",
291-
)
292-
)
288+
src = docker_windows_path_adjust(source)
289+
dst = docker_windows_path_adjust(target)
290+
writable = "rw" if writable else "ro"
291+
292+
# use only "os.path.isfile(source)" for Windows? check on this...
293+
if os.path.isfile(source) or os.path.isfile(src):
294+
bindmount_path = os.path.join(self.universal_file_bindmount_dir, str(uuid4()))
295+
os.link(src, bindmount_path)
296+
self.bindings_map.append((bindmount_path, dst, writable))
297+
# don't add a bind arg for the shared self.universal_file_bindmount_dir
298+
# here but at the very end
299+
else:
300+
# TODO: We can still bind enough dirs to exceed the max command line length.
301+
# Not sure how to handle this, since outputs deposited in mounted dirs
302+
# need to be there after the run.
303+
runtime.append(f"--bind={src}:{dst}:{writable}")
293304

294305
def add_file_or_directory_volume(
295306
self, runtime: List[str], volume: MapperEnt, host_outdir_tgt: Optional[str]

0 commit comments

Comments
 (0)