summaryrefslogtreecommitdiffstats
path: root/GitAutoDeploy.py
diff options
context:
space:
mode:
authorOliver Poignant <oliver@poignant.se>2015-08-18 19:18:13 +0200
committerOliver Poignant <oliver@poignant.se>2015-08-18 19:18:13 +0200
commitbaca5b9a49e1b8cbac1fe7a27e146cc316158b9c (patch)
tree320f7fd92da19813ad4cb0740b4eb0dd25af8bc7 /GitAutoDeploy.py
parent53bfd3951daad49a28d773b54e608ee4e3f657f4 (diff)
downloadGit-Auto-Deploy-baca5b9a49e1b8cbac1fe7a27e146cc316158b9c.zip
Git-Auto-Deploy-baca5b9a49e1b8cbac1fe7a27e146cc316158b9c.tar.gz
Git-Auto-Deploy-baca5b9a49e1b8cbac1fe7a27e146cc316158b9c.tar.bz2
Implemented fs mutex lock in Python instead of sh
Diffstat (limited to 'GitAutoDeploy.py')
-rwxr-xr-xGitAutoDeploy.py159
1 files changed, 111 insertions, 48 deletions
diff --git a/GitAutoDeploy.py b/GitAutoDeploy.py
index d5337f9..126e86e 100755
--- a/GitAutoDeploy.py
+++ b/GitAutoDeploy.py
@@ -1,6 +1,50 @@
#!/usr/bin/env python
+class Lock():
+ """Simple implementation of a mutex lock using the file systems. Works on *nix systems."""
+
+ path = None
+ _has_lock = False
+
+ def __init__(self, path):
+ self.path = path
+
+ def obtain(self):
+ import os
+ try:
+ os.open(self.path, os.O_CREAT | os.O_EXCL | os.O_WRONLY)
+ self._has_lock = True
+ print "Successfully obtained lock: %s" % self.path
+ except OSError:
+ return False
+ else:
+ return True
+
+ def release(self):
+ import os
+ if not self._has_lock:
+ raise Exception("Unable to release lock that is owned by another process")
+ try:
+ os.remove(self.path)
+ print "Successfully released lock: %s" % self.path
+ finally:
+ self._has_lock = False
+
+ def has_lock(self):
+ return self._has_lock
+
+ def clear(self):
+ import os
+ try:
+ os.remove(self.path)
+ except OSError:
+ pass
+ finally:
+ print "Successfully cleared lock: %s" % self.path
+ self._has_lock = False
+
+
class GitWrapper():
"""Wraps the git client. Currently uses git through shell command invocations."""
@@ -25,6 +69,12 @@ class GitWrapper():
return res
@staticmethod
+ def clone(url, path):
+ from subprocess import call
+ call(['git clone --recursive %s %s' % (url, path)], shell=True)
+
+
+ @staticmethod
def deploy(repo_config):
"""Executes any supplied post-pull deploy command"""
from subprocess import call
@@ -131,7 +181,7 @@ class WebhookRequestHandler(BaseHTTPRequestHandler):
class GitAutoDeploy(object):
- CONFIG_FILE_PATH = './GitAutoDeploy.conf.json'
+ config_path = './GitAutoDeploy.conf.json'
debug = True
daemon = False
@@ -147,26 +197,10 @@ class GitAutoDeploy(object):
return cls._instance
@staticmethod
- def lock(path):
- from subprocess import call
- return 0 == call(['sh lock.sh "' + path + '"'], shell=True)
-
- @staticmethod
- def unlock(path):
- from subprocess import call
- call(['sh unlock.sh "' + path + '"'], shell=True)
-
- @staticmethod
- def clear_lock(path):
- from subprocess import call
- call(['sh clear_lock.sh "' + path + '"'], shell=True)
-
- @staticmethod
- def debug_diagnosis():
+ def debug_diagnosis(port):
if GitAutoDeploy.debug is False:
return
- port = GitAutoDeploy().get_config()['port']
pid = GitAutoDeploy.get_pid_on_port(port)
if pid is False:
print 'I don\'t know the number of pid that is using my configured port'
@@ -218,7 +252,8 @@ class GitAutoDeploy(object):
@staticmethod
def process_repo_urls(urls):
- from subprocess import call
+ import os
+ import time
repo_config = GitAutoDeploy().get_matching_repo_config(urls)
@@ -226,65 +261,87 @@ class GitAutoDeploy(object):
print 'Unable to find any of the repository URLs in the config: %s' % ', '.join(urls)
return
- if GitAutoDeploy.lock(repo_config['path']):
+ running_lock = Lock(os.path.join(repo_config['path'], 'status_running'))
+ waiting_lock = Lock(os.path.join(repo_config['path'], 'status_waiting'))
+ try:
- try:
- n = 4
- while 0 < n and 0 != GitWrapper.pull(repo_config):
- n -= 1
- if 0 < n:
- GitWrapper.deploy(repo_config)
+ # Attempt to obtain the status_running lock
+ while not running_lock.obtain():
- except Exception as e:
- print e
- call(['echo "Error during \'pull\' or \'deploy\' operation on path: ' + repo_config['path'] + '"'],
- shell=True)
+ # If we're unable, try once to obtain the status_waiting lock
+ if not waiting_lock.has_lock() and not waiting_lock.obtain():
+ print "Unable to obtain the status_running lock nor the status_waiting lock. Another process is "\
+ + "already waiting, so we'll ignore the request."
- finally:
- GitAutoDeploy.unlock(repo_config['path'])
+ # If we're unable to obtain the waiting lock, ignore the request
+ return
+
+ # Keep on attempting to obtain the status_running lock until we succeed
+ time.sleep(5)
+
+ n = 4
+ while 0 < n and 0 != GitWrapper.pull(repo_config):
+ n -= 1
+ if 0 < n:
+ GitWrapper.deploy(repo_config)
+
+ except Exception as e:
+ print 'Error during \'pull\' or \'deploy\' operation on path: %s' % repo_config['path']
+ print e
+
+ finally:
+
+ # Release the lock if it's ours
+ if running_lock.has_lock():
+ running_lock.release()
+
+ # Release the lock if it's ours
+ if waiting_lock.has_lock():
+ waiting_lock.release()
def get_config(self):
import json
import sys
import os
- from subprocess import call
if self._config:
return self._config
try:
- config_string = open(self.CONFIG_FILE_PATH).read()
+ config_string = open(self.config_path).read()
except Exception as e:
- print "Could not load %s file\n" % self.CONFIG_FILE_PATH
+ print "Could not load %s file\n" % self.config_path
raise e
try:
self._config = json.loads(config_string)
except Exception as e:
- print "%s file is not valid JSON\n" % self.CONFIG_FILE_PATH
+ print "%s file is not valid JSON\n" % self.config_path
raise e
- for repository in self._config['repositories']:
+ for repo_config in self._config['repositories']:
- if not os.path.isdir(repository['path']):
+ if not os.path.isdir(repo_config['path']):
- print "Directory %s not found" % repository['path']
- call(['git clone --recursive '+repository['url']+' '+repository['path']], shell=True)
+ print "Directory %s not found" % repo_config['path']
+ GitWrapper.clone(url=repo_config['url'], path=repo_config['path'])
- if not os.path.isdir(repository['path']):
- print "Unable to clone repository %s" % repository['url']
+ if not os.path.isdir(repo_config['path']):
+ print "Unable to clone repository %s" % repo_config['url']
sys.exit(2)
else:
- print "Repository %s successfully cloned" % repository['url']
+ print "Repository %s successfully cloned" % repo_config['url']
- if not os.path.isdir(repository['path'] + '/.git'):
- print "Directory %s is not a Git repository" % repository['path']
+ if not os.path.isdir(repo_config['path'] + '/.git'):
+ print "Directory %s is not a Git repository" % repo_config['path']
sys.exit(2)
- self.clear_lock(repository['path'])
+ # Clear any existing lock files, with no regard to possible ongoing processes
+ Lock(os.path.join(repo_config['path'], 'status_running')).clear()
+ Lock(os.path.join(repo_config['path'], 'status_waiting')).clear()
return self._config
@@ -353,7 +410,6 @@ class GitAutoDeploy(object):
def run(self):
from sys import argv
import sys
- import os
from BaseHTTPServer import HTTPServer
import socket
@@ -371,6 +427,13 @@ class GitAutoDeploy(object):
print 'Attempting to kill any other process currently occupying port %s' % self.get_config()['port']
self.kill_conflicting_processes()
+ if '--config' in argv:
+ import os
+ pos = argv.index('--config')
+ if len(argv) > pos + 1:
+ self.config_path = os.path.realpath(argv[argv.index('--config') + 1])
+ print 'Using custom configuration file \'%s\'' % self.config_path
+
if GitAutoDeploy.daemon:
pid = os.fork()
if pid > 0:
@@ -398,7 +461,7 @@ class GitAutoDeploy(object):
if not GitAutoDeploy.daemon:
print "Error on socket: %s" % e
- GitAutoDeploy.debug_diagnosis()
+ GitAutoDeploy.debug_diagnosis(self.get_config()['port'])
sys.exit(1)