feat: migrate an instance's storage between swift and s3 - #4855
Open
Crash-- wants to merge 19 commits into
Open
Conversation
shepilov
force-pushed
the
feat/s3-vfs-backend
branch
from
July 30, 2026 15:13
52655f5 to
4c7b7a9
Compare
shepilov
force-pushed
the
feat/s3-vfs-backend
branch
from
September 3, 2026 12:07
4c7b7a9 to
562bd20
Compare
shepilov
force-pushed
the
feat/per-instance-s3-migration
branch
3 times, most recently
from
September 8, 2026 14:07
099e255 to
e0ee637
Compare
shepilov
force-pushed
the
feat/s3-vfs-backend
branch
from
September 8, 2026 15:15
562bd20 to
dea07f9
Compare
WriteContentAt puts bytes at the object key for a given (docID, internalID) pair without creating or touching any CouchDB document. It is the S3-side primitive used by Swift-to-S3 storage migration, which moves object bytes while preserving the shared CouchDB index.
The Swift->S3 migration needs to copy a user-uploaded avatar as-is (it is authoritative, not regenerable). OpenAvatar exposes a generic read-side (content reader + content-type) across all three vfs backends, returning os.ErrNotExist when no avatar is stored so the migration can treat "no avatar" as a skip.
…ollback Mirrors Task 3's vfss3.WriteContentAt so the Swift v3 backend can also act as a copy target for storagemigration.CopyContent, enabling full S3->Swift re-migration (rollback). Also corrects the storagemigration package doc comment, which previously oversold "Swift<->S3" before Swift-as-target was actually implemented.
Add Verify, which re-enumerates the same files/versions/avatar that CopyContent copies and confirms each object exists on the target with a matching byte size, without touching CouchDB. Backed by a new StatContentAt primitive on both the S3 and Swift v3 VFS backends, since either can be the copy target. The orchestrator (a later task) will only flip an instance's backend flag once Verify passes.
Add Migrate, which ties CopyContent/Verify together into the full migration flow: guard the target scheme and instance state, block the instance, copy and verify the content, then flip FsScheme only once Verify passes (DryRun and a failed Verify always leave FsScheme unchanged). FlagOnly supports rollback onto an already-populated backend and requires Force, since writes since cutover would be lost. PurgeSource best-effort deletes the source objects after a successful flip (S3-source only for now; swift-source purge reports "not implemented" rather than silently no-op'ing). Also add config.HasS3Client/HasSwiftConnection, non-panicking readiness checks needed to guard Migrate against a target backend that was never initialized.
FlagOnly previously flipped FsScheme after buildTarget merely ensured the target bucket/container existed, with no check that it actually held the source's content (buildTarget's EnsureBucket/ContainerCreate create an empty target if none exists). Add sourceReport, which computes the expected file/version counts and avatar presence from the source's CouchDB docs, and run Verify against it before flipping; a failed Verify now returns without touching FsScheme. purgeSource also returned "not implemented" for a swift source after the flip had already persisted, making a successful migration look like a failure. Reuse the same per-instance container Delete() that instance destroy/reset already use to actually purge the swift source. Adds coverage for the flag-only flip (populated vs. empty target) and an end-to-end swift-source purge test using the in-memory swifttest server.
Documents the fs.migration_target config key that lets an instance be migrated to a new storage backend (e.g. S3) without changing the global fs.url, and adds a step-by-step Swift-to-S3 migration guide covering the migrate-storage command, rollback, and source purge. Also regenerates the CLI reference page for the new instances migrate-storage command.
…ag-only dry-run Migrate rejected opts.To == the instance's current scheme even when PurgeSource was set, making the documented deferred reclaim step (docs/s3.md step 4, run --purge-source after the flip) and any retry of a failed inline purge impossible. Migrate now detects this case and runs a purge-only mode: it deletes the other backend's leftover data for the instance without copying, verifying, or flipping anything, and without blocking the instance. Also fix --flag-only --force --dry-run mutating state: the FlagOnly branch now checks opts.DryRun after Verify and returns without flipping FsScheme when set. Finally, guard model/stack Start against fs.migration_target pointing at S3 while fs.url is already an s3 scheme, which previously silently overwrote the global S3 client with the migration endpoint.
shepilov
force-pushed
the
feat/per-instance-s3-migration
branch
from
September 8, 2026 15:35
e0ee637 to
555c2da
Compare
shepilov
force-pushed
the
feat/per-instance-s3-migration
branch
from
September 8, 2026 15:38
555c2da to
2c32a43
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
fs_schemeoverride so an instance can use a different storage backend than the stack default; an empty value keeps the globalfs.urlscheme.fs.migration_targetconfig that initializes an S3 connection alongside the default one while the global scheme stays Swift.WriteContentAtandStatContentAton the S3 and Swift v3 backends, andOpenAvataron theAvatarerinterface.model/instance/storagemigration: copy an instance's files, file versions and avatar between backends at the object-storage level without touching the CouchDB index, verify the target, then flip the instance's backend inside a read-only window.--dry-run, an instant--flag-onlyrollback to the retained source, and a deferred purge-only reclaim of the previous backend.POST /instances/:domain/migrate-storage, the admin client method, and thecozy-stack instances migrate-storagecommand.fs.migration_targetand the migration workflow in docs/config.md and docs/s3.md.