Implement target selection, autodiscovery via aggregator, implement listTargets
This commit is contained in:
@@ -76,6 +76,7 @@ type PageData struct {
|
||||
Breadcrumbs []Crumb
|
||||
Windows []Tab
|
||||
GroupBys []Tab
|
||||
Targets []Tab // source/target picker; empty when only one target available
|
||||
RefreshSecs int
|
||||
Error string
|
||||
FilterExpr string // current filter serialised to mini-language for the input box
|
||||
@@ -340,6 +341,38 @@ func buildGroupByTabs(p QueryParams) []Tab {
|
||||
return tabs
|
||||
}
|
||||
|
||||
// buildTargetTabs builds the source/target picker tabs from a ListTargets response.
|
||||
// Returns nil (hide picker) when only one endpoint is reachable.
|
||||
func (h *Handler) buildTargetTabs(p QueryParams, lt *pb.ListTargetsResponse) []Tab {
|
||||
// "all" always points at the configured aggregator default.
|
||||
allTab := Tab{
|
||||
Label: "all",
|
||||
URL: p.buildURL(map[string]string{"target": h.defaultTarget}),
|
||||
Active: p.Target == h.defaultTarget,
|
||||
}
|
||||
|
||||
var collectorTabs []Tab
|
||||
if lt != nil {
|
||||
for _, t := range lt.Targets {
|
||||
addr := t.Addr
|
||||
if addr == "" {
|
||||
addr = p.Target // collector reporting itself; addr is the current target
|
||||
}
|
||||
collectorTabs = append(collectorTabs, Tab{
|
||||
Label: t.Name,
|
||||
URL: p.buildURL(map[string]string{"target": addr}),
|
||||
Active: p.Target == addr,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Only render the picker when there is more than one choice.
|
||||
if len(collectorTabs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return append([]Tab{allTab}, collectorTabs...)
|
||||
}
|
||||
|
||||
func buildTableRows(entries []*pb.TopNEntry, p QueryParams) ([]TableRow, int64) {
|
||||
if len(entries) == 0 {
|
||||
return nil, 0
|
||||
@@ -410,6 +443,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
topNCh := make(chan topNResult, 1)
|
||||
trendCh := make(chan trendResult, 1)
|
||||
ltCh := make(chan *pb.ListTargetsResponse, 1)
|
||||
|
||||
go func() {
|
||||
resp, err := client.TopN(ctx, &pb.TopNRequest{
|
||||
@@ -427,9 +461,18 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
trendCh <- trendResult{resp, err}
|
||||
}()
|
||||
go func() {
|
||||
resp, err := client.ListTargets(ctx, &pb.ListTargetsRequest{})
|
||||
if err != nil {
|
||||
ltCh <- nil
|
||||
} else {
|
||||
ltCh <- resp
|
||||
}
|
||||
}()
|
||||
|
||||
tn := <-topNCh
|
||||
tr := <-trendCh
|
||||
lt := <-ltCh
|
||||
|
||||
if tn.err != nil {
|
||||
h.render(w, http.StatusBadGateway, h.errorPage(params,
|
||||
@@ -459,6 +502,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
Breadcrumbs: buildCrumbs(params),
|
||||
Windows: buildWindowTabs(params),
|
||||
GroupBys: buildGroupByTabs(params),
|
||||
Targets: h.buildTargetTabs(params, lt),
|
||||
RefreshSecs: h.refreshSecs,
|
||||
FilterExpr: filterExprInput,
|
||||
FilterErr: filterErr,
|
||||
|
||||
@@ -34,6 +34,8 @@ a:hover { text-decoration: underline; }
|
||||
.error { color: #c00; border: 1px solid #fbb; background: #fff5f5; padding: 0.7em 1em; margin: 1em 0; border-radius: 3px; }
|
||||
.nodata { color: #999; margin: 2em 0; font-style: italic; }
|
||||
footer { margin-top: 2em; padding-top: 0.6em; border-top: 1px solid #e0e0e0; font-size: 0.8em; color: #999; }
|
||||
.tabs-targets { margin-top: -0.4em; }
|
||||
.tabs-label { font-size: 0.85em; color: #888; margin-right: 0.2em; align-self: center; }
|
||||
.filter-form { display: flex; gap: 0.4em; align-items: center; margin-bottom: 0.7em; }
|
||||
.filter-input { flex: 1; font-family: monospace; font-size: 13px; padding: 0.25em 0.5em; border: 1px solid #aaa; }
|
||||
.filter-form button { padding: 0.25em 0.8em; border: 1px solid #aaa; background: #f4f4f4; cursor: pointer; font-family: monospace; }
|
||||
|
||||
@@ -13,6 +13,13 @@
|
||||
{{- end}}
|
||||
</div>
|
||||
|
||||
{{if .Targets}}<div class="tabs tabs-targets">
|
||||
<span class="tabs-label">source:</span>
|
||||
{{- range .Targets}}
|
||||
<a href="{{.URL}}"{{if .Active}} class="active"{{end}}>{{.Label}}</a>
|
||||
{{- end}}
|
||||
</div>{{end}}
|
||||
|
||||
<form class="filter-form" method="get" action="/">
|
||||
<input type="hidden" name="target" value="{{.Params.Target}}">
|
||||
<input type="hidden" name="w" value="{{.Params.WindowS}}">
|
||||
|
||||
Reference in New Issue
Block a user