Files
dokku/plugins/scheduler-k3s/chart_hooks.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

105 lines
3.4 KiB
Go

package scheduler_k3s
import (
"context"
"fmt"
"sort"
"github.com/Masterminds/semver/v3"
)
// ChartHookFunc is the signature for a function that runs around a helm chart
// upgrade. It receives the active Kubernetes client, the chart definition, and
// the helm release that was installed before the upgrade started.
type ChartHookFunc func(ctx context.Context, clientset KubernetesClient, chart HelmChart, installedRevision Release) error
// ChartHook attaches version-specific behaviour to a helm chart upgrade. A
// hook fires when the currently installed release version is below
// TargetVersion and the chart's configured target version is at or above
// TargetVersion. PreUpgrade runs before the chart is upgraded to
// TargetVersion; PostUpgrade runs after that intermediate upgrade succeeds.
type ChartHook struct {
TargetVersion string
PreUpgrade ChartHookFunc
PostUpgrade ChartHookFunc
}
// ChartHooks is keyed by HelmChart.ReleaseName. selectChartHooks resorts
// entries by semver, so authoring order is informative rather than load-bearing.
var ChartHooks = map[string][]ChartHook{
"keda-add-ons-http": {
{
TargetVersion: "0.12.2",
PreUpgrade: deleteKedaAddOnsHttpDeployments,
},
},
}
// selectChartHooks returns the hooks for releaseName whose TargetVersion is
// strictly greater than installedVersion and less than or equal to
// targetVersion, sorted ascending by TargetVersion. An empty installedVersion
// signals a fresh install and yields no hooks.
func selectChartHooks(releaseName, installedVersion, targetVersion string) ([]ChartHook, error) {
if installedVersion == "" {
return nil, nil
}
hooks, ok := ChartHooks[releaseName]
if !ok || len(hooks) == 0 {
return nil, nil
}
installed, err := semver.NewVersion(installedVersion)
if err != nil {
return nil, fmt.Errorf("Invalid installed version %q for chart %s: %w", installedVersion, releaseName, err)
}
target, err := semver.NewVersion(targetVersion)
if err != nil {
return nil, fmt.Errorf("Invalid target version %q for chart %s: %w", targetVersion, releaseName, err)
}
type indexedHook struct {
version *semver.Version
hook ChartHook
}
selected := []indexedHook{}
for _, hook := range hooks {
hookVersion, err := semver.NewVersion(hook.TargetVersion)
if err != nil {
return nil, fmt.Errorf("Invalid hook TargetVersion %q for chart %s: %w", hook.TargetVersion, releaseName, err)
}
if hookVersion.Compare(installed) <= 0 {
continue
}
if hookVersion.Compare(target) > 0 {
continue
}
selected = append(selected, indexedHook{version: hookVersion, hook: hook})
}
sort.Slice(selected, func(i, j int) bool {
return selected[i].version.LessThan(selected[j].version)
})
out := make([]ChartHook, 0, len(selected))
for _, entry := range selected {
out = append(out, entry.hook)
}
return out, nil
}
// deleteKedaAddOnsHttpDeployments removes the deployments managed by the
// keda-add-ons-http helm release so the chart can recreate them with the
// updated selectors introduced in 0.12.x. Selectors on Deployments are
// immutable, so a straight helm upgrade fails without this step.
func deleteKedaAddOnsHttpDeployments(ctx context.Context, clientset KubernetesClient, chart HelmChart, installedRevision Release) error {
return clientset.DeleteDeployment(ctx, DeleteDeploymentInput{
Namespace: chart.Namespace,
LabelSelector: "app.kubernetes.io/instance=keda-add-ons-http",
})
}