From 21c273a77b300fe85a6dddee9bb34e748cc486ee Mon Sep 17 00:00:00 2001 From: hotpheex Date: Sun, 6 Aug 2023 08:38:02 +1000 Subject: [PATCH 1/3] Add discord webhook sender --- cmd/keel/main.go | 1 + constants/constants.go | 3 + extension/notification/discord/discord.go | 99 +++++++++++++++++++ .../notification/discord/discord_test.go | 59 +++++++++++ 4 files changed, 162 insertions(+) create mode 100644 extension/notification/discord/discord.go create mode 100644 extension/notification/discord/discord_test.go diff --git a/cmd/keel/main.go b/cmd/keel/main.go index 2cbec24a..dbfecde6 100644 --- a/cmd/keel/main.go +++ b/cmd/keel/main.go @@ -39,6 +39,7 @@ import ( // notification extensions "github.com/keel-hq/keel/extension/notification/auditor" + _ "github.com/keel-hq/keel/extension/notification/discord" _ "github.com/keel-hq/keel/extension/notification/hipchat" _ "github.com/keel-hq/keel/extension/notification/mail" _ "github.com/keel-hq/keel/extension/notification/mattermost" diff --git a/constants/constants.go b/constants/constants.go index 12d95cff..787b7cbb 100644 --- a/constants/constants.go +++ b/constants/constants.go @@ -34,6 +34,9 @@ const ( // MS Teams webhook url, see https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using#setting-up-a-custom-incoming-webhook EnvTeamsWebhookUrl = "TEAMS_WEBHOOK_URL" + // Discord webhook url, see https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks + EnvDiscordWebhookUrl = "DISCORD_WEBHOOK_URL" + // Mail notification settings EnvMailTo = "MAIL_TO" EnvMailFrom = "MAIL_FROM" diff --git a/extension/notification/discord/discord.go b/extension/notification/discord/discord.go new file mode 100644 index 00000000..1a80a5c0 --- /dev/null +++ b/extension/notification/discord/discord.go @@ -0,0 +1,99 @@ +package discord + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/url" + "os" + "time" + + "github.com/keel-hq/keel/constants" + "github.com/keel-hq/keel/extension/notification" + "github.com/keel-hq/keel/types" + + log "github.com/sirupsen/logrus" +) + +const timeout = 5 * time.Second + +type sender struct { + endpoint string + client *http.Client +} + +// Config represents the configuration of a Discord Webhook Sender. +type Config struct { + Endpoint string +} + +func init() { + log.Error("RUNNING") + notification.RegisterSender("discord", &sender{}) +} + +func (s *sender) Configure(config *notification.Config) (bool, error) { + // Get configuration + var httpConfig Config + + if os.Getenv(constants.EnvDiscordWebhookUrl) != "" { + httpConfig.Endpoint = os.Getenv(constants.EnvDiscordWebhookUrl) + } else { + return false, nil + } + + // Validate endpoint URL. + if httpConfig.Endpoint == "" { + return false, nil + } + if _, err := url.ParseRequestURI(httpConfig.Endpoint); err != nil { + return false, fmt.Errorf("could not parse endpoint URL: %s", err) + } + s.endpoint = httpConfig.Endpoint + + // Setup HTTP client. + s.client = &http.Client{ + Transport: http.DefaultTransport, + Timeout: timeout, + } + + log.WithFields(log.Fields{ + "name": "discord", + "endpoint": s.endpoint, + }).Info("extension.notification.discord: sender configured") + + return true, nil +} + +// type notificationEnvelope struct { +// types.EventNotification +// } + +type DiscordMessage struct { + Content string `json:"content"` + Username string `json:"username"` +} + +func (s *sender) Send(event types.EventNotification) error { + discordMessage := DiscordMessage{ + Content: fmt.Sprintf("**%s**\n%s", event.Name, event.Message), + Username: "Keel", + } + + jsonMessage, err := json.Marshal(discordMessage) + if err != nil { + return fmt.Errorf("could not marshal: %s", err) + } + + resp, err := s.client.Post(s.endpoint, "application/json", bytes.NewBuffer(jsonMessage)) + if err != nil || resp == nil || (resp.StatusCode != 200 && resp.StatusCode != 204) { + if resp != nil { + return fmt.Errorf("got status %d, expected 200/204", resp.StatusCode) + } + return err + } + defer resp.Body.Close() + + return nil +} diff --git a/extension/notification/discord/discord_test.go b/extension/notification/discord/discord_test.go new file mode 100644 index 00000000..37129201 --- /dev/null +++ b/extension/notification/discord/discord_test.go @@ -0,0 +1,59 @@ +package discord + +import ( + "io" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" + + "github.com/keel-hq/keel/types" +) + +func TestDiscordWebhookRequest(t *testing.T) { + currentTime := time.Now() + handler := func(resp http.ResponseWriter, req *http.Request) { + body, err := io.ReadAll(req.Body) + if err != nil { + t.Errorf("failed to parse body: %s", err) + } + + bodyStr := string(body) + + if !strings.Contains(bodyStr, types.NotificationPreDeploymentUpdate.String()) { + t.Errorf("missing deployment type") + } + + if !strings.Contains(bodyStr, "debug") { + t.Errorf("missing level") + } + + if !strings.Contains(bodyStr, "update deployment") { + t.Errorf("missing name") + } + if !strings.Contains(bodyStr, "message here") { + t.Errorf("missing message") + } + + t.Log(bodyStr) + + } + + // create test server with handler + ts := httptest.NewServer(http.HandlerFunc(handler)) + defer ts.Close() + + s := &sender{ + endpoint: ts.URL, + client: &http.Client{}, + } + + s.Send(types.EventNotification{ + Name: "update deployment", + Message: "message here", + CreatedAt: currentTime, + Type: types.NotificationPreDeploymentUpdate, + Level: types.LevelDebug, + }) +} From 52af7e9650be3dd8221f7817c58ea0c0c7752383 Mon Sep 17 00:00:00 2001 From: hotpheex Date: Tue, 8 Aug 2023 19:31:15 -0700 Subject: [PATCH 2/3] New notification sender for Discord webhooks --- extension/notification/discord/discord.go | 33 +++++++++++++------ .../notification/discord/discord_test.go | 1 - 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/extension/notification/discord/discord.go b/extension/notification/discord/discord.go index 1a80a5c0..5af00a06 100644 --- a/extension/notification/discord/discord.go +++ b/extension/notification/discord/discord.go @@ -29,11 +29,12 @@ type Config struct { } func init() { - log.Error("RUNNING") + log.Info(0) notification.RegisterSender("discord", &sender{}) } func (s *sender) Configure(config *notification.Config) (bool, error) { + log.Info(1) // Get configuration var httpConfig Config @@ -42,7 +43,6 @@ func (s *sender) Configure(config *notification.Config) (bool, error) { } else { return false, nil } - // Validate endpoint URL. if httpConfig.Endpoint == "" { return false, nil @@ -62,23 +62,36 @@ func (s *sender) Configure(config *notification.Config) (bool, error) { "name": "discord", "endpoint": s.endpoint, }).Info("extension.notification.discord: sender configured") - + log.Info(2) return true, nil } -// type notificationEnvelope struct { -// types.EventNotification -// } - type DiscordMessage struct { - Content string `json:"content"` - Username string `json:"username"` + Username string `json:"username"` + Content string `json:"content"` + Embeds []Embed `json:"embeds"` +} + +type Embed struct { + Title string `json:"title"` + Description string `json:"description"` + Footer Footer `json:"footer"` +} + +type Footer struct { + Text string `json:"text"` } func (s *sender) Send(event types.EventNotification) error { discordMessage := DiscordMessage{ - Content: fmt.Sprintf("**%s**\n%s", event.Name, event.Message), Username: "Keel", + Embeds: []Embed{ + { + Title: fmt.Sprintf("%s: %s", event.Type.String(), event.Name), + Description: event.Message, + Footer: Footer{Text: event.Level.String()}, + }, + }, } jsonMessage, err := json.Marshal(discordMessage) diff --git a/extension/notification/discord/discord_test.go b/extension/notification/discord/discord_test.go index 37129201..0e05cb44 100644 --- a/extension/notification/discord/discord_test.go +++ b/extension/notification/discord/discord_test.go @@ -37,7 +37,6 @@ func TestDiscordWebhookRequest(t *testing.T) { } t.Log(bodyStr) - } // create test server with handler From 23bd0a10848208b131acf30eb8cfc854b76e7a73 Mon Sep 17 00:00:00 2001 From: hotpheex Date: Tue, 8 Aug 2023 19:33:18 -0700 Subject: [PATCH 3/3] Update helm chart --- chart/keel/templates/secret.yaml | 3 +++ chart/keel/values.yaml | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/chart/keel/templates/secret.yaml b/chart/keel/templates/secret.yaml index 80fdde87..4151d950 100644 --- a/chart/keel/templates/secret.yaml +++ b/chart/keel/templates/secret.yaml @@ -27,6 +27,9 @@ data: {{- if .Values.teams.enabled }} TEAMS_WEBHOOK_URL: {{ .Values.teams.webhookUrl | b64enc }} {{- end }} +{{- if .Values.discord.enabled }} + DISCORD_WEBHOOK_URL: {{ .Values.discord.webhookUrl | b64enc }} +{{- end }} {{- if and .Values.mail.enabled .Values.mail.smtp.pass }} MAIL_SMTP_PASS: {{ .Values.mail.smtp.pass | b64enc }} {{- end }} diff --git a/chart/keel/values.yaml b/chart/keel/values.yaml index bc028632..79aa3d6f 100644 --- a/chart/keel/values.yaml +++ b/chart/keel/values.yaml @@ -93,6 +93,11 @@ teams: enabled: false webhookUrl: "" +# Discord notifications +discord: + enabled: false + webhookUrl: "" + # Mail notifications mail: enabled: false