Files
development-gitops-argo-cd-…/references/argocd-applications.md
oleg-lukasonokandClaude Opus 5 50d746d03f Add gitops-argo-cd agent skill
Consolidates the five cogarchhubgitops-* source skills into one skill with
scope routing: ArgoCD Applications, Copier templates, Helm values, environment
answer files, and the end-to-end resource-addition walkthrough.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 23:48:32 +03:00

5.7 KiB

ArgoCD Application Manifests

Everything under cluster/argocd/: the root app-of-apps, numbered child applications, sync policies, finalizers, and destinations.

Read this when creating a child Application, changing sync behaviour, moving a destination namespace, or debugging an app that will not sync.


1. Template anatomy

Every cluster/argocd/**/application.yaml.jinja follows this shape:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: <NNN-name>
  namespace: openshift-gitops
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    repoURL: {{ gitops.repo_url }}
    targetRevision: {{ gitops.branch }}
    path: cluster/resources/<NNN-name>
    helm:
      valueFiles:
        - values.yaml
  destination:
    server: https://kubernetes.default.svc
    namespace: <target-namespace>
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
Field Rule
metadata.name Exactly equal to the parent directory name (210-eso-operator-group)
metadata.namespace Always the ArgoCD install namespace — openshift-gitops on OpenShift GitOps
metadata.finalizers Must include resources-finalizer.argocd.argoproj.io
spec.project default unless the cluster defines scoped AppProjects
spec.source.repoURL Always {{ gitops.repo_url }} — never a literal URL
spec.source.targetRevision Always {{ gitops.branch }} — never a literal branch
spec.source.path Exactly the mirrored cluster/resources/ path
spec.source.helm.valueFiles Usually values.yaml, the rendered values.yaml.jinja
spec.destination.server https://kubernetes.default.svc for in-cluster
spec.destination.namespace The workload's target namespace

Note the asymmetry in spec.source.path: the path value is the plain rendered directory name (cluster/resources/210-eso-operator-group), while the directory on disk is the Jinja2 conditional expression. Copier renders the expression away, so the two agree in the output — but only if the literal you type in path matches what the expression evaluates to.


2. Sync policy rules

  • automated.prune: true — removes resources no longer present in Git. Required; without it a disabled feature flag leaves its resources running in the cluster forever.
  • automated.selfHeal: true — reverts manual cluster changes. Required; disabling it means kubectl edit silently wins over Git, which defeats the whole contract.
  • Any exception to either must be justified in a YAML comment directly above syncPolicy.
  • syncOptions: [CreateNamespace=true] — set for any app whose destination namespace is not guaranteed to pre-exist. Harmless when the namespace already exists.
  • Retry settings are optional; add syncPolicy.retry only for apps with known flaky dependencies, and document the reason.

3. Naming rules

  • Three-digit prefix from the correct band (see SKILL.md §4), next free number.
  • Descriptive part in lowercase kebab-case: 210-eso-operator-group.
  • metadata.name == directory name == the value inside the conditional expression.
  • Wrap the directory in {{ 'NNN-name' if <feature_flag> }} — no exceptions; an unconditional directory renders in every environment whether or not it is wanted.

4. Adding a child application

  1. Choose the band and the next free number in it.

  2. Create the conditional directory:

    cluster/argocd/{{ '<NNN-name>' if <feature_flag> }}/application.yaml.jinja
    
  3. Create the mirror:

    cluster/resources/{{ '<NNN-name>' if <feature_flag> }}/values.yaml.jinja
    
  4. Write the manifest from the anatomy in section 1.

  5. Declare <feature_flag> in copier.yaml with type: bool, default: false.

  6. Add the resource's configuration variables alongside the flag.

  7. Render and validate (SKILL.md §8).

No edit to the root application.yaml.jinja is needed — the app-of-apps discovers the new child automatically.


5. Troubleshooting

Symptom Likely cause
App missing entirely from ArgoCD Feature flag false in that environment's answer file, or the conditional directory expression is malformed and evaluated falsy
path does not exist / ComparisonError spec.source.path does not match the rendered cluster/resources/ directory, or the mirror directory's conditional expression differs from the argocd one
Literal {{ gitops.repo_url }} in the live manifest File was committed as application.yaml instead of application.yaml.jinja, so Copier copied it through unrendered
Permanently OutOfSync after a manual cluster edit selfHeal disabled — the cluster and Git disagree and nothing reconciles them
Resources survive after deleting the app Missing resources-finalizer.argocd.argoproj.io
Resources vanish after a rename Renaming a directory prunes the old app; expected — confirm the new app synced before assuming breakage
Sync fails on namespace not found Missing CreateNamespace=true in syncOptions

Inspect the live state:

argocd app list
argocd app get <NNN-name>
argocd app diff <NNN-name>
kubectl -n openshift-gitops get applications.argoproj.io

6. Mistakes to avoid

  • Hardcoding repoURL or targetRevision instead of the Jinja2 variables.
  • Omitting the finalizer.
  • Creating the argocd side without the mirrored resources side.
  • Reusing a numeric prefix across domains (a 3xx number for a database app).
  • Writing application.yaml without the .jinja extension.
  • Disabling selfHeal or prune silently.
  • Letting metadata.name drift from the directory name after a rename.