Skip to content

Commit 3c1d940

Browse files
author
Dennis Kennetz
committed
changed singularity.py to find env images and .sif files
1 parent 8f89637 commit 3c1d940

File tree

1 file changed

+56
-28
lines changed

1 file changed

+56
-28
lines changed

cwltool/singularity.py

Lines changed: 56 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@
2626

2727
if os.name == 'posix':
2828
if sys.version_info < (3, 5):
29-
from subprocess32 import ( # pylint: disable=import-error,no-name-in-module
29+
from subprocess32 import ( # nosec # pylint: disable=import-error,no-name-in-module
3030
check_call, check_output, CalledProcessError, DEVNULL, PIPE, Popen,
3131
TimeoutExpired)
3232
else:
33-
from subprocess import ( # pylint: disable=import-error,no-name-in-module
33+
from subprocess import ( # nosec # pylint: disable=import-error,no-name-in-module
3434
check_call, check_output, CalledProcessError, DEVNULL, PIPE, Popen,
3535
TimeoutExpired)
3636

@@ -44,7 +44,7 @@ def _singularity_supports_userns(): # type: ()->bool
4444
if _USERNS is None:
4545
try:
4646
hello_image = os.path.join(os.path.dirname(__file__), 'hello.simg')
47-
result = Popen(
47+
result = Popen( # nosec
4848
[u"singularity", u"exec", u"--userns", hello_image, u"true"],
4949
stderr=PIPE, stdout=DEVNULL,
5050
universal_newlines=True).communicate(timeout=60)[1]
@@ -57,6 +57,13 @@ def _normalize_image_id(string): # type: (Text)->Text
5757
candidate = re.sub(pattern=r'([a-z]*://)', repl=r'', string=string)
5858
return re.sub(pattern=r'[:/]', repl=r'-', string=candidate) + ".img"
5959

60+
def _normalize_sif_id(string): # type: (Text)->Text
61+
candidate = re.sub(pattern=r'([a-z]*://)', repl=r'', string=string)
62+
candidate_new = re.sub(pattern=r'[::]', repl=r'_', string=candidate) + ".sif"
63+
if '/' in candidate_new:
64+
return candidate_new.split('/')[1]
65+
else:
66+
return candidate_new
6067

6168
class SingularityCommandLineJob(ContainerCommandLineJob):
6269

@@ -79,39 +86,54 @@ def get_image(dockerRequirement, # type: Dict[Text, Text]
7986

8087
if "dockerImageId" not in dockerRequirement and "dockerPull" in dockerRequirement:
8188
match = re.search(pattern=r'([a-z]*://)', string=dockerRequirement["dockerPull"])
82-
candidate = _normalize_image_id(dockerRequirement['dockerPull'])
83-
candidates.append(candidate)
84-
dockerRequirement['dockerImageId'] = candidate
89+
candidate_image = _normalize_image_id(dockerRequirement['dockerPull'])
90+
candidates.append(candidate_image)
91+
candidate_sif = _normalize_sif_id(dockerRequirement['dockerPull'])
92+
candidates.append(candidate_sif)
93+
dockerRequirement['dockerImageId'] = candidate_image
8594
if not match:
8695
dockerRequirement["dockerPull"] = "docker://" + dockerRequirement["dockerPull"]
8796
elif "dockerImageId" in dockerRequirement:
8897
candidates.append(dockerRequirement['dockerImageId'])
8998
candidates.append(_normalize_image_id(dockerRequirement['dockerImageId']))
9099

91100
# check if Singularity image is available in $SINGULARITY_CACHEDIR
101+
# or any subdirs created in cachedir
92102
targets = [os.getcwd()]
93103
for env in ("SINGULARITY_CACHEDIR", "SINGULARITY_PULLFOLDER"):
94104
if env in os.environ:
95105
targets.append(os.environ[env])
96106
for target in targets:
97-
for candidate in candidates:
98-
path = os.path.join(target, candidate)
99-
if os.path.isfile(path):
100-
_logger.info(
101-
"Using local copy of Singularity image found in %s",
102-
target)
103-
dockerRequirement["dockerImageId"] = path
104-
found = True
105-
107+
for dirpath, subdirs, files in os.walk(target):
108+
for sif in files:
109+
if sif in candidates:
110+
path = os.path.join(dirpath, sif)
111+
if os.path.isfile(path):
112+
_logger.info(
113+
"Using local copy of Singularity image found in %s",
114+
dirpath)
115+
dockerRequirement["dockerImageId"] = path
116+
found = True
106117
if (force_pull or not found) and pull_image:
107118
cmd = [] # type: List[Text]
108119
if "dockerPull" in dockerRequirement:
109-
cmd = ["singularity", "pull", "--force", "--name",
110-
str(dockerRequirement["dockerImageId"]),
111-
str(dockerRequirement["dockerPull"])]
112-
_logger.info(Text(cmd))
113-
check_call(cmd, stdout=sys.stderr)
114-
found = True
120+
if "SINGULARITY_PULLFOLDER" in os.environ:
121+
pull_folder = str(os.environ["SINGULARITY_PULLFOLDER"])
122+
cmd = ["singularity", "pull", "--force", "--name",
123+
str('%s/' %pull_folder) + str(dockerRequirement["dockerImageId"]),
124+
str(dockerRequirement["dockerPull"])]
125+
_logger.info(Text(cmd))
126+
check_call(cmd, stdout=sys.stderr) # nosec
127+
dockerRequirement["dockerImageId"] = pull_folder + '/' + str(dockerRequirement["dockerImageId"])
128+
found = True
129+
else:
130+
cmd = ["singularity", "pull", "--force", "--name",
131+
str(dockerRequirement["dockerImageId"]),
132+
str(dockerRequirement["dockerPull"])]
133+
_logger.info(Text(cmd))
134+
check_call(cmd, stdout=sys.stderr) # nosec
135+
found = True
136+
115137
elif "dockerFile" in dockerRequirement:
116138
raise WorkflowException(SourceLine(
117139
dockerRequirement, 'dockerFile').makeError(
@@ -149,7 +171,10 @@ def get_from_requirements(self,
149171
raise WorkflowException(u"Container image {} not "
150172
"found".format(r["dockerImageId"]))
151173

152-
return os.path.abspath(r["dockerImageId"])
174+
if "SINGULARITY_PULLFOLDER" in os.environ:
175+
return str(r["dockerImageId"])
176+
else:
177+
return os.path.abspath(r["dockerImageId"])
153178

154179
@staticmethod
155180
def append_volume(runtime, source, target, writable=False):
@@ -226,7 +251,7 @@ def add_writable_directory_volume(self,
226251
new_dir = os.path.join(
227252
tempfile.mkdtemp(prefix=tmp_prefix, dir=tmp_dir),
228253
os.path.basename(volume.resolved))
229-
os.makedirs(new_dir, 0o0755)
254+
os.makedirs(new_dir)
230255
else:
231256
if host_outdir_tgt is not None:
232257
# workaround for lack of overlapping mounts in Singularity
@@ -252,8 +277,8 @@ def add_writable_directory_volume(self,
252277

253278

254279
def create_runtime(self,
255-
env, # type: MutableMapping[Text, Text]
256-
runtime_context # type: RuntimeContext
280+
env, # type: MutableMapping[Text, Text]
281+
runtime_context # type: RuntimeContext
257282
): # type: (...) -> Tuple[List, Optional[Text]]
258283
""" Returns the Singularity runtime list of commands and options."""
259284
any_path_okay = self.builder.get_requirement("DockerRequirement")[1] \
@@ -267,8 +292,9 @@ def create_runtime(self,
267292
docker_windows_path_adjust(os.path.realpath(self.outdir)),
268293
self.builder.outdir))
269294
runtime.append(u"--bind")
295+
tmpdir = "/tmp" # nosec
270296
runtime.append(u"{}:{}:rw".format(
271-
docker_windows_path_adjust(os.path.realpath(self.tmpdir)), "/tmp"))
297+
docker_windows_path_adjust(os.path.realpath(self.tmpdir)), tmpdir))
272298

273299
self.add_volumes(self.pathmapper, runtime, any_path_okay=True,
274300
secret_store=runtime_context.secret_store,
@@ -282,15 +308,17 @@ def create_runtime(self,
282308
runtime.append(u"--pwd")
283309
runtime.append(u"%s" % (docker_windows_path_adjust(self.builder.outdir)))
284310

311+
285312
if runtime_context.custom_net:
286313
raise UnsupportedRequirement(
287314
"Singularity implementation does not support custom networking")
288315
elif runtime_context.disable_net:
289316
runtime.append(u"--net")
290317

291-
env["SINGULARITYENV_TMPDIR"] = "/tmp"
318+
env["SINGULARITYENV_TMPDIR"] = tmpdir
292319
env["SINGULARITYENV_HOME"] = self.builder.outdir
293320

294321
for name, value in self.environment.items():
295-
env["SINGULARITYENV_{}".format(name)] = value
322+
env["SINGULARITYENV_{}".format(name)] = str(value)
296323
return (runtime, None)
324+

0 commit comments

Comments
 (0)