mirror of
https://github.com/helmfile/helmfile.git
synced 2026-09-30 10:51:41 +02:00
* feat: add `inherits:` for sub-helmfile config inheritance
Add an opt-in `inherits:` field to `helmfiles:` entries so a sub-helmfile
can inherit specific configuration categories from its parent:
helmfiles:
- path: myapp.yaml
inherits: [repositories, environments]
Allowed values: repositories, helmDefaults, commonLabels, apiVersions,
kubeVersion, templates, environments. Child values win; parent fills gaps
(consistent with `bases:`). This directly fixes #1495, where a repository
declared in the parent was unavailable to sub-helmfiles, producing a
confusing "repo not found" error.
Implementation notes:
- The 6 pure fields (repositories, helmDefaults, commonLabels, apiVersions,
kubeVersion, templates) are merged post-load via MergeInherited; verified
all are consumed post-load (ExecuteTemplates/converge), never at parse.
- environments is injected pre-load as ctxEnv, because RenderedValues is
baked at load time; the parent's resolved values become the base and the
child's own environments: block overrides per key.
- helmDefaults uses a *HelmSpec pointer (value type is non-comparable) with
a no-override mergo merge, so a child that omits helmDefaults inherits the
parent's fully.
- A footgun warning (WarnUninheritedRepos) suggests
`inherits: [repositories]` when a release references a repo the parent
declares but the child lacks.
- Unknown inherits keys are rejected at parse time with the allowed set.
Inheritance is opt-in and fully backward compatible: empty (the default)
preserves the historical independent-sub-helmfile behavior.
Fixes #1495
Signed-off-by: yxxhero <aiopsclub@163.com>
* fix: address review — deep-copy inherited config and fix bases: doc link
- BuildInheritedConfig now deep-copies the pure fields via a YAML round-trip
(Env via environment.DeepCopy) so the returned config never aliases the
parent state's slices/maps, matching its doc comment. Now returns an error
to surface round-trip failures; the call site in processNestedHelmfiles is
updated. Added TestBuildInheritedConfig_PureFieldsAreDeepCopied to lock
in the no-aliasing guarantee.
- Fix the broken `bases:` anchor (#) in shared-configuration-across-teams.md
to point to writing-helmfile.md#layering-state-files.
Signed-off-by: yxxhero <aiopsclub@163.com>
* fix: address review — reject inherits without path and document helmDefaults caveat
Signed-off-by: yxxhero <aiopsclub@163.com>
* fix: address review — make AllowedInherits immutable and clarify effective-repo wording
Signed-off-by: yxxhero <aiopsclub@163.com>
---------
Signed-off-by: yxxhero <aiopsclub@163.com>
69 lines
1.7 KiB
Go
69 lines
1.7 KiB
Go
package app
|
|
|
|
import (
|
|
"github.com/helmfile/helmfile/pkg/state"
|
|
"github.com/helmfile/helmfile/pkg/yaml"
|
|
)
|
|
|
|
type LoadOpts struct {
|
|
Selectors []string
|
|
Environment state.SubhelmfileEnvironmentSpec
|
|
|
|
RetainValuesFiles bool
|
|
|
|
// CalleePath is the absolute path to the file being loaded
|
|
CalleePath string
|
|
|
|
Reverse bool
|
|
|
|
Filter bool
|
|
|
|
// Inherited carries parent-helmfile config that this sub-helmfile opts into
|
|
// via `inherits:`. See state.InheritedConfig and state.MergeInherited.
|
|
Inherited *state.InheritedConfig
|
|
|
|
// ParentRepoNames is the parent's effective repository set (derived from
|
|
// st.Repositories, so it includes repositories brought in via bases: or
|
|
// inherits:). It is used by the "did you mean inherits: [repositories]?"
|
|
// footgun warning (state.WarnUninheritedRepos).
|
|
ParentRepoNames []string `yaml:"parentRepoNames,omitempty"`
|
|
}
|
|
|
|
func (o LoadOpts) DeepCopy() LoadOpts {
|
|
bytes, err := yaml.Marshal(o)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
new := LoadOpts{}
|
|
if err := yaml.Unmarshal(bytes, &new); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if src := o.Environment.OverrideCLISetValues; src != nil {
|
|
b, err := yaml.Marshal(src)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
var dst []any
|
|
if err := yaml.Unmarshal(b, &dst); err != nil {
|
|
panic(err)
|
|
}
|
|
new.Environment.OverrideCLISetValues = dst
|
|
}
|
|
|
|
// InheritedConfig.Env is tagged yaml:"-" so it does not survive the
|
|
// marshal/unmarshal round-trip above. Deep-copy it explicitly (mirroring
|
|
// the OverrideCLISetValues handling) so sub-helmfile processing never
|
|
// shares the parent's environment maps by reference.
|
|
if o.Inherited != nil && o.Inherited.Env != nil {
|
|
e := o.Inherited.Env.DeepCopy()
|
|
if new.Inherited == nil {
|
|
new.Inherited = &state.InheritedConfig{}
|
|
}
|
|
new.Inherited.Env = &e
|
|
}
|
|
|
|
return new
|
|
}
|