Manual Test Runbook — F8: K8s Reference Manifests Bundle (ArgoCD app-of-apps)
Owner: Sagar | Time: ~5 min (Parts A + B) · +35 min (optional Part C kind bootstrap) | Sandbox: kind (local) or snowops-sandbox-01 AKS
Promotes F8 (
gitops/) from 🟦 Code Complete → 🟩 Shipped. Parts A + B are 100% offline (no cluster). Part C bootstraps the bundle on a local kind cluster (~$0) and confirms all controllers reach Ready + a synthetic app deploys + the SnowOps Kyverno policies admit/reject correctly.
Prerequisites
- Local tooling:
ruby(forvalidate.sh),pre-commit - (Part C only)
kind≥ 0.23,kubectl≥ 1.28,helm≥ 3.14,argocdCLI ≥ 2.11, Docker/Podman running - (Part C only) a fork/branch of
snowops-automationyou can push to (ArgoCD pulls manifests from a git repo, not your working tree) — or use the public repo read-only if the paths are already onmain - Working directory: repo root
Steps
Part A — offline structural validation (~2 min)
- Run the bundle validator (YAML validity + kustomization refs + Application spec shape):
Expected: parsed 13 YAML files, 7 ArgoCD Applications then OK: bundle is structurally valid.
- Lint the shell + YAML:
shellcheck gitops/bootstrap/bootstrap.sh gitops/validate.sh
ruby -ryaml -e "Dir.glob('gitops/**/*.yaml').each { |f| YAML.load_stream(File.read(f)) }; puts 'all gitops YAML parses'"
Expected: shellcheck clean; all gitops YAML parses.
Part B — pre-commit + policy cross-check (~3 min)
- Run pre-commit on the bundle (check-yaml covers
gitops/**):
pre-commit run --files \
gitops/README.md gitops/validate.sh \
gitops/bootstrap/argocd-namespace.yaml gitops/bootstrap/argocd-values.yaml \
gitops/bootstrap/root-app.yaml gitops/bootstrap/bootstrap.sh gitops/bootstrap/README.md \
gitops/apps/kustomization.yaml gitops/apps/cert-manager.yaml gitops/apps/kyverno.yaml \
gitops/apps/external-secrets.yaml gitops/apps/ingress-nginx.yaml \
gitops/apps/snowops-policies.yaml gitops/apps/external-secrets-store.yaml \
gitops/components/external-secrets/serviceaccount.yaml \
gitops/components/external-secrets/cluster-secret-store.yaml \
gitops/components/external-secrets/kustomization.yaml \
docs/runbooks/test/F8.md
Expected: every hook PASS.
- Confirm the D4 policies F8 pre-applies still pass
kyverno test(F8 references them, doesn't fork them):
Expected: 5/5 suites pass.
Part C — kind cluster bootstrap (optional, ~35 min, ~$0)
kind has no Azure, so two things behave differently than on AKS and are expected: (a) the ingress-nginx internal-LB Service stays
<pending>(no Azure LB to fulfil it), and (b) the ESOClusterSecretStoreisSecretSyncedErroruntil you supply a real AKV + Workload Identity. The kind test validates the controllers, the policy floor, and the sync graph — not the Azure-bound data path (that's the F3 + F5 sandbox runbooks).
- Create a kind cluster:
- Push the bundle to your fork + repoint the Applications:
# In gitops/bootstrap/root-app.yaml, gitops/apps/snowops-policies.yaml,
# gitops/apps/external-secrets-store.yaml: replace SNOWOPS_ORG + targetRevision
# with your fork + branch, commit, push.
- Bootstrap ArgoCD + apply the root:
-
ArgoCD pods Ready (
kubectl -n argocd get pods). -
Watch the app-of-apps converge:
kubectl -n argocd port-forward svc/argocd-server 8080:443 &
admin_pw=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d)
argocd login localhost:8080 --username admin --password "$admin_pw" --insecure --grpc-web
argocd app get snowops-platform
argocd app list
-
snowops-platform(root) shows 6 child Applications. -
cert-manager,kyverno,external-secrets(wave 0) reachSynced / Healthy. -
ingress-nginx(wave 1)Synced; controller pod Ready (Service<pending>is OK on kind). -
snowops-policies(wave 2)Synced;kubectl get clusterpolicylists all 5 D4 policies. -
external-secrets-store(wave 2)Synced;kubectl get clustersecretstore snowops-akvexists (status error on kind is expected — no real AKV). -
Synthetic-app admission test (proves the Kyverno floor is live):
# A compliant pod is admitted:
kubectl create namespace f8-probe
kubectl -n f8-probe run good --image=nginx:1.27-alpine \
--labels="app.kubernetes.io/name=good,app.kubernetes.io/version=1,app.kubernetes.io/part-of=f8,snowops.io/owner=sagar" \
--restart=Never --dry-run=server
# A :latest-tagged pod is REJECTED by disallow-latest-tag:
kubectl -n f8-probe run bad --image=nginx:latest --restart=Never --dry-run=server || echo "rejected as expected"
- compliant pod passes server-side admission.
-
:latestpod is rejected with a Kyvernodisallow-latest-tagmessage.
Pass criteria
- Part A —
validate.shreports the bundle structurally valid; shellcheck clean - Part B — pre-commit all green;
kyverno test5/5 - Part C — ArgoCD bootstraps;
snowops-platformrenders 6 child apps - Part C — cert-manager / kyverno / external-secrets / ingress-nginx controllers Ready
- Part C — all 5 D4 ClusterPolicies present;
ClusterSecretStore snowops-akvcreated - Part C — compliant pod admitted;
:latestpod rejected by Kyverno
Teardown
kind delete cluster --name snowops-f8
# or on AKS:
kubectl delete -f gitops/bootstrap/root-app.yaml
helm uninstall argocd -n argocd && kubectl delete namespace argocd
Sign-off
- Tester: _ | Date: _ | Result: PASS / FAIL / N/A
- Notes: