Skip to content

Commit 0c65b1e

Browse files
committed
Inital commit
Signed-off-by: Christian König <[email protected]>
0 parents  commit 0c65b1e

15 files changed

+459
-0
lines changed

.devcontainer/Dockerfile

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
From golang:1.21-alpine3.18
2+
RUN apk add --no-cache \
3+
git
4+
5+
ARG USERNAME=vscode
6+
ARG USER_UID=1000
7+
ARG USER_GID=1000
8+
9+
# Setup user
10+
RUN adduser $USERNAME -s /bin/sh -D -u $USER_UID $USER_GID && \
11+
mkdir -p /etc/sudoers.d && \
12+
echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME && \
13+
chmod 0440 /etc/sudoers.d/$USERNAME
14+
15+
USER $USERNAME

.devcontainer/devcontainer.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "Go",
3+
"dockerFile": "Dockerfile",
4+
// Use 'postCreateCommand' to run commands after the container is created.
5+
//"postCreateCommand": "go version",
6+
"customizations": {
7+
// Configure properties specific to VS Code.
8+
"vscode": {
9+
"settings": {},
10+
"extensions": [
11+
"ms-vscode.go"
12+
]
13+
}
14+
},
15+
"mounts": [
16+
"type=bind,source=/home/${localEnv:USER}/.ssh,target=/root/.ssh,readonly"
17+
]
18+
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
19+
// "remoteUser": "root"
20+
}

.dockerignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/src/docker-event-monitor

.editorconfig

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# This file is for unifying the coding style for different editors and IDEs.
2+
# More information at http://editorconfig.org
3+
4+
root = true
5+
6+
[*]
7+
charset = utf-8
8+
indent_size = 2
9+
indent_style = space
10+
end_of_line = lf
11+
insert_final_newline = true
12+
trim_trailing_whitespace = true
13+
14+
[*.go]
15+
indent_style = tab

.github/dependabot.yml

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: github-actions
4+
directory: "/"
5+
schedule:
6+
interval: weekly
7+
day: sunday
8+
time: "10:00"
9+
open-pull-requests-limit: 10
10+
target-branch: development
11+
- package-ecosystem: gomod
12+
directory: "/"
13+
schedule:
14+
interval: weekly
15+
day: sunday
16+
time: "10:00"
17+
open-pull-requests-limit: 10
18+
target-branch: development

.github/workflows/ghcr.yml

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Build and publish Docker image
2+
3+
on:
4+
workflow_dispatch:
5+
pull_request:
6+
7+
jobs:
8+
build-and-push:
9+
runs-on: ubuntu-latest
10+
permissions:
11+
contents: read
12+
packages: write
13+
steps:
14+
- name: Checkout Code
15+
uses: actions/[email protected]
16+
17+
- name: Set up Docker Buildx
18+
uses: docker/[email protected]
19+
20+
- name: Extract metadata (tags, labels) for Docker
21+
id: meta
22+
uses: docker/metadata-action@v4
23+
with:
24+
images: |
25+
ghcr.io/${{ github.repository_owner }}/${{ github.repository }}
26+
tags: |
27+
type=sha
28+
# set latest tag for master branch
29+
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'master') }}
30+
31+
- name: Login to GitHub Container Registry
32+
if: github.event_name != 'pull_request'
33+
uses: docker/[email protected]
34+
with:
35+
registry: ghcr.io
36+
username: ${{ github.repository_owner }}
37+
password: ${{ secrets.GITHUB_TOKEN }}
38+
39+
- name: Build and push Docker image
40+
uses: docker/[email protected]
41+
with:
42+
context: .
43+
push: ${{ github.event_name != 'pull_request' }}
44+
platforms: linux/amd64,linux/arm/v7,linux/arm/v6,linux/arm64/v8
45+
tags: ${{ steps.meta.outputs.tags }}
46+
labels: ${{ steps.meta.outputs.labels }}
47+
cache-from: type=gha
48+
cache-to: type=gha
49+
provenance: false

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
docker-event-monitor

Dockerfile

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
From golang:1.21-alpine3.18 as builder
2+
3+
COPY /src /src
4+
WORKDIR /src
5+
RUN go mod download
6+
RUN CGO_ENABLED=0 go build -ldflags "-s -w" docker-event-monitor.go
7+
8+
From scratch as deploy
9+
COPY --from=builder /src/docker-event-monitor docker-event-monitor
10+
# the tls certificates:
11+
# this pulls directly from the upstream image, which already has ca-certificates:
12+
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
13+
ENTRYPOINT ["/docker-event-monitor"]

docker-compose.yml

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
version: '2.4'
2+
3+
services:
4+
docker-event-monitor:
5+
container_name: docker-event-monitor
6+
image: docker-event-monitor:local
7+
volumes:
8+
- /var/run/docker.sock:/var/run/docker.sock:ro
9+
- /etc/localtime:/etc/localtime:ro
10+
restart: unless-stopped
11+
environment:
12+
PUSHOVER_USER: 'USER'
13+
PUSHOVER_APITOKEN: 'TOKEN'
14+
PUSHOVER_DELAY: 500
15+
#FILTERS: 'filters={"event": ["start","stop"],"type": ["container"]}'
16+

legacy/Dockerfile

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM alpine:3.18
2+
RUN apk add --no-cache \
3+
bash \
4+
curl \
5+
jq
6+
7+
COPY docker-event-monitor.sh ./docker-event-monitor.sh
8+
9+
CMD ["/bin/bash", "docker-event-monitor.sh"]

legacy/docker-compose.yml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
version: '2.4'
2+
3+
services:
4+
docker-event-monitor:
5+
container_name: docker-event-monitor
6+
image: dem:local
7+
volumes:
8+
- /var/run/docker.sock:/var/run/docker.sock:ro
9+
- /etc/localtime:/etc/localtime:ro
10+
restart: unless-stopped
11+
environment:
12+
PUSHOVER_USER: 'USER'
13+
PUSHOVER_TOKEN: 'TOKEN'
14+
FILTERS: 'filters={"event": ["start","stop"],"type": ["container"]}'
15+

legacy/docker-event-monitor.sh

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/bin/bash
2+
3+
# The following variables need to be set via docker environment variables
4+
# PUSHOVER_TOKEN, PUSHOVER_USER and FILTERS
5+
6+
pushover(){
7+
local titel=$1 message=$2
8+
9+
curl -s -F "token=${PUSHOVER_TOKEN}" \
10+
-F "user=${PUSHOVER_USER}" \
11+
-F "title=$titel" \
12+
-F "message=$message" https://api.pushover.net/1/messages.json\
13+
-o /dev/null # omit response
14+
}
15+
16+
17+
# Monitor all Docker events
18+
curl --no-buffer\
19+
--silent\
20+
--request GET "http://localhost/events"\
21+
--unix-socket /var/run/docker.sock \
22+
--get --data-urlencode "${FILTERS}" | while read -r event
23+
do
24+
mapfile -t data <<<"$(echo "${event}" | jq --raw-output '.id, .status, .from, .time')"
25+
container_id="${data[0]:0:8}" #limit container id to 8 characters
26+
status="${data[1]}"
27+
image="${data[2]##*/}" # remove everything before the final '/' if exists
28+
time="$(date -d @"${data[3]}" '+%x %X')" # convert unix timestamp to local date + time
29+
echo "Container ${container_id} from image ${image} with new status: ${status}" # log to docker logs
30+
pushover "Container ${container_id} from image ${image}" "${time} Status: ${status}"
31+
done
32+

src/docker-event-monitor.go

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"time"
8+
9+
"github.com/docker/docker/api/types"
10+
"github.com/docker/docker/client"
11+
"github.com/docker/docker/api/types/filters"
12+
"github.com/docker/docker/api/types/events"
13+
14+
"github.com/gregdel/pushover"
15+
log "github.com/sirupsen/logrus"
16+
"github.com/jnovack/flag"
17+
)
18+
19+
// todo
20+
// Argument Parsing - filters
21+
// memory leak?
22+
// generalize to all event/typs
23+
24+
// Global variables
25+
var PUSHOVER_APITOKEN, PUSHOVER_USER string
26+
var PUSHOVER_DELAY int64
27+
28+
func sendPushover(message,title string) bool {
29+
30+
31+
// Create a new pushover app with a API token
32+
app := pushover.New(PUSHOVER_APITOKEN)
33+
34+
// Create a new recipient (user key)
35+
recipient := pushover.NewRecipient(PUSHOVER_USER)
36+
37+
// Create the message to send
38+
pushmessage := pushover.NewMessageWithTitle(message,title)
39+
40+
// Send the message to the recipient
41+
response, err := app.SendMessage(pushmessage, recipient)
42+
if err != nil {
43+
log.Panic(err)
44+
}
45+
if response != nil {
46+
log.Debugf("%s", response)
47+
}
48+
49+
if (*response).Status == 1 {
50+
// Pushover returns 1 if the message request to the API was valid
51+
// https://pushover.net/api#response
52+
return true
53+
}
54+
// default to false if response Status !=1
55+
return false
56+
}
57+
58+
func processEvent(event events.Message) {
59+
// the Docker Events endpoint will return a struct events.Message
60+
// https://pkg.go.dev/github.com/docker/docker/api/types/events#Message
61+
62+
timestamp := time.Unix(event.Time, 0).Format("02-01-2006 15:04:05")
63+
status := event.Status
64+
from := event.From
65+
ID := event.ID[:8]
66+
67+
message := fmt.Sprintf("%s New status: %s",timestamp, status)
68+
title := fmt.Sprintf("Container %s from image %s",ID, from)
69+
70+
log.Infof("Container %s from image %s with new status: %s",ID,from,status)
71+
72+
73+
delivered := sendPushover(message,title)
74+
if delivered == true {
75+
log.Debugf("Message delivered")
76+
} else {
77+
log.Warnf("Message not delivered")
78+
}
79+
}
80+
81+
func init() {
82+
// Output to stdout instead of the default stderr
83+
// Can be any io.Writer, see below for File example
84+
log.SetOutput(os.Stdout)
85+
86+
// Set log level - defaults to log.InfoLevel)
87+
//log.SetLevel(log.DebugLevel)
88+
89+
// Parse flags/config/env variables
90+
flag.StringVar(&PUSHOVER_APITOKEN, "PUSHOVER_APITOKEN", "", "Pushover's API token")
91+
flag.StringVar(&PUSHOVER_USER, "PUSHOVER_USER", "", "Pushover's user key")
92+
flag.Int64Var(&PUSHOVER_DELAY, "PUSHOVER_DELAY",0, "Delay before sending next Pushover message")
93+
flag.Parse()
94+
}
95+
96+
func main() {
97+
// set filters
98+
// https://docs.docker.com/engine/reference/commandline/events/
99+
filter := filters.NewArgs()
100+
filter.Add("type", "container")
101+
filter.Add("event", "start")
102+
filter.Add("event", "stop")
103+
filter.Add("event", "create")
104+
filter.Add("event", "die")
105+
filter.Add("event", "kill")
106+
107+
log.Infof("Starting docker event monitor")
108+
log.Infof("Using Pushover API Token: %s", PUSHOVER_APITOKEN)
109+
log.Infof("Using Pushover User Key %s", PUSHOVER_USER)
110+
log.Infof("Using Pushover delay of %d ms", PUSHOVER_DELAY)
111+
112+
sendPushover(time.Now().Format("02-01-2006 15:04:05"), "Starting docker event monitor")
113+
114+
115+
cli, err := client.NewClientWithOpts(client.FromEnv)
116+
if err != nil {
117+
log.Panic(err)
118+
}
119+
120+
// receives events
121+
event_chan, errs := cli.Events(context.Background(), types.EventsOptions{Filters: filter})
122+
123+
124+
for {
125+
select {
126+
case err := <-errs:log.Panic(err)
127+
case event := <-event_chan:
128+
processEvent(event)
129+
// Adding a small configurable delay here
130+
// Sometimes events are pushed through the channel really quickly, but
131+
// they arrive on the Pushover clients in wrong order (probably due to message delivery time)
132+
// Consuming the events with a small delay solves the issue
133+
time.Sleep(time.Duration(PUSHOVER_DELAY) * time.Millisecond)
134+
}
135+
}
136+
}

src/go.mod

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
module docker-event-monitor
2+
3+
go 1.21.0
4+
5+
require (
6+
github.com/docker/docker v24.0.5+incompatible
7+
github.com/gregdel/pushover v1.2.1
8+
github.com/sirupsen/logrus v1.9.3
9+
)
10+
11+
require (
12+
github.com/Microsoft/go-winio v0.6.1 // indirect
13+
github.com/docker/distribution v2.8.2+incompatible // indirect
14+
github.com/docker/go-connections v0.4.0 // indirect
15+
github.com/docker/go-units v0.5.0 // indirect
16+
github.com/gogo/protobuf v1.3.2 // indirect
17+
github.com/jnovack/flag v1.16.0 // indirect
18+
github.com/moby/term v0.5.0 // indirect
19+
github.com/morikuni/aec v1.0.0 // indirect
20+
github.com/opencontainers/go-digest v1.0.0 // indirect
21+
github.com/opencontainers/image-spec v1.0.2 // indirect
22+
github.com/pkg/errors v0.9.1 // indirect
23+
github.com/stretchr/testify v1.8.4 // indirect
24+
golang.org/x/mod v0.12.0 // indirect
25+
golang.org/x/net v0.14.0 // indirect
26+
golang.org/x/sys v0.11.0 // indirect
27+
golang.org/x/time v0.3.0 // indirect
28+
golang.org/x/tools v0.12.0 // indirect
29+
gotest.tools/v3 v3.5.0 // indirect
30+
)

0 commit comments

Comments
 (0)