]> Frank Brehm's Git Trees - pixelpark/puppetmaster-webhooks.git/commitdiff
Enabling sending mails in case of errors
authorFrank Brehm <frank.brehm@pixelpark.com>
Fri, 27 Jan 2017 12:38:42 +0000 (13:38 +0100)
committerFrank Brehm <frank.brehm@pixelpark.com>
Fri, 27 Jan 2017 12:38:42 +0000 (13:38 +0100)
deploy.yaml
lib/webhooks/__init__.py
lib/webhooks/deploy.py

index ef6c3a99e20800fe8a2ff98b2dd8bf0eabbadf41..bb2f4478cd8cb84ac58a39dc064c529f4c79161e 100644 (file)
@@ -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'
index 35f539426970f5da6ec8f548eeb532562a633550..1b9460ad4d8ba35883b63faabaf265296630da87 100644 (file)
@@ -1,6 +1,6 @@
 #!/bin/env python3
 # -*- coding: utf-8 -*-
 
-__version__ = '0.2.1'
+__version__ = '0.3.1'
 
 # vim: ts=4 et list
index 357695a18ea6a43f403a4487452041e27573cf9e..19e90af2c079bb0d9e6f2ed7018b7654107d353a 100644 (file)
@@ -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)