"It is just a refactor," says the PR PR-88

Open2 versionsInfrastructure as code · Hard · Recover · about 40 min ·Linux + Docker

Lab machine

A private Linux machine with Docker Engine. Starting takes about 30 seconds. Sessions last up to 60 minutes.
Sasha Lind opened PR-88 at 11:45task

PR #88 moves Ledger into a module. CI's plan destroys the volume that holds the settlement journal and creates a new one. The author says it is just a refactor, ship it.

To OpenTofu, a resource is its address and its arguments. Change the address without saying it moved, or change an argument that cannot be updated in place, and the plan deletes the old object and creates a new one. Deleting a volume deletes the data in it.

"It's just a rename, the plan is being dramatic. Ship it." (PR author)

The journal on that volume settles every carrier payment. There is no backup newer than last night.

Your task

Land the module refactor with a plan that destroys and recreates nothing, apply it, and add a guardrail so a future plan that would destroy the ledger volume fails.

On the machine

  • infra/ with the PR branch checked out
  • ci/plan-pr-88.txt
  • bin/tofu -chdir=infra plan
  • The real ledger volume and its journal

Timeline

MonPlatform decides every stateful service gets a module.
11:30PR #88 opened. CI's plan destroys and recreates the ledger volume.
11:45"The plan is being dramatic. Ship it."

Done when

  1. Ledger lives at module.ledger in config and state.
  2. Applying replaces nothing, and the settlement journal survives.
  3. A plan that would destroy the ledger volume is refused.

Hints

Hint 1

Read each `# forces replacement` and `will be destroyed` line, not just the summary.

Hint 2

A new address looks like a new object unless you say it moved.

Hint 3

A moved block cannot stop a replacement caused by changing an argument that cannot be updated in place, such as a volume's name or labels.

Hint 4

Make the plan move the volume instead of replacing it, then add `lifecycle { prevent_destroy = true }`.

Show the solution

Read the plan line by line. If it destroys `docker_volume.ledger_data` and creates `module.ledger.docker_volume.data`, add `moved` blocks for the volume and the container. If the plan still replaces the volume after the move (`# forces replacement`), find the argument that changed: the module renames the volume, or adds labels to it, and Docker volumes cannot change either in place. Keep the existing name and leave the labels for a planned migration. Add `lifecycle { prevent_destroy = true }` to the volume. The plan should read 0 to add, 0 to destroy. Apply it.