Skip to content

Commit

Permalink
feat: add rocketchat notifier
Browse files Browse the repository at this point in the history
Signed-off-by: Christoph Maser <christoph.maser+github@gmail.com>
Co-authored-by: George Robinson <george.robinson@grafana.com>
  • Loading branch information
TheMeier and grobinson-grafana committed Oct 23, 2024
1 parent e6cb11c commit e69340b
Show file tree
Hide file tree
Showing 14 changed files with 789 additions and 62 deletions.
142 changes: 91 additions & 51 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ func resolveFilepaths(baseDir string, cfg *Config) {
for _, cfg := range receiver.JiraConfigs {
cfg.HTTPConfig.SetDirectory(baseDir)
}
for _, cfg := range receiver.RocketchatConfigs {
cfg.HTTPConfig.SetDirectory(baseDir)
}
}
}

Expand Down Expand Up @@ -364,6 +367,14 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
return errors.New("at most one of smtp_auth_password & smtp_auth_password_file must be configured")
}

if c.Global.RocketchatToken != nil && len(c.Global.RocketchatTokenFile) > 0 {
return errors.New("at most one of rocketchat_token & rocketchat_token_file must be configured")
}

if c.Global.RocketchatTokenID != nil && len(c.Global.RocketchatTokenIDFile) > 0 {
return errors.New("at most one of rocketchat_token_id & rocketchat_token_id_file must be configured")
}

names := map[string]struct{}{}

for _, rcv := range c.Receivers {
Expand Down Expand Up @@ -573,6 +584,28 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
jira.APIURL = c.Global.JiraAPIURL
}
}
for _, rocketchat := range rcv.RocketchatConfigs {
if rocketchat.HTTPConfig == nil {
rocketchat.HTTPConfig = c.Global.HTTPConfig
}
if rocketchat.APIURL == nil {
rocketchat.APIURL = c.Global.RocketchatAPIURL
}
if rocketchat.TokenID == nil && len(rocketchat.TokenIDFile) == 0 {
if c.Global.RocketchatTokenID == nil && len(c.Global.RocketchatTokenIDFile) == 0 {
return errors.New("no global Rocketchat TokenID set either inline or in a file")
}
rocketchat.TokenID = c.Global.RocketchatTokenID
rocketchat.TokenIDFile = c.Global.RocketchatTokenIDFile
}
if rocketchat.Token == nil && len(rocketchat.TokenFile) == 0 {
if c.Global.RocketchatToken == nil && len(c.Global.RocketchatTokenFile) == 0 {
return errors.New("no global Rocketchat Token set either inline or in a file")
}
rocketchat.Token = c.Global.RocketchatToken
rocketchat.TokenFile = c.Global.RocketchatTokenFile
}
}

names[rcv.Name] = struct{}{}
}
Expand Down Expand Up @@ -665,17 +698,18 @@ func DefaultGlobalConfig() GlobalConfig {
defaultSMTPTLSConfig := commoncfg.TLSConfig{}

return GlobalConfig{
ResolveTimeout: model.Duration(5 * time.Minute),
HTTPConfig: &defaultHTTPConfig,
SMTPHello: "localhost",
SMTPRequireTLS: true,
SMTPTLSConfig: &defaultSMTPTLSConfig,
PagerdutyURL: mustParseURL("https://events.pagerduty.com/v2/enqueue"),
OpsGenieAPIURL: mustParseURL("https://api.opsgenie.com/"),
WeChatAPIURL: mustParseURL("https://qyapi.weixin.qq.com/cgi-bin/"),
VictorOpsAPIURL: mustParseURL("https://alert.victorops.com/integrations/generic/20131114/alert/"),
TelegramAPIUrl: mustParseURL("https://api.telegram.org"),
WebexAPIURL: mustParseURL("https://webexapis.com/v1/messages"),
ResolveTimeout: model.Duration(5 * time.Minute),
HTTPConfig: &defaultHTTPConfig,
SMTPHello: "localhost",
SMTPRequireTLS: true,
SMTPTLSConfig: &defaultSMTPTLSConfig,
PagerdutyURL: mustParseURL("https://events.pagerduty.com/v2/enqueue"),
OpsGenieAPIURL: mustParseURL("https://api.opsgenie.com/"),
WeChatAPIURL: mustParseURL("https://qyapi.weixin.qq.com/cgi-bin/"),
VictorOpsAPIURL: mustParseURL("https://alert.victorops.com/integrations/generic/20131114/alert/"),
TelegramAPIUrl: mustParseURL("https://api.telegram.org"),
WebexAPIURL: mustParseURL("https://webexapis.com/v1/messages"),
RocketchatAPIURL: mustParseURL("https://open.rocket.chat/"),
}
}

Expand Down Expand Up @@ -777,31 +811,36 @@ type GlobalConfig struct {

HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`

JiraAPIURL *URL `yaml:"jira_api_url,omitempty" json:"jira_api_url,omitempty"`
SMTPFrom string `yaml:"smtp_from,omitempty" json:"smtp_from,omitempty"`
SMTPHello string `yaml:"smtp_hello,omitempty" json:"smtp_hello,omitempty"`
SMTPSmarthost HostPort `yaml:"smtp_smarthost,omitempty" json:"smtp_smarthost,omitempty"`
SMTPAuthUsername string `yaml:"smtp_auth_username,omitempty" json:"smtp_auth_username,omitempty"`
SMTPAuthPassword Secret `yaml:"smtp_auth_password,omitempty" json:"smtp_auth_password,omitempty"`
SMTPAuthPasswordFile string `yaml:"smtp_auth_password_file,omitempty" json:"smtp_auth_password_file,omitempty"`
SMTPAuthSecret Secret `yaml:"smtp_auth_secret,omitempty" json:"smtp_auth_secret,omitempty"`
SMTPAuthIdentity string `yaml:"smtp_auth_identity,omitempty" json:"smtp_auth_identity,omitempty"`
SMTPRequireTLS bool `yaml:"smtp_require_tls" json:"smtp_require_tls,omitempty"`
SMTPTLSConfig *commoncfg.TLSConfig `yaml:"smtp_tls_config,omitempty" json:"smtp_tls_config,omitempty"`
SlackAPIURL *SecretURL `yaml:"slack_api_url,omitempty" json:"slack_api_url,omitempty"`
SlackAPIURLFile string `yaml:"slack_api_url_file,omitempty" json:"slack_api_url_file,omitempty"`
PagerdutyURL *URL `yaml:"pagerduty_url,omitempty" json:"pagerduty_url,omitempty"`
OpsGenieAPIURL *URL `yaml:"opsgenie_api_url,omitempty" json:"opsgenie_api_url,omitempty"`
OpsGenieAPIKey Secret `yaml:"opsgenie_api_key,omitempty" json:"opsgenie_api_key,omitempty"`
OpsGenieAPIKeyFile string `yaml:"opsgenie_api_key_file,omitempty" json:"opsgenie_api_key_file,omitempty"`
WeChatAPIURL *URL `yaml:"wechat_api_url,omitempty" json:"wechat_api_url,omitempty"`
WeChatAPISecret Secret `yaml:"wechat_api_secret,omitempty" json:"wechat_api_secret,omitempty"`
WeChatAPICorpID string `yaml:"wechat_api_corp_id,omitempty" json:"wechat_api_corp_id,omitempty"`
VictorOpsAPIURL *URL `yaml:"victorops_api_url,omitempty" json:"victorops_api_url,omitempty"`
VictorOpsAPIKey Secret `yaml:"victorops_api_key,omitempty" json:"victorops_api_key,omitempty"`
VictorOpsAPIKeyFile string `yaml:"victorops_api_key_file,omitempty" json:"victorops_api_key_file,omitempty"`
TelegramAPIUrl *URL `yaml:"telegram_api_url,omitempty" json:"telegram_api_url,omitempty"`
WebexAPIURL *URL `yaml:"webex_api_url,omitempty" json:"webex_api_url,omitempty"`
JiraAPIURL *URL `yaml:"jira_api_url,omitempty" json:"jira_api_url,omitempty"`
SMTPFrom string `yaml:"smtp_from,omitempty" json:"smtp_from,omitempty"`
SMTPHello string `yaml:"smtp_hello,omitempty" json:"smtp_hello,omitempty"`
SMTPSmarthost HostPort `yaml:"smtp_smarthost,omitempty" json:"smtp_smarthost,omitempty"`
SMTPAuthUsername string `yaml:"smtp_auth_username,omitempty" json:"smtp_auth_username,omitempty"`
SMTPAuthPassword Secret `yaml:"smtp_auth_password,omitempty" json:"smtp_auth_password,omitempty"`
SMTPAuthPasswordFile string `yaml:"smtp_auth_password_file,omitempty" json:"smtp_auth_password_file,omitempty"`
SMTPAuthSecret Secret `yaml:"smtp_auth_secret,omitempty" json:"smtp_auth_secret,omitempty"`
SMTPAuthIdentity string `yaml:"smtp_auth_identity,omitempty" json:"smtp_auth_identity,omitempty"`
SMTPRequireTLS bool `yaml:"smtp_require_tls" json:"smtp_require_tls,omitempty"`
SMTPTLSConfig *commoncfg.TLSConfig `yaml:"smtp_tls_config,omitempty" json:"smtp_tls_config,omitempty"`
SlackAPIURL *SecretURL `yaml:"slack_api_url,omitempty" json:"slack_api_url,omitempty"`
SlackAPIURLFile string `yaml:"slack_api_url_file,omitempty" json:"slack_api_url_file,omitempty"`
PagerdutyURL *URL `yaml:"pagerduty_url,omitempty" json:"pagerduty_url,omitempty"`
OpsGenieAPIURL *URL `yaml:"opsgenie_api_url,omitempty" json:"opsgenie_api_url,omitempty"`
OpsGenieAPIKey Secret `yaml:"opsgenie_api_key,omitempty" json:"opsgenie_api_key,omitempty"`
OpsGenieAPIKeyFile string `yaml:"opsgenie_api_key_file,omitempty" json:"opsgenie_api_key_file,omitempty"`
WeChatAPIURL *URL `yaml:"wechat_api_url,omitempty" json:"wechat_api_url,omitempty"`
WeChatAPISecret Secret `yaml:"wechat_api_secret,omitempty" json:"wechat_api_secret,omitempty"`
WeChatAPICorpID string `yaml:"wechat_api_corp_id,omitempty" json:"wechat_api_corp_id,omitempty"`
VictorOpsAPIURL *URL `yaml:"victorops_api_url,omitempty" json:"victorops_api_url,omitempty"`
VictorOpsAPIKey Secret `yaml:"victorops_api_key,omitempty" json:"victorops_api_key,omitempty"`
VictorOpsAPIKeyFile string `yaml:"victorops_api_key_file,omitempty" json:"victorops_api_key_file,omitempty"`
TelegramAPIUrl *URL `yaml:"telegram_api_url,omitempty" json:"telegram_api_url,omitempty"`
WebexAPIURL *URL `yaml:"webex_api_url,omitempty" json:"webex_api_url,omitempty"`
RocketchatAPIURL *URL `yaml:"rocketchat_api_url,omitempty" json:"rocketchat_api_url,omitempty"`
RocketchatToken *Secret `yaml:"rocketchat_token,omitempty" json:"rocketchat_token,omitempty"`
RocketchatTokenFile string `yaml:"rocketchat_token_file,omitempty" json:"rocketchat_token_file,omitempty"`
RocketchatTokenID *Secret `yaml:"rocketchat_token_id,omitempty" json:"rocketchat_token_id,omitempty"`
RocketchatTokenIDFile string `yaml:"rocketchat_token_id_file,omitempty" json:"rocketchat_token_id_file,omitempty"`
}

// UnmarshalYAML implements the yaml.Unmarshaler interface for GlobalConfig.
Expand Down Expand Up @@ -933,21 +972,22 @@ type Receiver struct {
// A unique identifier for this receiver.
Name string `yaml:"name" json:"name"`

DiscordConfigs []*DiscordConfig `yaml:"discord_configs,omitempty" json:"discord_configs,omitempty"`
EmailConfigs []*EmailConfig `yaml:"email_configs,omitempty" json:"email_configs,omitempty"`
PagerdutyConfigs []*PagerdutyConfig `yaml:"pagerduty_configs,omitempty" json:"pagerduty_configs,omitempty"`
SlackConfigs []*SlackConfig `yaml:"slack_configs,omitempty" json:"slack_configs,omitempty"`
WebhookConfigs []*WebhookConfig `yaml:"webhook_configs,omitempty" json:"webhook_configs,omitempty"`
OpsGenieConfigs []*OpsGenieConfig `yaml:"opsgenie_configs,omitempty" json:"opsgenie_configs,omitempty"`
WechatConfigs []*WechatConfig `yaml:"wechat_configs,omitempty" json:"wechat_configs,omitempty"`
PushoverConfigs []*PushoverConfig `yaml:"pushover_configs,omitempty" json:"pushover_configs,omitempty"`
VictorOpsConfigs []*VictorOpsConfig `yaml:"victorops_configs,omitempty" json:"victorops_configs,omitempty"`
SNSConfigs []*SNSConfig `yaml:"sns_configs,omitempty" json:"sns_configs,omitempty"`
TelegramConfigs []*TelegramConfig `yaml:"telegram_configs,omitempty" json:"telegram_configs,omitempty"`
WebexConfigs []*WebexConfig `yaml:"webex_configs,omitempty" json:"webex_configs,omitempty"`
MSTeamsConfigs []*MSTeamsConfig `yaml:"msteams_configs,omitempty" json:"msteams_configs,omitempty"`
MSTeamsV2Configs []*MSTeamsV2Config `yaml:"msteamsv2_configs,omitempty" json:"msteamsv2_configs,omitempty"`
JiraConfigs []*JiraConfig `yaml:"jira_configs,omitempty" json:"jira_configs,omitempty"`
DiscordConfigs []*DiscordConfig `yaml:"discord_configs,omitempty" json:"discord_configs,omitempty"`
EmailConfigs []*EmailConfig `yaml:"email_configs,omitempty" json:"email_configs,omitempty"`
PagerdutyConfigs []*PagerdutyConfig `yaml:"pagerduty_configs,omitempty" json:"pagerduty_configs,omitempty"`
SlackConfigs []*SlackConfig `yaml:"slack_configs,omitempty" json:"slack_configs,omitempty"`
WebhookConfigs []*WebhookConfig `yaml:"webhook_configs,omitempty" json:"webhook_configs,omitempty"`
OpsGenieConfigs []*OpsGenieConfig `yaml:"opsgenie_configs,omitempty" json:"opsgenie_configs,omitempty"`
WechatConfigs []*WechatConfig `yaml:"wechat_configs,omitempty" json:"wechat_configs,omitempty"`
PushoverConfigs []*PushoverConfig `yaml:"pushover_configs,omitempty" json:"pushover_configs,omitempty"`
VictorOpsConfigs []*VictorOpsConfig `yaml:"victorops_configs,omitempty" json:"victorops_configs,omitempty"`
SNSConfigs []*SNSConfig `yaml:"sns_configs,omitempty" json:"sns_configs,omitempty"`
TelegramConfigs []*TelegramConfig `yaml:"telegram_configs,omitempty" json:"telegram_configs,omitempty"`
WebexConfigs []*WebexConfig `yaml:"webex_configs,omitempty" json:"webex_configs,omitempty"`
MSTeamsConfigs []*MSTeamsConfig `yaml:"msteams_configs,omitempty" json:"msteams_configs,omitempty"`
MSTeamsV2Configs []*MSTeamsV2Config `yaml:"msteamsv2_configs,omitempty" json:"msteamsv2_configs,omitempty"`
JiraConfigs []*JiraConfig `yaml:"jira_configs,omitempty" json:"jira_configs,omitempty"`
RocketchatConfigs []*RocketchatConfig `yaml:"rocketchat_configs,omitempty" json:"rocketchat_configs,omitempty"`
}

// UnmarshalYAML implements the yaml.Unmarshaler interface for Receiver.
Expand Down
111 changes: 103 additions & 8 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -872,14 +872,15 @@ func TestEmptyFieldsAndRegex(t *testing.T) {
SMTPTLSConfig: &commoncfg.TLSConfig{
InsecureSkipVerify: false,
},
SlackAPIURL: (*SecretURL)(mustParseURL("http://slack.example.com/")),
SMTPRequireTLS: true,
PagerdutyURL: mustParseURL("https://events.pagerduty.com/v2/enqueue"),
OpsGenieAPIURL: mustParseURL("https://api.opsgenie.com/"),
WeChatAPIURL: mustParseURL("https://qyapi.weixin.qq.com/cgi-bin/"),
VictorOpsAPIURL: mustParseURL("https://alert.victorops.com/integrations/generic/20131114/alert/"),
TelegramAPIUrl: mustParseURL("https://api.telegram.org"),
WebexAPIURL: mustParseURL("https://webexapis.com/v1/messages"),
SlackAPIURL: (*SecretURL)(mustParseURL("http://slack.example.com/")),
SMTPRequireTLS: true,
PagerdutyURL: mustParseURL("https://events.pagerduty.com/v2/enqueue"),
OpsGenieAPIURL: mustParseURL("https://api.opsgenie.com/"),
WeChatAPIURL: mustParseURL("https://qyapi.weixin.qq.com/cgi-bin/"),
VictorOpsAPIURL: mustParseURL("https://alert.victorops.com/integrations/generic/20131114/alert/"),
TelegramAPIUrl: mustParseURL("https://api.telegram.org"),
WebexAPIURL: mustParseURL("https://webexapis.com/v1/messages"),
RocketchatAPIURL: mustParseURL("https://open.rocket.chat/"),
},

Templates: []string{
Expand Down Expand Up @@ -1203,6 +1204,100 @@ func TestInvalidSNSConfig(t *testing.T) {
}
}

func TestRocketchatDefaultToken(t *testing.T) {
conf, err := LoadFile("testdata/conf.rocketchat-default-token.yml")
if err != nil {
t.Fatalf("Error parsing %s: %s", "testdata/conf.rocketchat-default-token.yml", err)
}

defaultToken := conf.Global.RocketchatToken
overrideToken := Secret("token456")
if defaultToken != conf.Receivers[0].RocketchatConfigs[0].Token {
t.Fatalf("Invalid rocketchat key: %s\nExpected: %s", string(*conf.Receivers[0].RocketchatConfigs[0].Token), string(*defaultToken))
}
if overrideToken != *conf.Receivers[1].RocketchatConfigs[0].Token {
t.Errorf("Invalid rocketchat key: %s\nExpected: %s", string(*conf.Receivers[0].RocketchatConfigs[0].Token), string(overrideToken))
}
}

func TestRocketchatDefaultTokenID(t *testing.T) {
conf, err := LoadFile("testdata/conf.rocketchat-default-token.yml")
if err != nil {
t.Fatalf("Error parsing %s: %s", "testdata/conf.rocketchat-default-token.yml", err)
}

defaultTokenID := conf.Global.RocketchatTokenID
overrideTokenID := Secret("id456")
if defaultTokenID != conf.Receivers[0].RocketchatConfigs[0].TokenID {
t.Fatalf("Invalid rocketchat key: %s\nExpected: %s", string(*conf.Receivers[0].RocketchatConfigs[0].TokenID), string(*defaultTokenID))
}
if overrideTokenID != *conf.Receivers[1].RocketchatConfigs[0].TokenID {
t.Errorf("Invalid rocketchat key: %s\nExpected: %s", string(*conf.Receivers[0].RocketchatConfigs[0].TokenID), string(overrideTokenID))
}
}

func TestRocketchatDefaultTokenFile(t *testing.T) {
conf, err := LoadFile("testdata/conf.rocketchat-default-token-file.yml")
if err != nil {
t.Fatalf("Error parsing %s: %s", "testdata/conf.rocketchat-default-token-file.yml", err)
}

defaultTokenFile := conf.Global.RocketchatTokenFile
overrideTokenFile := "/override_file"
if defaultTokenFile != conf.Receivers[0].RocketchatConfigs[0].TokenFile {
t.Fatalf("Invalid Rocketchat key_file: %s\nExpected: %s", conf.Receivers[0].RocketchatConfigs[0].TokenFile, defaultTokenFile)
}
if overrideTokenFile != conf.Receivers[1].RocketchatConfigs[0].TokenFile {
t.Errorf("Invalid Rocketchat key_file: %s\nExpected: %s", conf.Receivers[0].RocketchatConfigs[0].TokenFile, overrideTokenFile)
}
}

func TestRocketchatDefaultIDTokenFile(t *testing.T) {
conf, err := LoadFile("testdata/conf.rocketchat-default-token-file.yml")
if err != nil {
t.Fatalf("Error parsing %s: %s", "testdata/conf.rocketchat-default-token-file.yml", err)
}

defaultTokenIDFile := conf.Global.RocketchatTokenIDFile
overrideTokenIDFile := "/override_file"
if defaultTokenIDFile != conf.Receivers[0].RocketchatConfigs[0].TokenIDFile {
t.Fatalf("Invalid Rocketchat key_file: %s\nExpected: %s", conf.Receivers[0].RocketchatConfigs[0].TokenIDFile, defaultTokenIDFile)
}
if overrideTokenIDFile != conf.Receivers[1].RocketchatConfigs[0].TokenIDFile {
t.Errorf("Invalid Rocketchat key_file: %s\nExpected: %s", conf.Receivers[0].RocketchatConfigs[0].TokenIDFile, overrideTokenIDFile)
}
}

func TestRocketchatBothTokenAndTokenFile(t *testing.T) {
_, err := LoadFile("testdata/conf.rocketchat-both-token-and-tokenfile.yml")
if err == nil {
t.Fatalf("Expected an error parsing %s: %s", "testdata/conf.rocketchat-both-token-and-tokenfile.yml", err)
}
if err.Error() != "at most one of rocketchat_token & rocketchat_token_file must be configured" {
t.Errorf("Expected: %s\nGot: %s", "at most one of rocketchat_token & rocketchat_token_file must be configured", err.Error())
}
}

func TestRocketchatBothTokenIDAndTokenIDFile(t *testing.T) {
_, err := LoadFile("testdata/conf.rocketchat-both-tokenid-and-tokenidfile.yml")
if err == nil {
t.Fatalf("Expected an error parsing %s: %s", "testdata/conf.rocketchat-both-tokenid-and-tokenidfile.yml", err)
}
if err.Error() != "at most one of rocketchat_token_id & rocketchat_token_id_file must be configured" {
t.Errorf("Expected: %s\nGot: %s", "at most one of rocketchat_token_id & rocketchat_token_id_file must be configured", err.Error())
}
}

func TestRocketchatNoToken(t *testing.T) {
_, err := LoadFile("testdata/conf.rocketchat-no-token.yml")
if err == nil {
t.Fatalf("Expected an error parsing %s: %s", "testdata/conf.rocketchat-no-token.yml", err)
}
if err.Error() != "no global Rocketchat Token set either inline or in a file" {
t.Errorf("Expected: %s\nGot: %s", "no global Rocketchat Token set either inline or in a file", err.Error())
}
}

func TestUnmarshalHostPort(t *testing.T) {
for _, tc := range []struct {
in string
Expand Down
Loading

0 comments on commit e69340b

Please sign in to comment.