...

Source file src/github.com/prometheus/alertmanager/config/notifiers.go

Documentation: github.com/prometheus/alertmanager/config

     1  // Copyright 2015 Prometheus Team
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package config
    15  
    16  import (
    17  	"fmt"
    18  	"net/textproto"
    19  	"regexp"
    20  	"strings"
    21  	"text/template"
    22  	"time"
    23  
    24  	"github.com/pkg/errors"
    25  	commoncfg "github.com/prometheus/common/config"
    26  	"github.com/prometheus/common/sigv4"
    27  )
    28  
    29  var (
    30  	// DefaultWebhookConfig defines default values for Webhook configurations.
    31  	DefaultWebhookConfig = WebhookConfig{
    32  		NotifierConfig: NotifierConfig{
    33  			VSendResolved: true,
    34  		},
    35  	}
    36  
    37  	// DefaultWebexConfig defines default values for Webex configurations.
    38  	DefaultWebexConfig = WebexConfig{
    39  		NotifierConfig: NotifierConfig{
    40  			VSendResolved: true,
    41  		},
    42  		Message: `{{ template "webex.default.message" . }}`,
    43  	}
    44  
    45  	// DefaultDiscordConfig defines default values for Discord configurations.
    46  	DefaultDiscordConfig = DiscordConfig{
    47  		NotifierConfig: NotifierConfig{
    48  			VSendResolved: true,
    49  		},
    50  		Title:   `{{ template "discord.default.title" . }}`,
    51  		Message: `{{ template "discord.default.message" . }}`,
    52  	}
    53  
    54  	// DefaultEmailConfig defines default values for Email configurations.
    55  	DefaultEmailConfig = EmailConfig{
    56  		NotifierConfig: NotifierConfig{
    57  			VSendResolved: false,
    58  		},
    59  		HTML: `{{ template "email.default.html" . }}`,
    60  		Text: ``,
    61  	}
    62  
    63  	// DefaultEmailSubject defines the default Subject header of an Email.
    64  	DefaultEmailSubject = `{{ template "email.default.subject" . }}`
    65  
    66  	// DefaultPagerdutyDetails defines the default values for PagerDuty details.
    67  	DefaultPagerdutyDetails = map[string]string{
    68  		"firing":       `{{ template "pagerduty.default.instances" .Alerts.Firing }}`,
    69  		"resolved":     `{{ template "pagerduty.default.instances" .Alerts.Resolved }}`,
    70  		"num_firing":   `{{ .Alerts.Firing | len }}`,
    71  		"num_resolved": `{{ .Alerts.Resolved | len }}`,
    72  	}
    73  
    74  	// DefaultPagerdutyConfig defines default values for PagerDuty configurations.
    75  	DefaultPagerdutyConfig = PagerdutyConfig{
    76  		NotifierConfig: NotifierConfig{
    77  			VSendResolved: true,
    78  		},
    79  		Description: `{{ template "pagerduty.default.description" .}}`,
    80  		Client:      `{{ template "pagerduty.default.client" . }}`,
    81  		ClientURL:   `{{ template "pagerduty.default.clientURL" . }}`,
    82  	}
    83  
    84  	// DefaultSlackConfig defines default values for Slack configurations.
    85  	DefaultSlackConfig = SlackConfig{
    86  		NotifierConfig: NotifierConfig{
    87  			VSendResolved: false,
    88  		},
    89  		Color:      `{{ if eq .Status "firing" }}danger{{ else }}good{{ end }}`,
    90  		Username:   `{{ template "slack.default.username" . }}`,
    91  		Title:      `{{ template "slack.default.title" . }}`,
    92  		TitleLink:  `{{ template "slack.default.titlelink" . }}`,
    93  		IconEmoji:  `{{ template "slack.default.iconemoji" . }}`,
    94  		IconURL:    `{{ template "slack.default.iconurl" . }}`,
    95  		Pretext:    `{{ template "slack.default.pretext" . }}`,
    96  		Text:       `{{ template "slack.default.text" . }}`,
    97  		Fallback:   `{{ template "slack.default.fallback" . }}`,
    98  		CallbackID: `{{ template "slack.default.callbackid" . }}`,
    99  		Footer:     `{{ template "slack.default.footer" . }}`,
   100  	}
   101  
   102  	// DefaultOpsGenieConfig defines default values for OpsGenie configurations.
   103  	DefaultOpsGenieConfig = OpsGenieConfig{
   104  		NotifierConfig: NotifierConfig{
   105  			VSendResolved: true,
   106  		},
   107  		Message:     `{{ template "opsgenie.default.message" . }}`,
   108  		Description: `{{ template "opsgenie.default.description" . }}`,
   109  		Source:      `{{ template "opsgenie.default.source" . }}`,
   110  		// TODO: Add a details field with all the alerts.
   111  	}
   112  
   113  	// DefaultWechatConfig defines default values for wechat configurations.
   114  	DefaultWechatConfig = WechatConfig{
   115  		NotifierConfig: NotifierConfig{
   116  			VSendResolved: false,
   117  		},
   118  		Message: `{{ template "wechat.default.message" . }}`,
   119  		ToUser:  `{{ template "wechat.default.to_user" . }}`,
   120  		ToParty: `{{ template "wechat.default.to_party" . }}`,
   121  		ToTag:   `{{ template "wechat.default.to_tag" . }}`,
   122  		AgentID: `{{ template "wechat.default.agent_id" . }}`,
   123  	}
   124  
   125  	// DefaultVictorOpsConfig defines default values for VictorOps configurations.
   126  	DefaultVictorOpsConfig = VictorOpsConfig{
   127  		NotifierConfig: NotifierConfig{
   128  			VSendResolved: true,
   129  		},
   130  		MessageType:       `CRITICAL`,
   131  		StateMessage:      `{{ template "victorops.default.state_message" . }}`,
   132  		EntityDisplayName: `{{ template "victorops.default.entity_display_name" . }}`,
   133  		MonitoringTool:    `{{ template "victorops.default.monitoring_tool" . }}`,
   134  	}
   135  
   136  	// DefaultPushoverConfig defines default values for Pushover configurations.
   137  	DefaultPushoverConfig = PushoverConfig{
   138  		NotifierConfig: NotifierConfig{
   139  			VSendResolved: true,
   140  		},
   141  		Title:    `{{ template "pushover.default.title" . }}`,
   142  		Message:  `{{ template "pushover.default.message" . }}`,
   143  		URL:      `{{ template "pushover.default.url" . }}`,
   144  		Priority: `{{ if eq .Status "firing" }}2{{ else }}0{{ end }}`, // emergency (firing) or normal
   145  		Retry:    duration(1 * time.Minute),
   146  		Expire:   duration(1 * time.Hour),
   147  		HTML:     false,
   148  	}
   149  
   150  	// DefaultSNSConfig defines default values for SNS configurations.
   151  	DefaultSNSConfig = SNSConfig{
   152  		NotifierConfig: NotifierConfig{
   153  			VSendResolved: true,
   154  		},
   155  		Subject: `{{ template "sns.default.subject" . }}`,
   156  		Message: `{{ template "sns.default.message" . }}`,
   157  	}
   158  
   159  	DefaultTelegramConfig = TelegramConfig{
   160  		NotifierConfig: NotifierConfig{
   161  			VSendResolved: true,
   162  		},
   163  		DisableNotifications: false,
   164  		Message:              `{{ template "telegram.default.message" . }}`,
   165  		ParseMode:            "HTML",
   166  	}
   167  )
   168  
   169  // NotifierConfig contains base options common across all notifier configurations.
   170  type NotifierConfig struct {
   171  	VSendResolved bool `yaml:"send_resolved" json:"send_resolved"`
   172  }
   173  
   174  func (nc *NotifierConfig) SendResolved() bool {
   175  	return nc.VSendResolved
   176  }
   177  
   178  // WebexConfig configures notifications via Webex.
   179  type WebexConfig struct {
   180  	NotifierConfig `yaml:",inline" json:",inline"`
   181  	HTTPConfig     *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
   182  	APIURL         *URL                        `yaml:"api_url,omitempty" json:"api_url,omitempty"`
   183  
   184  	Message string `yaml:"message,omitempty" json:"message,omitempty"`
   185  	RoomID  string `yaml:"room_id" json:"room_id"`
   186  }
   187  
   188  // UnmarshalYAML implements the yaml.Unmarshaler interface.
   189  func (c *WebexConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
   190  	*c = DefaultWebexConfig
   191  	type plain WebexConfig
   192  	if err := unmarshal((*plain)(c)); err != nil {
   193  		return err
   194  	}
   195  
   196  	if c.RoomID == "" {
   197  		return fmt.Errorf("missing room_id on webex_config")
   198  	}
   199  
   200  	if c.HTTPConfig == nil || c.HTTPConfig.Authorization == nil {
   201  		return fmt.Errorf("missing webex_configs.http_config.authorization")
   202  	}
   203  
   204  	return nil
   205  }
   206  
   207  // DiscordConfig configures notifications via Discord.
   208  type DiscordConfig struct {
   209  	NotifierConfig `yaml:",inline" json:",inline"`
   210  
   211  	HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
   212  	WebhookURL *SecretURL                  `yaml:"webhook_url,omitempty" json:"webhook_url,omitempty"`
   213  
   214  	Title   string `yaml:"title,omitempty" json:"title,omitempty"`
   215  	Message string `yaml:"message,omitempty" json:"message,omitempty"`
   216  }
   217  
   218  // UnmarshalYAML implements the yaml.Unmarshaler interface.
   219  func (c *DiscordConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
   220  	*c = DefaultDiscordConfig
   221  	type plain DiscordConfig
   222  	return unmarshal((*plain)(c))
   223  }
   224  
   225  // EmailConfig configures notifications via mail.
   226  type EmailConfig struct {
   227  	NotifierConfig `yaml:",inline" json:",inline"`
   228  
   229  	// Email address to notify.
   230  	To               string              `yaml:"to,omitempty" json:"to,omitempty"`
   231  	From             string              `yaml:"from,omitempty" json:"from,omitempty"`
   232  	Hello            string              `yaml:"hello,omitempty" json:"hello,omitempty"`
   233  	Smarthost        HostPort            `yaml:"smarthost,omitempty" json:"smarthost,omitempty"`
   234  	AuthUsername     string              `yaml:"auth_username,omitempty" json:"auth_username,omitempty"`
   235  	AuthPassword     Secret              `yaml:"auth_password,omitempty" json:"auth_password,omitempty"`
   236  	AuthPasswordFile string              `yaml:"auth_password_file,omitempty" json:"auth_password_file,omitempty"`
   237  	AuthSecret       Secret              `yaml:"auth_secret,omitempty" json:"auth_secret,omitempty"`
   238  	AuthIdentity     string              `yaml:"auth_identity,omitempty" json:"auth_identity,omitempty"`
   239  	Headers          map[string]string   `yaml:"headers,omitempty" json:"headers,omitempty"`
   240  	HTML             string              `yaml:"html,omitempty" json:"html,omitempty"`
   241  	Text             string              `yaml:"text,omitempty" json:"text,omitempty"`
   242  	RequireTLS       *bool               `yaml:"require_tls,omitempty" json:"require_tls,omitempty"`
   243  	TLSConfig        commoncfg.TLSConfig `yaml:"tls_config,omitempty" json:"tls_config,omitempty"`
   244  }
   245  
   246  // UnmarshalYAML implements the yaml.Unmarshaler interface.
   247  func (c *EmailConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
   248  	*c = DefaultEmailConfig
   249  	type plain EmailConfig
   250  	if err := unmarshal((*plain)(c)); err != nil {
   251  		return err
   252  	}
   253  	if c.To == "" {
   254  		return fmt.Errorf("missing to address in email config")
   255  	}
   256  	// Header names are case-insensitive, check for collisions.
   257  	normalizedHeaders := map[string]string{}
   258  	for h, v := range c.Headers {
   259  		normalized := textproto.CanonicalMIMEHeaderKey(h)
   260  		if _, ok := normalizedHeaders[normalized]; ok {
   261  			return fmt.Errorf("duplicate header %q in email config", normalized)
   262  		}
   263  		normalizedHeaders[normalized] = v
   264  	}
   265  	c.Headers = normalizedHeaders
   266  
   267  	return nil
   268  }
   269  
   270  // PagerdutyConfig configures notifications via PagerDuty.
   271  type PagerdutyConfig struct {
   272  	NotifierConfig `yaml:",inline" json:",inline"`
   273  
   274  	HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
   275  
   276  	ServiceKey     Secret            `yaml:"service_key,omitempty" json:"service_key,omitempty"`
   277  	ServiceKeyFile string            `yaml:"service_key_file,omitempty" json:"service_key_file,omitempty"`
   278  	RoutingKey     Secret            `yaml:"routing_key,omitempty" json:"routing_key,omitempty"`
   279  	RoutingKeyFile string            `yaml:"routing_key_file,omitempty" json:"routing_key_file,omitempty"`
   280  	URL            *URL              `yaml:"url,omitempty" json:"url,omitempty"`
   281  	Client         string            `yaml:"client,omitempty" json:"client,omitempty"`
   282  	ClientURL      string            `yaml:"client_url,omitempty" json:"client_url,omitempty"`
   283  	Description    string            `yaml:"description,omitempty" json:"description,omitempty"`
   284  	Details        map[string]string `yaml:"details,omitempty" json:"details,omitempty"`
   285  	Images         []PagerdutyImage  `yaml:"images,omitempty" json:"images,omitempty"`
   286  	Links          []PagerdutyLink   `yaml:"links,omitempty" json:"links,omitempty"`
   287  	Source         string            `yaml:"source,omitempty" json:"source,omitempty"`
   288  	Severity       string            `yaml:"severity,omitempty" json:"severity,omitempty"`
   289  	Class          string            `yaml:"class,omitempty" json:"class,omitempty"`
   290  	Component      string            `yaml:"component,omitempty" json:"component,omitempty"`
   291  	Group          string            `yaml:"group,omitempty" json:"group,omitempty"`
   292  }
   293  
   294  // PagerdutyLink is a link
   295  type PagerdutyLink struct {
   296  	Href string `yaml:"href,omitempty" json:"href,omitempty"`
   297  	Text string `yaml:"text,omitempty" json:"text,omitempty"`
   298  }
   299  
   300  // PagerdutyImage is an image
   301  type PagerdutyImage struct {
   302  	Src  string `yaml:"src,omitempty" json:"src,omitempty"`
   303  	Alt  string `yaml:"alt,omitempty" json:"alt,omitempty"`
   304  	Href string `yaml:"href,omitempty" json:"href,omitempty"`
   305  }
   306  
   307  // UnmarshalYAML implements the yaml.Unmarshaler interface.
   308  func (c *PagerdutyConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
   309  	*c = DefaultPagerdutyConfig
   310  	type plain PagerdutyConfig
   311  	if err := unmarshal((*plain)(c)); err != nil {
   312  		return err
   313  	}
   314  	if c.RoutingKey == "" && c.ServiceKey == "" && c.RoutingKeyFile == "" && c.ServiceKeyFile == "" {
   315  		return fmt.Errorf("missing service or routing key in PagerDuty config")
   316  	}
   317  	if len(c.RoutingKey) > 0 && len(c.RoutingKeyFile) > 0 {
   318  		return fmt.Errorf("at most one of routing_key & routing_key_file must be configured")
   319  	}
   320  	if len(c.ServiceKey) > 0 && len(c.ServiceKeyFile) > 0 {
   321  		return fmt.Errorf("at most one of service_key & service_key_file must be configured")
   322  	}
   323  	if c.Details == nil {
   324  		c.Details = make(map[string]string)
   325  	}
   326  	if c.Source == "" {
   327  		c.Source = c.Client
   328  	}
   329  	for k, v := range DefaultPagerdutyDetails {
   330  		if _, ok := c.Details[k]; !ok {
   331  			c.Details[k] = v
   332  		}
   333  	}
   334  	return nil
   335  }
   336  
   337  // SlackAction configures a single Slack action that is sent with each notification.
   338  // See https://api.slack.com/docs/message-attachments#action_fields and https://api.slack.com/docs/message-buttons
   339  // for more information.
   340  type SlackAction struct {
   341  	Type         string                  `yaml:"type,omitempty"  json:"type,omitempty"`
   342  	Text         string                  `yaml:"text,omitempty"  json:"text,omitempty"`
   343  	URL          string                  `yaml:"url,omitempty"   json:"url,omitempty"`
   344  	Style        string                  `yaml:"style,omitempty" json:"style,omitempty"`
   345  	Name         string                  `yaml:"name,omitempty"  json:"name,omitempty"`
   346  	Value        string                  `yaml:"value,omitempty"  json:"value,omitempty"`
   347  	ConfirmField *SlackConfirmationField `yaml:"confirm,omitempty"  json:"confirm,omitempty"`
   348  }
   349  
   350  // UnmarshalYAML implements the yaml.Unmarshaler interface for SlackAction.
   351  func (c *SlackAction) UnmarshalYAML(unmarshal func(interface{}) error) error {
   352  	type plain SlackAction
   353  	if err := unmarshal((*plain)(c)); err != nil {
   354  		return err
   355  	}
   356  	if c.Type == "" {
   357  		return fmt.Errorf("missing type in Slack action configuration")
   358  	}
   359  	if c.Text == "" {
   360  		return fmt.Errorf("missing text in Slack action configuration")
   361  	}
   362  	if c.URL != "" {
   363  		// Clear all message action fields.
   364  		c.Name = ""
   365  		c.Value = ""
   366  		c.ConfirmField = nil
   367  	} else if c.Name != "" {
   368  		c.URL = ""
   369  	} else {
   370  		return fmt.Errorf("missing name or url in Slack action configuration")
   371  	}
   372  	return nil
   373  }
   374  
   375  // SlackConfirmationField protect users from destructive actions or particularly distinguished decisions
   376  // by asking them to confirm their button click one more time.
   377  // See https://api.slack.com/docs/interactive-message-field-guide#confirmation_fields for more information.
   378  type SlackConfirmationField struct {
   379  	Text        string `yaml:"text,omitempty"  json:"text,omitempty"`
   380  	Title       string `yaml:"title,omitempty"  json:"title,omitempty"`
   381  	OkText      string `yaml:"ok_text,omitempty"  json:"ok_text,omitempty"`
   382  	DismissText string `yaml:"dismiss_text,omitempty"  json:"dismiss_text,omitempty"`
   383  }
   384  
   385  // UnmarshalYAML implements the yaml.Unmarshaler interface for SlackConfirmationField.
   386  func (c *SlackConfirmationField) UnmarshalYAML(unmarshal func(interface{}) error) error {
   387  	type plain SlackConfirmationField
   388  	if err := unmarshal((*plain)(c)); err != nil {
   389  		return err
   390  	}
   391  	if c.Text == "" {
   392  		return fmt.Errorf("missing text in Slack confirmation configuration")
   393  	}
   394  	return nil
   395  }
   396  
   397  // SlackField configures a single Slack field that is sent with each notification.
   398  // Each field must contain a title, value, and optionally, a boolean value to indicate if the field
   399  // is short enough to be displayed next to other fields designated as short.
   400  // See https://api.slack.com/docs/message-attachments#fields for more information.
   401  type SlackField struct {
   402  	Title string `yaml:"title,omitempty" json:"title,omitempty"`
   403  	Value string `yaml:"value,omitempty" json:"value,omitempty"`
   404  	Short *bool  `yaml:"short,omitempty" json:"short,omitempty"`
   405  }
   406  
   407  // UnmarshalYAML implements the yaml.Unmarshaler interface for SlackField.
   408  func (c *SlackField) UnmarshalYAML(unmarshal func(interface{}) error) error {
   409  	type plain SlackField
   410  	if err := unmarshal((*plain)(c)); err != nil {
   411  		return err
   412  	}
   413  	if c.Title == "" {
   414  		return fmt.Errorf("missing title in Slack field configuration")
   415  	}
   416  	if c.Value == "" {
   417  		return fmt.Errorf("missing value in Slack field configuration")
   418  	}
   419  	return nil
   420  }
   421  
   422  // SlackConfig configures notifications via Slack.
   423  type SlackConfig struct {
   424  	NotifierConfig `yaml:",inline" json:",inline"`
   425  
   426  	HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
   427  
   428  	APIURL     *SecretURL `yaml:"api_url,omitempty" json:"api_url,omitempty"`
   429  	APIURLFile string     `yaml:"api_url_file,omitempty" json:"api_url_file,omitempty"`
   430  
   431  	// Slack channel override, (like #other-channel or @username).
   432  	Channel  string `yaml:"channel,omitempty" json:"channel,omitempty"`
   433  	Username string `yaml:"username,omitempty" json:"username,omitempty"`
   434  	Color    string `yaml:"color,omitempty" json:"color,omitempty"`
   435  
   436  	Title       string         `yaml:"title,omitempty" json:"title,omitempty"`
   437  	TitleLink   string         `yaml:"title_link,omitempty" json:"title_link,omitempty"`
   438  	Pretext     string         `yaml:"pretext,omitempty" json:"pretext,omitempty"`
   439  	Text        string         `yaml:"text,omitempty" json:"text,omitempty"`
   440  	Fields      []*SlackField  `yaml:"fields,omitempty" json:"fields,omitempty"`
   441  	ShortFields bool           `yaml:"short_fields" json:"short_fields,omitempty"`
   442  	Footer      string         `yaml:"footer,omitempty" json:"footer,omitempty"`
   443  	Fallback    string         `yaml:"fallback,omitempty" json:"fallback,omitempty"`
   444  	CallbackID  string         `yaml:"callback_id,omitempty" json:"callback_id,omitempty"`
   445  	IconEmoji   string         `yaml:"icon_emoji,omitempty" json:"icon_emoji,omitempty"`
   446  	IconURL     string         `yaml:"icon_url,omitempty" json:"icon_url,omitempty"`
   447  	ImageURL    string         `yaml:"image_url,omitempty" json:"image_url,omitempty"`
   448  	ThumbURL    string         `yaml:"thumb_url,omitempty" json:"thumb_url,omitempty"`
   449  	LinkNames   bool           `yaml:"link_names" json:"link_names,omitempty"`
   450  	MrkdwnIn    []string       `yaml:"mrkdwn_in,omitempty" json:"mrkdwn_in,omitempty"`
   451  	Actions     []*SlackAction `yaml:"actions,omitempty" json:"actions,omitempty"`
   452  }
   453  
   454  // UnmarshalYAML implements the yaml.Unmarshaler interface.
   455  func (c *SlackConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
   456  	*c = DefaultSlackConfig
   457  	type plain SlackConfig
   458  	if err := unmarshal((*plain)(c)); err != nil {
   459  		return err
   460  	}
   461  
   462  	if c.APIURL != nil && len(c.APIURLFile) > 0 {
   463  		return fmt.Errorf("at most one of api_url & api_url_file must be configured")
   464  	}
   465  
   466  	return nil
   467  }
   468  
   469  // WebhookConfig configures notifications via a generic webhook.
   470  type WebhookConfig struct {
   471  	NotifierConfig `yaml:",inline" json:",inline"`
   472  
   473  	HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
   474  
   475  	// URL to send POST request to.
   476  	URL *URL `yaml:"url" json:"url"`
   477  	// MaxAlerts is the maximum number of alerts to be sent per webhook message.
   478  	// Alerts exceeding this threshold will be truncated. Setting this to 0
   479  	// allows an unlimited number of alerts.
   480  	MaxAlerts uint64 `yaml:"max_alerts" json:"max_alerts"`
   481  }
   482  
   483  // UnmarshalYAML implements the yaml.Unmarshaler interface.
   484  func (c *WebhookConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
   485  	*c = DefaultWebhookConfig
   486  	type plain WebhookConfig
   487  	if err := unmarshal((*plain)(c)); err != nil {
   488  		return err
   489  	}
   490  	if c.URL == nil {
   491  		return fmt.Errorf("missing URL in webhook config")
   492  	}
   493  	if c.URL.Scheme != "https" && c.URL.Scheme != "http" {
   494  		return fmt.Errorf("scheme required for webhook url")
   495  	}
   496  	return nil
   497  }
   498  
   499  // WechatConfig configures notifications via Wechat.
   500  type WechatConfig struct {
   501  	NotifierConfig `yaml:",inline" json:",inline"`
   502  
   503  	HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
   504  
   505  	APISecret   Secret `yaml:"api_secret,omitempty" json:"api_secret,omitempty"`
   506  	CorpID      string `yaml:"corp_id,omitempty" json:"corp_id,omitempty"`
   507  	Message     string `yaml:"message,omitempty" json:"message,omitempty"`
   508  	APIURL      *URL   `yaml:"api_url,omitempty" json:"api_url,omitempty"`
   509  	ToUser      string `yaml:"to_user,omitempty" json:"to_user,omitempty"`
   510  	ToParty     string `yaml:"to_party,omitempty" json:"to_party,omitempty"`
   511  	ToTag       string `yaml:"to_tag,omitempty" json:"to_tag,omitempty"`
   512  	AgentID     string `yaml:"agent_id,omitempty" json:"agent_id,omitempty"`
   513  	MessageType string `yaml:"message_type,omitempty" json:"message_type,omitempty"`
   514  }
   515  
   516  const wechatValidTypesRe = `^(text|markdown)$`
   517  
   518  var wechatTypeMatcher = regexp.MustCompile(wechatValidTypesRe)
   519  
   520  // UnmarshalYAML implements the yaml.Unmarshaler interface.
   521  func (c *WechatConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
   522  	*c = DefaultWechatConfig
   523  	type plain WechatConfig
   524  	if err := unmarshal((*plain)(c)); err != nil {
   525  		return err
   526  	}
   527  
   528  	if c.MessageType == "" {
   529  		c.MessageType = "text"
   530  	}
   531  
   532  	if !wechatTypeMatcher.MatchString(c.MessageType) {
   533  		return errors.Errorf("weChat message type %q does not match valid options %s", c.MessageType, wechatValidTypesRe)
   534  	}
   535  
   536  	return nil
   537  }
   538  
   539  // OpsGenieConfig configures notifications via OpsGenie.
   540  type OpsGenieConfig struct {
   541  	NotifierConfig `yaml:",inline" json:",inline"`
   542  
   543  	HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
   544  
   545  	APIKey       Secret                    `yaml:"api_key,omitempty" json:"api_key,omitempty"`
   546  	APIKeyFile   string                    `yaml:"api_key_file,omitempty" json:"api_key_file,omitempty"`
   547  	APIURL       *URL                      `yaml:"api_url,omitempty" json:"api_url,omitempty"`
   548  	Message      string                    `yaml:"message,omitempty" json:"message,omitempty"`
   549  	Description  string                    `yaml:"description,omitempty" json:"description,omitempty"`
   550  	Source       string                    `yaml:"source,omitempty" json:"source,omitempty"`
   551  	Details      map[string]string         `yaml:"details,omitempty" json:"details,omitempty"`
   552  	Entity       string                    `yaml:"entity,omitempty" json:"entity,omitempty"`
   553  	Responders   []OpsGenieConfigResponder `yaml:"responders,omitempty" json:"responders,omitempty"`
   554  	Actions      string                    `yaml:"actions,omitempty" json:"actions,omitempty"`
   555  	Tags         string                    `yaml:"tags,omitempty" json:"tags,omitempty"`
   556  	Note         string                    `yaml:"note,omitempty" json:"note,omitempty"`
   557  	Priority     string                    `yaml:"priority,omitempty" json:"priority,omitempty"`
   558  	UpdateAlerts bool                      `yaml:"update_alerts,omitempty" json:"update_alerts,omitempty"`
   559  }
   560  
   561  const opsgenieValidTypesRe = `^(team|teams|user|escalation|schedule)$`
   562  
   563  var opsgenieTypeMatcher = regexp.MustCompile(opsgenieValidTypesRe)
   564  
   565  // UnmarshalYAML implements the yaml.Unmarshaler interface.
   566  func (c *OpsGenieConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
   567  	*c = DefaultOpsGenieConfig
   568  	type plain OpsGenieConfig
   569  	if err := unmarshal((*plain)(c)); err != nil {
   570  		return err
   571  	}
   572  
   573  	if c.APIKey != "" && len(c.APIKeyFile) > 0 {
   574  		return fmt.Errorf("at most one of api_key & api_key_file must be configured")
   575  	}
   576  
   577  	for _, r := range c.Responders {
   578  		if r.ID == "" && r.Username == "" && r.Name == "" {
   579  			return errors.Errorf("opsGenieConfig responder %v has to have at least one of id, username or name specified", r)
   580  		}
   581  
   582  		if strings.Contains(r.Type, "{{") {
   583  			_, err := template.New("").Parse(r.Type)
   584  			if err != nil {
   585  				return errors.Errorf("opsGenieConfig responder %v type is not a valid template: %v", r, err)
   586  			}
   587  		} else {
   588  			r.Type = strings.ToLower(r.Type)
   589  			if !opsgenieTypeMatcher.MatchString(r.Type) {
   590  				return errors.Errorf("opsGenieConfig responder %v type does not match valid options %s", r, opsgenieValidTypesRe)
   591  			}
   592  		}
   593  	}
   594  
   595  	return nil
   596  }
   597  
   598  type OpsGenieConfigResponder struct {
   599  	// One of those 3 should be filled.
   600  	ID       string `yaml:"id,omitempty" json:"id,omitempty"`
   601  	Name     string `yaml:"name,omitempty" json:"name,omitempty"`
   602  	Username string `yaml:"username,omitempty" json:"username,omitempty"`
   603  
   604  	// team, user, escalation, schedule etc.
   605  	Type string `yaml:"type,omitempty" json:"type,omitempty"`
   606  }
   607  
   608  // VictorOpsConfig configures notifications via VictorOps.
   609  type VictorOpsConfig struct {
   610  	NotifierConfig `yaml:",inline" json:",inline"`
   611  
   612  	HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
   613  
   614  	APIKey            Secret            `yaml:"api_key,omitempty" json:"api_key,omitempty"`
   615  	APIKeyFile        string            `yaml:"api_key_file,omitempty" json:"api_key_file,omitempty"`
   616  	APIURL            *URL              `yaml:"api_url" json:"api_url"`
   617  	RoutingKey        string            `yaml:"routing_key" json:"routing_key"`
   618  	MessageType       string            `yaml:"message_type" json:"message_type"`
   619  	StateMessage      string            `yaml:"state_message" json:"state_message"`
   620  	EntityDisplayName string            `yaml:"entity_display_name" json:"entity_display_name"`
   621  	MonitoringTool    string            `yaml:"monitoring_tool" json:"monitoring_tool"`
   622  	CustomFields      map[string]string `yaml:"custom_fields,omitempty" json:"custom_fields,omitempty"`
   623  }
   624  
   625  // UnmarshalYAML implements the yaml.Unmarshaler interface.
   626  func (c *VictorOpsConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
   627  	*c = DefaultVictorOpsConfig
   628  	type plain VictorOpsConfig
   629  	if err := unmarshal((*plain)(c)); err != nil {
   630  		return err
   631  	}
   632  	if c.RoutingKey == "" {
   633  		return fmt.Errorf("missing Routing key in VictorOps config")
   634  	}
   635  	if c.APIKey != "" && len(c.APIKeyFile) > 0 {
   636  		return fmt.Errorf("at most one of api_key & api_key_file must be configured")
   637  	}
   638  
   639  	reservedFields := []string{"routing_key", "message_type", "state_message", "entity_display_name", "monitoring_tool", "entity_id", "entity_state"}
   640  
   641  	for _, v := range reservedFields {
   642  		if _, ok := c.CustomFields[v]; ok {
   643  			return fmt.Errorf("victorOps config contains custom field %s which cannot be used as it conflicts with the fixed/static fields", v)
   644  		}
   645  	}
   646  
   647  	return nil
   648  }
   649  
   650  type duration time.Duration
   651  
   652  func (d *duration) UnmarshalText(text []byte) error {
   653  	parsed, err := time.ParseDuration(string(text))
   654  	if err == nil {
   655  		*d = duration(parsed)
   656  	}
   657  	return err
   658  }
   659  
   660  func (d duration) MarshalText() ([]byte, error) {
   661  	return []byte(time.Duration(d).String()), nil
   662  }
   663  
   664  type PushoverConfig struct {
   665  	NotifierConfig `yaml:",inline" json:",inline"`
   666  
   667  	HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
   668  
   669  	UserKey  Secret   `yaml:"user_key,omitempty" json:"user_key,omitempty"`
   670  	Token    Secret   `yaml:"token,omitempty" json:"token,omitempty"`
   671  	Title    string   `yaml:"title,omitempty" json:"title,omitempty"`
   672  	Message  string   `yaml:"message,omitempty" json:"message,omitempty"`
   673  	URL      string   `yaml:"url,omitempty" json:"url,omitempty"`
   674  	URLTitle string   `yaml:"url_title,omitempty" json:"url_title,omitempty"`
   675  	Sound    string   `yaml:"sound,omitempty" json:"sound,omitempty"`
   676  	Priority string   `yaml:"priority,omitempty" json:"priority,omitempty"`
   677  	Retry    duration `yaml:"retry,omitempty" json:"retry,omitempty"`
   678  	Expire   duration `yaml:"expire,omitempty" json:"expire,omitempty"`
   679  	HTML     bool     `yaml:"html" json:"html,omitempty"`
   680  }
   681  
   682  // UnmarshalYAML implements the yaml.Unmarshaler interface.
   683  func (c *PushoverConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
   684  	*c = DefaultPushoverConfig
   685  	type plain PushoverConfig
   686  	if err := unmarshal((*plain)(c)); err != nil {
   687  		return err
   688  	}
   689  	if c.UserKey == "" {
   690  		return fmt.Errorf("missing user key in Pushover config")
   691  	}
   692  	if c.Token == "" {
   693  		return fmt.Errorf("missing token in Pushover config")
   694  	}
   695  	return nil
   696  }
   697  
   698  type SNSConfig struct {
   699  	NotifierConfig `yaml:",inline" json:",inline"`
   700  
   701  	HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
   702  
   703  	APIUrl      string            `yaml:"api_url,omitempty" json:"api_url,omitempty"`
   704  	Sigv4       sigv4.SigV4Config `yaml:"sigv4" json:"sigv4"`
   705  	TopicARN    string            `yaml:"topic_arn,omitempty" json:"topic_arn,omitempty"`
   706  	PhoneNumber string            `yaml:"phone_number,omitempty" json:"phone_number,omitempty"`
   707  	TargetARN   string            `yaml:"target_arn,omitempty" json:"target_arn,omitempty"`
   708  	Subject     string            `yaml:"subject,omitempty" json:"subject,omitempty"`
   709  	Message     string            `yaml:"message,omitempty" json:"message,omitempty"`
   710  	Attributes  map[string]string `yaml:"attributes,omitempty" json:"attributes,omitempty"`
   711  }
   712  
   713  // UnmarshalYAML implements the yaml.Unmarshaler interface.
   714  func (c *SNSConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
   715  	*c = DefaultSNSConfig
   716  	type plain SNSConfig
   717  	if err := unmarshal((*plain)(c)); err != nil {
   718  		return err
   719  	}
   720  	if (c.TargetARN == "") != (c.TopicARN == "") != (c.PhoneNumber == "") {
   721  		return fmt.Errorf("must provide either a Target ARN, Topic ARN, or Phone Number for SNS config")
   722  	}
   723  	return nil
   724  }
   725  
   726  // TelegramConfig configures notifications via Telegram.
   727  type TelegramConfig struct {
   728  	NotifierConfig `yaml:",inline" json:",inline"`
   729  
   730  	HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
   731  
   732  	APIUrl               *URL   `yaml:"api_url" json:"api_url,omitempty"`
   733  	BotToken             Secret `yaml:"bot_token,omitempty" json:"token,omitempty"`
   734  	ChatID               int64  `yaml:"chat_id,omitempty" json:"chat,omitempty"`
   735  	Message              string `yaml:"message,omitempty" json:"message,omitempty"`
   736  	DisableNotifications bool   `yaml:"disable_notifications,omitempty" json:"disable_notifications,omitempty"`
   737  	ParseMode            string `yaml:"parse_mode,omitempty" json:"parse_mode,omitempty"`
   738  }
   739  
   740  // UnmarshalYAML implements the yaml.Unmarshaler interface.
   741  func (c *TelegramConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
   742  	*c = DefaultTelegramConfig
   743  	type plain TelegramConfig
   744  	if err := unmarshal((*plain)(c)); err != nil {
   745  		return err
   746  	}
   747  	if c.BotToken == "" {
   748  		return fmt.Errorf("missing bot_token on telegram_config")
   749  	}
   750  	if c.ChatID == 0 {
   751  		return fmt.Errorf("missing chat_id on telegram_config")
   752  	}
   753  	if c.ParseMode != "" &&
   754  		c.ParseMode != "Markdown" &&
   755  		c.ParseMode != "MarkdownV2" &&
   756  		c.ParseMode != "HTML" {
   757  		return fmt.Errorf("unknown parse_mode on telegram_config, must be Markdown, MarkdownV2, HTML or empty string")
   758  	}
   759  	return nil
   760  }
   761  

View as plain text