Skip to content

fix(localnet): restore requires a prior up; never create a volume out of band - #273

Merged
zheli merged 3 commits into
mainfrom
fix-M1-RMV-001
Jul 13, 2026
Merged

fix(localnet): restore requires a prior up; never create a volume out of band#273
zheli merged 3 commits into
mainfrom
fix-M1-RMV-001

Conversation

@zheli

@zheli zheli commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Problem

The M1-RMV-001 e2e test fails with FAIL step 3b: volumes remain after remove:

Root cause

RunRestore loads a pg_dumpall stream into <project>_postgres via a throwaway docker run -v <vol>:.... When the target instance was never up, that docker run creates the volume out of band, with no com.docker.compose.* labels. A later up adopts it (hence the warning), and docker compose down --volumes — used by both localnet down and localnet remove --forceonly deletes volumes Compose itself created, so the adopted volume is stranded. On a persistent self-hosted runner, a detached volume left by an earlier job (e.g. a restore test) then poisons M1-RMV-001's assertion.

Fix

Enforce the invariant "never create a volume outside Compose" at the source: restore now requires the target instance to already be registered (it was up, so Compose owns the volume). If not, it refuses and tells the user to run localnet up <name> first. This also applies to cross-name restore (the target name must have been up too).

The precondition is enforced via the registry (a proxy for "the Compose-owned volume exists"). A stricter Docker volume-ownership label check (docker volume ls --filter label=com.docker.compose.project=... --filter label=com.docker.compose.volume=postgres) is intentionally deferred and documented as a known gap in docs/limitations.md.

Behavior change

  • Fresh restore (into a never-up instance) and no-prior-up cross-name restore are no longer allowed — run localnet up <name> first.

Relationship to related PRs

Tests

  • Seed a stopped instance in the round-trip, cross-name, and content-SHA restore tests.
  • Add TestRestore_RefusesUnknownInstance.
  • go build ./..., go test ./internal/localnet/... ./internal/cli/... ./internal/ui/handlers/... all pass.

Note for reviewers / CI

The product fix prevents new orphaned volumes but does not retroactively delete the stale canton-e2e-test-default_postgres volume already present on the self-hosted runner. The first green M1-RMV-001 run may need a one-time docker volume rm canton-e2e-test-default_postgres (or a clean runner).

… of band

RunRestore loaded a pg_dumpall stream into <project>_postgres via a
throwaway `docker run -v <vol>:...`. When the target instance had never
been `up`, that `docker run` CREATED the volume out of band, with no
`com.docker.compose.*` labels. A later `up` adopts it (the "volume ...
already exists but was not created by Docker Compose" warning), and
`docker compose down --volumes` (used by `down` and `remove --force`)
refuses to delete a volume Compose did not create — so the volume is
stranded. This is the root cause behind the M1-RMV-001 e2e failure
("FAIL step 3b: volumes remain after remove").

Require the target instance to already be registered (it was `up`, so
Compose owns the volume) before restoring; refuse otherwise and tell the
user to run `localnet up <name>` first. Applies to cross-name restore
too. This keeps the "never create a volume outside Compose" invariant.

The precondition is enforced via the registry (a proxy for "the
Compose-owned volume exists"). A stricter Docker volume-ownership label
check is deferred and documented in docs/limitations.md.

Tests: seed a stopped instance in the round-trip, cross-name, and
content-SHA restore tests; add TestRestore_RefusesUnknownInstance.
@zheli zheli added the run-e2e Trigger e2e workflows on PRs label Jul 12, 2026
zheli added 2 commits July 13, 2026 00:15
Restore now loads into the instance's EXISTING Compose-owned Postgres
volume and refuses to create one out of band. The snapshot/restore test
tore the instance down with `remove` (which reclaims the volume), then
restored — which the new contract correctly rejects with "instance not
found — run localnet up first". Switch the teardown to `down`, which
preserves the volume, so restore has a Compose-owned volume to load into.
The test brings up a fresh `e2e-version-test` but had no precondition
cleanup, so a leftover instance from a prior/aborted run caused step 1
to abort with "instance already running". Add a best-effort
`e2e_cleanup_instance` before `up`, matching the self-defending pattern
used by other lifecycle tests. Cleanup lives in the test's precondition
(not teardown) because M1-LST-001 depends on e2e-version-test staying up.
@zheli
zheli merged commit bfb8ad7 into main Jul 13, 2026
26 checks passed
@zheli
zheli deleted the fix-M1-RMV-001 branch July 13, 2026 07:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run-e2e Trigger e2e workflows on PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant