From 7f509dc4f4f7dbe4aefa865080568063c7f6f9bf Mon Sep 17 00:00:00 2001 From: gotjosh Date: Wed, 26 Feb 2020 17:44:09 +0000 Subject: [PATCH 1/4] API Client: Support new metadata endpoint in v1 Introduces support for the new metadata endpoint from Prometheus. The new endpoint provides information independent of targets and collapses the unique combinations of HELP, TYPE and UNIT. Fixes #705 Signed-off-by: gotjosh --- api/prometheus/v1/api.go | 35 +++++++++++++++++++++++++- api/prometheus/v1/api_test.go | 47 +++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/api/prometheus/v1/api.go b/api/prometheus/v1/api.go index 06319a89f..fc375691d 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" + epMetricsMetadata = 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) + // MetricMetadata returns metadata about metrics currently scraped by the metric name. + MetricsMetadata(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) MetricsMetadata(ctx context.Context, metric string, limit string) (map[string][]Metadata, error) { + u := h.client.URL(epMetricsMetadata, 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..21ffa7b9d 100644 --- a/api/prometheus/v1/api_test.go +++ b/api/prometheus/v1/api_test.go @@ -202,6 +202,13 @@ func TestAPIs(t *testing.T) { } } + doMetricsMetadata := func(metring string, limit string) func() (interface{}, Warnings, error) { + return func() (interface{}, Warnings, error) { + v, err := promAPI.MetricsMetadata(context.Background(), metring, 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: doMetricsMetadata("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: doMetricsMetadata("", "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 From bd79fe1bf1ea75d7f80e7bd1a50c0ad53fce9f06 Mon Sep 17 00:00:00 2001 From: gotjosh Date: Thu, 27 Feb 2020 17:04:26 +0000 Subject: [PATCH 2/4] Update api/prometheus/v1/api_test.go Co-Authored-By: Tobias Guggenmos Signed-off-by: gotjosh --- api/prometheus/v1/api_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/prometheus/v1/api_test.go b/api/prometheus/v1/api_test.go index 21ffa7b9d..a956ba95f 100644 --- a/api/prometheus/v1/api_test.go +++ b/api/prometheus/v1/api_test.go @@ -202,7 +202,7 @@ func TestAPIs(t *testing.T) { } } - doMetricsMetadata := func(metring string, limit string) func() (interface{}, Warnings, error) { + doMetricsMetadata := func(metric string, limit string) func() (interface{}, Warnings, error) { return func() (interface{}, Warnings, error) { v, err := promAPI.MetricsMetadata(context.Background(), metring, limit) return v, nil, err From 7810669cc3e8c322f5bbe0436d8b9dc6805a2929 Mon Sep 17 00:00:00 2001 From: gotjosh Date: Thu, 27 Feb 2020 17:04:33 +0000 Subject: [PATCH 3/4] Update api/prometheus/v1/api_test.go Co-Authored-By: Tobias Guggenmos Signed-off-by: gotjosh --- api/prometheus/v1/api_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/prometheus/v1/api_test.go b/api/prometheus/v1/api_test.go index a956ba95f..2f94b32d6 100644 --- a/api/prometheus/v1/api_test.go +++ b/api/prometheus/v1/api_test.go @@ -204,7 +204,7 @@ func TestAPIs(t *testing.T) { doMetricsMetadata := func(metric string, limit string) func() (interface{}, Warnings, error) { return func() (interface{}, Warnings, error) { - v, err := promAPI.MetricsMetadata(context.Background(), metring, limit) + v, err := promAPI.MetricsMetadata(context.Background(), metric, limit) return v, nil, err } } From 2463b8e78dd106f969e9f394727547995e5928e0 Mon Sep 17 00:00:00 2001 From: gotjosh Date: Thu, 27 Feb 2020 17:45:23 +0000 Subject: [PATCH 4/4] Address review feedback Signed-off-by: gotjosh --- api/prometheus/v1/api.go | 10 +++++----- api/prometheus/v1/api_test.go | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/prometheus/v1/api.go b/api/prometheus/v1/api.go index fc375691d..61f7fb4cb 100644 --- a/api/prometheus/v1/api.go +++ b/api/prometheus/v1/api.go @@ -130,7 +130,7 @@ const ( epSeries = apiPrefix + "/series" epTargets = apiPrefix + "/targets" epTargetsMetadata = apiPrefix + "/targets/metadata" - epMetricsMetadata = apiPrefix + "/metadata" + epMetadata = apiPrefix + "/metadata" epRules = apiPrefix + "/rules" epSnapshot = apiPrefix + "/admin/tsdb/snapshot" epDeleteSeries = apiPrefix + "/admin/tsdb/delete_series" @@ -249,8 +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) - // MetricMetadata returns metadata about metrics currently scraped by the metric name. - MetricsMetadata(ctx context.Context, metric string, limit string) (map[string][]Metadata, 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. @@ -812,8 +812,8 @@ func (h *httpAPI) TargetsMetadata(ctx context.Context, matchTarget string, metri return res, json.Unmarshal(body, &res) } -func (h *httpAPI) MetricsMetadata(ctx context.Context, metric string, limit string) (map[string][]Metadata, error) { - u := h.client.URL(epMetricsMetadata, nil) +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) diff --git a/api/prometheus/v1/api_test.go b/api/prometheus/v1/api_test.go index 2f94b32d6..79b00a9de 100644 --- a/api/prometheus/v1/api_test.go +++ b/api/prometheus/v1/api_test.go @@ -202,9 +202,9 @@ func TestAPIs(t *testing.T) { } } - doMetricsMetadata := func(metric string, limit string) func() (interface{}, Warnings, error) { + doMetadata := func(metric string, limit string) func() (interface{}, Warnings, error) { return func() (interface{}, Warnings, error) { - v, err := promAPI.MetricsMetadata(context.Background(), metric, limit) + v, err := promAPI.Metadata(context.Background(), metric, limit) return v, nil, err } } @@ -866,7 +866,7 @@ func TestAPIs(t *testing.T) { }, { - do: doMetricsMetadata("go_goroutines", "1"), + do: doMetadata("go_goroutines", "1"), inRes: map[string]interface{}{ "go_goroutines": []map[string]interface{}{ { @@ -894,7 +894,7 @@ func TestAPIs(t *testing.T) { }, { - do: doMetricsMetadata("", "1"), + do: doMetadata("", "1"), inErr: fmt.Errorf("some error"), reqMethod: "GET", reqPath: "/api/v1/metadata",