38
38
pass
39
39
40
40
_USERNS = None
41
+ _SINGULARITY_VERSION = None
41
42
42
43
def _singularity_supports_userns (): # type: ()->bool
43
44
global _USERNS # pylint: disable=global-statement
@@ -53,6 +54,25 @@ def _singularity_supports_userns(): # type: ()->bool
53
54
_USERNS = False
54
55
return _USERNS
55
56
57
+
58
+ def get_version ():
59
+ global _SINGULARITY_VERSION # pylint: disable=global-statement
60
+ if not _SINGULARITY_VERSION :
61
+ _SINGULARITY_VERSION = check_output (["singularity" , "--version" ], text = True )
62
+ if _SINGULARITY_VERSION .startswith ("singularity version " ):
63
+ _SINGULARITY_VERSION = _SINGULARITY_VERSION [20 :]
64
+ return _SINGULARITY_VERSION
65
+
66
+ def is_version_2_6 ():
67
+ return get_version ().startswith ("2.6" )
68
+
69
+ def is_version_3_or_newer ():
70
+ return int (get_version ()[0 ]) >= 3
71
+
72
+ def is_version_3_1_or_newer (): # OCI compatible ??
73
+ version = get_version ().split ('.' )
74
+ return int (version [0 ]) >= 4 or (int (version [0 ]) == 3 and int (version [1 ]) >= 1 )
75
+
56
76
def _normalize_image_id (string ): # type: (Text)->Text
57
77
return string .replace ('/' , '_' ) + '.img'
58
78
@@ -78,30 +98,40 @@ def get_image(dockerRequirement, # type: Dict[Text, Text]
78
98
79
99
candidates = []
80
100
101
+ cache_folder = None
102
+ if "CWL_SINGULARITY_CACHE" in os .environ :
103
+ cache_folder = os .environ ["CWL_SINGULARITY_CACHE" ]
104
+ elif is_version_2_6 () and "SINGULARITY_PULLFOLDER" in os .environ :
105
+ cache_folder = os .environ ["SINGULARITY_PULLFOLDER" ]
106
+
81
107
if "dockerImageId" not in dockerRequirement and "dockerPull" in dockerRequirement :
82
108
match = re .search (pattern = r'([a-z]*://)' , string = dockerRequirement ["dockerPull" ])
83
- candidate_image = _normalize_image_id (dockerRequirement ['dockerPull' ])
84
- candidates .append (candidate_image )
85
- candidate_sif = _normalize_sif_id (dockerRequirement ['dockerPull' ])
86
- candidates .append (candidate_sif )
87
- dockerRequirement ['dockerImageId' ] = candidate_image
109
+ img_name = _normalize_image_id (dockerRequirement ['dockerPull' ])
110
+ candidates .append (img_name )
111
+ if is_version_3_or_newer ():
112
+ sif_name = _normalize_sif_id (dockerRequirement ['dockerPull' ])
113
+ candidates .append (sif_name )
114
+ dockerRequirement ["dockerImageId" ] = sif_name
115
+ else :
116
+ dockerRequirement ["dockerImageId" ] = img_name
88
117
if not match :
89
118
dockerRequirement ["dockerPull" ] = "docker://" + dockerRequirement ["dockerPull" ]
90
119
elif "dockerImageId" in dockerRequirement :
91
120
candidates .append (dockerRequirement ['dockerImageId' ])
92
121
candidates .append (_normalize_image_id (dockerRequirement ['dockerImageId' ]))
122
+ if is_version_3_or_newer ():
123
+ candidates .append (_normalize_sif_id (dockerRequirement ['dockerPull' ]))
93
124
94
- # check if Singularity image is available in $SINGULARITY_CACHEDIR
95
- # or any subdirs created in cachedir
96
125
targets = [os .getcwd ()]
97
- for env in ("SINGULARITY_CACHEDIR" , "SINGULARITY_PULLFOLDER" ):
98
- if env in os .environ :
99
- targets .append (os .environ [env ])
126
+ if "CWL_SINGULARITY_CACHE" in os .environ :
127
+ targets .append (os .environ ["CWL_SINGULARITY_CACHE" ])
128
+ if is_version_2_6 () and "SINGULARITY_PULLFOLDER" in os .environ :
129
+ targets .append (os .environ ["SINGULARITY_PULLFOLDER" ])
100
130
for target in targets :
101
131
for dirpath , subdirs , files in os .walk (target ):
102
- for sif in files :
103
- if sif in candidates :
104
- path = os .path .join (dirpath , sif )
132
+ for entry in files :
133
+ if entry in candidates :
134
+ path = os .path .join (dirpath , entry )
105
135
if os .path .isfile (path ):
106
136
_logger .info (
107
137
"Using local copy of Singularity image found in %s" ,
@@ -111,15 +141,24 @@ def get_image(dockerRequirement, # type: Dict[Text, Text]
111
141
if (force_pull or not found ) and pull_image :
112
142
cmd = [] # type: List[Text]
113
143
if "dockerPull" in dockerRequirement :
114
- if "SINGULARITY_PULLFOLDER" in os .environ :
115
- pull_folder = str (os .environ ["SINGULARITY_PULLFOLDER" ])
116
- cmd = ["singularity" , "pull" , "--force" , "--name" ,
117
- str ('%s/' % pull_folder ) + str (dockerRequirement ["dockerImageId" ]),
118
- str (dockerRequirement ["dockerPull" ])]
144
+ if cache_folder :
145
+ env = os .environ .copy ()
146
+ if is_version_2_6 ():
147
+ env ['SINGULARITY_PULLFOLDER' ] = cache_folder
148
+ cmd = ["singularity" , "pull" , "--force" , "--name" ,
149
+ dockerRequirement ["dockerImageId" ],
150
+ str (dockerRequirement ["dockerPull" ])]
151
+ else :
152
+ cmd = ["singularity" , "pull" , "--force" , "--name" ,
153
+ "{}/{}" .format (
154
+ cache_folder ,
155
+ dockerRequirement ["dockerImageId" ]),
156
+ str (dockerRequirement ["dockerPull" ])]
157
+
119
158
_logger .info (Text (cmd ))
120
- check_call (cmd , stdout = sys .stderr ) # nosec
121
- dockerRequirement ["dockerImageId" ] = pull_folder + '/' + \
122
- str ( dockerRequirement ["dockerImageId" ])
159
+ check_call (cmd , env = env , stdout = sys .stderr ) # nosec
160
+ dockerRequirement ["dockerImageId" ] = '{}/{}' . format (
161
+ cache_folder , dockerRequirement ["dockerImageId" ])
123
162
found = True
124
163
else :
125
164
cmd = ["singularity" , "pull" , "--force" , "--name" ,
@@ -135,10 +174,21 @@ def get_image(dockerRequirement, # type: Dict[Text, Text]
135
174
"dockerFile is not currently supported when using the "
136
175
"Singularity runtime for Docker containers." ))
137
176
elif "dockerLoad" in dockerRequirement :
177
+ if is_version_3_1_or_newer ():
178
+ if 'dockerImageId' in dockerRequirement :
179
+ name = "{}.sif" .format (dockerRequirement ["dockerImageId" ])
180
+ else :
181
+ name = "{}.sif" .format (dockerRequirement ["dockerLoad" ])
182
+ cmd ["singularity" , "build" , name ,
183
+ "docker-archive://{}" .format (dockerRequirement ["dockerLoad" ])]
184
+ _logger .info (Text (cmd ))
185
+ check_call (cmd , stdout = sys .stderr ) # nosec
186
+ found = True
187
+ dockerRequirement ['dockerImageId' ] = name
138
188
raise WorkflowException (SourceLine (
139
189
dockerRequirement , 'dockerLoad' ).makeError (
140
190
"dockerLoad is not currently supported when using the "
141
- "Singularity runtime for Docker containers." ))
191
+ "Singularity runtime (version less than 3.1) for Docker containers." ))
142
192
elif "dockerImport" in dockerRequirement :
143
193
raise WorkflowException (SourceLine (
144
194
dockerRequirement , 'dockerImport' ).makeError (
@@ -166,10 +216,7 @@ def get_from_requirements(self,
166
216
raise WorkflowException (u"Container image {} not "
167
217
"found" .format (r ["dockerImageId" ]))
168
218
169
- if "SINGULARITY_PULLFOLDER" in os .environ :
170
- return str (r ["dockerImageId" ])
171
- else :
172
- return os .path .abspath (r ["dockerImageId" ])
219
+ return os .path .abspath (r ["dockerImageId" ])
173
220
174
221
@staticmethod
175
222
def append_volume (runtime , source , target , writable = False ):
@@ -282,10 +329,16 @@ def create_runtime(self,
282
329
u"--ipc" ]
283
330
if _singularity_supports_userns ():
284
331
runtime .append (u"--userns" )
285
- runtime .append (u"--bind" )
286
- runtime .append (u"{}:{}:rw" .format (
287
- docker_windows_path_adjust (os .path .realpath (self .outdir )),
288
- self .builder .outdir ))
332
+ if is_version_3_1_or_newer ():
333
+ runtime .append (u"--home" )
334
+ runtime .append (u"{}:{}" .format (
335
+ docker_windows_path_adjust (os .path .realpath (self .outdir )),
336
+ self .builder .outdir ))
337
+ else :
338
+ runtime .append (u"--bind" )
339
+ runtime .append (u"{}:{}:rw" .format (
340
+ docker_windows_path_adjust (os .path .realpath (self .outdir )),
341
+ self .builder .outdir ))
289
342
runtime .append (u"--bind" )
290
343
tmpdir = "/tmp" # nosec
291
344
runtime .append (u"{}:{}:rw" .format (
0 commit comments