Files
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

6.0 KiB

Adding a Cluster Resource End-to-End

The integration walkthrough: one atomic change touching all three layers — Copier schema, ArgoCD Application, Helm values — plus the environment answer file.

Read this when onboarding a brand-new operator, database, MCP component, or workload. For an isolated edit to one layer, read that layer's reference instead (argocd-applications.md, copier-templates.md, helm-values.md, environments.md).

Starting templates: assets/example-cluster-resource/.


Step 1 — Choose number, name, and flag

Band:    from the table in SKILL.md §4
Number:  next available in that band
Name:    <NNN>-<kebab-case-descriptor>
Flag:    <domain>.<descriptor>_is_enabled    (type: bool, default: false)

Confirm the number is free before writing anything:

ls cluster/argocd/ cluster/resources/ | grep -o '[0-9]\{3\}' | sort -u

The directory names are Jinja2 expressions, so the numbers are still visible in a plain listing — no rendering needed for this check.


Step 2 — Declare variables in copier.yaml

<domain>:
  <descriptor>_is_enabled:
    type: bool
    default: false
    help: "Enable <NNN>-<name>"

  <descriptor>:
    image:
      address:
        type: str
        default: ""
        help: "Container image for <name>"
      tag:
        type: str
        default: ""
        help: "Container image tag for <name>"
    # additional variables as the resource requires

The flag defaults to false so existing environments are unaffected until they opt in.


Step 3 — Create the ArgoCD Application

cluster/argocd/{{ '<NNN>-<name>' if <domain>.<descriptor>_is_enabled }}/application.yaml.jinja

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

Step 4 — Create the Helm values template

cluster/resources/{{ '<NNN>-<name>' if <domain>.<descriptor>_is_enabled }}/values.yaml.jinja

The conditional expression must be character-identical to step 3.

{% macro render() %}
# values for <NNN>-<name>
replicaCount: 1

image:
  repository: {{ <domain>.get('<descriptor>', {}).get('image', {}).get('address', '') }}
  tag: {{ <domain>.get('<descriptor>', {}).get('image', {}).get('tag', '') }}
  pullPolicy: Always

# ... remaining values
{% endmacro %}
{%- set result = render() -%}
{%- for line in result.split('\n') -%}
{% if line | trim | length != 0 -%}
{{ line }}
{% endif -%}
{%- endfor %}

Step 5 — Update environment answer files

For each environment that should run this resource:

<domain>:
  <descriptor>_is_enabled: true
  <descriptor>:
    image:
      address: <registry>/<image>
      tag: <version>

Enable it in one environment first — typically the lowest — and promote after it syncs cleanly. Enabling everywhere in the same commit means a template bug reaches production in the same sync cycle.


Step 6 — Validate both states

# Flag off (defaults): the directories must NOT appear
copier copy . /tmp/gitops-render-off --overwrite --defaults
ls /tmp/gitops-render-off/cluster/argocd/ | grep "<NNN>"     # expect no output

# Flag on: the directories must appear
copier copy . /tmp/gitops-render-on --overwrite --defaults \
  --data '<domain>.<descriptor>_is_enabled=true' \
  --data '<domain>.<descriptor>.image.address=myregistry/myimage' \
  --data '<domain>.<descriptor>.image.tag=1.0.0'
ls /tmp/gitops-render-on/cluster/argocd/ | grep "<NNN>"

# Both rendered files must parse
python3 -c "import yaml; yaml.safe_load(open('/tmp/gitops-render-on/cluster/argocd/<NNN>-<name>/application.yaml'))"
python3 -c "import yaml; yaml.safe_load(open('/tmp/gitops-render-on/cluster/resources/<NNN>-<name>/values.yaml'))"

# Helm lint, when a chart path is available
helm lint <chart-path> -f /tmp/gitops-render-on/cluster/resources/<NNN>-<name>/values.yaml

Testing the off state matters as much as the on state: a malformed conditional expression can render the directory unconditionally, silently deploying the new resource to every environment on the next sync.


Step 7 — Consistency checklist

  • The three-digit number is unique and in the correct band.
  • The flag is declared in copier.yaml with type: bool and default: false.
  • The ArgoCD metadata.name matches the directory name exactly.
  • spec.source.path matches the cluster/resources/ directory name exactly.
  • The conditional expressions in cluster/argocd/ and cluster/resources/ are identical.
  • repoURL and targetRevision use Jinja2 variables, not literals.
  • resources-finalizer.argocd.argoproj.io is present in metadata.finalizers.
  • values.yaml.jinja uses the render() macro and stripping footer.
  • All variable references use .get() with a type-appropriate default.
  • No secrets or credentials in any new file.
  • At least one environment's answer file enables and fully configures the resource.
  • Rendered and validated with the flag both off and on.

Mistakes to avoid

  • Creating the ArgoCD Application without the matching cluster/resources/ directory — ArgoCD fails with path-not-found.
  • Different conditional expressions on the two sides — one renders, the other is omitted.
  • Referencing a variable that was never declared in copier.yaml — the render fails for every environment, not just yours.
  • Hardcoding image address or tag in values.yaml.jinja.
  • Setting the new flag default: true — new resources are opt-in.
  • Committing without rendering; a broken template blocks every environment.