Module 03 · Platform foundation · 40 min · You are: Platform team
Hand the fleet over to Git
In this module
- You learn: The handover from Terraform to Git: a root Application points Argo CD at a folder of ApplicationSets, and an ApplicationSet turns the cluster list into one Application per cluster. Why a manual change does not survive.
- You do: Propose the root app and the first ApplicationSet by pull request, let Terraform create the root Application, watch Argo CD build the fleet from Git, and check the result on dev. Then delete something by hand and see it come back.
- You end with: Git runs the fleet. Argo CD syncs
dev-platform-namespacesandprod-platform-namespacesfrom your repo, and from now on Terraform only runs when clusters change.
Terraform built the clusters and installed Argo CD. From here on, Terraform only runs when clusters change; everything that runs on the clusters comes from Git through Argo CD. The root Application is the handover point: one object that points at a folder of ApplicationSets.
Open this module in the interactive course to track progress and switch tracks.
Change journey practised: Requested → Proposed → Validated → Previewed → Approved → Merged → Reconciled → Healthy
Concepts
- Root Application
root-app.yamlpoints Argo CD atapplicationsets/in your repo. Everything else is reached from there ("app of apps").- ApplicationSet
- A template plus a generator. The git files generator reads
clusters/*.yamland creates one Application per cluster:dev-platform-namespaces,prod-platform-namespaces. - AppProject
fleet-platformis the project for platform-owned Applications.- Reconcile and self-heal
- Argo CD compares live state with Git every few minutes and fixes differences. A manual change survives only until the next comparison.
Files to know
Open them in your editor or on GitHub; the bullets say what to look at.
root-app.yaml(reference, your repo): The handover point. Twenty lines.applicationsets/platform-namespaces-appset.yaml(reference, your repo): The simplest ApplicationSet. Read it top to bottom once.applicationsets/fleet-platform-project.yaml(reference, your repo): The fence around platform-owned Applications.addons/namespaces/overlays/dev/kustomization.yaml(reference, your repo): A Kustomize overlay: shared base plus dev labels.
Steps
1. Propose the root app and the first ApplicationSet
set-repo-url.sh (no arguments) points every copied manifest at your origin. The plan comment on the pull request lists the two Applications this will create.
cd ~/work/fleet-gitops && git switch main && git pull -q
git switch -c root-app
Copy the root app, a project, the first ApplicationSet and the namespaces it deploys:
git checkout reference/main -- root-app.yaml applicationsets/fleet-platform-project.yaml \
applicationsets/platform-namespaces-appset.yaml addons/namespaces
Point the copied files at your repo instead of the reference:
scripts/set-repo-url.sh
Check it worked. Find the repoURL line: it should be https://github.com/YOUR_GITHUB_USER/fleet-gitops.git, your repo.
git diff root-app.yaml
Run the checks. Expect them to pass:
scripts/validate.sh
git add -A && git commit -m "Root app: hand the fleet to Argo CD"
git push -u origin HEAD
2. Merge, then let Terraform finish the chain
Terraform now finds root-app.yaml and creates exactly one more thing: the root Application.
cd ~/work/fleet-gitops && git switch main && git pull -q
Apply. Read the plan before you type yes: it should say Plan: 1 to add, the root Application, and nothing else.
cd infrastructure/kind && terraform apply
On AKS, EKS or GKE
cd ~/work/fleet-gitops && git switch main && git pull -q
Create the root Application. The cloud scripts are imperative, so you apply the file yourself. (On kind, Terraform does this.)
kubectl --context kind-platform apply -f root-app.yaml
3. Watch Git become the fleet
The root Application reads applicationsets/, the ApplicationSets create Applications, and the Applications deploy to the clusters. You watch the chain build itself.
Within a minute you should see root, then dev-platform-namespaces and prod-platform-namespaces appear. Wait until all of them show Synced and Healthy, then press Ctrl-C.
kubectl --context kind-platform -n argocd get applications -w
4. Check a spoke
Argo CD ran on the hub, but its work happened on dev. Look at dev directly.
The namespaces Argo CD created on dev. Expect a short list, all labelled environment=dev:
kubectl --context kind-dev get namespaces -l environment=dev
One of them in detail. Expect labels such as the environment and the owner: the platform put them there, not you.
kubectl --context kind-dev get namespace observability -o jsonpath='{.metadata.labels}'; echo
Break it on purpose: Delete something Git owns
Someone "cleans up" a namespace on dev that looks unused, with kubectl.
You practise: finding which Application owns a resource, and making a per-environment change with a Kustomize overlay instead of by hand.
1. Break it
Delete the namespace on dev:
kubectl --context kind-dev delete namespace platform-system
Watch it. Press Ctrl-C after a minute.
kubectl --context kind-dev get namespace platform-system -w
2. What you see
The namespace is deleted, and a few seconds later it is back: Active, with a new age. Nobody ran anything. Expected (by whoever cleaned up): gone for good.
3. Fix it yourself
Dev really does not need platform-system. Remove it from dev only, keep it in prod, and do not touch base/. Prove the dev overlay renders without it before you push. Once it is gone from dev, put it back with a revert: the reference keeps it.
Hint 1
Which Application put it back? Search for platform-system in the Argo CD UI, or open dev-platform-namespaces. Which folder does it render?
Hint 2
Each environment has an overlay in addons/namespaces/overlays/. A Kustomize patch containing $patch: delete removes a resource the base defines.
The fix
cd ~/work/fleet-gitops && git switch main && git pull -q
git switch -c dev-without-platform-system
Add to ~/work/fleet-gitops/addons/namespaces/overlays/dev/kustomization.yaml at the end
patches:
- patch: |-
$patch: delete
apiVersion: v1
kind: Namespace
metadata:
name: platform-system
Prove the dev overlay no longer renders the namespace. Expect 0:
kubectl kustomize addons/namespaces/overlays/dev | grep -c platform-system
And that prod still does. Expect 1:
kubectl kustomize addons/namespaces/overlays/prod | grep -c platform-system
git add -A && git commit -m "dev: no platform-system namespace"
git push -u origin HEAD
Open the pull request and merge it. Prune removes the namespace from dev; prod keeps it. After the next sync, dev says NotFound:
kubectl --context kind-dev get namespace platform-system
Prod still has it:
kubectl --context kind-prod get namespace platform-system
Put it back with a revert, through a pull request:
git switch main && git pull -q
git switch -c restore-platform-system && git revert --no-edit HEAD && git push -u origin HEAD
After you fixed it: the five questions
- What failed?
- Live state drifted from Git: a namespace Git declares was deleted by hand.
- How was it detected?
- Argo CD: the Application went OutOfSync and self-heal recreated the namespace within seconds.
- Who responds?
- Nobody needs to restore anything; whoever wants the change makes it in Git.
- How is it corrected?
- A per-environment change in the dev overlay, through a pull request.
- How is it prevented?
- Self-heal makes hand-made changes pointless. Overlays make per-environment differences explicit and reviewed.
Quiz
- What does root-app.yaml point at?
- applicationsets/
- clusters/, the inventory, so that root creates one Application per cluster file
- infrastructure/, the add-on charts, so that root installs them directly on the hub
- You delete a namespace that Git declares. What happens?
- It stays deleted, because Argo CD only creates objects and never restores them afterwards
- Argo CD recreates it (self-heal)
- Terraform recreates it the next time someone runs terraform apply on the hub
- When does Terraform run after this module?
- On every merge, as the last job of the pipeline, so the cluster always matches Git
- Never again, because Argo CD takes over and Terraform is deleted from the repository
- Only when clusters or the hub bootstrap change
Answers
- applicationsets/ The root app syncs the ApplicationSets; they generate everything else.
- Argo CD recreates it (self-heal) selfHeal reconciles drift back to Git. Terraform is not involved after the handover.
- Only when clusters or the hub bootstrap change Infrastructure changes go through Terraform; everything on the clusters goes through Git and Argo CD.
Field note
Keep the root app boring. I have seen teams put logic in it; every one of them regretted it the first time they rebuilt a hub.