Skip to content

feat(autoscaler): add scale-down eligibility knobs (ignore-daemonsets-utilization, skip-nodes-with-*) - #5756

Merged
devantler merged 4 commits into
mainfrom
claude/autoscaler-ignore-daemonsets
Jul 3, 2026
Merged

feat(autoscaler): add scale-down eligibility knobs (ignore-daemonsets-utilization, skip-nodes-with-*)#5756
devantler merged 4 commits into
mainfrom
claude/autoscaler-ignore-daemonsets

Conversation

@devantler

Copy link
Copy Markdown
Contributor

🤖 Generated by the Daily AI Assistant

Why

On the Hetzner prod cluster, overflow (autoscaler) nodes could never scale back down: the Cluster Autoscaler counts each node’s system-DaemonSet requests (CNI, CSI, observability, security agents) toward utilization, and those alone exceed the 50% scale-down threshold — a one-way ratchet that pinned idle nodes and exhausted the Hetzner server quota (which in turn wedges the deploy pipeline). KSail exposed no way to relax this.

What

Adds three cluster-autoscaler scale-down knobs to spec.cluster.autoscaler.node so operators can let the autoscaler reclaim genuinely-idle overflow nodes: ignoreDaemonsetsUtilization, skipNodesWithLocalStorage, and skipNodesWithSystemPods. All default to the upstream behaviour (no change for existing clusters); the skip flags are opt-out (explicit false is preserved). Regenerated schema/CRD/docs/types accordingly.

Unblocks the platform node right-sizing work; the platform side sets ignoreDaemonsetsUtilization: true once this ships in a release. Complements dropping the too-small cx33 overflow type (platform#2420).

…-utilization, skip-nodes-with-*)

The Hetzner Cluster Autoscaler judges scale-down on requested utilization and,
by default, counts DaemonSet requests and refuses to remove nodes running pods
with local storage or non-DaemonSet system pods. On a cluster whose per-node
system DaemonSets (CNI, CSI, observability, security agents) already exceed the
50% scale-down threshold, overflow nodes become permanently unremovable — a
one-way ratchet that exhausts the Hetzner server quota.

Expose three cluster-autoscaler knobs on spec.cluster.autoscaler.node so the
autoscaler can actually reclaim idle overflow nodes:
- ignoreDaemonsetsUtilization (bool, default false) → --ignore-daemonsets-utilization
- skipNodesWithLocalStorage (*bool, upstream default true) → --skip-nodes-with-local-storage
- skipNodesWithSystemPods (*bool, upstream default true) → --skip-nodes-with-system-pods

The two skip flags are pointers so an explicit false is preserved (nil inherits
the upstream default, leaving existing releases free of values drift). Regenerated
schema, CRD, deepcopy, reference docs, chat docs, and web UI types.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds three cluster-autoscaler configuration options to the API type, Helm rendering path, CRD and JSON schemas, documentation, and installer tests.

Changes

Cluster autoscaler flag additions

Layer / File(s) Summary
API type and deepcopy
pkg/apis/cluster/v1alpha1/autoscaler.go, pkg/apis/cluster/v1alpha1/zz_generated.deepcopy.go
NodeAutoscalerConfig gains IgnoreDaemonsetsUtilization (bool), SkipNodesWithLocalStorage and SkipNodesWithSystemPods (*bool), and deepcopy copies the new pointer fields.
Installer wiring of new extraArgs
pkg/svc/installer/clusterautoscaler/installer.go
chartExtraArgs and buildChartValues render the three new flags into Helm extraArgs, omitting nil pointer flags and rendering explicit true/false otherwise.
Installer test coverage
pkg/svc/installer/clusterautoscaler/installer_test.go
New tests and a shared assertAutoscalerValuesYaml helper verify conditional rendering and omission of the new extraArgs.
CRD, JSON schema, and docs
charts/ksail-operator/crds/ksail.io_clusters.yaml, schemas/ksail-config.schema.json, docs/src/content/docs/configuration/declarative-configuration.mdx
Adds the three properties to the CRD and JSON schemas and documents their defaults and applicability.

Estimated code review effort: 2 (Simple) | ~12 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the autoscaler scale-down eligibility knobs added in this PR.
Description check ✅ Passed The description accurately matches the autoscaler configuration, schema, and docs changes in the changeset.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/autoscaler-ignore-daemonsets

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

MegaLinter analysis: Success

✅ Linters with no issues

actionlint, bash-exec, git_diff, hadolint, jscpd, jsonlint, lychee, markdown-table-formatter, markdownlint, prettier, prettier, shellcheck, shfmt, stylelint, syft, trivy-sbom, trufflehog, v8r, v8r, yamllint

Notices

📣 MegaLinter 9.5.0 is out! Discover the new features and security recommendations in the release announcement. (Skip this info by defining SECURITY_SUGGESTIONS: false)

See detailed reports in MegaLinter artifacts

MegaLinter is graciously provided by OX Security
Show us your support by starring ⭐ the repository

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/apis/cluster/v1alpha1/autoscaler.go`:
- Around line 47-54: The IgnoreDaemonsetsUtilization field is using a
comma-delimited jsonschema description, so the text for the Cluster Autoscaler
option gets truncated at the comma in the flag name. Update the Autoscaler
struct field in autoscaler.go to follow the same pattern as the sibling fields
by moving the full description into jsonschema_description instead of embedding
it in jsonschema:"description=...". After changing the tag on
IgnoreDaemonsetsUtilization, regenerate the schema/docs so the updated
description is reflected everywhere.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fa8141b7-d268-43a9-a3e9-83fb1187bc34

📥 Commits

Reviewing files that changed from the base of the PR and between 2e83b42 and 0d6c828.

⛔ Files ignored due to path filters (1)
  • web/ui/src/generated/ksail-config.ts is excluded by !**/generated/**
📒 Files selected for processing (8)
  • charts/ksail-operator/crds/ksail.io_clusters.yaml
  • docs/src/content/docs/configuration/declarative-configuration.mdx
  • pkg/apis/cluster/v1alpha1/autoscaler.go
  • pkg/apis/cluster/v1alpha1/zz_generated.deepcopy.go
  • pkg/svc/chat/docs_generated.go
  • pkg/svc/installer/clusterautoscaler/installer.go
  • pkg/svc/installer/clusterautoscaler/installer_test.go
  • schemas/ksail-config.schema.json
📜 Review details
⏰ Context from checks skipped due to timeout. (17)
  • GitHub Check: 🧪 Test
  • GitHub Check: 🧹 Lint - mega-linter
  • GitHub Check: 🔍 Dead Code Analysis
  • GitHub Check: 🧹 Lint - golangci-lint
  • GitHub Check: 📊 Code Coverage
  • GitHub Check: 📦 Tidy
  • GitHub Check: 🏗️ Build
  • GitHub Check: 🛡️ Vulnerability Scan
  • GitHub Check: ⛵ Operator Chart E2E
  • GitHub Check: 🏠 Home Isolation Guard
  • GitHub Check: 🏗️ Build KSail Binary
  • GitHub Check: 🔍 License Check
  • GitHub Check: 🏗️ Build (Linux)
  • GitHub Check: 🏗️ Build + cask (macOS)
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
🧰 Additional context used
📓 Path-based instructions (2)
docs/src/content/docs/configuration/declarative-configuration.mdx

📄 CodeRabbit inference engine (AGENTS.md)

Keep docs/src/content/docs/configuration/declarative-configuration.mdx synchronized with the generator output; do not hand-edit generated configuration docs.

Files:

  • docs/src/content/docs/configuration/declarative-configuration.mdx
schemas/ksail-config.schema.json

📄 CodeRabbit inference engine (AGENTS.md)

Do not hand-edit schemas/ksail-config.schema.json; regenerate it from the schema generator instead.

Files:

  • schemas/ksail-config.schema.json
🧠 Learnings (1)
📚 Learning: 2026-06-30T02:35:51.945Z
Learnt from: devantler
Repo: devantler-tech/ksail PR: 5561
File: pkg/apis/cluster/v1alpha1/options.go:95-108
Timestamp: 2026-06-30T02:35:51.945Z
Learning: For kubebuilder API fields typed as `metav1.Duration`, avoid using `+kubebuilder:validation:Pattern`. `controller-gen` rejects `Pattern` markers on `metav1.Duration` (even though the field may render as a string). Use a CEL `+kubebuilder:validation:XValidation` rule instead to enforce validation constraints, e.g. on duration format/range, so code generation and schema validation both succeed.

Applied to files:

  • pkg/apis/cluster/v1alpha1/zz_generated.deepcopy.go
  • pkg/apis/cluster/v1alpha1/autoscaler.go
🔇 Additional comments (9)
schemas/ksail-config.schema.json (2)

539-542: 🎯 Functional Correctness

Truncated description — downstream of a source struct-tag bug.

The ignoreDaemonsetsUtilization description is cut off mid-sentence at "--ignore-daemonsets-utilization". This is a generator-output symptom of a comma-splitting issue in the jsonschema tag on IgnoreDaemonsetsUtilization in pkg/apis/cluster/v1alpha1/autoscaler.go; see the root-cause comment there. Per coding guidelines this file should not be hand-edited — fix the source tag and regenerate.


543-550: LGTM!

docs/src/content/docs/configuration/declarative-configuration.mdx (2)

417-417: 🎯 Functional Correctness

Truncated description — downstream of a source struct-tag bug.

Same truncation as schemas/ksail-config.schema.json for ignoreDaemonsetsUtilization, tracing back to the jsonschema tag comma issue in pkg/apis/cluster/v1alpha1/autoscaler.go (see comment there). Per coding guidelines this doc should stay synchronized with the generator output — fix the source and regenerate rather than hand-editing this line.


418-419: LGTM!

pkg/apis/cluster/v1alpha1/autoscaler.go (1)

55-72: LGTM!

pkg/apis/cluster/v1alpha1/zz_generated.deepcopy.go (1)

513-523: LGTM!

charts/ksail-operator/crds/ksail.io_clusters.yaml (1)

115-124: LGTM!

Also applies to: 203-224

pkg/svc/installer/clusterautoscaler/installer.go (1)

151-162: LGTM!

Also applies to: 296-305

pkg/svc/installer/clusterautoscaler/installer_test.go (1)

652-758: LGTM!

Comment thread pkg/apis/cluster/v1alpha1/autoscaler.go Outdated
@github-project-automation github-project-automation Bot moved this from 🫴 Ready to 🏃🏻‍♂️ In Progress in 🌊 Project Board Jul 3, 2026
@github-code-quality

github-code-quality Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: Go

Go / code-coverage/go

The overall coverage in the claude/autoscaler-ig... branch remains at 65%, unchanged from the main branch.

Show a code coverage summary of the most impacted files.
File main e83b3b4 claude/autoscaler-ig... 74ced45 +/-
pkg/svc/provisi...etzner/token.go 77% 73% -4%
pkg/svc/provide...server_retry.go 99% 98% -1%
pkg/cli/cluster...ocal_service.go 92% 92% 0%
pkg/svc/install...er/installer.go 96% 96% 0%

Updated July 03, 2026 18:42 UTC
Code Coverage is in Public Preview. Learn more and provide us with your feedback.

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Assistant

The failing 🏗️ Build (Linux) / 🏗️ Build + cask (macOS) checks are not caused by this PR. They fail on a pre-existing latent break — desktop/go.sum is missing the gopacket checksums that pkg/svc/mirror needs via the replace ksail/v7 => ../ directive (the Desktop App workflow is path-filtered and had not re-run on main since the import landed). Fixed in #5768. Once that merges, updating this branch will make the build green. This PR (autoscaler scale-down knobs) does not touch desktop/, go.sum, or pkg/svc/mirror.

devantler and others added 2 commits July 3, 2026 20:15
…zation

The jsonschema:"description=..." tag is comma-delimited, so the comma in
"--ignore-daemonsets-utilization, off by default" truncated the generated
schema/doc description at that point. Switch to the dedicated
jsonschema_description tag (as the sibling scale-down fields already do) and
regenerate schema + reference docs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@devantler
devantler marked this pull request as ready for review July 3, 2026 19:04
@devantler
devantler merged commit d64a3e0 into main Jul 3, 2026
6 checks passed
@github-project-automation github-project-automation Bot moved this from 🏃🏻‍♂️ In Progress to ✅ Done in 🌊 Project Board Jul 3, 2026
@devantler
devantler deleted the claude/autoscaler-ignore-daemonsets branch July 3, 2026 21:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

1 participant