Files
dokku/plugins/scheduler-k3s/chart_hooks_test.go
Jose Diaz-Gonzalez da13367ca8 feat: add version-aware helm chart upgrade callbacks
Some helm chart upgrades require side-effects that a plain `helm upgrade` cannot perform, such as deleting deployments whose immutable selectors changed. Charts may now register pre and post upgrade hooks against a target version; applicable hooks fire in ascending semver order, each bracketed around an upgrade to its version, with a final upgrade to the chart's configured version when needed. The new `keda-add-ons-http` 0.12.2 hook deletes the chart-managed deployments before the upgrade so the new selectors take effect cleanly.
2026-04-27 07:57:38 -04:00

160 lines
3.8 KiB
Go

package scheduler_k3s
import (
"context"
"testing"
)
func makeHook(version string) ChartHook {
noop := func(context.Context, KubernetesClient, HelmChart, Release) error { return nil }
return ChartHook{
TargetVersion: version,
PreUpgrade: noop,
}
}
func versionsOf(hooks []ChartHook) []string {
out := make([]string, 0, len(hooks))
for _, hook := range hooks {
out = append(out, hook.TargetVersion)
}
return out
}
func equalStrings(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
func TestSelectChartHooks(t *testing.T) {
previous := ChartHooks
t.Cleanup(func() { ChartHooks = previous })
ChartHooks = map[string][]ChartHook{
"chart-with-hooks": {
makeHook("0.12.0"),
makeHook("0.10.0"),
makeHook("0.11.0"),
},
}
cases := []struct {
name string
releaseName string
installedVersion string
targetVersion string
want []string
wantErr bool
}{
{
name: "fresh install yields no hooks",
releaseName: "chart-with-hooks",
installedVersion: "",
targetVersion: "0.12.0",
want: nil,
},
{
name: "no hooks registered for release",
releaseName: "chart-without-hooks",
installedVersion: "0.5.0",
targetVersion: "0.6.0",
want: nil,
},
{
name: "single applicable hook returned",
releaseName: "chart-with-hooks",
installedVersion: "0.10.0",
targetVersion: "0.11.0",
want: []string{"0.11.0"},
},
{
name: "all hooks below installed version excluded",
releaseName: "chart-with-hooks",
installedVersion: "0.12.0",
targetVersion: "0.12.0",
want: nil,
},
{
name: "all applicable hooks returned in ascending semver order",
releaseName: "chart-with-hooks",
installedVersion: "0.9.0",
targetVersion: "0.12.0",
want: []string{"0.10.0", "0.11.0", "0.12.0"},
},
{
name: "partial coverage returns only unsatisfied hooks",
releaseName: "chart-with-hooks",
installedVersion: "0.10.0",
targetVersion: "0.12.0",
want: []string{"0.11.0", "0.12.0"},
},
{
name: "target above all hooks still caps at chart target",
releaseName: "chart-with-hooks",
installedVersion: "0.9.0",
targetVersion: "0.11.0",
want: []string{"0.10.0", "0.11.0"},
},
{
name: "unparseable installed version errors",
releaseName: "chart-with-hooks",
installedVersion: "not-a-version",
targetVersion: "0.12.0",
wantErr: true,
},
{
name: "unparseable target version errors",
releaseName: "chart-with-hooks",
installedVersion: "0.9.0",
targetVersion: "not-a-version",
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got, err := selectChartHooks(tc.releaseName, tc.installedVersion, tc.targetVersion)
if tc.wantErr {
if err == nil {
t.Fatalf("expected error, got nil")
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
gotVersions := versionsOf(got)
if !equalStrings(gotVersions, tc.want) {
t.Fatalf("versions mismatch: got %v, want %v", gotVersions, tc.want)
}
})
}
}
func TestSelectChartHooksTreatsLeadingVAsEqual(t *testing.T) {
previous := ChartHooks
t.Cleanup(func() { ChartHooks = previous })
ChartHooks = map[string][]ChartHook{
"chart-v-prefixed": {
makeHook("v1.13.3"),
},
}
got, err := selectChartHooks("chart-v-prefixed", "v1.12.0", "v1.13.3")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(got) != 1 || got[0].TargetVersion != "v1.13.3" {
t.Fatalf("expected v1.13.3 hook, got %v", versionsOf(got))
}
}