refactor: simplify node-agent, fix bugs, cut hot-path work - #65
Open
entlein wants to merge 14 commits into
Open
Conversation
Remove the unwired Prometheus metrics implementation and its two-impl seam (OTEL is the only wired metrics backend), the unused IMDS cloud-metadata path, dead CEL type/helper/converter registration, the unregistered procfs rule adapter, and the vestigial per-container monitor goroutine with its never-read shim/pid maps. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A8UV2B7b6dpDJQci31aC6c
The TaskBasedEnricher/thirdPartyEnricher hook had no implementation and was threaded as nil from main through the container-watcher constructors, the tracer factory, and every tracer. Remove the interface, the parameter and struct fields at all sites, and the enrichEvent helper (tracers now call the event callback directly). This also drops the per-event []uint64 syscall-list allocation the helper required in the open/exec/hardlink/symlink tracers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A8UV2B7b6dpDJQci31aC6c
…imiting - syftutil.NewSource: clone the diff-id slice before reversing it, so the in-place reverse no longer mutates the original that toRootFS and toLayers read, which had paired every layer with another layer's size. - HTTPExporter: set the host field from os.Hostname() so HTTP alerts and FIM reports carry a hostname instead of an empty string. - HTTPExporter rate limiter: suppress every over-limit alert for the rest of the window instead of only the one that crosses the threshold; the limit-reached notification is still emitted once. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A8UV2B7b6dpDJQci31aC6c
wasPathOpened and wasPathOpenedWithFlags ranged the entire Opens.Values set calling CompareDynamic on each entry, although the projection routes every dynamic entry to Patterns and keeps only literal paths in Values (see projection_apply). Query Values by exact map lookup, matching the exec-path rule. On a 500-entry profile this drops the Values check from ~50us to ~260ns per call. Add a benchmark covering the exact-match path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A8UV2B7b6dpDJQci31aC6c
Redirect the shared armoapi-go and utils-k8s-go type modules to the k8sstormcenter builds via replace directives, at versions matching the current pins (armoapi-go v0.0.742, utils-k8s-go v0.0.35). Content is unchanged at these versions; storage moves from v0.0.696/v0.0.30 to the common pin. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A8UV2B7b6dpDJQci31aC6c
MetricsMock had a single consumer (the reconciler test's countingMetrics, which embeds a MetricsManager only to satisfy the interface and overrides the two methods it asserts on). Embed MetricsNoop instead and delete the mock. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A8UV2B7b6dpDJQci31aC6c
fimmanager was a thin pass-through around hostfimsensor whose EnableFIM guard duplicated main's, with dead IsRunning/GetStatus/cloudMetadata surface. Construct the host FIM sensor directly in main and delete the package. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A8UV2B7b6dpDJQci31aC6c
getProcessInfo read /proc/self and os.Getpid(), labelling every fanotify FIM event with node-agent's own pid, comm, and cmdline. Use the fanotify event's Pid (the process that caused the event) and read that process's /proc entry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A8UV2B7b6dpDJQci31aC6c
pprof.Do wrapped the CEL evaluation per rule per event and the handler dispatch per event, allocating a pprof label set on every call solely to attribute CPU profiles. Call the work directly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A8UV2B7b6dpDJQci31aC6c
node-agent's MergeCloudServices duplicated storage's MergeStrings (string set-union). Call MergeStrings, which node-agent already imports, and delete the copy. MergeStrings also returns deterministic order (existing then new uniques) rather than map-iteration order. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A8UV2B7b6dpDJQci31aC6c
…p dead guard - NodeProfileManager.Start's goroutine selected on nothing but time.Sleep, so it ran past context cancellation; select on ctx.Done() during the initial delay and the interval ticker so it exits on shutdown. - HealthManager served http.DefaultServeMux, which the blank net/http/pprof import also populates, so the health port exposed pprof. Serve a private ServeMux with only the liveness/readiness probes. - Remove the unreachable runtime-detection Fatal in the network-tracing else branch (both flags are false there; the true branch already wires DNS when runtime detection is on). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A8UV2B7b6dpDJQci31aC6c
Runtime detection and malware detection each called InitExporters with the same cfg.Exporters, standing up two identical ExporterBus instances (duplicate HTTP clients, alertmanager clients, and bulk-manager goroutines) when both were enabled. Build the bus once and share it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A8UV2B7b6dpDJQci31aC6c
- getContainerInfo: use ContainerCollection.GetContainer for an O(1) lookup and drop the unbounded, never-invalidated containerCache that held stale pointers. - dnsmanager: bound the hot-path reverse lookup with a 2s context so a slow resolver can no longer stall DNS-event processing. - networkstream: run the reverse-DNS and pod-by-IP resolution outside the storage mutex (double-checked) so I/O no longer blocks ingestion; stop the per-container enrich goroutine from retrying forever on context.Background(); and retry the interval send instead of dropping it on a transient error. - host-sensor: scope the kernel-var file Close per file (readKernelVar) instead of deferring every open fd until the whole /proc/sys/kernel walk returns. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A8UV2B7b6dpDJQci31aC6c
The OTEL metrics manager was constructed unconditionally, so every Report*/Set*/Inc* call did work even when nothing consumes it: the default deployment sets prometheusExporterEnabled=false and no OTEL endpoint, which leaves the global providers no-op, yet each call still built its attribute cache key and entered the instrument. Measured on the default config with 20 rules per event, that is ~7.2us and 61 allocations per event. Select the OTEL implementation only when prometheusExporterEnabled is set or an OTEL endpoint/exporter env var is present — the same condition already used to gate Go runtime metrics — and fall back to the existing MetricsNoop otherwise. The MetricsManager interface is write-only, so no behaviour depends on the recorded values. Also skips the startup pod GET that only served the cgroup gauges. The chart maps nodeAgent.config.prometheusExporter=enable to prometheusExporterEnabled, which the benchmark sets, so metric-scraping and profiling runs are unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A8UV2B7b6dpDJQci31aC6c
entlein
force-pushed
the
refactor/simplify
branch
from
August 13, 2026 03:30
c8290dd to
8cfd25e
Compare
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.
Summary
Simplification and bug-fix pass over node-agent: remove dead code and vestigial machinery, fix real correctness/latency bugs, cut per-event work on the detection hot path, and pin the shared type modules to k8sstormcenter builds. 44 files, +257 / −2,387.
What changed
Dead code / vestigial removal
TaskBasedEnricher/thirdPartyEnricherextension point across the watcher, both constructors, the tracer factory and all tracers — also drops a per-event[]uint64allocation.MetricsMock, thefimmanagerpass-through wrapper (folded intomain), and the dead runtime-detectionFatalguard.Bug fixes
HTTPExporterhostname now set; rate limiter suppresses all over-limit alerts; FIM events attributed to the triggering process instead of node-agent itself.getContainerInfouses IG's O(1)GetContainerand drops an unbounded, never-invalidated cache of stale pointers.context.Background(), and the interval send retries instead of dropping on a transient error./proc/sys/kernelwalk returns.NodeProfileManagerloop honors shutdown;HealthManagerserves a private mux so port 7888 no longer exposes pprof.Hot path
wasPathOpened) uses an O(1) exact map lookup on the literalOpens.Values; dynamic/wildcard entries are still matched viaCompareDynamiconOpens.Patterns. ~205× on a 500-entry profile. Per-rule/per-eventpprof.Dolabel allocation removed.Cleanup / dependencies
MergeStrings; pinarmoapi-go/utils-k8s-goto k8sstormcenter builds via replace directives.Performance — CI benchmark (this build vs latest release, real Kind cluster)
Verification
Built and validated through the fork-ci build → component-tests → benchmark chain, all green:
Paired with kubescape/storage
refactor/simplify.