Repository navigation
Releases: mitis-cloud/understudy
Release list
v0.4.5
Disruption handover fixes
- Fix sentinel startup when
sentinel.enabled=trueby removing the obsolete--modeargument. The chart's arguments are now checked against both binaries in CI. (#7) - Honour the target Deployment or Rollout's
minReadySecondsbefore counting a stand-in as available, in both reconciliation and eviction admission. (#9) - Persist the eviction hold and relax it at
readinessDeadlineSecondseven when replica changes or pod deletion-cost updates fail. Failed surges remain visible through status, events, and logs. (#10) - Watch cordon and taint signals on every operator replica so standby replicas can make correct webhook decisions during multi-node disruptions. Workload reconciliation remains leader-gated. (#11)
Security
Update golang.org/x/net, gRPC, and OpenTelemetry dependencies past the identified advisories. (#8)
Upgrade
helm upgrade understudy oci://ghcr.io/kylan11/charts/understudy \
--namespace understudy-system --version 0.4.5There are no CRD schema or configuration changes from v0.4.4. Stand-ins now wait for the target's minReadySeconds; allow for that warm-up period when configuring readinessDeadlineSeconds. Upgrades from earlier versions should also follow UPGRADING.md.
- Image:
ghcr.io/kylan11/understudy:0.4.5(linux/amd64,linux/arm64) - Helm chart:
oci://ghcr.io/kylan11/charts/understudy, version0.4.5 - API:
apps.understudy.sh/v1alpha1, unchanged from v0.4.4
Thanks
Thank you to @0xmichalis for all five fixes and their regression tests.
v0.4.4
GitOps baseline capacity
Git can now supply desired capacity through Understudy.spec.baselineReplicas,
while Understudy manages the workload's effective replica count:
spec:
targetRef:
kind: Deployment # or Rollout
name: worker
baselineReplicas: 1During a disruption handover, Understudy preserves the captured baseline and
temporary surge. Capacity changes from Git remain pending; the latest request
converges when the handover completes, including scale-to-zero. The frozen
baseline remains the replacement readiness threshold throughout the handover.
status.baselineReplicas and the BaselineApplied condition expose applied and
pending capacity. An application release taking over a handover ends disruption
surging and holding while applying the latest desired baseline. Ordinary baseline
reconciliation continues during paused Rollout releases.
Argo CD must delegate the target's effective replica field and handover markers
using scoped ignoreDifferences plus RespectIgnoreDifferences=true, while
continuing to manage the Understudy baseline input. See the
complete configuration and lifecycle.
This is opt-in. Resources without baselineReplicas retain reactive-only
protection. Use one capacity writer per target; HPA/KEDA must not also manage
the same effective replica count under this contract.
Upgrade
Update the CRD explicitly before enabling the new field. Helm does not
upgrade CRDs in a chart's crds/ directory.
kubectl apply --server-side -f \
https://raw.githubusercontent.com/mitis-cloud/understudy/v0.4.4/charts/understudy/crds/apps.understudy.sh_understudies.yaml
helm upgrade understudy oci://ghcr.io/kylan11/charts/understudy \
--namespace understudy-system --version 0.4.4Configure the baseline input and Argo delegation together. The full
upgrade notes
describe the required order.
- Image:
ghcr.io/kylan11/understudy:0.4.4(linux/amd64,linux/arm64) - Helm chart:
oci://ghcr.io/kylan11/charts/understudy, version0.4.4 - API:
apps.understudy.sh/v1alpha1, with the v0.4.4 CRD schema
Verification
Build, lint, unit/envtest, Helm, and E2E CI pass. Live tests used Argo CD 3.5.5,
Argo Rollouts 1.10.1, and Kubernetes 1.36.4 in isolated Kind clusters.
Deployment, canary, and blue/green targets passed first install, unrelated
automatic sync during surge, repeated pending baseline updates, cold operator
restart and leadership recovery, eviction hold/release, final convergence,
steady scaling, HTTP serving, and release takeover. Blue/green also passed a
pending scale-to-zero followed by scale-up from Git. Automatic sync and self-heal
stayed enabled throughout.
The Argo test used its ordinary apply path. SSA and force/replace sync were not
exercised. Operator downtime retains the existing fail-open eviction behavior.
Delivered in PR #6.
v0.4.3
Argo Rollouts support
Understudy can now protect single-replica Argo Rollouts with fully promoted
canary and blue/green strategies:
spec:
targetRef:
kind: Rollout
name: your-rolloutUnderstudy scales the Rollout's native replica count, holds eviction until a
traffic-serving replacement is ready, and then restores the original replica
count. Ready preview pods and old revisions do not count as replacements.
The controller and eviction webhook stand down together during progressive
delivery, pauses, and aborts. Argo Rollouts is optional: existing Deployment
targets continue to work without its CRD. Install Argo Rollouts before starting
Understudy, or restart Understudy after installing it.
This release also adds the FAUN.dev score, open-source, and license badges to
the README.
Upgrade
helm upgrade understudy oci://ghcr.io/kylan11/charts/understudy \
--namespace understudy-system --version 0.4.3The chart includes the new Rollout RBAC permissions. If you manage RBAC yourself,
grant get, list, watch, update, and patch on rollouts.argoproj.io.
Existing Deployment targets require no configuration changes.
- Image:
ghcr.io/kylan11/understudy:0.4.3(linux/amd64,linux/arm64) - Helm chart:
oci://ghcr.io/kylan11/charts/understudy, version0.4.3 - Argo API library: v1.10.1, the current stable release
Verification
Lint, unit/envtest tests, startup without the Rollout CRD, and Helm checks pass.
Live tests with Argo Rollouts v1.10.1 on an isolated Kubernetes 1.36.4 Kind cluster
verified both strategies through eviction hold, surge, ready replacement,
scale-back, and successful HTTP serving. Paused releases admitted eviction
without starting an Understudy surge.
v0.4.2
Dependency security release. No behavior changes and nothing to do
beyond upgrading.
An automated scan (#3) flagged the dependency tree. The vulnerability it
cited turned out not to apply, but govulncheck traced four others that
this code actually reaches: two in golang.org/x/net (an HTTP/2 infinite
loop and a punycode validation gap) and one each in golang.org/x/text
and google.golang.org/grpc, all reachable through the cloud sentinel's
probe client and the eviction webhook server. All three modules are now
past their fixed versions (x/net v0.58.0, x/text v0.41.0, grpc v1.83.1)
and govulncheck reports zero reachable vulnerabilities.
The release push itself is now scripted in hack/release.sh rather than
hand-run.
Upgrading:
helm upgrade understudy oci://ghcr.io/kylan11/charts/understudy \
--namespace understudy-system
The image is built for arm64 and amd64.
v0.4.1
Nothing breaks, and there is a new UPGRADING.md that says so at more
length, covering this release and the move from 0.3.x.
hostageMode is now holdMode. The old name described a budget that took
evictions hostage, and 0.4.0 removed the budget. Both fields are read,
holdMode wins when both are set, and a resource that sets only the old
one keeps working while the operator raises a single warning event
against it. The field will be removed in a later release. Because a
defaulted field cannot be told apart from a chosen one, the schema
default moved into the resolver, and the mode the operator settled on is
published as status.mode, which is what the MODE column now prints.
The field was also never explained anywhere. It gates one thing, whether
an eviction is made to wait, and the surge happens in all three modes:
even with holdMode off a stand-in is brought up in parallel, so the
workload is down for as long as a replacement takes rather than as long
as a reschedule takes. voluntary-only exists for workloads that drain
connections on SIGTERM, where refusing an eviction whose node is leaving
at a fixed moment only burns the window the pod could have used.
Also fixes the intermittent build failure. The eviction specs attempted
one eviction and then waited for the surge, which races the doom lease
those attempts refresh; a drainer that gives up after a single try is not
the scenario being tested. They now keep evicting while they wait, the
way a real drainer retries.
Upgrading:
helm upgrade understudy oci://ghcr.io/kylan11/charts/understudy \
--namespace understudy-system
The image is built for arm64 and amd64.
v0.4.0
Understudy now holds protected evictions at admission instead of owning a PodDisruptionBudget. This removes the structural node-pinning failure: disrupters can select and begin terminating a node, while the fail-open webhook returns HTTP 429 until a healthy stand-in is ready.
Breaking changes
understudy_pdb_relaxed_totalis renamed tounderstudy_hold_relaxed_total.understudy_node_pinned_totaland theNodePinnedcondition are removed.- Helm values
signals.evictionWebhook*,signals.pinDetection, andfailsafeare retired. Webhook settings are nowwebhook.timeoutSecondsandwebhook.namespaceSelector. - The ValidatingWebhookConfiguration suffix changes from
-eviction-observerto-eviction-hold; the admission URL remains/observe-evictionfor safe upgrades. - Operator unavailability now creates a protection gap rather than leaving blocking budgets behind. Use two replicas on separate nodes for production.
Upgrade behavior
A leader-gated startup sweep deletes legacy PodDisruptionBudgets labeled understudy.sh/owned=true. PDB RBAC is reduced to get, list, and delete for this migration and will be removed in v0.5.0. Existing webhook certificate material is reused across Helm upgrades.
Artifacts
- Image:
ghcr.io/kylan11/understudy:0.4.0(linux/amd64,linux/arm64) - Chart:
oci://ghcr.io/kylan11/charts/understudy --version 0.4.0
v0.3.1
Nothing breaking.
A hostage budget on a singleton can pin its node: Karpenter checks budgets
before consolidating, drifting or expiring a node and simply never begins
while disruptions are not allowed, producing no signal anything could
react to. This release names that tradeoff in the README and makes it
visible per workload. A new watcher follows the DisruptionBlocked events
the blocker leaves behind and, when the named budget is one this operator
owns, raises a NodePinned condition on the affected Understudy, with a
Warning event and an understudy_node_pinned_total counter. The condition
clears when the refusals stop. Detection only, on by default
(signals.pinDetection in the chart): a pinned node is reported, never
resolved. The tradeoff section in the README lists the mitigations; what
actually moves a pinned pod is rescheduling it, via a deploy, a rollout
restart, or deleting the node claim so a real drain starts.
Also fixes the cross-build make target, which duplicated the platform
flag the Dockerfile already carries and masked the resulting failure.
The image is now tagged to match the chart appVersion exactly (0.3.1, no
v prefix), so installs no longer need an image.tag override.
Upgrading:
helm upgrade understudy oci://ghcr.io/kylan11/charts/understudy \
--namespace understudy-system
The image is built for arm64 and amd64.
v0.3.0
Zerover: breaking changes bump the minor until 1.0.
Breaking. The API group moved from apps.understudy.dev to
apps.understudy.sh, because the old domain belongs to an unrelated party. The
annotation and label keys moved with it. Existing installs need the old
custom resource definition deleted and their Understudy objects recreated.
The chart is now published, so installing no longer needs a checkout:
helm install understudy oci://ghcr.io/kylan11/charts/understudy \
--namespace understudy-system --create-namespace
The image is built for arm64 and amd64. Documentation covering the mechanism,
the signal contract and the failure behaviour is in docs/how-it-works.md.
v0.2.0
Zerover: the API may still change before 1.0.
Milestone 2 and milestone 3, plus a fail-safe for operator outages.
Eviction webhook. An observe-only admission webhook on pods/eviction sees
every eviction attempt, including those the PDB then rejects, so drainers
that neither cordon nor taint are now covered. It always admits and is
fail-open. It surges only for pods covered by a PDB this operator owns, so an
unrelated eviction on the same node does not disturb protected workloads.
Cloud sentinels. An optional DaemonSet reads the cloud's termination
notice and taints its own node with the deadline. The core decodes that as an
involuntary signal and can now decide between holding the eviction and
releasing it immediately. In v0.1.0 a spot interruption reached the operator
only as an ordinary drainer taint, so it was held blind against a hard
termination wall. AWS is validated against real spot interruptions driven by
Fault Injection Simulator. GCP and Azure probes are covered by unit tests
only and should be treated as experimental until run on GKE and AKS.
Operator-down fail-safe. The operator heartbeats the PDBs it owns and a
CronJob deletes those whose heartbeat has gone stale, so an outage degrades
to plain Kubernetes rather than leaving a blocking PDB behind. Verified live:
the stale budget was removed automatically once the operator stopped.
Also in this release: a metrics Service, image pull secret support, and a
chart that no longer doubles the release name.
v0.1.0
First tagged release. Zerover: the API may still change before 1.0.
Milestone 1 is complete and validated against a live EKS cluster running
Karpenter on arm64 spot nodes:
- Hostage PodDisruptionBudget routes every eviction through the operator.
- Surge on node cordon and on drainer taints (Karpenter, cluster-autoscaler),
matched by key. - Release only once the stand-in is traffic-ready, including pod readiness
gates, so load balancer registration is respected with no provider code. - Deterministic scale-down of the doomed pod via pod-deletion-cost.
- Never-wedge TTL relaxes the PDB rather than stalling a drain forever.
- Self-exclusion, graceful skip of unsupported targets, fail-open posture.
Measured on a single-replica workload behind an ALB, with no spare capacity
so the replacement waited for a cold EC2 node: a drain cost 1 failed request
in 350, and a real AWS spot interruption cost 1 in 764 with the handover
finishing 29 seconds before the instance was terminated. For comparison, an
ordinary rolling update of the same workload cost 2 in 38.