diff --git a/.github/workflows/e2e-s3tests.yml b/.github/workflows/e2e-s3tests.yml index c4f02c4d1..23598e803 100644 --- a/.github/workflows/e2e-s3tests.yml +++ b/.github/workflows/e2e-s3tests.yml @@ -174,6 +174,10 @@ jobs: run: | docker network inspect rustfs-net >/dev/null 2>&1 || docker network create rustfs-net 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 \ --network rustfs-net \ -p "${S3_PORT}:9000" \ @@ -181,6 +185,7 @@ jobs: -e RUSTFS_ACCESS_KEY="${S3_ACCESS_KEY}" \ -e RUSTFS_SECRET_KEY="${S3_SECRET_KEY}" \ -e RUSTFS_VOLUMES="/data/rustfs{0...3}" \ + -e RUSTFS_UNSAFE_BYPASS_DISK_CHECK=true \ -v /tmp/rustfs-single:/data \ rustfs-ci @@ -200,6 +205,10 @@ jobs: RUSTFS_ACCESS_KEY: ${S3_ACCESS_KEY} RUSTFS_SECRET_KEY: ${S3_SECRET_KEY} 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: rustfs1: diff --git a/entrypoint.sh b/entrypoint.sh index a302dcffa..53f776395 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -191,43 +191,72 @@ process_data_volumes() { # Convert comma/tab to space VOLUME_LIST_RAW=$(echo "$VOLUME_RAW" | tr ',\t' ' ') - VOLUME_LIST="" - for vol in $VOLUME_LIST_RAW; do - # Helper to manually expand {N..M} since sh doesn't support it on variables - if echo "$vol" | grep -E -q "\{[0-9]+\.\.\.?[0-9]+\}"; then - PREFIX=${vol%%\{*} - SUFFIX=${vol##*\}} - RANGE=${vol#*\{} - RANGE=${RANGE%\}} - RANGE=$(echo "$RANGE" | sed 's/\.\.\./../') - START=${RANGE%%..*} - END=${RANGE##*..} - - # Check if START and END are numbers - if [ "$START" -eq "$START" ] 2>/dev/null && [ "$END" -eq "$END" 2>/dev/null ]; then - i=$START - while [ "$i" -le "$END" ]; do - VOLUME_LIST="$VOLUME_LIST ${PREFIX}${i}${SUFFIX}" - i=$((i+1)) - done - else - # Fallback if not numbers - VOLUME_LIST="$VOLUME_LIST $vol" - fi - else - VOLUME_LIST="$VOLUME_LIST $vol" - fi + # Manually expand {N...M} ranges since sh doesn't support brace expansion on + # variables. A single token can carry MORE than one range — the distributed + # form "http://rustfs{1...4}:9000/data/rustfs{0...3}" has two — so expand the + # FIRST range in each token and re-scan until no ranges remain. Operating on + # only the first brace (via #*{ / %%}* rather than ##*} / %}) is what keeps + # multi-range tokens intact; the previous single-pass expander collapsed them + # to "http://rustfs1" and silently dropped the disk path. + VOLUME_LIST="$VOLUME_LIST_RAW" + while echo "$VOLUME_LIST" | grep -E -q "\{[0-9]+\.\.\.?[0-9]+\}"; do + NEXT_LIST="" + 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 + if [ "$START" -eq "$START" ] 2>/dev/null && [ "$END" -eq "$END" 2>/dev/null ]; then + i=$START + while [ "$i" -le "$END" ]; do + NEXT_LIST="$NEXT_LIST ${PREFIX}${i}${REST}" + i=$((i+1)) + done + else + # Fallback if not numbers + NEXT_LIST="$NEXT_LIST $vol" + fi + else + NEXT_LIST="$NEXT_LIST $vol" + fi + done + VOLUME_LIST="$NEXT_LIST" 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 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 done - - echo "Initializing data directories:$DATA_VOLUMES" - for vol in $DATA_VOLUMES; do + + echo "Initializing data directories:$CREATE_DIRS" + for vol in $CREATE_DIRS; do if [ ! -d "$vol" ]; then echo " mkdir -p $vol" mkdir -p "$vol"