diff --git a/deploy/docker/base.dockerfile b/deploy/docker/base.dockerfile index 42f0c45ab41..ecb6e553fcb 100644 --- a/deploy/docker/base.dockerfile +++ b/deploy/docker/base.dockerfile @@ -27,9 +27,10 @@ RUN set -o xtrace \ software-properties-common \ git \ && add-apt-repository -y ppa:git-core/ppa \ - # Install MongoDB v6, PostgreSQL v14 - && curl -fsSL https://www.mongodb.org/static/pgp/server-6.0.asc | gpg --dearmor -o /usr/share/keyrings/mongodb-server-6.0.gpg \ - && echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-6.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/6.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-6.0.list \ + # Install MongoDB v7, PostgreSQL v14 + # Note: MongoDB 7.0 does not publish apt packages for Ubuntu 24.04 (noble) yet, so we use the jammy (22.04) packages — same pattern used for the previous 6.0 install. + && curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | gpg --dearmor -o /usr/share/keyrings/mongodb-server-7.0.gpg \ + && echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-7.0.list \ && echo "deb http://apt.postgresql.org/pub/repos/apt $(grep CODENAME /etc/lsb-release | cut -d= -f2)-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list \ && curl --silent --show-error --location https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \ && apt update \ diff --git a/deploy/docker/fs/opt/appsmith/entrypoint.sh b/deploy/docker/fs/opt/appsmith/entrypoint.sh index 00f1920f5e2..50cc0fe54ac 100644 --- a/deploy/docker/fs/opt/appsmith/entrypoint.sh +++ b/deploy/docker/fs/opt/appsmith/entrypoint.sh @@ -334,6 +334,80 @@ init_replica_set() { fi } +# Pre-flight check for embedded MongoDB 7.0 upgrades on existing data. +# +# MongoDB 7.0 refuses to start on data whose featureCompatibilityVersion (FCV) +# is below 6.0. Without a check up front, supervisord would retry mongod a few +# times and give up, leaving the container in a confusing degraded state with +# no clear error for the administrator. +# +# Fast path: marker file ($MONGO_DB_PATH/.appsmith-mongo-fcv-min) is written by +# mongodb-fixer.sh only after mongod is confirmed running under this Appsmith +# release. Its presence proves this release has already booted successfully on +# this data, so the probe can be skipped with zero overhead. Contents record +# the FCV floor this release commits to, for future upgrade decisions; they +# aren't consulted here. +# +# First boot after upgrade: no marker yet. Do a one-time mongod --fork probe +# to verify the data is compatible. If it starts, proceed; the fixer will +# write the marker after supervisord brings mongod up for real. If it fails, +# print an actionable error and exit. +ensure_mongodb_fcv_compatible() { + # Only applies to existing local-Mongo data — fresh installs have nothing to + # check, and external Mongo is out of our control. + if [[ $shouldPerformInitdb -gt 0 || $isUriLocal -gt 0 ]]; then + return + fi + + local marker="$MONGO_DB_PATH/.appsmith-mongo-fcv-min" + if [[ -f "$marker" ]]; then + tlog "MongoDB FCV marker present; skipping pre-flight check" + return + fi + + tlog "No MongoDB FCV marker found on existing data; running one-time compatibility probe" + # Persist the probe log inside the Mongo data directory so it survives container + # restarts. $TMP would be wiped, leaving no forensic trail when an admin comes + # back to investigate why their container exited. Append rather than truncate so + # repeated probe runs (e.g. multiple failed upgrade attempts) all stay on record. + local probe_log="$MONGO_DB_PATH/fcv-probe.log" + printf '\n===== Appsmith MongoDB FCV pre-flight probe @ %s =====\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$probe_log" 2>/dev/null || true + if mongod --fork --port 27017 --dbpath "$MONGO_DB_PATH" --logpath "$probe_log" --logappend --bind_ip localhost >/dev/null 2>&1; then + if ! mongod --dbpath "$MONGO_DB_PATH" --shutdown >/dev/null 2>&1; then + tlog "ERROR: Pre-flight mongod probe started but shutdown failed. The probe mongod may still hold port 27017 or the data lock, which would prevent supervisord from starting mongod. Aborting. See $probe_log for details." >&2 + exit 1 + fi + tlog "Pre-flight probe succeeded; mongodb-fixer will write the FCV marker after supervisord starts mongod" + return + fi + + local probe_err + probe_err="$(grep -Ei 'featurecompatibilityversion|upgrade|downgrade' "$probe_log" 2>/dev/null | tail -n 1 || true)" + tlog "====================================================================================================" >&2 + tlog "==" >&2 + tlog "== ERROR: Embedded MongoDB 7.0 failed to start on the existing data. The most common cause is that the data is at featureCompatibilityVersion below the required 6.0 minimum." >&2 + if [[ -n "$probe_err" ]]; then + tlog "== mongod log: $probe_err" >&2 + fi + tlog "==" >&2 + tlog "== About this error:" >&2 + tlog "== Appsmith 2.x ships with MongoDB 7.x, which requires the database to be at featureCompatibilityVersion (FCV) 6.0 or higher. Appsmith releases 1.96 to 1.99 automatically raise FCV to 6.0 on boot, so any instance that has run one of those releases is fine. Instances that have only ever run Appsmith older than 1.70 may still be at FCV 5.0, which MongoDB 7.x refuses to load." >&2 + tlog "==" >&2 + tlog "== This check only runs for instances using the embedded MongoDB. Instances configured with an external MongoDB are not affected." >&2 + tlog "==" >&2 + tlog "== The failure happens during MongoDB pre-flight, before any Appsmith service comes online. No Appsmith database migrations have been attempted, so rolling back to a 1.x release is simply a matter of changing the image version on your deployment." >&2 + tlog "==" >&2 + tlog "== To recover:" >&2 + tlog "==" >&2 + tlog "== 1. Alter your Appsmith deployment to use a release in the 1.96 to 1.99 range (we recommend the latest, 1.99). These ship with MongoDB 6.x and will raise the compatibility version automatically." >&2 + tlog "== 2. Let the container start fully so the MongoDB FCV upgrade completes." >&2 + tlog "== 3. Shut down, then alter your Appsmith deployment to use this version again." >&2 + tlog "==" >&2 + tlog "== Full mongod log: $probe_log" >&2 + tlog "====================================================================================================" >&2 + exit 1 +} + use-mongodb-key() { # We copy the MongoDB key file to `$MONGODB_TMP_KEY_PATH`, so that we can reliably set its permissions to 600. # Why? When the host machine of this Docker container is Windows, file permissions cannot be set on files in volumes. @@ -633,6 +707,7 @@ if [[ -z "${DYNO}" ]]; then tlog "Initializing MongoDB" init_mongodb init_replica_set + ensure_mongodb_fcv_compatible fi else # These functions are used to limit heap size for Backend process when deployed on Heroku diff --git a/deploy/docker/fs/opt/appsmith/mongodb-fixer.sh b/deploy/docker/fs/opt/appsmith/mongodb-fixer.sh index a08d0be98e4..e423edd7537 100644 --- a/deploy/docker/fs/opt/appsmith/mongodb-fixer.sh +++ b/deploy/docker/fs/opt/appsmith/mongodb-fixer.sh @@ -3,6 +3,33 @@ set -o errexit set -o nounset +# Marker file recording the minimum MongoDB featureCompatibilityVersion that +# this Appsmith release commits to preserve. Written once mongod is confirmed +# RUNNING under this release; read (presence only) by entrypoint.sh on +# subsequent boots to fast-path the pre-flight compatibility check. See +# entrypoint.sh::ensure_mongodb_fcv_compatible. +# +# The marker value is a release-level contract, not a live reading of mongod's +# current FCV. Use `mongosh` if you want the live value. +MONGO_FCV_MIN_MARKER="/appsmith-stacks/data/mongodb/.appsmith-mongo-fcv-min" + +# Minimum FCV this Appsmith release commits to preserve. We deliberately do +# NOT raise FCV to 7.0 — keeping it at 6.0 preserves the ability to roll back +# to a 6.x Appsmith release if something goes wrong. When MongoDB 8 arrives, +# bump this constant to 7.0; the ensure_fcv_floor block below will handle the +# `setFeatureCompatibilityVersion` call automatically. +FCV_MIN="6.0" + +write_fcv_marker() { + local value="$1" + local tmp="${MONGO_FCV_MIN_MARKER}.tmp" + if ! printf '%s\n' "$value" > "$tmp" 2>/dev/null; then + tlog "warning: failed to write FCV marker temp file" + return 0 + fi + mv -f "$tmp" "$MONGO_FCV_MIN_MARKER" 2>/dev/null || tlog "warning: failed to move FCV marker into place" +} + { while [[ ! -S "$TMP/supervisor.sock" ]]; do @@ -10,22 +37,33 @@ while [[ ! -S "$TMP/supervisor.sock" ]]; do done tlog "supervisor.sock found" -while supervisorctl status mongodb | grep -q RUNNING; do +while ! supervisorctl status mongodb | grep -q RUNNING; do sleep 1 done tlog "MongoDB is RUNNING" +# Ensure FCV is at the floor this release commits to. In the steady state this +# is a no-op — entrypoint.sh's pre-flight probe already guarantees mongod won't +# come up on data below the supported FCV. The check is kept active so the +# upgrade scaffolding is exercised and the next major-version bump is just a +# constant change. +tlog "Ensuring MongoDB featureCompatibilityVersion is at least $FCV_MIN" for _ in {1..60}; do if mongosh --quiet "$APPSMITH_DB_URL" --eval ' - parseFloat(db.adminCommand({getParameter: 1, featureCompatibilityVersion: 1}).featureCompatibilityVersion.version) < 6 && - db.adminCommand({setFeatureCompatibilityVersion: "6.0"}) + const floor = '"$FCV_MIN"'; + const current = parseFloat(db.adminCommand({getParameter: 1, featureCompatibilityVersion: 1}).featureCompatibilityVersion.version); + if (current < floor) { + db.adminCommand({setFeatureCompatibilityVersion: "'"$FCV_MIN"'", confirm: true}); + } '; then - tlog "MongoDB version set to 6.0" + tlog "MongoDB featureCompatibilityVersion floor of $FCV_MIN confirmed" break fi sleep 1 done +tlog "Recording committed FCV minimum: $FCV_MIN" +write_fcv_marker "$FCV_MIN" tlog Done } | sed -u 's/^/mongodb-fixer: /'