Skip to content

chartutil: add Literal mode to ValuesReference (helm --set-literal semantics)#1218

Open
gecube wants to merge 1 commit into
fluxcd:mainfrom
gecube:feat/valuesfrom-literal
Open

chartutil: add Literal mode to ValuesReference (helm --set-literal semantics)#1218
gecube wants to merge 1 commit into
fluxcd:mainfrom
gecube:feat/valuesfrom-literal

Conversation

@gecube
Copy link
Copy Markdown

@gecube gecube commented May 25, 2026

Problem

When a HelmRelease consumes a ConfigMap or Secret via valuesFrom with a targetPath set, the referenced value is currently passed to Helm's strvals.ParseInto — the same parser as helm --set. That treats ,, [, ], {, }, = and \ in the value as syntactic metacharacters, so arbitrary file content (Spring application.yml with flow sequences, HOCON application.conf, JSON blobs, multi-line YAML with trailing commas, …) is misparsed or fails outright with errors like:

error parsing index: strconv.Atoi: parsing " \"prometheus\", \"health\", \"info\" ": invalid syntax
key "efm" has no value (cannot end with ,)

The single/double-quote workaround introduced in helm-controller#298 ('<value>'ParseIntoString) is impractical for delivering raw config files via kustomize configMapGenerator / secretGenerator: the generated CM data is the file content as-is, with no opportunity to wrap it without sidecar files or build steps. Flux's kustomize-controller explicitly disables Kustomize plugins (PluginConfig: kustypes.DisabledPluginConfig()), so a transformer-based workaround isn't available either.

Solution

Adds a Literal bool field to meta.ValuesReference. When Literal: true together with TargetPath, ChartValuesFromReferences calls a new ReplacePathLiteralValue helper instead of ReplacePathValue. The helper pre-escapes strvals metacharacters in the value, then delegates to strvals.ParseIntoString — that combination yields:

  • Value preserved verbatim (no ,/[/{/= interpretation; no type coercion to int/bool/list/map).
  • Path escapes still honoured (externalConfig.application\.yml.content resolves to the literal key application.yml, which the alternative strvals.ParseLiteralInto does not).

Literal has no effect when TargetPath is empty (the root YAML-merge path is unchanged). Default Literal: false is fully backward compatible.

Why not strvals.ParseLiteralInto?

It is the obvious choice and was the first thing I tried. It correctly preserves value metacharacters, but does not support \. escapes in the path:

externalConfig.application\.yml.content=hello
  → ParseIntoString:   externalConfig.application.yml.content = "hello"   ✓
  → ParseLiteralInto:  externalConfig["application\"]["yml"]["content"] = "hello"   ✗

Charts that use filenames as map keys (a common Helm pattern for per-file config) need the \. escape, so I went with pre-escaping the value and routing through ParseIntoString. Documented in the function doc comment.

API

spec:
  valuesFrom:
    - kind: ConfigMap
      name: my-service-config
      valuesKey: application.yml
      targetPath: 'externalConfig.application\.yml.content'
      literal: true   # NEW

Tests

chartutil/values_test.go gains:

  • TestChartValuesFromReferences — new cases:
    • non-literal target path mangles value with commas — pinning the current bug as a regression test.
    • literal target path preserves helm-set metacharacters — Spring-style YAML with flow sequence.
    • literal target path preserves multi-line HOCON with equals and braces — HOCON content read from a Secret.
    • literal flag without targetPath is ignored (root YAML merge) — documents the no-effect branch.
  • TestReplacePathLiteralValue — 10 unit cases covering commas, braces, brackets, equals, multi-line YAML, HOCON, JSON, boolean-shaped strings, and the \. path escape.

All existing tests continue to pass.

Backward compatibility

  • New optional field with omitempty; defaults to false.
  • DeepCopy is a no-op (*out = *in) — ValuesReference is a plain value type, no regeneration needed.
  • Existing references behave identically (call site path picks ReplacePathValue when Literal: false).

Follow-up

This PR ships the API + behaviour in fluxcd/pkg. A follow-up PR in fluxcd/helm-controller will:

  1. Bump the fluxcd/pkg dependency to a release containing this change.
  2. Regenerate config/crd/bases/helm.toolkit.fluxcd.io_helmreleases.yaml so the field is openAPI-validated by the API server.
  3. Add a valuesFrom literal section to docs/spec/v2/helmreleases.md.

Related

  • Resolves fluxcd/helm-controller#1317 (request for --set-literal semantics in valuesFrom + targetPath)
  • Addresses parts of #460 (YAML / raw value in valuesFrom)
  • Practical examples discussed in #853, flux2#1756, where users hit the manual-escape wall.
  • Helm landed --set-literal upstream in helm#9182 (v3.12); this brings the equivalent to Flux.

When a HelmRelease consumer references a ConfigMap or Secret via valuesFrom
with a targetPath set, the referenced value is currently fed to Helm's
strvals.ParseInto parser — the same parser as `helm --set`. That treats
commas, square brackets, braces and equal signs in the value as syntactic
metacharacters, so arbitrary file content (Spring application.yml with
flow sequences, HOCON application.conf, JSON, multi-line strings with
trailing commas, …) is misparsed or causes hard errors like
`error parsing index: strconv.Atoi: parsing " \"foo\", \"bar\" "`.

The existing single/double-quote workaround (introduced in helm-controller
fluxcd#298) is impractical for delivering raw config files from kustomize
configMapGenerator / secretGenerator: the generated CM data is the file
content as-is, with no opportunity to wrap it.

This commit adds a new boolean field `Literal` to meta.ValuesReference.
When set together with TargetPath, ChartValuesFromReferences calls a new
ReplacePathLiteralValue helper that pre-escapes strvals metacharacters in
the value before delegating to strvals.ParseIntoString. The result is a
verbatim value preserved at the target path, while the path itself still
honours `\.` escapes (which the alternative helm strvals.ParseLiteralInto
does not).

Has no effect when TargetPath is empty (root YAML merge is unchanged).
Default Literal=false preserves backward compatibility with all existing
HelmReleases.

Resolves fluxcd/helm-controller#1317, addresses parts of fluxcd#460, fluxcd#853,
flux2#1756. The CRD schema bump and consumer wiring in helm-controller
is a separate follow-up PR.

Signed-off-by: George Gaál <gb12335@gmail.com>
@gecube gecube force-pushed the feat/valuesfrom-literal branch from b4592f9 to 53e9227 Compare May 25, 2026 10:23
gecube added a commit to gecube/helm-controller that referenced this pull request May 25, 2026
Exposes the new `literal` field on `valuesFrom` entries, mirroring the
addition to `meta.ValuesReference` in fluxcd/pkg#1218. When set together
with `targetPath`, the referenced value is passed to Helm verbatim
(equivalent of `helm --set-literal`) instead of being parsed by
`strvals.ParseInto`, so file content containing commas, brackets, braces
or equal signs survives the round-trip intact.

This commit ships only the OpenAPI schema (so the field is validated by
the API server) and the user-facing documentation. The runtime support
lives in fluxcd/pkg/chartutil; once that PR merges and a `fluxcd/pkg`
release is cut, a follow-up will bump the dependency here so
helm-controller actually honours the field. Until then, setting
`literal: true` is accepted by the API but has no effect — the field is
forward-compatible.

Addresses fluxcd#1317.

Signed-off-by: George Gaál <gb12335@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: use helm --set-literal in valuesFrom with targetPath

1 participant