[infra] Remove iscsi-csi stack — TrueNAS decommissioned [ci skip]

## Context

The iSCSI CSI driver was deployed against a TrueNAS appliance at 10.0.10.15
that was decommissioned 2026-04-12 when all Immich PVCs migrated to the
proxmox-lvm-encrypted storage class. The stack has been dead code since —
live survey (2026-04-18):

- iscsi-csi namespace: empty (0 resources), 27h old (since last TF apply)
- No iscsi CSI driver registered in the cluster
- No PVs/PVCs reference iscsi
- TF state held only the empty namespace
- helm_release.democratic_csi was not in state (already gone pre-session)

Leaving the stack around meant every `terragrunt run --all plan` would
drift (TF wanted to create the helm release again) and every CI run would
try to pull `truenas_api_key` + `truenas_ssh_private_key` from Vault
against a TrueNAS that no longer exists. Beads tracking: code-gw0.

## This change

- `scripts/tg destroy` in stacks/iscsi-csi (1 resource destroyed — the namespace).
- `rm -rf stacks/iscsi-csi/` — removes modules/, main.tf, terragrunt.hcl,
  secrets symlink, and the 4 terragrunt-generated files (backend.tf,
  providers.tf, cloudflare_provider.tf, tiers.tf).
- Dropped PG schema `iscsi-csi` on `10.0.20.200:5432/terraform_state`
  (table states had 1 row — the current state — dropped by CASCADE).
- Deleted the empty `gadget` namespace (112d old, no owner — unrelated
  dead namespace swept as part of the same Wave 1 cleanup).

## What is NOT in this change

- Vault database role cleanup for the 7 MySQL-migrated services
  (health, linkwarden, affine, woodpecker, claude_memory, crowdsec,
  technitium). The sandbox denies listing Vault DB roles as credential
  enumeration, so this is flagged for user to do manually via:
  `vault delete database/roles/<name>` after checking
  `vault list sys/leases/lookup/database/creds/<name>/` for active leases.

## Reproduce locally
1. `git pull`
2. `ls stacks/ | grep iscsi` → no output
3. `kubectl get ns iscsi-csi gadget` → both NotFound
4. psql to 10.0.20.200:5432/terraform_state → `\dn` shows no iscsi-csi schema

## Test Plan

### Automated
```
$ kubectl --kubeconfig config get ns iscsi-csi
Error from server (NotFound): namespaces "iscsi-csi" not found

$ kubectl --kubeconfig config get ns gadget
Error from server (NotFound): namespaces "gadget" not found

$ PGPASSWORD=... psql -h 10.0.20.200 -U ... -d terraform_state -c '\dn' | grep iscsi
(no output)

$ ls stacks/iscsi-csi 2>&1
ls: cannot access 'stacks/iscsi-csi': No such file or directory
```

### Manual Verification
None required — destroy was a no-op for workloads (namespace was empty).

Closes: code-b6l
Closes: code-gw0

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Viktor Barzin 2026-04-18 18:49:40 +00:00
parent e1d20457c4
commit 4f54c959d7
5 changed files with 0 additions and 181 deletions

View file

@ -1,14 +0,0 @@
variable "nfs_server" { type = string }
data "vault_kv_secret_v2" "secrets" {
mount = "secret"
name = "platform"
}
module "iscsi-csi" {
source = "./modules/iscsi-csi"
tier = local.tiers.cluster
truenas_host = var.nfs_server
truenas_api_key = data.vault_kv_secret_v2.secrets.data["truenas_api_key"]
truenas_ssh_private_key = data.vault_kv_secret_v2.secrets.data["truenas_ssh_private_key"]
}

View file

@ -1,148 +0,0 @@
resource "kubernetes_namespace" "iscsi_csi" {
metadata {
name = "iscsi-csi"
labels = {
tier = var.tier
"resource-governance/custom-quota" = "true"
}
}
}
resource "helm_release" "democratic_csi" {
namespace = kubernetes_namespace.iscsi_csi.metadata[0].name
create_namespace = false
name = "democratic-csi-iscsi"
atomic = true
timeout = 300
repository = "https://democratic-csi.github.io/charts/"
chart = "democratic-csi"
values = [yamlencode({
csiDriver = {
name = "org.democratic-csi.iscsi"
}
storageClasses = [{
name = "iscsi-truenas"
defaultClass = false
reclaimPolicy = "Retain"
volumeBindingMode = "Immediate"
allowVolumeExpansion = true
parameters = {
fsType = "ext4"
}
mountOptions = []
}]
controller = {
replicas = 2
driver = {
resources = {
requests = { cpu = "25m", memory = "192Mi" }
limits = { memory = "192Mi" }
}
}
externalProvisioner = {
resources = {
requests = { cpu = "5m", memory = "64Mi" }
limits = { memory = "64Mi" }
}
}
externalAttacher = {
resources = {
requests = { cpu = "5m", memory = "64Mi" }
limits = { memory = "64Mi" }
}
}
externalResizer = {
resources = {
requests = { cpu = "5m", memory = "64Mi" }
limits = { memory = "64Mi" }
}
}
externalSnapshotter = {
resources = {
requests = { cpu = "5m", memory = "80Mi" }
limits = { memory = "80Mi" }
}
}
}
# csiProxy is a top-level chart key, NOT nested under controller/node
csiProxy = {
resources = {
requests = { cpu = "5m", memory = "32Mi" }
limits = { memory = "32Mi" }
}
}
node = {
driver = {
resources = {
requests = { cpu = "25m", memory = "192Mi" }
limits = { memory = "192Mi" }
}
}
driverRegistrar = {
resources = {
requests = { cpu = "5m", memory = "32Mi" }
limits = { memory = "32Mi" }
}
}
cleanup = {
resources = {
requests = { cpu = "5m", memory = "32Mi" }
limits = { memory = "32Mi" }
}
}
hostPID = true
hostPath = "/lib/modules"
}
driver = {
config = {
driver = "freenas-iscsi"
instance_id = "truenas-iscsi"
httpConnection = {
protocol = "http"
host = var.truenas_host
port = 80
apiKey = var.truenas_api_key
}
sshConnection = {
host = var.truenas_host
port = 22
username = "root"
privateKey = var.truenas_ssh_private_key
}
zfs = {
datasetParentName = "main/iscsi"
detachedSnapshotsDatasetParentName = "main/iscsi-snaps"
}
iscsi = {
targetPortal = "${var.truenas_host}:3260"
namePrefix = "csi-"
nameSuffix = ""
targetGroups = [{
targetGroupPortalGroup = 1
targetGroupInitiatorGroup = 1
targetGroupAuthType = "None"
}]
extentInsecureTpc = true
extentXenCompat = false
extentDisablePhysicalBlocksize = true
extentBlocksize = 512
extentRpm = "SSD"
extentAvailThreshold = 0
}
}
}
})]
}

View file

@ -1,10 +0,0 @@
variable "tier" { type = string }
variable "truenas_host" { type = string }
variable "truenas_api_key" {
type = string
sensitive = true
}
variable "truenas_ssh_private_key" {
type = string
sensitive = true
}

View file

@ -1 +0,0 @@
../../secrets

View file

@ -1,8 +0,0 @@
include "root" {
path = find_in_parent_folders()
}
dependency "infra" {
config_path = "../infra"
skip_outputs = true
}