Skip to content

fix: replace cloudinit volume when its source disk changes - #7

Merged
xnoto merged 1 commit into
mainfrom
fix/cloudinit-volume-replace
May 22, 2026
Merged

fix: replace cloudinit volume when its source disk changes#7
xnoto merged 1 commit into
mainfrom
fix/cloudinit-volume-replace

Conversation

@xnoto

@xnoto xnoto commented May 22, 2026

Copy link
Copy Markdown
Contributor

Summary

libvirt_cloudinit_disk.commoninit renders its ISO under a local /tmp path stored in state via the path attribute. Any tofu invocation on a host that doesn't have that file — CI runner pods (fresh /tmp each run), a different developer laptop, or a host where /tmp was wiped on reboot — sees the file missing and recreates the disk. That changes the URL referenced by libvirt_volume.cloudinit.create.content, and the dmacvicar/libvirt provider can't update libvirt_volume in place:

Error: Update Not Supported
  with module.<name>.libvirt_volume.cloudinit,
Storage volumes cannot be updated. All changes require replacement.

This adds a replace_triggered_by to the volume so it cycles automatically when the disk does, instead of forcing every operator to remember -replace=module.<name>.libvirt_volume.cloudinit on every cross-host apply.

Test plan

  • pre-commit (make test) green
  • Downstream consumer tfroot-libvirt plans show libvirt_volume.cloudinit replaced (not in-place updated) when libvirt_cloudinit_disk.commoninit is rebuilt
  • An apply that previously errored with "Update Not Supported" now succeeds end-to-end

Notes

  • Same lifecycle pattern already used on libvirt_domain.vm (replace_triggered_by = [libvirt_volume.boot]), applied to the cloudinit pairing.
  • The cloudinit volume cycling does not replace libvirt_domain.vm — only the volume.

🤖 Generated with Claude Code

libvirt_cloudinit_disk.commoninit renders the cloud-init ISO under a
local /tmp path that lives in state via its `path` attribute. Any tofu
invocation on a host that doesn't have that file — CI runners vs. a
developer laptop, or a host that has been rebooted since /tmp was wiped
— sees the file missing and marks the disk for recreation. That changes
the URL referenced by libvirt_volume.cloudinit.create.content, and the
dmacvicar/libvirt provider can't update libvirt_volume in place:

  Error: Update Not Supported
  Storage volumes cannot be updated. All changes require replacement.

Add replace_triggered_by so the volume cycles automatically when the
disk does, instead of forcing every operator to remember
`-replace=...libvirt_volume.cloudinit` on each cross-host apply.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@xnoto
xnoto merged commit d5d1a50 into main May 22, 2026
2 checks passed
@xnoto
xnoto deleted the fix/cloudinit-volume-replace branch May 22, 2026 05:44
xnoto added a commit that referenced this pull request May 22, 2026
## Summary
PR #7's `replace_triggered_by = [libvirt_cloudinit_disk.commoninit]`
didn't actually fix cross-host applies. When the provider's local `/tmp`
ISO is missing on the apply host (CI runner vs. laptop, post-reboot
`/tmp` wipe), the disk's `Read` returns "not found", tofu drops it from
state, and the next plan shows the disk as **created** (not
**replaced**). `replace_triggered_by` does **not** fire on a
create-following-drift, so the dependent `libvirt_volume.cloudinit`
still planned an in-place `url` update and apply died on:

```
Error: Update Not Supported
  Storage volumes cannot be updated. All changes require replacement.
```

This change moves the trigger to the cloud-init *content* itself,
captured in a `terraform_data` whose `input` is `sha256` of the rendered
`meta_data` + `user_data` + `network_config`. The volume now:

- `ignore_changes = [create]` — absorbs the volatile `/tmp` path drift
entirely
- `replace_triggered_by = [terraform_data.cloudinit_content]` — replaces
only when cloud-init content has actually changed

## One-time upgrade churn

On first apply for any existing consumer, the new `terraform_data`
resource is created. Because `replace_triggered_by` treats a create as a
planned action, the existing `libvirt_volume.cloudinit` will be replaced
once. That's a cdrom swap — harmless on running VMs (cloud-init only
runs on first boot). After this one event, the volume is stable unless
cloud-init content actually changes.

## Test plan
- [ ] pre-commit (`make test`) green
- [ ] Downstream `tfroot-libvirt` plan no longer shows
`libvirt_volume.cloudinit ... will be updated in-place` followed by an
Update Not Supported error at apply
- [ ] Subsequent applies on a different host (CI runner vs. laptop) are
no-ops for the cloudinit pair as long as content is unchanged

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant