From 1f9a7b6182de5b7d46fbe0e11e22810f3a341072 Mon Sep 17 00:00:00 2001 From: Alex Lardschneider Date: Mon, 14 May 2018 17:26:11 +0200 Subject: [PATCH] [Request] Add Slack actions to notifications (#1355) * Added slack actions to notifications Signed-off-by: Alex Lardschneider --- config/notifiers.go | 51 +++++++++++++++++++++++++++++++++++---------- notify/impl.go | 29 +++++++++++++++++++------- 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/config/notifiers.go b/config/notifiers.go index 44e35d87..f7f627c8 100644 --- a/config/notifiers.go +++ b/config/notifiers.go @@ -220,6 +220,34 @@ func (c *PagerdutyConfig) UnmarshalYAML(unmarshal func(interface{}) error) error return nil } +// SlackAction configures a single Slack action that is sent with each notification. +// Each action must contain a type, text, and url. +// See https://api.slack.com/docs/message-attachments#action_fields for more information. +type SlackAction struct { + Type string `yaml:"type,omitempty" json:"type,omitempty"` + Text string `yaml:"text,omitempty" json:"text,omitempty"` + URL string `yaml:"url,omitempty" json:"url,omitempty"` + Style string `yaml:"style,omitempty" json:"style,omitempty"` +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface for SlackAction. +func (c *SlackAction) UnmarshalYAML(unmarshal func(interface{}) error) error { + type plain SlackAction + if err := unmarshal((*plain)(c)); err != nil { + return err + } + if c.Type == "" { + return fmt.Errorf("missing type in Slack action configuration") + } + if c.Text == "" { + return fmt.Errorf("missing value in Slack text configuration") + } + if c.URL == "" { + return fmt.Errorf("missing value in Slack url configuration") + } + return nil +} + // SlackField configures a single Slack field that is sent with each notification. // Each field must contain a title, value, and optionally, a boolean value to indicate if the field // is short enough to be displayed next to other fields designated as short. @@ -258,17 +286,18 @@ type SlackConfig struct { Username string `yaml:"username,omitempty" json:"username,omitempty"` Color string `yaml:"color,omitempty" json:"color,omitempty"` - Title string `yaml:"title,omitempty" json:"title,omitempty"` - TitleLink string `yaml:"title_link,omitempty" json:"title_link,omitempty"` - Pretext string `yaml:"pretext,omitempty" json:"pretext,omitempty"` - Text string `yaml:"text,omitempty" json:"text,omitempty"` - Fields []*SlackField `yaml:"fields,omitempty" json:"fields,omitempty"` - ShortFields bool `yaml:"short_fields,omitempty" json:"short_fields,omitempty"` - Footer string `yaml:"footer,omitempty" json:"footer,omitempty"` - Fallback string `yaml:"fallback,omitempty" json:"fallback,omitempty"` - IconEmoji string `yaml:"icon_emoji,omitempty" json:"icon_emoji,omitempty"` - IconURL string `yaml:"icon_url,omitempty" json:"icon_url,omitempty"` - LinkNames bool `yaml:"link_names,omitempty" json:"link_names,omitempty"` + Title string `yaml:"title,omitempty" json:"title,omitempty"` + TitleLink string `yaml:"title_link,omitempty" json:"title_link,omitempty"` + Pretext string `yaml:"pretext,omitempty" json:"pretext,omitempty"` + Text string `yaml:"text,omitempty" json:"text,omitempty"` + Fields []*SlackField `yaml:"fields,omitempty" json:"fields,omitempty"` + ShortFields bool `yaml:"short_fields,omitempty" json:"short_fields,omitempty"` + Footer string `yaml:"footer,omitempty" json:"footer,omitempty"` + Fallback string `yaml:"fallback,omitempty" json:"fallback,omitempty"` + IconEmoji string `yaml:"icon_emoji,omitempty" json:"icon_emoji,omitempty"` + IconURL string `yaml:"icon_url,omitempty" json:"icon_url,omitempty"` + LinkNames bool `yaml:"link_names,omitempty" json:"link_names,omitempty"` + Actions []*SlackAction `yaml:"actions,omitempty" json:"actions,omitempty"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. diff --git a/notify/impl.go b/notify/impl.go index a5d212f6..0a29814b 100644 --- a/notify/impl.go +++ b/notify/impl.go @@ -638,13 +638,14 @@ type slackReq struct { // slackAttachment is used to display a richly-formatted message block. type slackAttachment struct { - Title string `json:"title,omitempty"` - TitleLink string `json:"title_link,omitempty"` - Pretext string `json:"pretext,omitempty"` - Text string `json:"text"` - Fallback string `json:"fallback"` - Fields []config.SlackField `json:"fields,omitempty"` - Footer string `json:"footer"` + Title string `json:"title,omitempty"` + TitleLink string `json:"title_link,omitempty"` + Pretext string `json:"pretext,omitempty"` + Text string `json:"text"` + Fallback string `json:"fallback"` + Fields []config.SlackField `json:"fields,omitempty"` + Actions []config.SlackAction `json:"actions,omitempty"` + Footer string `json:"footer"` Color string `json:"color,omitempty"` MrkdwnIn []string `json:"mrkdwn_in,omitempty"` @@ -691,6 +692,20 @@ func (n *Slack) Notify(ctx context.Context, as ...*types.Alert) (bool, error) { attachment.Fields = fields } + var numActions = len(n.conf.Actions) + if numActions > 0 { + var actions = make([]config.SlackAction, numActions) + for index, action := range n.conf.Actions { + actions[index] = config.SlackAction{ + Type: tmplText(action.Type), + Text: tmplText(action.Text), + URL: tmplText(action.URL), + Style: tmplText(action.Style), + } + } + attachment.Actions = actions + } + req := &slackReq{ Channel: tmplText(n.conf.Channel), Username: tmplText(n.conf.Username),