Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: making Slack action URL configurable #1535

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/guides/alerts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
[slack]
webhook="https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"
```
7. By default the "Open view" action in the Slack alert will attempt to open `http://localhost:{port}/inventory?view={viewId}`. However, you can configure this URL via the `host` field. For example, if komiser is hosted on `https://komiser.local` then set the field accordingly:
```
[slack]
webhook="https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"
host="https://komiser.local"
```

## Custom Webhook integration
To integrate a custom webhook, you need a URL that listens to the data posted to it when it is triggered. You don't need to edit the `config.toml` file for this integration.
Expand Down
4 changes: 2 additions & 2 deletions internal/alerts.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func checkingAlerts(ctx context.Context, cfg models.Config, telemetry bool, port
}
if alert.IsSlack {
log.Info("Sending Slack budget alert for view:", view.Name)
hitSlackWebhook(view.Name, port, int(view.Id), 0, stats.Costs, cfg.Slack.Webhook, alert.Type)
hitSlackWebhook(view.Name, port, int(view.Id), 0, stats.Costs, cfg.Slack.Webhook, cfg.Slack.Host, alert.Type)
} else {
log.Info("Sending Custom Webhook budget alert for view:", view.Name)
hitCustomWebhook(alert.Endpoint, alert.Secret, view.Name, 0, stats.Costs, alert.Type)
Expand All @@ -42,7 +42,7 @@ func checkingAlerts(ctx context.Context, cfg models.Config, telemetry bool, port
}
if alert.IsSlack {
log.Info("Sending Slack usage alert for view:", view.Name)
hitSlackWebhook(view.Name, port, int(view.Id), stats.Resources, 0, cfg.Slack.Webhook, alert.Type)
hitSlackWebhook(view.Name, port, int(view.Id), stats.Resources, 0, cfg.Slack.Webhook, cfg.Slack.Host, alert.Type)
} else {
log.Info("Sending Custom Webhook usage alert for view:", view.Name)
hitCustomWebhook(alert.Endpoint, alert.Secret, view.Name, stats.Resources, 0, alert.Type)
Expand Down
49 changes: 49 additions & 0 deletions internal/config/load_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package config

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestSlackConfig(t *testing.T) {
// test when a value is only specified for webhook
cfgText := `
[slack]
webhook = "https://example.com"
`
cfgBytes := []byte(cfgText)
config, _ := loadConfigFromBytes(cfgBytes)

assert.Equal(t, "https://example.com", config.Slack.Webhook)
assert.Equal(t, false, config.Slack.Reporting)
assert.Equal(t, "", config.Slack.Host)

// test when a value is specified for reporting
cfgText = `
[slack]
webhook = "https://example.com"
reporting = true
`
cfgBytes = []byte(cfgText)
config, _ = loadConfigFromBytes(cfgBytes)

assert.Equal(t, "https://example.com", config.Slack.Webhook)
assert.Equal(t, true, config.Slack.Reporting)
assert.Equal(t, "", config.Slack.Host)

// test when a value is specified for host
cfgText = `
[slack]
webhook = "https://example.com"
reporting = true
host = "https://example.com/komiser"
`
cfgBytes = []byte(cfgText)
config, _ = loadConfigFromBytes(cfgBytes)

assert.Equal(t, "https://example.com", config.Slack.Webhook)
assert.Equal(t, true, config.Slack.Reporting)
assert.Equal(t, "https://example.com/komiser", config.Slack.Host)

}
18 changes: 16 additions & 2 deletions internal/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,15 @@ func hitCustomWebhook(endpoint string, secret string, viewName string, resources
}
}

func hitSlackWebhook(viewName string, port int, viewId int, resources int, cost float64, webhookUrl string, alertType string) {
// createSlackAttachment creates a `slack.Attachment`.
// This attachment can then be added to the WebhookMessage for the alert.
func createSlackAttachment(viewName string, port int, viewId int, resources int, cost float64, hostname string, alertType string) slack.Attachment {
// if the hostname is empty i.e. not defined in config.toml
// default to localhost and the runtime port value
if hostname == "" {
hostname = fmt.Sprintf("http://localhost:%d", port)
}

attachment := slack.Attachment{
Color: "danger",
AuthorName: "Komiser",
Expand All @@ -77,7 +85,7 @@ func hitSlackWebhook(viewName string, port int, viewId int, resources int, cost
Name: "open",
Text: "Open view",
Type: "button",
URL: fmt.Sprintf("http://localhost:%d/inventory?view=%d", port, viewId),
URL: fmt.Sprintf("%s/inventory?view=%d", hostname, viewId),
},
},
Fields: []slack.AttachmentField{
Expand All @@ -103,6 +111,12 @@ func hitSlackWebhook(viewName string, port int, viewId int, resources int, cost
Value: fmt.Sprintf("%d", resources),
})
}
return attachment
}

func hitSlackWebhook(viewName string, port int, viewId int, resources int, cost float64, webhookUrl string, hostname string, alertType string) {

attachment := createSlackAttachment(viewName, port, viewId, resources, cost, hostname, alertType)

msg := slack.WebhookMessage{
Attachments: []slack.Attachment{attachment},
Expand Down
19 changes: 19 additions & 0 deletions internal/webhook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package internal

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestSlackAttachment(t *testing.T) {
// default - no slack host specified in config
attachment := createSlackAttachment("testViewName", 3000, 101, 99, 99.99, "", "RESOURCES")
expectedActionUrl := "http://localhost:3000/inventory?view=101"
assert.Equal(t, expectedActionUrl, attachment.Actions[0].URL)

// explicit - slack host specified in config
attachment = createSlackAttachment("testViewName", 3000, 101, 99, 99.99, "https://example.com", "RESOURCES")
expectedActionUrl = "https://example.com/inventory?view=101"
assert.Equal(t, expectedActionUrl, attachment.Actions[0].URL)
}
1 change: 1 addition & 0 deletions models/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,5 @@ type OVHConfig struct {
type SlackConfig struct {
Webhook string `toml:"webhook"`
Reporting bool `toml:"reporting"`
Host string `toml:"host"`
}
Loading