Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discord integration #5671

Merged
merged 17 commits into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
382 changes: 382 additions & 0 deletions bundle.yaml

Large diffs are not rendered by default.

2,804 changes: 1,784 additions & 1,020 deletions example/prometheus-operator-crd-full/monitoring.coreos.com_alertmanagerconfigs.yaml

Large diffs are not rendered by default.

Large diffs are not rendered by default.

396 changes: 396 additions & 0 deletions jsonnet/prometheus-operator/alertmanagerconfigs-crd.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

40 changes: 39 additions & 1 deletion pkg/alertmanager/amcfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,6 @@ func (cb *configBuilder) convertRoute(in *monitoringv1alpha1.Route, crKey types.
// convertReceiver converts a monitoringv1alpha1.Receiver to an alertmanager.receiver
func (cb *configBuilder) convertReceiver(ctx context.Context, in *monitoringv1alpha1.Receiver, crKey types.NamespacedName) (*receiver, error) {
var pagerdutyConfigs []*pagerdutyConfig

if l := len(in.PagerDutyConfigs); l > 0 {
pagerdutyConfigs = make([]*pagerdutyConfig, l)
for i := range in.PagerDutyConfigs {
Expand All @@ -513,6 +512,18 @@ func (cb *configBuilder) convertReceiver(ctx context.Context, in *monitoringv1al
}
}

var discordConfigs []*discordConfig
if l := len(in.DiscordConfigs); l > 0 {
discordConfigs = make([]*discordConfig, l)
for i := range in.DiscordConfigs {
receiver, err := cb.convertDiscordConfig(ctx, in.DiscordConfigs[i], crKey)
if err != nil {
return nil, errors.Wrapf(err, "DiscordConfig[%d]", i)
}
discordConfigs[i] = receiver
}
}

var slackConfigs []*slackConfig
if l := len(in.SlackConfigs); l > 0 {
slackConfigs = make([]*slackConfig, l)
Expand Down Expand Up @@ -625,6 +636,7 @@ func (cb *configBuilder) convertReceiver(ctx context.Context, in *monitoringv1al
Name: makeNamespacedString(in.Name, crKey),
OpsgenieConfigs: opsgenieConfigs,
PagerdutyConfigs: pagerdutyConfigs,
DiscordConfigs: discordConfigs,
SlackConfigs: slackConfigs,
WebhookConfigs: webhookConfigs,
WeChatConfigs: weChatConfigs,
Expand Down Expand Up @@ -670,6 +682,32 @@ func (cb *configBuilder) convertWebhookConfig(ctx context.Context, in monitoring
return out, nil
}

func (cb *configBuilder) convertDiscordConfig(ctx context.Context, in monitoringv1alpha1.DiscordConfig, crKey types.NamespacedName) (*discordConfig, error) {
out := &discordConfig{
VSendResolved: in.SendResolved,
Title: *in.Title,
Message: *in.Message,
}
Azanul marked this conversation as resolved.
Show resolved Hide resolved

if in.APIURL != nil {
url, err := cb.getValidURLFromSecret(ctx, crKey.Namespace, *in.APIURL)
if err != nil {
return nil, err
}
out.WebhookURL = url
}

if in.HTTPConfig != nil {
httpConfig, err := cb.convertHTTPConfig(ctx, *in.HTTPConfig, crKey)
if err != nil {
return nil, err
}
out.HTTPConfig = httpConfig
}

return out, nil
}

func (cb *configBuilder) convertSlackConfig(ctx context.Context, in monitoringv1alpha1.SlackConfig, crKey types.NamespacedName) (*slackConfig, error) {
out := &slackConfig{
VSendResolved: in.SendResolved,
Expand Down
28 changes: 28 additions & 0 deletions pkg/alertmanager/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -1189,6 +1189,12 @@ func checkReceivers(ctx context.Context, amc *monitoringv1alpha1.AlertmanagerCon
if err != nil {
return err
}

err = checkDiscordConfigs(ctx, receiver.DiscordConfigs, amc.GetNamespace(), amcKey, store, amVersion)
if err != nil {
return err
}

err = checkSlackConfigs(ctx, receiver.SlackConfigs, amc.GetNamespace(), amcKey, store, amVersion)
if err != nil {
return err
Expand Down Expand Up @@ -1309,6 +1315,28 @@ func checkOpsGenieResponder(opsgenieResponder []monitoringv1alpha1.OpsGenieConfi
return nil
}

func checkDiscordConfigs(
ctx context.Context,
configs []monitoringv1alpha1.DiscordConfig,
namespace string,
key string,
store *assets.Store,
amVersion semver.Version,
) error {
for i, config := range configs {
if err := checkHTTPConfig(config.HTTPConfig, amVersion); err != nil {
return err
}
discordConfigKey := fmt.Sprintf("%s/discord/%d", key, i)

if err := configureHTTPConfigInStore(ctx, config.HTTPConfig, namespace, discordConfigKey, store); err != nil {
return err
}
}

return nil
}

func checkSlackConfigs(
ctx context.Context,
configs []monitoringv1alpha1.SlackConfig,
Expand Down
1 change: 1 addition & 0 deletions pkg/alertmanager/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type globalConfig struct {
SMTPAuthSecret string `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,omitempty" json:"smtp_require_tls,omitempty"`
DiscordAPIURL *config.URL `yaml:"discord_api_url,omitempty" json:"discord_api_url,omitempty"`
SlackAPIURL *config.URL `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 *config.URL `yaml:"pagerduty_url,omitempty" json:"pagerduty_url,omitempty"`
Expand Down
13 changes: 13 additions & 0 deletions pkg/alertmanager/validation/v1alpha1/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ func validateReceivers(receivers []monitoringv1alpha1.Receiver) (map[string]stru
return nil, errors.Wrapf(err, "failed to validate 'opsGenieConfig' - receiver %s", receiver.Name)
}

if err := validateDiscordConfigs(receiver.DiscordConfigs); err != nil {
return nil, errors.Wrapf(err, "failed to validate 'discordConfig' - receiver %s", receiver.Name)
}

if err := validateSlackConfigs(receiver.SlackConfigs); err != nil {
return nil, errors.Wrapf(err, "failed to validate 'slackConfig' - receiver %s", receiver.Name)
}
Expand Down Expand Up @@ -137,6 +141,15 @@ func validateOpsGenieConfigs(configs []monitoringv1alpha1.OpsGenieConfig) error
return nil
}

func validateDiscordConfigs(configs []monitoringv1alpha1.DiscordConfig) error {
for _, config := range configs {
if err := config.HTTPConfig.Validate(); err != nil {
return err
}
}
return nil
}

func validateSlackConfigs(configs []monitoringv1alpha1.SlackConfig) error {
for _, config := range configs {
if err := config.Validate(); err != nil {
Expand Down
24 changes: 24 additions & 0 deletions pkg/apis/monitoring/v1alpha1/alertmanager_config_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ type Receiver struct {
OpsGenieConfigs []OpsGenieConfig `json:"opsgenieConfigs,omitempty"`
// List of PagerDuty configurations.
PagerDutyConfigs []PagerDutyConfig `json:"pagerdutyConfigs,omitempty"`
// List of Discord configurations.
Azanul marked this conversation as resolved.
Show resolved Hide resolved
// +optional
DiscordConfigs []DiscordConfig `json:"discordConfigs,omitempty"`
// List of Slack configurations.
SlackConfigs []SlackConfig `json:"slackConfigs,omitempty"`
// List of webhook configurations.
Expand Down Expand Up @@ -256,6 +259,27 @@ type PagerDutyLinkConfig struct {
Text string `json:"alt,omitempty"`
}

// DiscordConfig configures notifications via Discord.
// See https://prometheus.io/docs/alerting/latest/configuration/#discord_config
type DiscordConfig struct {
// Whether or not to notify about resolved alerts.
// +optional
SendResolved *bool `json:"sendResolved,omitempty"`
// The secret's key that contains the Discord webhook URL.
// The secret needs to be in the same namespace as the AlertmanagerConfig
// object and accessible by the Prometheus Operator.
// +optional
APIURL *v1.SecretKeySelector `json:"apiURL,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be a required field? IIUC there's no global Discord URL parameter and Alertmanager will fail to load a discord config with an empty URL.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strange, I can't seem to find discord configs in the docs, although I can see the support by reading Alertmanager's codebase.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prometheus/alertmanager#3360

It seems like it was supposed to be released but hasn't been yet.

// +optional
Azanul marked this conversation as resolved.
Show resolved Hide resolved
Title *string `json:"title,omitempty"`
// +optional
Azanul marked this conversation as resolved.
Show resolved Hide resolved
Message *string `json:"message,omitempty"`

// HTTP client configuration.
// +optional
HTTPConfig *HTTPConfig `json:"httpConfig,omitempty"`
}

// SlackConfig configures notifications via Slack.
// See https://prometheus.io/docs/alerting/latest/configuration/#slack_config
type SlackConfig struct {
Expand Down
47 changes: 47 additions & 0 deletions pkg/apis/monitoring/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions pkg/apis/monitoring/v1beta1/alertmanager_config_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (

monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"

v1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand Down Expand Up @@ -157,6 +158,8 @@ type Receiver struct {
// List of PagerDuty configurations.
PagerDutyConfigs []PagerDutyConfig `json:"pagerdutyConfigs,omitempty"`
// List of Slack configurations.
DiscordConfigs []DiscordConfig `json:"discordConfigs,omitempty"`
// List of Slack configurations.
SlackConfigs []SlackConfig `json:"slackConfigs,omitempty"`
// List of webhook configurations.
WebhookConfigs []WebhookConfig `json:"webhookConfigs,omitempty"`
Expand Down Expand Up @@ -254,6 +257,27 @@ type PagerDutyLinkConfig struct {
Text string `json:"alt,omitempty"`
}

// DiscordConfig configures notifications via Discord.
// See https://prometheus.io/docs/alerting/latest/configuration/#discord_config
type DiscordConfig struct {
// Whether or not to notify about resolved alerts.
// +optional
SendResolved *bool `json:"sendResolved,omitempty"`
// The secret's key that contains the Discord webhook URL.
// The secret needs to be in the same namespace as the AlertmanagerConfig
// object and accessible by the Prometheus Operator.
// +optional
APIURL *v1.SecretKeySelector `json:"apiURL,omitempty"`
// +optional
Title *string `json:"title,omitempty"`
// +optional
Message *string `json:"message,omitempty"`

// HTTP client configuration.
// +optional
HTTPConfig *HTTPConfig `json:"httpConfig,omitempty"`
}

// SlackConfig configures notifications via Slack.
// See https://prometheus.io/docs/alerting/latest/configuration/#slack_config
type SlackConfig struct {
Expand Down
17 changes: 17 additions & 0 deletions pkg/apis/monitoring/v1beta1/conversion_from.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,16 @@ func convertPagerDutyConfigFrom(in v1alpha1.PagerDutyConfig) PagerDutyConfig {
}
}

func convertDiscordConfigFrom(in v1alpha1.DiscordConfig) DiscordConfig {
return DiscordConfig{
APIURL: in.APIURL,
HTTPConfig: convertHTTPConfigFrom(in.HTTPConfig),
Title: in.Title,
Message: in.Message,
SendResolved: in.SendResolved,
}
}

func convertSlackFieldsFrom(in []v1alpha1.SlackField) []SlackField {
out := make([]SlackField, len(in))

Expand Down Expand Up @@ -454,6 +464,13 @@ func (dst *AlertmanagerConfig) ConvertFrom(srcRaw conversion.Hub) error {
)
}

for _, in := range in.DiscordConfigs {
out.DiscordConfigs = append(
out.DiscordConfigs,
convertDiscordConfigFrom(in),
)
}

for _, in := range in.SlackConfigs {
out.SlackConfigs = append(
out.SlackConfigs,
Expand Down
17 changes: 17 additions & 0 deletions pkg/apis/monitoring/v1beta1/conversion_to.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,16 @@ func convertPagerDutyConfigTo(in PagerDutyConfig) v1alpha1.PagerDutyConfig {
}
}

func convertDiscordConfigTo(in DiscordConfig) v1alpha1.DiscordConfig {
return v1alpha1.DiscordConfig{
APIURL: in.APIURL,
HTTPConfig: convertHTTPConfigTo(in.HTTPConfig),
Title: in.Title,
Message: in.Message,
SendResolved: in.SendResolved,
}
}

func convertSlackFieldsTo(in []SlackField) []v1alpha1.SlackField {
out := make([]v1alpha1.SlackField, len(in))

Expand Down Expand Up @@ -450,6 +460,13 @@ func (src *AlertmanagerConfig) ConvertTo(dstRaw conversion.Hub) error {
)
}

for _, in := range in.DiscordConfigs {
out.DiscordConfigs = append(
out.DiscordConfigs,
convertDiscordConfigTo(in),
)
}

for _, in := range in.SlackConfigs {
out.SlackConfigs = append(
out.SlackConfigs,
Expand Down