Skip to content

Commit dadb1b5

Browse files
committed
docs: added README
1 parent cc2bf7d commit dadb1b5

7 files changed

Lines changed: 210 additions & 1 deletion

File tree

README.md

Lines changed: 210 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,210 @@
1-
# argocd-values-renderer-plugin
1+
# argocd-appset-params-template-plugin
2+
3+
ApplicationSet plugin generator for one job: take structured input, merge it, render templates inside it, and return parameter objects for `ApplicationSet.template`.
4+
5+
It does **not** generate `ApplicationSet` manifests.
6+
It does **not** replace Argo CD multiple sources.
7+
It stays compatible with both:
8+
9+
- one Helm source with `toYaml .result`
10+
- multiple Helm sources with `toYaml (.result.<part> | default dict)`
11+
12+
## Why this exists
13+
14+
Plain ApplicationSet templating is good at wiring generators into a template.
15+
It is not a recursive structured data pipeline.
16+
17+
This plugin fills that gap:
18+
19+
- global `defaults`
20+
- global `overrides`
21+
- selector-based overrides by `context.*` or `params.*`
22+
- optional fanout through `targets`
23+
- recursive template rendering inside the final result
24+
- pure-expression type preservation
25+
26+
The main use case is Ansible-like templated defaults for Helm values, without hardcoding chart-specific logic into the plugin.
27+
28+
## Contract
29+
30+
All input lives under:
31+
32+
```yaml
33+
spec.generators[].plugin.input.parameters
34+
```
35+
36+
Supported top-level keys:
37+
38+
```yaml
39+
context: {}
40+
params: {}
41+
defaults: {}
42+
overrides: {}
43+
selectorOverrides:
44+
- path: context.cluster.name
45+
overrides:
46+
dev-blabla:
47+
domain: dev.example.com
48+
default:
49+
domain: default.example.com
50+
targets:
51+
- context: {}
52+
params: {}
53+
overrides: {}
54+
options:
55+
maxTemplatePasses: 4
56+
```
57+
58+
Strict rules:
59+
60+
- unknown top-level keys are rejected
61+
- unknown target keys are rejected
62+
- `selectorOverrides[*].path` must start with `context.` or `params.`
63+
- `targets[].defaults` is not supported
64+
- if `targets` is omitted, the plugin returns one result
65+
- if `targets` is present, the plugin returns one result per target
66+
67+
## Output
68+
69+
Each generated object looks like this:
70+
71+
```yaml
72+
context: {}
73+
params: {}
74+
result: {}
75+
```
76+
77+
Use it in `ApplicationSet.template` however you need.
78+
79+
Single Helm source:
80+
81+
```yaml
82+
helm:
83+
values: |
84+
{{- toYaml .result | nindent 12 }}
85+
```
86+
87+
Multiple Helm sources inside one `Application`:
88+
89+
```yaml
90+
sources:
91+
- repoURL: ...
92+
helm:
93+
values: |
94+
{{- toYaml (.result.operator | default dict) | nindent 12 }}
95+
96+
- repoURL: ...
97+
helm:
98+
values: |
99+
{{- toYaml (.result.cluster | default dict) | nindent 12 }}
100+
```
101+
102+
## Merge order
103+
104+
For one resolved item:
105+
106+
1. root `defaults`
107+
2. root `overrides`
108+
3. matching `selectorOverrides` in declaration order
109+
4. target `overrides`
110+
5. recursive template rendering until stable
111+
112+
`context` and `params` are merged separately:
113+
114+
1. root `context` + target `context`
115+
2. root `params` + target `params`
116+
117+
Merge semantics:
118+
119+
- map + map -> recursive merge
120+
- list + list -> replace
121+
- scalar + scalar -> replace
122+
- `null` stays `null`
123+
124+
## Template context
125+
126+
Template strings inside the final result get exactly these namespaces:
127+
128+
```gotemplate
129+
{{ .result.domain }}
130+
{{ .context.cluster.name }}
131+
{{ .params.appName }}
132+
{{ .target.params.appName }}
133+
```
134+
135+
No root aliases like `.name`, `.domain`, or `.appName`.
136+
137+
## Type preservation
138+
139+
There are two rendering modes for strings:
140+
141+
- **pure expression**: the whole string is one template action like `{{ 3 }}` or `{{ dict "tier" "frontend" }}`
142+
- **mixed string**: anything else like `svc-{{ .result.port }}`
143+
144+
Rules:
145+
146+
- pure expressions preserve type
147+
- mixed strings always stay strings
148+
149+
Examples:
150+
151+
```gotemplate
152+
{{ 3 }} -> int
153+
{{ true }} -> bool
154+
{{ list 80 443 }} -> []any{80, 443}
155+
{{ dict "tier" "frontend" }} -> map[string]any
156+
{{ .result.someString }} -> string
157+
svc-{{ .result.port }} -> string
158+
```
159+
160+
To force a string in a pure expression, use `quote`.
161+
162+
## Template functions
163+
164+
Built-ins plus:
165+
166+
- `default`
167+
- `lower`, `upper`, `trim`, `trimPrefix`, `trimSuffix`
168+
- `replace`, `contains`, `hasPrefix`, `hasSuffix`
169+
- `quote`, `squote`
170+
- `list`, `dict`
171+
- `join`, `split`
172+
- `toJson`, `toPrettyJson`, `toYaml`
173+
- `indent`, `nindent`
174+
- `normalize`
175+
- `required`
176+
177+
## Patterns
178+
179+
### 1. One app per cluster via label
180+
181+
Use `matrix(clusters, plugin)`.
182+
183+
See `manifests/applicationset-label-per-cluster-example.yaml`.
184+
185+
### 2. Multiple instances on one cluster
186+
187+
Use `matrix(list(instances), plugin)` when the instance list is explicit.
188+
189+
See `manifests/applicationset-multi-instance-example.yaml`.
190+
191+
### 3. One `Application`, multiple Helm charts
192+
193+
Let the plugin return one structured `result`, then split it in the template per source.
194+
195+
See `manifests/applicationset-multi-source-compatible-example.yaml`.
196+
197+
## Deploy
198+
199+
Runtime manifests are in `manifests/`.
200+
201+
They assume:
202+
203+
- the controller and the plugin share the same token stored in `argocd-appset-params-template-plugin-token`
204+
- the plugin is reachable at `http://argocd-appset-params-template-plugin.argocd.svc.cluster.local`
205+
206+
Apply:
207+
208+
```bash
209+
kubectl apply -k manifests
210+
```

0 commit comments

Comments
 (0)