test/ci(ecstore): fix MinIO SSE interop size assertion + nightly dockerized interop check (#4809)

* test(ecstore): assert decrypted_size for MinIO SSE interop round-trip

The ignored MinIO interop round-trip tests asserted `ObjectInfo.size`
against the plaintext length. For SSE objects `size` is the on-disk
DARE-encrypted size (plaintext + 32 bytes per 64 KiB block), so the
assertion can never hold once real fixtures are present — the two
`#[ignore]` tests failed the moment a real MinIO-written fixture was fed
in, even though the decoded data was byte-identical.

The client-visible object size comes from `decrypted_size()` /
`get_actual_size()`, which correctly reads MinIO's
`x-*-internal-actual-size` metadata (verified: both SSE-S3 and SSE-KMS
8 MiB multipart fixtures now report 8388608). Assert against that
instead and keep the plaintext length and SHA-256 data checks.

With real 4-drive MinIO fixtures (RELEASE.2025-09-07) all four tests
pass, confirming RustFS reads MinIO erasure-coded SSE objects with
byte-identical data and correct logical size.

Co-Authored-By: heihutu <heihutu@gmail.com>

* ci(ecstore): nightly MinIO interop check + dockerized fixture capture

Wire the ignored MinIO on-disk interop reader tests into a nightly,
non-required CI job, and make their fixtures reproducible without a host
MinIO install.

- Dockerfile + capture_via_docker.sh: build a throwaway image carrying
  the official MinIO server binary (pinned RELEASE.2025-09-07) plus the
  fixture lab on a small Python base, then run `lab.py capture-matrix` to
  write the SSE-S3 / SSE-KMS multipart fixtures the tests consume. lab.py
  drives MinIO's S3 API directly, so no `mc` is needed.
- .github/workflows/minio-interop.yml: nightly + manual workflow on
  GitHub-hosted ubuntu-latest (reliable Docker + Python, unlike the
  self-hosted fleet — see e2e-s3tests.yml infra note). Regenerates the
  gitignored fixtures each run and executes the #[ignore] reader tests.
  Not a PR gate.
- README: document the Docker capture path.

Validated end to end: the script builds the image, captures the two
multipart cases, and `cargo nextest run --run-ignored ignored-only`
passes all four interop tests.

Co-Authored-By: heihutu <heihutu@gmail.com>

---------

Co-authored-by: heihutu <heihutu@gmail.com>
This commit is contained in:
houseme
2026-07-14 23:08:14 +08:00
committed by GitHub
parent c53e34f13b
commit ea2e24ac13
5 changed files with 167 additions and 2 deletions
@@ -280,14 +280,20 @@ async fn rejects_minio_generated_sse_s3_fixture_with_truncated_ciphertext() {
async fn assert_fixture_round_trip(case_id: &str, expected_size: i64) {
let (object_info, encrypted, expected_sha256) = load_fixture_reader_input(case_id).await;
let object_size = object_info.size;
// `ObjectInfo.size` is the on-disk size. For SSE objects that is the
// DARE-encrypted size (plaintext + 32 bytes per 64 KiB block), which is
// deliberately larger than the logical object size. The size a client sees
// (and what MinIO records via `x-*-internal-actual-size`) comes from
// `decrypted_size()`/`get_actual_size()`, so assert against that — the raw
// `size` field would never equal the plaintext length for encrypted objects.
let decrypted_size = object_info.decrypted_size().expect("decrypted size from MinIO metadata");
let kms_key_b64 = minio_static_kms_key_b64();
let plaintext = read_fixture_plaintext(encrypted, object_info, kms_key_b64)
.await
.expect("fixture must restore with the configured KMS key");
assert_eq!(object_size, expected_size);
assert_eq!(decrypted_size, expected_size);
assert_eq!(plaintext.len(), expected_size as usize);
assert_eq!(sha256_hex(&plaintext), expected_sha256);
}