Skip to content

Releases: mitis-cloud/understudy

v0.4.5

Choose a tag to compare

@kylan11 kylan11 released this 19 Oct 09:22
60d0256

Disruption handover fixes

  • Fix sentinel startup when sentinel.enabled=true by removing the obsolete --mode argument. The chart's arguments are now checked against both binaries in CI. (#7)
  • Honour the target Deployment or Rollout's minReadySeconds before counting a stand-in as available, in both reconciliation and eviction admission. (#9)
  • Persist the eviction hold and relax it at readinessDeadlineSeconds even 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.5

There 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, version 0.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

Choose a tag to compare

@kylan11 kylan11 released this 14 Oct 09:21
7bae5f8

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: 1

During 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.4

Configure 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, version 0.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

Choose a tag to compare

@kylan11 kylan11 released this 14 Oct 07:31
672d1a2

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-rollout

Understudy 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.3

The 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, version 0.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

Choose a tag to compare

@kylan11 kylan11 released this 25 Aug 08:18

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

Choose a tag to compare

@kylan11 kylan11 released this 10 Aug 09:22

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

Choose a tag to compare

@kylan11 kylan11 released this 07 Aug 13:34
7ac0e59

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_total is renamed to understudy_hold_relaxed_total.
  • understudy_node_pinned_total and the NodePinned condition are removed.
  • Helm values signals.evictionWebhook*, signals.pinDetection, and failsafe are retired. Webhook settings are now webhook.timeoutSeconds and webhook.namespaceSelector.
  • The ValidatingWebhookConfiguration suffix changes from -eviction-observer to -eviction-hold; the admission URL remains /observe-eviction for 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

Choose a tag to compare

@kylan11 kylan11 released this 07 Aug 10:36

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

Choose a tag to compare

@kylan11 kylan11 released this 06 Aug 12:30

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

Choose a tag to compare

@kylan11 kylan11 released this 06 Aug 11:32

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

Choose a tag to compare

@kylan11 kylan11 released this 06 Aug 11:15

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.