diff --git a/alert.go b/alert.go index 528351c90..765737b3c 100644 --- a/alert.go +++ b/alert.go @@ -493,7 +493,9 @@ func newAlertNode(et *ExecutingTask, n *pipeline.AlertNode, d NodeDiagnostic) (a for _, s := range n.BigPandaHandlers { c := bigpanda.HandlerConfig{ - AppKey: s.AppKey, + AppKey: s.AppKey, + PrimaryProperty: s.PrimaryProperty, + SecondaryProperty: s.SecondaryProperty, } h, err := et.tm.BigPandaService.Handler(c, ctx...) if err != nil { diff --git a/pipeline/alert.go b/pipeline/alert.go index 8117795d8..cb7867bf4 100644 --- a/pipeline/alert.go +++ b/pipeline/alert.go @@ -1637,6 +1637,7 @@ type DiscordHandler struct { // enabled = true // app-key = "my-app-key" // token = "your-api-key" +// url = "BigPanda alert webhook url" // // In order to not post a message every alert interval // use AlertNode.StateChangesOnly so that only events @@ -1654,11 +1655,13 @@ type DiscordHandler struct { // |alert() // .bigPanda() // .appKey('my-application') +// .primaryProperty('property1') +// .secondaryProperty('property2') // -// send alerts with custom appKey +// send alerts with custom appKey, primary and secondary property // // If the 'bigpanda' section in the configuration has the option: global = true -// then all alerts are sent to BigpPanda without the need to explicitly state it +// then all alerts are sent to BigPanda without the need to explicitly state it // in the TICKscript. // // Example: @@ -1690,6 +1693,12 @@ type BigPandaHandler struct { // Application id // If empty uses the default config AppKey string `json:"app-key"` + + // Custom primary BigPanda property + PrimaryProperty string `json:"primary-property"` + + // Custom secondary BigPanda property + SecondaryProperty string `json:"secondary-property"` } // Send the alert to Telegram. diff --git a/services/bigpanda/config.go b/services/bigpanda/config.go index 92f31e2fc..292a27871 100644 --- a/services/bigpanda/config.go +++ b/services/bigpanda/config.go @@ -13,7 +13,7 @@ type Config struct { // Whether BigPanda integration is enabled. Enabled bool `toml:"enabled" override:"enabled"` - // Whether all alerts should automatically post to Teams. + // Whether all alerts should automatically post to BigPanda. Global bool `toml:"global" override:"global"` //Each integration must have an App Key in BigPanda to identify it as a unique source. @@ -26,10 +26,10 @@ type Config struct { // Only applies if global is also set. StateChangesOnly bool `toml:"state-changes-only" override:"state-changes-only"` - // Whether to skip the tls verification of the alerta host + // Whether to skip the tls verification InsecureSkipVerify bool `toml:"insecure-skip-verify" override:"insecure-skip-verify"` - //Optional alert api URL, if not specified https://api.bigpanda.io/data/v2/alerts is used + //BigPanda Alert api URL, if not specified https://api.bigpanda.io/data/v2/alerts is used URL string `toml:"url" override:"url"` } @@ -40,6 +40,10 @@ func NewConfig() Config { } func (c Config) Validate() error { + if c.Enabled && c.URL == "" { + return errors.New("must specify the BigPanda webhook URL") + } + if c.Enabled && c.AppKey == "" { return errors.New("must specify BigPanda app-key") } diff --git a/services/bigpanda/service.go b/services/bigpanda/service.go index 4c3923b48..805c05430 100644 --- a/services/bigpanda/service.go +++ b/services/bigpanda/service.go @@ -113,11 +113,14 @@ func (s *Service) Test(options interface{}) error { if !ok { return fmt.Errorf("unexpected options type %T", options) } - return s.Alert(o.AppKey, "", o.Message, "", o.Level, o.Timestamp, o.Data) + hc := &HandlerConfig{ + AppKey: o.AppKey, + } + return s.Alert("", o.Message, "", o.Level, o.Timestamp, o.Data, hc) } -func (s *Service) Alert(appKey, id string, message string, details string, level alert.Level, timestamp time.Time, data alert.EventData) error { - req, err := s.preparePost(appKey, id, message, details, level, timestamp, data) +func (s *Service) Alert(id string, message string, details string, level alert.Level, timestamp time.Time, data alert.EventData, hc *HandlerConfig) error { + req, err := s.preparePost(id, message, details, level, timestamp, data, hc) if err != nil { return err @@ -173,7 +176,7 @@ curl -X POST -H "Content-Type: application/json" \ "primary_property": "application", "secondary_property": "host" */ -func (s *Service) preparePost(appKey, id string, message string, details string, level alert.Level, timestamp time.Time, data alert.EventData) (*http.Request, error) { +func (s *Service) preparePost(id string, message string, details string, level alert.Level, timestamp time.Time, data alert.EventData, hc *HandlerConfig) (*http.Request, error) { c := s.config() if !c.Enabled { return nil, errors.New("service is not enabled") @@ -215,15 +218,26 @@ func (s *Service) preparePost(appKey, id string, message string, details string, bpData["timestamp"] = timestamp.Unix() bpData["status"] = status - if appKey == "" { - appKey = c.AppKey + // primary and secondary property + if hc.PrimaryProperty != "" { + bpData["primary_property"] = hc.PrimaryProperty + } + if hc.SecondaryProperty != "" { + bpData["secondary_property"] = hc.SecondaryProperty } - bpData["app_key"] = appKey - if len(data.Tags) > 0 { - for k, v := range data.Tags { - bpData[k] = v - } + if hc.AppKey != "" { + bpData["app_key"] = hc.AppKey + } else { + bpData["app_key"] = c.AppKey + } + + for k, v := range data.Tags { + bpData[k] = v + } + + for k, v := range data.Fields { + bpData[k] = fmt.Sprintf("%v", v) } var post bytes.Buffer @@ -232,7 +246,12 @@ func (s *Service) preparePost(appKey, id string, message string, details string, return nil, err } - alertUrl, err := url.Parse(c.URL) + bpUrl := hc.URL + if bpUrl == "" { + bpUrl = c.URL + } + + alertUrl, err := url.Parse(bpUrl) if err != nil { return nil, err } @@ -248,7 +267,17 @@ func (s *Service) preparePost(appKey, id string, message string, details string, // HandlerConfig defines the high-level struct required to connect to BigPanda type HandlerConfig struct { + // BigPanda AppKey AppKey string `mapstructure:"app-key"` + + // webhook URL used to post alert. + // If empty uses the service URL from the configuration. + URL string `mapstructure:"url"` + + // custom primary BigPanda property + PrimaryProperty string `mapstructure:"primary-property"` + // custom secondary BigPanda property + SecondaryProperty string `mapstructure:"secondary-property"` } type handler struct { @@ -267,13 +296,13 @@ func (s *Service) Handler(c HandlerConfig, ctx ...keyvalue.T) (alert.Handler, er func (h *handler) Handle(event alert.Event) { if err := h.s.Alert( - h.c.AppKey, event.State.ID, event.State.Message, event.State.Details, event.State.Level, event.State.Time, event.Data, + &h.c, ); err != nil { h.diag.Error("failed to send event to BigPanda", err) }