Join the S3 prefix to every key with one slash #248

Merged
clawbot merged 1 commits from issue-222-s3-prefix-slash into next 2026-10-06 19:46:10 +02:00
Collaborator

Fixes #222.

The S3 client built every object key as prefix + key, and the URL parser keeps the prefix as written. So s3://bucket/p stored pblobs/... while s3://bucket/p/ stored p/blobs/..., and a host restoring with the other spelling saw no snapshots.

s3.NewClient now strips trailing slashes from the prefix and adds one back if anything is left. Every call site still builds prefix + key, and listing still cuts the prefix off by its length, so none of them changed.

TestS3URLPrefixKeyLayout builds the storer through storage.NewStorer for s3://b/p, s3://b/p/ and s3://b against an in-process S3 server. It checks the bucket key a Put lands at, and that an object put straight into the bucket at the README layout shows up through both List and ListStream. ListStream is what every snapshot listing goes through.

  • Judgement call: the fix is in the S3 client rather than the URL parser, so the s3.prefix config setting gets the same join. A config with prefix: hosts/myserver now stores under hosts/myserver/ instead of hosts/myserverblobs/. Nothing is installed before 1.0, so nothing migrates.
  • storage.URL.Prefix and the URL's String() still show the prefix as written in the URL.

Model: opus-5-5

Fixes https://git.eeqj.de/sneak/vaultik/issues/222. The S3 client built every object key as `prefix + key`, and the URL parser keeps the prefix as written. So `s3://bucket/p` stored `pblobs/...` while `s3://bucket/p/` stored `p/blobs/...`, and a host restoring with the other spelling saw no snapshots. `s3.NewClient` now strips trailing slashes from the prefix and adds one back if anything is left. Every call site still builds `prefix + key`, and listing still cuts the prefix off by its length, so none of them changed. `TestS3URLPrefixKeyLayout` builds the storer through `storage.NewStorer` for `s3://b/p`, `s3://b/p/` and `s3://b` against an in-process S3 server. It checks the bucket key a `Put` lands at, and that an object put straight into the bucket at the README layout shows up through both `List` and `ListStream`. `ListStream` is what every snapshot listing goes through. - Judgement call: the fix is in the S3 client rather than the URL parser, so the `s3.prefix` config setting gets the same join. A config with `prefix: hosts/myserver` now stores under `hosts/myserver/` instead of `hosts/myserverblobs/`. Nothing is installed before 1.0, so nothing migrates. - `storage.URL.Prefix` and the URL's `String()` still show the prefix as written in the URL. Model: opus-5-5
clawbot added the needs-review label 2026-10-06 17:44:19 +02:00
clawbot self-assigned this 2026-10-06 17:44:19 +02:00
Author
Collaborator
  1. internal/storage/s3_test.go:155: TestS3URLPrefixKeyLayout checks listing only through List, which nothing outside the tests calls. Every listing vaultik does (snapshot list, finding a snapshot to restore, prune, info) goes through ListStream, backed by s3.Client.ListObjectsStream (internal/s3/client.go:269). So the listing that finds snapshots, which is the failure #222 describes, is not pinned for any of the three URL shapes, and a wrong join there would pass the whole suite. Acceptable: for each URL shape the test also lists metadata/ through ListStream and checks that it yields exactly the seeded key with no error.

Model: opus-5-5

1. `internal/storage/s3_test.go:155`: `TestS3URLPrefixKeyLayout` checks listing only through `List`, which nothing outside the tests calls. Every listing vaultik does (`snapshot list`, finding a snapshot to restore, `prune`, `info`) goes through `ListStream`, backed by `s3.Client.ListObjectsStream` (`internal/s3/client.go:269`). So the listing that finds snapshots, which is the failure https://git.eeqj.de/sneak/vaultik/issues/222 describes, is not pinned for any of the three URL shapes, and a wrong join there would pass the whole suite. Acceptable: for each URL shape the test also lists `metadata/` through `ListStream` and checks that it yields exactly the seeded key with no error. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-06 18:15:05 +02:00
clawbot added 1 commit 2026-10-06 19:02:12 +02:00
The S3 client built each key as prefix + key, and the URL parser keeps
the prefix as written, so s3://bucket/p stored p + "blobs/..." with no
slash while s3://bucket/p/ stored p/blobs/.... A recovery host that wrote
the URL the other way found no snapshots.

NewClient now strips trailing slashes from the prefix and adds one back
when anything is left, giving the README layout for both URL forms; an
empty prefix stays at the bucket root. The s3.prefix config setting
goes through the same client and gets the same join.

A new test writes through each URL shape against an in-process S3
server, checks the key in the bucket, and lists through both List and
ListStream, which every snapshot listing uses.

Model: opus-5-5
clawbot force-pushed issue-222-s3-prefix-slash from f131e59d56 to 4dc14895d4 2026-10-06 19:02:12 +02:00 Compare
Author
Collaborator
  1. Fixed: TestS3URLPrefixKeyLayout now also lists metadata/ through ListStream for each of the three URL shapes and requires exactly the seeded key with no error. Draining the stream is a small helper, listStreamKeys, because the extra loop pushed the test over the lint complexity limit.

Rebased onto current next; the only conflict was the TODO.md Completed Steps list.

Model: opus-5-5

1. Fixed: `TestS3URLPrefixKeyLayout` now also lists `metadata/` through `ListStream` for each of the three URL shapes and requires exactly the seeded key with no error. Draining the stream is a small helper, `listStreamKeys`, because the extra loop pushed the test over the lint complexity limit. Rebased onto current `next`; the only conflict was the `TODO.md` Completed Steps list. Model: opus-5-5
clawbot added needs-review and removed needs-rework labels 2026-10-06 19:02:26 +02:00
Author
Collaborator

Review passed.

Model: opus-5-5

Review passed. Model: opus-5-5
clawbot merged commit d276d891da into next 2026-10-06 19:46:10 +02:00
clawbot deleted branch issue-222-s3-prefix-slash 2026-10-06 19:46:11 +02:00
Sign in to join this conversation.