From bec01cd2afd31c1533b42d6c32f0f16ffa4b7f0a Mon Sep 17 00:00:00 2001
From: Dennis Ahrens <dennis.ahrens@hs-hannover.de>
Date: Thu, 2 Oct 2014 15:59:21 +0200
Subject: [PATCH] [TASK] Avoid execution of the same configuration.

By using pid files in the working directory. If there
is a pidfile at <abs_config_path>.pid, hshetl will not
execute and print en error on stdout.

This enhancement was made for proper behaviour when
using hshetl with cron.
---
 hshetl/cli.py | 30 ++++++++++++++++++++++++++----
 hshetl/exc.py |  4 ++++
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/hshetl/cli.py b/hshetl/cli.py
index 402963c..3732f64 100644
--- a/hshetl/cli.py
+++ b/hshetl/cli.py
@@ -19,7 +19,7 @@ import logging
 from connectors import connector_repository
 from entities import entity_repository
 from jobs import JobList
-from exc import ConfigurationException, NotMatchingArgumentsException, NotMatchingYAMLTagException, UnknownNameReferenceException
+from exc import ConfigurationException, NotMatchingArgumentsException, NotMatchingYAMLTagException, UnknownNameReferenceException, DuplicatedProcessException
 import hshetl
 
 
@@ -156,16 +156,23 @@ class Controller(object):
         if self.config_file[:1] == '/': return self.config_file
         else: return self.working_directory + '/' + self.config_file
 
+    def abs_pid_file(self):
+        '''Resolves the name of the pid file related to this config.'''
+        return self.abs_config_file + '.pid'
+
     def configure(self):
         '''Reads the yaml configuration from file and sets internal variables.'''
         try:
+            self.check_pid_file()
             self.jobs.extend(self._configuration_parser.parse(self.abs_config_file))
         except IOError as ioe:
             sys.exit('Can\'t read configuration file: {}'.format(str(ioe)))
         except yaml.parser.ParserError as pe:
             sys.exit('Error while parsing YAML configuration: {}'.format(str(pe)))
         except ConfigurationException as ce:
-            sys.exit('Error in Configuration: {}'.format(str(ce)))
+            sys.exit('Error in configuration: {}'.format(str(ce)))
+        except DuplicatedProcessException as dpe:
+            sys.exit('Error on execution: {}'.format(str(dpe)))
         return self
 
     def execute(self):
@@ -184,10 +191,25 @@ class Controller(object):
         except KeyboardInterrupt:
             logging.critical('The execution was manually interrupted.')
         finally:
-            connector_repository.shutdown()
-            entity_repository.shutdown()
+            self.shutdown()
         logging.info('The whole execution took {} seconds'.format(str(time.time() - self.start_time)))
 
+    def check_pid_file(self):
+        '''Creates a file holding the current process id, to check whether we currently run or not.'''
+        pidfile = self.abs_pid_file()
+        if os.path.isfile(pidfile):
+            with open(pidfile, 'r') as f:
+                raise DuplicatedProcessException('A process with pid %s currently works on the same configuration file (%s).' % (f.read(), self.abs_config_file))
+        else:
+            with open(pidfile, 'w') as f:
+                f.write(str(os.getpid()))
+
+    def shutdown(self):
+        '''Shutdown the repositories and remove the pid file.'''
+        connector_repository.shutdown()
+        entity_repository.shutdown()
+        os.unlink(self.abs_pid_file())
+
     def make_interactive(self, jobs):
         '''Applies interactive mode to all jobs.'''
         for job in jobs:
diff --git a/hshetl/exc.py b/hshetl/exc.py
index 099f83a..b70efa2 100644
--- a/hshetl/exc.py
+++ b/hshetl/exc.py
@@ -78,3 +78,7 @@ class DataTypeConversionException(Exception):
     '''Raised if a value can not be converted to wanted type.'''
     pass
 
+
+class DuplicatedProcessException(Exception):
+    '''Raised if another process is working on the given config currently'''
+    pass
\ No newline at end of file
-- 
GitLab