summaryrefslogtreecommitdiffstats
path: root/gitautodeploy
diff options
context:
space:
mode:
Diffstat (limited to 'gitautodeploy')
-rw-r--r--gitautodeploy/cli/config.py5
-rw-r--r--gitautodeploy/events.py65
-rw-r--r--gitautodeploy/gitautodeploy.py59
-rw-r--r--gitautodeploy/httpserver.py53
-rw-r--r--gitautodeploy/wwwroot/index.html1
5 files changed, 143 insertions, 40 deletions
diff --git a/gitautodeploy/cli/config.py b/gitautodeploy/cli/config.py
index 69491c5..e302da4 100644
--- a/gitautodeploy/cli/config.py
+++ b/gitautodeploy/cli/config.py
@@ -27,7 +27,10 @@ def get_config_defaults():
config['log-test-case'] = False
config['log-test-case-dir'] = None
- config['remote-whitelist'] = ['127.0.0.1']
+ config['web-ui'] = {
+ 'enabled': False,
+ 'remote-whitelist': ['127.0.0.1']
+ }
return config
diff --git a/gitautodeploy/events.py b/gitautodeploy/events.py
index b021165..bbf93a2 100644
--- a/gitautodeploy/events.py
+++ b/gitautodeploy/events.py
@@ -1,4 +1,4 @@
-class Action(object):
+class SystemEvent(object):
def __init__(self, name=None):
import logging
@@ -7,16 +7,25 @@ class Action(object):
self.hub = None
self.messages = []
self.name = name
+ self.id = None
+ self.waiting = None
+ self.success = None
def __repr__(self):
- if self.name:
- return "<Action:%s>" % self.name
+ if self.id:
+ return "<SystemEvent:%s>" % self.id
else:
- return "<Action>"
+ return "<SystemEvent>"
def dict_repr(self):
+ from time import time
return {
- "messages": self.messages
+ "id": self.id,
+ "type": type(self).__name__,
+ "timestamp": time(),
+ "messages": self.messages,
+ "waiting": self.waiting,
+ "success": self.success
}
def register_hub(self, hub):
@@ -26,6 +35,21 @@ class Action(object):
self.messages.append(message)
self.hub.update_action(self, message)
+ def notify(self):
+ self.hub.notify(self)
+
+ def set_id(self, id):
+ self.id = id
+
+ def get_id(self):
+ return self.id
+
+ def set_waiting(self, value):
+ self.waiting = value
+
+ def set_success(self, value):
+ self.success = value
+
def log_debug(self, message):
self.logger.debug(message)
self.register_message(message, "DEBUG")
@@ -49,7 +73,7 @@ class Action(object):
self.hub.update_action(self)
-class WebhookAction(Action):
+class WebhookAction(SystemEvent):
"""Represents a webhook request event and keeps a copy of all incoming and outgoing data for monitoring purposes."""
def __init__(self, client_address, request_headers, request_body):
@@ -63,8 +87,27 @@ class WebhookAction(Action):
def dict_repr(self):
data = super(WebhookAction, self).dict_repr()
- data['request_headers'] = self.request_headers
- data['request_body'] = self.request_body
+ data['client-address'] = self.client_address[0]
+ data['client-port'] = self.client_address[1]
+ data['request-headers'] = self.request_headers
+ data['request-body'] = self.request_body
+ return data
+
+
+class StartupEvent(SystemEvent):
+
+ def __init__(self, address=None, port=None):
+ self.address = address
+ self.port = port
+ super(StartupEvent, self).__init__()
+
+ def __repr__(self):
+ return "<StartupEvent>"
+
+ def dict_repr(self):
+ data = super(StartupEvent, self).dict_repr()
+ data['address'] = self.address
+ data['port'] = self.port
return data
@@ -73,6 +116,7 @@ class EventStore(object):
def __init__(self):
self.actions = []
self.observers = []
+ self.next_id = 0
def register_observer(self, observer):
self.observers.append(observer)
@@ -86,7 +130,9 @@ class EventStore(object):
observer.update(*args, **kwargs)
def register_action(self, action):
+ action.set_id(self.next_id)
action.register_hub(self)
+ self.next_id = self.next_id + 1
self.actions.append(action)
self.update_observers(action)
@@ -94,6 +140,9 @@ class EventStore(object):
if len(self.actions) > 100:
self.actions.pop(0)
+ def notify(self, event):
+ self.update_observers(event=event)
+
def update_action(self, action, message=None):
self.update_observers(action, message)
diff --git a/gitautodeploy/gitautodeploy.py b/gitautodeploy/gitautodeploy.py
index 167fae8..c0d5b21 100644
--- a/gitautodeploy/gitautodeploy.py
+++ b/gitautodeploy/gitautodeploy.py
@@ -18,6 +18,9 @@ class GitAutoDeploy(object):
_config = {}
_port = None
_pid = None
+ _event_store = None
+ _default_stdout = None
+ _default_stderr = None
def __new__(cls, *args, **kwargs):
"""Overload constructor to enable singleton access"""
@@ -227,11 +230,8 @@ class GitAutoDeploy(object):
return 0
- def update(self, action, message=None):
+ def update(self, *args, **kwargs):
pass
- #print "%s was updated" % action,
-# if message:
-# print "Message: %s" % message
def setup(self, config):
"""Setup an instance of GAD based on the provided config object."""
@@ -242,7 +242,7 @@ class GitAutoDeploy(object):
import logging
from lock import Lock
from httpserver import WebhookRequestHandlerFactory
- from events import EventStore
+ from events import EventStore, StartupEvent
# This solves https://github.com/olipo186/Git-Auto-Deploy/issues/118
try:
@@ -257,6 +257,12 @@ class GitAutoDeploy(object):
# Attatch config values to this instance
self._config = config
+ self._event_store = EventStore()
+ self._event_store.register_observer(self)
+
+ startup_event = StartupEvent()
+ self._event_store.register_action(startup_event)
+
# Set up logging
logger = logging.getLogger()
logFormatter = logging.Formatter("%(asctime)s [%(levelname)-5.5s] %(message)s")
@@ -290,11 +296,11 @@ class GitAutoDeploy(object):
logger.addHandler(fileHandler)
if 'ssh-keyscan' in self._config and self._config['ssh-keyscan']:
- logger.info('Scanning repository hosts for ssh keys...')
+ startup_event.log_info('Scanning repository hosts for ssh keys...')
self.ssh_key_scan()
if 'force' in self._config and self._config['force']:
- logger.info('Attempting to kill any other process currently occupying port %s' % self._config['port'])
+ startup_event.log_info('Attempting to kill any other process currently occupying port %s' % self._config['port'])
self.kill_conflicting_processes()
# Clone all repos once initially
@@ -309,10 +315,10 @@ class GitAutoDeploy(object):
sys.stderr = LogInterface(logger.error)
if 'daemon-mode' in self._config and self._config['daemon-mode']:
- logger.info('Starting Git Auto Deploy in daemon mode')
+ startup_event.log_info('Starting Git Auto Deploy in daemon mode')
GitAutoDeploy.create_daemon()
else:
- logger.info('Git Auto Deploy started')
+ startup_event.log_info('Git Auto Deploy started')
self._pid = os.getpid()
self.create_pid_file()
@@ -327,11 +333,8 @@ class GitAutoDeploy(object):
try:
- event_store = EventStore()
- event_store.register_observer(self)
-
# Create web hook request handler class
- WebhookRequestHandler = WebhookRequestHandlerFactory(self._config, event_store)
+ WebhookRequestHandler = WebhookRequestHandlerFactory(self._config, self._event_store)
self._server = HTTPServer((self._config['host'],
self._config['port']),
@@ -344,14 +347,16 @@ class GitAutoDeploy(object):
certfile=os.path.expanduser(self._config['ssl-pem']),
server_side=True)
sa = self._server.socket.getsockname()
- logger.info("Listening on %s port %s", sa[0], sa[1])
+ startup_event.log_info("Listening on %s port %s" % (sa[0], sa[1]))
+ startup_event.address = sa[0]
+ startup_event.port = sa[1]
+ startup_event.notify()
# Actual port bound to (nessecary when OS picks randomly free port)
self._port = sa[1]
except socket.error, e:
-
- logger.critical("Error on socket: %s" % e)
+ startup_event.log_critical("Error on socket: %s" % e)
GitAutoDeploy.debug_diagnosis(self._config['port'])
sys.exit(1)
@@ -361,6 +366,15 @@ class GitAutoDeploy(object):
import sys
import socket
import logging
+ import os
+ from events import SystemEvent
+
+ # Add script dir to sys path, allowing us to import sub modules even after changing cwd
+ sys.path.insert(1, os.path.dirname(os.path.realpath(__file__)))
+
+ # Set CWD to public www folder. This makes the http server serve files from the wwwroot directory.
+ wwwroot = os.path.join(os.path.dirname(os.path.realpath(__file__)), "wwwroot")
+ os.chdir(wwwroot)
# Set up logging
logger = logging.getLogger()
@@ -370,10 +384,16 @@ class GitAutoDeploy(object):
except socket.error, e:
logger.critical("Error on socket: %s" % e)
+ event = SystemEvent()
+ self._event_store.register_action(event)
+ event.log_critical("Error on socket: %s" % e)
sys.exit(1)
except KeyboardInterrupt, e:
logger.info('Requested close by keyboard interrupt signal')
+ event = SystemEvent()
+ self._event_store.register_action(event)
+ event.log_info('Requested close by keyboard interrupt signal')
self.stop()
self.exit()
@@ -404,6 +424,7 @@ class GitAutoDeploy(object):
self._server.socket.close()
def signal_handler(self, signum, frame):
+ from events import SystemEvent
import logging
logger = logging.getLogger()
self.stop()
@@ -415,9 +436,15 @@ class GitAutoDeploy(object):
elif signum == 2:
logger.info('Requested close by keyboard interrupt signal')
+ event = SystemEvent()
+ self._event_store.register_action(event)
+ event.log_info('Requested close by keyboard interrupt signal')
elif signum == 6:
logger.info('Requested close by SIGABRT (process abort signal). Code 6.')
+ event = SystemEvent()
+ self._event_store.register_action(event)
+ event.log_info('Requested close by SIGABRT (process abort signal). Code 6.')
self.exit()
diff --git a/gitautodeploy/httpserver.py b/gitautodeploy/httpserver.py
index 67e9744..38f28ff 100644
--- a/gitautodeploy/httpserver.py
+++ b/gitautodeploy/httpserver.py
@@ -255,8 +255,9 @@ class WebhookRequestFilter(object):
def WebhookRequestHandlerFactory(config, event_store):
"""Factory method for webhook request handler class"""
+ from SimpleHTTPServer import SimpleHTTPRequestHandler
- class WebhookRequestHandler(BaseHTTPRequestHandler, object):
+ class WebhookRequestHandler(SimpleHTTPRequestHandler, object):
"""Extends the BaseHTTPRequestHandler class and handles the incoming
HTTP requests."""
@@ -265,20 +266,36 @@ def WebhookRequestHandlerFactory(config, event_store):
self.event_store = event_store
super(WebhookRequestHandler, self).__init__(*args, **kwargs)
+ def end_headers (self):
+ self.send_header('Access-Control-Allow-Origin', '*')
+ SimpleHTTPRequestHandler.end_headers(self)
+
+ def do_HEAD(self):
+ import json
+
+ if not self._config['web-ui']['enabled'] or not self.client_address[0] in self._config['web-ui']['remote-whitelist']:
+ self.send_error(403)
+ return
+
+ return SimpleHTTPRequestHandler.do_HEAD(self)
+
def do_GET(self):
import json
- if not self.client_address[0] in self._config['remote-whitelist']:
+ if not self._config['web-ui']['enabled'] or not self.client_address[0] in self._config['web-ui']['remote-whitelist']:
self.send_error(403)
return
- data = self.event_store.dict_repr()
+ if self.path == "/api/status":
+ data = self.event_store.dict_repr()
+ self.send_response(200, 'OK')
+ self.send_header('Content-type', 'application/json')
+ self.end_headers()
+ self.wfile.write(json.dumps(data))
+ self.wfile.close()
+ return
- self.send_response(200, 'OK')
- self.send_header('Content-type', 'application/json')
- self.end_headers()
- self.wfile.write(json.dumps(data))
- self.wfile.close()
+ return SimpleHTTPRequestHandler.do_GET(self)
def do_POST(self):
"""Invoked on incoming POST requests"""
@@ -296,7 +313,8 @@ def WebhookRequestHandlerFactory(config, event_store):
request_headers = dict(self.headers)
request_headers = dict((k.lower(), v) for k, v in request_headers.iteritems())
- action = WebhookAction(self.client_address, request_body, request_headers)
+ action = WebhookAction(self.client_address, request_headers, request_body)
+ action.set_waiting(True)
event_store.register_action(action)
action.log_info('Incoming request from %s:%s' % (self.client_address[0], self.client_address[1]))
@@ -361,7 +379,7 @@ def WebhookRequestHandlerFactory(config, event_store):
action.log_info("Filter does not match")
return
- action.log_info("Deploying")
+ action.log_info("Executing deploy commands")
# Schedule the execution of the webhook (git pull and trigger deploy etc)
request_processor.execute_webhook(repo_configs, request_headers, request_body, action)
@@ -374,12 +392,16 @@ def WebhookRequestHandlerFactory(config, event_store):
'deploy': 'echo test!'
}
- action.log_info("Done")
+ action.log_info("Deploy commands were executed")
+ action.set_success(True)
+ action.update()
except ValueError, e:
self.send_error(400, 'Unprocessable request')
action.log_warning('Unable to process incoming request from %s:%s' % (self.client_address[0], self.client_address[1]))
test_case['expected']['status'] = 400
+ action.set_success(False)
+ action.update()
return
except Exception, e:
@@ -389,6 +411,8 @@ def WebhookRequestHandlerFactory(config, event_store):
test_case['expected']['status'] = 500
action.log_warning("Unable to process request")
+ action.set_success(False)
+ action.update()
raise e
@@ -398,6 +422,9 @@ def WebhookRequestHandlerFactory(config, event_store):
if 'log-test-case' in self._config and self._config['log-test-case']:
self.save_test_case(test_case)
+ action.set_waiting(False)
+ action.update()
+
def log_message(self, format, *args):
"""Overloads the default message logging method to allow messages to
go through our custom logger instead."""
@@ -428,7 +455,3 @@ def WebhookRequestHandlerFactory(config, event_store):
return WebhookRequestHandler
-class WebhookRequestHandler(BaseHTTPRequestHandler):
- """Extends the BaseHTTPRequestHandler class and handles the incoming
- HTTP requests."""
-
diff --git a/gitautodeploy/wwwroot/index.html b/gitautodeploy/wwwroot/index.html
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/gitautodeploy/wwwroot/index.html
@@ -0,0 +1 @@
+