Add support for Gotify notifications (#346)

This adds support for Gotify (https://gotify.net) notifications.

Work items:

* Two flags have been added to internal/flags/flags.go:
  "notification-gotify-url" and "notification-gotify-token".

* A Gotify notification driver has been added in
  notifications/gotify.go.

* "gotify" has been added to notification driver choices in
  notifications/notifier.go.

* Docs have been updated
pull/353/head
Luka Peschke 5 years ago committed by Simon Aronsson
parent d744b5ddf7
commit dff16dc639

@ -1,4 +1,4 @@
# Notifications
Watchtower can send notifications when containers are updated. Notifications are sent via hooks in the logging system, [logrus](http://github.com/sirupsen/logrus).
@ -7,6 +7,7 @@ The types of notifications to send are passed via the comma-separated option `--
- `email` to send notifications via e-mail
- `slack` to send notifications through a Slack webhook
- `msteams` to send notifications via MSTeams webhook
- `gotify` to send notifications via Gotify
## Settings
@ -90,3 +91,18 @@ docker run -d \
-e WATCHTOWER_NOTIFICATION_MSTEAMS_USE_LOG_DATA=true \
containrrr/watchtower
```
### Gotify
To push a notification to your Gotify instance, register a Gotify app and specify the Gotify URL and app token:
```bash
docker run -d \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
-e WATCHTOWER_NOTIFICATIONS=gotify \
-e WATCHTOWER_NOTIFICATION_GOTIFY_URL="https://my.gotify.tld/" \
-e WATCHTOWER_NOTIFICATION_GOTIFY_TOKEN="SuperSecretToken" \
containrrr/watchtower
```

@ -98,7 +98,7 @@ func RegisterNotificationFlags(rootCmd *cobra.Command) {
"notifications",
"n",
viper.GetStringSlice("WATCHTOWER_NOTIFICATIONS"),
" notification types to send (valid: email, slack, msteams")
" notification types to send (valid: email, slack, msteams, gotify)")
flags.StringP(
"notifications-level",
@ -192,6 +192,17 @@ Should only be used for testing.
"",
viper.GetBool("WATCHTOWER_NOTIFICATION_MSTEAMS_USE_LOG_DATA"),
"The MSTeams notifier will try to extract log entry fields as MSTeams message facts")
flags.StringP(
"notification-gotify-url",
"",
viper.GetString("WATCHTOWER_NOTIFICATION_GOTIFY_URL"),
"The Gotify URL to send notifications to")
flags.StringP(
"notification-gotify-token",
"",
viper.GetString("WATCHTOWER_NOTIFICATION_GOTIFY_TOKEN"),
"The Gotify Application required to query the Gotify API")
}
// SetDefaults provides default values for environment variables

@ -0,0 +1,100 @@
package notifications
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"strings"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
const (
gotifyType = "gotify"
)
type gotifyTypeNotifier struct {
gotifyURL string
gotifyAppToken string
logLevels []log.Level
}
func newGotifyNotifier(c *cobra.Command, acceptedLogLevels []log.Level) typeNotifier {
flags := c.PersistentFlags()
gotifyURL, _ := flags.GetString("notification-gotify-url")
if len(gotifyURL) < 1 {
log.Fatal("Required argument --notification-gotify-url(cli) or WATCHTOWER_NOTIFICATION_GOTIFY_URL(env) is empty.")
} else if !(strings.HasPrefix(gotifyURL, "http://") || strings.HasPrefix(gotifyURL, "https://")) {
log.Fatal("Gotify URL must start with \"http://\" or \"https://\"")
} else if strings.HasPrefix(gotifyURL, "http://") {
log.Warn("Using an HTTP url fpr Gotify is insecure")
}
gotifyToken, _ := flags.GetString("notification-gotify-token")
if len(gotifyToken) < 1 {
log.Fatal("Required argument --notification-gotify-token(cli) or WATCHTOWER_NOTIFICATION_GOTIFY_TOKEN(env) is empty.")
}
n := &gotifyTypeNotifier{
gotifyURL: gotifyURL,
gotifyAppToken: gotifyToken,
logLevels: acceptedLogLevels,
}
log.AddHook(n)
return n
}
func (n *gotifyTypeNotifier) StartNotification() {}
func (n *gotifyTypeNotifier) SendNotification() {}
func (n *gotifyTypeNotifier) Levels() []log.Level {
return n.logLevels
}
func (n *gotifyTypeNotifier) getURL() string {
url := n.gotifyURL
if !strings.HasSuffix(url, "/") {
url += "/"
}
return url + "message?token=" + n.gotifyAppToken
}
func (n *gotifyTypeNotifier) Fire(entry *log.Entry) error {
go func() {
jsonBody, err := json.Marshal(gotifyMessage{
Message: "(" + entry.Level.String() + "): " + entry.Message,
Title: "Watchtower",
Priority: 0,
})
if err != nil {
fmt.Println("Failed to create JSON body for Gotify notification: ", err)
return
}
jsonBodyBuffer := bytes.NewBuffer([]byte(jsonBody))
resp, err := http.Post(n.getURL(), "application/json", jsonBodyBuffer)
if err != nil {
fmt.Println("Failed to send Gotify notification: ", err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
fmt.Printf("Gotify notification returned %d HTTP status code", resp.StatusCode)
}
}()
return nil
}
type gotifyMessage struct {
Message string `json:"message"`
Title string `json:"title"`
Priority int `json:"priority"`
}

@ -38,6 +38,8 @@ func NewNotifier(c *cobra.Command) *Notifier {
tn = newSlackNotifier(c, acceptedLogLevels)
case msTeamsType:
tn = newMsTeamsNotifier(c, acceptedLogLevels)
case gotifyType:
tn = newGotifyNotifier(c, acceptedLogLevels)
default:
log.Fatalf("Unknown notification type %q", t)
}

Loading…
Cancel
Save