|
|
|
|
@ -119,6 +119,14 @@ type response struct { |
|
|
|
|
Data interface{} `json:"data,omitempty"` |
|
|
|
|
ErrorType errorType `json:"errorType,omitempty"` |
|
|
|
|
Error string `json:"error,omitempty"` |
|
|
|
|
Warnings []string `json:"warnings,omitempty"` |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type apiFuncResult struct { |
|
|
|
|
data interface{} |
|
|
|
|
err *apiError |
|
|
|
|
warnings storage.Warnings |
|
|
|
|
finalizer func() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Enables cross-site script calls.
|
|
|
|
|
@ -128,7 +136,7 @@ func setCORS(w http.ResponseWriter) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type apiFunc func(r *http.Request) (interface{}, *apiError, func()) |
|
|
|
|
type apiFunc func(r *http.Request) apiFuncResult |
|
|
|
|
|
|
|
|
|
// TSDBAdmin defines the tsdb interfaces used by the v1 API for admin operations.
|
|
|
|
|
type TSDBAdmin interface { |
|
|
|
|
@ -204,16 +212,16 @@ func (api *API) Register(r *route.Router) { |
|
|
|
|
wrap := func(f apiFunc) http.HandlerFunc { |
|
|
|
|
hf := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
setCORS(w) |
|
|
|
|
data, err, finalizer := f(r) |
|
|
|
|
if err != nil { |
|
|
|
|
api.respondError(w, err, data) |
|
|
|
|
} else if data != nil { |
|
|
|
|
api.respond(w, data) |
|
|
|
|
result := f(r) |
|
|
|
|
if result.err != nil { |
|
|
|
|
api.respondError(w, result.err, result.data) |
|
|
|
|
} else if result.data != nil { |
|
|
|
|
api.respond(w, result.data, result.warnings) |
|
|
|
|
} else { |
|
|
|
|
w.WriteHeader(http.StatusNoContent) |
|
|
|
|
} |
|
|
|
|
if finalizer != nil { |
|
|
|
|
finalizer() |
|
|
|
|
if result.finalizer != nil { |
|
|
|
|
result.finalizer() |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
return api.ready(httputil.CompressionHandler{ |
|
|
|
|
@ -258,17 +266,17 @@ type queryData struct { |
|
|
|
|
Stats *stats.QueryStats `json:"stats,omitempty"` |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (api *API) options(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
return nil, nil, nil |
|
|
|
|
func (api *API) options(r *http.Request) apiFuncResult { |
|
|
|
|
return apiFuncResult{nil, nil, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (api *API) query(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
func (api *API) query(r *http.Request) apiFuncResult { |
|
|
|
|
var ts time.Time |
|
|
|
|
if t := r.FormValue("time"); t != "" { |
|
|
|
|
var err error |
|
|
|
|
ts, err = parseTime(t) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorBadData, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
ts = api.now() |
|
|
|
|
@ -279,7 +287,7 @@ func (api *API) query(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
var cancel context.CancelFunc |
|
|
|
|
timeout, err := parseDuration(to) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorBadData, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ctx, cancel = context.WithTimeout(ctx, timeout) |
|
|
|
|
@ -288,12 +296,12 @@ func (api *API) query(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
|
|
|
|
|
qry, err := api.QueryEngine.NewInstantQuery(api.Queryable, r.FormValue("query"), ts) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorBadData, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
res := qry.Exec(ctx) |
|
|
|
|
if res.Err != nil { |
|
|
|
|
return nil, returnAPIError(res.Err), qry.Close |
|
|
|
|
return apiFuncResult{nil, returnAPIError(res.Err), res.Warnings, qry.Close} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Optional stats field in response if parameter "stats" is not empty.
|
|
|
|
|
@ -302,42 +310,42 @@ func (api *API) query(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
qs = stats.NewQueryStats(qry.Stats()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return &queryData{ |
|
|
|
|
return apiFuncResult{&queryData{ |
|
|
|
|
ResultType: res.Value.Type(), |
|
|
|
|
Result: res.Value, |
|
|
|
|
Stats: qs, |
|
|
|
|
}, nil, qry.Close |
|
|
|
|
}, nil, res.Warnings, qry.Close} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (api *API) queryRange(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
func (api *API) queryRange(r *http.Request) apiFuncResult { |
|
|
|
|
start, err := parseTime(r.FormValue("start")) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorBadData, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
end, err := parseTime(r.FormValue("end")) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorBadData, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
if end.Before(start) { |
|
|
|
|
err := errors.New("end timestamp must not be before start time") |
|
|
|
|
return nil, &apiError{errorBadData, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
step, err := parseDuration(r.FormValue("step")) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorBadData, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if step <= 0 { |
|
|
|
|
err := errors.New("zero or negative query resolution step widths are not accepted. Try a positive integer") |
|
|
|
|
return nil, &apiError{errorBadData, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// For safety, limit the number of returned points per timeseries.
|
|
|
|
|
// This is sufficient for 60s resolution for a week or 1h resolution for a year.
|
|
|
|
|
if end.Sub(start)/step > 11000 { |
|
|
|
|
err := errors.New("exceeded maximum resolution of 11,000 points per timeseries. Try decreasing the query resolution (?step=XX)") |
|
|
|
|
return nil, &apiError{errorBadData, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ctx := r.Context() |
|
|
|
|
@ -345,7 +353,7 @@ func (api *API) queryRange(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
var cancel context.CancelFunc |
|
|
|
|
timeout, err := parseDuration(to) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorBadData, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ctx, cancel = context.WithTimeout(ctx, timeout) |
|
|
|
|
@ -354,12 +362,12 @@ func (api *API) queryRange(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
|
|
|
|
|
qry, err := api.QueryEngine.NewRangeQuery(api.Queryable, r.FormValue("query"), start, end, step) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorBadData, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
res := qry.Exec(ctx) |
|
|
|
|
if res.Err != nil { |
|
|
|
|
return nil, returnAPIError(res.Err), qry.Close |
|
|
|
|
return apiFuncResult{nil, returnAPIError(res.Err), res.Warnings, qry.Close} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Optional stats field in response if parameter "stats" is not empty.
|
|
|
|
|
@ -368,11 +376,11 @@ func (api *API) queryRange(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
qs = stats.NewQueryStats(qry.Stats()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return &queryData{ |
|
|
|
|
return apiFuncResult{&queryData{ |
|
|
|
|
ResultType: res.Value.Type(), |
|
|
|
|
Result: res.Value, |
|
|
|
|
Stats: qs, |
|
|
|
|
}, nil, qry.Close |
|
|
|
|
}, nil, res.Warnings, qry.Close} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func returnAPIError(err error) *apiError { |
|
|
|
|
@ -392,39 +400,39 @@ func returnAPIError(err error) *apiError { |
|
|
|
|
return &apiError{errorExec, err} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (api *API) labelNames(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
func (api *API) labelNames(r *http.Request) apiFuncResult { |
|
|
|
|
q, err := api.Queryable.Querier(r.Context(), math.MinInt64, math.MaxInt64) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorExec, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorExec, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
defer q.Close() |
|
|
|
|
|
|
|
|
|
names, err := q.LabelNames() |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorExec, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorExec, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
return names, nil, nil |
|
|
|
|
return apiFuncResult{names, nil, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (api *API) labelValues(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
func (api *API) labelValues(r *http.Request) apiFuncResult { |
|
|
|
|
ctx := r.Context() |
|
|
|
|
name := route.Param(ctx, "name") |
|
|
|
|
|
|
|
|
|
if !model.LabelNameRE.MatchString(name) { |
|
|
|
|
return nil, &apiError{errorBadData, fmt.Errorf("invalid label name: %q", name)}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, fmt.Errorf("invalid label name: %q", name)}, nil, nil} |
|
|
|
|
} |
|
|
|
|
q, err := api.Queryable.Querier(ctx, math.MinInt64, math.MaxInt64) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorExec, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorExec, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
defer q.Close() |
|
|
|
|
|
|
|
|
|
vals, err := q.LabelValues(name) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorExec, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorExec, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return vals, nil, nil |
|
|
|
|
return apiFuncResult{vals, nil, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var ( |
|
|
|
|
@ -432,12 +440,12 @@ var ( |
|
|
|
|
maxTime = time.Unix(math.MaxInt64/1000-62135596801, 999999999) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
func (api *API) series(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
func (api *API) series(r *http.Request) apiFuncResult { |
|
|
|
|
if err := r.ParseForm(); err != nil { |
|
|
|
|
return nil, &apiError{errorBadData, fmt.Errorf("error parsing form values: %v", err)}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, fmt.Errorf("error parsing form values: %v", err)}, nil, nil} |
|
|
|
|
} |
|
|
|
|
if len(r.Form["match[]"]) == 0 { |
|
|
|
|
return nil, &apiError{errorBadData, fmt.Errorf("no match[] parameter provided")}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, fmt.Errorf("no match[] parameter provided")}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var start time.Time |
|
|
|
|
@ -445,7 +453,7 @@ func (api *API) series(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
var err error |
|
|
|
|
start, err = parseTime(t) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorBadData, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
start = minTime |
|
|
|
|
@ -456,7 +464,7 @@ func (api *API) series(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
var err error |
|
|
|
|
end, err = parseTime(t) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorBadData, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
end = maxTime |
|
|
|
|
@ -466,40 +474,42 @@ func (api *API) series(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
for _, s := range r.Form["match[]"] { |
|
|
|
|
matchers, err := promql.ParseMetricSelector(s) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorBadData, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
matcherSets = append(matcherSets, matchers) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
q, err := api.Queryable.Querier(r.Context(), timestamp.FromTime(start), timestamp.FromTime(end)) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorExec, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorExec, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
defer q.Close() |
|
|
|
|
|
|
|
|
|
var sets []storage.SeriesSet |
|
|
|
|
var warnings storage.Warnings |
|
|
|
|
for _, mset := range matcherSets { |
|
|
|
|
s, err := q.Select(nil, mset...) |
|
|
|
|
s, err, wrn := q.Select(nil, mset...) //TODO
|
|
|
|
|
warnings = append(warnings, wrn...) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorExec, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorExec, err}, warnings, nil} |
|
|
|
|
} |
|
|
|
|
sets = append(sets, s) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
set := storage.NewMergeSeriesSet(sets) |
|
|
|
|
set := storage.NewMergeSeriesSet(sets, nil) |
|
|
|
|
metrics := []labels.Labels{} |
|
|
|
|
for set.Next() { |
|
|
|
|
metrics = append(metrics, set.At().Labels()) |
|
|
|
|
} |
|
|
|
|
if set.Err() != nil { |
|
|
|
|
return nil, &apiError{errorExec, set.Err()}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorExec, set.Err()}, warnings, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return metrics, nil, nil |
|
|
|
|
return apiFuncResult{metrics, nil, warnings, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (api *API) dropSeries(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
return nil, &apiError{errorInternal, fmt.Errorf("not implemented")}, nil |
|
|
|
|
func (api *API) dropSeries(r *http.Request) apiFuncResult { |
|
|
|
|
return apiFuncResult{nil, &apiError{errorInternal, fmt.Errorf("not implemented")}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Target has the information for one target.
|
|
|
|
|
@ -528,7 +538,7 @@ type TargetDiscovery struct { |
|
|
|
|
DroppedTargets []*DroppedTarget `json:"droppedTargets"` |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (api *API) targets(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
func (api *API) targets(r *http.Request) apiFuncResult { |
|
|
|
|
flatten := func(targets map[string][]*scrape.Target) []*scrape.Target { |
|
|
|
|
var n int |
|
|
|
|
keys := make([]string, 0, len(targets)) |
|
|
|
|
@ -570,7 +580,7 @@ func (api *API) targets(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
DiscoveredLabels: t.DiscoveredLabels().Map(), |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
return res, nil, nil |
|
|
|
|
return apiFuncResult{res, nil, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func matchLabels(lset labels.Labels, matchers []*labels.Matcher) bool { |
|
|
|
|
@ -582,18 +592,18 @@ func matchLabels(lset labels.Labels, matchers []*labels.Matcher) bool { |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (api *API) targetMetadata(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
func (api *API) targetMetadata(r *http.Request) apiFuncResult { |
|
|
|
|
limit := -1 |
|
|
|
|
if s := r.FormValue("limit"); s != "" { |
|
|
|
|
var err error |
|
|
|
|
if limit, err = strconv.Atoi(s); err != nil { |
|
|
|
|
return nil, &apiError{errorBadData, fmt.Errorf("limit must be a number")}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, fmt.Errorf("limit must be a number")}, nil, nil} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
matchers, err := promql.ParseMetricSelector(r.FormValue("match_target")) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorBadData, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
metric := r.FormValue("metric") |
|
|
|
|
@ -633,9 +643,9 @@ func (api *API) targetMetadata(r *http.Request) (interface{}, *apiError, func()) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if len(res) == 0 { |
|
|
|
|
return nil, &apiError{errorNotFound, errors.New("specified metadata not found")}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorNotFound, errors.New("specified metadata not found")}, nil, nil} |
|
|
|
|
} |
|
|
|
|
return res, nil, nil |
|
|
|
|
return apiFuncResult{res, nil, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type metricMetadata struct { |
|
|
|
|
@ -657,7 +667,7 @@ type AlertmanagerTarget struct { |
|
|
|
|
URL string `json:"url"` |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (api *API) alertmanagers(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
func (api *API) alertmanagers(r *http.Request) apiFuncResult { |
|
|
|
|
urls := api.alertmanagerRetriever.Alertmanagers() |
|
|
|
|
droppedURLS := api.alertmanagerRetriever.DroppedAlertmanagers() |
|
|
|
|
ams := &AlertmanagerDiscovery{ActiveAlertmanagers: make([]*AlertmanagerTarget, len(urls)), DroppedAlertmanagers: make([]*AlertmanagerTarget, len(droppedURLS))} |
|
|
|
|
@ -667,7 +677,7 @@ func (api *API) alertmanagers(r *http.Request) (interface{}, *apiError, func()) |
|
|
|
|
for i, url := range droppedURLS { |
|
|
|
|
ams.DroppedAlertmanagers[i] = &AlertmanagerTarget{URL: url.String()} |
|
|
|
|
} |
|
|
|
|
return ams, nil, nil |
|
|
|
|
return apiFuncResult{ams, nil, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// AlertDiscovery has info for all active alerts.
|
|
|
|
|
@ -684,7 +694,7 @@ type Alert struct { |
|
|
|
|
Value float64 `json:"value"` |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (api *API) alerts(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
func (api *API) alerts(r *http.Request) apiFuncResult { |
|
|
|
|
alertingRules := api.rulesRetriever.AlertingRules() |
|
|
|
|
alerts := []*Alert{} |
|
|
|
|
|
|
|
|
|
@ -697,7 +707,7 @@ func (api *API) alerts(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
|
|
|
|
|
res := &AlertDiscovery{Alerts: alerts} |
|
|
|
|
|
|
|
|
|
return res, nil, nil |
|
|
|
|
return apiFuncResult{res, nil, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func rulesAlertsToAPIAlerts(rulesAlerts []*rules.Alert) []*Alert { |
|
|
|
|
@ -756,7 +766,7 @@ type recordingRule struct { |
|
|
|
|
Type string `json:"type"` |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (api *API) rules(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
func (api *API) rules(r *http.Request) apiFuncResult { |
|
|
|
|
ruleGroups := api.rulesRetriever.RuleGroups() |
|
|
|
|
res := &RuleDiscovery{RuleGroups: make([]*RuleGroup, len(ruleGroups))} |
|
|
|
|
for i, grp := range ruleGroups { |
|
|
|
|
@ -799,29 +809,29 @@ func (api *API) rules(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
} |
|
|
|
|
default: |
|
|
|
|
err := fmt.Errorf("failed to assert type of rule '%v'", rule.Name()) |
|
|
|
|
return nil, &apiError{errorInternal, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorInternal, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
apiRuleGroup.Rules = append(apiRuleGroup.Rules, enrichedRule) |
|
|
|
|
} |
|
|
|
|
res.RuleGroups[i] = apiRuleGroup |
|
|
|
|
} |
|
|
|
|
return res, nil, nil |
|
|
|
|
return apiFuncResult{res, nil, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type prometheusConfig struct { |
|
|
|
|
YAML string `json:"yaml"` |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (api *API) serveConfig(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
func (api *API) serveConfig(r *http.Request) apiFuncResult { |
|
|
|
|
cfg := &prometheusConfig{ |
|
|
|
|
YAML: api.config().String(), |
|
|
|
|
} |
|
|
|
|
return cfg, nil, nil |
|
|
|
|
return apiFuncResult{cfg, nil, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (api *API) serveFlags(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
return api.flagsMap, nil, nil |
|
|
|
|
func (api *API) serveFlags(r *http.Request) apiFuncResult { |
|
|
|
|
return apiFuncResult{api.flagsMap, nil, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (api *API) remoteRead(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
@ -873,7 +883,7 @@ func (api *API) remoteRead(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
set, err := querier.Select(selectParams, filteredMatchers...) |
|
|
|
|
set, err, _ := querier.Select(selectParams, filteredMatchers...) |
|
|
|
|
if err != nil { |
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError) |
|
|
|
|
return |
|
|
|
|
@ -911,20 +921,20 @@ func (api *API) remoteRead(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (api *API) deleteSeries(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
func (api *API) deleteSeries(r *http.Request) apiFuncResult { |
|
|
|
|
if !api.enableAdmin { |
|
|
|
|
return nil, &apiError{errorUnavailable, errors.New("Admin APIs disabled")}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorUnavailable, errors.New("Admin APIs disabled")}, nil, nil} |
|
|
|
|
} |
|
|
|
|
db := api.db() |
|
|
|
|
if db == nil { |
|
|
|
|
return nil, &apiError{errorUnavailable, errors.New("TSDB not ready")}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorUnavailable, errors.New("TSDB not ready")}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := r.ParseForm(); err != nil { |
|
|
|
|
return nil, &apiError{errorBadData, fmt.Errorf("error parsing form values: %v", err)}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, fmt.Errorf("error parsing form values: %v", err)}, nil, nil} |
|
|
|
|
} |
|
|
|
|
if len(r.Form["match[]"]) == 0 { |
|
|
|
|
return nil, &apiError{errorBadData, fmt.Errorf("no match[] parameter provided")}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, fmt.Errorf("no match[] parameter provided")}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var start time.Time |
|
|
|
|
@ -932,7 +942,7 @@ func (api *API) deleteSeries(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
var err error |
|
|
|
|
start, err = parseTime(t) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorBadData, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
start = minTime |
|
|
|
|
@ -943,7 +953,7 @@ func (api *API) deleteSeries(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
var err error |
|
|
|
|
end, err = parseTime(t) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorBadData, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
end = maxTime |
|
|
|
|
@ -952,7 +962,7 @@ func (api *API) deleteSeries(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
for _, s := range r.Form["match[]"] { |
|
|
|
|
matchers, err := promql.ParseMetricSelector(s) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorBadData, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var selector tsdbLabels.Selector |
|
|
|
|
@ -961,16 +971,16 @@ func (api *API) deleteSeries(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := db.Delete(timestamp.FromTime(start), timestamp.FromTime(end), selector...); err != nil { |
|
|
|
|
return nil, &apiError{errorInternal, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorInternal, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return nil, nil, nil |
|
|
|
|
return apiFuncResult{nil, nil, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (api *API) snapshot(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
func (api *API) snapshot(r *http.Request) apiFuncResult { |
|
|
|
|
if !api.enableAdmin { |
|
|
|
|
return nil, &apiError{errorUnavailable, errors.New("Admin APIs disabled")}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorUnavailable, errors.New("Admin APIs disabled")}, nil, nil} |
|
|
|
|
} |
|
|
|
|
var ( |
|
|
|
|
skipHead bool |
|
|
|
|
@ -979,13 +989,13 @@ func (api *API) snapshot(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
if r.FormValue("skip_head") != "" { |
|
|
|
|
skipHead, err = strconv.ParseBool(r.FormValue("skip_head")) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, &apiError{errorBadData, fmt.Errorf("unable to parse boolean 'skip_head' argument: %v", err)}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorBadData, fmt.Errorf("unable to parse boolean 'skip_head' argument: %v", err)}, nil, nil} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
db := api.db() |
|
|
|
|
if db == nil { |
|
|
|
|
return nil, &apiError{errorUnavailable, errors.New("TSDB not ready")}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorUnavailable, errors.New("TSDB not ready")}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var ( |
|
|
|
|
@ -996,31 +1006,31 @@ func (api *API) snapshot(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
dir = filepath.Join(snapdir, name) |
|
|
|
|
) |
|
|
|
|
if err := os.MkdirAll(dir, 0777); err != nil { |
|
|
|
|
return nil, &apiError{errorInternal, fmt.Errorf("create snapshot directory: %s", err)}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorInternal, fmt.Errorf("create snapshot directory: %s", err)}, nil, nil} |
|
|
|
|
} |
|
|
|
|
if err := db.Snapshot(dir, !skipHead); err != nil { |
|
|
|
|
return nil, &apiError{errorInternal, fmt.Errorf("create snapshot: %s", err)}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorInternal, fmt.Errorf("create snapshot: %s", err)}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return struct { |
|
|
|
|
return apiFuncResult{struct { |
|
|
|
|
Name string `json:"name"` |
|
|
|
|
}{name}, nil, nil |
|
|
|
|
}{name}, nil, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (api *API) cleanTombstones(r *http.Request) (interface{}, *apiError, func()) { |
|
|
|
|
func (api *API) cleanTombstones(r *http.Request) apiFuncResult { |
|
|
|
|
if !api.enableAdmin { |
|
|
|
|
return nil, &apiError{errorUnavailable, errors.New("Admin APIs disabled")}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorUnavailable, errors.New("Admin APIs disabled")}, nil, nil} |
|
|
|
|
} |
|
|
|
|
db := api.db() |
|
|
|
|
if db == nil { |
|
|
|
|
return nil, &apiError{errorUnavailable, errors.New("TSDB not ready")}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorUnavailable, errors.New("TSDB not ready")}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := db.CleanTombstones(); err != nil { |
|
|
|
|
return nil, &apiError{errorInternal, err}, nil |
|
|
|
|
return apiFuncResult{nil, &apiError{errorInternal, err}, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return nil, nil, nil |
|
|
|
|
return apiFuncResult{nil, nil, nil, nil} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func convertMatcher(m *labels.Matcher) tsdbLabels.Matcher { |
|
|
|
|
@ -1075,11 +1085,17 @@ func mergeLabels(primary, secondary []*prompb.Label) []*prompb.Label { |
|
|
|
|
return result |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (api *API) respond(w http.ResponseWriter, data interface{}) { |
|
|
|
|
func (api *API) respond(w http.ResponseWriter, data interface{}, warnings storage.Warnings) { |
|
|
|
|
statusMessage := statusSuccess |
|
|
|
|
var warningStrings []string |
|
|
|
|
for _, warning := range warnings { |
|
|
|
|
warningStrings = append(warningStrings, warning.Error()) |
|
|
|
|
} |
|
|
|
|
json := jsoniter.ConfigCompatibleWithStandardLibrary |
|
|
|
|
b, err := json.Marshal(&response{ |
|
|
|
|
Status: statusSuccess, |
|
|
|
|
Data: data, |
|
|
|
|
Status: statusMessage, |
|
|
|
|
Data: data, |
|
|
|
|
Warnings: warningStrings, |
|
|
|
|
}) |
|
|
|
|
if err != nil { |
|
|
|
|
level.Error(api.logger).Log("msg", "error marshaling json response", "err", err) |
|
|
|
|
|