docs: plan — relocate platform IaC to fxhnt, archive foxhunt (Phase 2D), inline+gated
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
206
docs/superpowers/plans/2026-06-21-relocate-iac-to-fxhnt.md
Normal file
206
docs/superpowers/plans/2026-06-21-relocate-iac-to-fxhnt.md
Normal file
@@ -0,0 +1,206 @@
|
||||
# Relocate Platform IaC foxhunt → fxhnt + Archive foxhunt — Implementation Plan (Phase 2D)
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans (INLINE, with checkpoints).
|
||||
> Spans TWO repos + a production Terraform state gate; the `terragrunt plan = No changes` check is the
|
||||
> safety pin. Steps use checkbox (`- [ ]`) syntax.
|
||||
|
||||
**Goal:** Move active platform IaC (terraform `live`+`modules`, platform k8s manifests) from `foxhunt`
|
||||
into `fxhnt`, preserving TF state keys (no migration), then archive `foxhunt`.
|
||||
|
||||
**Architecture:** Copy into fxhnt preserving the `live/production/<module>` structure → state keys stay
|
||||
identical → `terragrunt plan = No changes` proves it. Then `git rm` from foxhunt, commit/push both,
|
||||
archive foxhunt. Cluster untouched throughout (source-of-truth move only).
|
||||
|
||||
**Tech Stack:** terragrunt + OpenTofu (Scaleway s3 backend), kubectl manifests, git, Gitea.
|
||||
|
||||
**Repos:** source `~/Work/foxhunt`, dest `~/Work/fxhnt`. **Spec:**
|
||||
`docs/superpowers/specs/2026-06-21-relocate-iac-to-fxhnt-design.md`
|
||||
|
||||
---
|
||||
|
||||
## Shared env (terragrunt steps)
|
||||
```bash
|
||||
export TG_TF_PATH=tofu TERRAGRUNT_TFPATH=tofu
|
||||
export SCW_ACCESS_KEY=$(kubectl get secret scaleway-credentials -n foxhunt -o jsonpath='{.data.access-key}'|base64 -d)
|
||||
export SCW_SECRET_KEY=$(kubectl get secret scaleway-credentials -n foxhunt -o jsonpath='{.data.secret-key}'|base64 -d)
|
||||
export SCW_DEFAULT_PROJECT_ID=$(kubectl get secret scaleway-credentials -n foxhunt -o jsonpath='{.data.project-id}'|base64 -d)
|
||||
export SCW_DEFAULT_REGION=fr-par SCW_DEFAULT_ZONE=fr-par-2
|
||||
export AWS_ACCESS_KEY_ID="$SCW_ACCESS_KEY" AWS_SECRET_ACCESS_KEY="$SCW_SECRET_KEY" AWS_REGION=fr-par
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 0: Branches
|
||||
```bash
|
||||
cd /home/jgrusewski/Work/fxhnt && git checkout master && git checkout -b chore/relocate-iac
|
||||
cd /home/jgrusewski/Work/foxhunt && git checkout main && git checkout -b chore/relocate-iac-remove
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Collision pre-flight (must be empty)
|
||||
```bash
|
||||
cd /home/jgrusewski/Work/foxhunt
|
||||
for d in k8s/network-policies k8s/services k8s/jobs k8s/storage; do
|
||||
echo "--- $d ---"; comm -12 <(ls infra/$d 2>/dev/null|sort) <(ls ~/Work/fxhnt/infra/$d 2>/dev/null|sort)
|
||||
done
|
||||
```
|
||||
Expected: no filenames printed. **STOP + resolve if any collision appears.**
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Copy Terraform (live + modules) into fxhnt, structure preserved
|
||||
|
||||
- [ ] **Step 1: rsync source files (exclude generated/cached, keep .terraform.lock.hcl)**
|
||||
```bash
|
||||
cd /home/jgrusewski/Work/foxhunt
|
||||
rsync -a --exclude '.terraform/' --exclude 'backend.tf' --exclude 'provider.tf' --exclude '*.tfstate*' \
|
||||
infra/live infra/modules /home/jgrusewski/Work/fxhnt/infra/
|
||||
find /home/jgrusewski/Work/fxhnt/infra/live /home/jgrusewski/Work/fxhnt/infra/modules -name '*.hcl' -o -name '*.tf' | sort
|
||||
```
|
||||
Expected: `fxhnt/infra/live/production/{root.hcl,kapsule,public-gateway,dns,block-storage}` +
|
||||
`fxhnt/infra/modules/{kapsule,public-gateway,dns,block-storage}` present.
|
||||
|
||||
- [ ] **Step 2: Ensure fxhnt .gitignore covers terragrunt-generated files**
|
||||
```bash
|
||||
cd /home/jgrusewski/Work/fxhnt
|
||||
grep -qE '^backend\.tf' .gitignore || printf '\n# terragrunt-generated\n**/.terraform/\n**/backend.tf\n**/provider.tf\n*.tfstate\n*.tfstate.*\n' >> .gitignore
|
||||
tail -7 .gitignore
|
||||
```
|
||||
|
||||
- [ ] **Step 3: GATE — terragrunt plan = No changes for all 4 modules (from fxhnt)**
|
||||
```bash
|
||||
cd /home/jgrusewski/Work/fxhnt
|
||||
for m in kapsule public-gateway dns block-storage; do
|
||||
echo "=== $m ==="
|
||||
( cd infra/live/production/$m && terragrunt init -reconfigure -input=false -no-color >/dev/null 2>&1 && \
|
||||
terragrunt plan -input=false -no-color 2>&1 | sed 's/.*tofu: //' | grep -iE "No changes|^Plan:|Error" | head -1 )
|
||||
done
|
||||
```
|
||||
Expected: every module prints `No changes.` — proves state keys are unchanged (no migration). **STOP if any
|
||||
module shows a plan diff or Error** (structure not preserved → do not proceed).
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Copy platform k8s manifests + the one platform script into fxhnt
|
||||
|
||||
- [ ] **Step 1: rsync platform k8s dirs (everything except gpu-taint.yaml) + namespace.yaml**
|
||||
```bash
|
||||
cd /home/jgrusewski/Work/foxhunt
|
||||
for d in argo cert-manager databases dns gitea gitlab jobs mattermost minio monitoring network-policies secrets services stalwart tailscale training storage; do
|
||||
rsync -a "infra/k8s/$d" /home/jgrusewski/Work/fxhnt/infra/k8s/
|
||||
done
|
||||
cp infra/k8s/namespace.yaml /home/jgrusewski/Work/fxhnt/infra/k8s/
|
||||
mkdir -p /home/jgrusewski/Work/fxhnt/infra/scripts && cp infra/scripts/generate-minio-tls.sh /home/jgrusewski/Work/fxhnt/infra/scripts/
|
||||
echo "=== fxhnt infra/k8s now ==="; ls /home/jgrusewski/Work/fxhnt/infra/k8s/
|
||||
```
|
||||
Expected: fxhnt/infra/k8s/ now contains the platform dirs (merged with fxhnt's existing
|
||||
jobs/network-policies/services/storage/orchestration) + namespace.yaml. `gpu-taint.yaml` NOT copied.
|
||||
|
||||
- [ ] **Step 2: Sanity — no remaining hardcoded foxhunt-repo paths in moved manifests**
|
||||
```bash
|
||||
cd /home/jgrusewski/Work/fxhnt
|
||||
grep -rIn "Work/foxhunt\|foxhunt/infra\|root/foxhunt" infra/k8s/ infra/live/ infra/modules/ 2>/dev/null | grep -v "foxhunt.svc.cluster.local\|namespace: foxhunt\|name: foxhunt\|cluster_name\|foxhunt-tfstate\|foxhunt-gitlab\|foxhunt-training\|foxhunt-backups\|foxhunt-sccache\|foxhunt-binaries\|foxhunt-agent" | head
|
||||
echo "(above should be empty — 'foxhunt' as the k8s namespace / SCW resource names is expected + correct)"
|
||||
```
|
||||
Expected: empty (the namespace `foxhunt` + Scaleway resource names legitimately stay).
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Verify cluster unaffected + cockpit still deploys from fxhnt
|
||||
|
||||
- [ ] **Step 1: terragrunt still clean from fxhnt + platform health**
|
||||
```bash
|
||||
cd /home/jgrusewski/Work/fxhnt
|
||||
curl -sk -m 10 -o /dev/null -w "dashboard.fxhnt.ai: %{http_code}\n" https://dashboard.fxhnt.ai
|
||||
curl -sk -m 10 https://git.fxhnt.ai/api/healthz 2>/dev/null | grep -o '"status": "pass"'
|
||||
```
|
||||
Expected: dashboard 200; gitea pass. (No cluster change was made — this confirms nothing regressed.)
|
||||
|
||||
---
|
||||
|
||||
## Task 5: Commit fxhnt (additions) + push
|
||||
|
||||
- [ ] **Step 1**
|
||||
```bash
|
||||
cd /home/jgrusewski/Work/fxhnt
|
||||
git add infra/ .gitignore
|
||||
git commit -m "feat(infra): adopt platform IaC from foxhunt — terraform live+modules + k8s manifests (Phase 2D)
|
||||
|
||||
fxhnt is now the single home for platform IaC. Terraform state keys unchanged
|
||||
(live/production structure preserved) — terragrunt plan = No changes, no migration.
|
||||
Includes k8s platform manifests (cert-manager, databases, dns, gitea, minio, monitoring,
|
||||
network-policies, tailscale, stalwart, mattermost, services, storage, jobs, training,
|
||||
secrets, argo, gitlab proxy) + generate-minio-tls.sh.
|
||||
|
||||
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
|
||||
GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=accept-new" git push origin chore/relocate-iac 2>&1 | tail -2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 6: Remove the moved paths from foxhunt + commit + push
|
||||
|
||||
- [ ] **Step 1: git rm the relocated IaC from foxhunt (keep gpu-taint.yaml, docker, Rust scripts)**
|
||||
```bash
|
||||
cd /home/jgrusewski/Work/foxhunt
|
||||
git rm -r -q infra/live infra/modules
|
||||
for d in argo cert-manager databases dns gitea gitlab jobs mattermost minio monitoring network-policies secrets services stalwart tailscale training storage; do git rm -r -q "infra/k8s/$d"; done
|
||||
git rm -q infra/k8s/namespace.yaml infra/scripts/generate-minio-tls.sh
|
||||
echo "=== foxhunt infra/ remnant (Rust-only) ==="; find infra -maxdepth 2 | sort | head -30
|
||||
```
|
||||
Expected: `infra/k8s/gpu-taint.yaml` + `infra/docker/*` + `infra/scripts/{build-and-push,harden,smoke-test,train,...}` remain; live/modules + platform k8s gone.
|
||||
|
||||
- [ ] **Step 2: Commit + push foxhunt**
|
||||
```bash
|
||||
cd /home/jgrusewski/Work/foxhunt
|
||||
git commit -q -m "chore(infra): relocate platform IaC to fxhnt (Phase 2D)
|
||||
|
||||
Platform terraform (live+modules) + k8s manifests moved to the fxhnt repo (now the IaC
|
||||
home). Rust-only remnants kept here (docker build images, gpu-taint, Rust scripts).
|
||||
foxhunt is being archived; history retained. See fxhnt for live platform IaC."
|
||||
GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=accept-new" git push origin chore/relocate-iac-remove 2>&1 | tail -2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 7: Merge both branches, then archive foxhunt
|
||||
|
||||
- [ ] **Step 1: Merge fxhnt branch → master + push**
|
||||
```bash
|
||||
cd /home/jgrusewski/Work/fxhnt
|
||||
git checkout master && git merge --no-ff chore/relocate-iac -m "Merge: adopt platform IaC from foxhunt (Phase 2D)" 2>&1 | tail -2
|
||||
git branch -d chore/relocate-iac
|
||||
GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=accept-new" git push origin master 2>&1 | tail -1
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Merge foxhunt branch → main + push**
|
||||
```bash
|
||||
cd /home/jgrusewski/Work/foxhunt
|
||||
git checkout main && git merge --no-ff chore/relocate-iac-remove -m "Merge: relocate platform IaC to fxhnt (Phase 2D)" 2>&1 | tail -2
|
||||
git branch -d chore/relocate-iac-remove
|
||||
GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=accept-new" git push origin main 2>&1 | tail -1
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Final re-verify terragrunt from fxhnt (post-merge) + archive foxhunt in Gitea**
|
||||
```bash
|
||||
cd /home/jgrusewski/Work/fxhnt
|
||||
( cd infra/live/production/kapsule && terragrunt plan -input=false -no-color 2>&1 | sed 's/.*tofu: //' | grep -iE "No changes|^Plan:" | head -1 )
|
||||
GA=gitadmin; GP=$(kubectl get secret gitea-admin -n foxhunt -o jsonpath='{.data.password}'|base64 -d)
|
||||
curl -sk -u "$GA:$GP" -X PATCH "https://git.fxhnt.ai/api/v1/repos/$GA/foxhunt" -H 'Content-Type: application/json' -d '{"archived":true}' -o /dev/null -w "archive foxhunt: %{http_code}\n"
|
||||
```
|
||||
Expected: kapsule `No changes`; `archive foxhunt: 200`.
|
||||
|
||||
---
|
||||
|
||||
## Rollback
|
||||
- Before Task 6 (foxhunt rm): just delete `fxhnt/infra/{live,modules}` + the copied k8s dirs; nothing lost
|
||||
(foxhunt still authoritative).
|
||||
- After: both repos retain history; un-archive foxhunt + `git revert` if needed. TF state never moved.
|
||||
|
||||
## Acceptance criteria (from spec)
|
||||
- `fxhnt/infra/{live,modules}` present; `terragrunt plan = No changes` all 4 modules from fxhnt. ✅ T2, T7
|
||||
- Platform k8s manifests + generate-minio-tls.sh in fxhnt; no collisions lost. ✅ T1, T3
|
||||
- Moved paths `git rm`'d from foxhunt; both repos committed + pushed. ✅ T5, T6, T7
|
||||
- dashboard 200 + cockpit deploy unaffected. ✅ T4
|
||||
- foxhunt archived in Gitea; Rust code + history intact. ✅ T7
|
||||
Reference in New Issue
Block a user