You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(agent): set WhenRequired on the transfer manager too, not just the client
Review of #342 found the client-only option never reached the code path that
uploads real snapshots. manager.Uploader carries its OWN
RequestChecksumCalculation (NewUploader hardcodes WhenSupported) and never reads
the s3.Client option, so any snapshot over the manager's 5 MiB part size — every
real etcd snapshot — took the multipart branch and re-stamped the CRC32
STREAMING-UNSIGNED-PAYLOAD-TRAILER that Ceph RGW rejects. The previous fix only
covered the degenerate sub-5-MiB single-part case.
- Derive both knobs from one package-level constant (snapshotChecksumCalculation)
and set it on the uploader via newSnapshotUploader as well as the client, so the
two independent settings cannot silently drift.
- Add TestUploadS3StreamMultipartNoChecksumTrailer: drives the real uploadS3Stream
against a TLS httptest server (trusted via AWS_CA_BUNDLE) with a >5 MiB body and
asserts no UploadPart carries a checksum trailer/algorithm. Fails without the
uploader option; a check on s3.Options alone stays green while the bug is live.
- Make TestS3ClientChecksumWhenRequired hermetic (AWS_CONFIG_FILE/… = /dev/null,
empty AWS_PROFILE) and assert the response-side setting; keep it as a
client-option unit check only.
- Set ResponseChecksumValidation=WhenRequired symmetrically for the restore
download path (same mismatch class; defensive, the downloader sets no
ChecksumMode today).
- Rewrite the agent.go comment that wrongly claimed a plain PutObject, and fix the
stale "temp file it then multipart-uploads" sentence in docs/concepts.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Andrey Kolkov <andrey.kolkov@aenix.io>
Copy file name to clipboardExpand all lines: docs/concepts.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -449,7 +449,7 @@ Two surfaces, one agent. The operator image doubles as a snapshot agent: `main.g
449
449
An `EtcdSnapshot` is a one-shot record. The controller resolves `spec.clusterRef`, builds a Job (owned by the `EtcdSnapshot`, with `ttlSecondsAfterFinished` so it self-GCs, `automountServiceAccountToken: false`, and a restricted security context), and tracks `status.phase` through `Pending → Started → Complete | Failed`. The Job's Pod runs the snapshot agent, which:
450
450
451
451
1. dials the cluster's client Service — TLS material (server CA + operator-client cert) is mounted from the same Secrets the operator dials with, and when `status.authEnabled` is latched the agent authenticates as `root` using the password from `spec.auth.rootCredentialsSecretRef`;
452
-
2. streams `clientv3` `Maintenance.Snapshot` to a local file (PVC destination) or a temp file it then multipart-uploads to S3, hashing (sha256) and counting bytes as it goes;
452
+
2. streams `clientv3` `Maintenance.Snapshot` to a local file (PVC destination) or directly to S3 via the transfer manager — no local staging, multipart above the manager's 5 MiB part size — hashing (sha256) and counting bytes as it goes;
453
453
3. prints a marker line — `snapshot uploaded: uri="..." size=N sha256=<hex>` — that the controller scans out of the Pod log (via an uncached `APIReader` to find the Pod and the typed Clientset to read its log) to populate `status.artifact{uri,sizeBytes,checksum}`.
454
454
455
455
Why parse a log line rather than have the agent write status? The agent has no Kubernetes API access by design (`automountServiceAccountToken: false`), so the controller — which does — is the one that records the result. The marker is the agent→controller channel. Terminal phases are sticky: a `Complete`/`Failed` snapshot is a historical record and never re-runs.
0 commit comments