Feat: reuseValues in release

Adding properties to set reuseValues flag on release-level.

Signed-off-by: Adam Blasko <adam.blasko1@gmail.com>
This commit is contained in:
Adam Blasko 2025-04-15 11:50:46 +02:00 committed by yxxhero
parent 769d56d208
commit d5f4dc5d11
3 changed files with 208 additions and 6 deletions

View File

@ -315,7 +315,7 @@ releases:
# will attempt to decrypt it using helm-secrets plugin
secrets:
- vault_secret.yaml
# Override helmDefaults options for verify, wait, waitForJobs, timeout, recreatePods and force.
# Override helmDefaults options for verify, wait, waitForJobs, timeout, recreatePods, force and reuseValues.
verify: true
keyring: path/to/keyring.gpg
# --skip-schema-validation flag to helm 'install', 'upgrade' and 'lint', starts with helm 3.16.0 (default false)
@ -326,6 +326,7 @@ releases:
timeout: 60
recreatePods: true
force: false
reuseValues: false
# set `false` to uninstall this release on sync. (default true)
installed: true
# restores previous state in case of failed release (default false)

View File

@ -283,6 +283,8 @@ type ReleaseSpec struct {
Condition string `yaml:"condition,omitempty"`
// CreateNamespace, when set to true (default), --create-namespace is passed to helm3 on install (ignored for helm2)
CreateNamespace *bool `yaml:"createNamespace,omitempty"`
// ReuseValues, on helm upgrade/diff, reuse values currently set in the release and merge them with the ones defined within helmfile
ReuseValues *bool `yaml:"reuseValues,omitempty"`
// DisableOpenAPIValidation is rarely used to bypass OpenAPI validations only that is used for e.g.
// work-around against broken CRs
@ -706,7 +708,7 @@ func (st *HelmState) prepareSyncReleases(helm helmexec.Interface, additionalValu
flags = append(flags, "--skip-crds")
}
flags = st.appendValuesControlModeFlag(flags, opts.ReuseValues, opts.ResetValues)
flags = st.appendValuesControlModeFlag(flags, opts.ReuseValues, opts.ResetValues, release)
if len(errs) > 0 {
results <- syncPrepareResult{errors: errs, files: files}
@ -1845,8 +1847,6 @@ func (st *HelmState) commonDiffFlags(detailedExitCode bool, stripTrailingCR bool
flags = append(flags, "--output", opt.Output)
}
flags = st.appendValuesControlModeFlag(flags, opt.ReuseValues, opt.ResetValues)
if opt.Set != nil {
for _, s := range opt.Set {
flags = append(flags, "--set", s)
@ -1960,6 +1960,8 @@ func (st *HelmState) prepareDiffReleases(helm helmexec.Interface, additionalValu
flags = append(flags, "--values", valfile)
}
flags = st.appendValuesControlModeFlag(flags, opt.ReuseValues, opt.ResetValues, release)
flags = append(flags, commonDiffFlags...)
if len(errs) > 0 {
@ -3019,8 +3021,8 @@ func (st *HelmState) chartOCIFlags(r *ReleaseSpec) []string {
return flags
}
func (st *HelmState) appendValuesControlModeFlag(flags []string, reuseValues bool, resetValues bool) []string {
if !resetValues && (st.HelmDefaults.ReuseValues || reuseValues) {
func (st *HelmState) appendValuesControlModeFlag(flags []string, reuseValues bool, resetValues bool, release *ReleaseSpec) []string {
if !resetValues && (release.ReuseValues != nil && *release.ReuseValues || release.ReuseValues == nil && st.HelmDefaults.ReuseValues || reuseValues) {
flags = append(flags, "--reuse-values")
} else {
flags = append(flags, "--reset-values")

View File

@ -4375,3 +4375,202 @@ func TestHelmState_setStringFlags(t *testing.T) {
})
}
}
func TestPrepareDiffReleases_ValueControlReleaseOverride(t *testing.T) {
tests := []struct {
flags []string
diffOptions *DiffOpts
helmDefaults *HelmSpec
release *ReleaseSpec
}{
{
flags: []string{"--reuse-values"},
diffOptions: &DiffOpts{},
helmDefaults: &HelmSpec{},
release: &ReleaseSpec{
Name: "reuse-values-from-release",
ReuseValues: boolValue(true),
},
},
{
flags: []string{"--reuse-values"},
diffOptions: &DiffOpts{
ReuseValues: true,
},
helmDefaults: &HelmSpec{},
release: &ReleaseSpec{
Name: "reuse-values-from-cli",
ReuseValues: boolValue(false),
},
},
{
flags: []string{"--reuse-values"},
diffOptions: &DiffOpts{
ReuseValues: true,
},
helmDefaults: &HelmSpec{
ReuseValues: true,
},
release: &ReleaseSpec{
Name: "reuse-values-all",
ReuseValues: boolValue(true),
},
},
{
flags: []string{"--reset-values"},
diffOptions: &DiffOpts{},
helmDefaults: &HelmSpec{
ReuseValues: true,
},
release: &ReleaseSpec{
Name: "reset-values-from-helm-defaults",
ReuseValues: boolValue(false),
},
},
{
flags: []string{"--reset-values"},
diffOptions: &DiffOpts{},
helmDefaults: &HelmSpec{},
release: &ReleaseSpec{
Name: "reset-values-from-release",
ReuseValues: boolValue(false),
},
},
{
flags: []string{"--reset-values"},
diffOptions: &DiffOpts{
ResetValues: true,
},
helmDefaults: &HelmSpec{},
release: &ReleaseSpec{
Name: "reset-values-cli-overrides-release",
ReuseValues: boolValue(true),
},
},
}
for _, tt := range tests {
releases := []ReleaseSpec{
*tt.release,
}
state := &HelmState{
ReleaseSetSpec: ReleaseSetSpec{
Releases: releases,
HelmDefaults: *tt.helmDefaults,
},
logger: logger,
valsRuntime: valsRuntime,
}
helm := &exectest.Helm{
Lists: map[exectest.ListKey]string{},
Helm3: true,
}
results, es := state.prepareDiffReleases(helm, []string{}, 1, false, false, false, []string{}, false, false, false, tt.diffOptions)
require.Len(t, es, 0)
require.Len(t, results, 1)
r := results[0]
require.Equal(t, tt.flags, r.flags, "Wrong value control flag for release %s", r.release.Name)
}
}
func TestPrepareSyncReleases_ValueControlReleaseOverride(t *testing.T) {
tests := []struct {
flags []string
syncOptions *SyncOpts
helmDefaults *HelmSpec
release *ReleaseSpec
}{
{
flags: []string{"--reuse-values"},
syncOptions: &SyncOpts{},
helmDefaults: &HelmSpec{},
release: &ReleaseSpec{
Name: "reuse-values-from-release",
ReuseValues: boolValue(true),
},
},
{
flags: []string{"--reuse-values"},
syncOptions: &SyncOpts{
ReuseValues: true,
},
helmDefaults: &HelmSpec{},
release: &ReleaseSpec{
Name: "reuse-values-from-cli",
ReuseValues: boolValue(false),
},
},
{
flags: []string{"--reuse-values"},
syncOptions: &SyncOpts{
ReuseValues: true,
},
helmDefaults: &HelmSpec{
ReuseValues: true,
},
release: &ReleaseSpec{
Name: "reuse-values-all",
ReuseValues: boolValue(true),
},
},
{
flags: []string{"--reset-values"},
syncOptions: &SyncOpts{},
helmDefaults: &HelmSpec{
ReuseValues: true,
},
release: &ReleaseSpec{
Name: "reset-values-from-helm-defaults",
ReuseValues: boolValue(false),
},
},
{
flags: []string{"--reset-values"},
syncOptions: &SyncOpts{},
helmDefaults: &HelmSpec{},
release: &ReleaseSpec{
Name: "reset-values-from-release",
ReuseValues: boolValue(false),
},
},
{
flags: []string{"--reset-values"},
syncOptions: &SyncOpts{
ResetValues: true,
},
helmDefaults: &HelmSpec{},
release: &ReleaseSpec{
Name: "reset-values-cli-overrides-release",
ReuseValues: boolValue(true),
},
},
}
for _, tt := range tests {
releases := []ReleaseSpec{
*tt.release,
}
state := &HelmState{
ReleaseSetSpec: ReleaseSetSpec{
Releases: releases,
HelmDefaults: *tt.helmDefaults,
},
logger: logger,
valsRuntime: valsRuntime,
}
helm := &exectest.Helm{
Lists: map[exectest.ListKey]string{},
Helm3: true,
}
results, es := state.prepareSyncReleases(helm, []string{}, 1, tt.syncOptions)
require.Len(t, es, 0)
require.Len(t, results, 1)
r := results[0]
require.Equal(t, tt.flags, r.flags, "Wrong value control flag for release %s", r.release.Name)
}
}