mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-25 05:34:05 -08:00
web/api: add GET and DELETE /series endpoints
This commit is contained in:
parent
0acd44b0e3
commit
7bb7e565a4
|
@ -93,6 +93,9 @@ func (api *API) Register(r *route.Router) {
|
||||||
r.Get("/query_range", instr("query_range", api.queryRange))
|
r.Get("/query_range", instr("query_range", api.queryRange))
|
||||||
|
|
||||||
r.Get("/label/:name/values", instr("label_values", api.labelValues))
|
r.Get("/label/:name/values", instr("label_values", api.labelValues))
|
||||||
|
|
||||||
|
r.Get("/series", instr("series", api.series))
|
||||||
|
r.Del("/series", instr("drop_series", api.dropSeries))
|
||||||
}
|
}
|
||||||
|
|
||||||
type queryData struct {
|
type queryData struct {
|
||||||
|
@ -180,6 +183,60 @@ func (api *API) labelValues(r *http.Request) (interface{}, *apiError) {
|
||||||
return vals, nil
|
return vals, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *API) series(r *http.Request) (interface{}, *apiError) {
|
||||||
|
r.ParseForm()
|
||||||
|
if len(r.Form["match[]"]) == 0 {
|
||||||
|
return nil, &apiError{errorBadData, fmt.Errorf("no match[] parameter provided")}
|
||||||
|
}
|
||||||
|
fps := map[clientmodel.Fingerprint]struct{}{}
|
||||||
|
|
||||||
|
for _, lm := range r.Form["match[]"] {
|
||||||
|
matchers, err := promql.ParseMetricSelector(lm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &apiError{errorBadData, err}
|
||||||
|
}
|
||||||
|
for _, fp := range api.Storage.FingerprintsForLabelMatchers(matchers) {
|
||||||
|
fps[fp] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
metrics := make([]clientmodel.Metric, 0, len(fps))
|
||||||
|
for fp := range fps {
|
||||||
|
if met := api.Storage.MetricForFingerprint(fp).Metric; met != nil {
|
||||||
|
metrics = append(metrics, met)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return metrics, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) dropSeries(r *http.Request) (interface{}, *apiError) {
|
||||||
|
r.ParseForm()
|
||||||
|
if len(r.Form["match[]"]) == 0 {
|
||||||
|
return nil, &apiError{errorBadData, fmt.Errorf("no match[] parameter provided")}
|
||||||
|
}
|
||||||
|
fps := map[clientmodel.Fingerprint]struct{}{}
|
||||||
|
|
||||||
|
for _, lm := range r.Form["match[]"] {
|
||||||
|
matchers, err := promql.ParseMetricSelector(lm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &apiError{errorBadData, err}
|
||||||
|
}
|
||||||
|
for _, fp := range api.Storage.FingerprintsForLabelMatchers(matchers) {
|
||||||
|
fps[fp] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for fp := range fps {
|
||||||
|
api.Storage.DropMetricsForFingerprints(fp)
|
||||||
|
}
|
||||||
|
|
||||||
|
res := struct {
|
||||||
|
NumDeleted int `json:"numDeleted"`
|
||||||
|
}{
|
||||||
|
NumDeleted: len(fps),
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
func respond(w http.ResponseWriter, data interface{}) {
|
func respond(w http.ResponseWriter, data interface{}) {
|
||||||
w.WriteHeader(200)
|
w.WriteHeader(200)
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
|
@ -195,6 +195,94 @@ func TestEndpoints(t *testing.T) {
|
||||||
},
|
},
|
||||||
errType: errorBadData,
|
errType: errorBadData,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
endpoint: api.series,
|
||||||
|
query: url.Values{
|
||||||
|
"match[]": []string{`test_metric2`},
|
||||||
|
},
|
||||||
|
response: []clientmodel.Metric{
|
||||||
|
{
|
||||||
|
"__name__": "test_metric2",
|
||||||
|
"foo": "boo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
endpoint: api.series,
|
||||||
|
query: url.Values{
|
||||||
|
"match[]": []string{`test_metric1{foo=~"o$"}`},
|
||||||
|
},
|
||||||
|
response: []clientmodel.Metric{
|
||||||
|
{
|
||||||
|
"__name__": "test_metric1",
|
||||||
|
"foo": "boo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
endpoint: api.series,
|
||||||
|
query: url.Values{
|
||||||
|
"match[]": []string{`test_metric1{foo=~"o$"}`, `test_metric1{foo=~"o$"}`},
|
||||||
|
},
|
||||||
|
response: []clientmodel.Metric{
|
||||||
|
{
|
||||||
|
"__name__": "test_metric1",
|
||||||
|
"foo": "boo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
endpoint: api.series,
|
||||||
|
query: url.Values{
|
||||||
|
"match[]": []string{`test_metric1{foo=~"o$"}`, `none`},
|
||||||
|
},
|
||||||
|
response: []clientmodel.Metric{
|
||||||
|
{
|
||||||
|
"__name__": "test_metric1",
|
||||||
|
"foo": "boo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Missing match[] query params in series requests.
|
||||||
|
{
|
||||||
|
endpoint: api.series,
|
||||||
|
errType: errorBadData,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
endpoint: api.dropSeries,
|
||||||
|
errType: errorBadData,
|
||||||
|
},
|
||||||
|
// The following tests delete time series from the test storage. They
|
||||||
|
// must remain at the end and are fixed in their order.
|
||||||
|
{
|
||||||
|
endpoint: api.dropSeries,
|
||||||
|
query: url.Values{
|
||||||
|
"match[]": []string{`test_metric1{foo=~"o$"}`},
|
||||||
|
},
|
||||||
|
response: struct {
|
||||||
|
NumDeleted int `json:"numDeleted"`
|
||||||
|
}{1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
endpoint: api.series,
|
||||||
|
query: url.Values{
|
||||||
|
"match[]": []string{`test_metric1`},
|
||||||
|
},
|
||||||
|
response: []clientmodel.Metric{
|
||||||
|
{
|
||||||
|
"__name__": "test_metric1",
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
endpoint: api.dropSeries,
|
||||||
|
query: url.Values{
|
||||||
|
"match[]": []string{`{__name__=~".*"}`},
|
||||||
|
},
|
||||||
|
response: struct {
|
||||||
|
NumDeleted int `json:"numDeleted"`
|
||||||
|
}{2},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
@ -227,6 +315,8 @@ func TestEndpoints(t *testing.T) {
|
||||||
if !reflect.DeepEqual(resp, test.response) {
|
if !reflect.DeepEqual(resp, test.response) {
|
||||||
t.Fatalf("Response does not match, expected:\n%#v\ngot:\n%#v", test.response, resp)
|
t.Fatalf("Response does not match, expected:\n%#v\ngot:\n%#v", test.response, resp)
|
||||||
}
|
}
|
||||||
|
// Ensure that removed metrics are unindexed before the next request.
|
||||||
|
suite.Storage().WaitForIndexing()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue