Skip to content

Commit e2b3fec

Browse files
committed
Clean up Singularity support
Be explicit that docker{File,Load,Import} is not yet supported Add support for --docker-force-pull When checking for cached Singularity image, also try the normalized version of the provided dockerImageId
1 parent c9704f5 commit e2b3fec

File tree

1 file changed

+51
-45
lines changed

1 file changed

+51
-45
lines changed

cwltool/singularity.py

Lines changed: 51 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from .pathmapper import PathMapper, ensure_writable
1313
from .process import (UnsupportedRequirement)
1414
from .utils import docker_windows_path_adjust
15+
from schema_salad.sourceline import SourceLine
1516
if os.name == 'posix' and sys.version_info[0] < 3:
1617
from subprocess32 import (check_call, check_output, # pylint: disable=import-error
1718
CalledProcessError, DEVNULL, PIPE, Popen,
@@ -30,19 +31,27 @@ def _singularity_supports_userns(): # type: ()->bool
3031
global _USERNS # pylint: disable=global-statement
3132
if _USERNS is None:
3233
try:
33-
_USERNS = "No valid /bin/sh" in Popen(
34+
result = Popen(
3435
[u"singularity", u"exec", u"--userns", u"/etc", u"true"],
35-
stderr=PIPE, stdout=DEVNULL).communicate(timeout=60)[1]
36+
stderr=PIPE, stdout=DEVNULL,
37+
universal_newlines=True).communicate(timeout=60)[1]
38+
_USERNS = "No valid /bin/sh" in result
3639
except TimeoutExpired:
3740
_USERNS = False
3841
return _USERNS
3942

43+
def _normalizeImageId(string): # type: (Text)->Text
44+
candidate = re.sub(pattern=r'([a-z]*://)', repl=r'', string=string)
45+
return re.sub(pattern=r'[:/]', repl=r'-', string=candidate) + ".img"
46+
4047

4148
class SingularityCommandLineJob(ContainerCommandLineJob):
49+
4250
@staticmethod
4351
def get_image(dockerRequirement, # type: Dict[Text, Text]
4452
pull_image, # type: bool
45-
dry_run=False # type: bool
53+
dry_run=False, # type: bool
54+
force_pull=False # type: bool
4655
):
4756
# type: (...) -> bool
4857
"""
@@ -54,55 +63,56 @@ def get_image(dockerRequirement, # type: Dict[Text, Text]
5463
"""
5564
found = False
5665

66+
candidates = []
67+
5768
if "dockerImageId" not in dockerRequirement and "dockerPull" in dockerRequirement:
5869
match = re.search(pattern=r'([a-z]*://)', string=dockerRequirement["dockerPull"])
59-
if match:
60-
dockerRequirement["dockerImageId"] = re.sub(pattern=r'([a-z]*://)', repl=r'',
61-
string=dockerRequirement["dockerPull"])
62-
dockerRequirement["dockerImageId"] = re.sub(
63-
pattern=r'[:/]', repl=r'-', string=dockerRequirement["dockerImageId"]) + ".img"
64-
else:
65-
dockerRequirement["dockerImageId"] = re.sub(
66-
pattern=r'[:/]', repl=r'-', string=dockerRequirement["dockerPull"]) + ".img"
70+
candidate = _normalizeImageId(dockerRequirement['dockerPull'])
71+
candidates.append(candidate)
72+
dockerRequirement['dockerImageId'] = candidate
73+
if not match:
6774
dockerRequirement["dockerPull"] = "docker://" + dockerRequirement["dockerPull"]
75+
elif "dockerImageId" in dockerRequirement:
76+
candidates.append(dockerRequirement['dockerImageId'])
77+
candidates.append(_normalizeImageId(dockerRequirement['dockerImageId']))
6878

6979
# check if Singularity image is available in $SINGULARITY_CACHEDIR
70-
if "SINGULARITY_CACHEDIR" in os.environ and os.path.isfile(
71-
os.path.join(os.environ["SINGULARITY_CACHEDIR"],
72-
dockerRequirement["dockerImageId"])):
73-
_logger.info("Using local copy of Singularity image found in "
74-
"$SINGULARITY_CACHEDIR")
75-
dockerRequirement["dockerImageId"] = os.path.join(
76-
os.environ["SINGULARITY_CACHEDIR"],
77-
dockerRequirement["dockerImageId"])
78-
found = True
79-
80-
# check if Singularity image is available in $SINGULARITY_PULLFOLDER
81-
elif "SINGULARITY_PULLFOLDER" in os.environ and os.path.isfile(
82-
os.path.join(os.environ["SINGULARITY_PULLFOLDER"],
83-
dockerRequirement["dockerImageId"])):
84-
_logger.info("Using local copy of Singularity image found in "
85-
"$SINGULARITY_PULLFOLDER")
86-
dockerRequirement["dockerImageId"] = os.path.join(
87-
os.environ["SINGULARITY_PULLFOLDER"],
88-
dockerRequirement["dockerImageId"])
89-
found = True
90-
91-
# check if Singularity image is available in current working directory
92-
elif os.path.isfile(dockerRequirement["dockerImageId"]):
93-
_logger.info("Using local copy of Singularity image")
94-
found = True
95-
96-
# if the .img file is not already present, pull the image
97-
elif pull_image:
80+
for target in ("SINGULARITY_CACHEDIR", "SINGULARITY_PULLFOLDER",
81+
os.getcwd()):
82+
if target in os.environ:
83+
for candidate in candidates:
84+
path = os.path.join(os.environ[target], candidate)
85+
if os.path.isfile(path):
86+
_logger.info("Using local copy of Singularity image "
87+
"found in {}".format(target))
88+
dockerRequirement["dockerImageId"] = path
89+
found = True
90+
91+
if (force_pull or not found) and pull_image:
9892
cmd = [] # type: List[Text]
9993
if "dockerPull" in dockerRequirement:
100-
cmd = ["singularity", "pull", "--name", str(dockerRequirement["dockerImageId"]),
94+
cmd = ["singularity", "pull", "--force", "--name",
95+
str(dockerRequirement["dockerImageId"]),
10196
str(dockerRequirement["dockerPull"])]
10297
_logger.info(Text(cmd))
10398
if not dry_run:
10499
check_call(cmd, stdout=sys.stderr)
105100
found = True
101+
elif "dockerFile" in dockerRequirement:
102+
raise WorkflowException(SourceLine(
103+
dockerRequirement, 'dockerFile').makeError(
104+
"dockerFile is not currently supported when using the "
105+
"Singularity runtime for Docker containers."))
106+
elif "dockerLoad" in dockerRequirement:
107+
raise WorkflowException(SourceLine(
108+
dockerRequirement, 'dockerLoad').makeError(
109+
"dockerLoad is not currently supported when using the "
110+
"Singularity runtime for Docker containers."))
111+
elif "dockerImport" in dockerRequirement:
112+
raise WorkflowException(SourceLine(
113+
dockerRequirement, 'dockerImport').makeError(
114+
"dockerImport is not currently supported when using the "
115+
"Singularity runtime for Docker containers."))
106116

107117
return found
108118

@@ -119,10 +129,6 @@ def get_from_requirements(self,
119129
hello-world-latest.img).
120130
"""
121131

122-
if force_pull:
123-
_logger.warning("--force-docker-pull currently not supported for "
124-
"singularity")
125-
126132
if r:
127133
errmsg = None
128134
try:
@@ -138,7 +144,7 @@ def get_from_requirements(self,
138144
else:
139145
return None
140146

141-
if self.get_image(r, pull_image, dry_run):
147+
if self.get_image(r, pull_image, dry_run, force_pull):
142148
return os.path.abspath(r["dockerImageId"])
143149
else:
144150
if req:

0 commit comments

Comments
 (0)