mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-11-03 18:47:41 +01:00
* test/s3vectors: a hardened cluster, with TLS volume servers, read JWTs and encrypted chunks Three switches on the test cluster: VECTOR_TLS=1 puts the volume servers behind HTTPS requiring client certificates and enables https.client for every process; VECTOR_READ_JWT=1 gives every process a jwt.signing.read.key, so volume reads need a token; VECTOR_ENCRYPT_VOLUMES=1 starts the gateway with -encryptVolumeData. The integration matrix gains a hardened leg per volume server with all three on. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011UpvafvK8JpagF94MHWqwK * test/s3vectors: the hardened cluster proves its enforcement Once the gateway is up, the harness checks that the hardening holds, not only that it is configured: a TLS volume server refuses plain HTTP (Go with HTTP 400, Rust with a TLS alert) and a connection without the client certificate, and answers with it; under a read key an unsigned read of a real chunk, written through the gateway and located through the filer, gets 401. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011UpvafvK8JpagF94MHWqwK * test/s3vectors: the hardened cluster proves its chunks are encrypted With VECTOR_ENCRYPT_VOLUMES the probe reads the stored chunk straight from the volume server, signing the read when a read key is set, and requires a recorded cipher key and bytes that differ from the plaintext. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test/s3vectors: the signed probe read must return the chunk An empty 200 passed both branches; an unencrypted read now has to return the bytes written, and an encrypted one has to return something else. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test/s3vectors: a read key in the caller's environment does not leak into the cluster Only VECTOR_READ_JWT turns read JWTs on; an inherited WEED_JWT_SIGNING_READ_KEY made the volume servers refuse the probe's unsigned read in an encrypt-only run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test/s3vectors: quote certificate paths as TOML strings in security.toml Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test/s3vectors: keep non-ASCII certificate paths as UTF-8 in security.toml TOML has no surrogate-pair escapes, and the Rust volume server reads the quoted value without decoding escapes at all. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test/s3vectors: an encrypted probe chunk must not be a readable gzip stream The gateway may gzip the body before encrypting it, so a chunk uploaded compressed but unencrypted would differ from the body and still pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
162 lines
6.0 KiB
YAML
162 lines
6.0 KiB
YAML
name: "Rust Vector Tests"
|
|
|
|
on:
|
|
pull_request:
|
|
branches: [ master ]
|
|
paths:
|
|
- 'seaweed-vector/**'
|
|
- 'seaweed-volume/**'
|
|
- 'seaweed-worker/**'
|
|
- 'seaweed-common/**'
|
|
- 'weed/pb/plugin.proto'
|
|
- 'weed/pb/vector.proto'
|
|
- 'weed/pb/vector_pb/**'
|
|
- 'weed/s3api/**'
|
|
- 'weed/command/**'
|
|
- 'test/s3vectors/**'
|
|
- '.github/workflows/rust-vector-tests.yml'
|
|
push:
|
|
branches: [ master, main ]
|
|
paths:
|
|
- 'seaweed-vector/**'
|
|
- 'seaweed-volume/**'
|
|
- 'seaweed-worker/**'
|
|
- 'seaweed-common/**'
|
|
- 'weed/pb/plugin.proto'
|
|
- 'weed/pb/vector.proto'
|
|
- 'weed/pb/vector_pb/**'
|
|
- 'weed/s3api/**'
|
|
- 'weed/command/**'
|
|
- 'test/s3vectors/**'
|
|
- '.github/workflows/rust-vector-tests.yml'
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.event_name == 'push' && github.ref) || github.run_id }}
|
|
cancel-in-progress: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')) }}
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
rust-vector-build:
|
|
name: Rust Vector Build and Unit Tests
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 45
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v7
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Install Rust toolchain
|
|
uses: dtolnay/rust-toolchain@stable
|
|
|
|
- name: Fingerprint build toolchain
|
|
id: toolchain
|
|
run: echo "fingerprint=$(getconf GNU_LIBC_VERSION | tr ' ' '-')-rustc-$(rustc -V | awk '{print $2}')" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Cache cargo registry and target
|
|
if: runner.environment == 'github-hosted'
|
|
uses: actions/cache@v6
|
|
with:
|
|
path: |
|
|
~/.cargo/registry
|
|
~/.cargo/git
|
|
seaweed-vector/target/release
|
|
key: rust-vector-${{ steps.toolchain.outputs.fingerprint }}-${{ hashFiles('seaweed-vector/Cargo.lock') }}
|
|
restore-keys: |
|
|
rust-vector-${{ steps.toolchain.outputs.fingerprint }}-
|
|
|
|
- name: Build the vector crates
|
|
run: cd seaweed-vector && cargo build --release
|
|
|
|
- name: Run unit tests
|
|
run: cd seaweed-vector && cargo test --release --workspace
|
|
|
|
|
|
vectors-e2e:
|
|
name: S3 Vectors Integration (${{ matrix.volume }} volume${{ matrix.hardened && ', hardened' || '' }})
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 45
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
volume: [go, rust]
|
|
# Hardened: volume servers behind mutual TLS, read JWTs required,
|
|
# and every chunk encrypted by the gateway.
|
|
hardened: ["", "1"]
|
|
env:
|
|
CARGO_PROFILE_DEV_DEBUG: "0"
|
|
CARGO_BUILD_JOBS: "2"
|
|
CARGO_TARGET_DIR: ${{ github.workspace }}/seaweed-vector/target
|
|
VOLUME_SERVER_IMPL: ${{ matrix.volume }}
|
|
VECTOR_QUERY_MODE: single
|
|
VECTOR_TLS: ${{ matrix.hardened }}
|
|
VECTOR_READ_JWT: ${{ matrix.hardened }}
|
|
VECTOR_ENCRYPT_VOLUMES: ${{ matrix.hardened }}
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
persist-credentials: false
|
|
- uses: actions/setup-go@v7
|
|
with:
|
|
go-version-file: go.mod
|
|
cache: ${{ runner.environment == 'github-hosted' }}
|
|
- uses: dtolnay/rust-toolchain@stable
|
|
- name: Fingerprint build toolchain
|
|
id: toolchain
|
|
run: echo "fingerprint=$(getconf GNU_LIBC_VERSION | tr ' ' '-')-rustc-$(rustc -V | awk '{print $2}')" >> "$GITHUB_OUTPUT"
|
|
- name: Cache cargo registry and target
|
|
if: runner.environment == 'github-hosted'
|
|
uses: actions/cache@v6
|
|
with:
|
|
path: |
|
|
~/.cargo/registry
|
|
~/.cargo/git
|
|
seaweed-vector/target/debug
|
|
key: vectors-e2e-${{ runner.os }}-${{ runner.arch }}-${{ steps.toolchain.outputs.fingerprint }}-${{ hashFiles('seaweed-vector/Cargo.lock', 'seaweed-volume/Cargo.lock') }}
|
|
restore-keys: |
|
|
vectors-e2e-${{ runner.os }}-${{ runner.arch }}-${{ steps.toolchain.outputs.fingerprint }}-
|
|
- name: Test Go vector gateway
|
|
run: |
|
|
go test ./weed/s3api/s3vectors -count=1
|
|
go test ./weed/command -run 'Vector|Vectors' -count=1
|
|
- name: Build integration binaries
|
|
working-directory: test/s3vectors
|
|
# dev profile: correctness suite, no release codegen needed. weed-volume
|
|
# (the heaviest crate) only builds on the rust leg.
|
|
run: |
|
|
set -e
|
|
make ./weed ./weed-vector CARGO_PROFILE=dev
|
|
if [ "${{ matrix.volume }}" = "rust" ]; then make ./weed-volume CARGO_PROFILE=dev; fi
|
|
- uses: actions/setup-python@v7
|
|
with:
|
|
python-version: "3.12"
|
|
- name: Install test dependencies
|
|
run: python -m pip install -r test/s3vectors/requirements.txt
|
|
- name: Run the S3 Vectors integration matrix
|
|
working-directory: test/s3vectors
|
|
run: |
|
|
./run_test.sh --basetemp="$RUNNER_TEMP/vector-tests" --junitxml="$RUNNER_TEMP/vector-results.xml"
|
|
python - <<'PYTHON'
|
|
import os
|
|
from pathlib import Path
|
|
import xml.etree.ElementTree as ET
|
|
cases = ET.parse(Path(os.environ["RUNNER_TEMP"]) / "vector-results.xml").findall(".//testcase")
|
|
assert len(cases) >= 17, f"expected at least 17 cases, got {len(cases)}"
|
|
assert not any(case.find("skipped") is not None for case in cases), "integration cases were skipped"
|
|
volume = os.environ["VOLUME_SERVER_IMPL"]
|
|
assert all(case.get("name", "").endswith(f"[{volume}-single]") for case in cases), volume
|
|
PYTHON
|
|
- name: Upload integration diagnostics
|
|
if: always()
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: vectors-e2e-diagnostics-${{ matrix.volume }}${{ matrix.hardened && '-hardened' || '' }}
|
|
path: |
|
|
${{ runner.temp }}/vector-tests
|
|
${{ runner.temp }}/vector-results.xml
|
|
if-no-files-found: ignore
|
|
retention-days: 7
|