Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion src/DIRAC/Core/Utilities/Os.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
by default on Error they return None
"""
import os
import threading

import DIRAC
from DIRAC.Core.Utilities.Subprocess import shellCall, systemCall
from DIRAC.Core.Utilities import List
from DIRAC.Core.Utilities.Subprocess import shellCall, systemCall

DEBUG = 0

Expand Down Expand Up @@ -128,3 +129,33 @@ def sourceEnv(timeout, cmdTuple, inputEnv=None):
result["stderr"] = stderr

return result


def safe_listdir(directory, timeout=60):
"""This is a "safe" list directory,
for lazily-loaded File Systems like CVMFS.
There's by default a 60 seconds timeout.

.. warning::
There is no distinction between an empty directory, and a non existent one.
It will return `[]` in both cases.

:param str directory: directory to list
:param int timeout: optional timeout, in seconds. Defaults to 60.
"""

def listdir(directory):
try:
return os.listdir(directory)
except FileNotFoundError:
print(f"{directory} not found")
return []

contents = []
t = threading.Thread(target=lambda: contents.extend(listdir(directory)))
t.daemon = True # don't delay program's exit
t.start()
t.join(timeout)
if t.is_alive():
return None # timeout
return contents
13 changes: 10 additions & 3 deletions src/DIRAC/Core/scripts/dirac_apptainer_exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from DIRAC import S_ERROR, gConfig, gLogger
from DIRAC.Core.Base.Script import Script
from DIRAC.Core.Security.Locations import getCAsLocation, getProxyLocation, getVOMSLocation
from DIRAC.Core.Utilities.Os import safe_listdir
from DIRAC.Core.Utilities.Subprocess import systemCall


Expand Down Expand Up @@ -73,7 +74,7 @@ def main():
cmd.extend(["--contain"]) # use minimal /dev and empty other directories (e.g. /tmp and $HOME)
cmd.extend(["--ipc"]) # run container in a new IPC namespace
cmd.extend(["--pid"]) # run container in a new PID namespace
cmd.extend(["--bind", f"{os.getcwd()}:/mnt"]) # bind current directory for dirac_container.sh
cmd.extend(["--bind", f"{os.getcwd()}"]) # bind current directory for dirac_container.sh
if proxy_location:
cmd.extend(["--bind", f"{proxy_location}:/etc/proxy"]) # bind proxy file
cmd.extend(["--bind", f"{getCAsLocation()}:/etc/grid-security/certificates"]) # X509_CERT_DIR
Expand All @@ -83,12 +84,18 @@ def main():
cmd.extend(["--bind", f"{vomses_location}:/etc/grid-security/vomses"]) # X509_VOMSES
cmd.extend(["--bind", "{0}:{0}:ro".format(etc_dir)]) # etc dir for dirac.cfg
cmd.extend(["--bind", "{0}:{0}:ro".format(os.path.join(os.path.realpath(sys.base_prefix)))]) # code dir
cmd.extend(["--cwd", "/mnt"]) # set working directory to /mnt
# here bind optional paths
for bind_path in gConfig.getValue("/Resources/Computing/Singularity/BindPaths", []):
if safe_listdir(bind_path):
cmd.extend(["--bind", f"{bind_path}:{bind_path}"])
else:
gLogger.warning(f"Bind path {bind_path} does not exist, skipping")
cmd.extend(["--cwd", f"{os.getcwd()}"]) # set working directory

rootImage = user_image or gConfig.getValue("/Resources/Computing/Singularity/ContainerRoot") or CONTAINER_DEFROOT

if os.path.isdir(rootImage) or os.path.isfile(rootImage):
cmd.extend([rootImage, "/mnt/dirac_container.sh"])
cmd.extend([rootImage, f"{os.getcwd()}/dirac_container.sh"])
else:
# if we are here is because there's no image, or it is not accessible (e.g. not on CVMFS)
gLogger.error("Apptainer image to exec not found: ", rootImage)
Expand Down
Loading