mirror of
https://github.com/helmfile/helmfile.git
synced 2026-10-21 11:33:39 +02:00
feat: add --force-conflicts flag support for Helm 4 (#2480)
* feat: add --force-conflicts flag support for Helm 4 Add support for Helm 4's --force-conflicts flag which forces server-side apply changes against conflicts. This flag is mutually exclusive with --force/--force-replace and only available in Helm 4. Fixes #2429 Signed-off-by: yxxhero <aiopsclub@163.com> * fix: address review comments on force-conflicts feature - Fix comment grammar: 'forces' instead of 'force' - Improve error messages to indicate both sources (releases[] and helmDefaults) - Add test case for helmDefaults.forceConflicts with Helm 3 (should error) - Update TestGenerateID expected hashes after adding ForceConflicts field to structs Signed-off-by: yxxhero <aiopsclub@163.com> --------- Signed-off-by: yxxhero <aiopsclub@163.com>
This commit is contained in:
+20
-1
@@ -190,6 +190,8 @@ type HelmSpec struct {
|
|||||||
Atomic bool `yaml:"atomic"`
|
Atomic bool `yaml:"atomic"`
|
||||||
// CleanupOnFail, when set to true, the --cleanup-on-fail helm flag is passed to the upgrade command
|
// CleanupOnFail, when set to true, the --cleanup-on-fail helm flag is passed to the upgrade command
|
||||||
CleanupOnFail bool `yaml:"cleanupOnFail,omitempty"`
|
CleanupOnFail bool `yaml:"cleanupOnFail,omitempty"`
|
||||||
|
// ForceConflicts, when set to true, force server-side apply changes against conflicts (Helm 4 only)
|
||||||
|
ForceConflicts bool `yaml:"forceConflicts"`
|
||||||
// HistoryMax, limit the maximum number of revisions saved per release. Use 0 for no limit (default 10)
|
// HistoryMax, limit the maximum number of revisions saved per release. Use 0 for no limit (default 10)
|
||||||
HistoryMax *int `yaml:"historyMax,omitempty"`
|
HistoryMax *int `yaml:"historyMax,omitempty"`
|
||||||
// CreateNamespace, when set to true (default), --create-namespace is passed to helm on install/upgrade
|
// CreateNamespace, when set to true (default), --create-namespace is passed to helm on install/upgrade
|
||||||
@@ -306,6 +308,8 @@ type ReleaseSpec struct {
|
|||||||
Atomic *bool `yaml:"atomic,omitempty"`
|
Atomic *bool `yaml:"atomic,omitempty"`
|
||||||
// CleanupOnFail, when set to true, the --cleanup-on-fail helm flag is passed to the upgrade command
|
// CleanupOnFail, when set to true, the --cleanup-on-fail helm flag is passed to the upgrade command
|
||||||
CleanupOnFail *bool `yaml:"cleanupOnFail,omitempty"`
|
CleanupOnFail *bool `yaml:"cleanupOnFail,omitempty"`
|
||||||
|
// ForceConflicts, when set to true, force server-side apply changes against conflicts (Helm 4 only)
|
||||||
|
ForceConflicts *bool `yaml:"forceConflicts,omitempty"`
|
||||||
// HistoryMax, limit the maximum number of revisions saved per release. Use 0 for no limit (default 10)
|
// HistoryMax, limit the maximum number of revisions saved per release. Use 0 for no limit (default 10)
|
||||||
HistoryMax *int `yaml:"historyMax,omitempty"`
|
HistoryMax *int `yaml:"historyMax,omitempty"`
|
||||||
// Condition, when set, evaluate the mapping specified in this string to a boolean which decides whether or not to process the release
|
// Condition, when set, evaluate the mapping specified in this string to a boolean which decides whether or not to process the release
|
||||||
@@ -3451,7 +3455,18 @@ func (st *HelmState) flagsForUpgrade(helm helmexec.Interface, release *ReleaseSp
|
|||||||
|
|
||||||
flags = append(flags, st.timeoutFlags(release, opt)...)
|
flags = append(flags, st.timeoutFlags(release, opt)...)
|
||||||
|
|
||||||
if (release.Force != nil && *release.Force) || (release.Force == nil && st.HelmDefaults.Force) {
|
forceEnabled := (release.Force != nil && *release.Force) || (release.Force == nil && st.HelmDefaults.Force)
|
||||||
|
forceConflictsEnabled := (release.ForceConflicts != nil && *release.ForceConflicts) || (release.ForceConflicts == nil && st.HelmDefaults.ForceConflicts)
|
||||||
|
|
||||||
|
if forceConflictsEnabled && !helm.IsHelm4() {
|
||||||
|
return nil, nil, fmt.Errorf("forceConflicts requires Helm 4 or greater (set via releases[].forceConflicts or helmDefaults.forceConflicts)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if forceEnabled && forceConflictsEnabled {
|
||||||
|
return nil, nil, fmt.Errorf("force and forceConflicts are mutually exclusive (check both releases[].force/forceConflicts and helmDefaults.force/forceConflicts)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if forceEnabled {
|
||||||
if helm.IsHelm4() {
|
if helm.IsHelm4() {
|
||||||
flags = append(flags, "--force-replace")
|
flags = append(flags, "--force-replace")
|
||||||
} else {
|
} else {
|
||||||
@@ -3459,6 +3474,10 @@ func (st *HelmState) flagsForUpgrade(helm helmexec.Interface, release *ReleaseSp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if forceConflictsEnabled {
|
||||||
|
flags = append(flags, "--force-conflicts")
|
||||||
|
}
|
||||||
|
|
||||||
if release.RecreatePods != nil && *release.RecreatePods || release.RecreatePods == nil && st.HelmDefaults.RecreatePods {
|
if release.RecreatePods != nil && *release.RecreatePods || release.RecreatePods == nil && st.HelmDefaults.RecreatePods {
|
||||||
flags = append(flags, "--recreate-pods")
|
flags = append(flags, "--recreate-pods")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -376,6 +376,91 @@ func TestHelmState_flagsForUpgrade(t *testing.T) {
|
|||||||
"--namespace", "test-namespace",
|
"--namespace", "test-namespace",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "force-conflicts-helm4",
|
||||||
|
defaults: HelmSpec{
|
||||||
|
ForceConflicts: false,
|
||||||
|
CreateNamespace: &disable,
|
||||||
|
},
|
||||||
|
version: semver.MustParse("4.0.0"),
|
||||||
|
release: &ReleaseSpec{
|
||||||
|
Chart: "test/chart",
|
||||||
|
Version: "0.1",
|
||||||
|
ForceConflicts: &enable,
|
||||||
|
Name: "test-charts",
|
||||||
|
Namespace: "test-namespace",
|
||||||
|
},
|
||||||
|
want: []string{
|
||||||
|
"--version", "0.1",
|
||||||
|
"--force-conflicts",
|
||||||
|
"--namespace", "test-namespace",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "force-conflicts-from-default-helm4",
|
||||||
|
defaults: HelmSpec{
|
||||||
|
ForceConflicts: true,
|
||||||
|
CreateNamespace: &disable,
|
||||||
|
},
|
||||||
|
version: semver.MustParse("4.0.0"),
|
||||||
|
release: &ReleaseSpec{
|
||||||
|
Chart: "test/chart",
|
||||||
|
Version: "0.1",
|
||||||
|
Name: "test-charts",
|
||||||
|
Namespace: "test-namespace",
|
||||||
|
},
|
||||||
|
want: []string{
|
||||||
|
"--version", "0.1",
|
||||||
|
"--force-conflicts",
|
||||||
|
"--namespace", "test-namespace",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "force-conflicts-helm3-error",
|
||||||
|
defaults: HelmSpec{
|
||||||
|
CreateNamespace: &disable,
|
||||||
|
},
|
||||||
|
version: semver.MustParse("3.10.0"),
|
||||||
|
release: &ReleaseSpec{
|
||||||
|
Chart: "test/chart",
|
||||||
|
Version: "0.1",
|
||||||
|
ForceConflicts: &enable,
|
||||||
|
Name: "test-charts",
|
||||||
|
Namespace: "test-namespace",
|
||||||
|
},
|
||||||
|
wantErr: "forceConflicts requires Helm 4 or greater (set via releases[].forceConflicts or helmDefaults.forceConflicts)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "force-conflicts-from-default-helm3-error",
|
||||||
|
defaults: HelmSpec{
|
||||||
|
ForceConflicts: true,
|
||||||
|
CreateNamespace: &disable,
|
||||||
|
},
|
||||||
|
version: semver.MustParse("3.10.0"),
|
||||||
|
release: &ReleaseSpec{
|
||||||
|
Chart: "test/chart",
|
||||||
|
Version: "0.1",
|
||||||
|
Name: "test-charts",
|
||||||
|
Namespace: "test-namespace",
|
||||||
|
},
|
||||||
|
wantErr: "forceConflicts requires Helm 4 or greater (set via releases[].forceConflicts or helmDefaults.forceConflicts)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "force-and-force-conflicts-mutually-exclusive-helm4",
|
||||||
|
defaults: HelmSpec{
|
||||||
|
CreateNamespace: &disable,
|
||||||
|
},
|
||||||
|
version: semver.MustParse("4.0.0"),
|
||||||
|
release: &ReleaseSpec{
|
||||||
|
Chart: "test/chart",
|
||||||
|
Version: "0.1",
|
||||||
|
Force: &enable,
|
||||||
|
ForceConflicts: &enable,
|
||||||
|
Name: "test-charts",
|
||||||
|
Namespace: "test-namespace",
|
||||||
|
},
|
||||||
|
wantErr: "force and forceConflicts are mutually exclusive (check both releases[].force/forceConflicts and helmDefaults.force/forceConflicts)",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "recreate-pods",
|
name: "recreate-pods",
|
||||||
defaults: HelmSpec{
|
defaults: HelmSpec{
|
||||||
|
|||||||
@@ -38,39 +38,39 @@ func TestGenerateID(t *testing.T) {
|
|||||||
run(testcase{
|
run(testcase{
|
||||||
subject: "baseline",
|
subject: "baseline",
|
||||||
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"},
|
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"},
|
||||||
want: "foo-values-6d799cf798",
|
want: "foo-values-5bc9c89c6b",
|
||||||
})
|
})
|
||||||
|
|
||||||
run(testcase{
|
run(testcase{
|
||||||
subject: "different bytes content",
|
subject: "different bytes content",
|
||||||
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"},
|
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"},
|
||||||
data: []byte(`{"k":"v"}`),
|
data: []byte(`{"k":"v"}`),
|
||||||
want: "foo-values-7f885447bf",
|
want: "foo-values-7bf9c8bcdf",
|
||||||
})
|
})
|
||||||
|
|
||||||
run(testcase{
|
run(testcase{
|
||||||
subject: "different map content",
|
subject: "different map content",
|
||||||
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"},
|
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"},
|
||||||
data: map[string]any{"k": "v"},
|
data: map[string]any{"k": "v"},
|
||||||
want: "foo-values-86f5d8fb55",
|
want: "foo-values-65694d8947",
|
||||||
})
|
})
|
||||||
|
|
||||||
run(testcase{
|
run(testcase{
|
||||||
subject: "different chart",
|
subject: "different chart",
|
||||||
release: ReleaseSpec{Name: "foo", Chart: "stable/envoy"},
|
release: ReleaseSpec{Name: "foo", Chart: "stable/envoy"},
|
||||||
want: "foo-values-5cd5c65db5",
|
want: "foo-values-856c5f7dd5",
|
||||||
})
|
})
|
||||||
|
|
||||||
run(testcase{
|
run(testcase{
|
||||||
subject: "different name",
|
subject: "different name",
|
||||||
release: ReleaseSpec{Name: "bar", Chart: "incubator/raw"},
|
release: ReleaseSpec{Name: "bar", Chart: "incubator/raw"},
|
||||||
want: "bar-values-c59b4f979",
|
want: "bar-values-fff55fbf5",
|
||||||
})
|
})
|
||||||
|
|
||||||
run(testcase{
|
run(testcase{
|
||||||
subject: "specific ns",
|
subject: "specific ns",
|
||||||
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw", Namespace: "myns"},
|
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw", Namespace: "myns"},
|
||||||
want: "myns-foo-values-56d6cd88cc",
|
want: "myns-foo-values-6bfbb74765",
|
||||||
})
|
})
|
||||||
|
|
||||||
for id, n := range ids {
|
for id, n := range ids {
|
||||||
|
|||||||
Reference in New Issue
Block a user