Skip to content

Commit e4258cb

Browse files
committed
feat: add email notification for failed and success backup
1 parent 4c44166 commit e4258cb

File tree

13 files changed

+475
-91
lines changed

13 files changed

+475
-91
lines changed

Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ ENV TZ=UTC
4747
ARG WORKDIR="/config"
4848
ARG BACKUPDIR="/backup"
4949
ARG BACKUP_TMP_DIR="/tmp/backup"
50+
ARG TEMPLATES_DIR="/config/templates"
5051
ARG appVersion="v1.2.12"
5152
ENV VERSION=${appVersion}
5253
LABEL author="Jonas Kaninda"
@@ -55,13 +56,15 @@ LABEL version=${appVersion}
5556
RUN apk --update add --no-cache mysql-client mariadb-connector-c tzdata
5657
RUN mkdir $WORKDIR
5758
RUN mkdir $BACKUPDIR
59+
RUN mkdir $TEMPLATES_DIR
5860
RUN mkdir -p $BACKUP_TMP_DIR
5961
RUN chmod 777 $WORKDIR
6062
RUN chmod 777 $BACKUPDIR
6163
RUN chmod 777 $BACKUP_TMP_DIR
6264
RUN chmod 777 $WORKDIR
6365

6466
COPY --from=build /app/mysql-bkup /usr/local/bin/mysql-bkup
67+
COPY ./templates/* $TEMPLATES_DIR/
6568
RUN chmod +x /usr/local/bin/mysql-bkup
6669

6770
RUN ln -s /usr/local/bin/mysql-bkup /usr/local/bin/bkup

docs/how-tos/receive-notification.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
---
2+
title: Receive notifications
3+
layout: default
4+
parent: How Tos
5+
nav_order: 12
6+
---
7+
Send Email or Telegram notifications on success or failed backup.
8+
9+
### Email
10+
To send out email notifications on failed backup runs, provide SMTP credentials, a sender and a recipient:
11+
12+
```yaml
13+
services:
14+
mysql-bkup:
15+
image: jkaninda/mysql-bkup
16+
container_name: mysql-bkup
17+
command: backup
18+
volumes:
19+
- ./backup:/backup
20+
environment:
21+
- DB_PORT=3306
22+
- DB_HOST=mysql
23+
- DB_NAME=database
24+
- DB_USERNAME=username
25+
- DB_PASSWORD=password
26+
- MAIL_HOST=
27+
- MAIL_PORT=587
28+
- MAIL_USERNAME=
29+
- MAIL_PASSWORD=!
30+
- MAIL_FROM=sender@example.com
31+
- MAIL_TO=me@example.com,team@example.com,manager@example.com
32+
- MAIL_SKIP_TLS=false
33+
networks:
34+
- web
35+
networks:
36+
web:
37+
```
38+
39+
### Telegram
40+
41+
```yaml
42+
services:
43+
mysql-bkup:
44+
image: jkaninda/mysql-bkup
45+
container_name: mysql-bkup
46+
command: backup
47+
volumes:
48+
- ./backup:/backup
49+
environment:
50+
- DB_PORT=3306
51+
- DB_HOST=mysql
52+
- DB_NAME=database
53+
- DB_USERNAME=username
54+
- DB_PASSWORD=password
55+
- TG_TOKEN=[BOT ID]:[BOT TOKEN]
56+
- TG_CHAT_ID=
57+
networks:
58+
- web
59+
networks:
60+
web:
61+
```
62+
63+
### Customize notifications
64+
65+
The body of the notifications can be tailored to your needs using Go templates.
66+
Template sources must be mounted inside the container in /config/templates:
67+
68+
- email.template: Email notification template
69+
- telegram.template: Telegram notification template
70+
- error.template: Error notification template
71+
72+
> email.template:
73+
74+
75+
```html
76+
<!DOCTYPE html>
77+
<html lang="en">
78+
<head>
79+
<meta charset="UTF-8">
80+
<title>[✅ Database Backup Notification – {{.Database}}</title>
81+
</head>
82+
<body>
83+
<h2>Hi,</h2>
84+
<p>Backup of the {{.Database}} database has been successfully completed on {{.EndTime}}.</p>
85+
<h3>Backup Details:</h3>
86+
<ul>
87+
<li>Database Name: {{.Database}}</li>
88+
<li>Backup Start Time: {{.StartTime}}</li>
89+
<li>Backup End Time: {{.EndTime}}</li>
90+
<li>Backup Storage: {{.Storage}}</li>
91+
<li>Backup Location: {{.BackupLocation}}</li>
92+
<li>Backup Size: {{.BackupSize}} bytes</li>
93+
</ul>
94+
<p>Best regards,</p>
95+
</body>
96+
</html>
97+
```
98+
99+
> telegram.template
100+
101+
```html
102+
[✅ Database Backup Notification – {{.Database}}
103+
Hi,
104+
Backup of the {{.Database}} database has been successfully completed on {{.EndTime}}.
105+
106+
Backup Details:
107+
- Database Name: {{.Database}}
108+
- Backup Start Time: {{.StartTime}}
109+
- Backup EndTime: {{.EndTime}}
110+
- Backup Storage: {{.Storage}}
111+
- Backup Location: {{.BackupLocation}}
112+
- Backup Size: {{.BackupSize}} bytes
113+
```
114+
115+
> error.template
116+
117+
118+
```html
119+
🔴 Urgent: Database Backup Failure Notification
120+
121+
An error occurred during database backup.
122+
Failure Details:
123+
124+
Error Message: {{.Error}}
125+
Date: {{.EndTime}}
126+
```

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ require (
2020
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // indirect
2121
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect
2222
github.com/cloudflare/circl v1.3.3 // indirect
23+
github.com/go-mail/mail v2.3.1+incompatible // indirect
2324
github.com/hashicorp/errwrap v1.1.0 // indirect
2425
github.com/hashicorp/go-multierror v1.1.1 // indirect
2526
github.com/inconshreveable/mousetrap v1.1.0 // indirect
2627
github.com/jmespath/go-jmespath v0.4.0 // indirect
2728
github.com/pkg/errors v0.9.1 // indirect
2829
golang.org/x/sys v0.22.0 // indirect
2930
golang.org/x/text v0.14.0 // indirect
31+
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
3032
gopkg.in/fsnotify.v1 v1.4.7 // indirect
3133
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
3234
)

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
1717
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
1818
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1919
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
20+
github.com/go-mail/mail v2.3.1+incompatible h1:UzNOn0k5lpfVtO31cK3hn6I4VEVGhe3lX8AJBAxXExM=
21+
github.com/go-mail/mail v2.3.1+incompatible/go.mod h1:VPWjmmNyRsWXQZHVHT3g0YbIINUkSmuKOiLIDkWbL6M=
2022
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
2123
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
2224
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -94,6 +96,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
9496
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
9597
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
9698
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
99+
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
100+
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
97101
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
98102
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
99103
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=

pkg/backup.go

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -218,17 +218,31 @@ func BackupDatabase(db *dbConfig, backupFileName string, disableCompression bool
218218
}
219219
func localBackup(db *dbConfig, config *BackupConfig) {
220220
utils.Info("Backup database to local storage")
221+
startTime = time.Now().Format("2006-01-02 15:04:05")
221222
BackupDatabase(db, config.backupFileName, disableCompression)
222223
finalFileName := config.backupFileName
223224
if config.encryption {
224225
encryptBackup(config)
225226
finalFileName = fmt.Sprintf("%s.%s", config.backupFileName, gpgExtension)
226227
}
227-
228+
fileInfo, err := os.Stat(filepath.Join(tmpPath, finalFileName))
229+
if err != nil {
230+
utils.Error("Error:", err)
231+
}
232+
//Get backup info
233+
backupSize = fileInfo.Size()
228234
utils.Info("Backup name is %s", finalFileName)
229235
moveToBackup(finalFileName, storagePath)
230236
//Send notification
231-
utils.NotifySuccess(finalFileName)
237+
utils.NotifySuccess(&utils.NotificationData{
238+
File: finalFileName,
239+
BackupSize: backupSize,
240+
Database: db.dbName,
241+
Storage: config.storage,
242+
BackupLocation: filepath.Join(config.remotePath, finalFileName),
243+
StartTime: startTime,
244+
EndTime: time.Now().Format("2006-01-02 15:04:05"),
245+
})
232246
//Delete old backup
233247
if config.prune {
234248
deleteOldBackup(config.backupRetention)
@@ -242,6 +256,8 @@ func s3Backup(db *dbConfig, config *BackupConfig) {
242256
bucket := utils.GetEnvVariable("AWS_S3_BUCKET_NAME", "BUCKET_NAME")
243257
s3Path := utils.GetEnvVariable("AWS_S3_PATH", "S3_PATH")
244258
utils.Info("Backup database to s3 storage")
259+
startTime = time.Now().Format("2006-01-02 15:04:05")
260+
245261
//Backup database
246262
BackupDatabase(db, config.backupFileName, disableCompression)
247263
finalFileName := config.backupFileName
@@ -257,7 +273,12 @@ func s3Backup(db *dbConfig, config *BackupConfig) {
257273
utils.Fatal("Error uploading backup archive to S3: %s ", err)
258274

259275
}
260-
276+
//Get backup info
277+
fileInfo, err := os.Stat(filepath.Join(tmpPath, finalFileName))
278+
if err != nil {
279+
utils.Error("Error:", err)
280+
}
281+
backupSize = fileInfo.Size()
261282
//Delete backup file from tmp folder
262283
err = utils.DeleteFile(filepath.Join(tmpPath, config.backupFileName))
263284
if err != nil {
@@ -273,14 +294,24 @@ func s3Backup(db *dbConfig, config *BackupConfig) {
273294
}
274295
utils.Done("Uploading backup archive to remote storage S3 ... done ")
275296
//Send notification
276-
utils.NotifySuccess(finalFileName)
297+
utils.NotifySuccess(&utils.NotificationData{
298+
File: finalFileName,
299+
BackupSize: backupSize,
300+
Database: db.dbName,
301+
Storage: config.storage,
302+
BackupLocation: filepath.Join(config.remotePath, finalFileName),
303+
StartTime: startTime,
304+
EndTime: time.Now().Format("2006-01-02 15:04:05"),
305+
})
277306
//Delete temp
278307
deleteTemp()
279308
utils.Info("Backup completed successfully")
280309

281310
}
282311
func sshBackup(db *dbConfig, config *BackupConfig) {
283312
utils.Info("Backup database to Remote server")
313+
startTime = time.Now().Format("2006-01-02 15:04:05")
314+
284315
//Backup database
285316
BackupDatabase(db, config.backupFileName, disableCompression)
286317
finalFileName := config.backupFileName
@@ -295,7 +326,12 @@ func sshBackup(db *dbConfig, config *BackupConfig) {
295326
utils.Fatal("Error uploading file to the remote server: %s ", err)
296327

297328
}
298-
329+
//Get backup info
330+
fileInfo, err := os.Stat(filepath.Join(tmpPath, finalFileName))
331+
if err != nil {
332+
utils.Error("Error:", err)
333+
}
334+
backupSize = fileInfo.Size()
299335
//Delete backup file from tmp folder
300336
err = utils.DeleteFile(filepath.Join(tmpPath, finalFileName))
301337
if err != nil {
@@ -310,14 +346,24 @@ func sshBackup(db *dbConfig, config *BackupConfig) {
310346

311347
utils.Done("Uploading backup archive to remote storage ... done ")
312348
//Send notification
313-
utils.NotifySuccess(finalFileName)
349+
utils.NotifySuccess(&utils.NotificationData{
350+
File: finalFileName,
351+
BackupSize: backupSize,
352+
Database: db.dbName,
353+
Storage: config.storage,
354+
BackupLocation: filepath.Join(config.remotePath, finalFileName),
355+
StartTime: startTime,
356+
EndTime: time.Now().Format("2006-01-02 15:04:05"),
357+
})
314358
//Delete temp
315359
deleteTemp()
316360
utils.Info("Backup completed successfully")
317361

318362
}
319363
func ftpBackup(db *dbConfig, config *BackupConfig) {
320364
utils.Info("Backup database to the remote FTP server")
365+
startTime = time.Now().Format("2006-01-02 15:04:05")
366+
321367
//Backup database
322368
BackupDatabase(db, config.backupFileName, disableCompression)
323369
finalFileName := config.backupFileName
@@ -332,7 +378,12 @@ func ftpBackup(db *dbConfig, config *BackupConfig) {
332378
utils.Fatal("Error uploading file to the remote FTP server: %s ", err)
333379

334380
}
335-
381+
//Get backup info
382+
fileInfo, err := os.Stat(filepath.Join(tmpPath, finalFileName))
383+
if err != nil {
384+
utils.Error("Error:", err)
385+
}
386+
backupSize = fileInfo.Size()
336387
//Delete backup file from tmp folder
337388
err = utils.DeleteFile(filepath.Join(tmpPath, finalFileName))
338389
if err != nil {
@@ -347,7 +398,15 @@ func ftpBackup(db *dbConfig, config *BackupConfig) {
347398

348399
utils.Done("Uploading backup archive to the remote FTP server ... done ")
349400
//Send notification
350-
utils.NotifySuccess(finalFileName)
401+
utils.NotifySuccess(&utils.NotificationData{
402+
File: finalFileName,
403+
BackupSize: backupSize,
404+
Database: db.dbName,
405+
Storage: config.storage,
406+
BackupLocation: filepath.Join(config.remotePath, finalFileName),
407+
StartTime: startTime,
408+
EndTime: time.Now().Format("2006-01-02 15:04:05"),
409+
})
351410
//Delete temp
352411
deleteTemp()
353412
utils.Info("Backup completed successfully")

pkg/var.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ const gpgExtension = "gpg"
1414
const workingDir = "/config"
1515

1616
var (
17-
storage = "local"
18-
file = ""
19-
storagePath = "/backup"
20-
disableCompression = false
21-
encryption = false
22-
usingKey = false
17+
storage = "local"
18+
file = ""
19+
storagePath = "/backup"
20+
disableCompression = false
21+
encryption = false
22+
usingKey = false
23+
backupSize int64 = 0
24+
startTime string
2325
)
2426

2527
// dbHVars Required environment variables for database

templates/email-error.template

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>🔴 Urgent: Database Backup Failure Notification</title>
6+
</head>
7+
<body>
8+
<h2>Hi,</h2>
9+
<p>An error occurred during database backup.</p>
10+
<h3>Failure Details:</h3>
11+
<ul>
12+
<li>Error Message: {{.Error}}</li>
13+
<li>Date: {{.EndTime}}</li>
14+
</ul>
15+
<p>©2024 <a href="github.com/jkaninda/mysql-bkup">mysql-bkup</a></p>
16+
</body>
17+
</html>

0 commit comments

Comments
 (0)