fix(ci): unblock e2e-s3tests startup and create disk dirs for distributed volumes (#4768)

The scheduled e2e-s3tests sweep failed at "Wait for RustFS ready" in both
topologies because the server never started (issue #4762).

Two independent startup faults, both surfaced now that the local
physical-disk-independence guard is enforced:

- single: RUSTFS_VOLUMES=/data/rustfs{0...3} all live on one physical
  device on the runner, so the guard aborts startup. Set the
  CI-sanctioned RUSTFS_UNSAFE_BYPASS_DISK_CHECK=true (what the guard's own
  error message and the e2e tests already use).

- multi: the entrypoint's process_data_volumes skipped every non-absolute
  entry, so the distributed URL form
  "http://rustfs{1...4}:9000/data/rustfs{0...3}" never created
  /data/rustfs0..3. LocalDisk init then aborts with VolumeNotFound because
  resolve_local_disk_root no longer auto-creates the disk root. This also
  broke the shipped .docker/compose/docker-compose.cluster.yaml for real
  distributed docker deployments.

entrypoint.sh now:
  1. Expands multiple {N...M} ranges per token (the URL form carries two).
     The previous single-pass expander collapsed it to "http://rustfs1"
     and dropped the disk path; it now re-scans until no ranges remain,
     operating on only the first brace to keep multi-range tokens intact.
  2. Creates the local filesystem path for URL-form endpoints (stripping
     scheme://host:port) without appending them as CLI args — rustfs reads
     the distributed form from RUSTFS_VOLUMES directly.

Absolute-path and default (/data) inputs expand byte-identically to before.
The multi compose also gets the CI disk-check bypass, since the four disks
share one device inside each node's container.
This commit is contained in:
Zhengchao An
2026-07-12 13:41:46 +08:00
committed by GitHub
parent e3533a4611
commit b235762fdb
2 changed files with 69 additions and 31 deletions
+9
View File
@@ -174,6 +174,10 @@ jobs:
run: | run: |
docker network inspect rustfs-net >/dev/null 2>&1 || docker network create rustfs-net docker network inspect rustfs-net >/dev/null 2>&1 || docker network create rustfs-net
docker rm -f rustfs-single >/dev/null 2>&1 || true docker rm -f rustfs-single >/dev/null 2>&1 || true
# The four disks share one physical device on the runner (a single
# loopback filesystem), so the local physical-disk-independence guard
# would refuse to start. Bypass it — this is the CI use case the guard
# explicitly sanctions via RUSTFS_UNSAFE_BYPASS_DISK_CHECK.
docker run -d --name rustfs-single \ docker run -d --name rustfs-single \
--network rustfs-net \ --network rustfs-net \
-p "${S3_PORT}:9000" \ -p "${S3_PORT}:9000" \
@@ -181,6 +185,7 @@ jobs:
-e RUSTFS_ACCESS_KEY="${S3_ACCESS_KEY}" \ -e RUSTFS_ACCESS_KEY="${S3_ACCESS_KEY}" \
-e RUSTFS_SECRET_KEY="${S3_SECRET_KEY}" \ -e RUSTFS_SECRET_KEY="${S3_SECRET_KEY}" \
-e RUSTFS_VOLUMES="/data/rustfs{0...3}" \ -e RUSTFS_VOLUMES="/data/rustfs{0...3}" \
-e RUSTFS_UNSAFE_BYPASS_DISK_CHECK=true \
-v /tmp/rustfs-single:/data \ -v /tmp/rustfs-single:/data \
rustfs-ci rustfs-ci
@@ -200,6 +205,10 @@ jobs:
RUSTFS_ACCESS_KEY: ${S3_ACCESS_KEY} RUSTFS_ACCESS_KEY: ${S3_ACCESS_KEY}
RUSTFS_SECRET_KEY: ${S3_SECRET_KEY} RUSTFS_SECRET_KEY: ${S3_SECRET_KEY}
RUSTFS_VOLUMES: "http://rustfs{1...4}:9000/data/rustfs{0...3}" RUSTFS_VOLUMES: "http://rustfs{1...4}:9000/data/rustfs{0...3}"
# Each node's four disks share one physical device inside its
# container, so bypass the local physical-disk-independence guard
# (the CI use case it explicitly sanctions).
RUSTFS_UNSAFE_BYPASS_DISK_CHECK: "true"
services: services:
rustfs1: rustfs1:
+58 -29
View File
@@ -191,43 +191,72 @@ process_data_volumes() {
# Convert comma/tab to space # Convert comma/tab to space
VOLUME_LIST_RAW=$(echo "$VOLUME_RAW" | tr ',\t' ' ') VOLUME_LIST_RAW=$(echo "$VOLUME_RAW" | tr ',\t' ' ')
VOLUME_LIST="" # Manually expand {N...M} ranges since sh doesn't support brace expansion on
for vol in $VOLUME_LIST_RAW; do # variables. A single token can carry MORE than one range — the distributed
# Helper to manually expand {N..M} since sh doesn't support it on variables # form "http://rustfs{1...4}:9000/data/rustfs{0...3}" has two — so expand the
if echo "$vol" | grep -E -q "\{[0-9]+\.\.\.?[0-9]+\}"; then # FIRST range in each token and re-scan until no ranges remain. Operating on
PREFIX=${vol%%\{*} # only the first brace (via #*{ / %%}* rather than ##*} / %}) is what keeps
SUFFIX=${vol##*\}} # multi-range tokens intact; the previous single-pass expander collapsed them
RANGE=${vol#*\{} # to "http://rustfs1" and silently dropped the disk path.
RANGE=${RANGE%\}} VOLUME_LIST="$VOLUME_LIST_RAW"
RANGE=$(echo "$RANGE" | sed 's/\.\.\./../') while echo "$VOLUME_LIST" | grep -E -q "\{[0-9]+\.\.\.?[0-9]+\}"; do
START=${RANGE%%..*} NEXT_LIST=""
END=${RANGE##*..} for vol in $VOLUME_LIST; do
if echo "$vol" | grep -E -q "\{[0-9]+\.\.\.?[0-9]+\}"; then
PREFIX=${vol%%\{*}
REST=${vol#*\}}
RANGE=${vol#*\{}
RANGE=${RANGE%%\}*}
RANGE=$(echo "$RANGE" | sed 's/\.\.\./../')
START=${RANGE%%..*}
END=${RANGE##*..}
# Check if START and END are numbers # Check if START and END are numbers
if [ "$START" -eq "$START" ] 2>/dev/null && [ "$END" -eq "$END" 2>/dev/null ]; then if [ "$START" -eq "$START" ] 2>/dev/null && [ "$END" -eq "$END" 2>/dev/null ]; then
i=$START i=$START
while [ "$i" -le "$END" ]; do while [ "$i" -le "$END" ]; do
VOLUME_LIST="$VOLUME_LIST ${PREFIX}${i}${SUFFIX}" NEXT_LIST="$NEXT_LIST ${PREFIX}${i}${REST}"
i=$((i+1)) i=$((i+1))
done done
else else
# Fallback if not numbers # Fallback if not numbers
VOLUME_LIST="$VOLUME_LIST $vol" NEXT_LIST="$NEXT_LIST $vol"
fi fi
else else
VOLUME_LIST="$VOLUME_LIST $vol" NEXT_LIST="$NEXT_LIST $vol"
fi fi
done
VOLUME_LIST="$NEXT_LIST"
done done
# CREATE_DIRS holds every local filesystem path that must exist before
# startup. DATA_VOLUMES holds only the paths that are also appended as CLI
# args (bare absolute paths). Distributed URL-form endpoints are read from
# RUSTFS_VOLUMES by rustfs directly, so they must NOT be appended as args,
# but their local path component still has to exist on disk — otherwise
# LocalDisk init aborts with VolumeNotFound (rustfs no longer auto-creates
# the disk root).
CREATE_DIRS=""
for vol in $VOLUME_LIST; do for vol in $VOLUME_LIST; do
case "$vol" in case "$vol" in
/*) DATA_VOLUMES="$DATA_VOLUMES $vol" ;; /*)
*) : ;; # skip non-absolute paths (including http(s):// URLs) DATA_VOLUMES="$DATA_VOLUMES $vol"
CREATE_DIRS="$CREATE_DIRS $vol"
;;
*://*)
# e.g. http://node0:9000/data/rustfs0 -> /data/rustfs0
vol_rest=${vol#*://}
case "$vol_rest" in
*/*) CREATE_DIRS="$CREATE_DIRS /${vol_rest#*/}" ;;
*) : ;; # URL without a path component; nothing local to create
esac
;;
*) : ;; # skip anything else we don't recognize
esac esac
done done
echo "Initializing data directories:$DATA_VOLUMES" echo "Initializing data directories:$CREATE_DIRS"
for vol in $DATA_VOLUMES; do for vol in $CREATE_DIRS; do
if [ ! -d "$vol" ]; then if [ ! -d "$vol" ]; then
echo " mkdir -p $vol" echo " mkdir -p $vol"
mkdir -p "$vol" mkdir -p "$vol"