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>
196 lines
6.0 KiB
Markdown
196 lines
6.0 KiB
Markdown
# 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
|
|
|
|
```text
|
|
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:
|
|
|
|
```bash
|
|
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`
|
|
|
|
```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`
|
|
|
|
```yaml
|
|
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.
|
|
|
|
```jinja
|
|
{% 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:
|
|
|
|
```yaml
|
|
<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
|
|
|
|
```bash
|
|
# 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.
|