github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/server/query/server.go (about) 1 /* 2 Copyright 2023. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package queryserver 18 19 import ( 20 "crypto/tls" 21 "fmt" 22 htmltemplate "html/template" 23 "net" 24 texttemplate "text/template" 25 "time" 26 27 "github.com/fasthttp/router" 28 "github.com/oklog/run" 29 "github.com/siglens/siglens/pkg/alerts/alertsHandler" 30 "github.com/siglens/siglens/pkg/config" 31 "github.com/siglens/siglens/pkg/hooks" 32 "github.com/siglens/siglens/pkg/segment/query" 33 server_utils "github.com/siglens/siglens/pkg/server/utils" 34 "github.com/siglens/siglens/pkg/utils" 35 log "github.com/sirupsen/logrus" 36 "github.com/valyala/fasthttp" 37 "github.com/valyala/fasthttp/pprofhandler" 38 ) 39 40 type queryserverCfg struct { 41 Config config.WebConfig 42 Addr string 43 // Log *zap.Logger //ToDo implement debug logger 44 ln net.Listener 45 lnTls net.Listener 46 Router *router.Router 47 debug bool 48 } 49 50 var ( 51 corsAllowHeaders = "Access-Control-Allow-Origin, Access-Control-Request-Method, Access-Control-Allow-Methods, Access-Control-Max-Age, Content-Type, Authorization, Origin, X-Requested-With , Accept" 52 corsAllowMethods = "HEAD,GET,POST,PUT,DELETE,OPTIONS,UPGRADE" 53 corsAllowOrigin = "*" 54 ) 55 56 // ConstructHttpServer new fasthttp server 57 func ConstructQueryServer(cfg config.WebConfig, ServerAddr string) *queryserverCfg { 58 59 s := &queryserverCfg{ 60 Config: cfg, 61 Addr: ServerAddr, 62 Router: router.New(), 63 debug: true, 64 } 65 return s 66 } 67 68 func (hs *queryserverCfg) Close() { 69 _ = hs.ln.Close() 70 } 71 72 func getMyIds() []uint64 { 73 myids := make([]uint64, 1) 74 75 alreadyHandled := false 76 if hook := hooks.GlobalHooks.GetIdsConditionHook; hook != nil { 77 alreadyHandled = hook(myids) 78 } 79 80 if !alreadyHandled { 81 myids[0] = 0 82 } 83 84 return myids 85 } 86 87 func (hs *queryserverCfg) Run(htmlTemplate *htmltemplate.Template, textTemplate *texttemplate.Template) error { 88 query.InitQueryMetrics() 89 90 alertsHandler.InitAlertingService(getMyIds) 91 alertsHandler.InitMinionSearchService(getMyIds) 92 93 err := query.InitQueryNode(getMyIds, server_utils.ExtractKibanaRequests) 94 if err != nil { 95 log.Errorf("Failed to initialize query node: %v", err) 96 return err 97 } 98 99 hs.Router.GET("/{filename}.html", func(ctx *fasthttp.RequestCtx) { 100 renderHtmlTemplate(ctx, htmlTemplate) 101 }) 102 hs.Router.GET("/js/{filename}.js", func(ctx *fasthttp.RequestCtx) { 103 renderJavaScriptTemplate(ctx, textTemplate) 104 }) 105 hs.Router.GET(server_utils.API_PREFIX+"/search/live_tail", hs.Recovery(liveTailHandler())) 106 hs.Router.POST(server_utils.API_PREFIX+"/search/live_tail", hs.Recovery(liveTailHandler())) 107 hs.Router.POST(server_utils.API_PREFIX+"/search", hs.Recovery(pipeSearchHandler())) 108 hs.Router.POST(server_utils.API_PREFIX+"/search/{dbPanel-id}", hs.Recovery(dashboardPipeSearchHandler())) 109 hs.Router.GET(server_utils.API_PREFIX+"/search/ws", hs.Recovery(pipeSearchWebsocketHandler())) 110 111 hs.Router.POST(server_utils.API_PREFIX+"/search/ws", hs.Recovery(pipeSearchWebsocketHandler())) 112 hs.Router.POST(server_utils.API_PREFIX+"/sampledataset_bulk", hs.Recovery(sampleDatasetBulkHandler())) 113 114 // common routes 115 116 hs.Router.GET(server_utils.API_PREFIX+"/health", hs.Recovery(getHealthHandler())) 117 hs.Router.POST(server_utils.API_PREFIX+"/setconfig/transient", hs.Recovery(postSetconfigHandler(false))) 118 hs.Router.POST(server_utils.API_PREFIX+"/setconfig/persistent", hs.Recovery(postSetconfigHandler(true))) 119 hs.Router.GET(server_utils.API_PREFIX+"/config", hs.Recovery(getConfigHandler())) 120 hs.Router.POST(server_utils.API_PREFIX+"/config/reload", hs.Recovery(getConfigReloadHandler())) 121 122 //elasticsearch routes - common to both ingest and query 123 hs.Router.GET(server_utils.ELASTIC_PREFIX+"/", hs.Recovery(esGreetHandler())) 124 125 //elasticsearch routes - specific to query 126 hs.Router.POST(server_utils.ELASTIC_PREFIX+"/search", hs.Recovery(esGetSearchHandler())) 127 hs.Router.GET(server_utils.ELASTIC_PREFIX+"/_search", hs.Recovery(esGetSearchHandler())) 128 hs.Router.GET(server_utils.ELASTIC_PREFIX+"/{indexName}/_search", hs.Recovery(esGetSearchHandler())) 129 hs.Router.POST(server_utils.ELASTIC_PREFIX+"/_search", hs.Recovery(esGetSearchHandler())) 130 hs.Router.POST(server_utils.ELASTIC_PREFIX+"/{indexName}/_search", hs.Recovery(esGetSearchHandler())) 131 hs.Router.POST(server_utils.ELASTIC_PREFIX+"/{indexName}/_doc/_search", hs.Recovery(esGetSearchHandler())) 132 133 hs.Router.GET(server_utils.ELASTIC_PREFIX+"/{indexName}/{docType}/_search", hs.Recovery(esGetSearchHandler())) 134 hs.Router.POST(server_utils.ELASTIC_PREFIX+"/{indexName}/{docType}/_search", hs.Recovery(esGetSearchHandler())) 135 136 hs.Router.HEAD(server_utils.ELASTIC_PREFIX+"/", hs.Recovery(headHandler())) 137 138 hs.Router.GET(server_utils.ELASTIC_PREFIX+"/{indexName}/{docType}/{idVal}", hs.Recovery(esGetSingleDocHandler())) 139 hs.Router.HEAD(server_utils.ELASTIC_PREFIX+"/{indexName}/{docType}/{idVal}", hs.Recovery(esGetSingleDocHandler())) 140 141 // aliases api 142 hs.Router.GET(server_utils.ELASTIC_PREFIX+"/{indexName}/_alias/{aliasName}", hs.Recovery(esGetIndexAliasesHandler())) 143 hs.Router.GET(server_utils.ELASTIC_PREFIX+"/_alias/{aliasName}", hs.Recovery(esGetAliasHandler())) 144 hs.Router.HEAD(server_utils.ELASTIC_PREFIX+"/_alias/{aliasName}", hs.Recovery(esGetAliasHandler())) 145 hs.Router.HEAD(server_utils.ELASTIC_PREFIX+"/{indexName}/_alias/{aliasName?}", hs.Recovery(esGetIndexAliasesHandler())) 146 147 hs.Router.POST(server_utils.ELASTIC_PREFIX+"/_aliases", hs.Recovery(esPostAliasesHandler())) 148 149 hs.Router.PUT(server_utils.ELASTIC_PREFIX+"/{indexName}/_alias/{aliasName}", hs.Recovery(esPutIndexAliasHandler())) 150 hs.Router.PUT(server_utils.ELASTIC_PREFIX+"/{indexName}/_aliases/{aliasName}", hs.Recovery(esPutIndexAliasHandler())) 151 hs.Router.POST(server_utils.ELASTIC_PREFIX+"/{indexName}/_alias/{aliasName}", hs.Recovery(esPutIndexAliasHandler())) 152 hs.Router.POST(server_utils.ELASTIC_PREFIX+"/{indexName}/_aliases/{aliasName}", hs.Recovery(esPutIndexAliasHandler())) 153 154 hs.Router.GET(server_utils.ELASTIC_PREFIX+"/_aliases", hs.Recovery(esGetAllAliasesHandler())) 155 hs.Router.GET(server_utils.ELASTIC_PREFIX+"/_cat/aliases", hs.Recovery(esGetAllAliasesHandler())) 156 157 hs.Router.HEAD(server_utils.ELASTIC_PREFIX+"/{indexName}", hs.Recovery(esGetIndexAliasExistsHandler())) 158 /* 159 hs.router.DELETE(ELASTIC_PREFIX+"/{indexName}/_alias/{aliasName}", hs.Recovery(esDeleteAliasHandler())) 160 */ 161 162 //loki endpoint 163 hs.Router.GET(server_utils.LOKI_PREFIX+"/api/v1/labels", hs.Recovery(lokiLabelsHandler())) 164 hs.Router.GET(server_utils.LOKI_PREFIX+"/api/v1/label/{labelName}/values", hs.Recovery(lokiLabelValueHandler())) 165 hs.Router.GET(server_utils.LOKI_PREFIX+"/api/v1/query", hs.Recovery(lokiQueryHandler())) 166 hs.Router.GET(server_utils.LOKI_PREFIX+"/api/v1/query_range", hs.Recovery(lokiQueryHandler())) 167 hs.Router.GET(server_utils.LOKI_PREFIX+"/api/v1/index/stats", hs.Recovery(lokiIndexStatsHandler())) 168 hs.Router.GET(server_utils.LOKI_PREFIX+"/api/v1/series", hs.Recovery(lokiSeriesHandler())) 169 hs.Router.POST(server_utils.LOKI_PREFIX+"/api/v1/series", hs.Recovery(lokiSeriesHandler())) 170 171 //splunk endpoint 172 hs.Router.GET("/services/collector/health", hs.Recovery(getHealthHandler())) 173 hs.Router.GET("/services/collector/health/1.0", hs.Recovery(getHealthHandler())) 174 175 //OTSDB query endpoint 176 hs.Router.GET(server_utils.OTSDB_PREFIX+"/api/query", hs.Recovery(otsdbMetricQueryHandler())) 177 hs.Router.POST(server_utils.OTSDB_PREFIX+"/api/query", hs.Recovery(otsdbMetricQueryHandler())) 178 hs.Router.POST(server_utils.OTSDB_PREFIX+"/api/v1/query/exp", hs.Recovery(otsdbMetricQueryExpHandler())) 179 180 //prometheus query endpoint 181 hs.Router.POST(server_utils.PROMQL_PREFIX+"/api/v1/query", hs.Recovery(metricsSearchHandler())) 182 hs.Router.GET(server_utils.PROMQL_PREFIX+"/api/v1/query", hs.Recovery(metricsSearchHandler())) 183 hs.Router.POST(server_utils.PROMQL_PREFIX+"/api/ui/query", hs.Recovery(uiMetricsSearchHandler())) 184 185 // search api Handlers 186 hs.Router.POST(server_utils.API_PREFIX+"/echo", hs.Recovery(pipeSearchHandler())) 187 hs.Router.GET(server_utils.API_PREFIX+"/listIndices", hs.Recovery(listIndicesHandler())) 188 hs.Router.GET(server_utils.API_PREFIX+"/clusterStats", hs.Recovery(getClusterStatsHandler())) 189 hs.Router.POST(server_utils.API_PREFIX+"/clusterIngestStats", hs.Recovery(getClusterIngestStatsHandler())) 190 hs.Router.POST(server_utils.API_PREFIX+"/usersavedqueries/save", hs.Recovery(saveUserSavedQueriesHandler())) 191 hs.Router.GET(server_utils.API_PREFIX+"/usersavedqueries/getall", hs.Recovery(getUserSavedQueriesAllHandler())) 192 hs.Router.GET(server_utils.API_PREFIX+"/usersavedqueries/deleteone/{qname}", hs.Recovery(deleteUserSavedQueryHandler())) 193 hs.Router.GET(server_utils.API_PREFIX+"/usersavedqueries/{qname}", hs.Recovery(SearchUserSavedQueryHandler())) 194 hs.Router.GET(server_utils.API_PREFIX+"/pqs/clear", hs.Recovery(postPqsClearHandler())) 195 hs.Router.GET(server_utils.API_PREFIX+"/pqs/get", hs.Recovery(getPqsEnabledHandler())) 196 hs.Router.POST(server_utils.API_PREFIX+"/pqs/aggs", hs.Recovery(postPqsAggColsHandler())) 197 hs.Router.POST(server_utils.API_PREFIX+"/pqs/update", hs.Recovery(postPqsHandler())) 198 hs.Router.GET(server_utils.API_PREFIX+"/pqs", hs.Recovery(getPqsHandler())) 199 hs.Router.GET(server_utils.API_PREFIX+"/pqs/{pqid}", hs.Recovery(getPqsByIdHandler())) 200 hs.Router.POST(server_utils.API_PREFIX+"/dashboards/create", hs.Recovery(createDashboardHandler())) 201 hs.Router.GET(server_utils.API_PREFIX+"/dashboards/defaultlistall", hs.Recovery(getDefaultDashboardIdsHandler())) 202 hs.Router.GET(server_utils.API_PREFIX+"/dashboards/listall", hs.Recovery(getDashboardIdsHandler())) 203 hs.Router.POST(server_utils.API_PREFIX+"/dashboards/update", hs.Recovery(updateDashboardHandler())) 204 hs.Router.GET(server_utils.API_PREFIX+"/dashboards/{dashboard-id}", hs.Recovery(getDashboardIdHandler())) 205 hs.Router.GET(server_utils.API_PREFIX+"/dashboards/delete/{dashboard-id}", hs.Recovery(deleteDashboardHandler())) 206 hs.Router.PUT(server_utils.API_PREFIX+"/dashboards/favorite/{dashboard-id}", hs.Recovery(favoriteDashboardHandler())) 207 hs.Router.GET(server_utils.API_PREFIX+"/dashboards/listfavorites", hs.Recovery(getFavoriteDashboardIdsHandler())) 208 hs.Router.GET(server_utils.API_PREFIX+"/version/info", hs.Recovery(getVersionHandler())) 209 210 // alerting api endpoints 211 hs.Router.POST(server_utils.API_PREFIX+"/alerts/create", hs.Recovery(createAlertHandler())) 212 hs.Router.GET(server_utils.API_PREFIX+"/alerts/{alertID}", hs.Recovery(getAlertHandler())) 213 hs.Router.GET(server_utils.API_PREFIX+"/allalerts", hs.Recovery(getAllAlertsHandler())) 214 hs.Router.POST(server_utils.API_PREFIX+"/alerts/update", hs.Recovery(updateAlertHandler())) 215 hs.Router.DELETE(server_utils.API_PREFIX+"/alerts/delete", hs.Recovery(deleteAlertHandler())) 216 hs.Router.GET(server_utils.API_PREFIX+"/alerts/{alertID}/history", hs.Recovery(alertHistoryHandler())) 217 hs.Router.POST(server_utils.API_PREFIX+"/alerts/createContact", hs.Recovery(createContactHandler())) 218 hs.Router.GET(server_utils.API_PREFIX+"/alerts/allContacts", hs.Recovery(getAllContactsHandler())) 219 hs.Router.POST(server_utils.API_PREFIX+"/alerts/updateContact", hs.Recovery(updateContactHandler())) 220 hs.Router.DELETE(server_utils.API_PREFIX+"/alerts/deleteContact", hs.Recovery(deleteContactHandler())) 221 hs.Router.PUT(server_utils.API_PREFIX+"/alerts/silenceAlert", hs.Recovery(silenceAlertHandler())) 222 223 hs.Router.GET(server_utils.API_PREFIX+"/minionsearch/allMinionSearches", hs.Recovery(getAllMinionSearchesHandler())) 224 hs.Router.POST(server_utils.API_PREFIX+"/minionsearch/createMinionSearches", hs.Recovery(createMinionSearchHandler())) 225 hs.Router.GET(server_utils.API_PREFIX+"/minionsearch/{alertID}", hs.Recovery(getMinionSearchHandler())) 226 227 // tracing api endpoints 228 hs.Router.POST(server_utils.API_PREFIX+"/traces/search", hs.Recovery(searchTracesHandler())) 229 hs.Router.GET(server_utils.API_PREFIX+"/traces/dependencies", hs.Recovery(getDependencyGraphHandler())) 230 hs.Router.POST(server_utils.API_PREFIX+"/traces/ganttChart", hs.Recovery(ganttChartHandler())) 231 hs.Router.POST(server_utils.API_PREFIX+"/traces/count", hs.Recovery((totalTracesHandler()))) 232 // query server should still setup ES APIs for Kibana integration 233 hs.Router.POST(server_utils.ELASTIC_PREFIX+"/_bulk", hs.Recovery(esPostBulkHandler())) 234 hs.Router.PUT(server_utils.ELASTIC_PREFIX+"/{indexName}", hs.Recovery(esPutIndexHandler())) 235 236 hs.Router.GET(server_utils.API_PREFIX+"/system-info", hs.Recovery(getSystemInfoHandler())) 237 if config.IsDebugMode() { 238 hs.Router.GET("/debug/pprof/{profile:*}", pprofhandler.PprofHandler) 239 } 240 241 if hook := hooks.GlobalHooks.ExtraQueryEndpointsHook; hook != nil { 242 err := hook(hs.Router, hs.Recovery) 243 if err != nil { 244 log.Errorf("Run: error in ExtraQueryEndpointsHook: %v", err) 245 return err 246 } 247 } 248 249 if hook := hooks.GlobalHooks.ServeStaticHook; hook != nil { 250 hook(hs.Router, htmlTemplate) 251 } else { 252 hook = func(router *router.Router, htmlTemplate *htmltemplate.Template) { 253 router.GET("/{filepath:*}", func(ctx *fasthttp.RequestCtx) { 254 filepath := ctx.UserValue("filepath").(string) 255 if filepath == "" { 256 // Render index.html and send that. 257 ctx.Response.Header.Set("Content-Type", "text/html; charset=utf-8") 258 err := htmlTemplate.ExecuteTemplate(ctx, "index.html", hooks.GlobalHooks.HtmlSnippets) 259 if err != nil { 260 log.Fatalf("serveStatic: error executing index.html template: %v", err) 261 } 262 263 return 264 } 265 266 fasthttp.ServeFile(ctx, "static/"+filepath) 267 }) 268 } 269 270 hook(hs.Router, htmlTemplate) 271 } 272 273 hs.ln, err = net.Listen("tcp4", hs.Addr) 274 if err != nil { 275 return err 276 } 277 278 s := &fasthttp.Server{ 279 Handler: cors(hs.Router.Handler), 280 Name: hs.Config.Name, 281 ReadBufferSize: hs.Config.ReadBufferSize, 282 MaxConnsPerIP: hs.Config.MaxConnsPerIP, 283 MaxRequestsPerConn: hs.Config.MaxRequestsPerConn, 284 MaxRequestBodySize: hs.Config.MaxRequestBodySize, // 100 << 20, // 100MB // 1000 * 4, // MaxRequestBodySize: 285 Concurrency: hs.Config.Concurrency, 286 } 287 var g run.Group 288 289 if config.IsTlsEnabled() { 290 cfg := &tls.Config{ 291 Certificates: make([]tls.Certificate, 1), 292 } 293 294 cfg.Certificates[0], err = tls.LoadX509KeyPair(config.GetTLSCertificatePath(), config.GetTLSPrivateKeyPath()) 295 296 if err != nil { 297 fmt.Println("Run: error in loading TLS certificate: ", err) 298 log.Fatalf("Run: error in loading TLS certificate: %v", err) 299 } 300 301 hs.lnTls = tls.NewListener(hs.ln, cfg) 302 303 // run fasthttp server 304 g.Add(func() error { 305 return s.Serve(hs.lnTls) 306 }, func(e error) { 307 _ = hs.ln.Close() 308 }) 309 310 } else { 311 // run fasthttp server 312 g.Add(func() error { 313 return s.Serve(hs.ln) 314 }, func(e error) { 315 _ = hs.ln.Close() 316 }) 317 } 318 return g.Run() 319 } 320 321 func renderHtmlTemplate(ctx *fasthttp.RequestCtx, tpl *htmltemplate.Template) { 322 filename := utils.ExtractParamAsString(ctx.UserValue("filename")) 323 ctx.Response.Header.Set("Content-Type", "text/html; charset=utf-8") 324 err := tpl.ExecuteTemplate(ctx, filename+".html", hooks.GlobalHooks.HtmlSnippets) 325 if err != nil { 326 log.Errorf("renderHtmlTemplate: unable to execute template, err: %v", err.Error()) 327 return 328 } 329 } 330 331 func renderJavaScriptTemplate(ctx *fasthttp.RequestCtx, tpl *texttemplate.Template) { 332 filename := utils.ExtractParamAsString(ctx.UserValue("filename")) 333 ctx.Response.Header.Set("Content-Type", "application/javascript; charset=utf-8") 334 err := tpl.ExecuteTemplate(ctx, filename+".js", hooks.GlobalHooks.JsSnippets) 335 if err != nil { 336 log.Errorf("renderJavaScriptTemplate: unable to execute template, err: %v", err.Error()) 337 return 338 } 339 } 340 341 func (hs *queryserverCfg) RunSafeServer() error { 342 hs.Router.GET("/health", hs.Recovery(getSafeHealthHandler())) 343 var err error 344 hs.ln, err = net.Listen("tcp4", hs.Addr) 345 if err != nil { 346 return err 347 } 348 349 s := &fasthttp.Server{ 350 Handler: cors(hs.Router.Handler), 351 Name: hs.Config.Name, 352 ReadBufferSize: hs.Config.ReadBufferSize, 353 MaxConnsPerIP: hs.Config.MaxConnsPerIP, 354 MaxRequestsPerConn: hs.Config.MaxRequestsPerConn, 355 MaxRequestBodySize: hs.Config.MaxRequestBodySize, // 100 << 20, // 100MB // 1000 * 4, // MaxRequestBodySize: 356 Concurrency: hs.Config.Concurrency, 357 } 358 359 log.Infof("Starting Ingestion Server on safe mode...") 360 ticker := time.NewTicker(1 * time.Minute) 361 go func() { 362 for range ticker.C { 363 log.Infof("SigLens Ingestion Server has started in safe mode...") 364 } 365 }() 366 367 // run fasthttp server 368 var g run.Group 369 g.Add(func() error { 370 return s.Serve(hs.ln) 371 }, func(e error) { 372 _ = hs.ln.Close() 373 }) 374 return g.Run() 375 }