Skip to content

feat(infra): show allocatable capacity and utilization for Kubernetes nodes - #1390

Merged
Makisuo merged 2 commits into
mainfrom
feat/k8s-node-capacity
Oct 10, 2026
Merged

Makisuo merged 2 commits into
mainfrom
feat/k8s-node-capacity

Conversation

@Makisuo

@Makisuo Makisuo commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Infrastructure > Kubernetes > Nodes only showed CPU cores in use (k8s.node.cpu.usage) with no capacity next to it, so nobody, user or agent, could tell whether a node was full.

Receiver facts (contrib v0.151.0, as pinned in deploy/k8s-infra/builder-config.yaml)

Checked against receiver/k8sclusterreceiver/internal/node/nodes.go at v0.151.0:

Metric Type Unit Source
k8s.node.allocatable_cpu gauge, double {cpu} (cores, MilliValue()/1000) k8s_cluster
k8s.node.allocatable_memory gauge, int By k8s_cluster
k8s.node.memory.working_set gauge, int By kubeletstats, on by default

At this version the allocatable metrics are not in metadata.yaml. They are only emitted for the types listed in allocatable_types_to_report, and none are reported by default. The chart already sets allocatableTypesToReport: [cpu, memory] (it has since the chart was added), so no chart change is needed. I added a comment in values.yaml saying the Nodes view depends on that setting.

Changes

  • Queries (packages/query-engine/src/ch/queries/infra.ts)
    • listNodesQuery / nodeDetailSummaryQuery now also return cpuAllocatable, memoryUsage (working set), memoryAllocatable, and cpuUtilization / memoryUtilization (usage / allocatable as a 0-1 fraction). Each is 0 when the value was not collected.
    • nodeGaugeTimeseriesQuery takes an optional capacityMetricName and returns usage / allocatable for each bucket.
    • String attributes use anyIf(attr != ''). Fix: k8s.kubelet.version and k8s.node.uid are set only on k8s_cluster resources, but the old query only scanned kubeletstats metrics, so the kubelet version was always blank. It comes through now.
    • The list has a HAVING countIf(k8s.node.cpu.usage) > 0 clause. Nodes reported only by k8s_cluster (EKS Fargate virtual nodes, nodes without the DaemonSet) stay out, which matches the node facets and the presence probe.
  • API / domain
    • The node response schemas carry the new fields.
    • NodeInfraTimeseriesRequest.metric gains cpu_utilization, memory_usage and memory_utilization, with the units percent and bytes.
  • Web
    • Node list: "CPU / allocatable" and "Memory / allocatable" columns, each with a utilization meter and a "used / allocatable" caption. Both are sortable, and the list sorts by CPU utilization by default.
    • Node detail page:
      • The stat rail shows CPU and memory utilization, tinted by severity.
      • The chart pivot gains CPU / allocatable, Memory, and Memory / allocatable.
      • The infra correlation charts for a node show CPU cores, CPU / allocatable, and Memory / allocatable.
    • When allocatable was not collected, utilization shows an em dash instead of 0%.
  • Benchmarks:
    • Added builder fixtures for listNodesQuery and nodeDetailSummaryQuery and removed both from the exemption list.
    • Added a utilization fixture for nodeGaugeTimeseriesQuery.
    • The SQL baseline diff only adds entries; existing SQL is unchanged.

Follow-up

The MCP infra tools (list_infra, inspect_infra, merged in #1387) don't use these fields yet. The node view still says capacity is not reported. A follow-up can read cpuAllocatable / memoryAllocatable and the utilization fractions from listNodesQuery / nodeDetailSummaryQuery. This PR only updates their fixture world (apps/ai/src/mcp/__evals__/infra-world.ts) so its node rows match the wider row schema.

Testing

  • bunx vitest run on infra.test.ts, benchmark/catalog.test.ts, catalog.baseline.test.ts (query-engine), query-helpers.test.ts (backend), and format.test.ts / infra-correlations.test.ts (web): all pass.
  • Scoped typechecks:
    • @maple/query-engine, @maple/domain and @maple/backend pass.
    • @maple/api and @maple/web report only errors from unbuilt workspace lib dists (@maple-dev/effect-sdk, @maple-dev/alchemy). None are in files this PR touches.
  • Not verified against a live cluster or in the browser.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

… nodes

The Nodes views only showed CPU cores in use, so there was no way to tell
whether a node was full. Node queries now merge k8s_cluster allocatable
CPU/memory with kubelet usage and return usage/allocatable fractions.

- listNodesQuery / nodeDetailSummaryQuery: cpuAllocatable, memoryUsage
  (working set), memoryAllocatable, cpuUtilization, memoryUtilization
- nodeGaugeTimeseriesQuery: optional capacityMetricName for per-bucket
  utilization; node charts gain CPU / allocatable, Memory, Memory / allocatable
- string attributes use anyIf(!= '') so kubelet version and node uid (set only
  by k8s_cluster) are no longer blank
- the list keeps requiring kubelet usage (HAVING) so cluster-only nodes stay
  out, matching the facets
- benchmark fixtures for listNodesQuery and nodeDetailSummaryQuery
@maple-review-bot

maple-review-bot Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Maple review

🟢 Confidence 8/10 · likely safe to merge
No defect found; the new Memory columns rest on kubeletstats emitting k8s.node.memory.working_set, which the chart never enables explicitly and the PR never verified live.
quality 100/100 · no findings · tests partial · risk medium

Node queries now merge k8s_cluster allocatable CPU/memory with kubelet usage and return 0-1 utilization; domain schemas, the API mapper and the Nodes UI carry the new fields. Contained, and the SQL, helper and formatter tests pin the new behavior.

  • listNodesQuery/nodeDetailSummaryQuery return allocatable capacity and utilization fractions
  • nodeGaugeTimeseriesQuery divides usage by capacityMetricName per bucket
  • Nodes list, detail rail and node correlation charts gain CPU and memory capacity
  • Node string attributes read with anyIf(attr != ''), so kubelet version is no longer blank
What was checked
  • Utilization math is guarded by ifNotFinite/ifNull, so a missing allocatable gives 0, never NaN (infra.ts:763)
  • nodeCapacity supplies all seven fields to both domain responses (query-engine.http.ts:300, :1754, :1772)
  • Baseline SQL for the two builders and the utilization series matches the new SELECTs (catalog.sql:1140-1274)

b9c7b35 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@coderabbitai

coderabbitai Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 21 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 702cb135-d0ac-4982-a2d6-f897700d889f

📥 Commits

Reviewing files that changed from the base of the PR and between 6a3ef1e and 613237d.


📒 Files selected for processing (20)
  • apps/ai/src/mcp/__evals__/infra-world.ts
  • apps/api/src/routes/internal/query-engine.http.ts
  • apps/api/src/routes/queries.ts
  • apps/web/src/api/warehouse/infra.ts
  • apps/web/src/components/infra/format.test.ts
  • apps/web/src/components/infra/format.ts
  • apps/web/src/components/infra/infra-correlations.ts
  • apps/web/src/components/infra/k8s-detail-chart.tsx
  • apps/web/src/components/infra/node-table.tsx
  • apps/web/src/routes/infra/kubernetes/nodes/$nodeName.tsx
  • deploy/k8s-infra/values.yaml
  • packages/backend/src/queries/query-helpers.test.ts
  • packages/backend/src/queries/query-helpers.ts
  • packages/domain/src/http/query-engine.ts
  • packages/query-engine/src/__sql_baseline__/catalog.sql
  • packages/query-engine/src/benchmark/builders.ts
  • packages/query-engine/src/benchmark/catalog.test.ts
  • packages/query-engine/src/ch/index.ts
  • packages/query-engine/src/ch/queries/infra.test.ts
  • packages/query-engine/src/ch/queries/infra.ts

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

# Conflicts:
#	packages/backend/src/queries/query-helpers.test.ts
@maple-review-bot

maple-review-bot Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Maple review

🟢 Confidence 9/10 · safe to merge
SQL, spec mapping and formatting are pinned by unit tests and the SQL baseline; only the collector's allocatable metric names are unverifiable here.
quality 100/100 · no findings · tests covered · risk medium

Adds allocatable CPU/memory capacity and usage/allocatable utilization to the Kubernetes Nodes list and node detail: the fractions are computed in ClickHouse, mapped through the domain schemas, and rendered as meters with used/allocatable captions. Contained, consistent across the stack, and safe to merge.

  • listNodesQuery and nodeDetailSummaryQuery now scan k8s_cluster allocatable metrics alongside kubeletstats usage
  • nodeGaugeTimeseriesQuery divides a usage metric by a capacity metric per bucket
  • Node list/detail show utilization meters, sort by CPU utilization, and gain CPU/memory chart pivots
  • k8s.kubelet.version, k8s.node.uid and the cluster name are read with anyIf(attr != ''), so they no longer come back blank
What was checked
  • Fractions are 0-1 in SQL and rendered as unit: "percent" with formatPercent(fraction) (packages/ui/src/lib/format.ts:96)
  • capacityFraction yields NaN when allocatable is 0, so meter and percent show a dash rather than 0% (apps/web/src/components/infra/format.ts:48)
  • The list's new HAVING matches the facet probe metric, so the list and its facets exclude the same nodes (infra.ts:898)

613237d · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@Makisuo
Makisuo merged commit ea6b1ec into main Oct 10, 2026
40 of 41 checks passed
@Makisuo
Makisuo deleted the feat/k8s-node-capacity branch October 10, 2026 23:42
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.

1 participant