From 2c8c456512a1d35952363f980d20c38018fb16e6 Mon Sep 17 00:00:00 2001 From: lefes Date: Sun, 28 Jul 2024 02:05:17 +0300 Subject: [PATCH] feat(alerting): add telegram overriding token and id (#826) * feat(alerting): add telegram overriding token and id * Update alerting/provider/telegram/telegram_test.go * Update alerting/provider/telegram/telegram_test.go * Update alerting/provider/telegram/telegram_test.go --------- Co-authored-by: TwiN --- README.md | 22 ++++--- alerting/provider/telegram/telegram.go | 44 +++++++++++++- alerting/provider/telegram/telegram_test.go | 65 ++++++++++++++++++++- 3 files changed, 119 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 5d2a436e..8004653d 100644 --- a/README.md +++ b/README.md @@ -279,7 +279,7 @@ This allows you to monitor anything you want, even when what you want to check l For instance: - You can create your own agent that lives in a private network and pushes the status of your services to a publicly-exposed Gatus instance -- You can monitor services that are not supported by Gatus +- You can monitor services that are not supported by Gatus - You can implement your own monitoring system while using Gatus as the dashboard | Parameter | Description | Default | @@ -1185,14 +1185,18 @@ Here's an example of what the notifications look like: #### Configuring Telegram alerts -| Parameter | Description | Default | -|:----------------------------------|:-------------------------------------------------------------------------------------------|:---------------------------| -| `alerting.telegram` | Configuration for alerts of type `telegram` | `{}` | -| `alerting.telegram.token` | Telegram Bot Token | Required `""` | -| `alerting.telegram.id` | Telegram User ID | Required `""` | -| `alerting.telegram.api-url` | Telegram API URL | `https://api.telegram.org` | -| `alerting.telegram.client` | Client configuration.
See [Client configuration](#client-configuration). | `{}` | -| `alerting.telegram.default-alert` | Default alert configuration.
See [Setting a default alert](#setting-a-default-alert) | N/A | +| Parameter | Description | Default | +|:--------------------------------------|:-------------------------------------------------------------------------------------------|:---------------------------| +| `alerting.telegram` | Configuration for alerts of type `telegram` | `{}` | +| `alerting.telegram.token` | Telegram Bot Token | Required `""` | +| `alerting.telegram.id` | Telegram User ID | Required `""` | +| `alerting.telegram.api-url` | Telegram API URL | `https://api.telegram.org` | +| `alerting.telegram.client` | Client configuration.
See [Client configuration](#client-configuration). | `{}` | +| `alerting.telegram.default-alert` | Default alert configuration.
See [Setting a default alert](#setting-a-default-alert) | N/A | +| `alerting.telegram.overrides` | List of overrides that may be prioritized over the default configuration | `[]` | +| `alerting.telegram.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration | `""` | +| `alerting.telegram.overrides[].token` | Telegram Bot Token for override default value | `""` | +| `alerting.telegram.overrides[].id` | Telegram User ID for override default value | `""` | ```yaml alerting: diff --git a/alerting/provider/telegram/telegram.go b/alerting/provider/telegram/telegram.go index f5950a17..ecfd700b 100644 --- a/alerting/provider/telegram/telegram.go +++ b/alerting/provider/telegram/telegram.go @@ -25,6 +25,16 @@ type AlertProvider struct { // DefaultAlert is the default alert configuration to use for endpoints with an alert of the appropriate type DefaultAlert *alert.Alert `yaml:"default-alert,omitempty"` + + // Overrides is a list of Overrid that may be prioritized over the default configuration + Overrides []*Override `yaml:"overrides,omitempty"` +} + +// Override is a configuration that may be prioritized over the default configuration +type Override struct { + group string `yaml:"group"` + token string `yaml:"token"` + id string `yaml:"id"` } // IsValid returns whether the provider's configuration is valid @@ -32,6 +42,18 @@ func (provider *AlertProvider) IsValid() bool { if provider.ClientConfig == nil { provider.ClientConfig = client.GetDefaultConfig() } + + registerGroups := make(map[string]bool) + for _, override := range provider.Overrides { + if len(override.group) == 0 { + return false + } + if _, ok := registerGroups[override.group]; ok { + return false + } + registerGroups[override.group] = true + } + return len(provider.Token) > 0 && len(provider.ID) > 0 } @@ -42,7 +64,7 @@ func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *alert.Alert, r if apiURL == "" { apiURL = defaultAPIURL } - request, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/bot%s/sendMessage", apiURL, provider.Token), buffer) + request, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/bot%s/sendMessage", apiURL, provider.getTokenForGroup(ep.Group)), buffer) if err != nil { return err } @@ -59,6 +81,15 @@ func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *alert.Alert, r return err } +func (provider *AlertProvider) getTokenForGroup(group string) string { + for _, override := range provider.Overrides { + if override.group == group && len(override.token) > 0 { + return override.token + } + } + return provider.Token +} + type Body struct { ChatID string `json:"chat_id"` Text string `json:"text"` @@ -93,13 +124,22 @@ func (provider *AlertProvider) buildRequestBody(ep *endpoint.Endpoint, alert *al text = fmt.Sprintf("⛑ *Gatus* \n%s%s", message, formattedConditionResults) } bodyAsJSON, _ := json.Marshal(Body{ - ChatID: provider.ID, + ChatID: provider.getIDForGroup(ep.Group), Text: text, ParseMode: "MARKDOWN", }) return bodyAsJSON } +func (provider *AlertProvider) getIDForGroup(group string) string { + for _, override := range provider.Overrides { + if override.group == group && len(override.id) > 0 { + return override.id + } + } + return provider.ID +} + // GetDefaultAlert returns the provider's default alert configuration func (provider *AlertProvider) GetDefaultAlert() *alert.Alert { return provider.DefaultAlert diff --git a/alerting/provider/telegram/telegram_test.go b/alerting/provider/telegram/telegram_test.go index 6c9b9109..c1f201cb 100644 --- a/alerting/provider/telegram/telegram_test.go +++ b/alerting/provider/telegram/telegram_test.go @@ -11,7 +11,7 @@ import ( "github.com/TwiN/gatus/v5/test" ) -func TestAlertProvider_IsValid(t *testing.T) { +func TestAlertDefaultProvider_IsValid(t *testing.T) { t.Run("invalid-provider", func(t *testing.T) { invalidProvider := AlertProvider{Token: "", ID: ""} if invalidProvider.IsValid() { @@ -32,6 +32,69 @@ func TestAlertProvider_IsValid(t *testing.T) { }) } +func TestAlertProvider_IsValidWithOverrides(t *testing.T) { + t.Run("invalid-provider-override-nonexist-group", func(t *testing.T) { + invalidProvider := AlertProvider{Token: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11", ID: "12345678", Overrides: []*Override{{token: "token", id: "id"}}} + if invalidProvider.IsValid() { + t.Error("provider shouldn't have been valid") + } + }) + t.Run("invalid-provider-override-duplicate-group", func(t *testing.T) { + invalidProvider := AlertProvider{Token: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11", ID: "12345678", Overrides: []*Override{{group: "group1", token: "token", id: "id"}, {group: "group1", id: "id2"}}} + if invalidProvider.IsValid() { + t.Error("provider shouldn't have been valid") + } + }) + t.Run("valid-provider", func(t *testing.T) { + validProvider := AlertProvider{Token: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11", ID: "12345678", Overrides: []*Override{{group: "group", token: "token", id: "id"}}} + if validProvider.ClientConfig != nil { + t.Error("provider client config should have been nil prior to IsValid() being executed") + } + if !validProvider.IsValid() { + t.Error("provider should've been valid") + } + if validProvider.ClientConfig == nil { + t.Error("provider client config should have been set after IsValid() was executed") + } + }) +} + +func TestAlertProvider_getTokenAndIDForGroup(t *testing.T) { + t.Run("get-token-with-override", func(t *testing.T) { + provider := AlertProvider{Token: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11", ID: "12345678", Overrides: []*Override{{group: "group", token: "overrideToken", id: "overrideID"}}} + token := provider.getTokenForGroup("group") + if token != "overrideToken" { + t.Error("token should have been 'overrideToken'") + } + id := provider.getIDForGroup("group") + if id != "overrideID" { + t.Error("id should have been 'overrideID'") + } + }) + t.Run("get-default-token-with-overridden-id", func(t *testing.T) { + provider := AlertProvider{Token: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11", ID: "12345678", Overrides: []*Override{{group: "group", id: "overrideID"}}} + token := provider.getTokenForGroup("group") + if token != provider.Token { + t.Error("token should have been the default token") + } + id := provider.getIDForGroup("group") + if id != "overrideID" { + t.Error("id should have been 'overrideID'") + } + }) + t.Run("get-default-token-with-overridden-token", func(t *testing.T) { + provider := AlertProvider{Token: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11", ID: "12345678", Overrides: []*Override{{group: "group", token: "overrideToken"}}} + token := provider.getTokenForGroup("group") + if token != "overrideToken" { + t.Error("token should have been 'overrideToken'") + } + id := provider.getIDForGroup("group") + if id != provider.ID { + t.Error("id should have been the default id") + } + }) +} + func TestAlertProvider_Send(t *testing.T) { defer client.InjectHTTPClient(nil) firstDescription := "description-1"