Skip to content

Commit 725174e

Browse files
rupertnashmr-c
authored andcommitted
Enable (consistently) the preserve environment options with containers.
1 parent 82030b5 commit 725174e

File tree

5 files changed

+86
-33
lines changed

5 files changed

+86
-33
lines changed

cwltool/argparser.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ def arg_parser() -> argparse.ArgumentParser:
4949
type=str,
5050
action="append",
5151
help="Preserve specific environment variable when running "
52-
"CommandLineTools without a software container. May be provided "
53-
"multiple times. The default is to preserve only the PATH.",
52+
"CommandLineTools. May be provided multiple times. By default PATH is "
53+
"preserved when not running in a container.",
5454
metavar="ENVVAR",
55-
default=["PATH"],
55+
default=[],
5656
dest="preserve_environment",
5757
)
5858
envgroup.add_argument(

cwltool/context.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def __init__(self, kwargs: Optional[Dict[str, Any]] = None) -> None:
9595
self.no_read_only = False # type: bool
9696
self.custom_net = "" # type: Optional[str]
9797
self.no_match_user = False # type: bool
98-
self.preserve_environment = "" # type: Optional[Iterable[str]]
98+
self.preserve_environment = None # type: Optional[Iterable[str]]
9999
self.preserve_entire_environment = False # type: bool
100100
self.use_container = True # type: bool
101101
self.force_docker_pull = False # type: bool

cwltool/docker.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ def _check_docker_machine_path(path: Optional[str]) -> None:
7878
class DockerCommandLineJob(ContainerCommandLineJob):
7979
"""Runs a CommandLineJob in a sofware container using the Docker engine."""
8080

81+
CONTAINER_TMPDIR: str = "/tmp" # nosec
82+
8183
def __init__(
8284
self,
8385
builder: Builder,
@@ -318,6 +320,15 @@ def add_writable_directory_volume(
318320
shutil.copytree(volume.resolved, host_outdir_tgt)
319321
ensure_writable(host_outdir_tgt or new_dir)
320322

323+
def _required_env(self) -> Dict[str, str]:
324+
# spec currently says "HOME must be set to the designated output
325+
# directory." but spec might change to designated temp directory.
326+
# runtime.append("--env=HOME=/tmp")
327+
return {
328+
"TMPDIR": self.CONTAINER_TMPDIR,
329+
"HOME": self.builder.outdir,
330+
}
331+
321332
def create_runtime(
322333
self, env: MutableMapping[str, str], runtimeContext: RuntimeContext
323334
) -> Tuple[List[str], Optional[str]]:
@@ -335,9 +346,8 @@ def create_runtime(
335346
self.append_volume(
336347
runtime, os.path.realpath(self.outdir), self.builder.outdir, writable=True
337348
)
338-
tmpdir = "/tmp" # nosec
339349
self.append_volume(
340-
runtime, os.path.realpath(self.tmpdir), tmpdir, writable=True
350+
runtime, os.path.realpath(self.tmpdir), self.CONTAINER_TMPDIR, writable=True
341351
)
342352
self.add_volumes(
343353
self.pathmapper,
@@ -385,13 +395,6 @@ def create_runtime(
385395
if runtimeContext.rm_container:
386396
runtime.append("--rm")
387397

388-
runtime.append("--env=TMPDIR=/tmp")
389-
390-
# spec currently says "HOME must be set to the designated output
391-
# directory." but spec might change to designated temp directory.
392-
# runtime.append("--env=HOME=/tmp")
393-
runtime.append("--env=HOME=%s" % self.builder.outdir)
394-
395398
cidfile_path = None # type: Optional[str]
396399
# add parameters to docker to write a container ID file
397400
if runtimeContext.user_space_docker_cmd is None:

cwltool/job.py

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
from io import IOBase
1616
from threading import Timer
1717
from typing import (
18-
IO,
1918
Callable,
19+
Dict,
20+
IO,
2021
Iterable,
2122
List,
2223
Mapping,
@@ -420,6 +421,23 @@ def _execute(
420421
)
421422
shutil.rmtree(self.tmpdir, True)
422423

424+
@abstractmethod
425+
def _required_env(self) -> Dict[str, str]:
426+
"""Variables required by the CWL spec (HOME, TMPDIR, etc).
427+
428+
Note that with containers, the paths will (likely) be those from
429+
inside.
430+
"""
431+
pass
432+
433+
def _preserve_environment_on_containers_warning(
434+
self, varname: Optional[str] = None
435+
) -> None:
436+
"""When running in a container, issue a warning."""
437+
# By default, don't do anything; ContainerCommandLineJob below
438+
# will issue a warning.
439+
pass
440+
423441
def prepare_environment(
424442
self, runtimeContext: RuntimeContext, envVarReq: Mapping[str, str]
425443
) -> None:
@@ -431,24 +449,24 @@ def prepare_environment(
431449
applied (in that order).
432450
"""
433451
# Start empty
434-
env = {}
452+
env: Dict[str, str] = {}
435453

436454
# Preserve any env vars
437-
vars_to_preserve = runtimeContext.preserve_environment
438-
if runtimeContext.preserve_entire_environment is not False:
439-
vars_to_preserve = os.environ
440-
if vars_to_preserve:
441-
for key, value in os.environ.items():
442-
if key in vars_to_preserve and key not in env:
443-
env[key] = value
455+
if runtimeContext.preserve_entire_environment:
456+
self._preserve_environment_on_containers_warning()
457+
env.update(os.environ)
458+
elif runtimeContext.preserve_environment:
459+
for key in runtimeContext.preserve_environment:
460+
self._preserve_environment_on_containers_warning(key)
461+
try:
462+
env[key] = os.environ[key]
463+
except KeyError:
464+
_logger.warning(
465+
f"Attempting to preserve environment variable '{key}' which is not present"
466+
)
444467

445468
# Set required env vars
446-
env["HOME"] = self.outdir
447-
env["TMPDIR"] = self.tmpdir
448-
if "PATH" not in env:
449-
env["PATH"] = os.environ["PATH"]
450-
if "SYSTEMROOT" not in env and "SYSTEMROOT" in os.environ:
451-
env["SYSTEMROOT"] = os.environ["SYSTEMROOT"]
469+
env.update(self._required_env())
452470

453471
# Apply EnvVarRequirement
454472
env.update(envVarReq)
@@ -530,6 +548,15 @@ def run(
530548

531549
self._execute([], self.environment, runtimeContext, monitor_function)
532550

551+
def _required_env(self) -> Dict[str, str]:
552+
env = {}
553+
env["HOME"] = self.outdir
554+
env["TMPDIR"] = self.tmpdir
555+
env["PATH"] = os.environ["PATH"]
556+
if "SYSTEMROOT" in os.environ:
557+
env["SYSTEMROOT"] = os.environ["SYSTEMROOT"]
558+
return env
559+
533560

534561
CONTROL_CODE_RE = r"\x1b\[[0-9;]*[a-zA-Z]"
535562

@@ -588,6 +615,19 @@ def add_writable_directory_volume(
588615
) -> None:
589616
"""Append a writable directory mapping to the runtime option list."""
590617

618+
def _preserve_environment_on_containers_warning(
619+
self, varname: Optional[str] = None
620+
) -> None:
621+
"""When running in a container, issue a warning."""
622+
if varname is None:
623+
_logger.warning(
624+
"You have specified `--preserve-entire-environment` while running a container"
625+
)
626+
else:
627+
_logger.warning(
628+
f"You have specified `--preserve-environment={varname}` while running a container"
629+
)
630+
591631
def create_file_and_add_volume(
592632
self,
593633
runtime: List[str],

cwltool/singularity.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ def _normalize_sif_id(string: str) -> str:
8383

8484

8585
class SingularityCommandLineJob(ContainerCommandLineJob):
86+
CONTAINER_TMPDIR: str = "/tmp" # nosec
87+
8688
def __init__(
8789
self,
8890
builder: Builder,
@@ -370,6 +372,12 @@ def add_writable_directory_volume(
370372
self.append_volume(runtime, source, volume.target, writable=True)
371373
ensure_writable(source)
372374

375+
def _required_env(self) -> Dict[str, str]:
376+
return {
377+
"TMPDIR": self.CONTAINER_TMPDIR,
378+
"HOME": self.builder.outdir,
379+
}
380+
373381
def create_runtime(
374382
self, env: MutableMapping[str, str], runtime_context: RuntimeContext
375383
) -> Tuple[List[str], Optional[str]]:
@@ -381,6 +389,7 @@ def create_runtime(
381389
"exec",
382390
"--contain",
383391
"--ipc",
392+
"--cleanenv",
384393
]
385394
if _singularity_supports_userns():
386395
runtime.append("--userns")
@@ -403,8 +412,12 @@ def create_runtime(
403412
)
404413
)
405414
runtime.append("--bind")
406-
tmpdir = "/tmp" # nosec
407-
runtime.append("{}:{}:rw".format(os.path.realpath(self.tmpdir), tmpdir))
415+
runtime.append(
416+
"{}:{}:rw".format(
417+
os.path.realpath(self.tmpdir),
418+
self.CONTAINER_TMPDIR,
419+
)
420+
)
408421

409422
self.add_volumes(
410423
self.pathmapper,
@@ -432,9 +445,6 @@ def create_runtime(
432445
elif runtime_context.disable_net:
433446
runtime.append("--net")
434447

435-
env["SINGULARITYENV_TMPDIR"] = tmpdir
436-
env["SINGULARITYENV_HOME"] = self.builder.outdir
437-
438448
for name, value in self.environment.items():
439449
env[f"SINGULARITYENV_{name}"] = str(value)
440450
return (runtime, None)

0 commit comments

Comments
 (0)