web/api: enable running API legacy and v1 in parallel

This commit is contained in:
Fabian Reinartz 2015-06-04 18:24:04 +02:00
parent ab9c98acac
commit 5b713911e3
6 changed files with 42 additions and 31 deletions

13
main.go
View File

@ -41,7 +41,8 @@ import (
"github.com/prometheus/prometheus/storage/remote/influxdb" "github.com/prometheus/prometheus/storage/remote/influxdb"
"github.com/prometheus/prometheus/storage/remote/opentsdb" "github.com/prometheus/prometheus/storage/remote/opentsdb"
"github.com/prometheus/prometheus/web" "github.com/prometheus/prometheus/web"
"github.com/prometheus/prometheus/web/api" "github.com/prometheus/prometheus/web/api/legacy"
"github.com/prometheus/prometheus/web/api/v1"
) )
const deletionBatchSize = 100 const deletionBatchSize = 100
@ -184,16 +185,22 @@ func NewPrometheus() *prometheus {
PathPrefix: *pathPrefix, PathPrefix: *pathPrefix,
} }
metricsService := &api.MetricsService{ apiLegacy := &legacy.API{
Now: clientmodel.Now, Now: clientmodel.Now,
Storage: memStorage, Storage: memStorage,
QueryEngine: queryEngine, QueryEngine: queryEngine,
} }
apiv1 := &v1.API{
Storage: memStorage,
QueryEngine: queryEngine,
}
webService := web.NewWebService(&web.WebServiceOptions{ webService := web.NewWebService(&web.WebServiceOptions{
PathPrefix: *pathPrefix, PathPrefix: *pathPrefix,
StatusHandler: prometheusStatus, StatusHandler: prometheusStatus,
MetricsHandler: metricsService, APILegacy: apiLegacy,
APIv1: apiv1,
ConsolesHandler: consolesHandler, ConsolesHandler: consolesHandler,
AlertsHandler: alertsHandler, AlertsHandler: alertsHandler,
GraphsHandler: graphsHandler, GraphsHandler: graphsHandler,

View File

@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package api package legacy
import ( import (
"net/http" "net/http"
@ -26,18 +26,18 @@ import (
"github.com/prometheus/prometheus/util/route" "github.com/prometheus/prometheus/util/route"
) )
// MetricsService manages the /api HTTP endpoint. // API manages the /api HTTP endpoint.
type MetricsService struct { type API struct {
Now func() clientmodel.Timestamp Now func() clientmodel.Timestamp
Storage local.Storage Storage local.Storage
QueryEngine *promql.Engine QueryEngine *promql.Engine
} }
// RegisterHandler registers the handler for the various endpoints below /api. // RegisterHandler registers the handler for the various endpoints below /api.
func (msrv *MetricsService) RegisterHandler(router *route.Router) { func (api *API) Register(router *route.Router) {
router.Get("/query", handle("query", msrv.Query)) router.Get("/query", handle("query", api.Query))
router.Get("/query_range", handle("query_range", msrv.QueryRange)) router.Get("/query_range", handle("query_range", api.QueryRange))
router.Get("/metrics", handle("metrics", msrv.Metrics)) router.Get("/metrics", handle("metrics", api.Metrics))
} }
func handle(name string, f http.HandlerFunc) http.HandlerFunc { func handle(name string, f http.HandlerFunc) http.HandlerFunc {

View File

@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package api package legacy
import ( import (
"io/ioutil" "io/ioutil"
@ -93,13 +93,13 @@ func TestQuery(t *testing.T) {
}) })
storage.WaitForIndexing() storage.WaitForIndexing()
api := MetricsService{ api := &API{
Now: testNow, Now: testNow,
Storage: storage, Storage: storage,
QueryEngine: promql.NewEngine(storage), QueryEngine: promql.NewEngine(storage),
} }
rtr := route.New() rtr := route.New()
api.RegisterHandler(rtr.WithPrefix("/api")) api.Register(rtr.WithPrefix("/api"))
server := httptest.NewServer(rtr) server := httptest.NewServer(rtr)
defer server.Close() defer server.Close()

View File

@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package api package legacy
import ( import (
"encoding/json" "encoding/json"
@ -63,20 +63,20 @@ func parseDuration(d string) (time.Duration, error) {
} }
// Query handles the /api/query endpoint. // Query handles the /api/query endpoint.
func (serv MetricsService) Query(w http.ResponseWriter, r *http.Request) { func (api *API) Query(w http.ResponseWriter, r *http.Request) {
setAccessControlHeaders(w) setAccessControlHeaders(w)
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
params := httputil.GetQueryParams(r) params := httputil.GetQueryParams(r)
expr := params.Get("expr") expr := params.Get("expr")
timestamp, err := parseTimestampOrNow(params.Get("timestamp"), serv.Now()) timestamp, err := parseTimestampOrNow(params.Get("timestamp"), api.Now())
if err != nil { if err != nil {
httpJSONError(w, fmt.Errorf("invalid query timestamp %s", err), http.StatusBadRequest) httpJSONError(w, fmt.Errorf("invalid query timestamp %s", err), http.StatusBadRequest)
return return
} }
query, err := serv.QueryEngine.NewInstantQuery(expr, timestamp) query, err := api.QueryEngine.NewInstantQuery(expr, timestamp)
if err != nil { if err != nil {
httpJSONError(w, err, http.StatusOK) httpJSONError(w, err, http.StatusOK)
return return
@ -92,7 +92,7 @@ func (serv MetricsService) Query(w http.ResponseWriter, r *http.Request) {
} }
// QueryRange handles the /api/query_range endpoint. // QueryRange handles the /api/query_range endpoint.
func (serv MetricsService) QueryRange(w http.ResponseWriter, r *http.Request) { func (api *API) QueryRange(w http.ResponseWriter, r *http.Request) {
setAccessControlHeaders(w) setAccessControlHeaders(w)
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
@ -111,7 +111,7 @@ func (serv MetricsService) QueryRange(w http.ResponseWriter, r *http.Request) {
return return
} }
end, err := parseTimestampOrNow(params.Get("end"), serv.Now()) end, err := parseTimestampOrNow(params.Get("end"), api.Now())
if err != nil { if err != nil {
httpJSONError(w, fmt.Errorf("invalid query timestamp: %s", err), http.StatusBadRequest) httpJSONError(w, fmt.Errorf("invalid query timestamp: %s", err), http.StatusBadRequest)
return return
@ -121,7 +121,7 @@ func (serv MetricsService) QueryRange(w http.ResponseWriter, r *http.Request) {
// the current time as the end time. Instead, the "end" parameter should // the current time as the end time. Instead, the "end" parameter should
// simply be omitted or set to an empty string for that case. // simply be omitted or set to an empty string for that case.
if end == 0 { if end == 0 {
end = serv.Now() end = api.Now()
} }
// For safety, limit the number of returned points per timeseries. // For safety, limit the number of returned points per timeseries.
@ -136,7 +136,7 @@ func (serv MetricsService) QueryRange(w http.ResponseWriter, r *http.Request) {
end = end.Add(-time.Duration(end.UnixNano() % int64(step))) end = end.Add(-time.Duration(end.UnixNano() % int64(step)))
start := end.Add(-duration) start := end.Add(-duration)
query, err := serv.QueryEngine.NewRangeQuery(expr, start, end, step) query, err := api.QueryEngine.NewRangeQuery(expr, start, end, step)
if err != nil { if err != nil {
httpJSONError(w, err, http.StatusOK) httpJSONError(w, err, http.StatusOK)
return return
@ -152,11 +152,11 @@ func (serv MetricsService) QueryRange(w http.ResponseWriter, r *http.Request) {
} }
// Metrics handles the /api/metrics endpoint. // Metrics handles the /api/metrics endpoint.
func (serv MetricsService) Metrics(w http.ResponseWriter, r *http.Request) { func (api *API) Metrics(w http.ResponseWriter, r *http.Request) {
setAccessControlHeaders(w) setAccessControlHeaders(w)
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
metricNames := serv.Storage.LabelValuesForLabelName(clientmodel.MetricNameLabel) metricNames := api.Storage.LabelValuesForLabelName(clientmodel.MetricNameLabel)
sort.Sort(metricNames) sort.Sort(metricNames)
resultBytes, err := json.Marshal(metricNames) resultBytes, err := json.Marshal(metricNames)
if err != nil { if err != nil {

View File

@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package api package legacy
import ( import (
"testing" "testing"

View File

@ -26,13 +26,14 @@ import (
pprof_runtime "runtime/pprof" pprof_runtime "runtime/pprof"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/log" "github.com/prometheus/log"
"github.com/prometheus/prometheus/web/api/legacy"
"github.com/prometheus/prometheus/web/api/v1"
"github.com/prometheus/prometheus/util/route" "github.com/prometheus/prometheus/util/route"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/web/api"
"github.com/prometheus/prometheus/web/blob" "github.com/prometheus/prometheus/web/blob"
) )
@ -57,7 +58,8 @@ type WebService struct {
type WebServiceOptions struct { type WebServiceOptions struct {
PathPrefix string PathPrefix string
StatusHandler *PrometheusStatusHandler StatusHandler *PrometheusStatusHandler
MetricsHandler *api.MetricsService APILegacy *legacy.API
APIv1 *v1.API
AlertsHandler *AlertsHandler AlertsHandler *AlertsHandler
ConsolesHandler *ConsolesHandler ConsolesHandler *ConsolesHandler
GraphsHandler *GraphsHandler GraphsHandler *GraphsHandler
@ -75,7 +77,7 @@ func NewWebService(o *WebServiceOptions) *WebService {
if o.PathPrefix != "" { if o.PathPrefix != "" {
// If the prefix is missing for the root path, append it. // If the prefix is missing for the root path, append it.
router.Get("/", func(w http.ResponseWriter, r *http.Request) { router.Get("/", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, o.PathPrefix, 301) http.Redirect(w, r, o.PathPrefix, 302)
}) })
router = router.WithPrefix(o.PathPrefix) router = router.WithPrefix(o.PathPrefix)
} }
@ -89,7 +91,9 @@ func NewWebService(o *WebServiceOptions) *WebService {
router.Get(*metricsPath, prometheus.Handler().ServeHTTP) router.Get(*metricsPath, prometheus.Handler().ServeHTTP)
o.MetricsHandler.RegisterHandler(router.WithPrefix("/api")) o.APILegacy.Register(router.WithPrefix("/api"))
o.APIv1.Register(router.WithPrefix("/api/v1"))
router.Get("/consoles/*filepath", instr("consoles", o.ConsolesHandler)) router.Get("/consoles/*filepath", instr("consoles", o.ConsolesHandler))