Skip to content

Commit aa9bc83

Browse files
fix(flux): render-infra dropped all but the first substitute variable
renderKustomization joined every parameter into one comma-separated --parameters string. `substitute` cannot survive that: its value is itself a comma-separated list, so the join destroyed the boundary before the KCL module saw it, and the module's own split then kept only the first pair. The code exempted `substitute` from its own comma check with the comment "the module parses it that way" -- but by then there was nothing left to parse correctly. It failed silently, which is the worst part. The render succeeded and produced valid-looking Kustomizations. Measured on test-infra1 2026-08-24, rendering that cluster's own eight components: cilium-lb 2 variables in -> 1 out (no CILIUM_LB_IP_STOP) cilium-gateway 4 in -> 1 out (no domain, no TLS secret) cert-manager-selfsigned 8 in -> 1 out trust-manager 3 in -> 1 out flux-web / headlamp 6 in -> 1 out each Applied, the wildcard certificate would have been issued for the sentinel set-INFRA_DOMAIN.invalid and the Gateway would have had no TLS secret. Parameters now travel as a YAML file via --parameters-file, the channel the KCL module offers for exactly this. A YAML document has no such ambiguity. After the fix all eight components carry exactly the variables the values file names, and the rendered paths and substitute maps match the live cluster wherever the values file spells a value out. Also adds examples/infra-values-test-infra1.yaml: a full eight-component set describing a real cluster, which is what made the comparison possible. It carries certManagerVersion explicitly, and the comment says why -- the KCL module pins v1.19.1 as its own default and writes it as an explicit substitute, which beats the base default of v1.21.1. Without that line an apply downgrades cert-manager by two minor versions, silently. The module pin is worth bumping separately. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYkrt9hhBe7ei2d6v3Wudv
1 parent ee9f54c commit aa9bc83

2 files changed

Lines changed: 109 additions & 13 deletions

File tree

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
---
2+
# test-infra1 (LabDA) -- a full eight-component set, kept as the worked
3+
# example next to the minimal infra-values.yaml.
4+
#
5+
# It describes what that cluster actually runs, which is what made it useful:
6+
# rendering this and comparing against the live Kustomizations is how the
7+
# substitute bug and the stale cert-manager pin were both found. Every path
8+
# matches the running cluster, and the substitute maps match wherever this file
9+
# spells a value out.
10+
source:
11+
enabled: true
12+
name: flux-infra
13+
url: https://github.com/stuttgart-things/flux.git
14+
tag: v1.27.0
15+
interval: 1m0s
16+
17+
components:
18+
cilium-lb:
19+
enabled: true
20+
templateName: infrastructure
21+
params:
22+
path: ./infra/cilium/components/lb
23+
sourceRefName: flux-infra
24+
substitute: CILIUM_LB_IP_START=10.100.136.224,CILIUM_LB_IP_STOP=10.100.136.224
25+
26+
cilium-gateway:
27+
enabled: true
28+
templateName: infrastructure
29+
params:
30+
path: ./infra/cilium/components/gateway
31+
sourceRefName: flux-infra
32+
substitute: CILIUM_GATEWAY_NAME=cilium-gateway,CILIUM_GATEWAY_NAMESPACE=default,CILIUM_GATEWAY_DOMAIN=test-infra1.4sthings.tiab.ssc.sva.de,CILIUM_GATEWAY_TLS_SECRET=wildcard-test-infra1-tls
33+
34+
cert-manager-install:
35+
enabled: true
36+
templateName: cert-manager
37+
params:
38+
path: ./infra/cert-manager/components/install
39+
sourceRefName: flux-infra
40+
# MUSS gesetzt werden. Das KCL-Modul (claim-flux-kustomizations:0.3.33)
41+
# traegt v1.19.1 als eigenen Default und schreibt ihn als expliziten
42+
# substitute -- der schlaegt den Base-Default v1.21.1. Ohne diese Zeile
43+
# dreht ein Apply cert-manager zwei Minor-Versionen zurueck.
44+
certManagerVersion: v1.21.1
45+
46+
cert-manager-selfsigned:
47+
enabled: true
48+
templateName: infrastructure
49+
params:
50+
path: ./infra/cert-manager/components/selfsigned
51+
sourceRefName: flux-infra
52+
substitute: CERT_MANAGER_NAMESPACE=cert-manager,CERT_MANAGER_CA_NAME=cluster-ca,CERT_MANAGER_CA_SECRET=cluster-ca-secret,CERT_MANAGER_SELFSIGNED_DOMAIN=test-infra1.4sthings.tiab.ssc.sva.de,CERT_MANAGER_SELFSIGNED_CERT_NAME=wildcard-test-infra1-tls,CERT_MANAGER_SELFSIGNED_SECRET_NAME=wildcard-test-infra1-tls,CERT_MANAGER_SELFSIGNED_CERT_NAMESPACE=default,CERT_MANAGER_SELFSIGNED_ISSUER=vault-pki-4sthings
53+
54+
trust-manager:
55+
enabled: true
56+
templateName: infrastructure
57+
params:
58+
path: ./infra/trust-manager
59+
sourceRefName: flux-infra
60+
substitute: TRUST_MANAGER_NAMESPACE=cert-manager,TRUST_BUNDLE_VAULT_CA_SECRET=vault-pki-ca,TRUST_BUNDLE_VAULT_CA_KEY=ca.crt
61+
62+
openebs:
63+
enabled: true
64+
templateName: openebs
65+
params:
66+
path: ./infra/openebs
67+
sourceRefName: flux-infra
68+
openebsVersion: '4.5.1'
69+
70+
flux-web:
71+
enabled: true
72+
templateName: infrastructure
73+
params:
74+
path: ./apps/flux-web
75+
sourceRefName: flux-infra
76+
substitute: FLUX_WEB_NAMESPACE=flux-system,FLUX_WEB_VERSION=0.58.1,GATEWAY_NAME=cilium-gateway,GATEWAY_NAMESPACE=default,HOSTNAME=flux,DOMAIN=test-infra1.4sthings.tiab.ssc.sva.de
77+
78+
headlamp:
79+
enabled: true
80+
templateName: infrastructure
81+
params:
82+
path: ./apps/headlamp
83+
sourceRefName: flux-infra
84+
substitute: HEADLAMP_NAMESPACE=headlamp,HEADLAMP_VERSION=0.44.0,GATEWAY_NAME=cilium-gateway,GATEWAY_NAMESPACE=default,HOSTNAME=headlamp,DOMAIN=test-infra1.4sthings.tiab.ssc.sva.de

flux/infra.go

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"sort"
7+
"strconv"
78
"strings"
89
"time"
910

@@ -322,19 +323,30 @@ func renderKustomization(
322323
}
323324
sort.Strings(keys)
324325

325-
pairs := make([]string, 0, len(keys))
326+
// Parameters travel as a FILE, not as the comma-separated --parameters
327+
// string. That string cannot carry `substitute` at all: its value is itself
328+
// a comma-separated list, so joining it with the other parameters destroys
329+
// the boundary before the KCL module ever sees it, and the module's own
330+
// split then keeps only the first pair.
331+
//
332+
// It failed silently, which is the worst part -- the render succeeded and
333+
// produced a valid-looking Kustomization. Measured on test-infra1
334+
// 2026-08-24: eight substitute variables went in, `CERT_MANAGER_NAMESPACE`
335+
// came out, and the wildcard certificate would have been issued for the
336+
// sentinel `set-INFRA_DOMAIN.invalid`.
337+
//
338+
// A YAML document has no such ambiguity, and `--parameters-file` is the
339+
// channel the KCL module offers for exactly this.
340+
var b strings.Builder
341+
b.WriteString("---\n")
326342
for _, k := range keys {
327-
v := params[k]
328-
// The KCL module takes parameters as one comma-separated string, so a
329-
// value containing a comma would silently split into two parameters.
330-
// The substitute parameter is the documented exception -- it is itself
331-
// a comma-separated list and the module parses it that way.
332-
if strings.Contains(v, ",") && k != "substitute" {
333-
return "", fmt.Errorf("parameter %q contains a comma, which the KCL parameter string cannot express: %q", k, v)
334-
}
335-
pairs = append(pairs, k+"="+v)
343+
b.WriteString(k + ": " + strconv.Quote(params[k]) + "\n")
336344
}
337345

346+
paramsFile := dag.Directory().
347+
WithNewFile("parameters.yaml", b.String()).
348+
File("parameters.yaml")
349+
338350
// FormatOutput is left at its default. It used to corrupt this module's
339351
// output -- the post-processor assumed an `items:` list and its guard only
340352
// looked for a leading `---`, so a single document starting with
@@ -344,9 +356,9 @@ func renderKustomization(
344356
// anyway: the generated client drops optional arguments that equal the
345357
// zero value, so `FormatOutput: false` is never transmitted.
346358
return dag.Kcl().Run(dagger.KclRunOpts{
347-
OciSource: ociSource,
348-
Parameters: strings.Join(pairs, ","),
349-
Entrypoint: entrypoint,
359+
OciSource: ociSource,
360+
ParametersFile: paramsFile,
361+
Entrypoint: entrypoint,
350362
}).Contents(ctx)
351363
}
352364

0 commit comments

Comments
 (0)