| 
									
										
										
										
											2016-04-13 22:08:22 +08:00
										 |  |  | // Copyright 2016 The Prometheus Authors
 | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License");
 | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License.
 | 
					
						
							|  |  |  | // You may obtain a copy of the License at
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software
 | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS,
 | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and
 | 
					
						
							|  |  |  | // limitations under the License.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | package v1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2017-10-25 12:21:42 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2016-12-25 07:37:46 +08:00
										 |  |  | 	"math" | 
					
						
							| 
									
										
										
										
											2017-12-03 13:07:05 +08:00
										 |  |  | 	"math/rand" | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2017-04-25 13:42:33 +08:00
										 |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2017-12-03 13:07:05 +08:00
										 |  |  | 	"os" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2017-10-26 18:44:49 +08:00
										 |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-20 23:18:46 +08:00
										 |  |  | 	"github.com/prometheus/common/model" | 
					
						
							| 
									
										
										
										
											2015-09-24 23:07:11 +08:00
										 |  |  | 	"github.com/prometheus/common/route" | 
					
						
							| 
									
										
										
										
											2017-12-03 13:07:05 +08:00
										 |  |  | 	"github.com/prometheus/tsdb" | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-11 23:09:24 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/config" | 
					
						
							| 
									
										
										
										
											2016-12-29 16:27:30 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/pkg/labels" | 
					
						
							| 
									
										
										
										
											2016-12-30 17:43:44 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/pkg/timestamp" | 
					
						
							| 
									
										
										
										
											2017-10-24 04:28:17 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/prompb" | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/promql" | 
					
						
							| 
									
										
										
										
											2018-02-01 17:55:07 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/scrape" | 
					
						
							| 
									
										
										
										
											2016-12-29 16:27:30 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/storage" | 
					
						
							| 
									
										
										
										
											2017-10-24 04:28:17 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/storage/remote" | 
					
						
							| 
									
										
										
										
											2015-09-17 20:49:50 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/util/httputil" | 
					
						
							| 
									
										
										
										
											2017-02-08 19:58:40 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/util/stats" | 
					
						
							| 
									
										
										
										
											2017-12-03 13:07:05 +08:00
										 |  |  | 	tsdbLabels "github.com/prometheus/tsdb/labels" | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type status string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	statusSuccess status = "success" | 
					
						
							| 
									
										
										
										
											2017-12-02 21:52:43 +08:00
										 |  |  | 	statusError   status = "error" | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type errorType string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2017-12-03 13:07:05 +08:00
										 |  |  | 	errorNone        errorType = "" | 
					
						
							| 
									
										
										
										
											2017-12-04 02:46:10 +08:00
										 |  |  | 	errorTimeout     errorType = "timeout" | 
					
						
							|  |  |  | 	errorCanceled    errorType = "canceled" | 
					
						
							|  |  |  | 	errorExec        errorType = "execution" | 
					
						
							|  |  |  | 	errorBadData     errorType = "bad_data" | 
					
						
							|  |  |  | 	errorInternal    errorType = "internal" | 
					
						
							|  |  |  | 	errorUnavailable errorType = "unavailable" | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 08:32:46 +08:00
										 |  |  | var corsHeaders = map[string]string{ | 
					
						
							|  |  |  | 	"Access-Control-Allow-Headers":  "Accept, Authorization, Content-Type, Origin", | 
					
						
							|  |  |  | 	"Access-Control-Allow-Methods":  "GET, OPTIONS", | 
					
						
							|  |  |  | 	"Access-Control-Allow-Origin":   "*", | 
					
						
							|  |  |  | 	"Access-Control-Expose-Headers": "Date", | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | type apiError struct { | 
					
						
							|  |  |  | 	typ errorType | 
					
						
							|  |  |  | 	err error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *apiError) Error() string { | 
					
						
							|  |  |  | 	return fmt.Sprintf("%s: %s", e.typ, e.err) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-02 20:31:43 +08:00
										 |  |  | type targetRetriever interface { | 
					
						
							| 
									
										
										
										
											2018-02-01 17:55:07 +08:00
										 |  |  | 	Targets() []*scrape.Target | 
					
						
							| 
									
										
										
										
											2018-02-22 01:26:18 +08:00
										 |  |  | 	DroppedTargets() []*scrape.Target | 
					
						
							| 
									
										
										
										
											2016-12-02 20:31:43 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-13 17:20:11 +08:00
										 |  |  | type alertmanagerRetriever interface { | 
					
						
							| 
									
										
										
										
											2017-04-25 13:42:33 +08:00
										 |  |  | 	Alertmanagers() []*url.URL | 
					
						
							| 
									
										
										
										
											2018-02-21 17:00:07 +08:00
										 |  |  | 	DroppedAlertmanagers() []*url.URL | 
					
						
							| 
									
										
										
										
											2016-12-02 20:31:43 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | type response struct { | 
					
						
							|  |  |  | 	Status    status      `json:"status"` | 
					
						
							|  |  |  | 	Data      interface{} `json:"data,omitempty"` | 
					
						
							|  |  |  | 	ErrorType errorType   `json:"errorType,omitempty"` | 
					
						
							|  |  |  | 	Error     string      `json:"error,omitempty"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-12 03:46:57 +08:00
										 |  |  | // Enables cross-site script calls.
 | 
					
						
							|  |  |  | func setCORS(w http.ResponseWriter) { | 
					
						
							| 
									
										
										
										
											2016-01-26 08:32:46 +08:00
										 |  |  | 	for h, v := range corsHeaders { | 
					
						
							|  |  |  | 		w.Header().Set(h, v) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-11-12 03:46:57 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type apiFunc func(r *http.Request) (interface{}, *apiError) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | // API can register a set of endpoints in a router and handle
 | 
					
						
							|  |  |  | // them using the provided storage and query engine.
 | 
					
						
							|  |  |  | type API struct { | 
					
						
							| 
									
										
										
										
											2018-01-10 00:44:23 +08:00
										 |  |  | 	Queryable   storage.Queryable | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	QueryEngine *promql.Engine | 
					
						
							| 
									
										
										
										
											2015-06-09 03:19:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-13 17:20:11 +08:00
										 |  |  | 	targetRetriever       targetRetriever | 
					
						
							|  |  |  | 	alertmanagerRetriever alertmanagerRetriever | 
					
						
							| 
									
										
										
										
											2016-12-02 20:31:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												api: Added v1/status/flags endpoint. (#3864)
Endpoint URL: /api/v1/status/flags
Example Output:
```json
{
  "status": "success",
  "data": {
    "alertmanager.notification-queue-capacity": "10000",
    "alertmanager.timeout": "10s",
    "completion-bash": "false",
    "completion-script-bash": "false",
    "completion-script-zsh": "false",
    "config.file": "my_cool_prometheus.yaml",
    "help": "false",
    "help-long": "false",
    "help-man": "false",
    "log.level": "info",
    "query.lookback-delta": "5m",
    "query.max-concurrency": "20",
    "query.timeout": "2m",
    "storage.tsdb.max-block-duration": "36h",
    "storage.tsdb.min-block-duration": "2h",
    "storage.tsdb.no-lockfile": "false",
    "storage.tsdb.path": "data/",
    "storage.tsdb.retention": "15d",
    "version": "false",
    "web.console.libraries": "console_libraries",
    "web.console.templates": "consoles",
    "web.enable-admin-api": "false",
    "web.enable-lifecycle": "false",
    "web.external-url": "",
    "web.listen-address": "0.0.0.0:9090",
    "web.max-connections": "512",
    "web.read-timeout": "5m",
    "web.route-prefix": "/",
    "web.user-assets": ""
  }
}
```
Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
											
										 
											2018-02-21 16:49:02 +08:00
										 |  |  | 	now      func() time.Time | 
					
						
							|  |  |  | 	config   func() config.Config | 
					
						
							|  |  |  | 	flagsMap map[string]string | 
					
						
							|  |  |  | 	ready    func(http.HandlerFunc) http.HandlerFunc | 
					
						
							| 
									
										
										
										
											2017-12-03 13:07:05 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	db          func() *tsdb.DB | 
					
						
							|  |  |  | 	enableAdmin bool | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-12 03:46:57 +08:00
										 |  |  | // NewAPI returns an initialized API type.
 | 
					
						
							| 
									
										
										
										
											2017-10-06 23:20:20 +08:00
										 |  |  | func NewAPI( | 
					
						
							|  |  |  | 	qe *promql.Engine, | 
					
						
							| 
									
										
										
										
											2018-01-10 00:44:23 +08:00
										 |  |  | 	q storage.Queryable, | 
					
						
							| 
									
										
										
										
											2017-10-06 23:20:20 +08:00
										 |  |  | 	tr targetRetriever, | 
					
						
							|  |  |  | 	ar alertmanagerRetriever, | 
					
						
							|  |  |  | 	configFunc func() config.Config, | 
					
						
							| 
									
										
											  
											
												api: Added v1/status/flags endpoint. (#3864)
Endpoint URL: /api/v1/status/flags
Example Output:
```json
{
  "status": "success",
  "data": {
    "alertmanager.notification-queue-capacity": "10000",
    "alertmanager.timeout": "10s",
    "completion-bash": "false",
    "completion-script-bash": "false",
    "completion-script-zsh": "false",
    "config.file": "my_cool_prometheus.yaml",
    "help": "false",
    "help-long": "false",
    "help-man": "false",
    "log.level": "info",
    "query.lookback-delta": "5m",
    "query.max-concurrency": "20",
    "query.timeout": "2m",
    "storage.tsdb.max-block-duration": "36h",
    "storage.tsdb.min-block-duration": "2h",
    "storage.tsdb.no-lockfile": "false",
    "storage.tsdb.path": "data/",
    "storage.tsdb.retention": "15d",
    "version": "false",
    "web.console.libraries": "console_libraries",
    "web.console.templates": "consoles",
    "web.enable-admin-api": "false",
    "web.enable-lifecycle": "false",
    "web.external-url": "",
    "web.listen-address": "0.0.0.0:9090",
    "web.max-connections": "512",
    "web.read-timeout": "5m",
    "web.route-prefix": "/",
    "web.user-assets": ""
  }
}
```
Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
											
										 
											2018-02-21 16:49:02 +08:00
										 |  |  | 	flagsMap map[string]string, | 
					
						
							| 
									
										
										
										
											2017-10-06 23:20:20 +08:00
										 |  |  | 	readyFunc func(http.HandlerFunc) http.HandlerFunc, | 
					
						
							| 
									
										
										
										
											2017-12-03 13:07:05 +08:00
										 |  |  | 	db func() *tsdb.DB, | 
					
						
							|  |  |  | 	enableAdmin bool, | 
					
						
							| 
									
										
										
										
											2017-10-06 23:20:20 +08:00
										 |  |  | ) *API { | 
					
						
							| 
									
										
										
										
											2015-11-12 03:46:57 +08:00
										 |  |  | 	return &API{ | 
					
						
							| 
									
										
										
										
											2017-01-13 17:20:11 +08:00
										 |  |  | 		QueryEngine:           qe, | 
					
						
							| 
									
										
										
										
											2017-07-06 20:38:40 +08:00
										 |  |  | 		Queryable:             q, | 
					
						
							| 
									
										
										
										
											2017-01-13 17:20:11 +08:00
										 |  |  | 		targetRetriever:       tr, | 
					
						
							|  |  |  | 		alertmanagerRetriever: ar, | 
					
						
							| 
									
										
										
										
											2017-12-03 13:07:05 +08:00
										 |  |  | 		now:         time.Now, | 
					
						
							|  |  |  | 		config:      configFunc, | 
					
						
							| 
									
										
											  
											
												api: Added v1/status/flags endpoint. (#3864)
Endpoint URL: /api/v1/status/flags
Example Output:
```json
{
  "status": "success",
  "data": {
    "alertmanager.notification-queue-capacity": "10000",
    "alertmanager.timeout": "10s",
    "completion-bash": "false",
    "completion-script-bash": "false",
    "completion-script-zsh": "false",
    "config.file": "my_cool_prometheus.yaml",
    "help": "false",
    "help-long": "false",
    "help-man": "false",
    "log.level": "info",
    "query.lookback-delta": "5m",
    "query.max-concurrency": "20",
    "query.timeout": "2m",
    "storage.tsdb.max-block-duration": "36h",
    "storage.tsdb.min-block-duration": "2h",
    "storage.tsdb.no-lockfile": "false",
    "storage.tsdb.path": "data/",
    "storage.tsdb.retention": "15d",
    "version": "false",
    "web.console.libraries": "console_libraries",
    "web.console.templates": "consoles",
    "web.enable-admin-api": "false",
    "web.enable-lifecycle": "false",
    "web.external-url": "",
    "web.listen-address": "0.0.0.0:9090",
    "web.max-connections": "512",
    "web.read-timeout": "5m",
    "web.route-prefix": "/",
    "web.user-assets": ""
  }
}
```
Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
											
										 
											2018-02-21 16:49:02 +08:00
										 |  |  | 		flagsMap:    flagsMap, | 
					
						
							| 
									
										
										
										
											2017-12-03 13:07:05 +08:00
										 |  |  | 		ready:       readyFunc, | 
					
						
							|  |  |  | 		db:          db, | 
					
						
							|  |  |  | 		enableAdmin: enableAdmin, | 
					
						
							| 
									
										
										
										
											2015-11-12 03:46:57 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Register the API's endpoints in the given router.
 | 
					
						
							|  |  |  | func (api *API) Register(r *route.Router) { | 
					
						
							| 
									
										
										
										
											2018-03-21 16:16:16 +08:00
										 |  |  | 	wrap := func(f apiFunc) http.HandlerFunc { | 
					
						
							| 
									
										
										
										
											2015-09-17 20:49:50 +08:00
										 |  |  | 		hf := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 			setCORS(w) | 
					
						
							|  |  |  | 			if data, err := f(r); err != nil { | 
					
						
							|  |  |  | 				respondError(w, err, data) | 
					
						
							| 
									
										
										
										
											2016-01-26 08:32:46 +08:00
										 |  |  | 			} else if data != nil { | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 				respond(w, data) | 
					
						
							| 
									
										
										
										
											2016-01-26 08:32:46 +08:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				w.WriteHeader(http.StatusNoContent) | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2018-03-21 16:16:16 +08:00
										 |  |  | 		return api.ready(httputil.CompressionHandler{ | 
					
						
							| 
									
										
										
										
											2015-09-18 22:51:53 +08:00
										 |  |  | 			Handler: hf, | 
					
						
							| 
									
										
										
										
											2018-03-21 16:16:16 +08:00
										 |  |  | 		}.ServeHTTP) | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-21 16:16:16 +08:00
										 |  |  | 	r.Options("/*path", wrap(api.options)) | 
					
						
							| 
									
										
										
										
											2016-01-26 08:32:46 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-21 16:16:16 +08:00
										 |  |  | 	r.Get("/query", wrap(api.query)) | 
					
						
							|  |  |  | 	r.Post("/query", wrap(api.query)) | 
					
						
							|  |  |  | 	r.Get("/query_range", wrap(api.queryRange)) | 
					
						
							|  |  |  | 	r.Post("/query_range", wrap(api.queryRange)) | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-21 16:16:16 +08:00
										 |  |  | 	r.Get("/label/:name/values", wrap(api.labelValues)) | 
					
						
							| 
									
										
										
										
											2015-06-09 22:09:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-21 16:16:16 +08:00
										 |  |  | 	r.Get("/series", wrap(api.series)) | 
					
						
							|  |  |  | 	r.Del("/series", wrap(api.dropSeries)) | 
					
						
							| 
									
										
										
										
											2016-12-02 20:31:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-21 16:16:16 +08:00
										 |  |  | 	r.Get("/targets", wrap(api.targets)) | 
					
						
							|  |  |  | 	r.Get("/alertmanagers", wrap(api.alertmanagers)) | 
					
						
							| 
									
										
										
										
											2017-05-11 23:09:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-21 16:16:16 +08:00
										 |  |  | 	r.Get("/status/config", wrap(api.serveConfig)) | 
					
						
							|  |  |  | 	r.Get("/status/flags", wrap(api.serveFlags)) | 
					
						
							|  |  |  | 	r.Post("/read", api.ready(http.HandlerFunc(api.remoteRead))) | 
					
						
							| 
									
										
										
										
											2017-12-03 13:07:05 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Admin APIs
 | 
					
						
							| 
									
										
										
										
											2018-03-21 16:16:16 +08:00
										 |  |  | 	r.Post("/admin/tsdb/delete_series", wrap(api.deleteSeries)) | 
					
						
							|  |  |  | 	r.Post("/admin/tsdb/clean_tombstones", wrap(api.cleanTombstones)) | 
					
						
							|  |  |  | 	r.Post("/admin/tsdb/snapshot", wrap(api.snapshot)) | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type queryData struct { | 
					
						
							| 
									
										
										
										
											2017-11-16 23:30:48 +08:00
										 |  |  | 	ResultType promql.ValueType  `json:"resultType"` | 
					
						
							|  |  |  | 	Result     promql.Value      `json:"result"` | 
					
						
							| 
									
										
										
										
											2017-02-08 19:58:40 +08:00
										 |  |  | 	Stats      *stats.QueryStats `json:"stats,omitempty"` | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 08:32:46 +08:00
										 |  |  | func (api *API) options(r *http.Request) (interface{}, *apiError) { | 
					
						
							|  |  |  | 	return nil, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | func (api *API) query(r *http.Request) (interface{}, *apiError) { | 
					
						
							| 
									
										
										
										
											2016-12-25 07:37:46 +08:00
										 |  |  | 	var ts time.Time | 
					
						
							| 
									
										
										
										
											2015-11-12 03:46:57 +08:00
										 |  |  | 	if t := r.FormValue("time"); t != "" { | 
					
						
							|  |  |  | 		var err error | 
					
						
							|  |  |  | 		ts, err = parseTime(t) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, &apiError{errorBadData, err} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		ts = api.now() | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-11-12 03:46:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-03 07:49:29 +08:00
										 |  |  | 	ctx := r.Context() | 
					
						
							| 
									
										
										
										
											2017-03-07 01:32:21 +08:00
										 |  |  | 	if to := r.FormValue("timeout"); to != "" { | 
					
						
							|  |  |  | 		var cancel context.CancelFunc | 
					
						
							|  |  |  | 		timeout, err := parseDuration(to) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, &apiError{errorBadData, err} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ctx, cancel = context.WithTimeout(ctx, timeout) | 
					
						
							|  |  |  | 		defer cancel() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-10 00:44:23 +08:00
										 |  |  | 	qry, err := api.QueryEngine.NewInstantQuery(api.Queryable, r.FormValue("query"), ts) | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, &apiError{errorBadData, err} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 01:32:21 +08:00
										 |  |  | 	res := qry.Exec(ctx) | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	if res.Err != nil { | 
					
						
							| 
									
										
										
										
											2015-06-09 19:44:49 +08:00
										 |  |  | 		switch res.Err.(type) { | 
					
						
							|  |  |  | 		case promql.ErrQueryCanceled: | 
					
						
							|  |  |  | 			return nil, &apiError{errorCanceled, res.Err} | 
					
						
							|  |  |  | 		case promql.ErrQueryTimeout: | 
					
						
							|  |  |  | 			return nil, &apiError{errorTimeout, res.Err} | 
					
						
							| 
									
										
										
										
											2017-04-05 00:22:51 +08:00
										 |  |  | 		case promql.ErrStorage: | 
					
						
							|  |  |  | 			return nil, &apiError{errorInternal, res.Err} | 
					
						
							| 
									
										
										
										
											2015-06-09 19:44:49 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return nil, &apiError{errorExec, res.Err} | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-11-16 23:30:48 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Optional stats field in response if parameter "stats" is not empty.
 | 
					
						
							|  |  |  | 	var qs *stats.QueryStats | 
					
						
							|  |  |  | 	if r.FormValue("stats") != "" { | 
					
						
							|  |  |  | 		qs = stats.NewQueryStats(qry.Stats()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	return &queryData{ | 
					
						
							|  |  |  | 		ResultType: res.Value.Type(), | 
					
						
							|  |  |  | 		Result:     res.Value, | 
					
						
							| 
									
										
										
										
											2017-11-16 23:30:48 +08:00
										 |  |  | 		Stats:      qs, | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *API) queryRange(r *http.Request) (interface{}, *apiError) { | 
					
						
							|  |  |  | 	start, err := parseTime(r.FormValue("start")) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, &apiError{errorBadData, err} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	end, err := parseTime(r.FormValue("end")) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, &apiError{errorBadData, err} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-11-01 21:25:34 +08:00
										 |  |  | 	if end.Before(start) { | 
					
						
							|  |  |  | 		err := errors.New("end timestamp must not be before start time") | 
					
						
							|  |  |  | 		return nil, &apiError{errorBadData, err} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	step, err := parseDuration(r.FormValue("step")) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, &apiError{errorBadData, err} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-16 21:10:02 +08:00
										 |  |  | 	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} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	// 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} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-03 07:49:29 +08:00
										 |  |  | 	ctx := r.Context() | 
					
						
							| 
									
										
										
										
											2017-03-07 01:32:21 +08:00
										 |  |  | 	if to := r.FormValue("timeout"); to != "" { | 
					
						
							|  |  |  | 		var cancel context.CancelFunc | 
					
						
							|  |  |  | 		timeout, err := parseDuration(to) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, &apiError{errorBadData, err} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ctx, cancel = context.WithTimeout(ctx, timeout) | 
					
						
							|  |  |  | 		defer cancel() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-10 00:44:23 +08:00
										 |  |  | 	qry, err := api.QueryEngine.NewRangeQuery(api.Queryable, r.FormValue("query"), start, end, step) | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2015-06-09 19:44:49 +08:00
										 |  |  | 		return nil, &apiError{errorBadData, err} | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 01:32:21 +08:00
										 |  |  | 	res := qry.Exec(ctx) | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	if res.Err != nil { | 
					
						
							| 
									
										
										
										
											2015-06-09 19:44:49 +08:00
										 |  |  | 		switch res.Err.(type) { | 
					
						
							|  |  |  | 		case promql.ErrQueryCanceled: | 
					
						
							|  |  |  | 			return nil, &apiError{errorCanceled, res.Err} | 
					
						
							|  |  |  | 		case promql.ErrQueryTimeout: | 
					
						
							|  |  |  | 			return nil, &apiError{errorTimeout, res.Err} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil, &apiError{errorExec, res.Err} | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-29 16:27:30 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-08 19:58:40 +08:00
										 |  |  | 	// Optional stats field in response if parameter "stats" is not empty.
 | 
					
						
							|  |  |  | 	var qs *stats.QueryStats | 
					
						
							|  |  |  | 	if r.FormValue("stats") != "" { | 
					
						
							| 
									
										
										
										
											2017-11-16 23:30:48 +08:00
										 |  |  | 		qs = stats.NewQueryStats(qry.Stats()) | 
					
						
							| 
									
										
										
										
											2017-02-08 19:58:40 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	return &queryData{ | 
					
						
							|  |  |  | 		ResultType: res.Value.Type(), | 
					
						
							|  |  |  | 		Result:     res.Value, | 
					
						
							| 
									
										
										
										
											2017-02-08 19:58:40 +08:00
										 |  |  | 		Stats:      qs, | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-09 03:19:52 +08:00
										 |  |  | func (api *API) labelValues(r *http.Request) (interface{}, *apiError) { | 
					
						
							| 
									
										
										
										
											2017-10-05 03:04:15 +08:00
										 |  |  | 	ctx := r.Context() | 
					
						
							|  |  |  | 	name := route.Param(ctx, "name") | 
					
						
							| 
									
										
										
										
											2015-06-09 03:19:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-20 23:18:46 +08:00
										 |  |  | 	if !model.LabelNameRE.MatchString(name) { | 
					
						
							| 
									
										
										
										
											2015-06-09 03:19:52 +08:00
										 |  |  | 		return nil, &apiError{errorBadData, fmt.Errorf("invalid label name: %q", name)} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-10-05 03:04:15 +08:00
										 |  |  | 	q, err := api.Queryable.Querier(ctx, math.MinInt64, math.MaxInt64) | 
					
						
							| 
									
										
										
										
											2016-10-13 01:34:22 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, &apiError{errorExec, err} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer q.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-29 16:27:30 +08:00
										 |  |  | 	vals, err := q.LabelValues(name) | 
					
						
							| 
									
										
										
										
											2016-07-12 02:27:25 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, &apiError{errorExec, err} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-09 03:19:52 +08:00
										 |  |  | 	return vals, nil | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-16 21:09:59 +08:00
										 |  |  | var ( | 
					
						
							|  |  |  | 	minTime = time.Unix(math.MinInt64/1000+62135596801, 0) | 
					
						
							|  |  |  | 	maxTime = time.Unix(math.MaxInt64/1000-62135596801, 999999999) | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-09 22:09:31 +08:00
										 |  |  | 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")} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-12 05:59:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-25 07:37:46 +08:00
										 |  |  | 	var start time.Time | 
					
						
							| 
									
										
										
										
											2016-05-12 05:59:52 +08:00
										 |  |  | 	if t := r.FormValue("start"); t != "" { | 
					
						
							|  |  |  | 		var err error | 
					
						
							|  |  |  | 		start, err = parseTime(t) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, &apiError{errorBadData, err} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2017-01-16 21:09:59 +08:00
										 |  |  | 		start = minTime | 
					
						
							| 
									
										
										
										
											2016-05-12 05:59:52 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-25 07:37:46 +08:00
										 |  |  | 	var end time.Time | 
					
						
							| 
									
										
										
										
											2016-05-12 05:59:52 +08:00
										 |  |  | 	if t := r.FormValue("end"); t != "" { | 
					
						
							|  |  |  | 		var err error | 
					
						
							|  |  |  | 		end, err = parseTime(t) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, &apiError{errorBadData, err} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2017-01-16 21:09:59 +08:00
										 |  |  | 		end = maxTime | 
					
						
							| 
									
										
										
										
											2016-05-12 05:59:52 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-29 16:27:30 +08:00
										 |  |  | 	var matcherSets [][]*labels.Matcher | 
					
						
							| 
									
										
										
										
											2016-07-12 02:27:25 +08:00
										 |  |  | 	for _, s := range r.Form["match[]"] { | 
					
						
							|  |  |  | 		matchers, err := promql.ParseMetricSelector(s) | 
					
						
							| 
									
										
										
										
											2015-06-09 22:09:31 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, &apiError{errorBadData, err} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-07-12 02:27:25 +08:00
										 |  |  | 		matcherSets = append(matcherSets, matchers) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-05 03:04:15 +08:00
										 |  |  | 	q, err := api.Queryable.Querier(r.Context(), timestamp.FromTime(start), timestamp.FromTime(end)) | 
					
						
							| 
									
										
										
										
											2016-12-30 17:43:44 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, &apiError{errorExec, err} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer q.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-10 19:00:23 +08:00
										 |  |  | 	var sets []storage.SeriesSet | 
					
						
							| 
									
										
										
										
											2016-12-30 17:43:44 +08:00
										 |  |  | 	for _, mset := range matcherSets { | 
					
						
							| 
									
										
										
										
											2018-01-10 00:44:23 +08:00
										 |  |  | 		s, err := q.Select(nil, mset...) | 
					
						
							| 
									
										
										
										
											2017-11-23 20:50:06 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, &apiError{errorExec, err} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-12-10 19:00:23 +08:00
										 |  |  | 		sets = append(sets, s) | 
					
						
							| 
									
										
										
										
											2016-12-30 17:43:44 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-10 19:00:23 +08:00
										 |  |  | 	set := storage.NewMergeSeriesSet(sets) | 
					
						
							| 
									
										
										
										
											2017-04-04 17:09:11 +08:00
										 |  |  | 	metrics := []labels.Labels{} | 
					
						
							|  |  |  | 	for set.Next() { | 
					
						
							|  |  |  | 		metrics = append(metrics, set.At().Labels()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if set.Err() != nil { | 
					
						
							|  |  |  | 		return nil, &apiError{errorExec, set.Err()} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-30 17:43:44 +08:00
										 |  |  | 	return metrics, nil | 
					
						
							| 
									
										
										
										
											2015-06-09 22:09:31 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *API) dropSeries(r *http.Request) (interface{}, *apiError) { | 
					
						
							| 
									
										
										
										
											2017-07-06 20:38:40 +08:00
										 |  |  | 	return nil, &apiError{errorInternal, fmt.Errorf("not implemented")} | 
					
						
							| 
									
										
										
										
											2015-06-09 22:09:31 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 01:51:27 +08:00
										 |  |  | // Target has the information for one target.
 | 
					
						
							| 
									
										
										
										
											2016-12-02 20:31:43 +08:00
										 |  |  | type Target struct { | 
					
						
							|  |  |  | 	// Labels before any processing.
 | 
					
						
							| 
									
										
										
										
											2016-12-29 16:27:30 +08:00
										 |  |  | 	DiscoveredLabels map[string]string `json:"discoveredLabels"` | 
					
						
							| 
									
										
										
										
											2016-12-02 20:31:43 +08:00
										 |  |  | 	// Any labels that are added to this target and its metrics.
 | 
					
						
							| 
									
										
										
										
											2016-12-29 16:27:30 +08:00
										 |  |  | 	Labels map[string]string `json:"labels"` | 
					
						
							| 
									
										
										
										
											2016-12-02 20:31:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-14 00:15:04 +08:00
										 |  |  | 	ScrapeURL string `json:"scrapeUrl"` | 
					
						
							| 
									
										
										
										
											2016-12-02 20:31:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-01 17:55:07 +08:00
										 |  |  | 	LastError  string              `json:"lastError"` | 
					
						
							|  |  |  | 	LastScrape time.Time           `json:"lastScrape"` | 
					
						
							|  |  |  | 	Health     scrape.TargetHealth `json:"health"` | 
					
						
							| 
									
										
										
										
											2016-12-02 20:31:43 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 01:26:18 +08:00
										 |  |  | // DroppedTarget has the information for one target that was dropped during relabelling.
 | 
					
						
							|  |  |  | type DroppedTarget struct { | 
					
						
							|  |  |  | 	// Labels before any processing.
 | 
					
						
							|  |  |  | 	DiscoveredLabels map[string]string `json:"discoveredLabels"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-06 19:46:37 +08:00
										 |  |  | // TargetDiscovery has all the active targets.
 | 
					
						
							| 
									
										
										
										
											2017-01-14 00:15:04 +08:00
										 |  |  | type TargetDiscovery struct { | 
					
						
							| 
									
										
										
										
											2018-02-22 01:26:18 +08:00
										 |  |  | 	ActiveTargets  []*Target        `json:"activeTargets"` | 
					
						
							|  |  |  | 	DroppedTargets []*DroppedTarget `json:"droppedTargets"` | 
					
						
							| 
									
										
										
										
											2017-01-14 00:15:04 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-02 20:31:43 +08:00
										 |  |  | func (api *API) targets(r *http.Request) (interface{}, *apiError) { | 
					
						
							|  |  |  | 	targets := api.targetRetriever.Targets() | 
					
						
							| 
									
										
										
										
											2018-02-22 01:26:18 +08:00
										 |  |  | 	droppedTargets := api.targetRetriever.DroppedTargets() | 
					
						
							|  |  |  | 	res := &TargetDiscovery{ActiveTargets: make([]*Target, len(targets)), DroppedTargets: make([]*DroppedTarget, len(droppedTargets))} | 
					
						
							| 
									
										
										
										
											2016-12-02 20:31:43 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for i, t := range targets { | 
					
						
							|  |  |  | 		lastErrStr := "" | 
					
						
							|  |  |  | 		lastErr := t.LastError() | 
					
						
							|  |  |  | 		if lastErr != nil { | 
					
						
							|  |  |  | 			lastErrStr = lastErr.Error() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-14 00:15:04 +08:00
										 |  |  | 		res.ActiveTargets[i] = &Target{ | 
					
						
							| 
									
										
										
										
											2016-12-29 16:27:30 +08:00
										 |  |  | 			DiscoveredLabels: t.DiscoveredLabels().Map(), | 
					
						
							|  |  |  | 			Labels:           t.Labels().Map(), | 
					
						
							| 
									
										
										
										
											2017-01-14 00:15:04 +08:00
										 |  |  | 			ScrapeURL:        t.URL().String(), | 
					
						
							| 
									
										
										
										
											2016-12-02 20:31:43 +08:00
										 |  |  | 			LastError:        lastErrStr, | 
					
						
							|  |  |  | 			LastScrape:       t.LastScrape(), | 
					
						
							|  |  |  | 			Health:           t.Health(), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 01:26:18 +08:00
										 |  |  | 	for i, t := range droppedTargets { | 
					
						
							|  |  |  | 		res.DroppedTargets[i] = &DroppedTarget{ | 
					
						
							|  |  |  | 			DiscoveredLabels: t.DiscoveredLabels().Map(), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-02 20:31:43 +08:00
										 |  |  | 	return res, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 01:51:27 +08:00
										 |  |  | // AlertmanagerDiscovery has all the active Alertmanagers.
 | 
					
						
							| 
									
										
										
										
											2017-01-13 17:20:11 +08:00
										 |  |  | type AlertmanagerDiscovery struct { | 
					
						
							| 
									
										
										
										
											2018-02-21 17:00:07 +08:00
										 |  |  | 	ActiveAlertmanagers  []*AlertmanagerTarget `json:"activeAlertmanagers"` | 
					
						
							|  |  |  | 	DroppedAlertmanagers []*AlertmanagerTarget `json:"droppedAlertmanagers"` | 
					
						
							| 
									
										
										
										
											2017-01-13 17:20:11 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 01:51:27 +08:00
										 |  |  | // AlertmanagerTarget has info on one AM.
 | 
					
						
							| 
									
										
										
										
											2017-01-13 17:20:11 +08:00
										 |  |  | type AlertmanagerTarget struct { | 
					
						
							|  |  |  | 	URL string `json:"url"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *API) alertmanagers(r *http.Request) (interface{}, *apiError) { | 
					
						
							|  |  |  | 	urls := api.alertmanagerRetriever.Alertmanagers() | 
					
						
							| 
									
										
										
										
											2018-02-21 17:00:07 +08:00
										 |  |  | 	droppedURLS := api.alertmanagerRetriever.DroppedAlertmanagers() | 
					
						
							|  |  |  | 	ams := &AlertmanagerDiscovery{ActiveAlertmanagers: make([]*AlertmanagerTarget, len(urls)), DroppedAlertmanagers: make([]*AlertmanagerTarget, len(droppedURLS))} | 
					
						
							| 
									
										
										
										
											2017-04-25 13:42:33 +08:00
										 |  |  | 	for i, url := range urls { | 
					
						
							|  |  |  | 		ams.ActiveAlertmanagers[i] = &AlertmanagerTarget{URL: url.String()} | 
					
						
							| 
									
										
										
										
											2017-01-13 17:20:11 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-02-21 17:00:07 +08:00
										 |  |  | 	for i, url := range droppedURLS { | 
					
						
							|  |  |  | 		ams.DroppedAlertmanagers[i] = &AlertmanagerTarget{URL: url.String()} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-13 17:20:11 +08:00
										 |  |  | 	return ams, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-11 23:09:24 +08:00
										 |  |  | type prometheusConfig struct { | 
					
						
							|  |  |  | 	YAML string `json:"yaml"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *API) serveConfig(r *http.Request) (interface{}, *apiError) { | 
					
						
							|  |  |  | 	cfg := &prometheusConfig{ | 
					
						
							|  |  |  | 		YAML: api.config().String(), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return cfg, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												api: Added v1/status/flags endpoint. (#3864)
Endpoint URL: /api/v1/status/flags
Example Output:
```json
{
  "status": "success",
  "data": {
    "alertmanager.notification-queue-capacity": "10000",
    "alertmanager.timeout": "10s",
    "completion-bash": "false",
    "completion-script-bash": "false",
    "completion-script-zsh": "false",
    "config.file": "my_cool_prometheus.yaml",
    "help": "false",
    "help-long": "false",
    "help-man": "false",
    "log.level": "info",
    "query.lookback-delta": "5m",
    "query.max-concurrency": "20",
    "query.timeout": "2m",
    "storage.tsdb.max-block-duration": "36h",
    "storage.tsdb.min-block-duration": "2h",
    "storage.tsdb.no-lockfile": "false",
    "storage.tsdb.path": "data/",
    "storage.tsdb.retention": "15d",
    "version": "false",
    "web.console.libraries": "console_libraries",
    "web.console.templates": "consoles",
    "web.enable-admin-api": "false",
    "web.enable-lifecycle": "false",
    "web.external-url": "",
    "web.listen-address": "0.0.0.0:9090",
    "web.max-connections": "512",
    "web.read-timeout": "5m",
    "web.route-prefix": "/",
    "web.user-assets": ""
  }
}
```
Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
											
										 
											2018-02-21 16:49:02 +08:00
										 |  |  | func (api *API) serveFlags(r *http.Request) (interface{}, *apiError) { | 
					
						
							|  |  |  | 	return api.flagsMap, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-24 04:28:17 +08:00
										 |  |  | func (api *API) remoteRead(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	req, err := remote.DecodeReadRequest(r) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		http.Error(w, err.Error(), http.StatusBadRequest) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	resp := prompb.ReadResponse{ | 
					
						
							|  |  |  | 		Results: make([]*prompb.QueryResult, len(req.Queries)), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for i, query := range req.Queries { | 
					
						
							|  |  |  | 		from, through, matchers, err := remote.FromQuery(query) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			http.Error(w, err.Error(), http.StatusBadRequest) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		querier, err := api.Queryable.Querier(r.Context(), from, through) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			http.Error(w, err.Error(), http.StatusInternalServerError) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		defer querier.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Change equality matchers which match external labels
 | 
					
						
							|  |  |  | 		// to a matcher that looks for an empty label,
 | 
					
						
							|  |  |  | 		// as that label should not be present in the storage.
 | 
					
						
							|  |  |  | 		externalLabels := api.config().GlobalConfig.ExternalLabels.Clone() | 
					
						
							|  |  |  | 		filteredMatchers := make([]*labels.Matcher, 0, len(matchers)) | 
					
						
							|  |  |  | 		for _, m := range matchers { | 
					
						
							|  |  |  | 			value := externalLabels[model.LabelName(m.Name)] | 
					
						
							|  |  |  | 			if m.Type == labels.MatchEqual && value == model.LabelValue(m.Value) { | 
					
						
							|  |  |  | 				matcher, err := labels.NewMatcher(labels.MatchEqual, m.Name, "") | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					http.Error(w, err.Error(), http.StatusInternalServerError) | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				filteredMatchers = append(filteredMatchers, matcher) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				filteredMatchers = append(filteredMatchers, m) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-10 00:44:23 +08:00
										 |  |  | 		set, err := querier.Select(nil, filteredMatchers...) | 
					
						
							| 
									
										
										
										
											2017-11-23 20:50:06 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			http.Error(w, err.Error(), http.StatusInternalServerError) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		resp.Results[i], err = remote.ToQueryResult(set) | 
					
						
							| 
									
										
										
										
											2017-10-24 04:28:17 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			http.Error(w, err.Error(), http.StatusInternalServerError) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-26 18:44:49 +08:00
										 |  |  | 		// Add external labels back in, in sorted order.
 | 
					
						
							|  |  |  | 		sortedExternalLabels := make([]*prompb.Label, 0, len(externalLabels)) | 
					
						
							|  |  |  | 		for name, value := range externalLabels { | 
					
						
							|  |  |  | 			sortedExternalLabels = append(sortedExternalLabels, &prompb.Label{ | 
					
						
							|  |  |  | 				Name:  string(name), | 
					
						
							|  |  |  | 				Value: string(value), | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		sort.Slice(sortedExternalLabels, func(i, j int) bool { | 
					
						
							|  |  |  | 			return sortedExternalLabels[i].Name < sortedExternalLabels[j].Name | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-24 04:28:17 +08:00
										 |  |  | 		for _, ts := range resp.Results[i].Timeseries { | 
					
						
							| 
									
										
										
										
											2017-10-26 18:44:49 +08:00
										 |  |  | 			ts.Labels = mergeLabels(ts.Labels, sortedExternalLabels) | 
					
						
							| 
									
										
										
										
											2017-10-24 04:28:17 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := remote.EncodeReadResponse(&resp, w); err != nil { | 
					
						
							|  |  |  | 		http.Error(w, err.Error(), http.StatusInternalServerError) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-03 13:07:05 +08:00
										 |  |  | func (api *API) deleteSeries(r *http.Request) (interface{}, *apiError) { | 
					
						
							|  |  |  | 	if !api.enableAdmin { | 
					
						
							|  |  |  | 		return nil, &apiError{errorUnavailable, errors.New("Admin APIs disabled")} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	db := api.db() | 
					
						
							|  |  |  | 	if db == nil { | 
					
						
							|  |  |  | 		return nil, &apiError{errorUnavailable, errors.New("TSDB not ready")} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	r.ParseForm() | 
					
						
							|  |  |  | 	if len(r.Form["match[]"]) == 0 { | 
					
						
							|  |  |  | 		return nil, &apiError{errorBadData, fmt.Errorf("no match[] parameter provided")} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var start time.Time | 
					
						
							|  |  |  | 	if t := r.FormValue("start"); t != "" { | 
					
						
							|  |  |  | 		var err error | 
					
						
							|  |  |  | 		start, err = parseTime(t) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, &apiError{errorBadData, err} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		start = minTime | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var end time.Time | 
					
						
							|  |  |  | 	if t := r.FormValue("end"); t != "" { | 
					
						
							|  |  |  | 		var err error | 
					
						
							|  |  |  | 		end, err = parseTime(t) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, &apiError{errorBadData, err} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		end = maxTime | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, s := range r.Form["match[]"] { | 
					
						
							|  |  |  | 		matchers, err := promql.ParseMetricSelector(s) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, &apiError{errorBadData, err} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var selector tsdbLabels.Selector | 
					
						
							|  |  |  | 		for _, m := range matchers { | 
					
						
							|  |  |  | 			selector = append(selector, convertMatcher(m)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if err := db.Delete(timestamp.FromTime(start), timestamp.FromTime(end), selector...); err != nil { | 
					
						
							|  |  |  | 			return nil, &apiError{errorInternal, err} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *API) snapshot(r *http.Request) (interface{}, *apiError) { | 
					
						
							|  |  |  | 	if !api.enableAdmin { | 
					
						
							|  |  |  | 		return nil, &apiError{errorUnavailable, errors.New("Admin APIs disabled")} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-08 18:43:41 +08:00
										 |  |  | 	skipHead, _ := strconv.ParseBool(r.FormValue("skip_head")) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-03 13:07:05 +08:00
										 |  |  | 	db := api.db() | 
					
						
							|  |  |  | 	if db == nil { | 
					
						
							|  |  |  | 		return nil, &apiError{errorUnavailable, errors.New("TSDB not ready")} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		snapdir = filepath.Join(db.Dir(), "snapshots") | 
					
						
							| 
									
										
										
										
											2017-12-11 03:19:34 +08:00
										 |  |  | 		name    = fmt.Sprintf("%s-%x", | 
					
						
							|  |  |  | 			time.Now().UTC().Format("20060102T150405Z0700"), | 
					
						
							|  |  |  | 			rand.Int()) | 
					
						
							|  |  |  | 		dir = filepath.Join(snapdir, name) | 
					
						
							| 
									
										
										
										
											2017-12-03 13:07:05 +08:00
										 |  |  | 	) | 
					
						
							|  |  |  | 	if err := os.MkdirAll(dir, 0777); err != nil { | 
					
						
							|  |  |  | 		return nil, &apiError{errorInternal, fmt.Errorf("create snapshot directory: %s", err)} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-08 18:43:41 +08:00
										 |  |  | 	if err := db.Snapshot(dir, !skipHead); err != nil { | 
					
						
							| 
									
										
										
										
											2017-12-03 13:07:05 +08:00
										 |  |  | 		return nil, &apiError{errorInternal, fmt.Errorf("create snapshot: %s", err)} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return struct { | 
					
						
							|  |  |  | 		Name string `json:"name"` | 
					
						
							|  |  |  | 	}{name}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *API) cleanTombstones(r *http.Request) (interface{}, *apiError) { | 
					
						
							|  |  |  | 	if !api.enableAdmin { | 
					
						
							|  |  |  | 		return nil, &apiError{errorUnavailable, errors.New("Admin APIs disabled")} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	db := api.db() | 
					
						
							|  |  |  | 	if db == nil { | 
					
						
							|  |  |  | 		return nil, &apiError{errorUnavailable, errors.New("TSDB not ready")} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := db.CleanTombstones(); err != nil { | 
					
						
							|  |  |  | 		return nil, &apiError{errorInternal, err} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func convertMatcher(m *labels.Matcher) tsdbLabels.Matcher { | 
					
						
							|  |  |  | 	switch m.Type { | 
					
						
							|  |  |  | 	case labels.MatchEqual: | 
					
						
							|  |  |  | 		return tsdbLabels.NewEqualMatcher(m.Name, m.Value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case labels.MatchNotEqual: | 
					
						
							|  |  |  | 		return tsdbLabels.Not(tsdbLabels.NewEqualMatcher(m.Name, m.Value)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case labels.MatchRegexp: | 
					
						
							|  |  |  | 		res, err := tsdbLabels.NewRegexpMatcher(m.Name, "^(?:"+m.Value+")$") | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			panic(err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return res | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case labels.MatchNotRegexp: | 
					
						
							|  |  |  | 		res, err := tsdbLabels.NewRegexpMatcher(m.Name, "^(?:"+m.Value+")$") | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			panic(err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return tsdbLabels.Not(res) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	panic("storage.convertMatcher: invalid matcher type") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-26 18:44:49 +08:00
										 |  |  | // mergeLabels merges two sets of sorted proto labels, preferring those in
 | 
					
						
							|  |  |  | // primary to those in secondary when there is an overlap.
 | 
					
						
							|  |  |  | func mergeLabels(primary, secondary []*prompb.Label) []*prompb.Label { | 
					
						
							|  |  |  | 	result := make([]*prompb.Label, 0, len(primary)+len(secondary)) | 
					
						
							|  |  |  | 	i, j := 0, 0 | 
					
						
							|  |  |  | 	for i < len(primary) && j < len(secondary) { | 
					
						
							|  |  |  | 		if primary[i].Name < secondary[j].Name { | 
					
						
							|  |  |  | 			result = append(result, primary[i]) | 
					
						
							|  |  |  | 			i++ | 
					
						
							|  |  |  | 		} else if primary[i].Name > secondary[j].Name { | 
					
						
							|  |  |  | 			result = append(result, secondary[j]) | 
					
						
							|  |  |  | 			j++ | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			result = append(result, primary[i]) | 
					
						
							|  |  |  | 			i++ | 
					
						
							|  |  |  | 			j++ | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for ; i < len(primary); i++ { | 
					
						
							|  |  |  | 		result = append(result, primary[i]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for ; j < len(secondary); j++ { | 
					
						
							|  |  |  | 		result = append(result, secondary[j]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return result | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | func respond(w http.ResponseWriter, data interface{}) { | 
					
						
							|  |  |  | 	w.Header().Set("Content-Type", "application/json") | 
					
						
							| 
									
										
										
										
											2016-01-26 08:32:46 +08:00
										 |  |  | 	w.WriteHeader(http.StatusOK) | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	b, err := json.Marshal(&response{ | 
					
						
							|  |  |  | 		Status: statusSuccess, | 
					
						
							|  |  |  | 		Data:   data, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	w.Write(b) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func respondError(w http.ResponseWriter, apiErr *apiError, data interface{}) { | 
					
						
							|  |  |  | 	w.Header().Set("Content-Type", "application/json") | 
					
						
							| 
									
										
										
										
											2015-11-12 06:00:54 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var code int | 
					
						
							|  |  |  | 	switch apiErr.typ { | 
					
						
							|  |  |  | 	case errorBadData: | 
					
						
							|  |  |  | 		code = http.StatusBadRequest | 
					
						
							|  |  |  | 	case errorExec: | 
					
						
							|  |  |  | 		code = 422 | 
					
						
							|  |  |  | 	case errorCanceled, errorTimeout: | 
					
						
							|  |  |  | 		code = http.StatusServiceUnavailable | 
					
						
							| 
									
										
										
										
											2017-04-05 00:22:51 +08:00
										 |  |  | 	case errorInternal: | 
					
						
							|  |  |  | 		code = http.StatusInternalServerError | 
					
						
							| 
									
										
										
										
											2015-11-12 06:00:54 +08:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		code = http.StatusInternalServerError | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	w.WriteHeader(code) | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	b, err := json.Marshal(&response{ | 
					
						
							|  |  |  | 		Status:    statusError, | 
					
						
							|  |  |  | 		ErrorType: apiErr.typ, | 
					
						
							|  |  |  | 		Error:     apiErr.err.Error(), | 
					
						
							|  |  |  | 		Data:      data, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	w.Write(b) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-25 07:37:46 +08:00
										 |  |  | func parseTime(s string) (time.Time, error) { | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	if t, err := strconv.ParseFloat(s, 64); err == nil { | 
					
						
							| 
									
										
										
										
											2016-12-25 07:37:46 +08:00
										 |  |  | 		s, ns := math.Modf(t) | 
					
						
							|  |  |  | 		return time.Unix(int64(s), int64(ns*float64(time.Second))), nil | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if t, err := time.Parse(time.RFC3339Nano, s); err == nil { | 
					
						
							| 
									
										
										
										
											2016-12-25 07:37:46 +08:00
										 |  |  | 		return t, nil | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-25 07:37:46 +08:00
										 |  |  | 	return time.Time{}, fmt.Errorf("cannot parse %q to a valid timestamp", s) | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func parseDuration(s string) (time.Duration, error) { | 
					
						
							|  |  |  | 	if d, err := strconv.ParseFloat(s, 64); err == nil { | 
					
						
							| 
									
										
										
										
											2017-03-16 22:16:20 +08:00
										 |  |  | 		ts := d * float64(time.Second) | 
					
						
							|  |  |  | 		if ts > float64(math.MaxInt64) || ts < float64(math.MinInt64) { | 
					
						
							|  |  |  | 			return 0, fmt.Errorf("cannot parse %q to a valid duration. It overflows int64", s) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return time.Duration(ts), nil | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-01-29 22:23:11 +08:00
										 |  |  | 	if d, err := model.ParseDuration(s); err == nil { | 
					
						
							|  |  |  | 		return time.Duration(d), nil | 
					
						
							| 
									
										
										
										
											2015-06-05 00:07:57 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return 0, fmt.Errorf("cannot parse %q to a valid duration", s) | 
					
						
							|  |  |  | } |