@@ -86,7 +86,7 @@ display_help() {
8686 echo " [default: /..storage../opt-eessi]"
8787 echo " -l | --list-repos - list available repository identifiers [default: false]"
8888 echo " -m | --mode MODE - with MODE==shell (launch interactive shell) or"
89- echo " MODE==run (run a script or command) [default: shell]"
89+ echo " MODE==exec/ run (run a script or command) [default: shell]"
9090 echo " -n | --nvidia MODE - configure the container to work with NVIDIA GPUs,"
9191 echo " MODE==install for a CUDA installation, MODE==run to"
9292 echo " attach a GPU, MODE==all for both [default: false]"
@@ -98,7 +98,8 @@ display_help() {
9898 echo " -r | --repository CFG - configuration file or identifier defining the"
9999 echo " repository to use; can be given multiple times;"
100100 echo " CFG may include suffixes ',access={ro,rw},mode={bind,fuse}' to"
101- echo " overwrite the global access and/or mount mode for this repository"
101+ echo " overwrite the global access and/or mount mode for this repository;"
102+ echo " use 'None' to not mount any repositories"
102103 echo " [default: software.eessi.io via CVMFS config available"
103104 echo " via default container, see --container]"
104105 echo " -u | --resume DIR/TGZ - resume a previous run from a directory or tarball,"
@@ -118,11 +119,79 @@ display_help() {
118119 echo " -y | --https-proxy URL - provides URL for the env variable https_proxy"
119120 echo " [default: not set]; uses env var \$ https_proxy if set"
120121 echo
121- echo " If value for --mode is 'run', the SCRIPT/COMMAND provided is executed. If"
122+ echo " If value for --mode is 'exec' or ' run', the SCRIPT/COMMAND provided is executed. If"
122123 echo " arguments to the script/command start with '-' or '--', use the flag terminator"
123124 echo " '--' to let eessi_container.sh stop parsing arguments."
124125}
125126
127+ # function to parse and check bind paths
128+ # returns:
129+ # 0 if target found with matching source (no conflict)
130+ # 1 if target found with different source (conflict)
131+ # 2 if target not found
132+ check_bind_paths_for_target () {
133+ local search_target=" $1 "
134+ local search_src=" $2 "
135+ local bind_paths=" $3 " # typically used to pass value of $BIND_PATHS
136+
137+ # handle empty BIND_PATHS
138+ if [[ -z " ${bind_paths} " ]]; then
139+ return 2
140+ fi
141+
142+ # split by comma and process each entry
143+ IFS=' ,' read -ra BIND_ENTRIES <<< " ${bind_paths}"
144+
145+ for entry in " ${BIND_ENTRIES[@]} " ; do
146+ # skip empty entries
147+ [[ -z " ${entry} " ]] && continue
148+
149+ local bind_src bind_target
150+
151+ # split entry by ':'
152+ IFS=' :' read -ra PARTS <<< " ${entry}"
153+
154+ bind_src=" ${PARTS[0]} "
155+
156+ if [[ ${# PARTS[@]} -ge 2 ]]; then
157+ bind_target=" ${PARTS[1]} "
158+ else
159+ # no target given, use src
160+ bind_target=${bind_src}
161+ fi
162+
163+ # trim any possible whitespace
164+ bind_src=$( echo " ${bind_src} " | xargs)
165+ bind_target=$( echo " ${bind_target} " | xargs)
166+
167+ [[ ${VERBOSE} -eq 1 ]] && echo " Parsed bind entry: src='${bind_src} ' target='${bind_target} '"
168+
169+ # check if this entry matches our target
170+ if [[ " ${bind_target} " == " ${search_target} " ]]; then
171+ # found target -> need to compare normalised sources
172+ # try to normalise source paths, but don't fail if they don't exist (yet)
173+
174+ bind_src_normalised=$( readlink -f " ${bind_src} " 2> /dev/null)
175+ search_src_normalised=$( readlink -f " ${search_src} " 2> /dev/null)
176+
177+ # decide which path to use - normalised or original
178+ local bind_src_compare=" ${bind_src_normalised:- ${bind_src} } "
179+ local search_src_compare=" ${search_src_normalised:- ${search_src} } "
180+
181+ [[ ${VERBOSE} -eq 1 ]] && echo " Comparing: '${bind_src_compare} ' vs '${search_src_compare} '"
182+
183+ if [[ " ${bind_src_compare} " == " ${search_src_compare} " ]]; then
184+ return 0 # found target with same source (all good)
185+ else
186+ echo " ${bind_src} " # return the conflicting source for error message
187+ return 1 # found target with different source (conflict)
188+ fi
189+ fi
190+ done
191+
192+ return 2 # target not found in bind paths
193+ }
194+
126195# set defaults for command line arguments
127196ACCESS=" ro"
128197CONTAINER=" docker://ghcr.io/eessi/build-node:debian12"
@@ -285,6 +354,14 @@ if [[ ${#REPOSITORIES[@]} -eq 0 ]]; then
285354 REPOSITORIES+=(${eessi_default_cvmfs_repo} )
286355fi
287356
357+ # if the first element of REPOSITORIES is "none" (case-insensitive),
358+ # make sure it is an empty list from here on, i.e. no repositories will be mounted
359+ if [[ ${REPOSITORIES[0],,} == " none" ]]; then
360+ REPOSITORIES=()
361+ # also prevent the cvmfs-config repo from being mounted
362+ EESSI_DO_NOT_MOUNT_CVMFS_CONFIG_CERN_CH=1
363+ fi
364+
288365# 1. check if argument values are valid
289366# (arg -a|--access) check if ACCESS is supported
290367# use the value as global setting, suffix to --repository can specify an access mode per repository
300377# HOST_STORAGE_ERROR_EXITCODE
301378
302379# (arg -m|--mode) check if MODE is known
303- if [[ " ${MODE} " != " shell" && " ${MODE} " != " run" ]]; then
380+ if [[ " ${MODE} " != " shell" && " ${MODE} " != " exec " && " ${MODE} " != " run" ]]; then
304381 fatal_error " unknown execution mode '${MODE} '" " ${MODE_UNKNOWN_EXITCODE} "
305382fi
306383
384+ # the run mode should actually call "apptainer exec", so simply override run to exec
385+ if [[ " ${MODE} " == " run" ]]; then
386+ echo_yellow " Note: the behaviour of the run mode has changed."
387+ echo_yellow " Previously, it mistakenly ran 'apptainer/singularity run', but it now runs 'apptainer/singularity exec' instead."
388+ echo_yellow " You can silence this message by using --mode exec instead of --mode run."
389+ MODE=" exec"
390+ fi
391+
307392# Also validate the NVIDIA GPU mode (if present)
308393if [[ ${SETUP_NVIDIA} -eq 1 ]]; then
309394 if [[ " ${NVIDIA_MODE} " != " run" && " ${NVIDIA_MODE} " != " install" && " ${NVIDIA_MODE} " != " all" ]]; then
366451# TODO (arg -y|--https-proxy) check if https proxy is accessible
367452# HTTPS_PROXY_ERROR_EXITCODE
368453
369- # check if a script is provided if mode is 'run '
370- if [[ " ${MODE} " == " run " ]]; then
454+ # check if a script is provided if mode is 'exec '
455+ if [[ " ${MODE} " == " exec " ]]; then
371456 if [[ $# -eq 0 ]]; then
372457 fatal_error " no command specified to run?!" " ${RUN_SCRIPT_MISSING_EXITCODE} "
373458 fi
@@ -719,17 +804,38 @@ if [[ ! -z ${http_proxy} ]]; then
719804 HTTP_PROXY_IPV4=$( get_ipv4_address ${PROXY_HOST} )
720805 [[ ${VERBOSE} -eq 1 ]] && echo " HTTP_PROXY_IPV4='${HTTP_PROXY_IPV4} '"
721806 echo " CVMFS_HTTP_PROXY=\" ${http_proxy} |http://${HTTP_PROXY_IPV4} :${PROXY_PORT} \" " \
722- >> ${EESSI_TMPDIR} /repos_cfg/default.local
807+ >> ${EESSI_TMPDIR} /repos_cfg/default.local
723808 [[ ${VERBOSE} -eq 1 ]] && echo " contents of default.local"
724809 [[ ${VERBOSE} -eq 1 ]] && cat ${EESSI_TMPDIR} /repos_cfg/default.local
725810
726811 # if default.local is not BIND mounted into container, add it to BIND_PATHS
727812 src=${EESSI_TMPDIR} /repos_cfg/default.local
728813 target=/etc/cvmfs/default.local
729- if [[ ${BIND_PATHS} =~ " ${target} " ]]; then
730- fatal_error " BIND target in '${src} :${target} ' is already in paths to be bind mounted into the container ('${BIND_PATHS} ')" ${REPOSITORY_ERROR_EXITCODE}
731- fi
732- BIND_PATHS=" ${BIND_PATHS} ,${src} :${target} "
814+
815+ # check if target already exists in BIND_PATHS, and, if so, if sources are
816+ # the same
817+ conflict_src=$( check_bind_paths_for_target " ${target} " " ${src} " " ${BIND_PATHS} " )
818+ check_result=$?
819+
820+ case ${check_result} in
821+ 0)
822+ # target already bound with same source - no action needed
823+ [[ ${VERBOSE} -eq 1 ]] && echo " Bind mount already configured: ${src} :${target} "
824+ ;;
825+ 1)
826+ # target already bound with different source - conflict!
827+ fatal_error " BIND target '${target} ' conflict: already bound from '${conflict_src} ', cannot bind from '${src} '" ${REPOSITORY_ERROR_EXITCODE}
828+ ;;
829+ 2)
830+ # target not found - safe to add
831+ if [[ -z ${BIND_PATH} ]]; then
832+ BIND_PATHS=" ${src} :${target} "
833+ else
834+ BIND_PATHS=" ${BIND_PATHS} ,${src} :${target} "
835+ fi
836+ [[ ${VERBOSE} -eq 1 ]] && echo " Added bind mount: ${src} :${target} "
837+ ;;
838+ esac
733839fi
734840
735841# 4. set up vars and dirs specific to a scenario
0 commit comments