github.com/m3db/m3@v1.5.0/src/query/api/v1/httpd/handler.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package httpd 22 23 import ( 24 "encoding/json" 25 "fmt" 26 "net/http" 27 // needed for pprof handler registration 28 _ "net/http/pprof" 29 "time" 30 31 "github.com/gorilla/mux" 32 "github.com/jonboulle/clockwork" 33 "go.uber.org/zap" 34 35 "github.com/m3db/m3/src/cluster/placementhandler" 36 "github.com/m3db/m3/src/cluster/placementhandler/handleroptions" 37 "github.com/m3db/m3/src/cmd/services/m3query/config" 38 "github.com/m3db/m3/src/query/api/v1/handler" 39 "github.com/m3db/m3/src/query/api/v1/handler/database" 40 "github.com/m3db/m3/src/query/api/v1/handler/graphite" 41 "github.com/m3db/m3/src/query/api/v1/handler/influxdb" 42 m3json "github.com/m3db/m3/src/query/api/v1/handler/json" 43 "github.com/m3db/m3/src/query/api/v1/handler/namespace" 44 "github.com/m3db/m3/src/query/api/v1/handler/openapi" 45 "github.com/m3db/m3/src/query/api/v1/handler/prom" 46 "github.com/m3db/m3/src/query/api/v1/handler/prometheus/native" 47 "github.com/m3db/m3/src/query/api/v1/handler/prometheus/remote" 48 "github.com/m3db/m3/src/query/api/v1/handler/topic" 49 "github.com/m3db/m3/src/query/api/v1/middleware" 50 "github.com/m3db/m3/src/query/api/v1/options" 51 "github.com/m3db/m3/src/query/api/v1/route" 52 "github.com/m3db/m3/src/query/parser/promql" 53 "github.com/m3db/m3/src/query/util/queryhttp" 54 xdebug "github.com/m3db/m3/src/x/debug" 55 xhttp "github.com/m3db/m3/src/x/net/http" 56 ) 57 58 const ( 59 healthURL = "/health" 60 routesURL = "/routes" 61 62 // EngineURLParam defines query url parameter which is used to switch between 63 // prometheus and m3query engines. 64 EngineURLParam = "engine" 65 ) 66 67 var ( 68 remoteSource = map[string]string{"source": "remote"} 69 nativeSource = map[string]string{"source": "native"} 70 71 v1APIGroup = map[string]string{"api_group": "v1"} 72 ) 73 74 // Handler represents the top-level HTTP handler. 75 type Handler struct { 76 registry *queryhttp.EndpointRegistry 77 handler *mux.Router 78 options options.HandlerOptions 79 customHandlers []options.CustomHandler 80 logger *zap.Logger 81 middlewareConfig config.MiddlewareConfiguration 82 } 83 84 // Router returns the http handler registered with all relevant routes for query. 85 func (h *Handler) Router() http.Handler { 86 return h.handler 87 } 88 89 // NewHandler returns a new instance of handler with routes. 90 func NewHandler( 91 handlerOptions options.HandlerOptions, 92 middlewareConfig config.MiddlewareConfiguration, 93 customHandlers ...options.CustomHandler, 94 ) *Handler { 95 var ( 96 r = mux.NewRouter() 97 logger = handlerOptions.InstrumentOpts().Logger() 98 registry = queryhttp.NewEndpointRegistry(r) 99 ) 100 return &Handler{ 101 registry: registry, 102 handler: r, 103 options: handlerOptions, 104 customHandlers: customHandlers, 105 logger: logger, 106 middlewareConfig: middlewareConfig, 107 } 108 } 109 110 // RegisterRoutes registers all http routes. 111 func (h *Handler) RegisterRoutes() error { 112 instrumentOpts := h.options.InstrumentOpts() 113 114 // OpenAPI. 115 if err := h.registry.Register(queryhttp.RegisterOptions{ 116 Path: openapi.URL, 117 Handler: openapi.NewDocHandler(instrumentOpts), 118 Methods: methods(openapi.HTTPMethod), 119 }); err != nil { 120 return err 121 } 122 if err := h.registry.Register(queryhttp.RegisterOptions{ 123 PathPrefix: openapi.StaticURLPrefix, 124 Handler: openapi.StaticHandler(), 125 }); err != nil { 126 return err 127 } 128 129 // Prometheus remote read/write endpoints. 130 remoteSourceOpts := h.options.SetInstrumentOpts(instrumentOpts. 131 SetMetricsScope(instrumentOpts.MetricsScope(). 132 Tagged(remoteSource). 133 Tagged(v1APIGroup), 134 )) 135 136 promRemoteReadHandler := remote.NewPromReadHandler(remoteSourceOpts) 137 promRemoteWriteHandler, err := remote.NewPromWriteHandler(remoteSourceOpts) 138 if err != nil { 139 return err 140 } 141 142 nativeSourceOpts := h.options.SetInstrumentOpts(instrumentOpts. 143 SetMetricsScope(instrumentOpts.MetricsScope(). 144 Tagged(nativeSource). 145 Tagged(v1APIGroup), 146 )) 147 148 promqlQueryHandler, err := prom.NewReadHandler(nativeSourceOpts, 149 prom.WithEngine(h.options.PrometheusEngineFn())) 150 if err != nil { 151 return err 152 } 153 promqlInstantQueryHandler, err := prom.NewReadHandler(nativeSourceOpts, 154 prom.WithInstantEngine(h.options.PrometheusEngineFn())) 155 if err != nil { 156 return err 157 } 158 nativePromReadHandler := native.NewPromReadHandler(nativeSourceOpts) 159 nativePromReadInstantHandler := native.NewPromReadInstantHandler(nativeSourceOpts) 160 161 h.options.QueryRouter().Setup(options.QueryRouterOptions{ 162 DefaultQueryEngine: h.options.DefaultQueryEngine(), 163 PromqlHandler: promqlQueryHandler.ServeHTTP, 164 M3QueryHandler: nativePromReadHandler.ServeHTTP, 165 }) 166 167 h.options.InstantQueryRouter().Setup(options.QueryRouterOptions{ 168 DefaultQueryEngine: h.options.DefaultQueryEngine(), 169 PromqlHandler: promqlInstantQueryHandler.ServeHTTP, 170 M3QueryHandler: nativePromReadInstantHandler.ServeHTTP, 171 }) 172 173 // Query routable endpoints. 174 if err := h.registry.Register(queryhttp.RegisterOptions{ 175 Path: native.PromReadURL, 176 Handler: h.options.QueryRouter(), 177 Methods: native.PromReadHTTPMethods, 178 MiddlewareOverride: native.WithRangeQueryParamsAndRangeRewriting, 179 }); err != nil { 180 return err 181 } 182 if err := h.registry.Register(queryhttp.RegisterOptions{ 183 Path: native.PromReadInstantURL, 184 Handler: h.options.InstantQueryRouter(), 185 Methods: native.PromReadInstantHTTPMethods, 186 MiddlewareOverride: native.WithInstantQueryParamsAndRangeRewriting, 187 }); err != nil { 188 return err 189 } 190 191 // Prometheus endpoints. 192 if err := h.registry.Register(queryhttp.RegisterOptions{ 193 Path: "/prometheus" + native.PromReadURL, 194 Handler: promqlQueryHandler, 195 Methods: native.PromReadHTTPMethods, 196 MiddlewareOverride: native.WithRangeQueryParamsAndRangeRewriting, 197 }); err != nil { 198 return err 199 } 200 if err := h.registry.Register(queryhttp.RegisterOptions{ 201 Path: "/prometheus" + native.PromReadInstantURL, 202 Handler: promqlInstantQueryHandler, 203 Methods: native.PromReadInstantHTTPMethods, 204 MiddlewareOverride: native.WithInstantQueryParamsAndRangeRewriting, 205 }); err != nil { 206 return err 207 } 208 209 // M3Query endpoints. 210 if err := h.registry.Register(queryhttp.RegisterOptions{ 211 Path: "/m3query" + native.PromReadURL, 212 Handler: nativePromReadHandler, 213 Methods: native.PromReadHTTPMethods, 214 MiddlewareOverride: native.WithRangeQueryParamsAndRangeRewriting, 215 }); err != nil { 216 return err 217 } 218 if err := h.registry.Register(queryhttp.RegisterOptions{ 219 Path: "/m3query" + native.PromReadInstantURL, 220 Handler: nativePromReadInstantHandler, 221 Methods: native.PromReadInstantHTTPMethods, 222 MiddlewareOverride: native.WithInstantQueryParamsAndRangeRewriting, 223 }); err != nil { 224 return err 225 } 226 227 // Prometheus remote read and write endpoints. 228 if err := h.registry.Register(queryhttp.RegisterOptions{ 229 Path: remote.PromReadURL, 230 Handler: promRemoteReadHandler, 231 Methods: remote.PromReadHTTPMethods, 232 }); err != nil { 233 return err 234 } 235 if err := h.registry.Register(queryhttp.RegisterOptions{ 236 Path: remote.PromWriteURL, 237 Handler: promRemoteWriteHandler, 238 Methods: methods(remote.PromWriteHTTPMethod), 239 // Register with no response logging for write calls since so frequent. 240 MiddlewareOverride: middleware.WithNoResponseLogging, 241 }); err != nil { 242 return err 243 } 244 245 // InfluxDB write endpoint. 246 if err := h.registry.Register(queryhttp.RegisterOptions{ 247 Path: influxdb.InfluxWriteURL, 248 Handler: influxdb.NewInfluxWriterHandler(h.options), 249 Methods: methods(influxdb.InfluxWriteHTTPMethod), 250 // Register with no response logging for write calls since so frequent. 251 MiddlewareOverride: middleware.WithNoResponseLogging, 252 }); err != nil { 253 return err 254 } 255 256 // Native M3 search and write endpoints. 257 if err := h.registry.Register(queryhttp.RegisterOptions{ 258 Path: handler.SearchURL, 259 Handler: handler.NewSearchHandler(h.options), 260 Methods: methods(handler.SearchHTTPMethod), 261 }); err != nil { 262 return err 263 } 264 if err := h.registry.Register(queryhttp.RegisterOptions{ 265 Path: m3json.WriteJSONURL, 266 Handler: m3json.NewWriteJSONHandler(h.options), 267 Methods: methods(m3json.JSONWriteHTTPMethod), 268 }); err != nil { 269 return err 270 } 271 272 // Readiness endpoint. 273 if err := h.registry.Register(queryhttp.RegisterOptions{ 274 Path: handler.ReadyURL, 275 Handler: handler.NewReadyHandler(h.options), 276 Methods: methods(handler.ReadyHTTPMethod), 277 }); err != nil { 278 return err 279 } 280 281 // Tag completion endpoints. 282 if err := h.registry.Register(queryhttp.RegisterOptions{ 283 Path: native.CompleteTagsURL, 284 Handler: native.NewCompleteTagsHandler(h.options), 285 Methods: methods(native.CompleteTagsHTTPMethod), 286 MiddlewareOverride: native.WithQueryParams, 287 }); err != nil { 288 return err 289 } 290 if err := h.registry.Register(queryhttp.RegisterOptions{ 291 Path: remote.TagValuesURL, 292 Handler: remote.NewTagValuesHandler(h.options), 293 Methods: methods(remote.TagValuesHTTPMethod), 294 MiddlewareOverride: native.WithQueryParams, 295 }); err != nil { 296 return err 297 } 298 299 // List tag endpoints. 300 if err := h.registry.Register(queryhttp.RegisterOptions{ 301 Path: native.ListTagsURL, 302 Handler: native.NewListTagsHandler(h.options), 303 Methods: native.ListTagsHTTPMethods, 304 MiddlewareOverride: native.WithQueryParams, 305 }); err != nil { 306 return err 307 } 308 309 // Query parse endpoints. 310 if err := h.registry.Register(queryhttp.RegisterOptions{ 311 Path: native.PromParseURL, 312 Handler: native.NewPromParseHandler(h.options), 313 Methods: methods(native.PromParseHTTPMethod), 314 }); err != nil { 315 return err 316 } 317 if err := h.registry.Register(queryhttp.RegisterOptions{ 318 Path: native.PromThresholdURL, 319 Handler: native.NewPromThresholdHandler(h.options), 320 Methods: methods(native.PromThresholdHTTPMethod), 321 }); err != nil { 322 return err 323 } 324 325 // Series match endpoints. 326 if err := h.registry.Register(queryhttp.RegisterOptions{ 327 Path: route.SeriesMatchURL, 328 Handler: remote.NewPromSeriesMatchHandler(h.options), 329 Methods: remote.PromSeriesMatchHTTPMethods, 330 MiddlewareOverride: native.WithQueryParams, 331 }); err != nil { 332 return err 333 } 334 335 // Graphite routable endpoints. 336 h.options.GraphiteRenderRouter().Setup(options.GraphiteRenderRouterOptions{ 337 RenderHandler: graphite.NewRenderHandler(h.options).ServeHTTP, 338 }) 339 h.options.GraphiteFindRouter().Setup(options.GraphiteFindRouterOptions{ 340 FindHandler: graphite.NewFindHandler(h.options).ServeHTTP, 341 }) 342 if err := h.registry.Register(queryhttp.RegisterOptions{ 343 Path: graphite.ReadURL, 344 Handler: h.options.GraphiteRenderRouter(), 345 Methods: graphite.ReadHTTPMethods, 346 }); err != nil { 347 return err 348 } 349 if err := h.registry.Register(queryhttp.RegisterOptions{ 350 Path: graphite.FindURL, 351 Handler: h.options.GraphiteFindRouter(), 352 Methods: graphite.FindHTTPMethods, 353 }); err != nil { 354 return err 355 } 356 357 placementOpts, err := h.placementOpts() 358 if err != nil { 359 return err 360 } 361 362 var ( 363 serviceOptionDefaults = h.options.ServiceOptionDefaults() 364 clusterClient = h.options.ClusterClient() 365 config = h.options.Config() 366 ) 367 368 debugWriter, err := xdebug.NewZipWriterWithDefaultSources( 369 h.options.CPUProfileDuration(), 370 instrumentOpts) 371 if err != nil { 372 return fmt.Errorf("unable to create debug writer: %v", err) 373 } 374 375 // Register debug dump handler. 376 if err := h.registry.Register(queryhttp.RegisterOptions{ 377 Path: xdebug.DebugURL, 378 Handler: debugWriter.HTTPHandler(), 379 Methods: methods(xdebug.DebugMethod), 380 }); err != nil { 381 return err 382 } 383 384 if clusterClient != nil { 385 err = database.RegisterRoutes(h.registry, clusterClient, 386 h.options.Config(), h.options.EmbeddedDBCfg(), 387 serviceOptionDefaults, instrumentOpts, 388 h.options.NamespaceValidator(), h.options.KVStoreProtoParser()) 389 if err != nil { 390 return err 391 } 392 393 routes := placementhandler.MakeRoutes(serviceOptionDefaults, placementOpts) 394 for _, route := range routes { 395 err := h.registry.RegisterPaths(route.Paths, queryhttp.RegisterPathsOptions{ 396 Handler: route.Handler, 397 Methods: route.Methods, 398 }) 399 if err != nil { 400 return err 401 } 402 } 403 404 err = namespace.RegisterRoutes(h.registry, clusterClient, 405 h.options.Clusters(), serviceOptionDefaults, instrumentOpts, 406 h.options.NamespaceValidator()) 407 if err != nil { 408 return err 409 } 410 411 err = topic.RegisterRoutes(h.registry, clusterClient, config, instrumentOpts) 412 if err != nil { 413 return err 414 } 415 } 416 417 if err := h.registerHealthEndpoints(); err != nil { 418 return err 419 } 420 if err := h.registerProfileEndpoints(); err != nil { 421 return err 422 } 423 if err := h.registerRoutesEndpoint(); err != nil { 424 return err 425 } 426 427 customMiddle := make(map[*mux.Route]middleware.OverrideOptions) 428 // Register custom endpoints last to have these conflict with 429 // any existing routes. 430 for _, custom := range h.customHandlers { 431 for _, method := range custom.Methods() { 432 var prevHandler http.Handler 433 entry, prevRoute := h.registry.PathEntry(custom.Route(), method) 434 if prevRoute { 435 prevHandler = entry.GetHandler() 436 } 437 438 handler, err := custom.Handler(nativeSourceOpts, prevHandler) 439 if err != nil { 440 return err 441 } 442 443 if !prevRoute { 444 if err := h.registry.Register(queryhttp.RegisterOptions{ 445 Path: custom.Route(), 446 Handler: handler, 447 Methods: methods(method), 448 MiddlewareOverride: custom.MiddlewareOverride(), 449 }); err != nil { 450 return err 451 } 452 } else { 453 customMiddle[entry] = custom.MiddlewareOverride() 454 entry.Handler(handler) 455 } 456 } 457 } 458 459 // NB: the double http_handler was accidentally introduced and now we are 460 // stuck with it for backwards compatibility. 461 middleIOpts := instrumentOpts.SetMetricsScope( 462 h.options.InstrumentOpts().MetricsScope().SubScope("http_handler_http_handler")) 463 464 // Apply middleware after the custom handlers have overridden the previous handlers so the middleware functions 465 // are dispatched before the custom handler. 466 // req -> middleware fns -> custom handler -> previous handler. 467 err = h.registry.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { 468 handler := route.GetHandler() 469 opts := middleware.Options{ 470 InstrumentOpts: middleIOpts, 471 Route: route, 472 Clock: clockwork.NewRealClock(), 473 Logging: middleware.NewLoggingOptions(h.middlewareConfig.Logging), 474 Metrics: middleware.MetricsOptions{ 475 Config: h.middlewareConfig.Metrics, 476 ParseOptions: promql.NewParseOptions(). 477 SetRequireStartEndTime(h.options.Config().Query.RequireLabelsEndpointStartEndTime). 478 SetNowFn(h.options.NowFn()), 479 }, 480 PrometheusRangeRewrite: middleware.PrometheusRangeRewriteOptions{ 481 FetchOptionsBuilder: h.options.FetchOptionsBuilder(), 482 ResolutionMultiplier: h.middlewareConfig.Prometheus.ResolutionMultiplier, 483 DefaultLookback: h.options.DefaultLookback(), 484 Storage: h.options.Storage(), 485 PrometheusEngineFn: h.options.PrometheusEngineFn(), 486 }, 487 } 488 override := h.registry.MiddlewareOpts(route) 489 if override != nil { 490 opts = override(opts) 491 } 492 if customMiddle[route] != nil { 493 opts = customMiddle[route](opts) 494 } 495 middle := h.options.RegisterMiddleware()(opts) 496 497 // iterate through in reverse order so each Middleware fn gets the proper next handler to dispatch. this ensures the 498 // Middleware is dispatched in the expected order (first -> last). 499 for i := len(middle) - 1; i >= 0; i-- { 500 handler = middle[i].Middleware(handler) 501 } 502 503 route.Handler(handler) 504 return nil 505 }) 506 507 if err != nil { 508 return err 509 } 510 511 return nil 512 } 513 514 func (h *Handler) placementOpts() (placementhandler.HandlerOptions, error) { 515 return placementhandler.NewHandlerOptions( 516 h.options.ClusterClient(), 517 h.options.Config().ClusterManagement.Placement, 518 h.m3AggServiceOptions(), 519 h.options.InstrumentOpts(), 520 ) 521 } 522 523 func (h *Handler) m3AggServiceOptions() *handleroptions.M3AggServiceOptions { 524 clusters := h.options.Clusters() 525 if clusters == nil { 526 return nil 527 } 528 529 maxResolution := time.Duration(0) 530 for _, ns := range clusters.ClusterNamespaces() { 531 resolution := ns.Options().Attributes().Resolution 532 if resolution > maxResolution { 533 maxResolution = resolution 534 } 535 } 536 537 if maxResolution == 0 { 538 return nil 539 } 540 541 return &handleroptions.M3AggServiceOptions{ 542 MaxAggregationWindowSize: maxResolution, 543 } 544 } 545 546 // Endpoints useful for profiling the service. 547 func (h *Handler) registerHealthEndpoints() error { 548 return h.registry.Register(queryhttp.RegisterOptions{ 549 Path: healthURL, 550 Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 551 json.NewEncoder(w).Encode(struct { 552 Uptime string `json:"uptime"` 553 Now string `json:"now"` 554 }{ 555 Uptime: time.Since(h.options.CreatedAt()).String(), 556 Now: time.Now().String(), 557 }) 558 }), 559 Methods: methods(http.MethodGet), 560 }) 561 } 562 563 // Endpoints useful for profiling the service. 564 func (h *Handler) registerProfileEndpoints() error { 565 debugHandler := http.NewServeMux() 566 xdebug.RegisterPProfHandlers(debugHandler) 567 568 return h.registry.Register(queryhttp.RegisterOptions{ 569 PathPrefix: "/debug/pprof", 570 Handler: debugHandler, 571 }) 572 } 573 574 // Endpoints useful for viewing routes directory. 575 func (h *Handler) registerRoutesEndpoint() error { 576 return h.registry.Register(queryhttp.RegisterOptions{ 577 Path: routesURL, 578 Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 579 var routes []string 580 err := h.registry.Walk( 581 func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { 582 str, err := route.GetPathTemplate() 583 if err != nil { 584 return err 585 } 586 routes = append(routes, str) 587 return nil 588 }) 589 if err != nil { 590 xhttp.WriteError(w, err) 591 return 592 } 593 json.NewEncoder(w).Encode(struct { 594 Routes []string `json:"routes"` 595 }{ 596 Routes: routes, 597 }) 598 }), 599 Methods: methods(http.MethodGet), 600 }) 601 } 602 603 func methods(str ...string) []string { 604 return str 605 }