alertmanager/handlers/handlers.go (301 lines of code) (raw):

/* * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ package handlers import ( "encoding/json" "fmt" "io/ioutil" "net/http" "github.com/facebookincubator/prometheus-configmanager/alertmanager/client" "github.com/facebookincubator/prometheus-configmanager/alertmanager/config" "github.com/golang/glog" "github.com/labstack/echo" ) const ( v0rootPath = "/:tenant_id" v0receiverPath = "/receiver" v0RoutePath = "/receiver/route" v0receiverNameQueryParam = "receiver" v1rootPath = "/v1" tenantIDPart = "/:tenant_id" v1TenantRootPath = v1rootPath + tenantIDPart v1receiverPath = "/receiver" v1receiverNamePath = v1receiverPath + "/:" + receiverNameParam v1routePath = "/route" v1GlobalPath = "/global" v1TenantPath = "/tenants" v1TenancyPath = "/tenancy" receiverNameParam = "receiver_name" tenantIDParam = "tenant_id" // Templates v1TemplateRoot = v1rootPath + "/:tmpl_file_name" v1TemplatePath = "/template" v1TemplatesPath = "/templates" v1TemplateSpecPath = v1TemplatePath + "/:tmpl_name" templateFilenameParam = "tmpl_file_name" templateNameParam = "tmpl_name" ) func RegisterBaseHandlers(e *echo.Echo) { e.GET("/", statusHandler) } func RegisterV0Handlers(e *echo.Echo, client client.AlertmanagerClient) { v0 := e.Group(v0rootPath) v0.Use(tenancyMiddlewareProvider(client, pathTenantProvider)) v0.POST(v0receiverPath, GetReceiverPostHandler(client)) v0.GET(v0receiverPath, GetGetReceiversHandler(client)) v0.DELETE(v0receiverPath, GetDeleteReceiverHandler(client, v0receiverNameQueryProvider)) v0.PUT(v0receiverPath+"/:"+receiverNameParam, GetUpdateReceiverHandler(client, receiverNamePathProvider)) v0.POST(v0RoutePath, GetUpdateRouteHandler(client)) v0.GET(v0RoutePath, GetGetRouteHandler(client)) } func RegisterV1Handlers(e *echo.Echo, client client.AlertmanagerClient, tmplClient client.TemplateClient) { v1 := e.Group(v1rootPath) v1Template := e.Group(v1TemplateRoot) // these don't require tenancy so register before middleware v1.GET(v1TenantPath, GetGetTenantsHandler(client)) v1.GET(v1TenancyPath, GetGetTenancyHandler(client)) v1.POST(v1GlobalPath, GetUpdateGlobalConfigHandler(client)) v1.GET(v1GlobalPath, GetGetGlobalConfigHandler(client)) v1Tenant := e.Group(v1TenantRootPath) v1Tenant.Use(tenancyMiddlewareProvider(client, pathTenantProvider)) v1Tenant.POST(v1receiverPath, GetReceiverPostHandler(client)) v1Tenant.GET(v1receiverPath, GetGetReceiversHandler(client)) v1Tenant.DELETE(v1receiverNamePath, GetDeleteReceiverHandler(client, receiverNamePathProvider)) v1Tenant.PUT(v1receiverNamePath, GetUpdateReceiverHandler(client, receiverNamePathProvider)) v1Tenant.GET(v1receiverNamePath, GetGetReceiversHandler(client)) v1Tenant.POST(v1routePath, GetUpdateRouteHandler(client)) v1Tenant.GET(v1routePath, GetGetRouteHandler(client)) v1Template.Use(stringParamProvider(templateFilenameParam)) v1Template.GET(v1TemplatePath, GetGetTemplateFileHandler(client, tmplClient)) v1Template.POST(v1TemplatePath, GetPostTemplateFileHandler(client, tmplClient)) v1Template.PUT(v1TemplatePath, GetPutTemplateFileHandler(client, tmplClient)) v1Template.DELETE(v1TemplatePath, GetDeleteTemplateFileHandler(client, tmplClient)) v1Template.Use(stringParamProvider(templateNameParam)) v1Template.GET(v1TemplatesPath, GetGetTemplatesHandler(client, tmplClient)) v1Template.GET(v1TemplateSpecPath, GetGetTemplateHandler(client, tmplClient)) v1Template.POST(v1TemplateSpecPath, GetPostTemplateHandler(client, tmplClient)) v1Template.PUT(v1TemplateSpecPath, GetPutTemplateHandler(client, tmplClient)) v1Template.DELETE(v1TemplateSpecPath, GetDeleteTemplateHandler(client, tmplClient)) } func statusHandler(c echo.Context) error { return c.String(http.StatusOK, "Alertmanager Config server") } type paramProvider func(c echo.Context) string // For v0 tenant_id field in path var pathTenantProvider = func(c echo.Context) string { return c.Param(tenantIDParam) } var v0receiverNameQueryProvider = func(c echo.Context) string { return c.QueryParam(v0receiverNameQueryParam) } var receiverNamePathProvider = func(c echo.Context) string { return c.Param(receiverNameParam) } // Returns middleware func to check for tenant_id dependent on tenancy of the client func tenancyMiddlewareProvider(client client.AlertmanagerClient, getTenantID paramProvider) echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { providedTenantID := getTenantID(c) if client.Tenancy() != nil && providedTenantID == "" { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Must provide %s parameter", tenantIDParam)) } c.Set(tenantIDParam, providedTenantID) return next(c) } } } // GetReceiverPostHandler returns a handler function that creates a new // receiver and then reloads alertmanager func GetReceiverPostHandler(client client.AlertmanagerClient) func(c echo.Context) error { return func(c echo.Context) error { defer glog.Flush() receiver, err := decodeReceiverPostRequest(c) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } tenantID := c.Get(tenantIDParam).(string) glog.Infof("Configure Receiver: Tenant: %s, receiver: %+v", tenantID, receiver) err = client.CreateReceiver(tenantID, receiver) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } err = client.ReloadAlertmanager() if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } return c.NoContent(http.StatusOK) } } // GetGetReceiversHandler returns a handler function to retrieve receivers for // a filePrefix func GetGetReceiversHandler(client client.AlertmanagerClient) func(c echo.Context) error { return func(c echo.Context) error { defer glog.Flush() tenantID := c.Get(tenantIDParam).(string) receiverName := c.Param(receiverNameParam) glog.Infof("Get Receiver: Tenant: %s, receiver: %s", tenantID, receiverName) recs, err := client.GetReceivers(tenantID) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } if receiverName != "" { for _, rec := range recs { if rec.Name == receiverName { return c.JSON(http.StatusOK, rec) } } return echo.NewHTTPError(http.StatusBadRequest, fmt.Errorf("Receiver %s not found", receiverName)) } return c.JSON(http.StatusOK, recs) } } func GetGetTenantsHandler(client client.AlertmanagerClient) func(c echo.Context) error { return func(c echo.Context) error { tenants, err := client.GetTenants() if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } return c.JSON(http.StatusOK, tenants) } } func GetGetTenancyHandler(client client.AlertmanagerClient) func(c echo.Context) error { return func(c echo.Context) error { return c.JSON(http.StatusOK, client.Tenancy()) } } // GetUpdateReceiverHandler returns a handler function to update a receivers func GetUpdateReceiverHandler(client client.AlertmanagerClient, getReceiverName paramProvider) func(c echo.Context) error { return func(c echo.Context) error { defer glog.Flush() tenantID := c.Get(tenantIDParam).(string) receiverName := getReceiverName(c) glog.Infof("Get Receiver: Tenant: %s, receiver: %s", tenantID, receiverName) newReceiver, err := decodeReceiverPostRequest(c) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } err = client.UpdateReceiver(tenantID, receiverName, &newReceiver) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } err = client.ReloadAlertmanager() if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } return c.NoContent(http.StatusOK) } } func GetDeleteReceiverHandler(client client.AlertmanagerClient, getReceiverName paramProvider) func(c echo.Context) error { return func(c echo.Context) error { defer glog.Flush() tenantID := c.Get(tenantIDParam).(string) glog.Infof("Delete Receiver: Tenant: %s, receiver: %s", tenantID, getReceiverName(c)) err := client.DeleteReceiver(tenantID, getReceiverName(c)) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } err = client.ReloadAlertmanager() if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } return c.NoContent(http.StatusOK) } } func GetGetRouteHandler(client client.AlertmanagerClient) func(c echo.Context) error { return func(c echo.Context) error { defer glog.Flush() tenantID := c.Get(tenantIDParam).(string) glog.Infof("Get Route: Tenant: %s", tenantID) route, err := client.GetRoute(tenantID) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } return c.JSON(http.StatusOK, *route) } } func GetUpdateRouteHandler(client client.AlertmanagerClient) func(c echo.Context) error { return func(c echo.Context) error { defer glog.Flush() tenantID := c.Get(tenantIDParam).(string) glog.Infof("Update Route: Tenant: %s", tenantID) newRoute, err := decodeRoutePostRequest(c) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } err = client.ModifyTenantRoute(tenantID, &newRoute) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } err = client.ReloadAlertmanager() if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } return c.NoContent(http.StatusOK) } } func GetUpdateGlobalConfigHandler(client client.AlertmanagerClient) func(c echo.Context) error { return func(c echo.Context) error { defer glog.Flush() glog.Infof("Update Global Config") newGlobalConfig, err := decodeGlobalConfigPostRequest(c) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } err = client.SetGlobalConfig(newGlobalConfig) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } err = client.ReloadAlertmanager() if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } return c.NoContent(http.StatusOK) } } func GetGetGlobalConfigHandler(client client.AlertmanagerClient) func(c echo.Context) error { return func(c echo.Context) error { defer glog.Flush() glog.Infof("Get Global Config") globalConf, err := client.GetGlobalConfig() if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } return c.JSON(http.StatusOK, globalConf) } } func decodeGlobalConfigPostRequest(c echo.Context) (config.GlobalConfig, error) { body, err := ioutil.ReadAll(c.Request().Body) if err != nil { glog.Errorf("error decoding global config: %v", err) return config.GlobalConfig{}, fmt.Errorf("error reading request body: %v", err) } globalConfig := config.GlobalConfig{} err = json.Unmarshal(body, &globalConfig) if err != nil { glog.Errorf("error decoding global config: %v", err) return config.GlobalConfig{}, fmt.Errorf("error unmarshalling payload: %v", err) } return globalConfig, nil } func decodeReceiverPostRequest(c echo.Context) (config.Receiver, error) { body, err := ioutil.ReadAll(c.Request().Body) if err != nil { glog.Errorf("error decoding receiver config: %v", err) return config.Receiver{}, fmt.Errorf("error reading request body: %v", err) } receiver := config.Receiver{} err = json.Unmarshal(body, &receiver) if err == nil { return receiver, nil } // Try to unmarshal into the ReceiverJSONWrapper struct if prometheus struct doesn't work jsonPayload := config.ReceiverJSONWrapper{} err = json.Unmarshal(body, &jsonPayload) if err != nil { glog.Errorf("error decoding receiver config: %v", err) return receiver, fmt.Errorf("error unmarshalling payload: %v", err) } return jsonPayload.ToReceiverFmt() } func decodeRoutePostRequest(c echo.Context) (config.Route, error) { body, err := ioutil.ReadAll(c.Request().Body) if err != nil { glog.Errorf("error decoding route config: %v", err) return config.Route{}, fmt.Errorf("error reading request body: %v", err) } route := config.Route{} err = json.Unmarshal(body, &route) if err != nil { glog.Errorf("error decoding route config: %v", err) return config.Route{}, fmt.Errorf("error unmarshalling route: %v", err) } return route, nil }