mirror of
https://github.com/TwiN/gatus.git
synced 2026-09-21 07:14:14 +00:00
feat(alerting): allow configuring the topic for Zulip alerts (#1764)
Zulip alerts previously always used a hardcoded "Gatus" topic, sending every alert and resolution into a single thread. Add a "topic" config field (defaulting to "Gatus" for backwards compatibility) that supports the [ENDPOINT_NAME], [ENDPOINT_GROUP] and [ALERT_DESCRIPTION] placeholders, matching the templating convention used by other providers such as clickup. Closes #1387 Co-authored-by: Chris (ChrisJr404) <11917633+ChrisJr404@users.noreply.github.com> Co-authored-by: TwiN <twin@linux.com>
This commit is contained in:
@@ -2530,6 +2530,7 @@ endpoints:
|
||||
| `alerting.zulip.bot-api-key` | Bot API key | Required `""` |
|
||||
| `alerting.zulip.domain` | Full organization domain (e.g.: yourZulipDomain.zulipchat.com) | Required `""` |
|
||||
| `alerting.zulip.channel-id` | The channel ID where Gatus will send the alerts | Required `""` |
|
||||
| `alerting.zulip.topic` | The topic under which the alerts will be sent.<br />Supports the `[ENDPOINT_NAME]`, `[ENDPOINT_GROUP]` and `[ALERT_DESCRIPTION]` placeholders. | `Gatus` |
|
||||
| `alerting.zulip.overrides` | List of overrides that may be prioritized over the default configuration | `[]` |
|
||||
| `alerting.zulip.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration | `""` |
|
||||
| `alerting.zulip.overrides[].*` | See `alerting.zulip.*` parameters | `{}` |
|
||||
@@ -2541,6 +2542,7 @@ alerting:
|
||||
bot-api-key: "********************************"
|
||||
domain: some.zulip.org
|
||||
channel-id: 123456
|
||||
topic: "[ENDPOINT_NAME]" # Optional; groups each endpoint's alerts under its own topic. Defaults to "Gatus"
|
||||
|
||||
endpoints:
|
||||
- name: website
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/TwiN/gatus/v5/alerting/alert"
|
||||
"github.com/TwiN/gatus/v5/client"
|
||||
@@ -23,10 +24,14 @@ var (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
BotEmail string `yaml:"bot-email"` // Email of the bot user
|
||||
BotAPIKey string `yaml:"bot-api-key"` // API key of the bot user
|
||||
Domain string `yaml:"domain"` // Domain of the Zulip server
|
||||
ChannelID string `yaml:"channel-id"` // ID of the channel to send the message to
|
||||
BotEmail string `yaml:"bot-email"` // Email of the bot user
|
||||
BotAPIKey string `yaml:"bot-api-key"` // API key of the bot user
|
||||
Domain string `yaml:"domain"` // Domain of the Zulip server
|
||||
ChannelID string `yaml:"channel-id"` // ID of the channel to send the message to
|
||||
Topic string `yaml:"topic,omitempty"` // Topic to send the message to; defaults to "Gatus" when not set
|
||||
|
||||
// The following placeholders are supported in Topic:
|
||||
// [ENDPOINT_NAME], [ENDPOINT_GROUP], [ALERT_DESCRIPTION]
|
||||
}
|
||||
|
||||
func (cfg *Config) Validate() error {
|
||||
@@ -58,6 +63,9 @@ func (cfg *Config) Merge(override *Config) {
|
||||
if len(override.ChannelID) > 0 {
|
||||
cfg.ChannelID = override.ChannelID
|
||||
}
|
||||
if len(override.Topic) > 0 {
|
||||
cfg.Topic = override.Topic
|
||||
}
|
||||
}
|
||||
|
||||
// AlertProvider is the configuration necessary for sending an alert using Zulip
|
||||
@@ -138,10 +146,17 @@ func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpoint.Endpoi
|
||||
}
|
||||
message += fmt.Sprintf("\n%s - `%s`", prefix, conditionResult.Condition)
|
||||
}
|
||||
topic := cfg.Topic
|
||||
if len(topic) == 0 {
|
||||
topic = "Gatus"
|
||||
}
|
||||
topic = strings.ReplaceAll(topic, "[ENDPOINT_NAME]", ep.Name)
|
||||
topic = strings.ReplaceAll(topic, "[ENDPOINT_GROUP]", ep.Group)
|
||||
topic = strings.ReplaceAll(topic, "[ALERT_DESCRIPTION]", alert.GetDescription())
|
||||
return url.Values{
|
||||
"type": {"channel"},
|
||||
"to": {cfg.ChannelID},
|
||||
"topic": {"Gatus"},
|
||||
"topic": {topic},
|
||||
"content": {message},
|
||||
}.Encode()
|
||||
}
|
||||
|
||||
@@ -128,6 +128,52 @@ func TestAlertProvider_buildRequestBody(t *testing.T) {
|
||||
"type": {"channel"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Failed alert with custom topic",
|
||||
provider: AlertProvider{
|
||||
DefaultConfig: Config{
|
||||
BotEmail: "bot-email",
|
||||
BotAPIKey: "bot-api-key",
|
||||
Domain: "domain",
|
||||
ChannelID: "channel-id",
|
||||
Topic: "alerts",
|
||||
},
|
||||
},
|
||||
alert: basicAlert,
|
||||
resolved: false,
|
||||
hasConditions: false,
|
||||
expectedBody: url.Values{
|
||||
"content": {`An alert for **endpoint-Name** has been triggered due to having failed 3 time(s) in a row
|
||||
> Description
|
||||
`},
|
||||
"to": {"channel-id"},
|
||||
"topic": {"alerts"},
|
||||
"type": {"channel"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Failed alert with templated topic",
|
||||
provider: AlertProvider{
|
||||
DefaultConfig: Config{
|
||||
BotEmail: "bot-email",
|
||||
BotAPIKey: "bot-api-key",
|
||||
Domain: "domain",
|
||||
ChannelID: "channel-id",
|
||||
Topic: "[ENDPOINT_NAME]",
|
||||
},
|
||||
},
|
||||
alert: basicAlert,
|
||||
resolved: false,
|
||||
hasConditions: false,
|
||||
expectedBody: url.Values{
|
||||
"content": {`An alert for **endpoint-Name** has been triggered due to having failed 3 time(s) in a row
|
||||
> Description
|
||||
`},
|
||||
"to": {"channel-id"},
|
||||
"topic": {"endpoint-Name"},
|
||||
"type": {"channel"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Resolved alert with conditions",
|
||||
provider: AlertProvider{
|
||||
@@ -446,12 +492,14 @@ func TestAlertProvider_GetConfig(t *testing.T) {
|
||||
"bot-api-key": "alert-bot-api-key",
|
||||
"domain": "alert-domain",
|
||||
"channel-id": "alert-channel-id",
|
||||
"topic": "alert-topic",
|
||||
}},
|
||||
ExpectedOutput: Config{
|
||||
BotEmail: "alert-bot-email",
|
||||
BotAPIKey: "alert-bot-api-key",
|
||||
Domain: "alert-domain",
|
||||
ChannelID: "alert-channel-id",
|
||||
Topic: "alert-topic",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -473,6 +521,9 @@ func TestAlertProvider_GetConfig(t *testing.T) {
|
||||
if got.ChannelID != scenario.ExpectedOutput.ChannelID {
|
||||
t.Errorf("expected %s, got %s", scenario.ExpectedOutput.ChannelID, got.ChannelID)
|
||||
}
|
||||
if got.Topic != scenario.ExpectedOutput.Topic {
|
||||
t.Errorf("expected %s, got %s", scenario.ExpectedOutput.Topic, got.Topic)
|
||||
}
|
||||
// Test ValidateOverrides as well, since it really just calls GetConfig
|
||||
if err = scenario.Provider.ValidateOverrides(scenario.InputGroup, &scenario.InputAlert); err != nil {
|
||||
t.Errorf("unexpected error: %s", err)
|
||||
|
||||
Reference in New Issue
Block a user