web/api: add GET and DELETE /series endpoints

This commit is contained in:
Fabian Reinartz 2015-06-09 16:09:31 +02:00
parent 0acd44b0e3
commit 7bb7e565a4
2 changed files with 147 additions and 0 deletions

View file

@ -93,6 +93,9 @@ func (api *API) Register(r *route.Router) {
r.Get("/query_range", instr("query_range", api.queryRange))
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 {
@ -180,6 +183,60 @@ func (api *API) labelValues(r *http.Request) (interface{}, *apiError) {
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{}) {
w.WriteHeader(200)
w.Header().Set("Content-Type", "application/json")

View file

@ -195,6 +195,94 @@ func TestEndpoints(t *testing.T) {
},
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 {
@ -227,6 +315,8 @@ func TestEndpoints(t *testing.T) {
if !reflect.DeepEqual(resp, test.response) {
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()
}
}