diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index a8239e9..ee8b858 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -73,11 +73,11 @@ Decimal phases appear between their surrounding integers in numeric order. 3. Pushing a change to helm/taskplaner/values.yaml triggers automatic deployment within 3 minutes 4. Manually deleting a pod results in ArgoCD restoring it to match Git state 5. ArgoCD UI shows deployment history with sync status for each revision -**Plans**: TBD +**Plans**: 2 plans Plans: -- [ ] 07-01: ArgoCD Helm installation with Traefik ingress -- [ ] 07-02: Application sync and self-heal verification +- [ ] 07-01-PLAN.md — Register TaskPlanner Application with ArgoCD +- [ ] 07-02-PLAN.md — Verify auto-sync and self-heal behavior ### Phase 8: Observability Stack **Goal**: Full visibility into cluster and application health via metrics, logs, and dashboards @@ -125,13 +125,14 @@ Phases execute in numeric order: 7 -> 8 -> 9 | 4. Tags & Organization | v1.0 | 3/3 | Complete | 2026-01-31 | | 5. Search | v1.0 | 3/3 | Complete | 2026-01-31 | | 6. Deployment | v1.0 | 2/2 | Complete | 2026-02-01 | -| 7. GitOps Foundation | v2.0 | 0/2 | Not started | - | +| 7. GitOps Foundation | v2.0 | 0/2 | Planned | - | | 8. Observability Stack | v2.0 | 0/3 | Not started | - | | 9. CI Pipeline Hardening | v2.0 | 0/2 | Not started | - | --- *Roadmap created: 2026-01-29* *v2.0 phases added: 2026-02-03* +*Phase 7 planned: 2026-02-03* *Depth: standard* *v1.0 Coverage: 31/31 requirements mapped* *v2.0 Coverage: 17/17 requirements mapped* diff --git a/.planning/phases/07-gitops-foundation/07-01-PLAN.md b/.planning/phases/07-gitops-foundation/07-01-PLAN.md new file mode 100644 index 0000000..b9be613 --- /dev/null +++ b/.planning/phases/07-gitops-foundation/07-01-PLAN.md @@ -0,0 +1,240 @@ +--- +phase: 07-gitops-foundation +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - argocd/application.yaml + - argocd/repo-secret.yaml +autonomous: true + +must_haves: + truths: + - "ArgoCD can access TaskPlanner Git repository" + - "TaskPlanner Application exists in ArgoCD" + - "Application shows Synced status" + artifacts: + - path: "argocd/repo-secret.yaml" + provides: "Repository credentials for ArgoCD" + contains: "argocd.argoproj.io/secret-type: repository" + - path: "argocd/application.yaml" + provides: "ArgoCD Application manifest" + contains: "kind: Application" + key_links: + - from: "argocd/application.yaml" + to: "ArgoCD server" + via: "kubectl apply" + pattern: "kind: Application" + - from: "argocd/repo-secret.yaml" + to: "Gitea repository" + via: "repository secret" + pattern: "secret-type: repository" +--- + + +Register TaskPlanner with ArgoCD by creating repository credentials and applying the Application manifest. + +Purpose: Enable GitOps workflow where ArgoCD manages TaskPlanner deployment from Git source of truth. +Output: TaskPlanner Application registered in ArgoCD showing "Synced" status. + + + +@/home/tho/.claude/get-shit-done/workflows/execute-plan.md +@/home/tho/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/07-gitops-foundation/07-CONTEXT.md +@argocd/application.yaml +@helm/taskplaner/values.yaml + + + + + + Task 1: Create ArgoCD repository secret for TaskPlanner + argocd/repo-secret.yaml + +Create a Kubernetes Secret for ArgoCD to access the TaskPlanner Gitea repository. + +The secret must: +1. Be in namespace `argocd` +2. Have label `argocd.argoproj.io/secret-type: repository` +3. Use internal cluster URL: `http://gitea-http.gitea.svc.cluster.local:3000/tho/taskplaner.git` +4. Use same credentials as existing gitea-repo secret (username: admin) + +Create the file `argocd/repo-secret.yaml`: +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: taskplaner-repo + namespace: argocd + labels: + argocd.argoproj.io/secret-type: repository +stringData: + type: git + url: http://gitea-http.gitea.svc.cluster.local:3000/tho/taskplaner.git + username: admin + password: +``` + +Get the password from existing gitea-repo secret: +```bash +kubectl get secret gitea-repo -n argocd -o jsonpath='{.data.password}' | base64 -d +``` + +Apply the secret: +```bash +kubectl apply -f argocd/repo-secret.yaml +``` + +Note: Do NOT commit the password to Git. The file should use a placeholder or be gitignored. +Actually, create the secret directly with kubectl instead of a file with real credentials: + +```bash +PASSWORD=$(kubectl get secret gitea-repo -n argocd -o jsonpath='{.data.password}' | base64 -d) +kubectl create secret generic taskplaner-repo \ + --namespace argocd \ + --from-literal=type=git \ + --from-literal=url=http://gitea-http.gitea.svc.cluster.local:3000/tho/taskplaner.git \ + --from-literal=username=admin \ + --from-literal=password="$PASSWORD" \ + --dry-run=client -o yaml | kubectl label -f - argocd.argoproj.io/secret-type=repository --local -o yaml | kubectl apply -f - +``` + +Or simpler approach - just apply with label: +```bash +PASSWORD=$(kubectl get secret gitea-repo -n argocd -o jsonpath='{.data.password}' | base64 -d) +cat < + +```bash +kubectl get secret taskplaner-repo -n argocd +kubectl get secret taskplaner-repo -n argocd -o jsonpath='{.metadata.labels}' +``` +Should show the secret exists with repository label. + + Secret `taskplaner-repo` exists in argocd namespace with correct labels and credentials. + + + + Task 2: Update and apply ArgoCD Application manifest + argocd/application.yaml + +Update `argocd/application.yaml` to: +1. Use internal Gitea URL (matches the repo secret) +2. Remove the inline registry secret (it has a placeholder that shouldn't be in Git) +3. Ensure the Application references the correct image pull secret name + +Changes needed in application.yaml: +1. Change `repoURL` from `https://git.kube2.tricnet.de/tho/taskplaner.git` to `http://gitea-http.gitea.svc.cluster.local:3000/tho/taskplaner.git` +2. Remove the `---` separated Secret at the bottom (gitea-registry-secret with placeholder) +3. The helm values already reference `gitea-registry-secret` for imagePullSecrets + +The registry secret needs to exist separately. Check if it exists: +```bash +kubectl get secret gitea-registry-secret -n default +``` + +If it doesn't exist, create it (the helm chart expects it). Get Gitea registry credentials and create: +```bash +# Create the registry secret for image pulls +kubectl create secret docker-registry gitea-registry-secret \ + --namespace default \ + --docker-server=git.kube2.tricnet.de \ + --docker-username=admin \ + --docker-password="$(kubectl get secret gitea-repo -n argocd -o jsonpath='{.data.password}' | base64 -d)" +``` + +Then apply the Application: +```bash +kubectl apply -f argocd/application.yaml +``` + + +```bash +kubectl get application taskplaner -n argocd +kubectl get application taskplaner -n argocd -o jsonpath='{.status.sync.status}' +``` +Application should exist and show sync status. + + ArgoCD Application `taskplaner` exists and ArgoCD begins syncing. + + + + Task 3: Wait for sync and verify healthy status + + +Wait for ArgoCD to sync the application and verify it reaches Synced + Healthy status. + +```bash +# Wait for sync (up to 5 minutes) +kubectl wait --for=jsonpath='{.status.sync.status}'=Synced application/taskplaner -n argocd --timeout=300s + +# Check health status +kubectl get application taskplaner -n argocd -o jsonpath='{.status.health.status}' + +# Get full status +kubectl get application taskplaner -n argocd -o wide +``` + +If sync fails, check: +1. ArgoCD logs: `kubectl logs -n argocd -l app.kubernetes.io/name=argocd-repo-server` +2. Application status: `kubectl describe application taskplaner -n argocd` +3. Repo connectivity: ArgoCD UI Settings -> Repositories + +Common issues: +- Repo credentials incorrect: Check taskplaner-repo secret +- Helm chart errors: Check argocd-repo-server logs +- Image pull errors: Check gitea-registry-secret + + +```bash +kubectl get application taskplaner -n argocd -o jsonpath='{.status.sync.status}' +kubectl get application taskplaner -n argocd -o jsonpath='{.status.health.status}' +``` +Should output: `Synced` and `Healthy` + + Application shows "Synced" status and "Healthy" health in ArgoCD. + + + + + +Phase success indicators: +1. `kubectl get secret taskplaner-repo -n argocd` returns the secret +2. `kubectl get application taskplaner -n argocd` shows the application +3. Application status is Synced and Healthy +4. ArgoCD UI at argocd.kube2.tricnet.de shows TaskPlanner with green sync status + + + +- Repository secret created with correct labels +- Application manifest applied successfully +- ArgoCD shows TaskPlanner as Synced +- ArgoCD shows TaskPlanner as Healthy +- Requirements GITOPS-01 (already done) and GITOPS-02 satisfied + + + +After completion, create `.planning/phases/07-gitops-foundation/07-01-SUMMARY.md` + diff --git a/.planning/phases/07-gitops-foundation/07-02-PLAN.md b/.planning/phases/07-gitops-foundation/07-02-PLAN.md new file mode 100644 index 0000000..cb7750d --- /dev/null +++ b/.planning/phases/07-gitops-foundation/07-02-PLAN.md @@ -0,0 +1,209 @@ +--- +phase: 07-gitops-foundation +plan: 02 +type: execute +wave: 2 +depends_on: ["07-01"] +files_modified: + - helm/taskplaner/values.yaml +autonomous: false + +must_haves: + truths: + - "Pushing helm changes triggers automatic deployment" + - "Manual pod deletion triggers ArgoCD self-heal" + - "ArgoCD UI shows deployment history" + artifacts: + - path: "helm/taskplaner/values.yaml" + provides: "Test change to trigger sync" + key_links: + - from: "Git push" + to: "ArgoCD sync" + via: "polling (3 min)" + pattern: "automated sync" + - from: "kubectl delete pod" + to: "ArgoCD restore" + via: "selfHeal: true" + pattern: "pod restored" +--- + + +Verify GitOps workflow: auto-sync on Git push and self-healing on manual cluster changes. + +Purpose: Confirm ArgoCD delivers on GitOps promise - Git is source of truth, cluster self-heals. +Output: Verified auto-deploy and self-heal behavior with documentation of tests. + + + +@/home/tho/.claude/get-shit-done/workflows/execute-plan.md +@/home/tho/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/07-gitops-foundation/07-CONTEXT.md +@.planning/phases/07-gitops-foundation/07-01-SUMMARY.md +@argocd/application.yaml +@helm/taskplaner/values.yaml + + + + + + Task 1: Test auto-sync by pushing a helm change + helm/taskplaner/values.yaml + +Make a visible but harmless change to helm/taskplaner/values.yaml and push to trigger ArgoCD sync. + +1. Add or modify a pod annotation that won't affect functionality: +```yaml +podAnnotations: + gitops-test: "verified-YYYYMMDD-HHMMSS" +``` + +Use current timestamp to make change unique. + +2. Commit and push: +```bash +git add helm/taskplaner/values.yaml +git commit -m "test(gitops): verify auto-sync with annotation change" +git push +``` + +3. Wait for ArgoCD to detect and sync (up to 3 minutes polling interval): +```bash +# Watch for sync +echo "Waiting for ArgoCD to detect change (up to 3 minutes)..." +for i in {1..36}; do + REVISION=$(kubectl get application taskplaner -n argocd -o jsonpath='{.status.sync.revision}' 2>/dev/null) + CURRENT_COMMIT=$(git rev-parse HEAD) + if [ "$REVISION" = "$CURRENT_COMMIT" ]; then + echo "Synced to commit: $REVISION" + break + fi + echo "Waiting... ($i/36)" + sleep 5 +done +``` + +4. Verify the pod has the new annotation: +```bash +kubectl get pods -n default -l app.kubernetes.io/name=taskplaner -o jsonpath='{.items[0].metadata.annotations.gitops-test}' +``` + + +```bash +# Verify sync revision matches latest commit +kubectl get application taskplaner -n argocd -o jsonpath='{.status.sync.revision}' +git rev-parse HEAD +# Should match + +# Verify pod annotation +kubectl get pods -n default -l app.kubernetes.io/name=taskplaner -o jsonpath='{.items[0].metadata.annotations.gitops-test}' +# Should show the timestamp from values.yaml +``` + + Git push triggered ArgoCD sync within 3 minutes, pod shows new annotation. + + + + Task 2: Test self-heal by deleting a pod + + +Verify ArgoCD's self-heal restores manual changes to match Git state. + +1. Get current pod name: +```bash +POD_NAME=$(kubectl get pods -n default -l app.kubernetes.io/name=taskplaner -o jsonpath='{.items[0].metadata.name}') +echo "Current pod: $POD_NAME" +``` + +2. Delete the pod (simulating manual intervention): +```bash +kubectl delete pod $POD_NAME -n default +``` + +3. ArgoCD should detect the drift and restore (selfHeal: true in syncPolicy). +Watch for restoration: +```bash +echo "Waiting for ArgoCD to restore pod..." +kubectl get pods -n default -l app.kubernetes.io/name=taskplaner -w & +WATCH_PID=$! +sleep 30 +kill $WATCH_PID 2>/dev/null +``` + +4. Verify new pod is running: +```bash +kubectl get pods -n default -l app.kubernetes.io/name=taskplaner +``` + +5. Verify ArgoCD still shows Synced (not OutOfSync): +```bash +kubectl get application taskplaner -n argocd -o jsonpath='{.status.sync.status}' +``` + +Note: The Deployment controller recreates the pod immediately (Kubernetes behavior), but ArgoCD should also detect this and ensure the state matches Git. The key verification is that ArgoCD remains in Synced state. + + +```bash +kubectl get pods -n default -l app.kubernetes.io/name=taskplaner -o wide +kubectl get application taskplaner -n argocd -o jsonpath='{.status.sync.status}' +kubectl get application taskplaner -n argocd -o jsonpath='{.status.health.status}' +``` +Pod should be running, status should be Synced and Healthy. + + Pod deletion triggered restore, ArgoCD shows Synced + Healthy status. + + + + +GitOps workflow with ArgoCD managing TaskPlanner deployment: +- Repository credentials configured +- Application registered and syncing +- Auto-deploy on Git push verified +- Self-heal on manual changes verified + + +1. Open ArgoCD UI: https://argocd.kube2.tricnet.de +2. Log in (credentials should be available) +3. Find "taskplaner" application in the list +4. Verify: + - Status shows "Synced" (green checkmark) + - Health shows "Healthy" (green heart) + - Click on the application to see deployment details + - Check "History and Rollback" tab shows recent syncs including the test commit +5. Verify TaskPlanner still works: https://task.kube2.tricnet.de + + Type "approved" if ArgoCD shows TaskPlanner as Synced/Healthy and app works, or describe any issues. + + + + + +Phase 7 completion checklist: +1. GITOPS-01: ArgoCD server running - ALREADY DONE (pre-existing) +2. GITOPS-02: ArgoCD syncs TaskPlanner from Git - Verified by sync test +3. GITOPS-03: ArgoCD self-heals manual changes - Verified by pod deletion test +4. GITOPS-04: ArgoCD UI accessible via Traefik - ALREADY DONE (pre-existing) + +Success Criteria from ROADMAP.md: +- [x] ArgoCD server is running and accessible at argocd.tricnet.be +- [ ] TaskPlanner Application shows "Synced" status in ArgoCD UI +- [ ] Pushing a change to helm/taskplaner/values.yaml triggers automatic deployment within 3 minutes +- [ ] Manually deleting a pod results in ArgoCD restoring it to match Git state +- [ ] ArgoCD UI shows deployment history with sync status for each revision + + + +- Auto-sync test: Git push -> ArgoCD detects -> Pod updated (within 3 min) +- Self-heal test: Pod deleted -> ArgoCD restores -> Status remains Synced +- Human verification: ArgoCD UI shows healthy TaskPlanner with deployment history +- All GITOPS requirements satisfied + + + +After completion, create `.planning/phases/07-gitops-foundation/07-02-SUMMARY.md` +