The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/pkg/services/ngalert/api/api_configuration.go

165 lines
5.4 KiB

package api
import (
"context"
"errors"
"fmt"
"net/http"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/datasources"
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/store"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/util"
v1 "github.com/prometheus/client_golang/api/prometheus/v1"
)
type ConfigSrv struct {
datasourceService datasources.DataSourceService
alertmanagerProvider ExternalAlertmanagerProvider
store store.AdminConfigurationStore
log log.Logger
}
func (srv ConfigSrv) RouteGetAlertmanagers(c *models.ReqContext) response.Response {
urls := srv.alertmanagerProvider.AlertmanagersFor(c.OrgID)
droppedURLs := srv.alertmanagerProvider.DroppedAlertmanagersFor(c.OrgID)
ams := v1.AlertManagersResult{Active: make([]v1.AlertManager, len(urls)), Dropped: make([]v1.AlertManager, len(droppedURLs))}
for i, url := range urls {
ams.Active[i].URL = url.String()
}
for i, url := range droppedURLs {
ams.Dropped[i].URL = url.String()
}
return response.JSON(http.StatusOK, apimodels.GettableAlertmanagers{
Status: "success",
Data: ams,
})
}
func (srv ConfigSrv) RouteGetNGalertConfig(c *models.ReqContext) response.Response {
if c.OrgRole != org.RoleAdmin {
return accessForbiddenResp()
}
cfg, err := srv.store.GetAdminConfiguration(c.OrgID)
if err != nil {
if errors.Is(err, store.ErrNoAdminConfiguration) {
return ErrResp(http.StatusNotFound, err, "")
}
msg := "failed to fetch admin configuration from the database"
srv.log.Error(msg, "error", err)
return ErrResp(http.StatusInternalServerError, err, msg)
}
resp := apimodels.GettableNGalertConfig{
Alertmanagers: cfg.Alertmanagers,
AlertmanagersChoice: apimodels.AlertmanagersChoice(cfg.SendAlertsTo.String()),
}
return response.JSON(http.StatusOK, resp)
}
func (srv ConfigSrv) RoutePostNGalertConfig(c *models.ReqContext, body apimodels.PostableNGalertConfig) response.Response {
if c.OrgRole != org.RoleAdmin {
return accessForbiddenResp()
}
sendAlertsTo, err := ngmodels.StringToAlertmanagersChoice(string(body.AlertmanagersChoice))
if err != nil {
return response.Error(400, "Invalid alertmanager choice specified", err)
}
externalAlertmanagers, err := srv.externalAlertmanagers(c.Req.Context(), c.OrgID)
if err != nil {
return response.Error(500, "Couldn't fetch the external Alertmanagers from datasources", err)
}
if sendAlertsTo == ngmodels.ExternalAlertmanagers &&
len(body.Alertmanagers)+len(externalAlertmanagers) < 1 {
return response.Error(400, "At least one Alertmanager must be provided or configured as a datasource that handles alerts to choose this option", nil)
}
cfg := &ngmodels.AdminConfiguration{
Alertmanagers: body.Alertmanagers,
SendAlertsTo: sendAlertsTo,
OrgID: c.OrgID,
}
Alerting: Add external Alertmanagers (#39183) * building ui * saving alertmanager urls * add actions and api call to get external ams * add list to add modal * add validation and edit/delete * work on merging results * merging results * get color for status heart * adding tests * tests added * rename * add pollin and status * fix list sync * fix polling * add info icon with actual tooltip * fix test * Accessibility things * fix strict error * delete public/dist files * Add API tests for invalid URL * start redo admin test * Fix for empty configuration and test * remove admin test * text updates after review * suppress appevent error * fix tests * update description Co-authored-by: gotjosh <josue@grafana.com> * fix text plus go lint * updates after pr review * Adding docs * Update docs/sources/alerting/unified-alerting/fundamentals/alertmanager.md Co-authored-by: gotjosh <josue@grafana.com> * Update docs/sources/alerting/unified-alerting/fundamentals/alertmanager.md Co-authored-by: gotjosh <josue@grafana.com> * Update docs/sources/alerting/unified-alerting/fundamentals/alertmanager.md Co-authored-by: gotjosh <josue@grafana.com> * Update docs/sources/alerting/unified-alerting/fundamentals/alertmanager.md Co-authored-by: gotjosh <josue@grafana.com> * Update docs/sources/alerting/unified-alerting/fundamentals/alertmanager.md Co-authored-by: gotjosh <josue@grafana.com> * Update docs/sources/alerting/unified-alerting/fundamentals/alertmanager.md Co-authored-by: gotjosh <josue@grafana.com> * Update docs/sources/alerting/unified-alerting/fundamentals/alertmanager.md Co-authored-by: gotjosh <josue@grafana.com> * prettier * updates after docs feedback Co-authored-by: gotjosh <josue.abreu@gmail.com> Co-authored-by: gotjosh <josue@grafana.com>
4 years ago
if err := cfg.Validate(); err != nil {
msg := "failed to validate admin configuration"
srv.log.Error(msg, "error", err)
Alerting: Add external Alertmanagers (#39183) * building ui * saving alertmanager urls * add actions and api call to get external ams * add list to add modal * add validation and edit/delete * work on merging results * merging results * get color for status heart * adding tests * tests added * rename * add pollin and status * fix list sync * fix polling * add info icon with actual tooltip * fix test * Accessibility things * fix strict error * delete public/dist files * Add API tests for invalid URL * start redo admin test * Fix for empty configuration and test * remove admin test * text updates after review * suppress appevent error * fix tests * update description Co-authored-by: gotjosh <josue@grafana.com> * fix text plus go lint * updates after pr review * Adding docs * Update docs/sources/alerting/unified-alerting/fundamentals/alertmanager.md Co-authored-by: gotjosh <josue@grafana.com> * Update docs/sources/alerting/unified-alerting/fundamentals/alertmanager.md Co-authored-by: gotjosh <josue@grafana.com> * Update docs/sources/alerting/unified-alerting/fundamentals/alertmanager.md Co-authored-by: gotjosh <josue@grafana.com> * Update docs/sources/alerting/unified-alerting/fundamentals/alertmanager.md Co-authored-by: gotjosh <josue@grafana.com> * Update docs/sources/alerting/unified-alerting/fundamentals/alertmanager.md Co-authored-by: gotjosh <josue@grafana.com> * Update docs/sources/alerting/unified-alerting/fundamentals/alertmanager.md Co-authored-by: gotjosh <josue@grafana.com> * Update docs/sources/alerting/unified-alerting/fundamentals/alertmanager.md Co-authored-by: gotjosh <josue@grafana.com> * prettier * updates after docs feedback Co-authored-by: gotjosh <josue.abreu@gmail.com> Co-authored-by: gotjosh <josue@grafana.com>
4 years ago
return ErrResp(http.StatusBadRequest, err, msg)
}
cmd := store.UpdateAdminConfigurationCmd{AdminConfiguration: cfg}
if err := srv.store.UpdateAdminConfiguration(cmd); err != nil {
msg := "failed to save the admin configuration to the database"
srv.log.Error(msg, "error", err)
return ErrResp(http.StatusBadRequest, err, msg)
}
return response.JSON(http.StatusCreated, util.DynMap{"message": "admin configuration updated"})
}
func (srv ConfigSrv) RouteDeleteNGalertConfig(c *models.ReqContext) response.Response {
if c.OrgRole != org.RoleAdmin {
return accessForbiddenResp()
}
err := srv.store.DeleteAdminConfiguration(c.OrgID)
if err != nil {
srv.log.Error("unable to delete configuration", "error", err)
return ErrResp(http.StatusInternalServerError, err, "")
}
return response.JSON(http.StatusOK, util.DynMap{"message": "admin configuration deleted"})
}
// externalAlertmanagers returns the URL of any external alertmanager that is
// configured as datasource. The URL does not contain any auth.
func (srv ConfigSrv) externalAlertmanagers(ctx context.Context, orgID int64) ([]string, error) {
var alertmanagers []string
query := &datasources.GetDataSourcesByTypeQuery{
OrgId: orgID,
Type: datasources.DS_ALERTMANAGER,
}
err := srv.datasourceService.GetDataSourcesByType(ctx, query)
if err != nil {
return nil, fmt.Errorf("failed to fetch datasources for org: %w", err)
}
for _, ds := range query.Result {
if ds.JsonData.Get(apimodels.HandleGrafanaManagedAlerts).MustBool(false) {
// we don't need to build the exact URL as we only need
// to know if any is set
alertmanagers = append(alertmanagers, ds.Uid)
}
}
return alertmanagers, nil
}
func (srv ConfigSrv) RouteGetAlertingStatus(c *models.ReqContext) response.Response {
sendsAlertsTo := ngmodels.InternalAlertmanager
cfg, err := srv.store.GetAdminConfiguration(c.OrgID)
if err != nil && !errors.Is(err, store.ErrNoAdminConfiguration) {
msg := "failed to fetch configuration from the database"
srv.log.Error(msg, "error", err)
return ErrResp(http.StatusInternalServerError, err, msg)
}
if cfg != nil {
sendsAlertsTo = cfg.SendAlertsTo
}
resp := apimodels.AlertingStatus{
AlertmanagersChoice: apimodels.AlertmanagersChoice(sendsAlertsTo.String()),
}
return response.JSON(http.StatusOK, resp)
}