From 5d7f2958fbc712c2793ad8603f80abff68577767 Mon Sep 17 00:00:00 2001 From: Frank Brehm Date: Fri, 27 Jan 2017 13:38:42 +0100 Subject: [PATCH] Enabling sending mails in case of errors --- deploy.yaml | 2 + lib/webhooks/__init__.py | 2 +- lib/webhooks/deploy.py | 120 ++++++++++++++++++++++++++++++++------- 3 files changed, 104 insertions(+), 20 deletions(-) diff --git a/deploy.yaml b/deploy.yaml index ef6c3a9..bb2f447 100644 --- a/deploy.yaml +++ b/deploy.yaml @@ -5,6 +5,8 @@ do_sudo: true log_dir: '/var/log/webhooks' default_email: 'frank@brehm-online.com' default_parent_dir: '/www/data' +smtp_server: 'smtp.pixelpark.com' +smtp_port: 25 ignore_projects: mail_cc_addresses: - 'webmaster@pixelpark.com' diff --git a/lib/webhooks/__init__.py b/lib/webhooks/__init__.py index 35f5394..1b9460a 100644 --- a/lib/webhooks/__init__.py +++ b/lib/webhooks/__init__.py @@ -1,6 +1,6 @@ #!/bin/env python3 # -*- coding: utf-8 -*- -__version__ = '0.2.1' +__version__ = '0.3.1' # vim: ts=4 et list diff --git a/lib/webhooks/deploy.py b/lib/webhooks/deploy.py index 357695a..19e90af 100644 --- a/lib/webhooks/deploy.py +++ b/lib/webhooks/deploy.py @@ -19,6 +19,8 @@ import traceback import copy import pipes import subprocess +import smtplib +from email.message import EmailMessage # Third party modules import yaml @@ -94,6 +96,8 @@ class WebhookDeployApp(object): self.ignore_projects = [] self.error_data = [] + self.smtp_server = 'smtp.pixelpark.com' + self.smtp_port = 25 self.default_parent_dir = '/www/data' self.default_email = DEFAULT_EMAIL @@ -243,6 +247,21 @@ class WebhookDeployApp(object): if 'default_email' in config and config['default_email']: self.default_email = config['default_email'] + if 'smtp_server' in config and config['smtp_server'].strip(): + self.smtp_server = config['smtp_server'].strip() + + if 'smtp_port' in config and config['smtp_port']: + msg = "Invalid port {p!r} for SMTP in {f!r} found.".format( + p=config['smtp_port'], f=yaml_file) + try: + port = int(config['smtp_port']) + if port > 0 and port < 2**16: + self.smtp_port = port + else: + self.error_data.append(msg) + except ValueError: + self.error_data.append(msg) + if 'default_parent_dir' in config and config['default_parent_dir']: pdir = config['default_parent_dir'] if os.path.isabs(pdir): @@ -326,7 +345,7 @@ class WebhookDeployApp(object): # create formatter format_str = '' - if self.verbose > 1: + if 'REQUEST_METHOD' in os.environ or self.verbose > 1: format_str = '[%(asctime)s]: ' format_str += self.appname + ': ' if self.verbose: @@ -432,14 +451,9 @@ class WebhookDeployApp(object): 'user_name': 'Frank Brehm'} """ - #print("Content-Type: text/plain;charset=utf-8") - #print() - #sys.stdout.buffer.write(to_bytes("Python CGI läuft.\n")) - #sys.stdout.flush() self.print_out("Content-Type: text/plain;charset=utf-8\n") self.print_out("Python CGI läuft.\n") - LOG.info("Starting ...") LOG.debug("Base directory: {!r}".format(self.base_dir)) self.data = sys.stdin.read() @@ -449,7 +463,7 @@ class WebhookDeployApp(object): msg = "Got a {n} reading input data as JSON: {e}".format(n=e.__class__.__name__, e=e) msg += "\nInput data: {!r}".format(self.data) LOG.error(msg) - error_data.append(msg) + self.error_data.append(msg) else: LOG.debug("Got JSON data:\n{}".format(pp(self.json_data))) @@ -459,12 +473,80 @@ class WebhookDeployApp(object): except Exception as e: msg = "Got a {n} performing the deploy: {e}".format(n=e.__class__.__name__, e=e) msg += "\n\nTraceback:\n{}".format(traceback.format_exc()) - error_data.append(msg) + self.error_data.append(msg) LOG.error(msg) finally: + if self.full_name: + self.send_error_msgs(self.full_name) + else: + self.send_error_msgs() LOG.info("Finished.") sys.exit(0) + # ------------------------------------------------------------------------- + def send_error_msgs(self, project='undefined'): + + if not self.error_data: + return + + msg = EmailMessage() + + s = '' + if len(self.error_data) > 1: + s = 's' + + body = 'Error{s} while processing {p!r} project:\n\n'.format( + s=s, p=project) + body += '\n\n'.join(self.error_data) + body += '\n\nCheers\nPuppetmaster' + msg.set_content(body) + msg.set_charset('utf-8') + + msg['Subject'] = 'Puppetmaster deploy error{s} for project {p!r}'.format( + s=s, p=project) + msg['From'] = self.sender_address + to_addresses = '' + if self.mail_to_addresses: + to_addresses = ', '.join(self.mail_to_addresses) + else: + to_addresses = self.default_email + msg['To'] = to_addresses + if self.mail_cc_addresses: + msg['CC'] = ', '.join(self.mail_cc_addresses) + + msg['X-Mailer'] = "Puppetmaster deploy script v.{}".format(__version__) + + if self.verbose: + LOG.info("Sending the following mail to {r!r} via {s}:{p}:\n{m}".format( + r=to_addresses, m=msg.as_string(unixfrom=True), + s=self.smtp_server, p=self.smtp_port)) + else: + LOG.info("Sending a mail to {r!r} via {s}:{p}:\n{e}".format( + r=to_addresses, e=pp(self.error_data), + s=self.smtp_server, p=self.smtp_port)) + + server = smtplib.SMTP(self.smtp_server, self.smtp_port) + if 'REQUEST_METHOD' not in os.environ: + if self.verbose > 2: + server.set_debuglevel(2) + elif self.verbose > 1: + server.set_debuglevel(1) + server.starttls() + result = server.send_message(msg) + server.quit() + + if not result.keys(): + LOG.debug("Susseccful sent message to {r!r} via {s}:{p}.".format( + r=to_addresses, s=self.smtp_server, p=self.smtp_port)) + else: + LOG.error(( + "Errors on sending error message for project " + "{pr!r} to {r!r} via {s}:{p}:\n{e}").format( + r=to_addresses, s=self.smtp_server, p=self.smtp_port, + pr=project, e=pp(result))) + + return + # ------------------------------------------------------------------------- def perform(self): '''Performing the stuff...''' @@ -481,7 +563,7 @@ class WebhookDeployApp(object): if self.special_chars_re.search(self.name): msg = "Project {!r}: ".format(self.full_name) + self.mail_bodies['special_chars'] LOG.error(msg) - error_data.append(msg) + self.error_data.append(msg) return True committers = [] @@ -536,7 +618,7 @@ class WebhookDeployApp(object): return self.deploy(cfg) msg = "Could not find a definition for project {!r}.".format(self.full_name) - error_data.append(msg) + self.error_data.append(msg) LOG.error(msg) return True @@ -563,13 +645,13 @@ class WebhookDeployApp(object): if not os.access(parent_dir, os.F_OK): msg = "Parent directory {!r} for Hiera does not exists.".format(parent_dir) LOG.error(msg) - error_data.append(msg) + self.error_data.append(msg) return True if not os.path.isdir(parent_dir): msg = "Path of parent directory {!r} for Hiera is not a directory.".format(parent_dir) LOG.error(msg) - error_data.append(msg) + self.error_data.append(msg) return True return self.ensure_workingdir(parent_dir, workdir) @@ -590,14 +672,14 @@ class WebhookDeployApp(object): msg = "Parent directory {d!r} of project {p!r} does not exists.".format( d=parent_dir, p=full_name) LOG.error(msg) - error_data.append(msg) + self.error_data.append(msg) return True if not os.path.isdir(parent_dir): msg = ("Path for parent directory {d!r} for project {p!r} " "is not a directory.").format(d=parent_dir, p=full_name) LOG.error(msg) - error_data.append(msg) + self.error_data.append(msg) return True env_branch = 'undefined' @@ -610,7 +692,7 @@ class WebhookDeployApp(object): msg = "Branch directory {d!r} of project {p!r} does not exists.".format( d=full_path_branch, p=full_name) LOG.error(msg) - error_data.append(msg) + self.error_data.append(msg) return True modules_dir = os.path.join(full_path_branch, 'modules') @@ -618,7 +700,7 @@ class WebhookDeployApp(object): msg = "Modules directory {d!r} of project {p!r} does not exists.".format( d=modules_dir, p=full_name) LOG.error(msg) - error_data.append(msg) + self.error_data.append(msg) return True branc2clone = None @@ -651,7 +733,7 @@ class WebhookDeployApp(object): msg = "Parent directory {d!r} of project {p!r} does not exists.".format( d=parent_dir, p=full_name) LOG.error(msg) - error_data.append(msg) + self.error_data.append(msg) return True if not os.path.isdir(parent_dir): @@ -659,7 +741,7 @@ class WebhookDeployApp(object): "Path for parent directory {d!r} for project {p!r} " "is not a directory.").format(d=parent_dir, p=full_name) LOG.error(msg) - error_data.append(msg) + self.error_data.append(msg) return True return self.ensure_workingdir(parent_dir, workdir, branch) @@ -700,7 +782,7 @@ class WebhookDeployApp(object): if stderrdata: msg = "Error messages on '{c}':\n{e}".format(c=cmd_str, e=to_str(stderrdata)) LOG.warn(msg) - error_data.append(msg) + self.error_data.append(msg) self.print_out(msg) finally: os.chdir(cur_dir) -- 2.39.5