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

Ntfy notification support #747

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
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
5 changes: 5 additions & 0 deletions chart/keel/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ spec:
- name: WEBHOOK_ENDPOINT
value: "{{ .Values.webhook.endpoint }}"
{{- end }}
{{- if .Values.ntfy.enabled }}
# Enable webhook endpoint
- name: NTFY_WEBHOOK_URL
value: "{{ .Values.ntfy.webhookUrl }}"
{{- end }}
{{- if .Values.mattermost.enabled }}
# Enable mattermost endpoint
- name: MATTERMOST_ENDPOINT
Expand Down
5 changes: 5 additions & 0 deletions chart/keel/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ discord:
enabled: false
webhookUrl: ""

# ntfy notifications
ntfy:
enabled: false
webhookUrl: ""

# Mail notifications
mail:
enabled: false
Expand Down
1 change: 1 addition & 0 deletions cmd/keel/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import (
_ "github.com/keel-hq/keel/extension/notification/hipchat"
_ "github.com/keel-hq/keel/extension/notification/mail"
_ "github.com/keel-hq/keel/extension/notification/mattermost"
_ "github.com/keel-hq/keel/extension/notification/ntfy"
_ "github.com/keel-hq/keel/extension/notification/slack"
_ "github.com/keel-hq/keel/extension/notification/teams"
_ "github.com/keel-hq/keel/extension/notification/webhook"
Expand Down
3 changes: 3 additions & 0 deletions constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ const (
// Discord webhook url, see https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks
EnvDiscordWebhookUrl = "DISCORD_WEBHOOK_URL"

// ntfy webhook url, see https://docs.ntfy.sh/publish/
EnvNtfyWebhookUrl = "NTFY_WEBHOOK_URL"

// Mail notification settings
EnvMailTo = "MAIL_TO"
EnvMailFrom = "MAIL_FROM"
Expand Down
3 changes: 3 additions & 0 deletions deployment/deployment-template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ spec:
# Enable MS Teams webhook endpoint
- name: TEAMS_WEBHOOK_URL
value: "{{ .teams_webhook_url }}"
# Enable ntfy webhook endpoint
- name: NTFY_WEBHOOK_URL
value: "{{ .ntfy_webhook_url }}"
- name: SLACK_TOKEN
value: "{{ .slack_token }}"
- name: SLACK_CHANNELS
Expand Down
82 changes: 82 additions & 0 deletions extension/notification/ntfy/ntfy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package discord

import (
"fmt"
"net/http"
"net/url"
"os"
"strings"
"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() {
notification.RegisterSender("ntfy", &sender{})
}

func (s *sender) Configure(config *notification.Config) (bool, error) {
// Get configuration
var httpConfig Config

if os.Getenv(constants.EnvNtfyWebhookUrl) != "" {
httpConfig.Endpoint = os.Getenv(constants.EnvNtfyWebhookUrl)
} 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": "ntfy",
"endpoint": s.endpoint,
}).Info("extension.notification.ntfy: sender configured")
return true, nil
}

func (s *sender) Send(event types.EventNotification) error {

req, _ := http.NewRequest("POST", s.endpoint, strings.NewReader(fmt.Sprintf("%s: %s", event.Type.String(), event.Name)))
req.Header.Set("Title", event.Message)
req.Header.Set("Tags", "keel")
req.Header.Set("X-Icon", constants.KeelLogoURL)

resp, err := s.client.Do(req)
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
}
59 changes: 59 additions & 0 deletions extension/notification/ntfy/ntfy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package discord

import (
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"

"github.com/keel-hq/keel/types"
)

func TestNtfyWebhookRequest(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(req.Header.Get("Title"), "message here") {
t.Errorf("missing message")
}

if !strings.Contains(req.Header.Get("Tags"), "keel") {
t.Errorf("missing deployment type")
}

if !strings.Contains(bodyStr, "update deployment") {
t.Errorf("missing update deployment")
}

if !strings.Contains(bodyStr, types.NotificationPreDeploymentUpdate.String()) {
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,
})
}