From a5a56d8d5585ca41d0546a02e1411268b50ec4ae Mon Sep 17 00:00:00 2001 From: Florian Felten Date: Thu, 24 Jul 2025 08:59:26 +0200 Subject: [PATCH 1/3] utils.container.Singularity: Use SCRATCH environment variable if it is set --- engibench/utils/container.py | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/engibench/utils/container.py b/engibench/utils/container.py index 875f9dce..3e98af38 100644 --- a/engibench/utils/container.py +++ b/engibench/utils/container.py @@ -3,6 +3,7 @@ from collections.abc import Sequence import os import subprocess +import tempfile def pull(image: str) -> None: @@ -223,6 +224,19 @@ class Singularity(ContainerRuntime): name = "singularity" executable = "singularity" + @classmethod + def _set_apptainer_env(cls) -> None: + """Set Apptainer environment variables.""" + # See https://scicomp.ethz.ch/wiki/Apptainer#Settings + # Set cache directory to SCRATCH if available, otherwise use default + scratch_dir = os.environ.get("SCRATCH") + if scratch_dir: + # stores apptainer images in your $SCRATCH directory + os.environ["APPTAINER_CACHEDIR"] = f"{scratch_dir}/.apptainer" + + # uses the local temporary directory to store temporary data when building images + os.environ["APPTAINER_TMPDIR"] = os.environ.get("TMPDIR", tempfile.gettempdir()) + @classmethod def pull(cls, image: str) -> None: """Pull an image. @@ -230,6 +244,9 @@ def pull(cls, image: str) -> None: Args: image: Container image to pull. """ + # Set Apptainer environment variables + cls._set_apptainer_env() + # Convert to docker URI if needed if "://" not in image: docker_uri = "docker://" + image @@ -272,21 +289,11 @@ def run( env: Mapping of environment variable names and values to set inside the container. name: Optional name for the container (not supported by all runtimes). """ - # Create a mutable working copy to add required system mounts - working_mounts = list(mounts) - - # HPC/Singularity containers require explicit /tmp mounting to prevent memory issues - # and ensure application compatibility. This is container configuration, not insecure temp file creation. - if working_mounts: # Only add /tmp mount if we have existing mounts - # Use the first mount's host path for /tmp (existing logic) - tmp_host_path = working_mounts[0][0] - working_mounts.append((tmp_host_path, "/tmp")) # noqa: S108 - else: - # Handle the empty mounts case - perhaps use a default temp directory - # or skip the /tmp mount altogether - pass + # Set Apptainer environment variables + cls._set_apptainer_env() + + mount_args = (["--mount", f"type=bind,src={src},target={target}"] for src, target in mounts) - mount_args = (["--mount", f"type=bind,src={src},target={target}"] for src, target in working_mounts) env_args = (["--env", f"{var}={value}"] for var, value in (env or {}).items()) if "://" not in image: image = "docker://" + image From 83ba0e6b3a36beae4184c9fbfdeedcd6e05f920d Mon Sep 17 00:00:00 2001 From: fgvangessel-umd Date: Thu, 24 Jul 2025 11:21:23 +0200 Subject: [PATCH 2/3] utils.container.Singularity: Use sif images if present --- engibench/utils/container.py | 47 ++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/engibench/utils/container.py b/engibench/utils/container.py index 3e98af38..bd057e40 100644 --- a/engibench/utils/container.py +++ b/engibench/utils/container.py @@ -218,6 +218,9 @@ def is_available(cls) -> bool: return False +DOCKER_PREFIX = "docker://" + + class Singularity(ContainerRuntime): """Singularity / Apptainer.""" @@ -237,6 +240,19 @@ def _set_apptainer_env(cls) -> None: # uses the local temporary directory to store temporary data when building images os.environ["APPTAINER_TMPDIR"] = os.environ.get("TMPDIR", tempfile.gettempdir()) + @classmethod + def sif_filename(cls, image: str) -> str: + """Construct the sif filename from an image specifier.""" + # Extract just the image part if it's a docker URI + image = image.removeprefix(DOCKER_PREFIX) + + # Parse the image name to match Singularity's naming convention + # For "mdolab/public:u22-gcc-ompi-stable", Singularity creates "public_u22-gcc-ompi-stable.sif" + image_name = image.rsplit("/", 1)[-1] if "/" in image else image + + # Replace ":" with "_" in the image name + return image_name.replace(":", "_") + ".sif" + @classmethod def pull(cls, image: str) -> None: """Pull an image. @@ -246,28 +262,15 @@ def pull(cls, image: str) -> None: """ # Set Apptainer environment variables cls._set_apptainer_env() - - # Convert to docker URI if needed - if "://" not in image: - docker_uri = "docker://" + image - else: - docker_uri = image - # Extract just the image part if it's already a docker URI - if docker_uri.startswith("docker://"): - image = docker_uri[len("docker://") :] - - # Parse the image name to match Singularity's naming convention - # For "mdolab/public:u22-gcc-ompi-stable", Singularity creates "public_u22-gcc-ompi-stable.sif" - image_name = image.split("/")[-1] if "/" in image else image - - # Replace ":" with "_" in the image name - sif_filename = image_name.replace(":", "_") + ".sif" + # Get sif filename + sif_filename = cls.sif_filename(image) # Check if the image already exists if os.path.exists(sif_filename): print(f"Image file already exists: {sif_filename} - skipping pull") return - + # Convert to docker URI if needed + docker_uri = DOCKER_PREFIX + image if "://" not in image else image # Image doesn't exist, proceed with pull subprocess.run([cls.executable, "pull", docker_uri], check=True) @@ -292,11 +295,13 @@ def run( # Set Apptainer environment variables cls._set_apptainer_env() - mount_args = (["--mount", f"type=bind,src={src},target={target}"] for src, target in mounts) + # Get sif filename + sif_image = cls.sif_filename(image) + # Reconstruct mount and env args + mount_args = (["--mount", f"type=bind,src={src},target={target}"] for src, target in mounts) env_args = (["--env", f"{var}={value}"] for var, value in (env or {}).items()) - if "://" not in image: - image = "docker://" + image + return subprocess.run( [ cls.executable, @@ -304,7 +309,7 @@ def run( "--compat", *(arg for args in mount_args for arg in args), *(arg for args in env_args for arg in args), - image, + sif_image, *command, ], check=False, From d8dae9da9e77462b2e6998c7686dc02ed08f4920 Mon Sep 17 00:00:00 2001 From: Florian Felten Date: Thu, 24 Jul 2025 11:28:28 +0200 Subject: [PATCH 3/3] utils.container: Use apptainer instead of singularity --- engibench/utils/container.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/engibench/utils/container.py b/engibench/utils/container.py index bd057e40..8fa4c9d1 100644 --- a/engibench/utils/container.py +++ b/engibench/utils/container.py @@ -221,11 +221,11 @@ def is_available(cls) -> bool: DOCKER_PREFIX = "docker://" -class Singularity(ContainerRuntime): - """Singularity / Apptainer.""" +class Apptainer(ContainerRuntime): + """Apptainer.""" - name = "singularity" - executable = "singularity" + name = "apptainer" + executable = "apptainer" @classmethod def _set_apptainer_env(cls) -> None: