Skip to content

Commit

Permalink
feat: add BigPanda handler options (#2643)
Browse files Browse the repository at this point in the history
* feat: add BigPanda attribute option

* style: apply go fmt

* docs: add BigPanda entry

* chore: add BigPanda configuration

* chore: extend BigPanda entry

* feat: use default attributes for backward compatibility

* style: apply go fmt

* docs: remove duplicate entry
  • Loading branch information
alespour authored Nov 19, 2021
1 parent 01377d3 commit 8c9ed5d
Show file tree
Hide file tree
Showing 12 changed files with 421 additions and 76 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Features
- [#2575](https://github.com/influxdata/kapacitor/pull/2575): Support the "attributes" attribute in Alerta node
- [#2630](https://github.com/influxdata/kapacitor/pull/2630): Upgrade to the new `google.golang` Protobuf library
- [#2643](https://github.com/influxdata/kapacitor/pull/2643): Add "host" and "attribute" options to BigPanda node and "auto-attributes" configuration option

## v1.6.2 [2021-09-24]

Expand Down
2 changes: 2 additions & 0 deletions alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -521,8 +521,10 @@ func newAlertNode(et *ExecutingTask, n *pipeline.AlertNode, d NodeDiagnostic) (a
for _, s := range n.BigPandaHandlers {
c := bigpanda.HandlerConfig{
AppKey: s.AppKey,
Host: s.Host,
PrimaryProperty: s.PrimaryProperty,
SecondaryProperty: s.SecondaryProperty,
Attributes: s.Attributes,
}
h, err := et.tm.BigPandaService.Handler(c, ctx...)
if err != nil {
Expand Down
12 changes: 12 additions & 0 deletions etc/kapacitor/kapacitor.conf
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,18 @@ default-retention-policy = ""
# Default origin.
origin = "kapacitor"

[bigpanda]
# Configure BigPanda.
enabled = false
# BigPanda Alerts URL.
url = "https://api.bigpanda.io/data/v2/alerts"
# Application key
# app-key = ""
# Authentication token
# token = ""
# Default BigPanda alert additional attributes.
# auto-attributes = "tags,fields"

[servicenow]
# Configure ServiceNow.
enabled = false
Expand Down
59 changes: 38 additions & 21 deletions integrations/streamer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9379,18 +9379,27 @@ stream
.warn(lambda: "count" > 7.0)
.crit(lambda: "count" > 8.0)
.bigPanda()
.AppKey('111111')
.appKey('111111')
.host('{{.Tags.host}}')
.primaryProperty('host')
.bigPanda()
.AppKey('222222')
.appKey('222222')
.host('serverA-1')
.secondaryProperty('application')
.bigPanda()
.attribute('x_host', '{{.Tags.host}}')
.attribute('x_duration', '{{.Duration}}')
.attribute('x_detail', '{{.Details}}')
.attribute('x_value', '{{.Fields.count}}')
`
tmInit := func(tm *kapacitor.TaskMaster) {

c := bigpanda.NewConfig()
c.Enabled = true
c.AppKey = "XXXXXXX"
c.AppKey = "012345"
c.Token = "testtoken1231234"
c.URL = ts.URL + "/test/bigpanda/url"
c.AutoAttributes = ""

d := diagService.NewBigPandaHandler().WithContext(keyvalue.KV("test", "111"))
sl, err := bigpanda.NewService(c, d)
Expand All @@ -9407,40 +9416,48 @@ stream
bigpandatest.Request{
URL: "/test/bigpanda/url",
PostData: bigpandatest.PostData{
Check: "kapacitor/cpu/serverA",
Description: "kapacitor/cpu/serverA is CRITICAL @1971-01-01 00:00:10 +0000 UTC",
AppKey: "111111",
Status: "critical",
Host: "serverA",
Timestamp: 31536010,
Task: "TestStream_Alert:cpu",
Details: "https://example.org/link",
Check: "kapacitor/cpu/serverA",
Description: "kapacitor/cpu/serverA is CRITICAL @1971-01-01 00:00:10 +0000 UTC",
AppKey: "111111",
Status: "critical",
Host: "serverA",
Timestamp: 31536010,
Task: "TestStream_Alert:cpu",
Details: "https://example.org/link",
PrimaryProperty: "host",
},
},
bigpandatest.Request{
URL: "/test/bigpanda/url",
PostData: bigpandatest.PostData{
Check: "kapacitor/cpu/serverA",
Description: "kapacitor/cpu/serverA is CRITICAL @1971-01-01 00:00:10 +0000 UTC",
AppKey: "222222",
Status: "critical",
Host: "serverA",
Timestamp: 31536010,
Task: "TestStream_Alert:cpu",
Details: "https://example.org/link",
Check: "kapacitor/cpu/serverA",
Description: "kapacitor/cpu/serverA is CRITICAL @1971-01-01 00:00:10 +0000 UTC",
AppKey: "222222",
Status: "critical",
Host: "serverA-1",
Timestamp: 31536010,
Task: "TestStream_Alert:cpu",
Details: "https://example.org/link",
SecondaryProperty: "application",
},
},
bigpandatest.Request{
URL: "/test/bigpanda/url",
PostData: bigpandatest.PostData{
Check: "kapacitor/cpu/serverA",
Description: "kapacitor/cpu/serverA is CRITICAL @1971-01-01 00:00:10 +0000 UTC",
AppKey: "XXXXXXX",
AppKey: "012345",
Status: "critical",
Host: "serverA",
Host: "",
Timestamp: 31536010,
Task: "TestStream_Alert:cpu",
Details: "https://example.org/link",
Attributes: map[string]string{
"x_detail": "https://example.org/link",
"x_duration": "0s",
"x_host": "serverA",
"x_value": "10",
},
},
},
}
Expand Down
19 changes: 18 additions & 1 deletion pipeline/alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -1705,15 +1705,32 @@ func (n *AlertNodeData) BigPanda() *BigPandaHandler {
// tick:embedded:AlertNode.BigPanda
type BigPandaHandler struct {
*AlertNodeData `json:"-"`
// Application id
// Application key
// If empty uses the default config
AppKey string `json:"app-key"`

// Object that caused the alert
Host string `json:"host"`

// Custom primary BigPanda property
PrimaryProperty string `json:"primary-property"`

// Custom secondary BigPanda property
SecondaryProperty string `json:"secondary-property"`

// Additional attributes
// tick:ignore
Attributes map[string]interface{} `tick:"Attribute" json:"attributes"`
}

// Attribute adds additional attributes to the request.
// tick:property
func (bp *BigPandaHandler) Attribute(key string, value interface{}) *BigPandaHandler {
if bp.Attributes == nil {
bp.Attributes = make(map[string]interface{})
}
bp.Attributes[key] = value
return bp
}

// Send the alert to Telegram.
Expand Down
11 changes: 11 additions & 0 deletions pipeline/tick/alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,19 @@ func (n *AlertNode) Build(a *pipeline.AlertNode) (ast.Node, error) {
for _, h := range a.BigPandaHandlers {
n.Dot("bigPanda").
Dot("appKey", h.AppKey).
Dot("host", h.Host).
Dot("primaryProperty", h.PrimaryProperty).
Dot("secondaryProperty", h.SecondaryProperty)

// Use stable key order
keys := make([]string, 0, len(h.Attributes))
for k := range h.Attributes {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
n.Dot("attribute", k, h.Attributes[k])
}
}

for _, h := range a.SlackHandlers {
Expand Down
2 changes: 2 additions & 0 deletions pipeline/tick/alert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ func TestAlertBigPanda(t *testing.T) {
pipe, _, from := StreamFrom()
handler := from.Alert().BigPanda()
handler.AppKey = "A"
handler.Host = "H"
handler.PrimaryProperty = "B"
handler.SecondaryProperty = "C"

Expand All @@ -134,6 +135,7 @@ func TestAlertBigPanda(t *testing.T) {
.history(21)
.bigPanda()
.appKey('A')
.host('H')
.primaryProperty('B')
.secondaryProperty('C')
`
Expand Down
17 changes: 12 additions & 5 deletions server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7889,6 +7889,7 @@ func TestServer_UpdateConfig(t *testing.T) {
"insecure-skip-verify": false,
"token": false,
"app-key": "",
"auto-attributes": "tags,fields",
},
Redacted: []string{
"token",
Expand All @@ -7905,6 +7906,7 @@ func TestServer_UpdateConfig(t *testing.T) {
"insecure-skip-verify": false,
"token": false,
"app-key": "",
"auto-attributes": "tags,fields",
},
Redacted: []string{
"token",
Expand All @@ -7914,10 +7916,11 @@ func TestServer_UpdateConfig(t *testing.T) {
{
updateAction: client.ConfigUpdateAction{
Set: map[string]interface{}{
"enabled": true,
"url": "https://dev123456.bigpanda.io/data/v2/alerts",
"app-key": "appkey-123",
"token": "token-123",
"enabled": true,
"url": "https://dev123456.bigpanda.io/data/v2/alerts",
"app-key": "appkey-123",
"token": "token-123",
"auto-attributes": "",
},
},
expSection: client.ConfigSection{
Expand All @@ -7931,6 +7934,7 @@ func TestServer_UpdateConfig(t *testing.T) {
"url": "https://dev123456.bigpanda.io/data/v2/alerts",
"token": true,
"app-key": "appkey-123",
"auto-attributes": "",
"insecure-skip-verify": false,
},
Redacted: []string{
Expand All @@ -7946,6 +7950,7 @@ func TestServer_UpdateConfig(t *testing.T) {
"state-changes-only": false,
"url": "https://dev123456.bigpanda.io/data/v2/alerts",
"app-key": "appkey-123",
"auto-attributes": "",
"token": true,
"insecure-skip-verify": false,
},
Expand Down Expand Up @@ -9146,7 +9151,8 @@ func TestServer_ListServiceTests(t *testing.T) {
Link: client.Link{Relation: client.Self, Href: "/kapacitor/v1/service-tests/bigpanda"},
Name: "bigpanda",
Options: client.ServiceTestOptions{
"app_key": "",
"app_key": "012345",
"host": "serverA",
"level": "CRITICAL",
"message": "test bigpanda message",
"timestamp": "1970-01-01T00:00:01Z",
Expand Down Expand Up @@ -10528,6 +10534,7 @@ func TestServer_AlertHandlers(t *testing.T) {
c.BigPanda.Token = "my-token-123"
c.BigPanda.AppKey = "my-app-key"
c.BigPanda.URL = ts.URL + "/test/bigpanda/alert"
c.BigPanda.AutoAttributes = ""
return ctxt, nil
},
result: func(ctxt context.Context) error {
Expand Down
90 changes: 79 additions & 11 deletions services/bigpanda/bigpandatest/bigpandatest.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,83 @@ type Request struct {

// PostData is the default struct to send an element through to BigPanda
type PostData struct {
AppKey string `json:"app_key"`
Status string `json:"status"`
Host string `json:"host"`
Timestamp int64 `json:"timestamp"`
Check string `json:"check"`
Description string `json:"description"`
Cluster string `json:"cluster"`
Task string `json:"task"`
Details string `json:"details"`
PrimaryProperty string `json:"primary_property"`
SecondaryProperty string `json:"secondary_property"`
AppKey string `json:"app_key"`
Status string `json:"status"`
Host string `json:"host"`
Timestamp int64 `json:"timestamp"`
Check string `json:"check"`
Description string `json:"description"`
Cluster string `json:"cluster"`
Task string `json:"task"`
Details string `json:"details"`
PrimaryProperty string `json:"primary_property"`
SecondaryProperty string `json:"secondary_property"`
Attributes map[string]string `json:"-,omitempty"`
}

func (pd *PostData) UnmarshalJSON(data []byte) error {
var x map[string]interface{}
if err := json.Unmarshal(data, &x); err != nil {
return nil
}
if appKey, ok := x["app_key"]; ok {
pd.AppKey = appKey.(string)
delete(x, "app_key")
}
if status, ok := x["status"]; ok {
pd.Status = status.(string)
delete(x, "status")
}
if host, ok := x["host"]; ok {
pd.Host = host.(string)
delete(x, "host")
}
if timestamp, ok := x["timestamp"]; ok {
pd.Timestamp = int64(timestamp.(float64))
delete(x, "timestamp")
}
if check, ok := x["check"]; ok {
pd.Check = check.(string)
delete(x, "check")
}
if description, ok := x["description"]; ok {
pd.Description = description.(string)
delete(x, "description")
}
if cluster, ok := x["cluster"]; ok {
pd.Cluster = cluster.(string)
delete(x, "cluster")
}
if task, ok := x["task"]; ok {
pd.Task = task.(string)
delete(x, "task")
}
if details, ok := x["details"]; ok {
pd.Details = details.(string)
delete(x, "details")
}
if primary, ok := x["primary_property"]; ok {
pd.PrimaryProperty = primary.(string)
delete(x, "primary_property")
}
if secondary, ok := x["secondary_property"]; ok {
pd.SecondaryProperty = secondary.(string)
delete(x, "secondary_property")
}
if len(x) > 0 {
pd.Attributes = make(map[string]string, len(x))
for k, v := range x {
switch value := v.(type) {
case string:
pd.Attributes[k] = value
default:
b, err := json.Marshal(value)
if err != nil {
return err
}
pd.Attributes[k] = string(b)
}
}
}
return nil
}
Loading

0 comments on commit 8c9ed5d

Please sign in to comment.