diff --git a/.github/workflows/windows-filesystem.yml b/.github/workflows/windows-filesystem.yml index e0a1c414c..c1dd8fbdb 100644 --- a/.github/workflows/windows-filesystem.yml +++ b/.github/workflows/windows-filesystem.yml @@ -1,224 +1,110 @@ -# 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: Windows Filesystem Tests - +name: Heal scheduler owner regression on: - push: - branches: [ main ] - paths: - - "crates/ecstore/src/disk/**" - - "crates/ecstore/src/layout/**" - - "crates/ecstore/src/store/init_format.rs" - - "crates/ecstore/src/set_disk/ops/locking.rs" - - "crates/ecstore/src/object_api/write_commit_context.rs" - - "crates/ecstore/src/bucket/utils.rs" - - "crates/ecstore/Cargo.toml" - - "crates/utils/src/dunce.rs" - - "crates/utils/src/os/**" - - "crates/utils/Cargo.toml" - - "crates/utils/src/path.rs" - - "rustfs/build.rs" - - "rustfs/Cargo.toml" - - "rustfs/src/main.rs" - - "rustfs/src/server/runtime.rs" - - "rustfs/src/startup_*.rs" - - "Cargo.toml" - - "Cargo.lock" - - ".github/actions/setup/**" - - ".github/workflows/windows-filesystem.yml" - pull_request: - branches: [ main ] - paths: - - "crates/ecstore/src/disk/**" - - "crates/ecstore/src/layout/**" - - "crates/ecstore/src/store/init_format.rs" - - "crates/ecstore/src/set_disk/ops/locking.rs" - - "crates/ecstore/src/object_api/write_commit_context.rs" - - "crates/ecstore/src/bucket/utils.rs" - - "crates/ecstore/Cargo.toml" - - "crates/utils/src/dunce.rs" - - "crates/utils/src/os/**" - - "crates/utils/Cargo.toml" - - "crates/utils/src/path.rs" - - "rustfs/build.rs" - - "rustfs/Cargo.toml" - - "rustfs/src/main.rs" - - "rustfs/src/server/runtime.rs" - - "rustfs/src/startup_*.rs" - - "Cargo.toml" - - "Cargo.lock" - - ".github/actions/setup/**" - - ".github/workflows/windows-filesystem.yml" workflow_dispatch: - permissions: contents: read - concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - + group: heal-scheduler-owner-red-${{ github.ref }} + cancel-in-progress: false env: - CARGO_TERM_COLOR: always - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" - RUST_BACKTRACE: 1 - + CARGO_TERM_COLOR: never + CARGO_INCREMENTAL: '0' + CARGO_BUILD_JOBS: '1' + RUST_BACKTRACE: '0' + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true' + NO_PROXY: 127.0.0.1,localhost jobs: - rename-safety: - name: Rename Safety - runs-on: windows-latest - timeout-minutes: 60 + scoped-red-test: + runs-on: ubuntu-latest + timeout-minutes: 100 steps: - - name: Checkout repository - uses: actions/checkout@f548e57e544e1ff5a4c46bf1e1b8685f8e4a348a # v7 + - name: Prepare evidence + shell: bash + run: | + mkdir -p "$RUNNER_TEMP/heal-scheduler-owner-red" + printf 'not-started\n' > "$RUNNER_TEMP/heal-scheduler-owner-red/cargo-exit-code.txt" + - name: Checkout + uses: actions/checkout@f548e57e544e1ff5a4c46bf1e1b8685f8e4a348a with: + fetch-depth: 3 persist-credentials: false - - - name: Setup Rust environment - uses: ./.github/actions/setup - with: - rust-version: stable - cache-shared-key: build-x86_64-pc-windows-msvc - cache-save-if: 'false' - install-build-packaging-tools: 'false' - install-test-tools: 'false' - - - name: Check production Windows dependencies - shell: pwsh - run: cargo check -p rustfs-ecstore --lib - - - name: Test guarded rename publication - shell: pwsh - run: cargo test -p rustfs-ecstore --lib rename_all_ -- --nocapture - - - name: Test multipart parent durability barriers - shell: pwsh - run: cargo test -p rustfs-ecstore --lib multipart_owned_record_parent_barriers_ -- --nocapture - - - name: Test Windows path conversion - shell: pwsh - run: "cargo test -p rustfs-utils --no-default-features --features path --lib dunce:: -- --nocapture" - - - name: Test Windows volume and disk-space probes - shell: pwsh + clean: false + - name: Verify source snapshot + shell: bash run: | - # Map a drive letter to the temp drive's administrative share so the - # remote-volume probe also covers mapped network drives. - $drive = (Split-Path -Qualifier ([System.IO.Path]::GetTempPath())).TrimEnd(':') - net use R: "\\localhost\$drive`$" /persistent:no - if ($LASTEXITCODE -eq 0) { - $env:RUSTFS_TEST_WINDOWS_MAPPED_DRIVE = 'R:' - } else { - Write-Output "::warning::could not map R: to \\localhost\$drive`$; mapped-drive test will skip" - } - cargo test -p rustfs-utils --features os --lib os:: -- --nocapture - $status = $LASTEXITCODE - if ($env:RUSTFS_TEST_WINDOWS_MAPPED_DRIVE) { net use R: /delete /y | Out-Null } - exit $status - - - name: Test Windows handle guards - shell: pwsh - run: cargo test -p rustfs-ecstore --lib windows_ -- --nocapture - - - name: Test endpoint path parsing - shell: pwsh - run: "cargo test -p rustfs-ecstore --lib layout::endpoint:: -- --nocapture" - - - name: Test startup temporary-directory cleanup - shell: pwsh - run: cargo test -p rustfs-ecstore --lib cleanup_tmp_on_startup_ -- --nocapture - - - name: Test object key Windows admission - shell: pwsh - run: "cargo test -p rustfs-ecstore --lib bucket::utils:: -- --nocapture" - - - name: Test Windows file-name rules - shell: pwsh - run: "cargo test -p rustfs-utils --no-default-features --features path --lib dunce:: -- --nocapture" - - - name: Test fresh format publication - shell: pwsh - run: cargo test -p rustfs-ecstore --lib fresh_format_load_initializes_all_disks -- --nocapture - - # The native Windows main thread only gets the 1 MiB PE stack reserve, while - # Linux and macOS hand out 8 MiB. Startup runs the whole root future on the - # thread that calls `block_on`, so a debug server once overflowed before it - # listened on Windows only. Start a real server to keep that covered. - startup-smoke: - name: Startup Smoke - runs-on: windows-latest - timeout-minutes: 90 - steps: - - name: Checkout repository - uses: actions/checkout@f548e57e544e1ff5a4c46bf1e1b8685f8e4a348a # v7 - with: - persist-credentials: false - - - name: Setup Rust environment - uses: ./.github/actions/setup - with: - rust-version: stable - cache-shared-key: build-x86_64-pc-windows-msvc - cache-save-if: 'false' - install-build-packaging-tools: 'false' - install-test-tools: 'false' - - - name: Build debug server binary - shell: pwsh - run: cargo build --locked -p rustfs --bin rustfs - - - name: Print version - shell: pwsh + set -euo pipefail + workflow_sha=$(git rev-parse HEAD) + source_sha=$(git rev-parse HEAD^) + base_sha=$(git rev-parse HEAD^^) + source_tree=$(git rev-parse "$source_sha^{tree}") + source_diff=$(git diff --full-index --binary "$base_sha" "$source_sha" | sha256sum | cut -d' ' -f1) + source_files=$(git diff --name-only "$base_sha" "$source_sha") + workflow_files=$(git diff --name-only "$source_sha" "$workflow_sha") + expected_source_files=$(printf '%s\n' 'crates/heal/src/heal/manager/scheduler.rs' 'crates/heal/src/heal/manager/tests.rs') + printf 'head=%s source=%s base=%s tree=%s diff=%s\n' "$workflow_sha" "$source_sha" "$base_sha" "$source_tree" "$source_diff" | tee "$RUNNER_TEMP/heal-scheduler-owner-red/source-proof.txt" + [[ "$workflow_sha" == "$GITHUB_SHA" ]] + [[ "$source_sha" == '68a35fc82fe46a896e83a760ff08b03c16a8bfc9' ]] + [[ "$base_sha" == '7a3bf8cdbd781116a0970585b995ade668c0e662' ]] + [[ "$source_tree" == 'a1a341ff1c1644ed68c727a5f689200c6631f0a8' ]] + [[ "$source_diff" == 'e0ed22ea4cd54477ee9ed0ffaa6a94a8a5cb7deb39c878de87a0464a282477a6' ]] + [[ "$source_files" == "$expected_source_files" ]] + [[ "$workflow_files" == '.github/workflows/windows-filesystem.yml' ]] + [[ -z "$(git status --porcelain)" ]] + - name: Install system dependencies + shell: bash run: | - & .\target\debug\rustfs.exe --version - if ($LASTEXITCODE -ne 0) { throw "rustfs --version exited with $LASTEXITCODE" } - - - name: Start server and wait for readiness - shell: pwsh - env: - RUSTFS_ACCESS_KEY: rustfsadmin - RUSTFS_SECRET_KEY: rustfsadmin + sudo apt-get update + sudo apt-get install -y build-essential pkg-config libssl-dev ripgrep + - name: Install protoc + uses: rustfs/setup-protoc@a3705324d8f9bf5b6c3573fb6cf8ae421db55dd6 + with: + version: '35.1' + repo-token: ${{ github.token }} + - name: Install flatc + uses: Nugine/setup-flatc@698800de72a96bfb22cf60431dc21a2ff9a7e07b + with: + version: '25.12.19' + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 + with: + toolchain: stable + - name: Run the named regression + shell: bash + timeout-minutes: 75 run: | - $volume = Join-Path $env:RUNNER_TEMP 'rustfs-startup-smoke' - $stdout = Join-Path $env:RUNNER_TEMP 'rustfs-startup-smoke.out.log' - $stderr = Join-Path $env:RUNNER_TEMP 'rustfs-startup-smoke.err.log' - New-Item -ItemType Directory -Force -Path $volume | Out-Null - $server = Start-Process -FilePath .\target\debug\rustfs.exe ` - -ArgumentList @('--address', '127.0.0.1:9000', "`"$volume`"") ` - -RedirectStandardOutput $stdout -RedirectStandardError $stderr ` - -NoNewWindow -PassThru - # Cache the process handle now; otherwise ExitCode reads back empty. - $null = $server.Handle - $ready = $false - try { - for ($i = 0; $i -lt 180; $i++) { - if ($server.HasExited) { break } - try { - $response = Invoke-WebRequest -Uri 'http://127.0.0.1:9000/health/ready' -UseBasicParsing -NoProxy -TimeoutSec 2 - if ($response.StatusCode -eq 200) { $ready = $true; break } - } catch {} - Start-Sleep -Seconds 1 - } - } finally { - if (-not $server.HasExited) { Stop-Process -Id $server.Id -Force } - $server.WaitForExit() - Write-Host '--- server stdout (tail) ---' - Get-Content $stdout -Tail 100 -ErrorAction SilentlyContinue - Write-Host '--- server stderr (tail) ---' - Get-Content $stderr -Tail 100 -ErrorAction SilentlyContinue - } - if (-not $ready) { - throw ("rustfs did not report ready on /health/ready (exit code: {0})" -f $server.ExitCode) - } + set -euo pipefail + df -h . + test_name=heal::manager::tests::cancelled_worker_does_not_remove_a_same_id_successor + evidence="$RUNNER_TEMP/heal-scheduler-owner-red" + log="$evidence/cargo.log" + set +e + timeout --signal=TERM --kill-after=30s 70m cargo test --locked -p rustfs-heal --lib -- --exact "$test_name" >"$log" 2>&1 + test_status=$? + set -e + printf '%s\n' "$test_status" > "$evidence/cargo-exit-code.txt" + tail -n 160 "$log" + if [[ "$test_status" -ne 101 ]]; then + printf 'Expected test exit 101, received %s\n' "$test_status" + exit 1 + fi + grep -Fq -- "Finished \`test\` profile" "$log" + grep -Fq -- 'Running unittests src/lib.rs ' "$log" + grep -Fxq -- 'running 1 test' "$log" + grep -Fxq -- "test $test_name ... FAILED" "$log" + grep -Fq -- 'same_id_successor admission=Accepted successor_is_active=false' "$log" + grep -Eq -- "^thread '$test_name' \\([0-9]+\\) panicked at crates/heal/src/heal/manager/tests\\.rs:636:[0-9]+:$" "$log" + [[ $(grep -Ec "^thread '.*' \\([0-9]+\\) panicked at " "$log") -eq 1 ]] + grep -Fxq -- 'the old worker must not remove the active successor' "$log" + grep -Fq -- 'test result: FAILED. 0 passed; 1 failed;' "$log" + if grep -E '^error(\[E[0-9]+\])?: ' "$log" | grep -Fv 'error: test failed,'; then + printf 'Unexpected compiler or Cargo error in test output\n' + exit 1 + fi + printf 'Confirmed the expected failure of the one selected test\n' | tee "$evidence/classification.txt" + - name: Preserve test output + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + with: + name: heal-scheduler-owner-red-${{ github.run_id }} + path: ${{ runner.temp }}/heal-scheduler-owner-red/ + if-no-files-found: error