From dc5cbb0b3cb81a698efa9c3f9027c2da288e11a1 Mon Sep 17 00:00:00 2001 From: Oleg Smirnov Date: Tue, 30 Jan 2024 22:01:43 +0200 Subject: [PATCH] fixes #189. Adds parent access attributes to Parameter --- scabha/cargo.py | 5 +++++ stimela/backends/singularity.py | 29 +++++++++++++++++++++-------- stimela/backends/utils.py | 11 +++++++++-- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/scabha/cargo.py b/scabha/cargo.py index ba85e736..37fda805 100644 --- a/scabha/cargo.py +++ b/scabha/cargo.py @@ -127,6 +127,11 @@ class Parameter(object): # if true, treat parameter as a path, and ensure that the parent directories it refers to exist mkdir: bool = False + # if True, and parameter is a path, access to its parent directory is required + access_parent_dir: bool = False + # if True, and parameter is a path, access to its parent directory is required in writable mode + write_parent_dir: bool = False + # for file and dir-type parameters: if True, the file(s)/dir(s) must exist. If False, they can be missing. # if None, then the default logic applies: inputs must exist, and outputs don't must_exist: Optional[bool] = None diff --git a/stimela/backends/singularity.py b/stimela/backends/singularity.py index 7789d4b0..938bb334 100644 --- a/stimela/backends/singularity.py +++ b/stimela/backends/singularity.py @@ -106,19 +106,24 @@ def build_command_line(cab: 'stimela.kitchen.cab.Cab', backend: 'stimela.backend subst: Optional[Dict[str, Any]] = None, binary: Optional[str] = None, simg_path: Optional[str] = None): + from .utils import resolve_required_mounts + args = cab.flavour.get_arguments(cab, params, subst, check_executable=False) if simg_path is None: _, simg_path = get_image_info(cab, backend) cwd = os.getcwd() + bind_opts = ["--bind", f"{cwd}:{cwd}"] + # get extra required filesystem bindings + extra_bindings = resolve_required_mounts(params, cab.inputs, cab.outputs, prior_mounts={cwd: True}) + for path in extra_bindings.keys(): + bind_opts += ["--bind", f"{path}:{path}"] - return [binary or backend.singularity.executable or BINARY, - "exec", - "--containall", - "--bind", f"{cwd}:{cwd}", - "--pwd", cwd, - simg_path] + args + return [binary or backend.singularity.executable or BINARY, + "exec", + "--containall", "--pwd", cwd ] + bind_opts + \ + [simg_path] + args def build(cab: 'stimela.kitchen.cab.Cab', backend: 'stimela.backend.StimelaBackendOptions', log: logging.Logger, command_wrapper: Optional[Callable]=None, @@ -254,6 +259,8 @@ def run(cab: 'stimela.kitchen.cab.Cab', params: Dict[str, Any], fqname: str, Returns: Any: return value (e.g. exit code) of content """ + from .utils import resolve_required_mounts + native.update_rlimits(backend.rlimits, log) # get path to image, rebuilding if backend options allow this @@ -264,9 +271,15 @@ def run(cab: 'stimela.kitchen.cab.Cab', params: Dict[str, Any], fqname: str, args = [backend.singularity.executable or BINARY, "exec", "--containall", - "--bind", f"{cwd}:{cwd}", "--pwd", cwd, - simg_path] + "--bind", f"{cwd}:{cwd}:rw"] + + # get extra required filesystem bindings + extra_bindings = resolve_required_mounts(params, cab.inputs, cab.outputs, prior_mounts={cwd: True}) + for path, rw in extra_bindings.items(): + args += ["--bind", f"{path}:{path}:{'rw' if rw else 'ro'}"] + + args += [simg_path] args += cab.flavour.get_arguments(cab, params, subst, check_executable=False) log.debug(f"command line is {args}") diff --git a/stimela/backends/utils.py b/stimela/backends/utils.py index 90e3fc09..aaf748ca 100644 --- a/stimela/backends/utils.py +++ b/stimela/backends/utils.py @@ -61,9 +61,16 @@ def add_target(path, must_exist, readwrite): must_exist = name in inputs readwrite = schema.writable or name in outputs - # for symlink targets, we need to mount the parent directory for path in files: - add_target(path, must_exist=must_exist, readwrite=readwrite) + path = path.rstrip("/") + # check parent access + if schema.access_parent_dir or schema.write_parent_dir: + add_target(os.path.dirname(path), must_exist=True, readwrite=schema.write_parent_dir) + # for symlink targets, we need to mount the parent directory + if os.path.islink(path): + add_target(os.path.dirname(path), must_exist=True, readwrite=readwrite) + else: + add_target(path, must_exist=must_exist, readwrite=readwrite) # now eliminate unnecessary targets (those that have a parent mount with the same read/write property)