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.
This commit is contained in:
104
plugins/scheduler-k3s/chart_hooks.go
Normal file
104
plugins/scheduler-k3s/chart_hooks.go
Normal file
@@ -0,0 +1,104 @@
|
||||
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",
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user