mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-29 03:45:46 +08:00
ci(nightly): persist the nightly deb on Cloudflare R2 (#6643)
* ci(nightly): persist the nightly deb on Cloudflare R2 Upload the deb to artifacts/rustfs/packages/nightly/ (dated name plus a rustfs-nightly-latest.deb alias) through the same R2 channel package.yml uses, so the nightly package can be downloaded later with a stable URL. The step is skipped when the R2 secrets are not configured, keeping the artifact-only mode intact. * test: add pool expansion / decommission E2E script and workflow Add the admin-API based pool expansion, rebalance and decommission test script (scripts/test/rustfs_pool_expand.sh) plus a workflow_dispatch / nightly workflow that runs it on a self-hosted runner against real nodes. The workflow accepts a release tag or a direct .deb URL (e.g. nightly/R2 package) via the package_url input. * ci(pool-test): run the pool expansion test on the smoke-testing runner
This commit is contained in:
@@ -161,6 +161,49 @@ jobs:
|
||||
path: ${{ steps.deb.outputs.deb_file }}
|
||||
if-no-files-found: error
|
||||
|
||||
# Persist the nightly deb on Cloudflare R2 (same channel as package.yml)
|
||||
# so it can be downloaded later with a stable, unauthenticated URL —
|
||||
# e.g. https://dl.rustfs.com/artifacts/rustfs/packages/nightly/... .
|
||||
# Skipped when the R2 secrets are not configured (artifact-only mode).
|
||||
- name: Upload DEB to Cloudflare R2
|
||||
if: env.R2_ACCESS_KEY_ID != ''
|
||||
env:
|
||||
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
|
||||
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
|
||||
R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
|
||||
R2_BUCKET: ${{ secrets.R2_BUCKET }}
|
||||
AWS_EC2_METADATA_DISABLED: true
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if [[ -z "$R2_ACCESS_KEY_ID" || -z "$R2_SECRET_ACCESS_KEY" || -z "$R2_ENDPOINT" || -z "$R2_BUCKET" ]]; then
|
||||
echo "⚠️ R2 credentials missing, skipping upload"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! command -v aws >/dev/null 2>&1; then
|
||||
sudo apt-get update && sudo apt-get install -y -qq awscli
|
||||
fi
|
||||
|
||||
export AWS_ACCESS_KEY_ID="$R2_ACCESS_KEY_ID"
|
||||
export AWS_SECRET_ACCESS_KEY="$R2_SECRET_ACCESS_KEY"
|
||||
export AWS_DEFAULT_REGION="auto"
|
||||
|
||||
DEB_FILE="${{ steps.deb.outputs.deb_file }}"
|
||||
R2_PREFIX="s3://${R2_BUCKET}/artifacts/rustfs/packages/nightly/"
|
||||
|
||||
echo "📤 Uploading ${DEB_FILE} to ${R2_PREFIX}"
|
||||
aws s3 cp "${DEB_FILE}" "${R2_PREFIX}" --endpoint-url "$R2_ENDPOINT" --only-show-errors
|
||||
|
||||
# Stable "latest" alias so tests can fetch the newest nightly
|
||||
# without knowing today's date.
|
||||
echo "📤 Uploading latest alias"
|
||||
aws s3 cp "${DEB_FILE}" "${R2_PREFIX}rustfs-nightly-latest.deb" \
|
||||
--endpoint-url "$R2_ENDPOINT" --only-show-errors
|
||||
|
||||
echo "✅ R2 upload complete"
|
||||
|
||||
# Live-Vault lane for the rustfs-kms suite (rustfs/backlog#1774).
|
||||
#
|
||||
# RUSTFS_KMS_VAULT_TOKEN is the single switch that adds the Vault KV2 and
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
name: RustFS Pool Expansion / Decommission Test
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
rustfs_version:
|
||||
description: 'RustFS release tag to test (e.g. 1.0.0-rc.3)'
|
||||
required: false
|
||||
default: '1.0.0-rc.3'
|
||||
package_url:
|
||||
description: 'Direct .deb URL (nightly/R2/dev). Overrides rustfs_version.'
|
||||
required: false
|
||||
type: string
|
||||
pools:
|
||||
description: 'Number of pools to expand to (2 = first rebalance only)'
|
||||
type: choice
|
||||
options:
|
||||
- '2'
|
||||
- '3'
|
||||
default: '3'
|
||||
storage_threshold:
|
||||
description: 'Stop writing when storage usage reaches N%'
|
||||
required: false
|
||||
default: '50'
|
||||
warp_duration:
|
||||
description: 'warp write duration (e.g. 5m, 10m)'
|
||||
required: false
|
||||
default: '10m'
|
||||
run_decommission:
|
||||
description: 'Run the pool decommission step (3-pool topology only)'
|
||||
type: boolean
|
||||
default: true
|
||||
cleanup_before:
|
||||
description: 'Reset the nodes before the test (DESTROYS existing data/config)'
|
||||
type: boolean
|
||||
default: true
|
||||
cleanup_after:
|
||||
description: 'Reset the nodes after the test (DESTROYS test data/config)'
|
||||
type: boolean
|
||||
default: true
|
||||
schedule:
|
||||
# Nightly regression run; remove if you do not want a schedule.
|
||||
- cron: '0 21 * * *'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# Only one pool-expansion test at a time: the workflow mutates a shared
|
||||
# test environment, so concurrent runs must not clobber each other.
|
||||
concurrency:
|
||||
group: rustfs-pool-expansion-test
|
||||
cancel-in-progress: false
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
RUSTFS_ACCESS_KEY: ${{ secrets.RUSTFS_ACCESS_KEY }}
|
||||
RUSTFS_SECRET_KEY: ${{ secrets.RUSTFS_SECRET_KEY }}
|
||||
RUSTFS_API_ENDPOINT: ${{ vars.RUSTFS_API_ENDPOINT || vars.RUSTFS_RC_ENDPOINT }}
|
||||
RUSTFS_NODES: ${{ vars.RUSTFS_NODES }}
|
||||
RUSTFS_SSH_USER: ${{ vars.RUSTFS_SSH_USER }}
|
||||
|
||||
jobs:
|
||||
pool-expansion-test:
|
||||
runs-on: smoke-testing
|
||||
timeout-minutes: 360
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Show environment
|
||||
run: |
|
||||
uname -a
|
||||
jq --version
|
||||
openssl version
|
||||
warp --version || true
|
||||
df -h /data | tail -1
|
||||
|
||||
- name: Reset test environment (before)
|
||||
if: ${{ inputs.cleanup_before }}
|
||||
run: |
|
||||
chmod +x scripts/test/rustfs_pool_expand.sh
|
||||
./scripts/test/rustfs_pool_expand.sh --reset -y
|
||||
|
||||
- name: Preflight checks
|
||||
run: |
|
||||
ARGS=(--preflight --endpoint "${{ env.RUSTFS_API_ENDPOINT }}")
|
||||
if [ -n "${{ inputs.package_url }}" ]; then
|
||||
ARGS+=(--package-url "${{ inputs.package_url }}")
|
||||
else
|
||||
ARGS+=(--version "${{ inputs.rustfs_version }}")
|
||||
fi
|
||||
./scripts/test/rustfs_pool_expand.sh "${ARGS[@]}"
|
||||
|
||||
- name: Run pool expansion & decommission test
|
||||
id: pool_test
|
||||
run: |
|
||||
set -o pipefail
|
||||
STEPS="1,2,3,4,5,6"
|
||||
if [ "${{ inputs.pools }}" = "3" ]; then
|
||||
STEPS="$STEPS,7,8"
|
||||
if [ "${{ inputs.run_decommission }}" = "true" ]; then
|
||||
STEPS="$STEPS,9"
|
||||
fi
|
||||
fi
|
||||
ARGS=(--steps "$STEPS" --with-warp -y \
|
||||
--endpoint "${{ env.RUSTFS_API_ENDPOINT }}" \
|
||||
--storage-threshold "${{ inputs.storage_threshold }}" \
|
||||
--warp-duration "${{ inputs.warp_duration }}" \
|
||||
--log-file /tmp/rustfs-pool-test.log)
|
||||
if [ -n "${{ inputs.package_url }}" ]; then
|
||||
ARGS+=(--package-url "${{ inputs.package_url }}")
|
||||
else
|
||||
ARGS+=(--version "${{ inputs.rustfs_version }}")
|
||||
fi
|
||||
./scripts/test/rustfs_pool_expand.sh "${ARGS[@]}"
|
||||
|
||||
- name: Upload test logs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
with:
|
||||
name: rustfs-pool-test-${{ github.run_id }}
|
||||
path: |
|
||||
/tmp/rustfs-pool-test.log
|
||||
/tmp/rustfs-warp.log
|
||||
if-no-files-found: warn
|
||||
|
||||
- name: Reset test environment (after)
|
||||
if: ${{ always() && inputs.cleanup_after }}
|
||||
run: |
|
||||
./scripts/test/rustfs_pool_expand.sh --reset -y
|
||||
|
||||
- name: Notify on failure
|
||||
if: failure()
|
||||
run: |
|
||||
echo "RustFS pool expansion test failed"
|
||||
echo "Package source: ${{ inputs.package_url || format('release {0}', inputs.rustfs_version) }}"
|
||||
echo "See the uploaded log artifact for details."
|
||||
@@ -0,0 +1,106 @@
|
||||
# RustFS Pool Expansion / Decommission Test
|
||||
|
||||
End-to-end test for **storage pool expansion**, **data rebalancing** and
|
||||
**pool decommission**, driven by
|
||||
[`scripts/test/rustfs_pool_expand.sh`](rustfs_pool_expand.sh).
|
||||
|
||||
All pool / rebalance / decommission checks talk to the RustFS **admin API
|
||||
directly** with SigV4-signed HTTP requests and assert on the JSON responses
|
||||
(`jq`), so the results are exact and independent of any client CLI output
|
||||
formatting. **`rc` is not required.**
|
||||
|
||||
## What it does
|
||||
|
||||
1. Downloads the RustFS `.deb` package on all nodes (a release tag, or a
|
||||
direct URL such as a nightly/R2 package).
|
||||
2. Installs it (`dpkg -i`).
|
||||
3. Starts the first pool and verifies it via
|
||||
`GET /rustfs/admin/v3/pools/list`.
|
||||
4. Writes data with `warp` and monitors storage usage
|
||||
(`GET /rustfs/admin/v3/storageinfo`) until the threshold.
|
||||
5. Expands to a second pool (nodes started in parallel).
|
||||
6. Starts rebalance (`POST /rustfs/admin/v3/rebalance/start`) and waits for
|
||||
**all** pools to report `Completed`.
|
||||
7. (3-pool mode) Expands to a third pool, rebalances again.
|
||||
8. (optional) Decommissions pool 0
|
||||
(`POST /rustfs/admin/v3/pools/decommission`), with automatic clear + retry
|
||||
on failure, and waits for `decommissionInfo.complete == true`.
|
||||
|
||||
When an assertion fails, the script prints a per-pool summary **and the full
|
||||
JSON response** (pool state, progress, failure counters, `waitingReason`,
|
||||
`unresolvedEntries`, last rebalance error), so the GitHub Actions log shows
|
||||
exactly where the test stopped. Credentials never appear in the logs.
|
||||
|
||||
## Self-hosted runner prerequisites
|
||||
|
||||
- Register the admin host (e.g. `heal`) as a runner with the
|
||||
`smoke-testing` label.
|
||||
- Install `jq`, `openssl`, `curl` and `warp` (only needed for `--with-warp`)
|
||||
on the runner. `rc` is **not** required.
|
||||
- The runner user must be able to SSH to all nodes without a password prompt
|
||||
(`~/.ssh/config` with keys).
|
||||
- The nodes need passwordless `sudo` for the SSH user, resolvable
|
||||
`rustfs-node*` hostnames in `/etc/hosts`, and writable data directories.
|
||||
- The admin API credentials must have the `admin:server-info`,
|
||||
`admin:decommission` and `admin:rebalance` actions.
|
||||
|
||||
## Configuration
|
||||
|
||||
Set these in the repository (secrets/variables):
|
||||
|
||||
| Kind | Name | Purpose |
|
||||
| ------ | --------------------- | ---------------------------------------------- |
|
||||
| Secret | `RUSTFS_ACCESS_KEY` | RustFS access key |
|
||||
| Secret | `RUSTFS_SECRET_KEY` | RustFS secret key |
|
||||
| Var | `RUSTFS_API_ENDPOINT` | Admin API endpoint, e.g. `http://10.0.0.7:9000` (`RUSTFS_RC_ENDPOINT` is used as a fallback) |
|
||||
| Var | `RUSTFS_NODES` | Space-separated node names, e.g. `vm000 vm001 vm002` |
|
||||
| Var | `RUSTFS_SSH_USER` | SSH user for the nodes, e.g. `azureuser` |
|
||||
|
||||
## Workflow inputs
|
||||
|
||||
| Input | Default | Meaning |
|
||||
| ------------------- | ------------- | ---------------------------------------- |
|
||||
| `rustfs_version` | `1.0.0-rc.3` | GitHub release tag to test |
|
||||
| `package_url` | *(empty)* | Direct `.deb` URL (e.g. nightly/R2); overrides `rustfs_version` |
|
||||
| `pools` | `3` | Expand to 2 or 3 pools |
|
||||
| `storage_threshold` | `50` | Stop warp writes at N% usage |
|
||||
| `warp_duration` | `10m` | warp write duration |
|
||||
| `run_decommission` | `true` | Run decommission (3-pool mode only) |
|
||||
| `cleanup_before` | `true` | Reset nodes before the test |
|
||||
| `cleanup_after` | `true` | Reset nodes after the test |
|
||||
|
||||
> ⚠️ `cleanup_before` / `cleanup_after` run the script's `--reset` mode, which
|
||||
> **stops the services and deletes the data directories and config** on all
|
||||
> nodes. Only use this workflow against a dedicated test environment.
|
||||
|
||||
## Manual usage
|
||||
|
||||
```bash
|
||||
# Full workflow with a release tag
|
||||
./scripts/test/rustfs_pool_expand.sh --all --with-warp -y \
|
||||
--version 1.0.0-rc.3 --endpoint http://10.0.0.7:9000
|
||||
|
||||
# Use a direct .deb URL (e.g. nightly package on R2)
|
||||
./scripts/test/rustfs_pool_expand.sh --all --with-warp -y \
|
||||
--package-url https://dl.rustfs.com/artifacts/rustfs/packages/nightly/rustfs-nightly-latest.deb \
|
||||
--endpoint http://10.0.0.7:9000
|
||||
|
||||
# Preflight / reset / single step
|
||||
./scripts/test/rustfs_pool_expand.sh --preflight --version 1.0.0-rc.3
|
||||
./scripts/test/rustfs_pool_expand.sh --reset -y
|
||||
./scripts/test/rustfs_pool_expand.sh --step 9 --finalize-decommission -y
|
||||
```
|
||||
|
||||
## Known issues and caveats
|
||||
|
||||
- RustFS `1.0.0-rc.3` fails decommission with a large `warp`-written bucket
|
||||
("metacache listing quorum failed / timeout"). If decommission repeatedly
|
||||
fails, reduce the written data (lower `storage_threshold`) or remove the test
|
||||
bucket, then re-run step 9. The script detects the failure, clears metadata
|
||||
and retries `DECOMMISSION_RETRIES` times before giving up.
|
||||
- Multi-pool nodes must start **simultaneously** (the script does this) or the
|
||||
first node dies with `not first disk`.
|
||||
- The admin API is SigV4-signed (`host`, `x-amz-content-sha256:
|
||||
UNSIGNED-PAYLOAD`, `x-amz-date`), matching the signer the RustFS server
|
||||
itself trusts. If the cluster requires a non-default region, set
|
||||
`SIGV4_REGION` in the script.
|
||||
Executable
+1074
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user