26
26
27
27
if os .name == 'posix' :
28
28
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
30
30
check_call , check_output , CalledProcessError , DEVNULL , PIPE , Popen ,
31
31
TimeoutExpired )
32
32
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
34
34
check_call , check_output , CalledProcessError , DEVNULL , PIPE , Popen ,
35
35
TimeoutExpired )
36
36
@@ -44,7 +44,7 @@ def _singularity_supports_userns(): # type: ()->bool
44
44
if _USERNS is None :
45
45
try :
46
46
hello_image = os .path .join (os .path .dirname (__file__ ), 'hello.simg' )
47
- result = Popen (
47
+ result = Popen ( # nosec
48
48
[u"singularity" , u"exec" , u"--userns" , hello_image , u"true" ],
49
49
stderr = PIPE , stdout = DEVNULL ,
50
50
universal_newlines = True ).communicate (timeout = 60 )[1 ]
@@ -57,6 +57,13 @@ def _normalize_image_id(string): # type: (Text)->Text
57
57
candidate = re .sub (pattern = r'([a-z]*://)' , repl = r'' , string = string )
58
58
return re .sub (pattern = r'[:/]' , repl = r'-' , string = candidate ) + ".img"
59
59
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
60
67
61
68
class SingularityCommandLineJob (ContainerCommandLineJob ):
62
69
@@ -79,39 +86,54 @@ def get_image(dockerRequirement, # type: Dict[Text, Text]
79
86
80
87
if "dockerImageId" not in dockerRequirement and "dockerPull" in dockerRequirement :
81
88
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
85
94
if not match :
86
95
dockerRequirement ["dockerPull" ] = "docker://" + dockerRequirement ["dockerPull" ]
87
96
elif "dockerImageId" in dockerRequirement :
88
97
candidates .append (dockerRequirement ['dockerImageId' ])
89
98
candidates .append (_normalize_image_id (dockerRequirement ['dockerImageId' ]))
90
99
91
100
# check if Singularity image is available in $SINGULARITY_CACHEDIR
101
+ # or any subdirs created in cachedir
92
102
targets = [os .getcwd ()]
93
103
for env in ("SINGULARITY_CACHEDIR" , "SINGULARITY_PULLFOLDER" ):
94
104
if env in os .environ :
95
105
targets .append (os .environ [env ])
96
106
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
106
117
if (force_pull or not found ) and pull_image :
107
118
cmd = [] # type: List[Text]
108
119
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
+
115
137
elif "dockerFile" in dockerRequirement :
116
138
raise WorkflowException (SourceLine (
117
139
dockerRequirement , 'dockerFile' ).makeError (
@@ -149,7 +171,10 @@ def get_from_requirements(self,
149
171
raise WorkflowException (u"Container image {} not "
150
172
"found" .format (r ["dockerImageId" ]))
151
173
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" ])
153
178
154
179
@staticmethod
155
180
def append_volume (runtime , source , target , writable = False ):
@@ -226,7 +251,7 @@ def add_writable_directory_volume(self,
226
251
new_dir = os .path .join (
227
252
tempfile .mkdtemp (prefix = tmp_prefix , dir = tmp_dir ),
228
253
os .path .basename (volume .resolved ))
229
- os .makedirs (new_dir , 0o0755 )
254
+ os .makedirs (new_dir )
230
255
else :
231
256
if host_outdir_tgt is not None :
232
257
# workaround for lack of overlapping mounts in Singularity
@@ -252,8 +277,8 @@ def add_writable_directory_volume(self,
252
277
253
278
254
279
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
257
282
): # type: (...) -> Tuple[List, Optional[Text]]
258
283
""" Returns the Singularity runtime list of commands and options."""
259
284
any_path_okay = self .builder .get_requirement ("DockerRequirement" )[1 ] \
@@ -267,8 +292,9 @@ def create_runtime(self,
267
292
docker_windows_path_adjust (os .path .realpath (self .outdir )),
268
293
self .builder .outdir ))
269
294
runtime .append (u"--bind" )
295
+ tmpdir = "/tmp" # nosec
270
296
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 ))
272
298
273
299
self .add_volumes (self .pathmapper , runtime , any_path_okay = True ,
274
300
secret_store = runtime_context .secret_store ,
@@ -282,15 +308,17 @@ def create_runtime(self,
282
308
runtime .append (u"--pwd" )
283
309
runtime .append (u"%s" % (docker_windows_path_adjust (self .builder .outdir )))
284
310
311
+
285
312
if runtime_context .custom_net :
286
313
raise UnsupportedRequirement (
287
314
"Singularity implementation does not support custom networking" )
288
315
elif runtime_context .disable_net :
289
316
runtime .append (u"--net" )
290
317
291
- env ["SINGULARITYENV_TMPDIR" ] = "/tmp"
318
+ env ["SINGULARITYENV_TMPDIR" ] = tmpdir
292
319
env ["SINGULARITYENV_HOME" ] = self .builder .outdir
293
320
294
321
for name , value in self .environment .items ():
295
- env ["SINGULARITYENV_{}" .format (name )] = value
322
+ env ["SINGULARITYENV_{}" .format (name )] = str ( value )
296
323
return (runtime , None )
324
+
0 commit comments