From d75f3e018ccb5e0b6c617c8d4fadadd2596424ea Mon Sep 17 00:00:00 2001 From: Ernesto Serrano Date: Mon, 9 Sep 2019 13:56:17 +0200 Subject: [PATCH] Added hangouts chat notification support --- docs/notifications.md | 14 ++++++ internal/flags/flags.go | 8 +++- pkg/notifications/hangouts.go | 86 +++++++++++++++++++++++++++++++++++ pkg/notifications/notifier.go | 2 + 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 pkg/notifications/hangouts.go diff --git a/docs/notifications.md b/docs/notifications.md index 5741566..9382968 100644 --- a/docs/notifications.md +++ b/docs/notifications.md @@ -8,6 +8,7 @@ The types of notifications to send are passed via the comma-separated option `-- - `slack` to send notifications through a Slack webhook - `msteams` to send notifications via MSTeams webhook - `gotify` to send notifications via Gotify +- `hangouts` to send notifications via Hangouts Chat webhook ## Settings @@ -108,3 +109,16 @@ docker run -d \ -e WATCHTOWER_NOTIFICATION_GOTIFY_TOKEN="SuperSecretToken" \ containrrr/watchtower ``` + +### Hangouts Chat + +To push a notification to a Hangouts Chat channel, create a new [channel webhoook](https://developers.google.com/hangouts/chat/how-tos/webhooks) and set the webhook URL: + +```bash +docker run -d \ + --name watchtower \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -e WATCHTOWER_NOTIFICATIONS=hangouts \ + -e WATCHTOWER_NOTIFICATION_HANGOUTS_CHAT_WEBHOOK_URL="https://chat.googleapis.com/v1/spaces/XXXXXXX/messages?key=YYYYYYYYY&token=ZZZZZZZZZZ" \ + containrrr/watchtower +``` diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 6e9ea55..fc92291 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -109,7 +109,7 @@ func RegisterNotificationFlags(rootCmd *cobra.Command) { "notifications", "n", viper.GetStringSlice("WATCHTOWER_NOTIFICATIONS"), - " notification types to send (valid: email, slack, msteams, gotify)") + " notification types to send (valid: email, slack, msteams, gotify, hangouts)") flags.StringP( "notifications-level", @@ -220,6 +220,12 @@ Should only be used for testing. "", viper.GetString("WATCHTOWER_NOTIFICATION_GOTIFY_TOKEN"), "The Gotify Application required to query the Gotify API") + + flags.StringP( + "notification-hangouts-url", + "", + viper.GetString("WATCHTOWER_NOTIFICATION_HANGOUTS_CHAT_WEBHOOK_URL"), + "The Hangouts Chat Webhook Channel URL to send notifications to") } // SetDefaults provides default values for environment variables diff --git a/pkg/notifications/hangouts.go b/pkg/notifications/hangouts.go new file mode 100644 index 0000000..18c2fa4 --- /dev/null +++ b/pkg/notifications/hangouts.go @@ -0,0 +1,86 @@ +package notifications + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "strings" + + t "github.com/containrrr/watchtower/pkg/types" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +const ( + hangoutsType = "hangouts" +) + +type hangoutsTypeNotifier struct { + hangoutsURL string + logLevels []log.Level +} + +func newHangoutsNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Notifier { + flags := c.PersistentFlags() + + hangoutsURL, _ := flags.GetString("notification-hangouts-url") + if len(hangoutsURL) < 1 { + log.Fatal("Required argument --notification-hangouts-url(cli) or WATCHTOWER_NOTIFICATION_HANGOUTS_CHAT_WEBHOOK_URL(env) is empty.") + } else if !(strings.HasPrefix(hangoutsURL, "http://") || strings.HasPrefix(hangoutsURL, "https://")) { + log.Fatal("Hangouts URL must start with \"http://\" or \"https://\"") + } else if strings.HasPrefix(hangoutsURL, "http://") { + log.Warn("Using an HTTP url for Hangouts is insecure") + } + + n := &hangoutsTypeNotifier{ + hangoutsURL: hangoutsURL, + logLevels: acceptedLogLevels, + } + + log.AddHook(n) + + return n +} + +func (n *hangoutsTypeNotifier) StartNotification() {} + +func (n *hangoutsTypeNotifier) SendNotification() {} + +func (n *hangoutsTypeNotifier) Levels() []log.Level { + return n.logLevels +} + +func (n *hangoutsTypeNotifier) getURL() string { + return n.hangoutsURL +} + +func (n *hangoutsTypeNotifier) Fire(entry *log.Entry) error { + + go func() { + jsonBody, err := json.Marshal(hangoutsMessage{ + Text: "(" + entry.Level.String() + "): " + entry.Message, + }) + if err != nil { + fmt.Println("Failed to create JSON body for Hangouts 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 Hangouts notification: ", err) + } + defer resp.Body.Close() + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + fmt.Printf("Hangouts notification returned %d HTTP status code", resp.StatusCode) + } + + }() + return nil +} + +type hangoutsMessage struct { + Text string `json:"text"` +} diff --git a/pkg/notifications/notifier.go b/pkg/notifications/notifier.go index 2f25824..422217c 100644 --- a/pkg/notifications/notifier.go +++ b/pkg/notifications/notifier.go @@ -40,6 +40,8 @@ func NewNotifier(c *cobra.Command) *Notifier { tn = newMsTeamsNotifier(c, acceptedLogLevels) case gotifyType: tn = newGotifyNotifier(c, acceptedLogLevels) + case hangoutsType: + tn = newHangoutsNotifier(c, acceptedLogLevels) default: log.Fatalf("Unknown notification type %q", t) }