feat(fieldmap): tier-3 HelmRelease values field maps - #34
Merged
Conversation
…pers Pull the match-rule gating and name recovery out of TranslateTarget into matchWorkload and recoverName, both keyed on a MatchRule + cached regexp rather than a *CRMap. Tier-3 HelmRelease translation will share the same two helpers, so the gating (workloadKind, containerToComponent) and the NamePattern/NameSuffix recovery stay in one place across tiers. Pure refactor: no behavior change. Existing tier-2 translate tests and the tier-1/tier-2 golden suites prove the output is byte-identical. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add ChartConfig/ChartMap/ChartComponent — the tier-3 registry, keyed by chart name (spec.chart.spec.chart) rather than tier-2's (group, kind), because every Flux HelmRelease shares one apiVersion+kind and the chart is the real discriminator. Resources live at an absolute path under spec.values (spec.values.resources, or nested like spec.values.controller.resources). Multi-workload charts use Components, each carrying its OWN Match: unlike a tier-2 Component, each is a separate generated workload with a distinct name, so its rule recovers the shared release name and identifies the component. Validate/findChartMap/MergedChartMaps mirror the tier-2 equivalents (user maps win, built-ins fill gaps, NamePatterns precompiled). Built-in catalog (keycloakx single-workload, ingress-nginx multi-workload) ships best-effort match rules marked // TODO verify (#33): Helm's generated names depend on release name and fullname overrides, so a wrong rule degrades safely to a tier-1 fallback or a not-found revert. Extract validateNamePattern from MapConfig.Validate so both the tier-2 and tier-3 config validators share one MatchRule pattern check. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tier3 implements FieldMapper for Flux HelmReleases: Supports matches a HelmRelease whose spec.chart.spec.chart has a chart map; ResolvePath keys on that chart, selects the component (single ResourcePath or a named Component), verifies the resources block's parent exists under spec.values, and returns the absolute cell path. chartRef-only releases (no inline chart name) and unmapped charts cleanly report unsupported. Add byte-exact golden fixtures proving the path-addressed editor edits nested values surgically: helmrelease_ingress (multi-component controller + defaultBackend, CPU-limit delete with cascade) and helmrelease_create (create a resources block under an existing controller). Comments and unrelated values keys survive. No editor code change — absolute spec.values paths flow through ApplyPaths exactly like tier-2. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add TranslateHelmTarget: it rewrites a Helm-generated workload (e.g. the Deployment "ingress-nginx-controller") to the owning Flux HelmRelease identity, reusing matchWorkload/recoverName. Single-workload charts match on the top-level rule; multi-workload charts try each component's own rule, so the controller and defaultBackend of one release recover the same name and their own component. Wire it through the run loop: Runner gains ChartMaps; translateTargets now tries tier-2 CR translation then tier-3 Helm translation, each committed only when the owning resource exists in the repo (crResolvable), so a coincidental name match still falls back to tier-1. Register the Tier3 mapper (Tier2, Tier3, Tier1) and add a helmReleaseMaps config section (validated alongside fieldMaps). Integration tests prove the full chain end-to-end: a generated controller Deployment right-sizes the HelmRelease's spec.values, and a controller + defaultBackend pair collapses into a single PR editing one file. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Update the tier-3 line to reflect the shipped config-driven per-chart maps (helmReleaseMaps) with keycloakx/ingress-nginx built-ins and the inline spec.values-only scope. Add a compact chart-map config example, and broaden the operator-CR translation note into a workload-to-source note that also covers HelmReleases (chart-generated Deployment -> HelmRelease, per-component match rules, chart-keyed values subtree). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #6.
Tier-3 completes the three-tier headline: right-size Flux
HelmReleasevalues:, on the same machinery as tiers 1–2 (theFieldMapperinterface, the path-addressedApplyPathseditor, the translate-before-group pattern, and the golden harness).The problem
The recommender reports the Helm-generated workload (e.g. the Deployment
ingress-nginx-controller), but the repo holds theHelmRelease. A chart keeps its resources at a chart-specific path underspec.values(spec.values.resources, or nested likespec.values.controller.resources). EveryHelmReleaseshares one(group, kind), so the tier-2(group,kind)key doesn't fit — the discriminator is the chart name (spec.chart.spec.chart).What's here
helmReleaseMaps):ChartConfig/ChartMap/ChartComponent, mirroring the tier-2 registry but keyed by chart. Multi-workload charts usecomponents, each carrying its own match rule (each component is a separate generated workload with a distinct name).Tier3mapper:Supports/ResolvePath/Resolve— keys on the manifest's chart, resolves thespec.values…resourcescell, verifies the parent exists (never fabricates nesting), reusesresolveWant/sortEdits.TranslateHelmTarget: rewrites the generated workload →{HelmRelease, release, component}so discovery finds the release and a chart's workloads collapse onto one PR. SharesmatchWorkload/recoverNamewith tier-2 (extracted in the first commit).// TODO verify, folded into chore: capture real krr -f json samples; verify tier-2 built-in match rules #33):keycloakx(single) andingress-nginx(multi-componentcontroller/defaultBackend). A wrong match rule degrades safely — the workload falls back to tier-1, or the rewritten release isn't found and reverts.Tier2, Tier3, Tier1;helmReleaseMapsvalidated alongsidefieldMaps.Scope / non-goals (v0.1)
spec.valuesonly;spec.valuesFromandchartRef-only releases are out of scope (clean skip).krr -f jsonsamples (chore: capture real krr -f json samples; verify tier-2 built-in match rules #33).Verification
go test ./...green (145 tests); tier-1/tier-2 golden suites unchanged (the shared-helper refactor is byte-stable).spec.values…diffs end-to-end (adapter → translate → discover → fieldmap → policy → yamledit), no cluster/creds.docs-framework validate docs/passes.Commits (one concern each)
refactor(fieldmap): extract sharedmatchWorkload/recoverNamefeat(fieldmap): chart-keyed config + built-in catalogfeat(fieldmap): tier-3 mapper + golden fixturesfeat(fieldmap): HelmRelease translation + run wiringdocs(explanation): tier-3 architecture + config example🤖 Generated with Claude Code