TT Lab
Get started
Learn Learning paths Courses

CGOA — GitOps Certified Associate

Create Drift, See It, and Undo It

Continue in TT Lab

Goal

You create drift yourself, compute the difference and leave it in a file, and then go through one full lap by hand, reverting to the declared state. Next you write sync wave and PreSync hook manifests, and search the cluster to determine the prune targets.

Why it matters

Argo CD's reconcile loop is not magic; it repeats what you do in this lab every 180 seconds by default. It re-renders, reads live, compares, and applies if the policy allows. If you run this loop by hand once, you come to understand in your bones why refresh and sync are different actions and what discipline turning on selfHeal demands of operations. The same goes for determining prune targets — the agent does not delete every resource in the cluster; it deletes only those, among the ones it has marked as created by itself, that have disappeared from Git. If you do not know this boundary, you cannot explain either "why isn't that one being deleted?" or "why did that get deleted?" The Argo CD CRDs are not in this environment, so you write waves and hooks as files, and grading also works by reading the files.

Steps

  1. In /root/cgoa-drift/desired/orders.yaml, write a Deployment named orders — namespace cgoa-drift, spec.replicas: 2, selector and Pod labels app: orders, container name app, image nginx:1.27-alpine. Create the namespace cgoa-drift and apply this file with kubectl apply -f.
  2. Leaving the file as is, change only the orders replicas on the cluster to 5 to create drift. Then read the changed live value with kubectl get ... -o jsonpath and save it to /root/cgoa-drift/live-replicas.txt (it stays as evidence even after you revert it later).
  3. Save the output of kubectl diff -f /root/cgoa-drift/desired/orders.yaml to /root/cgoa-drift/drift.txt. The file must show the replicas difference.
  4. Apply the original manifest again to revert replicas to 2.
  5. In /root/cgoa-drift/wave/, create three files. namespace.yaml — Namespace cgoa-wave, annotation argocd.argoproj.io/sync-wave: "-2". config.yaml — ConfigMap orders-wave-config (namespace cgoa-wave, any one key in data), annotation argocd.argoproj.io/sync-wave: "-1". app.yaml — Deployment orders-wave (namespace cgoa-wave), annotation argocd.argoproj.io/sync-wave: "1".
  6. In /root/cgoa-drift/hooks/db-migrate.yaml, write a Job named db-migrate — namespace cgoa-drift, the annotations argocd.argoproj.io/hook: PreSync, argocd.argoproj.io/hook-delete-policy: BeforeHookCreation, and argocd.argoproj.io/sync-wave: "-1", spec.backoffLimit: 1, the Pod's restartPolicy: Never, and container name migrate.
  7. Determine the prune targets. First, declare a ConfigMap orders-config (namespace cgoa-drift, any one key in data) in /root/cgoa-drift/desired/config.yaml and apply it. Then create a ConfigMap named legacy-config in the cgoa-drift namespace imperatively, without a file. Finally, in /root/cgoa-drift/prune-candidates.txt, write, one per line, the names of the ConfigMaps in the cgoa-drift namespace that are not declared anywhere in the desired/ directory. (kube-root-ca.crt, which Kubernetes creates automatically, is not a managed object, so exclude it.)

Notes

Declare and apply the desired state

In /root/cgoa-drift/desired/orders.yaml, write a Deployment named orders — namespace cgoa-drift, spec.replicas: 2, selector and Pod labels app: orders, container name app, image nginx:1.27-alpine. Create the namespace cgoa-drift and apply this file with kubectl apply -f.

When you apply declaratively, the last applied configuration is left on the object as an annotation. If you create it imperatively, that annotation is absent — grading looks at that difference.

Create drift by editing by hand

Leaving the file as is, change only the orders replicas on the cluster to 5 to create drift. Then read the changed live value with kubectl get ... -o jsonpath and save it to /root/cgoa-drift/live-replicas.txt (it stays as evidence even after you revert it later).

You must leave the file as is and change only the cluster. There is an imperative subcommand that changes the scale. Extract the changed live value with jsonpath and leave it in a file — it serves as evidence even after you revert.

Compute the difference and leave it in a file

Save the output of kubectl diff -f /root/cgoa-drift/desired/orders.yaml to /root/cgoa-drift/drift.txt. The file must show the replicas difference.

kubectl diff -f <파일> (the placeholder is the file) sends a dry run to the server and shows the actual difference. When there are differences the exit code is 1, so make sure it does not abort when you redirect.

Revert to the declared state

Apply the original manifest again to revert replicas to 2.

You do by hand what the agent's selfHeal does. Just apply the original file again as is.

Three sync wave annotations

In /root/cgoa-drift/wave/, create three files. namespace.yaml — Namespace cgoa-wave, annotation argocd.argoproj.io/sync-wave: "-2". config.yaml — ConfigMap orders-wave-config (namespace cgoa-wave, any one key in data), annotation argocd.argoproj.io/sync-wave: "-1". app.yaml — Deployment orders-wave (namespace cgoa-wave), annotation argocd.argoproj.io/sync-wave: "1".

Write wave numbers as strings. Choose the numbers so that infrastructure comes first, configuration next, and the workload last.

PreSync hook Job

In /root/cgoa-drift/hooks/db-migrate.yaml, write a Job named db-migrate — namespace cgoa-drift, the annotations argocd.argoproj.io/hook: PreSync, argocd.argoproj.io/hook-delete-policy: BeforeHookCreation, and argocd.argoproj.io/sync-wave: "-1", spec.backoffLimit: 1, the Pod's restartPolicy: Never, and container name migrate.

The hook type and the deletion policy are different annotation keys. For the deletion policy, it is better for review to write the default value explicitly.

Determine the prune targets

Determine the prune targets. First, declare a ConfigMap orders-config (namespace cgoa-drift, any one key in data) in /root/cgoa-drift/desired/config.yaml and apply it. Then create a ConfigMap named legacy-config in the cgoa-drift namespace imperatively, without a file. Finally, in /root/cgoa-drift/prune-candidates.txt, write, one per line, the names of the ConfigMaps in the cgoa-drift namespace that are not declared anywhere in the desired/ directory. (kube-root-ca.crt, which Kubernetes creates automatically, is not a managed object, so exclude it.)

A prune target is a resource that is "in the cluster but not in the declaration directory." Remember that objects the cluster creates automatically are not targets.