diff --git a/api/prometheus/v1/api.go b/api/prometheus/v1/api.go index 06319a89f..61f7fb4cb 100644 --- a/api/prometheus/v1/api.go +++ b/api/prometheus/v1/api.go @@ -130,6 +130,7 @@ const ( epSeries = apiPrefix + "/series" epTargets = apiPrefix + "/targets" epTargetsMetadata = apiPrefix + "/targets/metadata" + epMetadata = apiPrefix + "/metadata" epRules = apiPrefix + "/rules" epSnapshot = apiPrefix + "/admin/tsdb/snapshot" epDeleteSeries = apiPrefix + "/admin/tsdb/delete_series" @@ -248,6 +249,8 @@ type API interface { Targets(ctx context.Context) (TargetsResult, error) // TargetsMetadata returns metadata about metrics currently scraped by the target. TargetsMetadata(ctx context.Context, matchTarget string, metric string, limit string) ([]MetricMetadata, error) + // Metadata returns metadata about metrics currently scraped by the metric name. + Metadata(ctx context.Context, metric string, limit string) (map[string][]Metadata, error) } // AlertsResult contains the result from querying the alerts endpoint. @@ -357,7 +360,7 @@ type DroppedTarget struct { DiscoveredLabels map[string]string `json:"discoveredLabels"` } -// MetricMetadata models the metadata of a metric. +// MetricMetadata models the metadata of a metric with its scrape target and name. type MetricMetadata struct { Target map[string]string `json:"target"` Metric string `json:"metric,omitempty"` @@ -366,6 +369,13 @@ type MetricMetadata struct { Unit string `json:"unit"` } +// Metadata models the metadata of a metric. +type Metadata struct { + Type MetricType `json:"type"` + Help string `json:"help"` + Unit string `json:"unit"` +} + // queryResult contains result data for a query. type queryResult struct { Type model.ValueType `json:"resultType"` @@ -802,6 +812,29 @@ func (h *httpAPI) TargetsMetadata(ctx context.Context, matchTarget string, metri return res, json.Unmarshal(body, &res) } +func (h *httpAPI) Metadata(ctx context.Context, metric string, limit string) (map[string][]Metadata, error) { + u := h.client.URL(epMetadata, nil) + q := u.Query() + + q.Set("metric", metric) + q.Set("limit", limit) + + u.RawQuery = q.Encode() + + req, err := http.NewRequest(http.MethodGet, u.String(), nil) + if err != nil { + return nil, err + } + + _, body, _, err := h.client.Do(ctx, req) + if err != nil { + return nil, err + } + + var res map[string][]Metadata + return res, json.Unmarshal(body, &res) +} + // Warnings is an array of non critical errors type Warnings []string diff --git a/api/prometheus/v1/api_test.go b/api/prometheus/v1/api_test.go index b43318e65..79b00a9de 100644 --- a/api/prometheus/v1/api_test.go +++ b/api/prometheus/v1/api_test.go @@ -202,6 +202,13 @@ func TestAPIs(t *testing.T) { } } + doMetadata := func(metric string, limit string) func() (interface{}, Warnings, error) { + return func() (interface{}, Warnings, error) { + v, err := promAPI.Metadata(context.Background(), metric, limit) + return v, nil, err + } + } + queryTests := []apiTest{ { do: doQuery("2", testTime), @@ -857,6 +864,46 @@ func TestAPIs(t *testing.T) { }, err: fmt.Errorf("some error"), }, + + { + do: doMetadata("go_goroutines", "1"), + inRes: map[string]interface{}{ + "go_goroutines": []map[string]interface{}{ + { + "type": "gauge", + "help": "Number of goroutines that currently exist.", + "unit": "", + }, + }, + }, + reqMethod: "GET", + reqPath: "/api/v1/metadata", + reqParam: url.Values{ + "metric": []string{"go_goroutines"}, + "limit": []string{"1"}, + }, + res: map[string][]Metadata{ + "go_goroutines": []Metadata{ + { + Type: "gauge", + Help: "Number of goroutines that currently exist.", + Unit: "", + }, + }, + }, + }, + + { + do: doMetadata("", "1"), + inErr: fmt.Errorf("some error"), + reqMethod: "GET", + reqPath: "/api/v1/metadata", + reqParam: url.Values{ + "metric": []string{""}, + "limit": []string{"1"}, + }, + err: fmt.Errorf("some error"), + }, } var tests []apiTest