feat(base): support HPA->ScaledObject migration via transfer-hpa-ownership annotation#204
Conversation
…rship annotation Adds two backward-compatible passthroughs to the KEDA ScaledObject template in the `base` chart so consumers can use KEDA's transfer-hpa-ownership migration path without forking the chart: 1. `autoscaling.scaledObjectAnnotations` (map) - rendered as `metadata.annotations` on the ScaledObject. Useful for `scaledobject.keda.sh/transfer-hpa-ownership: "true"` and similar. 2. `autoscaling.hpaName` (string) - rendered as `advanced.horizontalPodAutoscalerConfig.name`. Combined with the existing `behavior` block under a single `advanced` clause guarded by `or` of the two values. Also adds a "Migrating from plain HPA to KEDA" section to charts/base/README.md documenting the full recipe (annotation + matching HPA name + optional `helm.sh/resource-policy: keep` for zero-gap migration), the ~30s metrics-blind window without the keep annotation, and the reverse-direction caveat. Bumps charts/base/Chart.yaml from 0.3.28 to 0.3.29. Background: kedacore/keda#6250
|
🎉🥳 Congratulations on your new adventurous journey! 🥳🎉 This PR is an absolute game-changer! So, let's explore the awesome new features and improvements we've bundled. PR Summary
So, embrace these changes and thrive on the benefits they offer! 🎈💪🌟 |
Summary
Adds two backward-compatible passthroughs to the KEDA
ScaledObjecttemplate in thebasechart so consumers can use KEDA'stransfer-hpa-ownershipmigration path without forking the chart:autoscaling.scaledObjectAnnotations(map) — rendered asmetadata.annotationson the ScaledObject.autoscaling.hpaName(string) — rendered asadvanced.horizontalPodAutoscalerConfig.name. Combined with the existingbehaviorblock under a singleadvancedclause guarded byorof the two values.Also adds a "Migrating from plain HPA to KEDA" section to
charts/base/README.mddocumenting the full recipe.Motivation
The chart already commits to a dual-mode design where
templates/hpa.yaml(whenautoscaling.triggersis unset) andtemplates/keda.yaml(when set) are mutually exclusive and both render a resource named{{ include "base.fullname" . }}. Flipping a release between the two viahelm upgradeis rejected by KEDA'svscaledobject.kb.ioadmission webhook because the same-named HPA still exists when the new ScaledObject is created (Helm creates new resources before deleting old ones during upgrade):KEDA's documented migration path is the
scaledobject.keda.sh/transfer-hpa-ownership: "true"annotation paired withadvanced.horizontalPodAutoscalerConfig.namematching the existing HPA. Today's chart exposes neither, so a chart consumer hitting this has to either fork the chart, use a post-renderer, orkubectl delete hpa <name>before the upgrade (which leaves a brief window with no autoscaler at all). See kedacore/keda#6250 for the broader context on why this is awkward by default.This PR finishes the dual-mode design the chart already started.
Changes
charts/base/templates/keda.yamlmetadata.annotationsblock guarded bywith .Values.autoscaling.scaledObjectAnnotations.advanced.horizontalPodAutoscalerConfig.namewith the existingbehaviorblock under a singleadvancedclause guarded byor .Values.autoscaling.behavior .Values.autoscaling.hpaName. Each child field useswithso it only renders when set.charts/base/values.yamlscaledObjectAnnotationsandhpaNameexamples in theautoscaling:section with inline guidance.charts/base/README.mdkubectl annotate hpa/<name> helm.sh/resource-policy=keepfor zero-gap migration), the residual ~30s metrics-blind window note (Helm deletes the old HPA at end of upgrade; KEDA recreates it on next reconcile), and the reverse-direction caveat.charts/base/Chart.yamlversionandappVersionfrom0.3.28to0.3.29.Backward Compatibility
autoscaling.enabled: true, notrigger/triggers): zero changes — HPA still renders, no ScaledObject.autoscaling.trigger): zero changes — singular trigger path is untouched.autoscaling.triggers, possibly withbehavior): zero changes — the newwith .Values.autoscaling.hpaNameis conditionally rendered only when the new field is set, and the existingbehaviorblock is wrapped in its ownwithto remain unchanged.scaledObjectAnnotations/hpaName: fully opt-in.Example Usage
Migrate an existing plain HPA named
my-release-nameto a KEDA ScaledObject without manually deleting the HPA:For zero metrics-blind window during the transition, also pre-annotate the existing HPA so Helm leaves it alone during the upgrade:
Validation
helm lint charts/base— passes clean.helm template charts/base --values <file>smoke tests:autoscaling.enabled: true, no triggers, withbehavior): renders an HPA, no ScaledObject, no annotations. Byte-for-byte identical to pre-change template output (only the chart-version label moves0.3.28→0.3.29).triggersarray +behavior, no new fields): renders ScaledObject with no annotations and noadvanced.horizontalPodAutoscalerConfig.name. Byte-for-byte identical to pre-change output (only chart-version label change).scaledObjectAnnotations+hpaNameset): renders ScaledObject withmetadata.annotations.scaledobject.keda.sh/transfer-hpa-ownership: "true"andadvanced.horizontalPodAutoscalerConfig.name: my-existing-hpa. Nobehaviorblock (correctly omitted when not set).hpaName+behaviorcombo: both render correctly nested under a singleadvanced.horizontalPodAutoscalerConfigblock.pre-commit run --all-files— all hooks pass (including helmlint, gitleaks, conventional-commits).