Skip to content

feat(agent): Resources field on ProbeResult + access-log log_sources - #135

Merged
sarg3nt merged 2 commits into
mainfrom
feature/issue-112-probe-resources
May 17, 2026
Merged

feat(agent): Resources field on ProbeResult + access-log log_sources#135
sarg3nt merged 2 commits into
mainfrom
feature/issue-112-probe-resources

Conversation

@sarg3nt

@sarg3nt sarg3nt commented May 17, 2026

Copy link
Copy Markdown
Owner

Summary

Phase 2 of issue #112 introduces a structured Resources field on agent probe results so the dashboard's capability-driven UI can read concrete resource lists (log sources, services, metric sources) directly from the agent rather than inferring them from gear-availability flags.

  • ProbeResult gains Resources map[string]any, serialized as the resources JSON object on CapabilityEntry. Omitted when empty so older dashboards see the unchanged wire shape.
  • New gear.ProbeAvailableWithResources(reason, capabilities, resources) constructor.
  • access-log gear now publishes log_sources — a slice of {name, display_name, path} for every readable web-server access log it discovers (haproxy / nginx / apache / caddy). The flat <src>_log keys in Capabilities stay for backward compat.

Dashboard-side consumer lives in the sibling PR on sarg3nt/gearbox.

Test plan

  • go build ./..., go vet ./..., go test -count=1 ./... all clean
  • TestProbePopulatesLogSourcesResource covers the structured shape, name→display_name mapping, and absence of unreadable paths
  • TestCapabilitiesEndpointSurfacesResources round-trips Resources through the JSON envelope (decoded via map[string]any to catch any field-name regression on the wire)
  • Deploy to mjolnir, curl /api/v1/system/capabilities, confirm the access-log gear's resources.log_sources shows up with the host's actual access-log paths.

🤖 Generated with Claude Code

…112)

The dashboard's capability-driven UI has been inferring resource lists
(log sources, services, metric sources) from gear-availability flags.
That works for binary "is this gear present?" decisions but breaks
down for "what concrete sources does this gear expose?" — the Logs
page's hardcoded [haproxy, system] fallback was the worst offender.

Phase 2 of issue #112 introduces a structured Resources field the
agent populates per-gear, the dashboard consumes by name.

Agent changes:

  - ProbeResult gains `Resources map[string]any`, serialized as the
    `resources` JSON object on CapabilityEntry. Omitted when empty so
    older dashboards see the unchanged wire shape.
  - New gear.ProbeAvailableWithResources(reason, capabilities, resources)
    constructor for gears that want to publish typed resource lists;
    gears that don't need this keep using ProbeAvailable.
  - access-log gear now publishes `log_sources` — a slice of
    {name, display_name, path} for every readable web-server access
    log it discovers (haproxy / nginx / apache / caddy). The flat
    `<src>_log` keys in Capabilities stay for backward compat with
    pre-Phase-2 dashboards.

Tests:

  - TestProbePopulatesLogSourcesResource on the access-log gear asserts
    the structured shape, name → display_name mapping, and absence of
    entries for non-readable paths.
  - TestCapabilitiesEndpointSurfacesResources on the manager asserts
    Resources round-trips through the JSON envelope at
    /api/v1/system/capabilities — decoded via map[string]any so we
    catch any field-name regression on the wire.

Dashboard-side consumer lands in a follow-up PR (sibling to this one).

Phase 2 of #112.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 17, 2026 17:04
@github-actions github-actions Bot added the agent label May 17, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a structured Resources payload to agent probe results and the capabilities API, enabling the dashboard to drive UI off concrete resource inventories (starting with access-log publishing log_sources) instead of inferring from flat capability flags.

Changes:

  • Extend gear.ProbeResult / CapabilityEntry with Resources map[string]any and add ProbeAvailableWithResources(...).
  • Update access-log probe to publish structured resources.log_sources entries while keeping legacy <src>_log capability keys.
  • Add/extend tests to validate probe population and JSON round-tripping through /api/v1/system/capabilities.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
gearbox-agent/internal/gears/accesslog/plugin.go Publishes structured resources.log_sources alongside legacy capability keys.
gearbox-agent/internal/gears/accesslog/plugin_test.go Adds coverage asserting log_sources structure and filtering of unreadable paths.
gearbox-agent/internal/framework/gear/manager.go Extends capabilities API envelope with resources per gear.
gearbox-agent/internal/framework/gear/manager_probe_test.go Adds test ensuring resources round-trips through capabilities JSON response.
gearbox-agent/internal/framework/gear/interface.go Adds Resources to ProbeResult and introduces ProbeAvailableWithResources.

Comment thread gearbox-agent/internal/gears/accesslog/plugin.go Outdated
Comment thread gearbox-agent/internal/gears/accesslog/plugin.go Outdated
Comment thread gearbox-agent/internal/framework/gear/manager.go
Comment thread gearbox-agent/internal/framework/gear/manager_probe_test.go Outdated
Four review findings, all addressed:

1. Probe was mutating g.paths despite the ProbeableGear contract
   that Probe must be side-effect-free. Refactor: extract
   pathsFromDeps as a shared deps→map helper, add
   resolveLogPathWith(src, overrides) so Probe operates on a local
   override map. Initialize is now the single place g.paths gets
   written.

2. Resources was always populated with a "log_sources" key even when
   no readable log files were found, so the JSON envelope's
   `omitempty` tag never fired and the wire format gained a
   permanent (empty-slice) field. Only attach Resources when
   log_sources actually has entries.

3. Manager.ProbeResults returned a shallow copy — callers could
   mutate nested Capabilities/Resources maps and race against
   manager state under load. Added cloneProbeResult that
   duplicates Capabilities as a fresh map and JSON-round-trips
   Resources (so nested slices and sub-maps come back as fresh
   allocations). JSON round-trip is the same cost the capabilities
   endpoint encoder pays anyway, and falls back to a shallow
   Resources copy if marshaling fails.

4. Test comment claimed "decode through json.RawMessage" but the
   test actually decoded straight into map[string]any. Switch the
   test to a two-stage decode: first peel the outer envelope into
   json.RawMessage per gear (catches field-name regressions in the
   wire format), then decode the resources object into a typed
   shape and assert on payload contents. Avoids Go's `any`
   decoding quirks in the wire-format guard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sarg3nt
sarg3nt merged commit e09943a into main May 17, 2026
22 checks passed
@sarg3nt
sarg3nt deleted the feature/issue-112-probe-resources branch May 17, 2026 19:03
sarg3nt added a commit that referenced this pull request May 17, 2026
The agent-side companion PR adds a structured Resources field to
ProbeResult and has the access-log gear publish a `log_sources` list
(one entry per discovered web-server access log, name + display_name
+ path). This commit teaches the dashboard to read it.

  - agent.CapabilityEntry mirrors the new `resources` JSON field as
    map[string]any so per-gear shapes don't need a shared Go struct.
  - BoxCapabilities.Resource(gearName, key) — small helper to look up
    a single resource without manually chaining Entry/.Resources/map
    indexing.
  - defaultLogSourcesForBox prefers the agent's published log_sources
    when present, falling back to the existing capability heuristic
    for pre-Phase-2 agents and the legacy [haproxy, system] pair when
    capabilities aren't reachable. The path field the agent publishes
    is dropped at this layer — the dashboard doesn't expose log paths
    to the browser.

The resolution chain is now (in priority):
  1. Operator's saved per-box log-source settings (DB)
  2. Agent's structured log_sources resource (Phase 2)
  3. Capability heuristic (pre-Phase-2 fallback)
  4. Legacy [haproxy, system] (agent unreachable / forward-compat)

Tests cover the new path: JSON-decoded shape (production), Go-typed
shape (in-process tests), missing access-log gear, missing resource
key, and five malformed-payload variants (string instead of array,
array of strings, missing fields, empty array). All exercise
logSourcesFromResources directly so the helper's contract is locked
in independently of the surrounding handler.

Phase 2 of #112. Stacked on fix/issue-112-log-sources-capabilities
(PR #116); pairs with feature/issue-112-probe-resources (PR #135)
on the agent.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants