2021-08-06 13:06:56 +01:00
package api
import (
2022-07-20 16:50:49 +02:00
"context"
2021-08-06 13:06:56 +01:00
"errors"
2022-07-20 16:50:49 +02:00
"fmt"
2021-08-06 13:06:56 +01:00
"net/http"
2023-01-30 09:55:35 +01:00
v1 "github.com/prometheus/client_golang/api/prometheus/v1"
2021-08-06 13:06:56 +01:00
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/infra/log"
2023-01-27 08:50:36 +01:00
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
2022-07-20 16:50:49 +02:00
"github.com/grafana/grafana/pkg/services/datasources"
2021-08-06 13:06:56 +01:00
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"
2022-08-10 11:56:48 +02:00
"github.com/grafana/grafana/pkg/services/org"
2021-08-13 13:14:36 +01:00
"github.com/grafana/grafana/pkg/util"
2021-08-06 13:06:56 +01:00
)
2022-07-18 15:08:08 +08:00
type ConfigSrv struct {
2022-07-20 16:50:49 +02:00
datasourceService datasources . DataSourceService
2022-07-12 15:13:04 -04:00
alertmanagerProvider ExternalAlertmanagerProvider
store store . AdminConfigurationStore
log log . Logger
2021-08-13 13:14:36 +01:00
}
2023-01-27 08:50:36 +01:00
func ( srv ConfigSrv ) RouteGetAlertmanagers ( c * contextmodel . ReqContext ) response . Response {
2023-10-09 10:40:19 +02:00
urls := srv . alertmanagerProvider . AlertmanagersFor ( c . SignedInUser . GetOrgID ( ) )
droppedURLs := srv . alertmanagerProvider . DroppedAlertmanagersFor ( c . SignedInUser . GetOrgID ( ) )
2021-08-13 13:14:36 +01:00
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 ,
} )
2021-08-06 13:06:56 +01:00
}
2023-01-27 08:50:36 +01:00
func ( srv ConfigSrv ) RouteGetNGalertConfig ( c * contextmodel . ReqContext ) response . Response {
2023-11-14 15:47:34 +01:00
if c . SignedInUser . GetOrgRole ( ) != org . RoleAdmin {
2021-08-06 13:06:56 +01:00
return accessForbiddenResp ( )
}
2023-10-09 10:40:19 +02:00
cfg , err := srv . store . GetAdminConfiguration ( c . SignedInUser . GetOrgID ( ) )
2021-08-06 13:06:56 +01:00
if err != nil {
if errors . Is ( err , store . ErrNoAdminConfiguration ) {
return ErrResp ( http . StatusNotFound , err , "" )
}
msg := "failed to fetch admin configuration from the database"
2022-10-19 16:36:54 -05:00
srv . log . Error ( msg , "error" , err )
2021-08-06 13:06:56 +01:00
return ErrResp ( http . StatusInternalServerError , err , msg )
}
resp := apimodels . GettableNGalertConfig {
Alerting: send alerts to external, internal, or both alertmanagers (#40341)
* (WIP) send alerts to external, internal, or both alertmanagers
* Modify admin configuration endpoint, update swagger docs
* Integration test for admin config updated
* Code review changes
* Fix alertmanagers choice not changing bug, add unit test
* Add AlertmanagersChoice as enum in swagger, code review changes
* Fix API and tests errors
* Change enum from int to string, use 'SendAlertsTo' instead of 'AlertmanagerChoice' where necessary
* Fix tests to reflect last changes
* Keep senders running when alerts are handled just internally
* Check if any external AM has been discovered before sending alerts, update tests
* remove duplicate data from logs
* update comment
* represent alertmanagers choice as an int instead of a string
* default alertmanagers choice to all alertmanagers, test cases
* update definitions and generate spec
2022-02-01 20:36:55 -03:00
AlertmanagersChoice : apimodels . AlertmanagersChoice ( cfg . SendAlertsTo . String ( ) ) ,
2021-08-06 13:06:56 +01:00
}
return response . JSON ( http . StatusOK , resp )
}
2023-01-27 08:50:36 +01:00
func ( srv ConfigSrv ) RoutePostNGalertConfig ( c * contextmodel . ReqContext , body apimodels . PostableNGalertConfig ) response . Response {
2023-11-14 15:47:34 +01:00
if c . SignedInUser . GetOrgRole ( ) != org . RoleAdmin {
2021-08-06 13:06:56 +01:00
return accessForbiddenResp ( )
}
Alerting: send alerts to external, internal, or both alertmanagers (#40341)
* (WIP) send alerts to external, internal, or both alertmanagers
* Modify admin configuration endpoint, update swagger docs
* Integration test for admin config updated
* Code review changes
* Fix alertmanagers choice not changing bug, add unit test
* Add AlertmanagersChoice as enum in swagger, code review changes
* Fix API and tests errors
* Change enum from int to string, use 'SendAlertsTo' instead of 'AlertmanagerChoice' where necessary
* Fix tests to reflect last changes
* Keep senders running when alerts are handled just internally
* Check if any external AM has been discovered before sending alerts, update tests
* remove duplicate data from logs
* update comment
* represent alertmanagers choice as an int instead of a string
* default alertmanagers choice to all alertmanagers, test cases
* update definitions and generate spec
2022-02-01 20:36:55 -03:00
sendAlertsTo , err := ngmodels . StringToAlertmanagersChoice ( string ( body . AlertmanagersChoice ) )
if err != nil {
2024-02-28 01:39:51 +09:00
return response . Error ( http . StatusBadRequest , "Invalid alertmanager choice specified" , err )
Alerting: send alerts to external, internal, or both alertmanagers (#40341)
* (WIP) send alerts to external, internal, or both alertmanagers
* Modify admin configuration endpoint, update swagger docs
* Integration test for admin config updated
* Code review changes
* Fix alertmanagers choice not changing bug, add unit test
* Add AlertmanagersChoice as enum in swagger, code review changes
* Fix API and tests errors
* Change enum from int to string, use 'SendAlertsTo' instead of 'AlertmanagerChoice' where necessary
* Fix tests to reflect last changes
* Keep senders running when alerts are handled just internally
* Check if any external AM has been discovered before sending alerts, update tests
* remove duplicate data from logs
* update comment
* represent alertmanagers choice as an int instead of a string
* default alertmanagers choice to all alertmanagers, test cases
* update definitions and generate spec
2022-02-01 20:36:55 -03:00
}
2023-10-09 10:40:19 +02:00
externalAlertmanagers , err := srv . externalAlertmanagers ( c . Req . Context ( ) , c . SignedInUser . GetOrgID ( ) )
2022-07-20 16:50:49 +02:00
if err != nil {
2024-02-28 01:39:51 +09:00
return response . Error ( http . StatusInternalServerError , "Couldn't fetch the external Alertmanagers from datasources" , err )
2022-07-20 16:50:49 +02:00
}
2022-11-10 16:34:13 +01:00
if sendAlertsTo == ngmodels . ExternalAlertmanagers && len ( externalAlertmanagers ) < 1 {
2024-02-28 01:39:51 +09:00
return response . Error ( http . StatusBadRequest , "At least one Alertmanager must be provided or configured as a datasource that handles alerts to choose this option" , nil )
Alerting: send alerts to external, internal, or both alertmanagers (#40341)
* (WIP) send alerts to external, internal, or both alertmanagers
* Modify admin configuration endpoint, update swagger docs
* Integration test for admin config updated
* Code review changes
* Fix alertmanagers choice not changing bug, add unit test
* Add AlertmanagersChoice as enum in swagger, code review changes
* Fix API and tests errors
* Change enum from int to string, use 'SendAlertsTo' instead of 'AlertmanagerChoice' where necessary
* Fix tests to reflect last changes
* Keep senders running when alerts are handled just internally
* Check if any external AM has been discovered before sending alerts, update tests
* remove duplicate data from logs
* update comment
* represent alertmanagers choice as an int instead of a string
* default alertmanagers choice to all alertmanagers, test cases
* update definitions and generate spec
2022-02-01 20:36:55 -03:00
}
2021-08-06 13:06:56 +01:00
cfg := & ngmodels . AdminConfiguration {
2022-11-10 16:34:13 +01:00
SendAlertsTo : sendAlertsTo ,
2023-10-09 10:40:19 +02:00
OrgID : c . SignedInUser . GetOrgID ( ) ,
2021-11-12 22:19:16 +01:00
}
2021-08-06 13:06:56 +01:00
cmd := store . UpdateAdminConfigurationCmd { AdminConfiguration : cfg }
if err := srv . store . UpdateAdminConfiguration ( cmd ) ; err != nil {
msg := "failed to save the admin configuration to the database"
2022-10-19 16:36:54 -05:00
srv . log . Error ( msg , "error" , err )
2021-08-06 13:06:56 +01:00
return ErrResp ( http . StatusBadRequest , err , msg )
}
2021-08-13 13:14:36 +01:00
return response . JSON ( http . StatusCreated , util . DynMap { "message" : "admin configuration updated" } )
2021-08-06 13:06:56 +01:00
}
2023-01-27 08:50:36 +01:00
func ( srv ConfigSrv ) RouteDeleteNGalertConfig ( c * contextmodel . ReqContext ) response . Response {
2023-11-14 15:47:34 +01:00
if c . SignedInUser . GetOrgRole ( ) != org . RoleAdmin {
2021-08-06 13:06:56 +01:00
return accessForbiddenResp ( )
}
2023-10-09 10:40:19 +02:00
err := srv . store . DeleteAdminConfiguration ( c . SignedInUser . GetOrgID ( ) )
2021-08-06 13:06:56 +01:00
if err != nil {
2023-09-04 18:46:34 +02:00
srv . log . Error ( "Unable to delete configuration" , "error" , err )
2021-08-06 13:06:56 +01:00
return ErrResp ( http . StatusInternalServerError , err , "" )
}
2021-08-13 13:14:36 +01:00
return response . JSON ( http . StatusOK , util . DynMap { "message" : "admin configuration deleted" } )
2021-08-06 13:06:56 +01:00
}
2022-07-20 16:50:49 +02:00
// 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 {
2023-02-02 17:22:43 +01:00
OrgID : orgID ,
2022-07-20 16:50:49 +02:00
Type : datasources . DS_ALERTMANAGER ,
}
2023-02-09 15:49:44 +01:00
dataSources , err := srv . datasourceService . GetDataSourcesByType ( ctx , query )
2022-07-20 16:50:49 +02:00
if err != nil {
return nil , fmt . Errorf ( "failed to fetch datasources for org: %w" , err )
}
2023-02-09 15:49:44 +01:00
for _ , ds := range dataSources {
2022-07-20 16:50:49 +02:00
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
2023-02-02 17:22:43 +01:00
alertmanagers = append ( alertmanagers , ds . UID )
2022-07-20 16:50:49 +02:00
}
}
return alertmanagers , nil
}
2022-09-14 14:03:10 -04:00
2023-01-27 08:50:36 +01:00
func ( srv ConfigSrv ) RouteGetAlertingStatus ( c * contextmodel . ReqContext ) response . Response {
2022-09-14 14:03:10 -04:00
sendsAlertsTo := ngmodels . InternalAlertmanager
2023-10-09 10:40:19 +02:00
cfg , err := srv . store . GetAdminConfiguration ( c . SignedInUser . GetOrgID ( ) )
2022-09-14 14:03:10 -04:00
if err != nil && ! errors . Is ( err , store . ErrNoAdminConfiguration ) {
msg := "failed to fetch configuration from the database"
2022-10-19 16:36:54 -05:00
srv . log . Error ( msg , "error" , err )
2022-09-14 14:03:10 -04:00
return ErrResp ( http . StatusInternalServerError , err , msg )
}
if cfg != nil {
sendsAlertsTo = cfg . SendAlertsTo
}
2023-02-01 15:51:05 +01:00
// handle errors
2023-10-09 10:40:19 +02:00
externalAlertManagers , err := srv . externalAlertmanagers ( c . Req . Context ( ) , c . SignedInUser . GetOrgID ( ) )
2023-02-01 15:51:05 +01:00
if err != nil {
return ErrResp ( http . StatusInternalServerError , err , "" )
}
2022-09-14 14:03:10 -04:00
resp := apimodels . AlertingStatus {
2023-02-01 15:51:05 +01:00
AlertmanagersChoice : apimodels . AlertmanagersChoice ( sendsAlertsTo . String ( ) ) ,
NumExternalAlertmanagers : len ( externalAlertManagers ) ,
2022-09-14 14:03:10 -04:00
}
return response . JSON ( http . StatusOK , resp )
}