Skip to content

Commit

Permalink
Slack multi-config allows for multiple slack configurations keyed by …
Browse files Browse the repository at this point in the history
…the workspace name.
  • Loading branch information
Adam committed Mar 21, 2018
1 parent 6d9c9d8 commit c2be03b
Show file tree
Hide file tree
Showing 13 changed files with 751 additions and 82 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### Features


- [#1833](https://github.com/influxdata/kapacitor/pull/1833): Config format updated to allow for more than one slack configuration.
- [#1844](https://github.com/influxdata/kapacitor/pull/1844): Added a new kapacitor node changeDetect that emits a value
for each time a series field changes.
- [#1828](https://github.com/influxdata/kapacitor/pull/1828): Add recoverable field to JSON alert response to indicate whether the
Expand Down
1 change: 1 addition & 0 deletions alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ func newAlertNode(et *ExecutingTask, n *pipeline.AlertNode, d NodeDiagnostic) (a

for _, s := range n.SlackHandlers {
c := slack.HandlerConfig{
Workspace: s.Workspace,
Channel: s.Channel,
Username: s.Username,
IconEmoji: s.IconEmoji,
Expand Down
13 changes: 12 additions & 1 deletion etc/kapacitor/kapacitor.conf
Original file line number Diff line number Diff line change
Expand Up @@ -375,9 +375,20 @@ default-retention-policy = ""
# # Specify an absolute path to a template file.
# row-template-file = "/path/to/template/file"

[slack]
# Slack client configuration
# Mutliple different clients may be configured by
# repeating [[slack]] sections.
[[slack]]
# Configure Slack.
enabled = false
# identify one of the slack configurations as the default
default = true

# workspace ID
# This can be any string to identify this particular slack configuration
# A logical choice is the name of the slack workspace, e.g. <workspace>.slack.com
workspace = ""

# The Slack webhook URL, can be obtained by adding
# an Incoming Webhook integration.
# Visit https://slack.com/services/new/incoming-webhook
Expand Down
19 changes: 13 additions & 6 deletions integrations/streamer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8471,18 +8471,25 @@ stream
.warn(lambda: "count" > 7.0)
.crit(lambda: "count" > 8.0)
.slack()
.workspace('company_private')
.channel('#alerts')
.slack()
.channel('@jim')
`

tmInit := func(tm *kapacitor.TaskMaster) {
c := slack.NewConfig()
c.Enabled = true
c.URL = ts.URL + "/test/slack/url"
c.Channel = "#channel"
c1 := slack.NewConfig()
c1.Default = true
c1.Enabled = true
c1.URL = ts.URL + "/test/slack/url"
c1.Channel = "#channel"
c2 := slack.NewConfig()
c2.Workspace = "company_private"
c2.Enabled = true
c2.URL = ts.URL + "/test/slack/url2"
c2.Channel = "#channel"
d := diagService.NewSlackHandler().WithContext(keyvalue.KV("test", "slack"))
sl, err := slack.NewService(c, d)
sl, err := slack.NewService([]slack.Config{c1, c2}, d)
if err != nil {
t.Error(err)
}
Expand All @@ -8508,7 +8515,7 @@ stream
},
},
slacktest.Request{
URL: "/test/slack/url",
URL: "/test/slack/url2",
PostData: slacktest.PostData{
Channel: "#alerts",
Username: "kapacitor",
Expand Down
21 changes: 18 additions & 3 deletions pipeline/alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -1370,15 +1370,15 @@ type PushoverHandler struct {
// |alert()
// .slack()
//
// Send alerts to Slack channel in the configuration file.
// Send alerts to the default worskace Slack channel in the configuration file.
//
// Example:
// stream
// |alert()
// .slack()
// .channel('#alerts')
//
// Send alerts to Slack channel '#alerts'
// Send alerts to the default workspace with Slack channel '#alerts'
//
// Example:
// stream
Expand All @@ -1388,13 +1388,24 @@ type PushoverHandler struct {
//
// Send alert to user '@jsmith'
//
// Example:
// stream
// |alert()
// .slack()
// .workspace('opencommunity')
// .channel('#support')
//
// send alerts to the opencommunity workspace on the channel '#support'
//
// If the 'slack' section in the configuration has the option: global = true
// then all alerts are sent to Slack without the need to explicitly state it
// in the TICKscript.
//
// Example:
// [slack]
// [[slack]]
// enabled = true
// default = true
// workspace = examplecorp
// url = "https://hooks.slack.com/services/xxxxxxxxx/xxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxxxx"
// channel = "#general"
// global = true
Expand All @@ -1418,6 +1429,10 @@ func (n *AlertNodeData) Slack() *SlackHandler {
type SlackHandler struct {
*AlertNodeData `json:"-"`

// The workspace to publish the alert to. If empty defaults to the configured
// default broker.
Workspace string `json:"workspace"`

// Slack channel in which to post messages.
// If empty uses the channel from the configuration.
Channel string `json:"channel"`
Expand Down
1 change: 1 addition & 0 deletions pipeline/tick/alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ func (n *AlertNode) Build(a *pipeline.AlertNode) (ast.Node, error) {

for _, h := range a.SlackHandlers {
n.Dot("slack").
Dot("workspace", h.Workspace).
Dot("channel", h.Channel).
Dot("username", h.Username).
Dot("iconEmoji", h.IconEmoji)
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 @@ -379,6 +379,7 @@ func TestAlertSensu(t *testing.T) {
func TestAlertSlack(t *testing.T) {
pipe, _, from := StreamFrom()
handler := from.Alert().Slack()
handler.Workspace = "openchannel"
handler.Channel = "#application"
handler.Username = "prbot"
handler.IconEmoji = ":non-potable_water:"
Expand All @@ -391,6 +392,7 @@ func TestAlertSlack(t *testing.T) {
.details('{{ json . }}')
.history(21)
.slack()
.workspace('openchannel')
.channel('#application')
.username('prbot')
.iconEmoji(':non-potable_water:')
Expand Down
4 changes: 2 additions & 2 deletions server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ type Config struct {
SMTP smtp.Config `toml:"smtp" override:"smtp"`
SNMPTrap snmptrap.Config `toml:"snmptrap" override:"snmptrap"`
Sensu sensu.Config `toml:"sensu" override:"sensu"`
Slack slack.Config `toml:"slack" override:"slack"`
Slack slack.Configs `toml:"slack" override:"slack,element-key=workspace"`
Talk talk.Config `toml:"talk" override:"talk"`
Telegram telegram.Config `toml:"telegram" override:"telegram"`
VictorOps victorops.Config `toml:"victorops" override:"victorops"`
Expand Down Expand Up @@ -157,7 +157,7 @@ func NewConfig() *Config {
c.HTTPPost = httppost.Configs{httppost.NewConfig()}
c.SMTP = smtp.NewConfig()
c.Sensu = sensu.NewConfig()
c.Slack = slack.NewConfig()
c.Slack = slack.Configs{slack.NewDefaultConfig()}
c.Talk = talk.NewConfig()
c.SNMPTrap = snmptrap.NewConfig()
c.Telegram = telegram.NewConfig()
Expand Down
117 changes: 117 additions & 0 deletions server/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,120 @@ headers = { Authorization = "your-key" }
t.Fatalf("unexpected header Authorization: %s", c.InfluxDB[0].URLs[0])
}
}

// Ensure the configuration can be parsed.
func TestConfig_Single_Conf(t *testing.T) {
// Parse configuration.
var c server.Config
if _, err := toml.Decode(`
[slack]
enabled = true
channel = "#kapacitor_test"
`, &c); err != nil {
t.Fatal(err)
}

// Validate configuration.
if c.Slack[0].Channel != "#kapacitor_test" || c.Slack[0].Enabled != true {
t.Fatalf("unexpected slack channel or channel not enabled: %s, %v", c.Slack[0].Channel, c.Slack[0].Enabled)
}
}

// Ensure the configuration can be parsed.
func TestConfig_Single_Multiple_Conf(t *testing.T) {
// Parse configuration.
var c server.Config
if _, err := toml.Decode(`
[[slack]]
enabled = true
channel = "#kapacitor_test"
`, &c); err != nil {
t.Fatal(err)
}

// Validate configuration.
if c.Slack[0].Channel != "#kapacitor_test" || c.Slack[0].Enabled != true {
t.Fatalf("unexpected slack channel or channel not enabled: %s, %v", c.Slack[0].Channel, c.Slack[0].Enabled)
}
}

// Ensure the configuration can be parsed.
func TestConfig_Multiple_Multiple_Conf(t *testing.T) {
// Parse configuration.
var c server.Config
if _, err := toml.Decode(`
[[slack]]
workspace = "private"
url = "private.slack.com"
default = true
enabled = true
channel = "#kapacitor_private"
[[slack]]
workspace = "public"
url = "public.slack.com"
default = false
enabled = false
channel = "#kapacitor_public"
`, &c); err != nil {
t.Fatal(err)
}

if err := c.Slack.Validate(); err != nil {
t.Fatalf("Expected config to be valid, %v", err)
}

// Validate configuration.
if c.Slack[0].Workspace != "private" || c.Slack[0].Channel != "#kapacitor_private" || c.Slack[0].Enabled != true {
t.Fatalf("unexpected slack workspace, channel or channel not enabled: %s %s, %v", c.Slack[0].Workspace, c.Slack[0].Channel, c.Slack[0].Enabled)
} else if c.Slack[1].Workspace != "public" || c.Slack[1].Channel != "#kapacitor_public" || c.Slack[1].Enabled != false {
t.Fatalf("unexpected slack workspace, channel or channel enabled: %s %s, %v", c.Slack[1].Workspace, c.Slack[1].Channel, c.Slack[1].Enabled)
}
}

// Ensure the configuration can be parsed.
func TestConfig_Invalid_Multiple_Conf(t *testing.T) {
// Parse configuration.
var c server.Config
cStr := `
[[slack]]
workspace = "private"
url = "private.slack.com"
default = false
enabled = true
channel = "#kapacitor_private"
[[slack]]
workspace = "public"
url = "public.slack.com"
default = false
enabled = false
channel = "#kapacitor_public"
`
if _, err := toml.Decode(cStr, &c); err != nil {
t.Fatal(err)
}

if err := c.Slack.Validate(); err == nil {
t.Fatalf("Expected config to be invalid, %s", cStr)
}

cStr = `
[[slack]]
workspace = "private"
default = true
enabled = true
channel = "#kapacitor_private"
[[slack]]
workspace = "public"
url = "public.slack.com"
default = false
enabled = false
channel = "#kapacitor_public"
`
if _, err := toml.Decode(cStr, &c); err != nil {
t.Fatal(err)
}

if err := c.Slack.Validate(); err == nil {
t.Fatalf("Expected config to be invalid, %s", cStr)
}
}
2 changes: 2 additions & 0 deletions server/server_helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/influxdata/kapacitor/server"
"github.com/influxdata/kapacitor/services/diagnostic"
"github.com/influxdata/kapacitor/services/mqtt"
"github.com/influxdata/kapacitor/services/slack"
"github.com/influxdata/wlog"
)

Expand Down Expand Up @@ -222,6 +223,7 @@ func (s *Server) Stats() (stats, error) {
func NewConfig() *server.Config {
c := server.NewConfig()
c.MQTT = mqtt.Configs{}
c.Slack = slack.Configs{slack.NewConfig()}
c.Reporting.Enabled = false
c.Replay.Dir = MustTempDir()
c.Storage.BoltDBPath = filepath.Join(MustTempDir(), "bolt.db")
Expand Down
Loading

0 comments on commit c2be03b

Please sign in to comment.