docs(07): create phase plan

Phase 07: GitOps Foundation
- 2 plan(s) in 2 wave(s)
- Wave 1: 07-01 (register application)
- Wave 2: 07-02 (verify gitops behavior)
- Ready for execution

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Thomas Richter
2026-02-03 14:54:41 +01:00
parent c1c46d9581
commit 1d4302d5bf
3 changed files with 454 additions and 4 deletions

View File

@@ -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*

View File

@@ -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"
---
<objective>
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.
</objective>
<execution_context>
@/home/tho/.claude/get-shit-done/workflows/execute-plan.md
@/home/tho/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/07-gitops-foundation/07-CONTEXT.md
@argocd/application.yaml
@helm/taskplaner/values.yaml
</context>
<tasks>
<task type="auto">
<name>Task 1: Create ArgoCD repository secret for TaskPlanner</name>
<files>argocd/repo-secret.yaml</files>
<action>
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_FROM_EXISTING_SECRET>
```
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 <<EOF | kubectl apply -f -
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: "$PASSWORD"
EOF
```
</action>
<verify>
```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.
</verify>
<done>Secret `taskplaner-repo` exists in argocd namespace with correct labels and credentials.</done>
</task>
<task type="auto">
<name>Task 2: Update and apply ArgoCD Application manifest</name>
<files>argocd/application.yaml</files>
<action>
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
```
</action>
<verify>
```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.
</verify>
<done>ArgoCD Application `taskplaner` exists and ArgoCD begins syncing.</done>
</task>
<task type="auto">
<name>Task 3: Wait for sync and verify healthy status</name>
<files></files>
<action>
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
</action>
<verify>
```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`
</verify>
<done>Application shows "Synced" status and "Healthy" health in ArgoCD.</done>
</task>
</tasks>
<verification>
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
</verification>
<success_criteria>
- 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
</success_criteria>
<output>
After completion, create `.planning/phases/07-gitops-foundation/07-01-SUMMARY.md`
</output>

View File

@@ -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"
---
<objective>
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.
</objective>
<execution_context>
@/home/tho/.claude/get-shit-done/workflows/execute-plan.md
@/home/tho/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.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
</context>
<tasks>
<task type="auto">
<name>Task 1: Test auto-sync by pushing a helm change</name>
<files>helm/taskplaner/values.yaml</files>
<action>
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}'
```
</action>
<verify>
```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
```
</verify>
<done>Git push triggered ArgoCD sync within 3 minutes, pod shows new annotation.</done>
</task>
<task type="auto">
<name>Task 2: Test self-heal by deleting a pod</name>
<files></files>
<action>
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.
</action>
<verify>
```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.
</verify>
<done>Pod deletion triggered restore, ArgoCD shows Synced + Healthy status.</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<what-built>
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
</what-built>
<how-to-verify>
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
</how-to-verify>
<resume-signal>Type "approved" if ArgoCD shows TaskPlanner as Synced/Healthy and app works, or describe any issues.</resume-signal>
</task>
</tasks>
<verification>
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
</verification>
<success_criteria>
- 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
</success_criteria>
<output>
After completion, create `.planning/phases/07-gitops-foundation/07-02-SUMMARY.md`
</output>