8
8
import sys
9
9
from io import open
10
10
11
- from typing import (Dict , List , Text , MutableMapping , Any )
11
+ from typing import (Dict , List , Text , Optional , MutableMapping , Any )
12
12
13
13
from .errors import WorkflowException
14
14
from .job import ContainerCommandLineJob
21
21
22
22
class SingularityCommandLineJob (ContainerCommandLineJob ):
23
23
@staticmethod
24
- def get_image (dockerRequirement , pull_image , dry_run = False ):
25
- # type: (Dict[Text, Text], bool, bool) -> bool
24
+ def get_image (dockerRequirement , # type: Dict[Text, Text]
25
+ pull_image , # type: bool
26
+ dry_run = False # type: bool
27
+ ):
28
+ # type: (...) -> bool
29
+ """
30
+ Acquire the software container image in the specified dockerRequirement
31
+ using Singularity and returns the success as a bool. Updates the
32
+ provided dockerRequirement with the specific dockerImageId to the full
33
+ path of the local image, if found. Likewise the
34
+ dockerRequirement['dockerPull'] is updated to a docker:// URI if needed.
35
+ """
26
36
found = False
27
37
28
38
if "dockerImageId" not in dockerRequirement and "dockerPull" in dockerRequirement :
29
39
match = re .search (pattern = r'([a-z]*://)' , string = dockerRequirement ["dockerPull" ])
30
40
if match :
31
41
dockerRequirement ["dockerImageId" ] = re .sub (pattern = r'([a-z]*://)' , repl = r'' ,
32
42
string = dockerRequirement ["dockerPull" ])
33
- dockerRequirement ["dockerImageId" ] = re .sub (pattern = r'[:/]' , repl = r'-' ,
34
- string = dockerRequirement ["dockerImageId" ]) + ".img"
43
+ dockerRequirement ["dockerImageId" ] = re .sub (
44
+ pattern = r'[:/]' , repl = r'-' , string = dockerRequirement ["dockerImageId" ]) + ".img"
35
45
else :
36
- dockerRequirement ["dockerImageId" ] = re .sub (pattern = r'[:/]' , repl = r'-' ,
37
- string = dockerRequirement ["dockerPull" ]) + ".img"
46
+ dockerRequirement ["dockerImageId" ] = re .sub (
47
+ pattern = r'[:/]' , repl = r'-' , string = dockerRequirement ["dockerPull" ]) + ".img"
38
48
dockerRequirement ["dockerPull" ] = "docker://" + dockerRequirement ["dockerPull" ]
39
49
40
50
# check if Singularity image is available in $SINGULARITY_CACHEDIR
41
- if "SINGULARITY_CACHEDIR" in os .environ \
42
- and os .path .isfile (os .path .join (os .environ ["SINGULARITY_CACHEDIR" ], dockerRequirement ["dockerImageId" ])):
43
- _logger .info ("Using local copy of Singularity image found in $SINGULARITY_CACHEDIR" )
44
- dockerRequirement ["dockerImageId" ] = os .path .join (os .environ ["SINGULARITY_CACHEDIR" ], dockerRequirement ["dockerImageId" ])
51
+ if "SINGULARITY_CACHEDIR" in os .environ and os .path .isfile (
52
+ os .path .join (os .environ ["SINGULARITY_CACHEDIR" ],
53
+ dockerRequirement ["dockerImageId" ])):
54
+ _logger .info ("Using local copy of Singularity image found in "
55
+ "$SINGULARITY_CACHEDIR" )
56
+ dockerRequirement ["dockerImageId" ] = os .path .join (
57
+ os .environ ["SINGULARITY_CACHEDIR" ],
58
+ dockerRequirement ["dockerImageId" ])
45
59
found = True
46
60
47
61
# check if Singularity image is available in $SINGULARITY_PULLFOLDER
48
- elif "SINGULARITY_PULLFOLDER" in os .environ \
49
- and os .path .isfile (os .path .join (os .environ ["SINGULARITY_PULLFOLDER" ], dockerRequirement ["dockerImageId" ])):
50
- _logger .info ("Using local copy of Singularity image found in $SINGULARITY_PULLFOLDER" )
51
- dockerRequirement ["dockerImageId" ] = os .path .join (os .environ ["SINGULARITY_PULLFOLDER" ], dockerRequirement ["dockerImageId" ])
62
+ elif "SINGULARITY_PULLFOLDER" in os .environ and os .path .isfile (
63
+ os .path .join (os .environ ["SINGULARITY_PULLFOLDER" ],
64
+ dockerRequirement ["dockerImageId" ])):
65
+ _logger .info ("Using local copy of Singularity image found in "
66
+ "$SINGULARITY_PULLFOLDER" )
67
+ dockerRequirement ["dockerImageId" ] = os .path .join (
68
+ os .environ ["SINGULARITY_PULLFOLDER" ],
69
+ dockerRequirement ["dockerImageId" ])
52
70
found = True
53
71
54
72
# check if Singularity image is available in current working directory
@@ -69,21 +87,31 @@ def get_image(dockerRequirement, pull_image, dry_run=False):
69
87
70
88
return found
71
89
72
- def get_from_requirements (self , r , req , pull_image , dry_run = False , force_pull = False ):
73
- # type: (Dict[Text, Text], bool, bool, bool, bool) -> Text
74
- # returns the filename of the Singularity image (e.g. hello-world-latest.img)
90
+ def get_from_requirements (self ,
91
+ r , # type: Optional[Dict[Text, Text]]
92
+ req , # type: bool
93
+ pull_image , # type: bool
94
+ dry_run = False , # type: bool
95
+ force_pull = False # type: bool
96
+ ):
97
+ # type: (...) -> Text
98
+ """
99
+ Returns the filename of the Singularity image (e.g.
100
+ hello-world-latest.img).
101
+ """
75
102
76
103
if force_pull :
77
- _logger .warn ("--force-docker-pull currently not supported for singularity" )
104
+ _logger .warning ("--force-docker-pull currently not supported for "
105
+ "singularity" )
78
106
79
107
if r :
80
108
errmsg = None
81
109
try :
82
110
subprocess .check_output (["singularity" , "--version" ])
83
- except subprocess .CalledProcessError as e :
84
- errmsg = "Cannot execute 'singularity --version' " + Text ( e )
85
- except OSError as e :
86
- errmsg = "'singularity' executable not found: " + Text ( e )
111
+ except subprocess .CalledProcessError as err :
112
+ errmsg = "Cannot execute 'singularity --version' {}" . format ( err )
113
+ except OSError as err :
114
+ errmsg = "'singularity' executable not found: {}" . format ( err )
87
115
88
116
if errmsg :
89
117
if req :
@@ -95,7 +123,8 @@ def get_from_requirements(self, r, req, pull_image, dry_run=False, force_pull=Fa
95
123
return os .path .abspath (r ["dockerImageId" ])
96
124
else :
97
125
if req :
98
- raise WorkflowException (u"Container image %s not found" % r ["dockerImageId" ])
126
+ raise WorkflowException (u"Container image {} not "
127
+ "found" .format (r ["dockerImageId" ]))
99
128
100
129
return None
101
130
@@ -104,10 +133,10 @@ def add_volumes(self, pathmapper, runtime, stage_output):
104
133
105
134
host_outdir = self .outdir
106
135
container_outdir = self .builder .outdir
107
- for src , vol in pathmapper .items ():
136
+ for _ , vol in pathmapper .items ():
108
137
if not vol .staged :
109
138
continue
110
- if stage_output :
139
+ if stage_output and not vol . target . startswith ( container_outdir ) :
111
140
containertgt = container_outdir + vol .target [len (host_outdir ):]
112
141
else :
113
142
containertgt = vol .target
@@ -119,13 +148,15 @@ def add_volumes(self, pathmapper, runtime, stage_output):
119
148
if vol .type in ("File" , "Directory" ):
120
149
if not vol .resolved .startswith ("_:" ):
121
150
runtime .append (u"--bind" )
122
- runtime .append ("%s:%s:ro" % (
123
- docker_windows_path_adjust (vol .resolved ), docker_windows_path_adjust (containertgt )))
151
+ runtime .append ("{}:{}:ro" .format (
152
+ docker_windows_path_adjust (vol .resolved ),
153
+ docker_windows_path_adjust (containertgt )))
124
154
elif vol .type == "WritableFile" :
125
155
if self .inplace_update :
126
156
runtime .append (u"--bind" )
127
- runtime .append ("%s:%s:rw" % (
128
- docker_windows_path_adjust (vol .resolved ), docker_windows_path_adjust (containertgt )))
157
+ runtime .append ("{}:{}:rw" .format (
158
+ docker_windows_path_adjust (vol .resolved ),
159
+ docker_windows_path_adjust (containertgt )))
129
160
else :
130
161
shutil .copy (vol .resolved , host_outdir_tgt )
131
162
ensure_writable (host_outdir_tgt )
@@ -135,28 +166,40 @@ def add_volumes(self, pathmapper, runtime, stage_output):
135
166
else :
136
167
if self .inplace_update :
137
168
runtime .append (u"--bind" )
138
- runtime .append ("%s:%s:rw" % (
139
- docker_windows_path_adjust (vol .resolved ), docker_windows_path_adjust (containertgt )))
169
+ runtime .append ("{}:{}:rw" .format (
170
+ docker_windows_path_adjust (vol .resolved ),
171
+ docker_windows_path_adjust (containertgt )))
140
172
else :
141
173
shutil .copytree (vol .resolved , vol .target )
142
174
elif vol .type == "CreateFile" :
143
175
createtmp = os .path .join (host_outdir , os .path .basename (vol .target ))
144
- with open (createtmp , "wb" ) as f :
145
- f .write (vol .resolved .encode ("utf-8" ))
176
+ with open (createtmp , "wb" ) as tmp :
177
+ tmp .write (vol .resolved .encode ("utf-8" ))
146
178
runtime .append (u"--bind" )
147
- runtime .append (
148
- "%s:%s:ro" % (docker_windows_path_adjust (createtmp ), docker_windows_path_adjust (vol .target )))
149
-
150
- def create_runtime (self , env , rm_container = True , record_container_id = False , cidfile_dir = "" ,
151
- cidfile_prefix = "" , ** kwargs ):
152
- # type: (MutableMapping[Text, Text], bool, bool, Text, Text, **Any) -> List
153
-
154
- runtime = [u"singularity" , u"--quiet" , u"exec" ]
179
+ runtime .append ("{}:{}:ro" .format (
180
+ docker_windows_path_adjust (createtmp ),
181
+ docker_windows_path_adjust (vol .target )))
182
+
183
+ def create_runtime (self ,
184
+ env , # type: MutableMapping[Text, Text]
185
+ rm_container = True , # type: bool
186
+ record_container_id = False , # type: bool
187
+ cidfile_dir = "" , # type: Text
188
+ cidfile_prefix = "" , # type: Text
189
+ ** kwargs
190
+ ):
191
+ # type: (...) -> List
192
+ """ Returns the Singularity runtime list of commands and options."""
193
+
194
+ runtime = [u"singularity" , u"--quiet" , u"exec" , u"--contain" , u"--pid" ,
195
+ u"--ipc" ] # , u"--userns"]
155
196
runtime .append (u"--bind" )
156
- runtime .append (
157
- u"%s:%s:rw" % (docker_windows_path_adjust (os .path .realpath (self .outdir )), self .builder .outdir ))
197
+ runtime .append (u"{}:{}:rw" .format (
198
+ docker_windows_path_adjust (os .path .realpath (self .outdir )),
199
+ self .builder .outdir ))
158
200
runtime .append (u"--bind" )
159
- runtime .append (u"%s:%s:rw" % (docker_windows_path_adjust (os .path .realpath (self .tmpdir )), "/tmp" ))
201
+ runtime .append (u"{}:{}:rw" .format (
202
+ docker_windows_path_adjust (os .path .realpath (self .tmpdir )), "/tmp" ))
160
203
161
204
self .add_volumes (self .pathmapper , runtime , stage_output = False )
162
205
if self .generatemapper :
@@ -167,11 +210,13 @@ def create_runtime(self, env, rm_container=True, record_container_id=False, cidf
167
210
168
211
if kwargs .get ("custom_net" , None ) is not None :
169
212
raise UnsupportedRequirement (
170
- "Singularity implementation does not support networking" )
213
+ "Singularity implementation does not support custom networking" )
214
+ elif kwargs .get ("disable_net" , None ):
215
+ runtime .append (u"--net" )
171
216
172
217
env ["SINGULARITYENV_TMPDIR" ] = "/tmp"
173
218
env ["SINGULARITYENV_HOME" ] = self .builder .outdir
174
219
175
- for t , v in self .environment .items ():
176
- env ["SINGULARITYENV_" + t ] = v
220
+ for name , value in self .environment .items ():
221
+ env ["SINGULARITYENV_{}" . format ( name ) ] = value
177
222
return runtime
0 commit comments