diff --git a/api/prometheus/v1/api.go b/api/prometheus/v1/api.go index b98c075..28cdaef 100644 --- a/api/prometheus/v1/api.go +++ b/api/prometheus/v1/api.go @@ -128,6 +128,7 @@ const ( epLabelValues = apiPrefix + "/label/:name/values" epSeries = apiPrefix + "/series" epTargets = apiPrefix + "/targets" + epTargetsMetadata = apiPrefix + "/targets/metadata" epRules = apiPrefix + "/rules" epSnapshot = apiPrefix + "/admin/tsdb/snapshot" epDeleteSeries = apiPrefix + "/admin/tsdb/delete_series" @@ -151,6 +152,9 @@ type RuleType string // RuleHealth models the health status of a rule. type RuleHealth string +// MetricType models the type of a metric. +type MetricType string + const ( // Possible values for AlertState. AlertStateFiring AlertState = "firing" @@ -179,6 +183,16 @@ const ( RuleHealthGood = "ok" RuleHealthUnknown = "unknown" RuleHealthBad = "err" + + // Possible values for MetricType + MetricTypeCounter MetricType = "counter" + MetricTypeGauge MetricType = "gauge" + MetricTypeHistogram MetricType = "histogram" + MetricTypeGaugeHistogram MetricType = "gaugehistogram" + MetricTypeSummary MetricType = "summary" + MetricTypeInfo MetricType = "info" + MetricTypeStateset MetricType = "stateset" + MetricTypeUnknown MetricType = "unknown" ) // Error is an error returned by the API. @@ -242,6 +256,8 @@ type API interface { Rules(ctx context.Context) (RulesResult, api.Error) // Targets returns an overview of the current state of the Prometheus target discovery. Targets(ctx context.Context) (TargetsResult, api.Error) + // TargetsMetadata returns metadata about metrics currently scraped by the target. + TargetsMetadata(ctx context.Context, matchTarget string, metric string, limit string) ([]MetricMetadata, api.Error) } // AlertsResult contains the result from querying the alerts endpoint. @@ -351,6 +367,15 @@ type DroppedTarget struct { DiscoveredLabels map[string]string `json:"discoveredLabels"` } +// MetricMetadata models the metadata of a metric. +type MetricMetadata struct { + Target map[string]string `json:"target"` + Metric string `json:"metric,omitempty"` + 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"` @@ -760,6 +785,31 @@ func (h *httpAPI) Targets(ctx context.Context) (TargetsResult, api.Error) { return res, api.NewErrorAPI(err, nil) } +func (h *httpAPI) TargetsMetadata(ctx context.Context, matchTarget string, metric string, limit string) ([]MetricMetadata, api.Error) { + u := h.client.URL(epTargetsMetadata, nil) + q := u.Query() + + q.Set("match_target", matchTarget) + 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, api.NewErrorAPI(err, nil) + } + + _, body, apiErr := h.client.Do(ctx, req) + if apiErr != nil { + return nil, apiErr + } + + var res []MetricMetadata + err = json.Unmarshal(body, &res) + return res, api.NewErrorAPI(err, nil) +} + // apiClient wraps a regular client and processes successful API responses. // Successful also includes responses that errored at the API level. type apiClient struct { diff --git a/api/prometheus/v1/api_test.go b/api/prometheus/v1/api_test.go index 9a58272..340a659 100644 --- a/api/prometheus/v1/api_test.go +++ b/api/prometheus/v1/api_test.go @@ -174,6 +174,12 @@ func TestAPIs(t *testing.T) { } } + doTargetsMetadata := func(matchTarget string, metric string, limit string) func() (interface{}, api.Error) { + return func() (interface{}, api.Error) { + return promAPI.TargetsMetadata(context.Background(), matchTarget, metric, limit) + } + } + queryTests := []apiTest{ { do: doQuery("2", testTime), @@ -296,7 +302,7 @@ func TestAPIs(t *testing.T) { "end": []string{testTime.Format(time.RFC3339Nano)}, }, res: []model.LabelSet{ - model.LabelSet{ + { "__name__": "up", "job": "prometheus", "instance": "localhost:9090", @@ -645,6 +651,52 @@ func TestAPIs(t *testing.T) { inErr: fmt.Errorf("some error"), err: fmt.Errorf("some error"), }, + + { + do: doTargetsMetadata("{job=\"prometheus\"}", "go_goroutines", "1"), + inRes: []map[string]interface{}{ + { + "target": map[string]interface{}{ + "instance": "127.0.0.1:9090", + "job": "prometheus", + }, + "type": "gauge", + "help": "Number of goroutines that currently exist.", + "unit": "", + }, + }, + reqMethod: "GET", + reqPath: "/api/v1/targets/metadata", + reqParam: url.Values{ + "match_target": []string{"{job=\"prometheus\"}"}, + "metric": []string{"go_goroutines"}, + "limit": []string{"1"}, + }, + res: []MetricMetadata{ + { + Target: map[string]string{ + "instance": "127.0.0.1:9090", + "job": "prometheus", + }, + Type: "gauge", + Help: "Number of goroutines that currently exist.", + Unit: "", + }, + }, + }, + + { + do: doTargetsMetadata("{job=\"prometheus\"}", "go_goroutines", "1"), + inErr: fmt.Errorf("some error"), + reqMethod: "GET", + reqPath: "/api/v1/targets/metadata", + reqParam: url.Values{ + "match_target": []string{"{job=\"prometheus\"}"}, + "metric": []string{"go_goroutines"}, + "limit": []string{"1"}, + }, + err: fmt.Errorf("some error"), + }, } var tests []apiTest