Skip to content

Commit e7267fb

Browse files
committed
Slack Webhook messages integration
1 parent cff7534 commit e7267fb

3 files changed

Lines changed: 62 additions & 6 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ Environment values meaning:
2020
- AWS_DEFAULT_REGION: AWS region where Glacier vault will be created
2121
- AWS_ACCESS_KEY_ID: access key for your AWS account
2222
- AWS_SECRET_ACCESS_KEY: secret key for your AWS account
23+
- SLACK_WEBHOOK: incoming [webhook url](https://my.slack.com/services/new/incoming-webhook) for posting messages to a channel
24+
- PROJECT_NAME: project name that will be send in slack message
2325

2426
## Notes
25-
Currently only supports one database per docker container for either PostgreSQL or MySQL databases.
27+
Currently only supports one database per docker container for either PostgreSQL or MySQL databases.
2628

2729
Was only tested on MySQL 5.6 and PostgreSQL 10.11, but should work just fine with other versions as well.

src/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ FROM python:3.9.0-alpine3.12
22

33
RUN apk add --no-cache postgresql-client mysql-client
44

5-
RUN python -m pip install apscheduler boto3
5+
RUN python -m pip install apscheduler boto3 slack_sdk
66

77
COPY scheduler.py /scheduler/scheduler.py
88
COPY logging.ini /scheduler/logging.ini

src/scheduler.py

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import boto3
33
import logging.config
44
from datetime import datetime
5+
from slack_sdk.webhook import WebhookClient
56

67
from apscheduler.triggers.cron import CronTrigger
78
from apscheduler.schedulers.background import BlockingScheduler
@@ -10,6 +11,45 @@
1011
logger = logging.getLogger('ddg_scheduler')
1112

1213

14+
def send_slack_message(environment, message, success=True):
15+
webhook_url = environment['SLACK_WEBHOOK']
16+
project_name = environment['PROJECT_NAME']
17+
18+
if project_name and len(project_name) > 0:
19+
project_name += " "
20+
21+
if not webhook_url:
22+
return
23+
try:
24+
client = WebhookClient(webhook_url)
25+
response = client.send(
26+
attachments=[
27+
{
28+
"color": "#36a64f" if success else "#ee2700",
29+
"blocks": [
30+
{
31+
"type": "header",
32+
"text": {
33+
"type": "plain_text",
34+
"text": f"{project_name}Glacierizer"
35+
}
36+
},
37+
{
38+
"type": "section",
39+
"text": {
40+
"type": "plain_text",
41+
"text": message
42+
}
43+
}
44+
]
45+
}
46+
]
47+
)
48+
logger.info(message)
49+
except Exception as e:
50+
logger.exception(e)
51+
52+
1353
def cast_to_type(value, cast_type):
1454
if type(value) == cast_type:
1555
return value
@@ -34,6 +74,8 @@ def get_env():
3474
'AWS_DEFAULT_REGION': {'type': str},
3575
'AWS_ACCESS_KEY_ID': {'type': str},
3676
'AWS_SECRET_ACCESS_KEY': {'type': str},
77+
'PROJECT_NAME': {'type': str, 'required': False},
78+
'SLACK_WEBHOOK': {'type': str, 'required': False}
3779
}
3880

3981
for name, options in env_variables.items():
@@ -75,7 +117,7 @@ def dump_database():
75117
dump_path = os.path.join('/tmp', filename)
76118

77119
dump_database_templates = {
78-
'mysql': 'mysqldump -h {host} -u {user} -p{password} --databases {database} -P {port} --protocol tcp | gzip -9 > {dump_path}',
120+
'mysql': 'set -o pipefail; mysqldump -h {host} -u {user} -p{password} --databases {database} -P {port} --protocol tcp | gzip -9 > {dump_path}',
79121
'postgresql': 'PGPASSWORD={password} pg_dump -h {host} -U {user} -d {database} -p {port} -Fp -Z9 > {dump_path}',
80122
}
81123

@@ -91,11 +133,21 @@ def dump_database():
91133
port=environment.get('DATABASE_PORT'),
92134
dump_path=dump_path.format(database=environment.get('DATABASE_NAME')),
93135
)
94-
return_code = os.system(dump_command)
136+
wait_exit_status = os.system(dump_command)
137+
exit_status = os.waitstatus_to_exitcode(wait_exit_status)
95138

96-
if return_code != 0:
139+
if wait_exit_status != 0 or exit_status != 0:
97140
logger.error("RETURN CODE OF DUMP PROCESS != 0. CHECK OUTPUT ABOVE FOR ERRORS!")
141+
send_slack_message(environment, "Failed to create DB dump. Please check the error in the container logs.", False)
98142
else:
143+
file_size = 0
144+
145+
try:
146+
file_size = os.path.getsize(dump_path) / 1024
147+
except Exception as e:
148+
logger.error("Failed to get size of file.")
149+
logger.exception(e)
150+
99151
logger.info(f'{datetime.now()}: Backup created. Uploading to glacier')
100152
try:
101153
glacier = boto3.client('glacier')
@@ -107,8 +159,10 @@ def dump_database():
107159
body=f,
108160
))
109161
logger.info('Glacier upload done.')
162+
send_slack_message(environment, f"Successfully created and uploaded DB dump ({round(file_size, 2)} KB).")
110163
except Exception as e:
111164
logger.exception(e)
165+
send_slack_message(environment, f"Failed to upload DB dump ({round(file_size, 2)} KB) to AWS Glacier. Please check the error in the container logs.", False)
112166
else:
113167
logger.error(f'Database of type {database_type} is not supported. If you see this message something went horribly wrong.')
114168

@@ -118,7 +172,7 @@ def dump_database():
118172

119173
if environment.get('TEST'):
120174
import time
121-
time.sleep(30)
175+
time.sleep(10)
122176
dump_database()
123177
time.sleep(300)
124178
else:

0 commit comments

Comments
 (0)