]> Frank Brehm's Git Trees - pixelpark/puppetmaster-webhooks.git/commitdiff
Reorganized object inheritance
authorFrank Brehm <frank.brehm@pixelpark.com>
Tue, 14 Feb 2017 16:20:42 +0000 (17:20 +0100)
committerFrank Brehm <frank.brehm@pixelpark.com>
Tue, 14 Feb 2017 16:20:42 +0000 (17:20 +0100)
hooks.yaml [new file with mode: 0644]
lib/webhooks/base_app.py
lib/webhooks/r10k.py
r10k-hook.yaml [new file with mode: 0644]
r10k_hook-new.py

diff --git a/hooks.yaml b/hooks.yaml
new file mode 100644 (file)
index 0000000..00fccb9
--- /dev/null
@@ -0,0 +1,12 @@
+---
+verbose: 0
+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
+mail_cc_addresses:
+  - 'webmaster@pixelpark.com'
+  - 'frank.brehm@pixelpark.com'
+sender_address: 'Puppetmaster <frank.brehm@pixelpark.com>'
index ad545fa113cf05665559e7aa01438c12451f2cc5..3e73999e6818ebd256cff2064be8a8450c3dab02 100644 (file)
@@ -42,7 +42,7 @@ class BaseHookApp(object):
     dev_re = re.compile(r'^dev')
 
     # -------------------------------------------------------------------------
-    def __init__(self, appname=None, version=__version__):
+    def __init__(self, appname=None, verbose=0, version=__version__):
         """Constructor."""
 
         self._appname = None
@@ -63,11 +63,12 @@ class BaseHookApp(object):
         @type: str
         """
 
-        self._verbose = 1
+        self._verbose = verbose
         """
         @ivar: verbosity level (0 - 9)
         @type: int
         """
+        self._start_verbose = verbose
 
         self.data = None
         self.json_data = None
@@ -145,7 +146,7 @@ class BaseHookApp(object):
     @property
     def error_logfile(self):
         """The logfile for STDERR of this application."""
-        return os.path.join(self.log_directory, self.appname + '.log')
+        return os.path.join(self.log_directory, self.appname + '.error.log')
 
     # -------------------------------------------------------------------------
     def __str__(self):
@@ -168,7 +169,6 @@ class BaseHookApp(object):
         @rtype:  dict
         """
 
-        res = self.__dict__
         res = {}
         for key in self.__dict__:
             if key.startswith('_') and not key.startswith('__'):
@@ -190,9 +190,13 @@ class BaseHookApp(object):
         """Reading configuration from different YAML files."""
 
         yaml_files = []
-        # ./deploy.yaml
+        # ./hooks.yaml
+        yaml_files.append(os.path.join(self.base_dir, 'hooks.yaml'))
+        # ./<appname>.yaml
         yaml_files.append(os.path.join(self.base_dir, self.appname + '.yaml'))
-        # /etc/pixelpark/deploy.yaml
+        # /etc/pixelpark/hooks.yaml
+        yaml_files.append(os.sep + os.path.join('etc', 'pixelpark', 'hooks.yaml'))
+        # /etc/pixelpark/<appname>.yaml
         yaml_files.append(os.sep + os.path.join('etc', 'pixelpark', self.appname + '.yaml'))
 
         for yaml_file in yaml_files:
@@ -202,18 +206,36 @@ class BaseHookApp(object):
     def read_from_yaml(self, yaml_file):
         """Reading configuration from given YAML file."""
 
-        # LOG.debug("Trying to read config from {!r} ...".format(yaml_file))
+        if self._start_verbose > 1:
+            self.print_err("Trying to read config from {!r} ...".format(yaml_file))
         if not os.access(yaml_file, os.F_OK):
-            # LOG.debug("File {!r} does not exists.".format(yaml_file))
             return
-        # LOG.debug("Reading config from {!r} ...".format(yaml_file))
+        if self._start_verbose > 1:
+            self.print_err("Reading config from {!r} ...".format(yaml_file))
         config = {}
         with open(yaml_file, 'rb') as fh:
             config = yaml.load(fh.read())
-        # LOG.debug("Read config:\n{}".format(pp(config)))
+        if self._start_verbose > 2:
+            self.print_err("Read config:\n{}".format(pp(config)))
+        if config and isinstance(config, dict):
+            self.evaluate_config(config, yaml_file)
+
+    # -------------------------------------------------------------------------
+    def evaluate_config(self, config, yaml_file):
 
         if 'verbose' in config:
-            self.verbose = config['verbose']
+            try:
+                v = int(config['verbose'])
+                if v >= 0:
+                    if v >= self.verbose:
+                        self._verbose = v
+                else:
+                    LOG.warn("Wrong verbose level {v!d} in file {f!r}, must be >= 0".format(
+                        v=v, f=yaml_file))
+            except ValueError as e:
+                msg = "Wrong verbose level {v!r} in file {f!r}: {e}".format(
+                    v=config['verbose'], f=yaml_file, e=e)
+                LOG.warn(msg)
 
         if 'do_sudo' in config:
             self.do_sudo = to_bool(config['do_sudo'])
@@ -322,6 +344,10 @@ class BaseHookApp(object):
 
         return
 
+    # -------------------------------------------------------------------------
+    def print_err(self, *objects, sep=' ', end='\n', file=sys.stderr.buffer, flush=True):
+        self.print_out(*objects, sep=sep, end=end, file=file, flush=flush)
+
     # -------------------------------------------------------------------------
     def print_out(self, *objects, sep=' ', end='\n', file=sys.stdout.buffer, flush=True):
 
@@ -341,6 +367,15 @@ class BaseHookApp(object):
         if self.verbose > 1:
             LOG.debug("Base directory: {!r}".format(self.base_dir))
 
+        self.run()
+
+    # -------------------------------------------------------------------------
+    def run(self):
+        """Main routine, must be overridden in descendant classes."""
+
+        msg = "Method run() must be overridden in descendant classes of {!r}.".format(
+            self.__class__.__name__)
+        raise NotImplementedError(msg)
 
 
 # =============================================================================
index 5cd6b5612425b5d4cb67b75ef8a75876ca19956b..224f14b0c8f5042cc5dece9061f790ca40111c84 100644 (file)
@@ -23,6 +23,8 @@ import webhooks
 
 from webhooks.common import pp, to_bytes, to_str, to_bool
 
+from webhooks.base_app import BaseHookApp
+
 __version__ = webhooks.__version__
 LOG = logging.getLogger(__name__)
 DEFAULT_EMAIL = 'frank.brehm@pixelpark.com'
@@ -30,130 +32,19 @@ DEFAULT_SENDER = 'Puppetmaster <{}>'.format(DEFAULT_EMAIL)
 
 
 # =============================================================================
-class R10kHookApp(object):
+class R10kHookApp(BaseHookApp):
     """
-    Class for the application objects.
+    Class for the r10k-hook application object.
     """
 
-    cgi_bin_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
-    base_dir = os.path.dirname(cgi_bin_dir)
-
-    special_chars_re = re.compile(r'[^a-z0-9_\-]', re.IGNORECASE)
-    dev_re = re.compile(r'^dev')
-
     # -------------------------------------------------------------------------
-    def __init__(self, appname=None, version=__version__):
+    def __init__(self, appname=None, verbose=0, version=__version__):
         """Constructor."""
 
-        self._appname = None
-        """
-        @ivar: name of the current running application
-        @type: str
-        """
-        if appname:
-            v = str(appname).strip()
-            if v:
-                self._appname = v
-        if not self._appname:
-            self._appname = os.path.basename(sys.argv[0])
-
-        self._version = version
-        """
-        @ivar: version string of the current object or application
-        @type: str
-        """
-
-        self._verbose = 1
-        """
-        @ivar: verbosity level (0 - 9)
-        @type: int
-        """
-
-        self.data = None
-        self.json_data = None
-        self.ref = None
-        self.namespace = None
-        self.name = None
-        self.full_name = None
-        self.git_ssh_url = None
-        self.do_sudo = True
-
         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
-        self.mail_to_addresses = []
-        self.mail_cc_addresses = [
-            'webmaster@pixelpark.com',
-            self.default_email,
-        ]
-        self.sender_address = DEFAULT_SENDER
-
-        self._log_directory = os.sep + os.path.join('var', 'log', 'webhooks')
-
-        self.read_config()
-        self.init_logging()
-
-    # -----------------------------------------------------------
-    @property
-    def appname(self):
-        """The name of the current running application."""
-        return self._appname
-
-    @appname.setter
-    def appname(self, value):
-        if value:
-            v = str(value).strip()
-            if v:
-                self._appname = v
-
-    # -----------------------------------------------------------
-    @property
-    def version(self):
-        """The version string of the current object or application."""
-        return self._version
-
-    # -----------------------------------------------------------
-    @property
-    def verbose(self):
-        """The verbosity level."""
-        return getattr(self, '_verbose', 0)
-
-    @verbose.setter
-    def verbose(self, value):
-        v = int(value)
-        if v >= 0:
-            self._verbose = v
-        else:
-            LOG.warn("Wrong verbose level %r, must be >= 0", value)
-
-    # -----------------------------------------------------------
-    @property
-    def log_directory(self):
-        """The directory containing the logfiles of this application."""
-        return self._log_directory
-
-    # -----------------------------------------------------------
-    @property
-    def logfile(self):
-        """The logfile of this application."""
-        return os.path.join(self.log_directory, self.appname + '.log')
-
-    # -------------------------------------------------------------------------
-    def __str__(self):
-        """
-        Typecasting function for translating object structure
-        into a string
-
-        @return: structure as string
-        @rtype:  str
-        """
-
-        return pp(self.as_dict())
+        super(R10kHookApp, self).__init__(
+            appname=appname, verbose=verbose, version=version)
 
     # -------------------------------------------------------------------------
     def as_dict(self):
@@ -164,166 +55,35 @@ class R10kHookApp(object):
         @rtype:  dict
         """
 
-        res = self.__dict__
-        res = {}
-        for key in self.__dict__:
-            if key.startswith('_') and not key.startswith('__'):
-                continue
-            res[key] = self.__dict__[key]
-        res['__class_name__'] = self.__class__.__name__
-        res['appname'] = self.appname
-        res['verbose'] = self.verbose
-        res['base_dir'] = self.base_dir
-        res['cgi_bin_dir'] = self.cgi_bin_dir
-        res['log_directory'] = self.log_directory
-        res['logfile'] = self.logfile
+        res = super(R10kHookApp, self).as_dict()
 
         return res
 
     # -------------------------------------------------------------------------
-    def read_config(self):
-        """Reading configuration from different YAML files."""
-
-        yaml_files = []
-        # ./deploy.yaml
-        yaml_files.append(os.path.join(self.base_dir, self.appname + '.yaml'))
-        # /etc/pixelpark/deploy.yaml
-        yaml_files.append(os.sep + os.path.join('etc', 'pixelpark', self.appname + '.yaml'))
-
-        for yaml_file in yaml_files:
-            self.read_from_yaml(yaml_file)
-
-    # -------------------------------------------------------------------------
-    def read_from_yaml(self, yaml_file):
-        """Reading configuration from given YAML file."""
-
-        # LOG.debug("Trying to read config from {!r} ...".format(yaml_file))
-        if not os.access(yaml_file, os.F_OK):
-            # LOG.debug("File {!r} does not exists.".format(yaml_file))
-            return
-        # LOG.debug("Reading config from {!r} ...".format(yaml_file))
-        config = {}
-        with open(yaml_file, 'rb') as fh:
-            config = yaml.load(fh.read())
-        # LOG.debug("Read config:\n{}".format(pp(config)))
-
-        if 'verbose' in config:
-            self.verbose = config['verbose']
-
-        if 'do_sudo' in config:
-            self.do_sudo = to_bool(config['do_sudo'])
-
-        if 'log_dir' in config and config['log_dir']:
-            self._log_directory = config['log_dir']
-
-        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):
-                self.default_parent_dir = pdir
-
-        if 'mail_cc_addresses' in config:
-            if config['mail_cc_addresses'] is None:
-                self.mail_cc_addresses = []
-            elif isinstance(config['mail_cc_addresses'], str):
-                if config['mail_cc_addresses']:
-                    self.mail_cc_addresses = [config['mail_cc_addresses']]
-                else:
-                    self.mail_cc_addresses = []
-            elif isinstance(config['mail_cc_addresses'], list):
-                self.mail_cc_addresses = config['mail_cc_addresses']
-
-    # -------------------------------------------------------------------------
-    def init_logging(self):
-        """
-        Initialize the logger object.
-        It creates a colored loghandler with all output to STDERR.
-        Maybe overridden in descendant classes.
-
-        @return: None
-        """
-
-        root_log = logging.getLogger()
-        root_log.setLevel(logging.INFO)
-        if self.verbose:
-            root_log.setLevel(logging.DEBUG)
-
-        # create formatter
-        format_str = ''
-        if 'REQUEST_METHOD' in os.environ or self.verbose > 1:
-            format_str = '[%(asctime)s]: '
-        format_str += self.appname + ': '
-        if self.verbose:
-            if self.verbose > 1:
-                format_str += '%(name)s(%(lineno)d) %(funcName)s() '
-            else:
-                format_str += '%(name)s '
-        format_str += '%(levelname)s - %(message)s'
-        formatter = logging.Formatter(format_str)
+    def evaluate_config(self, config, yaml_file):
 
-        if 'REQUEST_METHOD' in os.environ:
+        super(R10kHookApp, self).evaluate_config(config, yaml_file)
 
-            #sys.stderr.write("Trying to open logfile {!r} ...\n".format(self.logfile))
-            # we are in a CGI environment
-            if os.path.isdir(self.log_directory) and os.access(self.log_directory, os.W_OK):
-                lh_file = logging.FileHandler(
-                    self.logfile, mode='a', encoding='utf-8', delay=True)
-                if self.verbose:
-                    lh_file.setLevel(logging.DEBUG)
-                else:
-                    lh_file.setLevel(logging.INFO)
-                lh_file.setFormatter(formatter)
-                root_log.addHandler(lh_file)
+        if 'ignore_projects' in config:
+            if config['ignore_projects'] is None:
+                self.ignore_projects = []
+            elif isinstance(config['ignore_projects'], str):
+                if config['ignore_projects']:
+                    self.ignore_projects = [config['ignore_projects']]
+            elif isinstance(config['ignore_projects'], list):
+                self.ignore_projects = config['ignore_projects']
 
-        else:
-            # create log handler for console output
-            lh_console = logging.StreamHandler(sys.stderr)
-            if self.verbose:
-                lh_console.setLevel(logging.DEBUG)
-            else:
-                lh_console.setLevel(logging.INFO)
-            lh_console.setFormatter(formatter)
-
-            root_log.addHandler(lh_console)
-
-        return
+        if 'add_ignore_projects' in config and config['add_ignore_projects']:
+            if isinstance(config['add_ignore_projects'], str):
+                self.ignore_projects.append(config['add_ignore_projects'])
+            elif isinstance(config['add_ignore_projects'], list):
+                self.ignore_projects += config['add_ignore_projects']
 
     # -------------------------------------------------------------------------
-    def print_out(self, *objects, sep=' ', end='\n', file=sys.stdout.buffer, flush=True):
-
-        file.write(to_bytes(sep.join(map(lambda x: str(x), objects))))
-        if end:
-            file.write(to_bytes(end))
-        if flush:
-            file.flush()
-
-    # -------------------------------------------------------------------------
-    def __call__(self):
-        """Helper method to make the resulting object callable."""
-
-        self.print_out("Content-Type: text/plain;charset=utf-8\n")
-        self.print_out("Python CGI läuft.\n")
-
-        if self.verbose > 1:
-            LOG.debug("Base directory: {!r}".format(self.base_dir))
+    def run(self):
+        """Main routine."""
 
+        LOG.info("Starting {} ...".format(self.appname))
 
 
 # =============================================================================
diff --git a/r10k-hook.yaml b/r10k-hook.yaml
new file mode 100644 (file)
index 0000000..aecbeec
--- /dev/null
@@ -0,0 +1,4 @@
+---
+#add_ignore_projects:
+#  - nova
+
index b7a318987a0df17eceaa2952787f4d0b1f568658..8091d8b37475b4b557cb987d224496bfeddf4c07 100755 (executable)
@@ -20,7 +20,7 @@ from webhooks.r10k import R10kHookApp
 MY_APPNAME = 'r10k-hook'
 LOG = logging.getLogger(MY_APPNAME)
 
-app = R10kHookApp(appname=MY_APPNAME)
+app = R10kHookApp(appname=MY_APPNAME, verbose=3)
 
 if app.verbose > 2:
     LOG.debug("{c} object:\n{o}".format(c=app.__class__.__name__, o=app))