Skip to content

feat(controller): change replicas through the scale subresource - #75

Merged
slauger merged 1 commit into
developfrom
feat/scale-subresource
Sep 6, 2026
Merged

slauger merged 1 commit into
developfrom
feat/scale-subresource

Conversation

@slauger

@slauger slauger commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Summary

A full-object update is the wrong tool for a replica change on a workload that something else also manages. A HorizontalPodAutoscaler writes through /scale, so a narrow write competes with it cleanly, whereas a full-object update replays the entire spec on a conflict retry.

Deployment and StatefulSet now change replicas through client.SubResource("scale"). CronJob is unchanged: it has no scale subresource, and spec.suspend remains the mechanism.

The two branches were near-identical, so they collapse into one helper.

The write order is deliberate and is the part worth reviewing. Annotations are written first, then the scale call. If the scale fails, the workload is still running and the next evaluation retries it, so the state corrects itself. In the other order, a failed annotation write would leave a workload stopped with no record of its previous replica count, and the zero-replica guard at the top would stop the operator ever revisiting it, making the loss permanent. There is a test for this with an injected scale failure.

RBAC gains deployments/scale and statefulsets/scale with get and update. Narrow and explicit; make check-rbac confirms the chart and the markers agree.

The generic "any kind implementing /scale" variant remains rejected, with the three blockers recorded on #68 so the question is not reopened without them.

Closes #68

Test plan

  • make ci passes; existing scale-down tests continue to assert replicas == 0, so the subresource path is genuinely exercised rather than silently skipped.
  • New test injects a failing scale subresource and asserts the workload is left running and the previous replica count was already recorded. That test fails if the order is swapped.
  • New tests for an already-stopped workload, which must write no annotations at all, and for dry run, which must touch neither replicas nor annotations.
  • Coverage 64.1 percent.

A full-object update is the wrong tool for a replica change on a
workload something else also manages. A HorizontalPodAutoscaler writes
through /scale, so a narrow write competes with it cleanly, whereas a
full update replays the entire spec on a conflict retry.

Deployment and StatefulSet now go through client.SubResource("scale").
CronJob is unchanged: it has no scale subresource and spec.suspend
remains the mechanism.

The two branches were near-identical, so they collapse into one helper.
The annotations are written before the scale call on purpose: if the
scale then fails the workload is still running and the next evaluation
retries it, whereas the other order could leave a stopped workload with
no record of its previous replica count, and the zero-replica guard
would stop the operator ever revisiting it.

Adds deployments/scale and statefulsets/scale with get and update.
Narrow and explicit; the generic variant was considered and rejected,
with the reasoning recorded on the issue.

Closes #68

Signed-off-by: Simon Lauger <simon@lauger.de>
@slauger
slauger merged commit 571d1e4 into develop Sep 6, 2026
14 checks passed
@slauger
slauger deleted the feat/scale-subresource branch September 6, 2026 09:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(controller): change replicas through the scale subresource

1 participant