...

Source file src/github.com/prometheus/alertmanager/notify/victorops/victorops.go

Documentation: github.com/prometheus/alertmanager/notify/victorops

     1  // Copyright 2019 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 victorops
    15  
    16  import (
    17  	"bytes"
    18  	"context"
    19  	"encoding/json"
    20  	"fmt"
    21  	"net/http"
    22  	"os"
    23  	"strings"
    24  
    25  	"github.com/go-kit/log"
    26  	"github.com/go-kit/log/level"
    27  	"github.com/pkg/errors"
    28  	commoncfg "github.com/prometheus/common/config"
    29  	"github.com/prometheus/common/model"
    30  
    31  	"github.com/prometheus/alertmanager/config"
    32  	"github.com/prometheus/alertmanager/notify"
    33  	"github.com/prometheus/alertmanager/template"
    34  	"github.com/prometheus/alertmanager/types"
    35  )
    36  
    37  // https://help.victorops.com/knowledge-base/incident-fields-glossary/ - 20480 characters.
    38  const maxMessageLenRunes = 20480
    39  
    40  // Notifier implements a Notifier for VictorOps notifications.
    41  type Notifier struct {
    42  	conf    *config.VictorOpsConfig
    43  	tmpl    *template.Template
    44  	logger  log.Logger
    45  	client  *http.Client
    46  	retrier *notify.Retrier
    47  }
    48  
    49  // New returns a new VictorOps notifier.
    50  func New(c *config.VictorOpsConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) {
    51  	client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "victorops", httpOpts...)
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  	return &Notifier{
    56  		conf:   c,
    57  		tmpl:   t,
    58  		logger: l,
    59  		client: client,
    60  		// Missing documentation therefore assuming only 5xx response codes are
    61  		// recoverable.
    62  		retrier: &notify.Retrier{},
    63  	}, nil
    64  }
    65  
    66  const (
    67  	victorOpsEventTrigger = "CRITICAL"
    68  	victorOpsEventResolve = "RECOVERY"
    69  )
    70  
    71  // Notify implements the Notifier interface.
    72  func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) {
    73  	var err error
    74  	var (
    75  		data   = notify.GetTemplateData(ctx, n.tmpl, as, n.logger)
    76  		tmpl   = notify.TmplText(n.tmpl, data, &err)
    77  		apiURL = n.conf.APIURL.Copy()
    78  	)
    79  
    80  	var apiKey string
    81  	if n.conf.APIKey != "" {
    82  		apiKey = string(n.conf.APIKey)
    83  	} else {
    84  		content, fileErr := os.ReadFile(n.conf.APIKeyFile)
    85  		if fileErr != nil {
    86  			return false, errors.Wrap(fileErr, "failed to read API key from file")
    87  		}
    88  		apiKey = strings.TrimSpace(string(content))
    89  	}
    90  
    91  	apiURL.Path += fmt.Sprintf("%s/%s", apiKey, tmpl(n.conf.RoutingKey))
    92  	if err != nil {
    93  		return false, fmt.Errorf("templating error: %s", err)
    94  	}
    95  
    96  	buf, err := n.createVictorOpsPayload(ctx, as...)
    97  	if err != nil {
    98  		return true, err
    99  	}
   100  
   101  	resp, err := notify.PostJSON(ctx, n.client, apiURL.String(), buf)
   102  	if err != nil {
   103  		return true, notify.RedactURL(err)
   104  	}
   105  	defer notify.Drain(resp)
   106  
   107  	return n.retrier.Check(resp.StatusCode, resp.Body)
   108  }
   109  
   110  // Create the JSON payload to be sent to the VictorOps API.
   111  func (n *Notifier) createVictorOpsPayload(ctx context.Context, as ...*types.Alert) (*bytes.Buffer, error) {
   112  	victorOpsAllowedEvents := map[string]bool{
   113  		"INFO":     true,
   114  		"WARNING":  true,
   115  		"CRITICAL": true,
   116  	}
   117  
   118  	key, err := notify.ExtractGroupKey(ctx)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	var (
   124  		alerts = types.Alerts(as...)
   125  		data   = notify.GetTemplateData(ctx, n.tmpl, as, n.logger)
   126  		tmpl   = notify.TmplText(n.tmpl, data, &err)
   127  
   128  		messageType  = tmpl(n.conf.MessageType)
   129  		stateMessage = tmpl(n.conf.StateMessage)
   130  	)
   131  
   132  	if alerts.Status() == model.AlertFiring && !victorOpsAllowedEvents[messageType] {
   133  		messageType = victorOpsEventTrigger
   134  	}
   135  
   136  	if alerts.Status() == model.AlertResolved {
   137  		messageType = victorOpsEventResolve
   138  	}
   139  
   140  	stateMessage, truncated := notify.TruncateInRunes(stateMessage, maxMessageLenRunes)
   141  	if truncated {
   142  		level.Warn(n.logger).Log("msg", "Truncated state_message", "incident", key, "max_runes", maxMessageLenRunes)
   143  	}
   144  
   145  	msg := map[string]string{
   146  		"message_type":        messageType,
   147  		"entity_id":           key.Hash(),
   148  		"entity_display_name": tmpl(n.conf.EntityDisplayName),
   149  		"state_message":       stateMessage,
   150  		"monitoring_tool":     tmpl(n.conf.MonitoringTool),
   151  	}
   152  
   153  	if err != nil {
   154  		return nil, fmt.Errorf("templating error: %s", err)
   155  	}
   156  
   157  	// Add custom fields to the payload.
   158  	for k, v := range n.conf.CustomFields {
   159  		msg[k] = tmpl(v)
   160  		if err != nil {
   161  			return nil, fmt.Errorf("templating error: %s", err)
   162  		}
   163  	}
   164  
   165  	var buf bytes.Buffer
   166  	if err := json.NewEncoder(&buf).Encode(msg); err != nil {
   167  		return nil, err
   168  	}
   169  	return &buf, nil
   170  }
   171  

View as plain text