Skip to content

Commit 1697703

Browse files
authored
Add global= option for plugstack.conf to configure singularity global options (#9)
2 parents c752089 + 62d1653 commit 1697703

File tree

7 files changed

+138
-4
lines changed

7 files changed

+138
-4
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ singularity-exec.so
33
build/
44
install/
55
build-*
6+
_codeql_build_dir/
7+
_codeql_detected_source_root

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ set(SLURM_PLUGSTACK_CONF_D ${SLURM_SYSCONFDIR}/plugstack.conf.d
1717
set(PLUGIN_DEFAULT_ARG "" CACHE STRING "Plugin default= arg")
1818
set(PLUGIN_BIND_ARG ${SLURM_SYSCONFDIR},/var/spool/slurm,/var/run/munge
1919
CACHE STRING "Plugin bind= arg")
20+
set(PLUGIN_GLOBAL_ARG "" CACHE STRING "Plugin global= arg")
2021
set(PLUGIN_EXTRA_ARGS "" CACHE STRING "Plugin args= arg")
2122

2223
find_path(SLURM_INCLUDE_DIR

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ CMake option | Default
4949
`-DSLURM_PLUGSTACK_CONF_D=...` | `${SLURM_SYSCONFDIR}/plugstack.conf.d` | Slurm plugstack conf dir
5050
`-DPLUGIN_DEFAULT_ARG=...` | `""` | Plugin default= arg
5151
`-DPLUGIN_BIND_ARG=...` | `${SLURM_SYSCONFDIR},/var/spool/slurm,/var/run/munge` | Plugin bind= arg
52+
`-DPLUGIN_GLOBAL_ARG=...` | `""` | Plugin global= arg
5253
`-DPLUGIN_EXTRA_ARGS=...` | `""` | Plugin args= arg
5354
`-DSLURM_INCLUDE_DIR=...` | Detected by CMake, typically `/usr/include` | Slurm include dir passed to compiler via `-I` so `#include <slurm/spank.h>` resolves
5455
`-DCMAKE_INSTALL_LIBEXECDIR=...` | `libexec` on RHEL-based systems | FHS "internal binaries" directory [^sSrfT]
@@ -82,7 +83,7 @@ include /etc/slurm/plugstack.conf.d/*.conf'
8283
EOF
8384
# reference the path to the plug-in and the wrapper script
8485
cat > /etc/slurm/plugstack.conf.d/singularity-exec.conf <<EOF
85-
required /usr/lib64/slurm/singularity-exec.so default= script=/usr/libexec/slurm-singularity-wrapper.sh bind= args=disabled
86+
required /usr/lib64/slurm/singularity-exec.so default= script=/usr/libexec/slurm-singularity-wrapper.sh bind= global= args=disabled
8687
EOF
8788
```
8889

@@ -95,6 +96,7 @@ Option | Description
9596
`default=<path>` | Path to the Singularity container launched by default. If this is set user require to explicitly use an empty `--singularity-container=` option to prevent the start of a container.
9697
`script=<path>` | Path to the wrapper script which consumes the input arguments and environment variables set by the plugin to launch the Singularity container.
9798
`bind=<spec>` | List of paths to bind-mount into the container by default. Please reference the section about [User-defined bind paths][95] in the Singularity User Documentation [^E9F6O].
99+
`global=<options>` | List of [global command-line options][93] passed to the `singularity` command itself (e.g., `--silent`, `--quiet`). Equivalent to using the environment variable `SLURM_SINGULARITY_GLOBAL`.
98100
`args=<string>` | List of [command-line arguments][94] passed to `singularity exec`. Disable support for this feature by setting `args=disabled`. This will prompt an error for an unrecognized option if the user adds the `--singularity-args=` option. Use an empty string `args=""` to enable support for singularity arguments without a default configuration. Supply default for all users by adding a list of options i.e. `args="--home /network/$USER"`
99101

100102
Passing `-DINSTALL_PLUGSTACK_CONF=ON` to the CMake configure command will automate the above configuration.

main.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ struct singularity_exec
210210
inline static std::string s_container_name = {};
211211
inline static std::string s_singularity_script = "/usr/lib/slurm/slurm-singularity-wrapper.sh";
212212
inline static std::string s_singularity_args = {};
213+
inline static std::string s_singularity_global = {};
213214
inline static std::string s_bind_defaults = {};
214215
inline static std::string s_bind_mounts = {};
215216
inline static bool s_no_args_option = false;
@@ -323,6 +324,8 @@ struct singularity_exec
323324
s_singularity_script = arg.substr(7);
324325
else if (starts_with(arg, "bind="))
325326
s_bind_defaults = arg.substr(5);
327+
else if (starts_with(arg, "global="))
328+
s_singularity_global = arg.substr(7);
326329
else if (arg == "args=disabled")
327330
s_no_args_option = true;
328331
else if (starts_with(arg, "args=\""))
@@ -343,6 +346,7 @@ struct singularity_exec
343346
"/usr/lib/slurm/slurm-singularity-wrapper.sh\n"
344347
"bind=src[:dest[:opts]][,src[:dest[:opts]]]*\n"
345348
" set default bind mounts\n"
349+
"global=<global options> set default global singularity options\n"
346350
"args=disabled Disable custom arguments\n"
347351
"args=\"<singulary args>\" quotes are mandatory; "
348352
"string may be empty\n",
@@ -424,10 +428,11 @@ struct singularity_exec
424428
s_bind_mounts = s_bind_defaults + ',' + s_bind_mounts;
425429
}
426430

427-
// unconditionally set these two variables so they don't become an
431+
// unconditionally set these three variables so they don't become an
428432
// accidental user interface
429433
s.setenv("SLURM_SINGULARITY_BIND", s_bind_mounts.c_str());
430434
s.setenv("SLURM_SINGULARITY_ARGS", s_singularity_args.c_str());
435+
s.setenv("SLURM_SINGULARITY_GLOBAL", s_singularity_global.c_str());
431436
std::vector<char*> argv = s.job_argument_vector();
432437
argv.insert(argv.begin(),
433438
{ s_singularity_script.data(), s_container_name.data() });

singularity-exec.conf.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Singularity Plug-in
22
#
3-
required @CMAKE_INSTALL_FULL_LIBEXECDIR@/@PROJECT_NAME@.so default=@PLUGIN_DEFAULT_ARG@ script=@CMAKE_INSTALL_FULL_LIBEXECDIR@/@WRAPPER@ bind=@PLUGIN_BIND_ARG@ args="@PLUGIN_EXTRA_ARGS@"
3+
required @CMAKE_INSTALL_FULL_LIBEXECDIR@/@PROJECT_NAME@.so default=@PLUGIN_DEFAULT_ARG@ script=@CMAKE_INSTALL_FULL_LIBEXECDIR@/@WRAPPER@ bind=@PLUGIN_BIND_ARG@ global=@PLUGIN_GLOBAL_ARG@ args="@PLUGIN_EXTRA_ARGS@"

tests/runtime/entrypoint-plugin-builder.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ cmake -GNinja -S "$SRC_DIR" -B "$BUILD_DIR" \
1212
-DCMAKE_INSTALL_PREFIX=/usr \
1313
-DSLURM_SYSCONFDIR=/etc/slurm \
1414
-DINSTALL_PLUGSTACK_CONF=ON \
15-
-DPLUGIN_BIND_ARG="/etc/slurm,/var/spool/slurm,/var/spool/slurmd,/var/run/munge"
15+
-DPLUGIN_BIND_ARG="/etc/slurm,/var/spool/slurm,/var/spool/slurmd,/var/run/munge" \
16+
-DPLUGIN_GLOBAL_ARG="--silent"
1617

1718
cmake --build "$BUILD_DIR"
1819

tests/runtime/test-integration.sh

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,5 +280,128 @@ else
280280
echo
281281
fi
282282

283+
# Test 12: Verify global= option in plugstack.conf is propagated correctly
284+
echo "Test 12: Verifying global= option configuration..."
285+
286+
if [ ! -f "$PLUGSTACK_CONF" ]; then
287+
echo "✗ ERROR: Plugin config not found at $PLUGSTACK_CONF"
288+
exit 1
289+
fi
290+
291+
# Check if global= parameter exists in the config
292+
if ! grep -q "global=" "$PLUGSTACK_CONF"; then
293+
echo "⚠ Warning: global= parameter not found in $PLUGSTACK_CONF"
294+
echo " This may indicate the plugin was built without PLUGIN_GLOBAL_ARG configured"
295+
echo
296+
echo "=== All tests passed! ==="
297+
exit 0
298+
fi
299+
300+
echo "✓ Found global= parameter in $PLUGSTACK_CONF"
301+
302+
# Extract the global= value from config
303+
GLOBAL_VALUE=$(grep "global=" "$PLUGSTACK_CONF" | sed -n 's/.*global=\([^ ]*\).*/\1/p')
304+
echo " Configuration value: global=${GLOBAL_VALUE}"
305+
306+
# If container tests are not available, skip runtime verification
307+
if [ "$SKIP_CONTAINER_TEST" = "true" ]; then
308+
echo " Skipping runtime verification (no container available)"
309+
echo
310+
echo "=== All tests passed! ==="
311+
exit 0
312+
fi
313+
314+
# Verify the value is used in a containerized job
315+
echo " Testing global option propagation with containerized job..."
316+
317+
# Create a test job script that checks if SLURM_SINGULARITY_GLOBAL is set and prints its value
318+
TEST_GLOBAL_SCRIPT=$(mktemp /tmp/test_global.XXXXXX.sh)
319+
cat > "$TEST_GLOBAL_SCRIPT" <<'GLOBALEOF'
320+
#!/bin/bash
321+
# Check if SLURM_SINGULARITY_GLOBAL is set and print its value
322+
if [ -n "${SLURM_SINGULARITY_GLOBAL+x}" ]; then
323+
echo "SLURM_SINGULARITY_GLOBAL is set to: '${SLURM_SINGULARITY_GLOBAL}'"
324+
else
325+
echo "SLURM_SINGULARITY_GLOBAL is NOT set"
326+
fi
327+
hostname
328+
GLOBALEOF
329+
chmod +x "$TEST_GLOBAL_SCRIPT"
330+
331+
# Submit job with container
332+
GLOBAL_JOB_ID=$(sbatch --singularity-container="$TEST_CONTAINER" \
333+
--output="${SLURM_JOB_SPOOL}/test_global_%j.out" \
334+
--error="${SLURM_JOB_SPOOL}/test_global_%j.err" \
335+
"$TEST_GLOBAL_SCRIPT" 2>&1 | awk '{print $NF}')
336+
337+
if [ -z "$GLOBAL_JOB_ID" ]; then
338+
echo "⚠ Warning: Failed to submit global option test job"
339+
rm -f "$TEST_GLOBAL_SCRIPT"
340+
echo
341+
echo "=== All tests passed! ==="
342+
exit 0
343+
fi
344+
345+
echo " Job submitted: $GLOBAL_JOB_ID"
346+
347+
# Wait for job to complete
348+
retry --times="$RETRY_TIMES" --delay="$JOB_RETRY_DELAY" -- \
349+
bash -c "scontrol show job $GLOBAL_JOB_ID 2>/dev/null | grep -qE 'JobState=(COMPLETED|FAILED|CANCELLED)'" >/dev/null 2>&1
350+
351+
JOB_STATE=$(scontrol show job "$GLOBAL_JOB_ID" 2>/dev/null | grep "JobState" | awk '{print $1}' | cut -d= -f2)
352+
353+
if [ "$JOB_STATE" != "COMPLETED" ] && [ "$JOB_STATE" != "COMPLETING" ]; then
354+
echo "⚠ Warning: Global option test job did not complete successfully (State: $JOB_STATE)"
355+
rm -f "$TEST_GLOBAL_SCRIPT"
356+
echo
357+
echo "=== All tests passed! ==="
358+
exit 0
359+
fi
360+
361+
# Check stdout for the SLURM_SINGULARITY_GLOBAL environment variable check
362+
GLOBAL_OUT_FILE="${SLURM_JOB_SPOOL}/test_global_${GLOBAL_JOB_ID}.out"
363+
364+
if [ ! -f "$GLOBAL_OUT_FILE" ]; then
365+
echo "⚠ Warning: Job output file not found, cannot verify global option propagation"
366+
rm -f "$TEST_GLOBAL_SCRIPT"
367+
echo
368+
echo "=== All tests passed! ==="
369+
exit 0
370+
fi
371+
372+
# Check if the variable is set
373+
if grep -q "SLURM_SINGULARITY_GLOBAL is NOT set" "$GLOBAL_OUT_FILE"; then
374+
echo "✗ ERROR: SLURM_SINGULARITY_GLOBAL was not set by plugin"
375+
cat "$GLOBAL_OUT_FILE"
376+
rm -f "$TEST_GLOBAL_SCRIPT" "${SLURM_JOB_SPOOL}/test_global_${GLOBAL_JOB_ID}."*
377+
exit 1
378+
fi
379+
380+
if ! grep -q "SLURM_SINGULARITY_GLOBAL is set to:" "$GLOBAL_OUT_FILE"; then
381+
echo "⚠ Warning: Could not verify SLURM_SINGULARITY_GLOBAL in job output"
382+
rm -f "$TEST_GLOBAL_SCRIPT" "${SLURM_JOB_SPOOL}/test_global_${GLOBAL_JOB_ID}."*
383+
echo
384+
echo "=== All tests passed! ==="
385+
exit 0
386+
fi
387+
388+
echo "✓ SLURM_SINGULARITY_GLOBAL environment variable was set by plugin"
389+
390+
# Show the actual value that was set
391+
ACTUAL_GLOBAL=$(grep "SLURM_SINGULARITY_GLOBAL is set to:" "$GLOBAL_OUT_FILE" | head -1)
392+
echo " ${ACTUAL_GLOBAL}"
393+
394+
# Extract just the value for comparison (between quotes)
395+
ACTUAL_VALUE=$(echo "$ACTUAL_GLOBAL" | sed -n "s/.*SLURM_SINGULARITY_GLOBAL is set to: '\(.*\)'/\1/p")
396+
if [ "$ACTUAL_VALUE" = "$GLOBAL_VALUE" ]; then
397+
echo "✓ Value matches configuration: '${GLOBAL_VALUE}'"
398+
else
399+
echo "⚠ Warning: Value mismatch (config: '${GLOBAL_VALUE}', actual: '${ACTUAL_VALUE}')"
400+
fi
401+
402+
# Cleanup test files
403+
rm -f "$TEST_GLOBAL_SCRIPT" "${SLURM_JOB_SPOOL}/test_global_${GLOBAL_JOB_ID}."*
404+
echo
405+
283406
echo "=== All tests passed! ==="
284407
exit 0

0 commit comments

Comments
 (0)