Skip to content

Commit

Permalink
feature: making Slack action URL configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
jkeyes committed Oct 8, 2024
1 parent 2a07052 commit 1a1542e
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 4 deletions.
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"`
}

0 comments on commit 1a1542e

Please sign in to comment.