diff --git a/docs/config_reference.rst b/docs/config_reference.rst index b4caf1b43..4f48e20a3 100644 --- a/docs/config_reference.rst +++ b/docs/config_reference.rst @@ -336,6 +336,17 @@ System Partition Configuration .. versionadded:: 4.4 The ``ssh`` scheduler is added. + .. versionchanged:: 4.8.1 + All ``SBATCH_*`` variables are unset before submitting a job through the Slurm-based backends. + See note below for information. + + .. note:: + The Slurm-based backends unset all ``SBATCH_*`` environment variables before submitting a job. + This is done to avoid environment variables bypassing ReFrame's configuration. + For example, if the :attr:`~config.systems.partitions.access` options for a partition used ``-A foo`` and ``SBATCH_ACCOUNT=bar`` was set, then the environment variable would override the configuration, as ReFrame emits those (by default) in the submission script. + + Job submission in ReFrame is controlled exclusively by the system partition's :attr:`~config.systems.partitions.access` options, the test job's :attr:`~reframe.core.schedulers.Job.options` and the :option:`-J` command-line option. + .. note:: The way that multiple node jobs are submitted using the SGE scheduler can be very site-specific. diff --git a/reframe/core/schedulers/slurm.py b/reframe/core/schedulers/slurm.py index d70df4bcc..0d62c5034 100644 --- a/reframe/core/schedulers/slurm.py +++ b/reframe/core/schedulers/slurm.py @@ -6,6 +6,7 @@ import functools import glob import itertools +import os import re import shlex import time @@ -260,17 +261,35 @@ def emit_preamble(self, job): # Filter out empty statements before returning return list(filter(None, preamble)) + def sbatch(self, args): + '''Run sbatch using a clean environment + + We unset all `SBATCH_*` environment variables before submitting; + submission should be controlled entirely by ReFrame through the + partition's `access` options, the test's `job.options` and the + command-line `-J` option + ''' + + with rt.temp_environment(): + for var in [name for name in os.environ.keys() + if name.startswith('SBATCH_')]: + self.log(f'unsetting environment variable {var}', + logging.DEBUG) + del os.environ[var] + + cmd = ' '.join(['sbatch'] + args) + return _run_strict(cmd, timeout=self._submit_timeout) + def submit(self, job): - cmd_parts = ['sbatch'] + sbatch_args = [] if self._sched_access_in_submit: - cmd_parts += job.sched_access + sbatch_args += job.sched_access - cmd_parts += [job.script_filename] - cmd = ' '.join(cmd_parts) + sbatch_args += [job.script_filename] intervals = itertools.cycle([1, 2, 3]) while True: try: - completed = _run_strict(cmd, timeout=self._submit_timeout) + completed = self.sbatch(sbatch_args) break except SpawnedProcessError as e: error_match = re.search( diff --git a/unittests/test_environments.py b/unittests/test_environments.py index ee7f2d73c..1f808bb0d 100644 --- a/unittests/test_environments.py +++ b/unittests/test_environments.py @@ -139,6 +139,15 @@ def test_temp_environment(base_environ, user_runtime, modules_system): assert not rt.is_env_loaded(environ) +def test_temp_environment_as_snapshot(base_environ): + with rt.temp_environment(): + del os.environ['_var0'] + os.environ['_var1'] = 'foo' + + assert os.environ['_var0'] == 'val0' + assert os.environ['_var1'] == 'val1' + + def test_env_load_already_present(base_environ, user_runtime, modules_system, env0): modules_system.load_module('testmod_boo')