From fd35f5f21b1429fb91ea438a624ddb458750302f Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Wed, 19 Aug 2026 08:51:38 +0200 Subject: [PATCH] feat(opentofu): destroy the rustfs LXCs 800/801/802 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stage 2 of the decommission the shim was written for. Deleting the file makes the next apply destroy the three containers. The containers were taken out of service months ago — they are stopped (pve_up = 0 for all three) and rustfs-{1,2,3}.srv.renner.uno no longer resolve — but the LXC objects and their disks still exist on saturn/jupiter/neptune. Two earlier destroy attempts (#270) died instantly with "context deadline exceeded", because the bpg provider reads timeout_delete from STATE and the imported resources had stored 0. That is now fixed: apply 5675 landed timeout_delete = 1800 and the rest of the timeouts in state, in place, with 0 destroyed. The destroy has a real deadline to work with for the first time. Checked before removing, since the containers are tagged `state-backend` and `terraform-backend`: - The tofu backend is Hetzner S3 (renner-uno-tfstate at fsn1.your-objectstorage.com), not rustfs. Destroying these cannot pull the state out from under the apply. The tags are historical, from before that migration. - No Kubernetes or Ansible resource references rustfs. - scripts/restore-authentik-db.sh mentions rustfs only as an `mc` alias name; its endpoint is s3.renner.uno:9000, the Ceph RGW `migration` store. - DNS for the three names is already gone. Co-Authored-By: Claude Bot --- opentofu/lxc_800_rustfs_decommission.tf | 122 ------------------------ 1 file changed, 122 deletions(-) delete mode 100644 opentofu/lxc_800_rustfs_decommission.tf diff --git a/opentofu/lxc_800_rustfs_decommission.tf b/opentofu/lxc_800_rustfs_decommission.tf deleted file mode 100644 index 1808cc3c..00000000 --- a/opentofu/lxc_800_rustfs_decommission.tf +++ /dev/null @@ -1,122 +0,0 @@ -# TEMPORARY shims v3 — rustfs LXCs 800/801/802, decommission in progress. -# -# Stage 2 (#270) failed: both destroy attempts died instantly with -# "context deadline exceeded" on the pre-destroy status GET, while the PVE -# API answers the same call in <1s. Cause: the bpg provider reads -# `timeout_delete` from STATE at destroy time, and importing a resource -# whose config declares no timeouts stored 0 — a zero-second context. -# (The import/plan read path uses its own timeout, which is why stage 1 -# planned clean.) -# -# Fix: declare the timeouts explicitly, apply ONCE so they land in state -# (in-place no-API update), then delete this file again and the next apply -# destroys the containers. -# -# v3: v2 declared ONLY the ids, `started`, `unprivileged` and the timeouts, -# claiming "the other attributes mirror the real containers so the plan stays -# in-place". They did not. Everything absent from config planned as a removal, -# and two of those removals — `mount_point` and `operating_system` — force -# replacement, so the plan came back `must be replaced` for all three. Applying -# that would have destroyed and recreated containers tagged `critical`, -# `state-backend`, `terraform-backend` instead of nudging a timeout into state. -# -# So the blocks below are not decoration: each one exists to keep an attribute -# out of the diff. They mirror the live containers, read from the plan's own -# state values. `tags` is declared for the same reason — without it the apply -# strips the tags off the real containers. -# -# 1. merge; run PIPELINE=deploy-infrastructure (plan MUST show in-place -# update only, no replace), then TOFU_APPLY=true -# 2. follow-up PR deletes this file; plan shows 3 destroys; apply. - -locals { - # Per-container values mirrored from live state. hostname and mac_address - # differ per container; declaring them wrong would itself force a diff. - rustfs_decommission = { - "800" = { node = "saturn", hostname = "rustfs-1.srv.renner.uno", mac = "BC:24:11:0B:E5:7B" } - "801" = { node = "jupiter", hostname = "rustfs-2.srv.renner.uno", mac = "BC:24:11:EA:1D:38" } - "802" = { node = "neptune", hostname = "rustfs-3.srv.renner.uno", mac = "BC:24:11:8F:F4:67" } - } -} - -resource "proxmox_virtual_environment_container" "rustfs_decommission" { - for_each = local.rustfs_decommission - - node_name = each.value.node - vm_id = tonumber(each.key) - started = false - unprivileged = true - start_on_boot = true - - tags = [ - "critical", - "rustfs", - "state-backend", - "terraform-backend", - ] - - timeout_create = 1800 - timeout_clone = 1800 - timeout_update = 1800 - timeout_delete = 1800 - timeout_start = 300 - - cpu { - architecture = "amd64" - cores = 1 - units = 1024 - } - - memory { - dedicated = 1024 - swap = 0 - } - - disk { - datastore_id = "local" - size = 10 - } - - # forces replacement if absent — this is the block that broke v2. - mount_point { - path = "/mnt/data" - size = "50G" - volume = "local:${each.key}/vm-${each.key}-disk-1.raw" - backup = true - replicate = true - } - - network_interface { - name = "eth0" - bridge = "v40srv" - enabled = true - firewall = false - mac_address = each.value.mac - } - - initialization { - hostname = each.value.hostname - - ip_config { - ipv4 { - address = "dhcp" - } - } - } - - # operating_system is NOT declared, and must not be. The provider requires - # template_file_id inside it, which is a create-time-only value: PVE does not - # report the original template for an existing container, so it is absent - # from state and cannot be mirrored. Declaring the block without it fails - # validation ("Missing required argument"); declaring it with a guessed - # template would be a lie that only surfaces if something ever recreates - # these. - # - # Left undeclared it would plan as a removal, and removing operating_system - # forces replacement — the exact failure this file exists to avoid. So it is - # ignored instead. The timeouts above are deliberately NOT ignored: they are - # the one thing this shim needs to actually apply. - lifecycle { - ignore_changes = [operating_system] - } -} -- 2.54.0