Skip to content

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

Merged
matheuscscp merged 1 commit into
fluxcd:mainfrom
gecube:feat/valuesfrom-literal
Jun 4, 2026
Merged

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

Conversation

@gecube
Copy link
Copy Markdown
Contributor

@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.

@gecube gecube requested a review from a team as a code owner May 25, 2026 09:41
@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>
@stefanprodan
Copy link
Copy Markdown
Member

Does this solves fluxcd/flux2#2625?

@gecube
Copy link
Copy Markdown
Contributor Author

gecube commented Jun 3, 2026

@stefanprodan Hi! I think that yes, I did not find that issue before, but came from the other end - I got a very weird error when trying to put the configmap in yaml format as a key with valuesFrom. So now it looks like that in fact it is the same thing

@stefanprodan
Copy link
Copy Markdown
Member

Please add a test that replicates fluxcd/flux2#2625 and let's see if it passes

@gecube
Copy link
Copy Markdown
Contributor Author

gecube commented Jun 3, 2026

Ok, thanks, I will do it tomorrow and return asap

gecube added a commit to gecube/pkg that referenced this pull request Jun 4, 2026
Replicates the exact scenario from fluxcd/flux2#2625: a Secret named
`kafka` with a `brokers` key whose value is a comma-separated list of
`host:port` endpoints (`kafka01.net:9092,kafka02.net:9092,...`),
referenced via `valuesFrom` with `targetPath: kafka.brokers`.

Without Literal mode the strvals parser splits on the commas and tries
to interpret each `host:port` segment as a key/value pair, producing the
exact error the issue reports:

  key "net:9092" has no value (cannot end with ,)

Two integration cases in TestChartValuesFromReferences pin the bug and
its fix:
- non-literal path fails (`wantErr: true`) — documents the original
  defect.
- `Literal: true` succeeds and preserves the broker list verbatim at
  `kafka.brokers` — confirms the new mode resolves it.

A unit case in TestReplacePathLiteralValue exercises the same input
through the lower-level helper.

Requested by stefanprodan in fluxcd#1218 review.

Signed-off-by: George Gaál <gb12335@gmail.com>
@gecube
Copy link
Copy Markdown
Contributor Author

gecube commented Jun 4, 2026

@stefanprodan please check

@matheuscscp
Copy link
Copy Markdown
Member

@gecube Please rebase and force-push 🙏

@gecube gecube force-pushed the feat/valuesfrom-literal branch from 4f6d31e to 48faddc Compare June 4, 2026 07:36
@gecube
Copy link
Copy Markdown
Contributor Author

gecube commented Jun 4, 2026

@matheuscscp done, thanks

@matheuscscp
Copy link
Copy Markdown
Member

@gecube Strange, I still see the Update branch button... Maybe you just squashed? What I usually do is the following:

git pull --rebase origin main

Assuming origin is the github.com/fluxcd/pkg remote (not your fork)

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 48faddc to bf5caf5 Compare June 4, 2026 07:51
Copy link
Copy Markdown
Member

@matheuscscp matheuscscp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@matheuscscp matheuscscp merged commit 2944b86 into fluxcd:main Jun 4, 2026
14 checks passed
@gecube gecube deleted the feat/valuesfrom-literal branch June 4, 2026 13:00
gecube added a commit to gecube/helm-controller that referenced this pull request Jun 4, 2026
The OpenAPI schema for the new `literal` field on `valuesFrom` entries
landed via the CRD regeneration in fluxcd#1506 (k8s 1.36 / Go 1.26 bump,
which picked up the corresponding field from the regenerated meta
package). This commit ships only the matching user-facing documentation
in docs/spec/v2/helmreleases.md.

The runtime support lives in fluxcd/pkg/chartutil (fluxcd/pkg#1218);
once that merges and a release is cut, a follow-up here will bump the
dependency so helm-controller actually honours the field. Until then,
setting `literal: true` is accepted by the API but has no effect.

Addresses fluxcd#1317. Closes fluxcd/flux2#2625.

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

3 participants