mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 16:28:15 +00:00
f29455b32b
* ci: add e2e-full merge-gate job (single-node full e2e via nextest) Adds the [profile.e2e-full] nextest profile and a matching e2e-full job in ci.yml (push main + merge_group + workflow_dispatch) that runs the never-automated user-visible e2e suites (KMS, object_lock, multipart_auth, quota, checksum, encryption, security-boundary, ...) the fast PR e2e-smoke subset skips. The filter is the whole e2e_test crate minus the sets other lanes own: protocols:: (ci-6/ci-7), the 6 RustFSTestClusterEnvironment cluster suites (ci-7 nightly), replication_extension_test (repl-1's smoke + repl-nightly lanes), and #[ignore]d tests (ci-13). The 4-disk reliability tests are serialized, mirroring the ci profile. Reuses the downloaded debug binary and uploads junit. backlog#1149 ci-5. * ci: exclude characterized known-failures from e2e-full with tracked issues First-ever automated run of the full suite (dispatch 29381309848: 341 ran / 32 failed / 460s) surfaced five real product-bug families, each now tracked: rustfs#4842 (extract 500s, mtime=0 OffsetDateTime), rustfs#4843 (path-limit vs ignore-errors), rustfs#4844 (anonymous POST SSE-S3 500), rustfs#4845 (object-lock POST + list metadata=true 403), rustfs#4846 (lock quorum misclassified as timeout under load). Deterministic failures cannot be retried away, so each family is excluded with its OPEN issue cited and the fixing PR contract to delete the exclusion — passing negative-path siblings stay in as regression guards.
695 lines
28 KiB
YAML
695 lines
28 KiB
YAML
# Copyright 2024 RustFS Team
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
name: Continuous Integration
|
|
|
|
on:
|
|
push:
|
|
branches: [ main ]
|
|
paths-ignore:
|
|
- "**.md"
|
|
- "docs/**"
|
|
- "deploy/**"
|
|
- "scripts/dev_*.sh"
|
|
- "scripts/probe.sh"
|
|
- "LICENSE*"
|
|
- ".gitignore"
|
|
- ".dockerignore"
|
|
- "README*"
|
|
- "**/*.png"
|
|
- "**/*.jpg"
|
|
- "**/*.svg"
|
|
- ".github/workflows/build.yml"
|
|
- ".github/workflows/docker.yml"
|
|
- ".github/workflows/audit.yml"
|
|
pull_request:
|
|
types: [ opened, synchronize, reopened, closed ]
|
|
branches: [ main ]
|
|
# Keep this list in sync with the `paths` list in ci-docs-only.yml, which
|
|
# reports the required "Test and Lint" check for PRs skipped here.
|
|
paths-ignore:
|
|
- "**.md"
|
|
- "docs/**"
|
|
- "deploy/**"
|
|
- "scripts/dev_*.sh"
|
|
- "scripts/probe.sh"
|
|
- "LICENSE*"
|
|
- ".gitignore"
|
|
- ".dockerignore"
|
|
- "README*"
|
|
- "**/*.png"
|
|
- "**/*.jpg"
|
|
- "**/*.svg"
|
|
- ".github/workflows/build.yml"
|
|
- ".github/workflows/docker.yml"
|
|
- ".github/workflows/audit.yml"
|
|
merge_group:
|
|
types: [ checks_requested ]
|
|
schedule:
|
|
- cron: "0 0 * * 0" # Weekly on Sunday at midnight UTC
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
# Concurrency groups are scoped per event so different triggers never cancel
|
|
# each other: PR pushes cancel the previous run of that PR, main pushes keep
|
|
# latest-wins semantics among themselves, and scheduled runs always complete
|
|
# (a shared group used to let every merge kill the weekly scheduled run).
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: ${{ github.event_name != 'schedule' }}
|
|
|
|
env:
|
|
CARGO_TERM_COLOR: always
|
|
RUST_BACKTRACE: 1
|
|
|
|
jobs:
|
|
|
|
cancel-closed-pr-runs:
|
|
name: Cancel Closed PR Runs
|
|
if: github.event_name == 'pull_request' && github.event.action == 'closed'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Explain cancellation run
|
|
run: echo "PR closed; this run only cancels older runs in the same concurrency group."
|
|
|
|
typos:
|
|
name: Typos
|
|
if: github.event_name != 'pull_request' || github.event.action != 'closed'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
- name: Typos check with custom config file
|
|
uses: crate-ci/typos@37bb98842b0d8c4ffebdb75301a13db0267cef89 # master
|
|
|
|
# Fast, compile-free checks that fail early so contributors get feedback in
|
|
# ~1 minute instead of waiting for the full test job.
|
|
quick-checks:
|
|
name: Quick Checks
|
|
if: github.event_name != 'pull_request' || github.event.action != 'closed'
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
|
|
- name: Install ripgrep
|
|
run: sudo apt-get update && sudo apt-get install -y ripgrep
|
|
|
|
- name: Install Rust toolchain
|
|
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
|
|
with:
|
|
components: rustfmt
|
|
|
|
- name: Check code formatting
|
|
run: cargo fmt --all --check
|
|
|
|
- name: Check unsafe code allowances
|
|
run: ./scripts/check_unsafe_code_allowances.sh
|
|
|
|
- name: Check layered dependencies
|
|
run: ./scripts/check_layer_dependencies.sh
|
|
|
|
- name: Check architecture migration rules
|
|
run: ./scripts/check_architecture_migration_rules.sh
|
|
|
|
- name: Check tokio io-uring feature guard
|
|
run: ./scripts/check_no_tokio_io_uring.sh
|
|
|
|
- name: Check extension schema boundaries
|
|
run: ./scripts/check_extension_schema_boundaries.sh
|
|
|
|
- name: Check body-cache whitelist guard
|
|
run: ./scripts/check_body_cache_whitelist.sh
|
|
|
|
- name: Check no planning docs committed
|
|
run: ./scripts/check_no_planning_docs.sh
|
|
|
|
test-and-lint:
|
|
name: Test and Lint
|
|
if: github.event_name != 'pull_request' || github.event.action != 'closed'
|
|
runs-on: sm-standard-4
|
|
timeout-minutes: 60
|
|
env:
|
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
|
|
- name: Setup Rust environment
|
|
uses: ./.github/actions/setup
|
|
with:
|
|
rust-version: stable
|
|
cache-shared-key: ci-test
|
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
cache-save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
|
|
# Clippy runs before the test pass: lint failures are the most common
|
|
# CI-only breakage and should surface in minutes, not after 20+ minutes
|
|
# of tests.
|
|
- name: Run clippy lints
|
|
run: cargo clippy --all-targets -- -D warnings
|
|
|
|
- name: Run tests
|
|
run: |
|
|
cargo nextest run --profile ci --all --exclude e2e_test
|
|
cargo test --all --doc
|
|
|
|
- name: Upload test junit report
|
|
if: always()
|
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
|
with:
|
|
name: junit-test-and-lint-${{ github.run_number }}
|
|
path: target/nextest/ci/junit.xml
|
|
retention-days: 3
|
|
if-no-files-found: ignore
|
|
|
|
# Explicit gate for migration-critical suites. These tests already ran in
|
|
# the full nextest pass above; a single filtered nextest invocation keeps
|
|
# the named gate without rebuilding or re-running them one package at a time.
|
|
#
|
|
# The gate selects tests by name substring (data_movement / rebalance /
|
|
# decommission / source_cleanup / delete_marker), so renames can silently
|
|
# thin it. The script owns the filter expression and first verifies the
|
|
# selected-test count against the committed floor in
|
|
# .config/migration-gate-floor.txt before running the gate; renames or
|
|
# removals must update that file consciously (see the script header).
|
|
# Kept on the default profile (no --profile ci): a second --profile ci run
|
|
# would clobber target/nextest/ci/junit.xml, and none of these tests are
|
|
# quarantined so they gain nothing from the ci profile's retry overrides.
|
|
- name: Run rebalance/decommission migration proofs
|
|
run: ./scripts/check_migration_gate_count.sh
|
|
|
|
# Dedicated serial lane for the ILM / lifecycle integration tests. These tests
|
|
# drive the object layer through process-global singletons (the GLOBAL_ENV
|
|
# ECStore, the global tier-config manager, background-expiry workers) and bind
|
|
# fixed ports (9002 for the scanner suite, 9003 for the app suite), so they are
|
|
# marked #[ignore] to keep them out of the default parallel `cargo nextest run
|
|
# --all` pass. Run them here explicitly with `--run-ignored ignored-only` and
|
|
# `-j1` so no two of them share global state or race for a port at once.
|
|
# serial_test's #[serial] does NOT serialize across nextest's process-per-test
|
|
# boundary (see .config/nextest.toml); `-j1` is what actually serializes them.
|
|
# See rustfs/backlog#1148 (ilm-1) and #1155.
|
|
test-ilm-integration-serial:
|
|
name: ILM Integration (serial)
|
|
if: github.event_name != 'pull_request' || github.event.action != 'closed'
|
|
runs-on: sm-standard-4
|
|
timeout-minutes: 45
|
|
env:
|
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
|
|
- name: Setup Rust environment
|
|
uses: ./.github/actions/setup
|
|
with:
|
|
rust-version: stable
|
|
cache-shared-key: ci-ilm-serial
|
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
cache-save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
|
|
# Three scanner tests fail on main independently of this lane (restore of
|
|
# a transitioned multipart object, noncurrent transition/expiry after an
|
|
# immediate compensation transition); they keep #[ignore] with a backlog
|
|
# reference and are excluded here by name until fixed (rustfs/backlog#1148).
|
|
- name: Run ignored ILM integration tests serially
|
|
run: |
|
|
cargo nextest run -j1 --run-ignored ignored-only \
|
|
-p rustfs-scanner -p rustfs \
|
|
-E '(binary(lifecycle_integration_test) or (package(rustfs) and test(lifecycle_transition_api_test))) and not (test(test_transition_and_restore_flows) or test(test_noncurrent_expiry_still_works_after_immediate_compensation_transition) or test(test_noncurrent_transition_still_works_after_immediate_compensation_transition))'
|
|
|
|
test-and-lint-rio-v2:
|
|
name: Test and Lint (rio-v2)
|
|
if: github.event_name != 'pull_request' || github.event.action != 'closed'
|
|
runs-on: sm-standard-4
|
|
timeout-minutes: 60
|
|
env:
|
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
|
|
- name: Setup Rust environment
|
|
uses: ./.github/actions/setup
|
|
with:
|
|
rust-version: stable
|
|
cache-shared-key: ci-test-rio-v2
|
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
cache-save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
|
|
- name: Run rio-v2 clippy lints
|
|
run: cargo clippy -p rustfs -p rustfs-ecstore --all-targets --features rio-v2 -- -D warnings
|
|
|
|
- name: Run rio-v2 feature tests
|
|
run: |
|
|
cargo nextest run -p rustfs -p rustfs-ecstore --features rio-v2
|
|
cargo test -p rustfs --doc --features rio-v2
|
|
|
|
test-and-lint-protocols:
|
|
name: "Test and Lint (${{ matrix.features.name }})"
|
|
if: github.event_name != 'pull_request' || github.event.action != 'closed'
|
|
runs-on: sm-standard-4
|
|
timeout-minutes: 60
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
features:
|
|
- name: swift
|
|
flags: "--features swift"
|
|
- name: sftp
|
|
flags: "--features sftp"
|
|
env:
|
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
|
|
- name: Setup Rust environment
|
|
uses: ./.github/actions/setup
|
|
with:
|
|
rust-version: stable
|
|
cache-shared-key: ci-test-${{ matrix.features.name }}
|
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
cache-save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
|
|
- name: Run clippy with ${{ matrix.features.name }}
|
|
run: |
|
|
cargo clippy -p rustfs -p rustfs-protocols --all-targets ${{ matrix.features.flags }} -- -D warnings
|
|
|
|
- name: Run tests with ${{ matrix.features.name }}
|
|
run: |
|
|
cargo nextest run -p rustfs -p rustfs-protocols ${{ matrix.features.flags }}
|
|
|
|
build-rustfs-debug-binary:
|
|
name: Build RustFS Debug Binary
|
|
if: github.event_name != 'pull_request' || github.event.action != 'closed'
|
|
runs-on: sm-standard-4
|
|
timeout-minutes: 30
|
|
env:
|
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
|
|
- name: Setup Rust environment
|
|
uses: ./.github/actions/setup
|
|
with:
|
|
rust-version: stable
|
|
cache-shared-key: ci-rustfs-debug-binary
|
|
cache-save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Build debug binary
|
|
run: cargo build -p rustfs --bins
|
|
|
|
- name: Upload debug binary
|
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
|
with:
|
|
name: rustfs-debug-binary
|
|
path: target/debug/rustfs
|
|
if-no-files-found: error
|
|
retention-days: 1
|
|
|
|
build-rustfs-debug-binary-rio-v2:
|
|
name: Build RustFS Debug Binary (rio-v2)
|
|
if: github.event_name != 'pull_request' || github.event.action != 'closed'
|
|
runs-on: sm-standard-4
|
|
timeout-minutes: 30
|
|
env:
|
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
|
|
- name: Setup Rust environment
|
|
uses: ./.github/actions/setup
|
|
with:
|
|
rust-version: stable
|
|
cache-shared-key: ci-rustfs-debug-binary-rio-v2
|
|
cache-save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Build debug binary with rio-v2
|
|
run: cargo build -p rustfs --bins --features rio-v2
|
|
|
|
- name: Upload debug binary
|
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
|
with:
|
|
name: rustfs-debug-binary-rio-v2
|
|
path: target/debug/rustfs
|
|
if-no-files-found: error
|
|
retention-days: 1
|
|
|
|
uring-integration:
|
|
name: io_uring Integration (real)
|
|
# GitHub-hosted ubuntu-latest runs a recent kernel with io_uring and, unlike
|
|
# a container, applies no seccomp filter that would block io_uring_setup — so
|
|
# the probe succeeds and the tests exercise the real UringBackend/FdCache/
|
|
# latch paths instead of the StdBackend fallback (rustfs/backlog#1179). The
|
|
# self-hosted sm-standard runners cannot guarantee this.
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
|
|
- name: Setup Rust environment
|
|
uses: ./.github/actions/setup
|
|
with:
|
|
rust-version: stable
|
|
cache-shared-key: ci-uring
|
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
cache-save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
|
|
- name: Install build dependencies
|
|
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
|
|
|
|
# ext4 supports O_DIRECT; the runner's default TMPDIR may sit on tmpfs or
|
|
# overlayfs, where open(O_DIRECT) returns EINVAL/EOPNOTSUPP and the native
|
|
# read_at_direct path silently latches off to the aligned StdBackend
|
|
# fallback. A dedicated ext4 loopback guarantees the native O_DIRECT read
|
|
# path is actually exercised, not just the fallback (rustfs/backlog#1220).
|
|
- name: Prepare ext4 loopback for O_DIRECT coverage
|
|
run: |
|
|
set -euo pipefail
|
|
dd if=/dev/zero of=/tmp/rustfs-odirect.img bs=1M count=1024
|
|
mkfs.ext4 -q -F /tmp/rustfs-odirect.img
|
|
sudo mkdir -p /mnt/rustfs-odirect
|
|
sudo mount -o loop /tmp/rustfs-odirect.img /mnt/rustfs-odirect
|
|
sudo chmod 1777 /mnt/rustfs-odirect
|
|
mount | grep rustfs-odirect
|
|
|
|
# RUSTFS_URING_TESTS_MUST_RUN=1 makes the io_uring tests fail instead of
|
|
# silently skipping if io_uring is unavailable, so this leg can never pass
|
|
# vacuously. RUSTFS_IO_URING_READ_ENABLE=true selects the UringBackend.
|
|
# TMPDIR points every tempfile-based test fixture at the ext4 loopback so
|
|
# eligible reads keep O_DIRECT and the native path is covered end to end.
|
|
- name: Run ecstore io_uring integration tests on real io_uring
|
|
env:
|
|
RUSTFS_IO_URING_READ_ENABLE: "true"
|
|
RUSTFS_URING_TESTS_MUST_RUN: "1"
|
|
TMPDIR: /mnt/rustfs-odirect
|
|
run: cargo test -p rustfs-ecstore uring_ -- --test-threads=1 --nocapture
|
|
|
|
e2e-tests:
|
|
name: End-to-End Tests
|
|
needs: [ build-rustfs-debug-binary ]
|
|
runs-on: sm-standard-2
|
|
timeout-minutes: 30
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
|
|
# Full setup with dependency caching: the smoke-suite step below
|
|
# compiles the e2e_test crate, which pulls in most of the workspace.
|
|
# Without a restored cache this was a cold multi-GB build on every run.
|
|
- name: Setup Rust environment
|
|
uses: ./.github/actions/setup
|
|
with:
|
|
rust-version: stable
|
|
cache-shared-key: ci-e2e
|
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
cache-save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
|
|
# Download after the cache restore so the freshly built binary from the
|
|
# build job always wins over anything restored into target/debug.
|
|
- name: Download debug binary
|
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
|
with:
|
|
name: rustfs-debug-binary
|
|
path: target/debug
|
|
|
|
- name: Make binary executable
|
|
run: chmod +x ./target/debug/rustfs
|
|
|
|
# Guard the security negative-auth smoke subset (backlog#1151 sec-5)
|
|
# against a rename or deletion silently dropping it out of the e2e-smoke
|
|
# filter. The script lists what the profile selects and fails if the count
|
|
# of security auth-rejection tests falls below the committed floor in
|
|
# .config/security-smoke-floor.txt (infra-12 count-floor mechanism). Run
|
|
# before the smoke suite so a thinned gate fails fast; the `nextest list`
|
|
# here compiles the e2e_test binaries the run below reuses.
|
|
- name: Check security smoke subset count floor
|
|
run: ./scripts/check_security_smoke_count.sh check
|
|
|
|
# PR smoke subset of the in-repo e2e suite (backlog#1149 ci-4). The
|
|
# profile.e2e-smoke default-filter in .config/nextest.toml is the single
|
|
# wiring mechanism for e2e tests in CI — extend that filter instead of
|
|
# adding new e2e jobs here. Each test spawns its own rustfs server on a
|
|
# random port and reuses the downloaded debug binary above.
|
|
- name: Run e2e smoke suite
|
|
run: cargo nextest run --profile e2e-smoke -p e2e_test
|
|
|
|
- name: Install s3s-e2e test tool
|
|
uses: taiki-e/cache-cargo-install-action@7447f04c51f2ba27ca35e7f1e28fab848c5b3ba7 # v2
|
|
with:
|
|
tool: s3s-e2e
|
|
git: https://github.com/s3s-project/s3s.git
|
|
rev: 62cb4a71dd759a6ec56b64c4c42fcc183a2c6a52
|
|
|
|
- name: Run end-to-end tests
|
|
run: |
|
|
s3s-e2e --version
|
|
RUN_ROOT="${RUNNER_TEMP}/rustfs-e2e-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${GITHUB_JOB}"
|
|
mkdir -p "${RUN_ROOT}"
|
|
RUSTFS_TEST_PORT="$(python3 -c 'import socket; s=socket.socket(); s.bind(("127.0.0.1", 0)); print(s.getsockname()[1]); s.close()')"
|
|
RUSTFS_TEST_PORT="${RUSTFS_TEST_PORT}" \
|
|
RUSTFS_TEST_LOG="${RUN_ROOT}/rustfs.log" \
|
|
./scripts/e2e-run.sh ./target/debug/rustfs "${RUN_ROOT}/data"
|
|
|
|
- name: Upload test logs
|
|
if: failure()
|
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
|
with:
|
|
name: e2e-test-logs-${{ github.run_number }}
|
|
path: ${{ runner.temp }}/rustfs-e2e-*/rustfs.log
|
|
retention-days: 3
|
|
|
|
e2e-full:
|
|
name: End-to-End Tests (full merge gate)
|
|
# Merge gate only (backlog#1149 ci-5): the never-automated user-visible
|
|
# suites — KMS, object_lock, multipart_auth, quota, checksum, encryption,
|
|
# security-boundary, ... — via the e2e-full nextest profile. Too heavy for
|
|
# every PR, so it is gated to main pushes, the merge queue, and manual
|
|
# dispatch. protocols / the 6 cluster suites / replication / #[ignore] are
|
|
# owned by other lanes (see .config/nextest.toml profile.e2e-full).
|
|
if: >-
|
|
github.event_name == 'workflow_dispatch' ||
|
|
github.event_name == 'merge_group' ||
|
|
(github.event_name == 'push' && github.ref == 'refs/heads/main')
|
|
needs: [ build-rustfs-debug-binary ]
|
|
runs-on: sm-standard-2
|
|
timeout-minutes: 55
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
|
|
- name: Setup Rust environment
|
|
uses: ./.github/actions/setup
|
|
with:
|
|
rust-version: stable
|
|
cache-shared-key: ci-e2e
|
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
cache-save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
|
|
# Download after the cache restore so the freshly built binary from the
|
|
# build job always wins over anything restored into target/debug.
|
|
- name: Download debug binary
|
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
|
with:
|
|
name: rustfs-debug-binary
|
|
path: target/debug
|
|
|
|
- name: Make binary executable
|
|
run: chmod +x ./target/debug/rustfs
|
|
|
|
# Full single-node e2e lane (backlog#1149 ci-5). The e2e-full
|
|
# default-filter in .config/nextest.toml is the single wiring mechanism —
|
|
# extend that filter, never add ad-hoc e2e jobs here. Reuses the downloaded
|
|
# debug binary; each test spawns its own rustfs server on a random port.
|
|
- name: Run e2e full suite
|
|
run: cargo nextest run --profile e2e-full -p e2e_test
|
|
|
|
- name: Upload junit
|
|
if: always()
|
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
|
with:
|
|
name: e2e-full-junit-${{ github.run_number }}
|
|
path: target/nextest/e2e-full/junit.xml
|
|
retention-days: 7
|
|
|
|
e2e-tests-rio-v2:
|
|
name: End-to-End Tests (rio-v2)
|
|
needs: [ build-rustfs-debug-binary-rio-v2 ]
|
|
runs-on: sm-standard-2
|
|
timeout-minutes: 30
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
|
|
- name: Clean up previous test run
|
|
run: |
|
|
rm -rf /tmp/rustfs
|
|
rm -f /tmp/rustfs.log
|
|
|
|
- name: Download debug binary
|
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
|
with:
|
|
name: rustfs-debug-binary-rio-v2
|
|
path: target/debug
|
|
|
|
- name: Make binary executable
|
|
run: chmod +x ./target/debug/rustfs
|
|
|
|
- name: Setup Rust toolchain for s3s-e2e installation
|
|
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
|
|
|
|
- name: Install s3s-e2e test tool
|
|
uses: taiki-e/cache-cargo-install-action@7447f04c51f2ba27ca35e7f1e28fab848c5b3ba7 # v2
|
|
with:
|
|
tool: s3s-e2e
|
|
git: https://github.com/s3s-project/s3s.git
|
|
rev: 62cb4a71dd759a6ec56b64c4c42fcc183a2c6a52
|
|
|
|
- name: Run end-to-end tests
|
|
run: |
|
|
s3s-e2e --version
|
|
./scripts/e2e-run.sh ./target/debug/rustfs /tmp/rustfs
|
|
|
|
- name: Upload test logs
|
|
if: failure()
|
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
|
with:
|
|
name: e2e-test-logs-rio-v2-${{ github.run_number }}
|
|
path: /tmp/rustfs.log
|
|
retention-days: 3
|
|
|
|
s3-implemented-tests:
|
|
name: S3 Implemented Tests
|
|
needs: [ build-rustfs-debug-binary, e2e-tests ]
|
|
runs-on: sm-standard-4
|
|
timeout-minutes: 60
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
|
|
- name: Download debug binary
|
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
|
with:
|
|
name: rustfs-debug-binary
|
|
path: target/debug
|
|
|
|
- name: Make binary executable
|
|
run: chmod +x ./target/debug/rustfs
|
|
|
|
- name: Run implemented s3-tests
|
|
run: |
|
|
RUN_ROOT="${RUNNER_TEMP}/rustfs-s3tests-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${GITHUB_JOB}"
|
|
mkdir -p "${RUN_ROOT}"
|
|
S3_PORT="$(python3 -c 'import socket; s=socket.socket(); s.bind(("127.0.0.1", 0)); print(s.getsockname()[1]); s.close()')"
|
|
DEPLOY_MODE=binary \
|
|
RUSTFS_BINARY=./target/debug/rustfs \
|
|
TEST_MODE=single \
|
|
MAXFAIL=0 \
|
|
XDIST=4 \
|
|
S3_PORT="${S3_PORT}" \
|
|
DATA_ROOT="${RUN_ROOT}" \
|
|
S3TESTS_CONF=artifacts/s3tests-single/s3tests.conf \
|
|
./scripts/s3-tests/run.sh
|
|
|
|
- name: Upload s3 test artifacts
|
|
if: always()
|
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
|
with:
|
|
name: s3tests-implemented-${{ github.run_number }}
|
|
path: artifacts/s3tests-single/**
|
|
if-no-files-found: ignore
|
|
retention-days: 3
|
|
|
|
# Dedicated lifecycle behavior lane (backlog#1148 ilm-10, master plan #1155).
|
|
#
|
|
# These ceph/s3-tests cases assert that objects/versions/uploads are actually
|
|
# removed by the background scanner and the stale-multipart cleanup loop, so
|
|
# they need a debug-accelerated day plus an enabled scanner. That configuration
|
|
# cannot be applied to the s3-implemented-tests lane above because a global
|
|
# RUSTFS_ILM_DEBUG_DAY_SECS also shrinks the x-amz-expiration header those tests
|
|
# assert on (test_lifecycle_expiration_header_*). Hence a separate server here.
|
|
#
|
|
# RUSTFS_ILM_DEBUG_DAY_SECS MUST stay equal to the s3-tests lc_debug_interval
|
|
# default (10s). test_lifecycle_expiration polls the Days=1 rule with a
|
|
# 4*lc_interval window while the Days=5 rule fires at 5*debug_day; observing
|
|
# the intermediate 4-object plateau requires 4*lc_interval < 5*debug_day, i.e.
|
|
# debug_day > 8. A smaller value lets the Days=5 rule fire inside the Days=1
|
|
# poll window so the count collapses straight to 2 and the test fails (#4764).
|
|
#
|
|
# RUSTFS_DATA_USAGE_UPDATE_DIR_CYCLES=1 is also required. A compacted directory
|
|
# is only re-descended (and its objects re-evaluated for ILM) once every
|
|
# DATA_USAGE_UPDATE_DIR_CYCLES scanner cycles (default 16). At the accelerated
|
|
# RUSTFS_SCANNER_CYCLE=2 that is ~32s between evaluations, so a Days=1 object
|
|
# that becomes due at debug_day (10s) is not actually expired until the next
|
|
# ~32s boundary (~42s), landing just past the 4*lc_interval (40s) poll window
|
|
# and making the count stall at 6 (#4767). Forcing every-cycle re-descent
|
|
# evaluates ILM within ~2s of the due time, well inside the poll window.
|
|
s3-lifecycle-behavior-tests:
|
|
name: S3 Lifecycle Behavior Tests
|
|
needs: [ build-rustfs-debug-binary ]
|
|
runs-on: sm-standard-4
|
|
timeout-minutes: 30
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
|
|
- name: Download debug binary
|
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
|
with:
|
|
name: rustfs-debug-binary
|
|
path: target/debug
|
|
|
|
- name: Make binary executable
|
|
run: chmod +x ./target/debug/rustfs
|
|
|
|
- name: Run lifecycle behavior s3-tests
|
|
run: |
|
|
RUN_ROOT="${RUNNER_TEMP}/rustfs-s3tests-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${GITHUB_JOB}"
|
|
mkdir -p "${RUN_ROOT}"
|
|
S3_PORT="$(python3 -c 'import socket; s=socket.socket(); s.bind(("127.0.0.1", 0)); print(s.getsockname()[1]); s.close()')"
|
|
DEPLOY_MODE=binary \
|
|
RUSTFS_BINARY=./target/debug/rustfs \
|
|
TEST_MODE=single \
|
|
MAXFAIL=0 \
|
|
XDIST=0 \
|
|
IMPLEMENTED_TESTS_FILE=scripts/s3-tests/lifecycle_behavior_tests.txt \
|
|
RUSTFS_ILM_DEBUG_DAY_SECS=10 \
|
|
RUSTFS_ILM_PROCESS_TIME=1 \
|
|
RUSTFS_SCANNER_ENABLED=true \
|
|
RUSTFS_SCANNER_CYCLE=2 \
|
|
RUSTFS_DATA_USAGE_UPDATE_DIR_CYCLES=1 \
|
|
RUSTFS_SCANNER_START_DELAY_SECS=0 \
|
|
RUSTFS_API_STALE_UPLOADS_CLEANUP_INTERVAL=2s \
|
|
S3_PORT="${S3_PORT}" \
|
|
DATA_ROOT="${RUN_ROOT}" \
|
|
S3TESTS_CONF=artifacts/s3tests-single/s3tests.conf \
|
|
./scripts/s3-tests/run.sh
|
|
|
|
- name: Upload s3 test artifacts
|
|
if: always()
|
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
|
with:
|
|
name: s3tests-lifecycle-behavior-${{ github.run_number }}
|
|
path: artifacts/s3tests-single/**
|
|
if-no-files-found: ignore
|
|
retention-days: 3
|