diff --git a/docs/alert-routing.md b/docs/alert-routing.md index 9bacb05e..2070ad23 100644 --- a/docs/alert-routing.md +++ b/docs/alert-routing.md @@ -36,7 +36,7 @@ Pessimism currently supports the following alert destinations: |-----------|-------------------------------------| | slack | Sends alerts to a Slack channel | | pagerduty | Sends alerts to a PagerDuty service | -| pagerduty | Sends alerts to a PagerDuty service | +| telegram | Sends alerts to a Telegram service | ## Alert Severity diff --git a/internal/alert/manager.go b/internal/alert/manager.go index 7f2b43f1..e063cea3 100644 --- a/internal/alert/manager.go +++ b/internal/alert/manager.go @@ -142,6 +142,35 @@ func (am *alertManager) handlePagerDutyPost(alert core.Alert) error { return nil } +func (am *alertManager) handleTelegramPost(alert core.Alert, policy *core.AlertPolicy) error { + telegramClients := am.cm.GetTelegramClients(alert.Sev) + if telegramClients == nil { + am.logger.Warn("No telegram clients defined for criticality", zap.Any("alert", alert)) + return nil + } + + // Create Telegram event trigger + event := &client.AlertEventTrigger{ + Message: am.interpolator.TelegramMessage(alert, policy.Msg), + Severity: alert.Sev, + } + + for _, tc := range telegramClients { + resp, err := tc.PostEvent(am.ctx, event) + if err != nil { + return err + } + + if resp.Status != core.SuccessStatus { + return fmt.Errorf("client %s could not post to telegram: %s", tc.GetName(), resp.Message) + } + am.logger.Debug("Successfully posted to Telegram", zap.String("resp", resp.Message)) + am.metrics.RecordAlertGenerated(alert, core.Telegram, tc.GetName()) + } + + return nil +} + // EventLoop ... Event loop for alert manager subsystem func (am *alertManager) EventLoop() error { ticker := time.NewTicker(time.Second * 1) @@ -202,6 +231,10 @@ func (am *alertManager) HandleAlert(alert core.Alert, policy *core.AlertPolicy) if err := am.handlePagerDutyPost(alert); err != nil { am.logger.Error("could not post to pagerduty", zap.Error(err)) } + + if err := am.handleTelegramPost(alert, policy); err != nil { + am.logger.Error("could not post to telegram", zap.Error(err)) + } } // Shutdown ... Shuts down the alert manager subsystem diff --git a/internal/alert/routing.go b/internal/alert/routing.go index ed3459c6..de62646f 100644 --- a/internal/alert/routing.go +++ b/internal/alert/routing.go @@ -109,8 +109,8 @@ func (rd *routingDirectory) paramsToRouteDirectory(acc *core.AlertClientCfg, sev if acc.Telegram != nil { for name, cfg := range acc.Telegram { conf := &client.TelegramConfig{ - Bot: cfg.Bot.String(), - Token: cfg.Token.String(), + ChatID: cfg.ChatID.String(), + Token: cfg.Token.String(), } client := client.NewTelegramClient(conf, name) rd.telegramClients[sev] = append(rd.telegramClients[sev], client) diff --git a/internal/client/telegram.go b/internal/client/telegram.go index 960ba5bf..e34a1b21 100644 --- a/internal/client/telegram.go +++ b/internal/client/telegram.go @@ -22,12 +22,13 @@ type TelegramConfig struct { } type telegramClient struct { + name string token string chatID string client *http.Client } -func NewTelegramClient(cfg *TelegramConfig) TelegramClient { +func NewTelegramClient(cfg *TelegramConfig, name string) TelegramClient { if cfg.Token == "" { logging.NoContext().Warn("No Telegram token provided") } @@ -35,6 +36,7 @@ func NewTelegramClient(cfg *TelegramConfig) TelegramClient { return &telegramClient{ token: cfg.Token, chatID: cfg.ChatID, + name: name, client: &http.Client{}, } } @@ -101,5 +103,5 @@ func (tc *telegramClient) PostEvent(ctx context.Context, data *AlertEventTrigger } func (tc *telegramClient) GetName() string { - return "TelegramClient" + return tc.name } diff --git a/internal/core/alert.go b/internal/core/alert.go index 5e15928d..fc913993 100644 --- a/internal/core/alert.go +++ b/internal/core/alert.go @@ -141,9 +141,9 @@ type AlertClientCfg struct { // AlertConfig ... The config for an alert client type AlertConfig struct { - URL StringFromEnv `yaml:"url"` - Channel StringFromEnv `yaml:"channel"` - IntegrationKey StringFromEnv `yaml:"integration_key"` - TelegramBotToken StringFromEnv `yaml:"telegram_bot_token"` - TelegramChatID StringFromEnv `yaml:"telegram_chat_id"` + URL StringFromEnv `yaml:"url"` + Channel StringFromEnv `yaml:"channel"` + IntegrationKey StringFromEnv `yaml:"integration_key"` + Token StringFromEnv `yaml:"telegram_bot_token"` + ChatID StringFromEnv `yaml:"telegram_chat_id"` } diff --git a/internal/core/constants.go b/internal/core/constants.go index edef5960..37e6144d 100644 --- a/internal/core/constants.go +++ b/internal/core/constants.go @@ -173,6 +173,7 @@ const ( Slack AlertDestination = iota + 1 PagerDuty ThirdParty + Telegram ) // String ... Converts an alerting destination type to a string @@ -182,6 +183,8 @@ func (ad AlertDestination) String() string { return "slack" case PagerDuty: return "pager_duty" + case Telegram: + return "telegram" case ThirdParty: return "third_party" default: @@ -196,6 +199,8 @@ func StringToAlertingDestType(stringType string) AlertDestination { return Slack case "pager_duty": return PagerDuty + case "telegram": + return Telegram case "third_party": return ThirdParty }