2021-08-13 07:14:36 -05:00
|
|
|
package alerting
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2022-02-15 16:24:39 -06:00
|
|
|
"github.com/prometheus/common/model"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
2021-08-25 08:11:22 -05:00
|
|
|
"github.com/grafana/grafana/pkg/models"
|
|
|
|
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
2021-08-13 07:14:36 -05:00
|
|
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
2022-07-19 08:32:54 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/ngalert/sender"
|
2022-06-28 07:32:25 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/user"
|
2021-08-13 07:14:36 -05:00
|
|
|
"github.com/grafana/grafana/pkg/tests/testinfra"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestAdminConfiguration_SendingToExternalAlertmanagers(t *testing.T) {
|
2021-09-29 09:16:40 -05:00
|
|
|
const disableOrgID int64 = 3
|
2021-08-13 07:14:36 -05:00
|
|
|
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
2021-09-29 09:16:40 -05:00
|
|
|
DisableLegacyAlerting: true,
|
|
|
|
EnableUnifiedAlerting: true,
|
2021-09-16 09:33:51 -05:00
|
|
|
DisableAnonymous: true,
|
|
|
|
NGAlertAdminConfigPollInterval: 2 * time.Second,
|
2021-09-29 09:16:40 -05:00
|
|
|
UnifiedAlertingDisabledOrgs: []int64{disableOrgID}, // disable unified alerting for organisation 3
|
2022-02-09 03:26:06 -06:00
|
|
|
AppModeProduction: true,
|
2021-08-13 07:14:36 -05:00
|
|
|
})
|
|
|
|
|
2021-08-25 08:11:22 -05:00
|
|
|
grafanaListedAddr, s := testinfra.StartGrafana(t, dir, path)
|
2021-08-13 07:14:36 -05:00
|
|
|
|
|
|
|
// Create a user to make authenticated requests
|
2022-06-28 07:32:25 -05:00
|
|
|
userID := createUser(t, s, user.CreateUserCommand{
|
2021-08-13 07:14:36 -05:00
|
|
|
DefaultOrgRole: string(models.ROLE_ADMIN),
|
|
|
|
Login: "grafana",
|
|
|
|
Password: "password",
|
|
|
|
})
|
2022-06-21 10:39:22 -05:00
|
|
|
apiClient := newAlertingApiClient(grafanaListedAddr, "grafana", "password")
|
2021-09-29 09:16:40 -05:00
|
|
|
// create another organisation
|
|
|
|
orgID := createOrg(t, s, "another org", userID)
|
|
|
|
// ensure that the orgID is 3 (the disabled org)
|
|
|
|
require.Equal(t, disableOrgID, orgID)
|
|
|
|
|
|
|
|
// create user under different organisation
|
2022-06-28 07:32:25 -05:00
|
|
|
createUser(t, s, user.CreateUserCommand{
|
2021-09-29 09:16:40 -05:00
|
|
|
DefaultOrgRole: string(models.ROLE_ADMIN),
|
|
|
|
Password: "admin-42",
|
|
|
|
Login: "admin-42",
|
2022-06-28 07:32:25 -05:00
|
|
|
OrgID: orgID,
|
2021-09-29 09:16:40 -05:00
|
|
|
})
|
|
|
|
|
2021-08-13 07:14:36 -05:00
|
|
|
// Create a couple of "fake" Alertmanagers
|
2022-07-19 08:32:54 -05:00
|
|
|
fakeAM1 := sender.NewFakeExternalAlertmanager(t)
|
|
|
|
fakeAM2 := sender.NewFakeExternalAlertmanager(t)
|
|
|
|
fakeAM3 := sender.NewFakeExternalAlertmanager(t)
|
2021-08-13 07:14:36 -05: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
|
|
|
|
b, err := ioutil.ReadAll(resp.Body)
|
|
|
|
require.NoError(t, err)
|
2022-04-14 10:54:49 -05:00
|
|
|
var res map[string]interface{}
|
|
|
|
err = json.Unmarshal(b, &res)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "no admin configuration available", res["message"])
|
2021-08-13 07:14:36 -05: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-01 17:36:55 -06:00
|
|
|
// An invalid alertmanager choice should return an error.
|
2021-08-13 07:14:36 -05: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-01 17:36:55 -06: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
|
|
|
|
b, err := ioutil.ReadAll(resp.Body)
|
|
|
|
require.NoError(t, err)
|
2022-07-20 09:50:49 -05:00
|
|
|
var res map[string]interface{}
|
|
|
|
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-01 17:36:55 -06: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
|
|
|
|
b, err := ioutil.ReadAll(resp.Body)
|
|
|
|
require.NoError(t, err)
|
2022-07-20 09:50:49 -05:00
|
|
|
var res map[string]interface{}
|
|
|
|
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-01 17:36:55 -06: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{
|
|
|
|
Alertmanagers: []string{fakeAM1.URL(), fakeAM2.URL()},
|
|
|
|
AlertmanagersChoice: apimodels.AlertmanagersChoice(ngmodels.ExternalAlertmanagers.String()),
|
2021-08-13 07:14:36 -05: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
|
|
|
|
b, err := ioutil.ReadAll(resp.Body)
|
|
|
|
require.NoError(t, err)
|
2022-07-20 09:50:49 -05:00
|
|
|
var res map[string]interface{}
|
|
|
|
err = json.Unmarshal(b, &res)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "admin configuration updated", res["message"])
|
2021-08-13 07:14:36 -05: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
|
|
|
|
b, err := ioutil.ReadAll(resp.Body)
|
|
|
|
require.NoError(t, err)
|
2022-02-09 03:26:06 -06:00
|
|
|
require.JSONEq(t, fmt.Sprintf("{\"alertmanagers\":[\"%s\",\"%s\"], \"alertmanagersChoice\": %q}\n", fakeAM1.URL(), fakeAM2.URL(), ngmodels.ExternalAlertmanagers), string(b))
|
2021-08-13 07:14:36 -05:00
|
|
|
}
|
|
|
|
|
2021-09-29 09:16:40 -05:00
|
|
|
// With the configuration set, we should eventually discover those Alertmanagers.
|
2021-08-13 07:14:36 -05: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
|
|
|
|
b, err := ioutil.ReadAll(resp.Body)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
var alertmanagers apimodels.GettableAlertmanagers
|
|
|
|
require.NoError(t, json.Unmarshal(b, &alertmanagers))
|
|
|
|
|
|
|
|
return len(alertmanagers.Data.Active) == 2
|
2021-09-29 09:16:40 -05:00
|
|
|
}, 16*time.Second, 8*time.Second) // the sync interval is 2s so after 8s all alertmanagers most probably are started
|
2021-08-13 07:14:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Now, let's set an alert that should fire as quickly as possible.
|
|
|
|
{
|
2022-05-16 05:45:41 -05:00
|
|
|
// Create the namespace we'll save our alerts to
|
2022-06-21 10:39:22 -05:00
|
|
|
apiClient.CreateFolder(t, "default", "default")
|
2021-08-13 07:14:36 -05: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 10:46:26 -05:00
|
|
|
For: &interval,
|
2021-08-13 07:14:36 -05: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",
|
|
|
|
Data: []ngmodels.AlertQuery{
|
|
|
|
{
|
|
|
|
RefID: "A",
|
|
|
|
RelativeTimeRange: ngmodels.RelativeTimeRange{
|
|
|
|
From: ngmodels.Duration(time.Duration(5) * time.Hour),
|
|
|
|
To: ngmodels.Duration(time.Duration(3) * time.Hour),
|
|
|
|
},
|
|
|
|
DatasourceUID: "-100",
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
//Eventually, our Alertmanagers should receiver the alert.
|
|
|
|
{
|
|
|
|
require.Eventually(t, func() bool {
|
|
|
|
return fakeAM1.AlertsCount() == 1 && fakeAM2.AlertsCount() == 1
|
|
|
|
}, 60*time.Second, 5*time.Second)
|
|
|
|
}
|
2021-09-29 09:16:40 -05: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-01 17:36:55 -06:00
|
|
|
// Sending an empty value for AlertmanagersChoice should default to AllAlertmanagers.
|
2021-09-29 09:16:40 -05:00
|
|
|
{
|
|
|
|
ac := apimodels.PostableNGalertConfig{
|
|
|
|
Alertmanagers: []string{fakeAM3.URL()},
|
|
|
|
}
|
|
|
|
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
|
|
|
|
b, err := ioutil.ReadAll(resp.Body)
|
|
|
|
require.NoError(t, err)
|
2022-07-20 09:50:49 -05:00
|
|
|
var res map[string]interface{}
|
|
|
|
err = json.Unmarshal(b, &res)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "admin configuration updated", res["message"])
|
2021-09-29 09:16:40 -05: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
|
|
|
|
b, err := ioutil.ReadAll(resp.Body)
|
|
|
|
require.NoError(t, err)
|
2022-02-09 03:26:06 -06:00
|
|
|
require.JSONEq(t, fmt.Sprintf("{\"alertmanagers\":[\"%s\"], \"alertmanagersChoice\": %q}\n", fakeAM3.URL(), ngmodels.AllAlertmanagers), string(b))
|
2021-09-29 09:16:40 -05: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
|
|
|
|
b, err := ioutil.ReadAll(resp.Body)
|
|
|
|
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 07:14:36 -05:00
|
|
|
}
|