Skip to content

Commit

Permalink
Merge pull request #100 from yubiuser/development
Browse files Browse the repository at this point in the history
v2.4.0
  • Loading branch information
yubiuser authored Mar 16, 2024
2 parents e9663b3 + 1cc506b commit 21b5445
Show file tree
Hide file tree
Showing 10 changed files with 191 additions and 77 deletions.
Binary file modified .github/pushover.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 10 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ Monitor Docker events and send push notifications for each event.
- Pushover notification
- Gotify notification
- E-Mail notification (SMTP)
- Filter events
- Mattermost notifications (via Incoming Webhooks)
- Filter and exclude events

## Background

Expand Down Expand Up @@ -60,6 +61,10 @@ services:
MAIL_PASSWORD: 'PASSWORD'
MAIL_PORT: 587
MAIL_HOST: 'smtp@provider.com'
MATTERMOST: false
MATTERMOST_URL: 'URL'
MATTERMOST_CHANNEL: 'Channel'
MATTERMOST_USER: 'User'
FILTER: 'type=container'
EXCLUDE: 'Action=exec_start,Action=exec_die,Action=exec_create'
DELAY: '500ms'
Expand Down Expand Up @@ -100,6 +105,10 @@ Configurations can use the CLI flags or environment variables. The table below o
| `--mailpassword` | `MAIL_PASSWORD` | `""` | |
| `--mailport` | `MAIL_PORT` | `587` | |
| `--mailhost` | `MAIL_HOST` | `""` | `smtp@provider.com` |
| `--mattermost` | `MATTERMOST` | `false` | Enable/Disable Mattermost notification|
| `--mattermosturl` | `MATTERMOST_URL` | `""` | |
| `--mattermostchannel` | `MATTERMOST_CHANNEL` | `""` | optional |
| `--mattermostuser` | `MATTERMOST_USER` | `"Docker Event Monitor"` | |
| `--filter` | `FILTER` | `""` | Filter events. Uses the same filters as `docker events` (see [here](https://docs.docker.com/engine/reference/commandline/events/#filter)) |
| `--exclude` | `EXCLUDE` | `""` | Exclude events from being reported |
| `--loglevel` | `LOG_LEVEL` | `"info"`| Use `debug` for more verbose logging |
Expand Down
1 change: 0 additions & 1 deletion src/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ go 1.21
require (
github.com/alexflint/go-arg v1.4.3
github.com/docker/docker v25.0.4+incompatible
github.com/gregdel/pushover v1.3.0
github.com/rs/zerolog v1.32.0
golang.org/x/text v0.14.0
)
Expand Down
2 changes: 0 additions & 2 deletions src/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gregdel/pushover v1.3.0 h1:CewbxqsThoN/1imgwkDKFkRkltaQMoyBV0K9IquQLtw=
github.com/gregdel/pushover v1.3.0/go.mod h1:EcaO66Nn1StkpEm1iKtBTV3d2A16SoMsVER1PthX7to=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
Expand Down
37 changes: 13 additions & 24 deletions src/gotify.go
Original file line number Diff line number Diff line change
@@ -1,39 +1,28 @@
package main

import (
"io"
"net/http"
"net/url"
"encoding/json"
)

type GotifyMessage struct {
Title string `json:"title"`
Message string `json:"message"`
}

func sendGotify(message string, title string) {
// Send a message to Gotify

response, err := http.PostForm(glb_arguments.GotifyURL+"/message?token="+glb_arguments.GotifyToken,
url.Values{"message": {message}, "title": {title}})
if err != nil {
logger.Error().Err(err).Str("reporter", "Gotify").Msg("")
return
m := GotifyMessage{
Title: title,
Message: message,
}

defer response.Body.Close()

statusCode := response.StatusCode

body, err := io.ReadAll(response.Body)
messageJSON, err := json.Marshal(m)
if err != nil {
logger.Error().Err(err).Str("reporter", "Gotify").Msg("")
logger.Error().Err(err).Str("reporter", "Gotify").Msg("Faild to marshal JSON")
return
}

logger.Debug().Str("reporter", "Gotify").Msgf("Gotify response statusCode: %d", statusCode)
logger.Debug().Str("reporter", "Gotify").Msgf("Gotify response body: %s", string(body))

// Log non successfull status codes
if statusCode == 200 {
logger.Debug().Str("reporter", "Gotify").Msgf("Gotify message delivered")
} else {
logger.Error().Str("reporter", "Gotify").Msgf("Pushing gotify message failed.")
logger.Error().Str("reporter", "Gotify").Msgf("Gotify response body: %s", string(body))
}
sendhttpMessage("Gotify", glb_arguments.GotifyURL+"/message?token="+glb_arguments.GotifyToken, messageJSON)

}
51 changes: 30 additions & 21 deletions src/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,31 @@ import (
)

type args struct {
Pushover bool `arg:"env:PUSHOVER" default:"false" help:"Enable/Disable Pushover Notification (True/False)"`
PushoverAPIToken string `arg:"env:PUSHOVER_APITOKEN" help:"Pushover's API Token/Key"`
PushoverUserKey string `arg:"env:PUSHOVER_USER" help:"Pushover's User Key"`
Gotify bool `arg:"env:GOTIFY" default:"false" help:"Enable/Disable Gotify Notification (True/False)"`
GotifyURL string `arg:"env:GOTIFY_URL" help:"URL of your Gotify server"`
GotifyToken string `arg:"env:GOTIFY_TOKEN" help:"Gotify's App Token"`
Mail bool `arg:"env:MAIL" default:"false" help:"Enable/Disable Mail (SMTP) Notification (True/False)"`
MailFrom string `arg:"env:MAIL_FROM" help:"your.username@provider.com"`
MailTo string `arg:"env:MAIL_TO" help:"recipient@provider.com"`
MailUser string `arg:"env:MAIL_USER" help:"SMTP Username"`
MailPassword string `arg:"env:MAIL_PASSWORD" help:"SMTP Password"`
MailPort int `arg:"env:MAIL_PORT" default:"587" help:"SMTP Port"`
MailHost string `arg:"env:MAIL_HOST" help:"SMTP Host"`
Delay time.Duration `arg:"env:DELAY" default:"500ms" help:"Delay before next message is send"`
FilterStrings []string `arg:"env:FILTER,--filter,separate" help:"Filter docker events using Docker syntax."`
Filter map[string][]string `arg:"-"`
ExcludeStrings []string `arg:"env:EXCLUDE,--exclude,separate" help:"Exclude docker events using Docker syntax."`
Exclude map[string][]string `arg:"-"`
LogLevel string `arg:"env:LOG_LEVEL" default:"info" help:"Set log level. Use debug for more logging."`
ServerTag string `arg:"env:SERVER_TAG" help:"Prefix to include in the title of notifications. Useful when running docker-event-monitors on multiple machines."`
Version bool `arg:"-v" help:"Print version information."`
Pushover bool `arg:"env:PUSHOVER" default:"false" help:"Enable/Disable Pushover Notification (True/False)"`
PushoverAPIToken string `arg:"env:PUSHOVER_APITOKEN" help:"Pushover's API Token/Key"`
PushoverUserKey string `arg:"env:PUSHOVER_USER" help:"Pushover's User Key"`
Gotify bool `arg:"env:GOTIFY" default:"false" help:"Enable/Disable Gotify Notification (True/False)"`
GotifyURL string `arg:"env:GOTIFY_URL" help:"URL of your Gotify server"`
GotifyToken string `arg:"env:GOTIFY_TOKEN" help:"Gotify's App Token"`
Mail bool `arg:"env:MAIL" default:"false" help:"Enable/Disable Mail (SMTP) Notification (True/False)"`
MailFrom string `arg:"env:MAIL_FROM" help:"your.username@provider.com"`
MailTo string `arg:"env:MAIL_TO" help:"recipient@provider.com"`
MailUser string `arg:"env:MAIL_USER" help:"SMTP Username"`
MailPassword string `arg:"env:MAIL_PASSWORD" help:"SMTP Password"`
MailPort int `arg:"env:MAIL_PORT" default:"587" help:"SMTP Port"`
MailHost string `arg:"env:MAIL_HOST" help:"SMTP Host"`
Mattermost bool `arg:"env:MATTERMOST" default:"false" help:"Enable/Disable Mattermost Notification (True/False)"`
MattermostURL string `arg:"env:MATTERMOST_URL" help:"URL of your Mattermost incoming webhook"`
MattermostChannel string `arg:"env:MATTERMOST_CHANNEL" help:"Mattermost channel to post in"`
MattermostUser string `arg:"env:MATTERMOST_USER" default:"Docker Event Monitor" help:"Mattermost user to post as"`
Delay time.Duration `arg:"env:DELAY" default:"500ms" help:"Delay before next message is send"`
FilterStrings []string `arg:"env:FILTER,--filter,separate" help:"Filter docker events using Docker syntax."`
Filter map[string][]string `arg:"-"`
ExcludeStrings []string `arg:"env:EXCLUDE,--exclude,separate" help:"Exclude docker events using Docker syntax."`
Exclude map[string][]string `arg:"-"`
LogLevel string `arg:"env:LOG_LEVEL" default:"info" help:"Set log level. Use debug for more logging."`
ServerTag string `arg:"env:SERVER_TAG" help:"Prefix to include in the title of notifications. Useful when running docker-event-monitors on multiple machines."`
Version bool `arg:"-v" help:"Print version information."`
}

// Creating a global logger
Expand Down Expand Up @@ -90,6 +94,11 @@ func init() {
logger.Fatal().Msg("E-Mail notification enabled. SMTP host address required!")
}
}
if glb_arguments.Mattermost {
if len(glb_arguments.MattermostURL) == 0 {
logger.Fatal().Msg("Mattermost enabled. Mattermost URL required!")
}
}
}

func main() {
Expand Down
33 changes: 33 additions & 0 deletions src/mattermost.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package main

import (
"encoding/json"
)

// Adapted from https://github.com/mdeheij/mattergo

// Message is a chat message to be sent using a webhook
type MattermostMessage struct {
Username string `json:"username"`
Channel string `json:"channel"`
Text string `json:"text"`
}

// Send a message to a Mattermost chat channel
func sendMattermost(message string, title string) {

m := MattermostMessage{
Username: glb_arguments.MattermostUser,
Channel: glb_arguments.MattermostChannel,
Text: "##### " + title + "\n" + message,
}

messageJSON, err := json.Marshal(m)
if err != nil {
logger.Error().Err(err).Str("reporter", "Mattermost").Msg("Faild to marshal JSON")
return
}

sendhttpMessage("Mattermost", glb_arguments.MattermostURL, messageJSON)

}
60 changes: 59 additions & 1 deletion src/notifications.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package main

import (
"bytes"
"io"
"net/http"
"sync"
"time"
)
Expand All @@ -21,7 +24,7 @@ func sendNotifications(timestamp time.Time, message string, title string) {
wg.Add(1)
go func() {
defer wg.Done()
sendPushover(message, title)
sendPushover(timestamp, message, title)
}()
}

Expand All @@ -40,6 +43,61 @@ func sendNotifications(timestamp time.Time, message string, title string) {
sendMail(timestamp, message, title)
}()
}

if glb_arguments.Mattermost {
wg.Add(1)
go func() {
defer wg.Done()
sendMattermost(message, title)
}()
}
wg.Wait()

}

func sendhttpMessage(reporter string, address string, messageJSON []byte) {

// Create request
req, err := http.NewRequest("POST", address, bytes.NewBuffer(messageJSON))
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
if err != nil {
logger.Error().Err(err).Str("reporter", reporter).Msg("Faild to build request")
return
}

// define custom httpClient with a default timeout
var netClient = &http.Client{
Timeout: time.Second * 10,
}

// Send request
resp, err := netClient.Do(req)
if err != nil {
logger.Error().Err(err).Str("reporter", reporter).Msg("Faild to send request")
return
}
defer resp.Body.Close()

statusCode := resp.StatusCode

respBody, err := io.ReadAll(resp.Body)
if err != nil {
logger.Error().Err(err).Str("reporter", reporter).Msg("")
return
}

// Log non successfull status codes
if statusCode == 200 {
logger.Debug().
Str("reporter", reporter).
Int("statusCode", statusCode).
Str("responseBody", string(respBody)).
Msg("Message delivered")
} else {
logger.Error().
Str("reporter", reporter).
Int("statusCode", statusCode).
Str("responseBody", string(respBody)).
Msg("Pushing message failed.")
}
}
49 changes: 25 additions & 24 deletions src/pushover.go
Original file line number Diff line number Diff line change
@@ -1,35 +1,36 @@
package main

import "github.com/gregdel/pushover"

func sendPushover(message string, title string) {
// Create a new pushover app with an API token
app := pushover.New(glb_arguments.PushoverAPIToken)

// Create a new recipient (user key)
recipient := pushover.NewRecipient(glb_arguments.PushoverUserKey)
import (
"encoding/json"
"strconv"
"time"
)

type PushoverMessage struct {
Token string `json:"token"`
User string `json:"user"`
Title string `json:"title"`
Message string `json:"message"`
Timestamp string `json:"timestamp"`
}

// Create the message to send
pushmessage := pushover.NewMessageWithTitle(message, title)
func sendPushover(timestamp time.Time, message string, title string) {
// Send a message to Pushover

// Send the message to the recipient
response, err := app.SendMessage(pushmessage, recipient)
if err != nil {
logger.Error().Err(err).Str("reporter", "Pushover").Msg("")
return
}
if response != nil {
logger.Debug().Str("reporter", "Pushover").Msgf("%s", response)
m := PushoverMessage{
Token: glb_arguments.PushoverAPIToken,
User: glb_arguments.PushoverUserKey,
Title: title,
Message: message,
Timestamp: strconv.FormatInt(timestamp.Unix(), 10),
}

if response.Status == 1 {
// Pushover returns 1 if the message request to the API was valid
// https://pushover.net/api#response
logger.Debug().Str("reporter", "Pushover").Msgf("Pushover message delivered")
messageJSON, err := json.Marshal(m)
if err != nil {
logger.Error().Err(err).Str("reporter", "Pushover").Msg("Faild to marshal JSON")
return
}

// if response Status !=1
logger.Error().Str("reporter", "Pushover").Msg("Pushover message not delivered")
sendhttpMessage("Pushover", "https://api.pushover.net/1/messages.json", messageJSON)

}
24 changes: 21 additions & 3 deletions src/startup.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,34 @@ func buildStartupMessage(timestamp time.Time) string {
startup_message_builder.WriteString("Docker event monitor version: " + version + "\n")

if glb_arguments.Pushover {
startup_message_builder.WriteString("Notify via Pushover, using API Token " + glb_arguments.PushoverAPIToken + " and user key " + glb_arguments.PushoverUserKey)
startup_message_builder.WriteString("Pushover notification enabled")
} else {
startup_message_builder.WriteString("Pushover notification disabled")
}

if glb_arguments.Gotify {
startup_message_builder.WriteString("\nNotify via Gotify, using URL " + glb_arguments.GotifyURL + " and APP Token " + glb_arguments.GotifyToken)
startup_message_builder.WriteString("\nGotify notification enabled")
} else {
startup_message_builder.WriteString("\nGotify notification disabled")
}
if glb_arguments.Mail {
startup_message_builder.WriteString("\nNotify via E-Mail from " + glb_arguments.MailFrom + " to " + glb_arguments.MailTo + " using host " + glb_arguments.MailHost + " and port " + strconv.Itoa(glb_arguments.MailPort))
startup_message_builder.WriteString("\nE-Mail notification enabled")
} else {
startup_message_builder.WriteString("\nE-Mail notification disabled")
}

if glb_arguments.Mattermost {
startup_message_builder.WriteString("\nMattermost notification enabled")
if glb_arguments.MattermostChannel != "" {
startup_message_builder.WriteString("\nMattermost channel: " + glb_arguments.MattermostChannel)
}
if glb_arguments.MattermostUser != "" {
startup_message_builder.WriteString("\nMattermost username: " + glb_arguments.MattermostUser)
}
} else {
startup_message_builder.WriteString("\nMattermost notification disabled")
}

if glb_arguments.Delay > 0 {
startup_message_builder.WriteString("\nUsing delay of " + glb_arguments.Delay.String())
} else {
Expand Down Expand Up @@ -81,6 +93,12 @@ func logArguments() {
Str("MailHost", glb_arguments.MailHost).
Str("MailUser", glb_arguments.MailUser).
Int("Port", glb_arguments.MailPort),
).
Dict("Mattermost", zerolog.Dict().
Bool("enabled", glb_arguments.Mattermost).
Str("MattermostURL", glb_arguments.MattermostURL).
Str("MattermostChannel", glb_arguments.MattermostChannel).
Str("MattermostUser", glb_arguments.MattermostUser),
),
).
Str("Delay", glb_arguments.Delay.String()).
Expand Down

0 comments on commit 21b5445

Please sign in to comment.