| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | package alerting | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2022-11-29 16:59:03 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2022-08-10 21:37:51 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-01 22:53:15 +08:00
										 |  |  | 	"github.com/prometheus/alertmanager/config" | 
					
						
							|  |  |  | 	"github.com/prometheus/alertmanager/pkg/labels" | 
					
						
							| 
									
										
										
										
											2022-02-16 06:24:39 +08:00
										 |  |  | 	"github.com/prometheus/common/model" | 
					
						
							|  |  |  | 	"github.com/stretchr/testify/require" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-10 23:34:13 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/components/simplejson" | 
					
						
							| 
									
										
										
										
											2024-02-01 22:53:15 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/expr" | 
					
						
							| 
									
										
										
										
											2022-11-10 23:34:13 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/datasources" | 
					
						
							| 
									
										
										
										
											2021-08-25 21:11:22 +08:00
										 |  |  | 	apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 	ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" | 
					
						
							| 
									
										
										
										
											2022-07-19 21:32:54 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/ngalert/sender" | 
					
						
							| 
									
										
										
										
											2022-08-10 17:56:48 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/org" | 
					
						
							| 
									
										
										
										
											2022-11-29 16:59:03 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/org/orgimpl" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/quota/quotatest" | 
					
						
							| 
									
										
										
										
											2022-06-28 20:32:25 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/user" | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/tests/testinfra" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-09 15:11:56 +08:00
										 |  |  | func TestIntegrationAdminConfiguration_SendingToExternalAlertmanagers(t *testing.T) { | 
					
						
							|  |  |  | 	testinfra.SQLiteIntegrationTest(t) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-29 22:16:40 +08:00
										 |  |  | 	const disableOrgID int64 = 3 | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 	dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{ | 
					
						
							| 
									
										
										
										
											2021-09-29 22:16:40 +08:00
										 |  |  | 		DisableLegacyAlerting:          true, | 
					
						
							|  |  |  | 		EnableUnifiedAlerting:          true, | 
					
						
							| 
									
										
										
										
											2021-09-16 22:33:51 +08:00
										 |  |  | 		DisableAnonymous:               true, | 
					
						
							|  |  |  | 		NGAlertAdminConfigPollInterval: 2 * time.Second, | 
					
						
							| 
									
										
										
										
											2021-09-29 22:16:40 +08:00
										 |  |  | 		UnifiedAlertingDisabledOrgs:    []int64{disableOrgID}, // disable unified alerting for organisation 3
 | 
					
						
							| 
									
										
										
										
											2022-02-09 17:26:06 +08:00
										 |  |  | 		AppModeProduction:              true, | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-04 21:04:47 +08:00
										 |  |  | 	grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path) | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-04 21:04:47 +08:00
										 |  |  | 	orgService, err := orgimpl.ProvideService(env.SQLStore, env.Cfg, quotatest.New(false, nil)) | 
					
						
							| 
									
										
										
										
											2022-11-29 16:59:03 +08:00
										 |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 	// Create a user to make authenticated requests
 | 
					
						
							| 
									
										
										
										
											2024-04-04 21:04:47 +08:00
										 |  |  | 	userID := createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{ | 
					
						
							| 
									
										
										
										
											2022-08-10 17:56:48 +08:00
										 |  |  | 		DefaultOrgRole: string(org.RoleAdmin), | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 		Login:          "grafana", | 
					
						
							|  |  |  | 		Password:       "password", | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-06-21 23:39:22 +08:00
										 |  |  | 	apiClient := newAlertingApiClient(grafanaListedAddr, "grafana", "password") | 
					
						
							| 
									
										
										
										
											2022-11-29 16:59:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-29 22:16:40 +08:00
										 |  |  | 	// create another organisation
 | 
					
						
							| 
									
										
										
										
											2022-11-29 16:59:03 +08:00
										 |  |  | 	newOrg, err := orgService.CreateWithMember(context.Background(), &org.CreateOrgCommand{Name: "another org", UserID: userID}) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 	orgID := newOrg.ID | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-29 22:16:40 +08:00
										 |  |  | 	// ensure that the orgID is 3 (the disabled org)
 | 
					
						
							|  |  |  | 	require.Equal(t, disableOrgID, orgID) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// create user under different organisation
 | 
					
						
							| 
									
										
										
										
											2024-04-04 21:04:47 +08:00
										 |  |  | 	createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{ | 
					
						
							| 
									
										
										
										
											2022-08-10 17:56:48 +08:00
										 |  |  | 		DefaultOrgRole: string(org.RoleAdmin), | 
					
						
							| 
									
										
										
										
											2021-09-29 22:16:40 +08:00
										 |  |  | 		Password:       "admin-42", | 
					
						
							|  |  |  | 		Login:          "admin-42", | 
					
						
							| 
									
										
										
										
											2022-06-28 20:32:25 +08:00
										 |  |  | 		OrgID:          orgID, | 
					
						
							| 
									
										
										
										
											2021-09-29 22:16:40 +08:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 	// Create a couple of "fake" Alertmanagers
 | 
					
						
							| 
									
										
										
										
											2022-07-19 21:32:54 +08:00
										 |  |  | 	fakeAM1 := sender.NewFakeExternalAlertmanager(t) | 
					
						
							|  |  |  | 	fakeAM2 := sender.NewFakeExternalAlertmanager(t) | 
					
						
							|  |  |  | 	fakeAM3 := sender.NewFakeExternalAlertmanager(t) | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Now, let's test the configuration API.
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		alertsURL := fmt.Sprintf("http://grafana:password@%s/api/v1/ngalert/admin_config", grafanaListedAddr) | 
					
						
							|  |  |  | 		resp := getRequest(t, alertsURL, http.StatusNotFound) // nolint
 | 
					
						
							| 
									
										
										
										
											2022-08-10 21:37:51 +08:00
										 |  |  | 		b, err := io.ReadAll(resp.Body) | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 		require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2023-08-30 23:46:47 +08:00
										 |  |  | 		var res map[string]any | 
					
						
							| 
									
										
										
										
											2022-04-14 23:54:49 +08:00
										 |  |  | 		err = json.Unmarshal(b, &res) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.Equal(t, "no admin configuration available", res["message"]) | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
											
												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-02 07:36:55 +08:00
										 |  |  | 	// An invalid alertmanager choice should return an error.
 | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 	{ | 
					
						
							|  |  |  | 		ac := apimodels.PostableNGalertConfig{ | 
					
						
							| 
									
										
										
											
												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-02 07:36:55 +08:00
										 |  |  | 			AlertmanagersChoice: apimodels.AlertmanagersChoice("invalid"), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		buf := bytes.Buffer{} | 
					
						
							|  |  |  | 		enc := json.NewEncoder(&buf) | 
					
						
							|  |  |  | 		err := enc.Encode(&ac) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		alertsURL := fmt.Sprintf("http://grafana:password@%s/api/v1/ngalert/admin_config", grafanaListedAddr) | 
					
						
							|  |  |  | 		resp := postRequest(t, alertsURL, buf.String(), http.StatusBadRequest) // nolint
 | 
					
						
							| 
									
										
										
										
											2022-08-10 21:37:51 +08:00
										 |  |  | 		b, err := io.ReadAll(resp.Body) | 
					
						
							| 
									
										
										
											
												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-02 07:36:55 +08:00
										 |  |  | 		require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2023-08-30 23:46:47 +08:00
										 |  |  | 		var res map[string]any | 
					
						
							| 
									
										
										
										
											2022-07-20 22:50:49 +08:00
										 |  |  | 		err = json.Unmarshal(b, &res) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.Equal(t, "Invalid alertmanager choice specified", res["message"]) | 
					
						
							| 
									
										
										
											
												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-02 07:36:55 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Let's try to send all the alerts to an external Alertmanager
 | 
					
						
							|  |  |  | 	// but never specify any. This should return an error.
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		ac := apimodels.PostableNGalertConfig{ | 
					
						
							|  |  |  | 			AlertmanagersChoice: apimodels.AlertmanagersChoice(ngmodels.ExternalAlertmanagers.String()), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		buf := bytes.Buffer{} | 
					
						
							|  |  |  | 		enc := json.NewEncoder(&buf) | 
					
						
							|  |  |  | 		err := enc.Encode(&ac) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		alertsURL := fmt.Sprintf("http://grafana:password@%s/api/v1/ngalert/admin_config", grafanaListedAddr) | 
					
						
							|  |  |  | 		resp := postRequest(t, alertsURL, buf.String(), http.StatusBadRequest) // nolint
 | 
					
						
							| 
									
										
										
										
											2022-08-10 21:37:51 +08:00
										 |  |  | 		b, err := io.ReadAll(resp.Body) | 
					
						
							| 
									
										
										
											
												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-02 07:36:55 +08:00
										 |  |  | 		require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2023-08-30 23:46:47 +08:00
										 |  |  | 		var res map[string]any | 
					
						
							| 
									
										
										
										
											2022-07-20 22:50:49 +08:00
										 |  |  | 		err = json.Unmarshal(b, &res) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.Equal(t, "At least one Alertmanager must be provided or configured as a datasource that handles alerts to choose this option", res["message"]) | 
					
						
							| 
									
										
										
											
												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-02 07:36:55 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-10 23:34:13 +08:00
										 |  |  | 	// Add an alertmanager datasource
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		cmd := datasources.AddDataSourceCommand{ | 
					
						
							| 
									
										
										
										
											2023-02-03 00:22:43 +08:00
										 |  |  | 			OrgID:  1, | 
					
						
							| 
									
										
										
										
											2022-11-10 23:34:13 +08:00
										 |  |  | 			Name:   "AM1", | 
					
						
							|  |  |  | 			Type:   datasources.DS_ALERTMANAGER, | 
					
						
							|  |  |  | 			Access: "proxy", | 
					
						
							| 
									
										
										
										
											2023-02-03 00:22:43 +08:00
										 |  |  | 			URL:    fakeAM1.URL(), | 
					
						
							| 
									
										
										
										
											2023-08-30 23:46:47 +08:00
										 |  |  | 			JsonData: simplejson.NewFromAny(map[string]any{ | 
					
						
							| 
									
										
										
										
											2022-11-10 23:34:13 +08:00
										 |  |  | 				"handleGrafanaManagedAlerts": true, | 
					
						
							|  |  |  | 				"implementation":             "prometheus", | 
					
						
							|  |  |  | 			}), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		buf := bytes.Buffer{} | 
					
						
							|  |  |  | 		enc := json.NewEncoder(&buf) | 
					
						
							|  |  |  | 		err := enc.Encode(&cmd) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		dataSourcesUrl := fmt.Sprintf("http://grafana:password@%s/api/datasources", grafanaListedAddr) | 
					
						
							|  |  |  | 		resp := postRequest(t, dataSourcesUrl, buf.String(), http.StatusOK) // nolint
 | 
					
						
							|  |  |  | 		b, err := io.ReadAll(resp.Body) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2023-08-30 23:46:47 +08:00
										 |  |  | 		var res map[string]any | 
					
						
							| 
									
										
										
										
											2022-11-10 23:34:13 +08:00
										 |  |  | 		err = json.Unmarshal(b, &res) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.Equal(t, "Datasource added", res["message"]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Add another alertmanager datasource
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		cmd := datasources.AddDataSourceCommand{ | 
					
						
							| 
									
										
										
										
											2023-02-03 00:22:43 +08:00
										 |  |  | 			OrgID:  1, | 
					
						
							| 
									
										
										
										
											2022-11-10 23:34:13 +08:00
										 |  |  | 			Name:   "AM2", | 
					
						
							|  |  |  | 			Type:   datasources.DS_ALERTMANAGER, | 
					
						
							|  |  |  | 			Access: "proxy", | 
					
						
							| 
									
										
										
										
											2023-02-03 00:22:43 +08:00
										 |  |  | 			URL:    fakeAM2.URL(), | 
					
						
							| 
									
										
										
										
											2023-08-30 23:46:47 +08:00
										 |  |  | 			JsonData: simplejson.NewFromAny(map[string]any{ | 
					
						
							| 
									
										
										
										
											2022-11-10 23:34:13 +08:00
										 |  |  | 				"handleGrafanaManagedAlerts": true, | 
					
						
							|  |  |  | 				"implementation":             "prometheus", | 
					
						
							|  |  |  | 			}), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		buf := bytes.Buffer{} | 
					
						
							|  |  |  | 		enc := json.NewEncoder(&buf) | 
					
						
							|  |  |  | 		err := enc.Encode(&cmd) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		dataSourcesUrl := fmt.Sprintf("http://grafana:password@%s/api/datasources", grafanaListedAddr) | 
					
						
							|  |  |  | 		resp := postRequest(t, dataSourcesUrl, buf.String(), http.StatusOK) // nolint
 | 
					
						
							|  |  |  | 		b, err := io.ReadAll(resp.Body) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2023-08-30 23:46:47 +08:00
										 |  |  | 		var res map[string]any | 
					
						
							| 
									
										
										
										
											2022-11-10 23:34:13 +08:00
										 |  |  | 		err = json.Unmarshal(b, &res) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.Equal(t, "Datasource added", res["message"]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
											
												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-02 07:36:55 +08:00
										 |  |  | 	// Now, lets re-set external Alertmanagers for main organisation
 | 
					
						
							|  |  |  | 	// and make it so that only the external Alertmanagers handle the alerts.
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		ac := apimodels.PostableNGalertConfig{ | 
					
						
							|  |  |  | 			AlertmanagersChoice: apimodels.AlertmanagersChoice(ngmodels.ExternalAlertmanagers.String()), | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		buf := bytes.Buffer{} | 
					
						
							|  |  |  | 		enc := json.NewEncoder(&buf) | 
					
						
							|  |  |  | 		err := enc.Encode(&ac) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		alertsURL := fmt.Sprintf("http://grafana:password@%s/api/v1/ngalert/admin_config", grafanaListedAddr) | 
					
						
							|  |  |  | 		resp := postRequest(t, alertsURL, buf.String(), http.StatusCreated) // nolint
 | 
					
						
							| 
									
										
										
										
											2022-08-10 21:37:51 +08:00
										 |  |  | 		b, err := io.ReadAll(resp.Body) | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 		require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2023-08-30 23:46:47 +08:00
										 |  |  | 		var res map[string]any | 
					
						
							| 
									
										
										
										
											2022-07-20 22:50:49 +08:00
										 |  |  | 		err = json.Unmarshal(b, &res) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.Equal(t, "admin configuration updated", res["message"]) | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// If we get the configuration again, it shows us what we've set.
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		alertsURL := fmt.Sprintf("http://grafana:password@%s/api/v1/ngalert/admin_config", grafanaListedAddr) | 
					
						
							|  |  |  | 		resp := getRequest(t, alertsURL, http.StatusOK) // nolint
 | 
					
						
							| 
									
										
										
										
											2022-08-10 21:37:51 +08:00
										 |  |  | 		b, err := io.ReadAll(resp.Body) | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 		require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2022-11-10 23:34:13 +08:00
										 |  |  | 		require.JSONEq(t, fmt.Sprintf("{\"alertmanagersChoice\": %q}\n", ngmodels.ExternalAlertmanagers), string(b)) | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-29 22:16:40 +08:00
										 |  |  | 	// With the configuration set, we should eventually discover those Alertmanagers.
 | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 	{ | 
					
						
							|  |  |  | 		alertsURL := fmt.Sprintf("http://grafana:password@%s/api/v1/ngalert/alertmanagers", grafanaListedAddr) | 
					
						
							|  |  |  | 		require.Eventually(t, func() bool { | 
					
						
							|  |  |  | 			resp := getRequest(t, alertsURL, http.StatusOK) // nolint
 | 
					
						
							| 
									
										
										
										
											2022-08-10 21:37:51 +08:00
										 |  |  | 			b, err := io.ReadAll(resp.Body) | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 			require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			var alertmanagers apimodels.GettableAlertmanagers | 
					
						
							|  |  |  | 			require.NoError(t, json.Unmarshal(b, &alertmanagers)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			return len(alertmanagers.Data.Active) == 2 | 
					
						
							| 
									
										
										
										
											2021-09-29 22:16:40 +08:00
										 |  |  | 		}, 16*time.Second, 8*time.Second) // the sync interval is 2s so after 8s all alertmanagers most probably are started
 | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Now, let's set an alert that should fire as quickly as possible.
 | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2022-05-16 18:45:41 +08:00
										 |  |  | 		// Create the namespace we'll save our alerts to
 | 
					
						
							| 
									
										
										
										
											2022-06-21 23:39:22 +08:00
										 |  |  | 		apiClient.CreateFolder(t, "default", "default") | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 		interval, err := model.ParseDuration("10s") | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		rules := apimodels.PostableRuleGroupConfig{ | 
					
						
							|  |  |  | 			Name:     "arulegroup", | 
					
						
							|  |  |  | 			Interval: interval, | 
					
						
							|  |  |  | 			Rules: []apimodels.PostableExtendedRuleNode{ | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					ApiRuleNode: &apimodels.ApiRuleNode{ | 
					
						
							| 
									
										
										
										
											2022-06-30 23:46:26 +08:00
										 |  |  | 						For:         &interval, | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 						Labels:      map[string]string{"label1": "val1"}, | 
					
						
							|  |  |  | 						Annotations: map[string]string{"annotation1": "val1"}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					// this rule does not explicitly set no data and error states
 | 
					
						
							|  |  |  | 					// therefore it should get the default values
 | 
					
						
							|  |  |  | 					GrafanaManagedAlert: &apimodels.PostableGrafanaRule{ | 
					
						
							|  |  |  | 						Title:     "AlwaysFiring", | 
					
						
							|  |  |  | 						Condition: "A", | 
					
						
							| 
									
										
										
										
											2023-03-27 23:55:13 +08:00
										 |  |  | 						Data: []apimodels.AlertQuery{ | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 							{ | 
					
						
							|  |  |  | 								RefID: "A", | 
					
						
							| 
									
										
										
										
											2023-03-27 23:55:13 +08:00
										 |  |  | 								RelativeTimeRange: apimodels.RelativeTimeRange{ | 
					
						
							|  |  |  | 									From: apimodels.Duration(time.Duration(5) * time.Hour), | 
					
						
							|  |  |  | 									To:   apimodels.Duration(time.Duration(3) * time.Hour), | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 								}, | 
					
						
							| 
									
										
										
										
											2023-02-01 01:50:10 +08:00
										 |  |  | 								DatasourceUID: expr.DatasourceUID, | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 								Model: json.RawMessage(`{ | 
					
						
							|  |  |  | 								"type": "math", | 
					
						
							|  |  |  | 								"expression": "2 + 3 > 1" | 
					
						
							|  |  |  | 								}`), | 
					
						
							|  |  |  | 							}, | 
					
						
							|  |  |  | 						}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		buf := bytes.Buffer{} | 
					
						
							|  |  |  | 		enc := json.NewEncoder(&buf) | 
					
						
							|  |  |  | 		err = enc.Encode(&rules) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ruleURL := fmt.Sprintf("http://grafana:password@%s/api/ruler/grafana/api/v1/rules/default", grafanaListedAddr) | 
					
						
							|  |  |  | 		// nolint
 | 
					
						
							|  |  |  | 		_ = postRequest(t, ruleURL, buf.String(), http.StatusAccepted) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-01 01:50:10 +08:00
										 |  |  | 	// Eventually, our Alertmanagers should receiver the alert.
 | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 	{ | 
					
						
							|  |  |  | 		require.Eventually(t, func() bool { | 
					
						
							|  |  |  | 			return fakeAM1.AlertsCount() == 1 && fakeAM2.AlertsCount() == 1 | 
					
						
							| 
									
										
										
										
											2022-11-22 15:09:15 +08:00
										 |  |  | 		}, time.Minute, 5*time.Second) | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-09-29 22:16:40 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-10 23:34:13 +08:00
										 |  |  | 	// Add an alertmanager datasource fot the other organisation
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		cmd := datasources.AddDataSourceCommand{ | 
					
						
							| 
									
										
										
										
											2023-02-03 00:22:43 +08:00
										 |  |  | 			OrgID:  2, | 
					
						
							| 
									
										
										
										
											2022-11-10 23:34:13 +08:00
										 |  |  | 			Name:   "AM3", | 
					
						
							|  |  |  | 			Type:   datasources.DS_ALERTMANAGER, | 
					
						
							|  |  |  | 			Access: "proxy", | 
					
						
							| 
									
										
										
										
											2023-02-03 00:22:43 +08:00
										 |  |  | 			URL:    fakeAM3.URL(), | 
					
						
							| 
									
										
										
										
											2023-08-30 23:46:47 +08:00
										 |  |  | 			JsonData: simplejson.NewFromAny(map[string]any{ | 
					
						
							| 
									
										
										
										
											2022-11-10 23:34:13 +08:00
										 |  |  | 				"handleGrafanaManagedAlerts": true, | 
					
						
							|  |  |  | 				"implementation":             "prometheus", | 
					
						
							|  |  |  | 			}), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		buf := bytes.Buffer{} | 
					
						
							|  |  |  | 		enc := json.NewEncoder(&buf) | 
					
						
							|  |  |  | 		err := enc.Encode(&cmd) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		dataSourcesUrl := fmt.Sprintf("http://admin-42:admin-42@%s/api/datasources", grafanaListedAddr) | 
					
						
							|  |  |  | 		resp := postRequest(t, dataSourcesUrl, buf.String(), http.StatusOK) // nolint
 | 
					
						
							|  |  |  | 		b, err := io.ReadAll(resp.Body) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2023-08-30 23:46:47 +08:00
										 |  |  | 		var res map[string]any | 
					
						
							| 
									
										
										
										
											2022-11-10 23:34:13 +08:00
										 |  |  | 		err = json.Unmarshal(b, &res) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.Equal(t, "Datasource added", res["message"]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-29 22:16:40 +08:00
										 |  |  | 	// Now, lets re-set external Alertmanagers for the other organisation.
 | 
					
						
							| 
									
										
										
											
												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-02 07:36:55 +08:00
										 |  |  | 	// Sending an empty value for AlertmanagersChoice should default to AllAlertmanagers.
 | 
					
						
							| 
									
										
										
										
											2021-09-29 22:16:40 +08:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2022-11-10 23:34:13 +08:00
										 |  |  | 		ac := apimodels.PostableNGalertConfig{} | 
					
						
							| 
									
										
										
										
											2021-09-29 22:16:40 +08:00
										 |  |  | 		buf := bytes.Buffer{} | 
					
						
							|  |  |  | 		enc := json.NewEncoder(&buf) | 
					
						
							|  |  |  | 		err := enc.Encode(&ac) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		alertsURL := fmt.Sprintf("http://admin-42:admin-42@%s/api/v1/ngalert/admin_config", grafanaListedAddr) | 
					
						
							|  |  |  | 		resp := postRequest(t, alertsURL, buf.String(), http.StatusCreated) // nolint
 | 
					
						
							| 
									
										
										
										
											2022-08-10 21:37:51 +08:00
										 |  |  | 		b, err := io.ReadAll(resp.Body) | 
					
						
							| 
									
										
										
										
											2021-09-29 22:16:40 +08:00
										 |  |  | 		require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2023-08-30 23:46:47 +08:00
										 |  |  | 		var res map[string]any | 
					
						
							| 
									
										
										
										
											2022-07-20 22:50:49 +08:00
										 |  |  | 		err = json.Unmarshal(b, &res) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.Equal(t, "admin configuration updated", res["message"]) | 
					
						
							| 
									
										
										
										
											2021-09-29 22:16:40 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// If we get the configuration again, it shows us what we've set.
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		alertsURL := fmt.Sprintf("http://admin-42:admin-42@%s/api/v1/ngalert/admin_config", grafanaListedAddr) | 
					
						
							|  |  |  | 		resp := getRequest(t, alertsURL, http.StatusOK) // nolint
 | 
					
						
							| 
									
										
										
										
											2022-08-10 21:37:51 +08:00
										 |  |  | 		b, err := io.ReadAll(resp.Body) | 
					
						
							| 
									
										
										
										
											2021-09-29 22:16:40 +08:00
										 |  |  | 		require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2022-11-10 23:34:13 +08:00
										 |  |  | 		require.JSONEq(t, fmt.Sprintf("{\"alertmanagersChoice\": %q}\n", ngmodels.AllAlertmanagers), string(b)) | 
					
						
							| 
									
										
										
										
											2021-09-29 22:16:40 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// With the configuration set, we should eventually not discover Alertmanagers.
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		alertsURL := fmt.Sprintf("http://admin-42:admin-42@%s/api/v1/ngalert/alertmanagers", grafanaListedAddr) | 
					
						
							|  |  |  | 		require.Eventually(t, func() bool { | 
					
						
							|  |  |  | 			resp := getRequest(t, alertsURL, http.StatusOK) // nolint
 | 
					
						
							| 
									
										
										
										
											2022-08-10 21:37:51 +08:00
										 |  |  | 			b, err := io.ReadAll(resp.Body) | 
					
						
							| 
									
										
										
										
											2021-09-29 22:16:40 +08:00
										 |  |  | 			require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			var alertmanagers apimodels.GettableAlertmanagers | 
					
						
							|  |  |  | 			require.NoError(t, json.Unmarshal(b, &alertmanagers)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			return len(alertmanagers.Data.Active) == 0 | 
					
						
							|  |  |  | 		}, 16*time.Second, 8*time.Second) // the sync interval is 2s so after 8s all alertmanagers (if any) most probably are started
 | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-13 20:14:36 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2024-02-01 22:53:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func TestIntegrationAdminConfiguration_CannotCreateInhibitionRules(t *testing.T) { | 
					
						
							|  |  |  | 	testinfra.SQLiteIntegrationTest(t) | 
					
						
							|  |  |  | 	dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{ | 
					
						
							|  |  |  | 		DisableLegacyAlerting: true, | 
					
						
							|  |  |  | 		EnableUnifiedAlerting: true, | 
					
						
							|  |  |  | 		AppModeProduction:     true, | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2024-04-04 21:04:47 +08:00
										 |  |  | 	grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path) | 
					
						
							|  |  |  | 	createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{ | 
					
						
							| 
									
										
										
										
											2024-02-01 22:53:15 +08:00
										 |  |  | 		DefaultOrgRole: string(org.RoleAdmin), | 
					
						
							|  |  |  | 		Password:       "admin", | 
					
						
							|  |  |  | 		Login:          "admin", | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	client := newAlertingApiClient(grafanaListedAddr, "admin", "admin") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cfg := apimodels.PostableUserConfig{ | 
					
						
							|  |  |  | 		AlertmanagerConfig: apimodels.PostableApiAlertingConfig{ | 
					
						
							|  |  |  | 			Config: apimodels.Config{ | 
					
						
							|  |  |  | 				Route: &apimodels.Route{ | 
					
						
							|  |  |  | 					Receiver: "test", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				InhibitRules: []config.InhibitRule{{ | 
					
						
							|  |  |  | 					SourceMatchers: config.Matchers{{ | 
					
						
							|  |  |  | 						Type:  labels.MatchEqual, | 
					
						
							|  |  |  | 						Name:  "foo", | 
					
						
							|  |  |  | 						Value: "bar", | 
					
						
							|  |  |  | 					}}, | 
					
						
							|  |  |  | 					TargetMatchers: config.Matchers{{ | 
					
						
							|  |  |  | 						Type:  labels.MatchEqual, | 
					
						
							|  |  |  | 						Name:  "bar", | 
					
						
							|  |  |  | 						Value: "baz", | 
					
						
							|  |  |  | 					}}, | 
					
						
							|  |  |  | 				}}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			Receivers: []*apimodels.PostableApiReceiver{{ | 
					
						
							|  |  |  | 				Receiver: config.Receiver{ | 
					
						
							|  |  |  | 					Name: "test", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ok, err := client.PostConfiguration(t, cfg) | 
					
						
							|  |  |  | 	require.False(t, ok) | 
					
						
							|  |  |  | 	require.EqualError(t, err, "inhibition rules are not supported") | 
					
						
							|  |  |  | } |