Feedback

Chat Icon

GitOps the Hard Way, with Argo CD

Build Real GitOps Pipelines From Empty Clusters to Automated Deploys

Sync Phases and Hooks: Run Your Own Code Around a Sync
63%

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 <because it needed the shell to expand ${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/v1alpha1

GitOps the Hard Way, with Argo CD

Build Real GitOps Pipelines From Empty Clusters to Automated Deploys

Enroll now to unlock all content and receive all future updates for free.