Sync Phases and Hooks: Run Your Own Code Around a Sync
Your First Hook: A PostSync Smoke Test
The todo app keeps its tasks in a Python list in memory. There is no database, no schema, nothing to prepare before a deploy. So PreSync has no honest job to do here. The useful question for this app is the opposite one: after a deploy, is the API actually answering? That is a PostSync hook, a smoke test that hits the running service and fails the sync if the service is down.
Write the Hook
Create the hook manifest in the same directory the Application already tracks, manifests/plain. Because the file lives in the source path, Argo CD picks it up on the next sync. The hook annotation is the only thing that makes it a hook rather than a normal managed Job.
cat <<'EOF' > $HOME/todo/app/manifests/plain/smoke-test-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
# generateName, not name: each sync creates a fresh Job, so the test
# runs every time. A fixed name runs once and is then skipped.
generateName: todo-app-smoke-test-
namespace: default
annotations:
# The hook phase
argocd.argoproj.io/hook: PostSync
# Delete the Job on success.
# Keep it on failure for debugging.
argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
# How many times a Job's pod can fail
# before Kubernetes marks the whole Job failed.
backoffLimit: 2
# The whole Job must finish within 120s.
activeDeadlineSeconds: 120
template:
spec:
restartPolicy: Never
containers:
- name: smoke-test
image: curlimages/curl:8.11.0 # pin to a current tag
command: ["/bin/sh", "-c"]
args:
- |
# A healthy Deployment means pods are Ready, which is only
# as reliable as the readiness probe. Retry before failing.
for i in $(seq 1 10); do
if curl -fsS http://todo-app.default.svc.cluster.local:5000/tasks; then
echo "smoke test passed"
exit 0
fi
echo "attempt $i failed, retrying in 3s"
sleep 3
done
echo "smoke test failed"
exit 1
EOF
Note the quoted delimiter, <<'EOF'. The Application manifest in chapter 6 used plain <${GITLAB_URL}. Here you need the opposite. The script contains $i and $(seq 1 10), and those must reach the file untouched so the container's shell expands them at runtime. Quote the delimiter and the shell copies the script into the file unchanged.
Two values to match against your real manifests in manifests/plain: the Service name (todo-app above) and its port (5000). Use whatever your Service actually declares.
Here is the Application manifest:
cat < $HOME/todo/app/manifests/app-plain.yaml
apiVersion: argoproj.io/v1alpha1GitOps the Hard Way, with Argo CD
Build Real GitOps Pipelines From Empty Clusters to Automated DeploysEnroll now to unlock all content and receive all future updates for free.
