fix(web-app-moodle): make the code-volume bootstrap race-safe on shared NFS

moodle runs replicas:3 sharing one NFS code volume. The entrypoint guarded its cp -a with a version.php TOCTOU check, so several replicas copied concurrently; losers hit 'File exists' and, under set -euo pipefail, aborted before chown + php-fpm, leaving an incomplete/unreadable tree -> install_database.php failed Permission denied (rc 255). Serialize the bootstrap under flock, gate on an atomic .bootstrap.done sentinel, cp -an || true so a benign collision can't abort, make the tree world-readable before the install (root_squash neuters the in-container chown), and make losing replicas wait for the sentinel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 4.8 committed 2026-06-27 00:50:53 +02:00
1 parent b5272e2e17
commit 5c3749c437
1 file changed
+27 -3
@@ -14,13 +14,37 @@ set -euo pipefail
: "${MOODLE_RUNTIME_USER:?required}"
: "${MOODLE_VERSION_FILE:?required}"
MOODLE_BOOTSTRAP_SENTINEL="${MOODLE_CODE_DIR}/.bootstrap.done"
MOODLE_BOOTSTRAP_LOCK="${MOODLE_CODE_DIR}/.bootstrap.lock"
mkdir -p "${MOODLE_DATA_DIR}"
chown -R "${MOODLE_RUNTIME_USER}:${MOODLE_RUNTIME_USER}" "${MOODLE_DATA_DIR}" || true
if [ ! -f "${MOODLE_CODE_DIR}/${MOODLE_VERSION_FILE}" ] && [ -d "${MOODLE_SOURCE_DIR}" ]; then
cp -a "${MOODLE_SOURCE_DIR}/." "${MOODLE_CODE_DIR}/"
moodle_bootstrap_code_dir() {
if [ -f "${MOODLE_BOOTSTRAP_SENTINEL}" ]; then
return 0
fi
if [ -d "${MOODLE_SOURCE_DIR}" ]; then
cp -an "${MOODLE_SOURCE_DIR}/." "${MOODLE_CODE_DIR}/" || true
fi
chown -R "${MOODLE_RUNTIME_USER}:${MOODLE_RUNTIME_USER}" "${MOODLE_CODE_DIR}" || true
find "${MOODLE_CODE_DIR}" -type d -exec chmod 0755 {} + || true
find "${MOODLE_CODE_DIR}" -type f -exec chmod 0644 {} + || true
touch "${MOODLE_BOOTSTRAP_SENTINEL}"
}
if [ ! -f "${MOODLE_BOOTSTRAP_SENTINEL}" ]; then
mkdir -p "${MOODLE_CODE_DIR}"
exec 9>"${MOODLE_BOOTSTRAP_LOCK}"
flock 9
moodle_bootstrap_code_dir
flock -u 9
exec 9>&-
fi
chown -R "${MOODLE_RUNTIME_USER}:${MOODLE_RUNTIME_USER}" "${MOODLE_CODE_DIR}" || true
while [ ! -f "${MOODLE_BOOTSTRAP_SENTINEL}" ]; do
sleep 1
done
if [ "$(id -u)" -eq 0 ] && [ "${1:-}" != "php-fpm" ]; then
exec gosu "${MOODLE_RUNTIME_USER}" "$@"