Repository navigation
feat: config-bounded targeting query (ADR 0011, slice 6b) - #33
Merged
Merged
Conversation
The node asks the controller which workloads to profile; the controller answers the top-N by consumption from its own rollups. This is the one node<->controller endpoint whose reply carries data the node acts on — a deliberate, config-bounded exception to ADR 0010 §1's ack-only discipline (ADR 0011 §3). It does not break invariant 1: the reply is workload identifiers derived from the cluster's own metrics, never a command; the external backend is untouched; and the node's ConfigMap — not the reply — bounds what may be profiled. The published list is already filtered to the eligible set and capped at TopN, and the node re-checks eligibility before acting (slice 6c). - internal/targeting: a Publisher fed by the usage poller's snapshot callback. It ranks eligible workloads by CPU consumption and publishes the top N into an atomic pointer. This exists to keep the query handler off the single-owner, unsynchronized Accumulator: the poller passes an already-deep-copied snapshot, and the handler reads only the atomically published result. - internal/nodeintake: Target / TargetsResponse wire types, a TargetSource interface, and a TargetsHandler on /v1/node-targets (same token auth, same port). NewServer now mounts three routes; the targets route is present only when a source is configured. - cmd/agent: the publisher is wired into the snapshot callback and gated on profiling being enabled; a nil-safe TargetSource keeps the route absent otherwise. A targetsClient (node side) queries the endpoint and decodes the reply; it is exercised by tests and driven by the live pipeline in slice 6c. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Slice 6b (plan, ADR 0011). The node asks the controller which workloads to profile; the controller answers the top-N by consumption from its rollups.
The carve-out, kept honest
This is the one node↔controller endpoint whose reply carries data the node acts on — a deliberate, config-bounded exception to ADR 0010 §1's ack-only discipline (ADR 0011 §3). It does not break invariant 1: the reply is workload identifiers derived from the cluster's own metrics, never a command; the external backend is untouched; and the node's ConfigMap — not the reply — bounds what may be profiled. The published list is already filtered to the eligible set and capped at TopN, and the node re-checks eligibility before acting (6c). Worst case for a rogue controller: reorder already-permitted targets within already-set ceilings.
Don't race the Accumulator
internal/targeting.Publisheris fed by the usage poller's snapshot callback (records already deep-copied), ranks eligible workloads byCPU.CoreNanoseconds, and publishes the top N into anatomic.Pointer. The handler reads only the atomic snapshot — never the single-owner, unsynchronized Accumulator.Pieces
internal/targeting: thePublisher(rank → filter eligible → clamp TopN → publish).internal/nodeintake:Target/TargetsResponsewire types, aTargetSourceinterface, andTargetsHandleron/v1/node-targets(same token auth, same port).NewServernow mounts three routes; targets is present only when a source is configured.cmd/agent: publisher wired into the snapshot callback, gated onprofiling.enabled(nil-safeTargetSourcekeeps the route absent otherwise); a node-sidetargetsClientthat queries + decodes the reply (driven by the live pipeline in 6c).Tests
Publisher: ranks + eligible filter + TopN clamp + per-workload sum + empty-eligible admits none + nil-before-publish. Handler: valid → 200 + snapshot; bad token → 401; GET → 405. Client: POST +
Bearerround-trip; nil when no endpoint; non-2xx → error.go test -race ./..., build darwin +GOOS=linux,golangci-lint ./...— all clean.Next
6c — the live pipeline: capture → group-by-container (cgroup resolve) → filter → serialize → validate → ship, on the cadence (duration / rotation / overhead), consuming the targets and the config.
🤖 Generated with Claude Code