1010from typing import Callable , Dict , List , MutableMapping , Optional , Tuple , cast
1111
1212from schema_salad .sourceline import SourceLine
13+ from spython .main import Client
14+ from spython .main .parse .parsers .docker import DockerParser
15+ from spython .main .parse .writers .singularity import SingularityWriter
1316
1417from .builder import Builder
1518from .context import RuntimeContext
@@ -140,6 +143,7 @@ def __init__(
140143 def get_image (
141144 dockerRequirement : Dict [str , str ],
142145 pull_image : bool ,
146+ tmp_outdir_prefix : str ,
143147 force_pull : bool = False ,
144148 ) -> bool :
145149 """
@@ -162,7 +166,35 @@ def get_image(
162166 elif is_version_2_6 () and "SINGULARITY_PULLFOLDER" in os .environ :
163167 cache_folder = os .environ ["SINGULARITY_PULLFOLDER" ]
164168
165- if "dockerImageId" not in dockerRequirement and "dockerPull" in dockerRequirement :
169+ if "dockerFile" in dockerRequirement :
170+ if cache_folder is None : # if environment variables were not set
171+ cache_folder = create_tmp_dir (tmp_outdir_prefix )
172+
173+ absolute_path = os .path .abspath (cache_folder )
174+ dockerfile_path = os .path .join (absolute_path , "Dockerfile" )
175+ singularityfile_path = dockerfile_path + ".def"
176+ # if you do not set APPTAINER_TMPDIR will crash
177+ # WARNING: 'nodev' mount option set on /tmp, it could be a
178+ # source of failure during build process
179+ # FATAL: Unable to create build: 'noexec' mount option set on
180+ # /tmp, temporary root filesystem won't be usable at this location
181+ with open (dockerfile_path , "w" ) as dfile :
182+ dfile .write (dockerRequirement ["dockerFile" ])
183+
184+ singularityfile = SingularityWriter (DockerParser (dockerfile_path ).parse ()).convert ()
185+ with open (singularityfile_path , "w" ) as file :
186+ file .write (singularityfile )
187+
188+ os .environ ["APPTAINER_TMPDIR" ] = absolute_path
189+ singularity_options = ["--fakeroot" ] if not shutil .which ("proot" ) else []
190+ Client .build (
191+ recipe = singularityfile_path ,
192+ build_folder = absolute_path ,
193+ sudo = False ,
194+ options = singularity_options ,
195+ )
196+ found = True
197+ elif "dockerImageId" not in dockerRequirement and "dockerPull" in dockerRequirement :
166198 match = re .search (pattern = r"([a-z]*://)" , string = dockerRequirement ["dockerPull" ])
167199 img_name = _normalize_image_id (dockerRequirement ["dockerPull" ])
168200 candidates .append (img_name )
@@ -243,13 +275,6 @@ def get_image(
243275 check_call (cmd , stdout = sys .stderr ) # nosec
244276 found = True
245277
246- elif "dockerFile" in dockerRequirement :
247- raise SourceLine (
248- dockerRequirement , "dockerFile" , WorkflowException , debug
249- ).makeError (
250- "dockerFile is not currently supported when using the "
251- "Singularity runtime for Docker containers."
252- )
253278 elif "dockerLoad" in dockerRequirement :
254279 if is_version_3_1_or_newer ():
255280 if "dockerImageId" in dockerRequirement :
@@ -298,7 +323,7 @@ def get_from_requirements(
298323 if not bool (shutil .which ("singularity" )):
299324 raise WorkflowException ("singularity executable is not available" )
300325
301- if not self .get_image (cast (Dict [str , str ], r ), pull_image , force_pull ):
326+ if not self .get_image (cast (Dict [str , str ], r ), pull_image , tmp_outdir_prefix , force_pull ):
302327 raise WorkflowException ("Container image {} not found" .format (r ["dockerImageId" ]))
303328
304329 return os .path .abspath (cast (str , r ["dockerImageId" ]))
0 commit comments