Skip to content

feat(haproxy): filter dashboard tiles by agent haproxy capability - #115

Merged
sarg3nt merged 3 commits into
mainfrom
fix/issue-112-haproxy-tile-capabilities
May 17, 2026
Merged

feat(haproxy): filter dashboard tiles by agent haproxy capability#115
sarg3nt merged 3 commits into
mainfrom
fix/issue-112-haproxy-tile-capabilities

Conversation

@sarg3nt

@sarg3nt sarg3nt commented May 17, 2026

Copy link
Copy Markdown
Owner

Summary

  • The HAProxy dashboard (/haproxy) attaches hx-get="/htmx/{box}/stats" + /metrics to every enabled server. On a box whose agent has no haproxy gear (e.g. container-mode agent on TrueNAS — issue Capability-driven autoconfig: every gear page must honor the active box's actual capabilities (umbrella) #112), those polls return 503 No stats available forever and the tiles stay empty. Light-hugger's deployment exhibits this whenever Mjolnir is in the box roster.
  • Plumb the framework Handler.CapabilitiesCache through into the ServerAdapter and expose GetEnabledServersWithGearAvailable(gearName). The HAProxy gear's overview + status-grid handlers now filter through it, so boxes whose probe table marks haproxy as unavailable simply don't appear on the page.
  • Fail-open: a box whose capabilities can't be fetched (agent unreachable, no API key, TLS verify off) is still included, matching how filterGearsByAgentCapabilities handles the same case. A transient agent outage doesn't make the page go dark.
  • Default landing route per box (the other half of slice 3 in the roadmap) is left for a follow-up — this PR is scoped to the tile-filtering symptom.

Phase 2 slice of #112.

Test plan

  • go build ./... clean
  • go vet ./... clean
  • go test -count=1 ./internal/framework/services/... ./internal/framework/handler/... ./internal/framework/agent/... ./internal/gears/haproxy/... clean
  • On a deployment with a mixed box roster (one HAProxy host + one non-HAProxy agent), confirm the non-HAProxy box no longer appears on /haproxy and the 503 burst on /htmx/{nonHAProxy}/stats is gone.
  • Confirm the page still renders for an agent that's temporarily unreachable (TLS verify off / agent down) — fail-open keeps the box visible.

🤖 Generated with Claude Code

The HAProxy dashboard at /haproxy iterates every enabled server and
attaches `hx-get="/htmx/{box}/stats"` + `/metrics` to each row. On a
box whose agent has no haproxy gear (e.g. container-mode agent on
TrueNAS — see issue #112), those polls return 503 forever and the
tiles stay empty. Light-hugger's deployment exhibits this whenever
mjolnir is in the box roster.

Plumb the framework Handler's CapabilitiesCache through into the
ServerAdapter (wired in main.go via the new SetCapabilitiesCache /
CapabilitiesCache pair) and add GetEnabledServersWithGearAvailable on
the adapter. The HAProxy gear's overview + status-grid handlers now
filter through that, so boxes whose probe table marks haproxy as
unavailable just don't appear on the page.

Fail-open contract: an agent whose capabilities can't be fetched
(unreachable, no API key, TLS verify off) is still included, matching
how filterGearsByAgentCapabilities handles the same case. A transient
agent outage doesn't make the page go dark.

Default landing route per box (the other half of slice 3 in the
roadmap) is left for a follow-up — this PR is scoped to the tile-
filtering symptom.

Phase 2 slice 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 05:46

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

This PR updates the HAProxy dashboard to avoid rendering (and continuously polling) HAProxy tiles for boxes whose agents report that the haproxy gear is unavailable, using the dashboard’s existing agent capability probe-table cache as the source of truth.

Changes:

  • Plumbs the handler’s shared CapabilitiesCache into ServerAdapter and adds GetEnabledServersWithGearAvailable(gearName) for capability-aware box filtering.
  • Updates HAProxy overview + status-grid handlers to use the new adapter method to filter out non-HAProxy-capable boxes (fail-open when capabilities can’t be fetched).
  • Exposes Handler.CapabilitiesCache() and wires it in cmd/server/main.go during app startup.

Reviewed changes

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

File Description
gearbox/internal/gears/haproxy/handlers.go Filters HAProxy pages to boxes whose agents report haproxy available.
gearbox/internal/framework/services/server_adapter.go Adds optional capabilities cache + filtering method for enabled servers by agent gear availability.
gearbox/internal/framework/handler/handler.go Exposes the handler’s shared capabilities cache via an accessor.
gearbox/cmd/server/main.go Wires the handler capabilities cache into the server adapter for use by gear plugins.
Comments suppressed due to low confidence (1)

gearbox/internal/gears/haproxy/handlers.go:36

  • OverviewPage now bases the “no servers configured” redirect on the filtered HAProxy-capable list. If boxes are configured but none advertises the haproxy capability, this will redirect to /settings/boxes (which is misleading) and makes it impossible to show a “no HAProxy-capable boxes” empty state. Consider distinguishing between (a) zero enabled boxes vs (b) zero HAProxy-capable boxes, and render an explicit empty-state for (b) (or redirect to a more relevant settings page).
	servers := h.getHAProxyServers()

	// If no servers configured, redirect to servers settings page
	if len(servers) == 0 {
		http.Redirect(w, r, "/settings/boxes", http.StatusSeeOther)
		return

Comment thread gearbox/internal/framework/services/server_adapter.go Outdated
Comment thread gearbox/internal/framework/services/server_adapter.go
…ear list (#112)

Two Copilot review findings on the original capability-aware filter:

  1. Doc inaccuracy: the example agent-gear list included "services",
     but the agent has no `services` gear (the /api/v1/services
     endpoint is served by the `metrics` gear). Replace with the
     correct list and call out that some dashboard gears (services,
     alerts, bx) don't have agent counterparts and shouldn't be
     filtered via this helper.

  2. O(N * fetchTimeout) cold-cache latency: synchronous fetches
     could stall a page render to N × fetchTimeout when multiple
     agents are unreachable. Parallelize across boxes with one
     goroutine per box and a sync.WaitGroup join, so cold-cache
     renders pay one round of timeout at most. Warm-cache renders
     hit the cache's read-lock and stay sub-millisecond. Box order
     is preserved by writing into a positional `keep []bool` and
     compacting after the wait.

`go test -race` clean on services/handler/agent/haproxy packages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…oxy boxes (#112) (#118)

Previously, OverviewPage redirected to /settings/boxes whenever its
server list was empty. After the haproxy-capability filter from the
parent commit, that list can be empty for two distinct reasons:

  1. No enabled boxes at all — operator hasn't configured anything.
  2. Enabled boxes exist, but none advertise the haproxy gear (e.g.
     a TrueNAS host running gearbox-agent in a container without
     HAProxy installed).

Redirecting on case 2 is hostile UX: the operator is on /haproxy on
purpose and gets bounced away with no explanation. Render an inline
InfoAlert instead, pointing them at the Bx fleet view and the boxes
settings page so they can either fix the agent or add a different
host.

Touches the Overview templ to accept an optional emptyReason string;
existing call site is the only one and is updated in this commit.
StatusGridPage gets the same redirect-vs-render treatment (its
empty-state markup is the template's own concern).

Phase 2 slice of #112. Stacked on fix/issue-112-haproxy-tile-capabilities
(PR #115).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 17, 2026 15:27
@sarg3nt
sarg3nt merged commit a0faaca into main May 17, 2026
18 checks passed

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

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Comment on lines +82 to +90
func (h *Handlers) getServerLists() (all, haproxyCapable []models.BoxConfig) {
serverAdapter, ok := h.deps.Servers.(*services.ServerAdapter)
if !ok {
h.deps.Logger.Error("failed to get server adapter - unexpected type")
return nil
return nil, nil
}
return serverAdapter.GetEnabledServersAsModels()
all = serverAdapter.GetEnabledServersAsModels()
haproxyCapable = serverAdapter.GetEnabledServersWithGearAvailable("haproxy")
return all, haproxyCapable
@sarg3nt
sarg3nt deleted the fix/issue-112-haproxy-tile-capabilities branch May 28, 2026 16:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants