summaryrefslogtreecommitdiffstats
path: root/gitautodeploy/gitautodeploy.py
diff options
context:
space:
mode:
authorOliver Poignant <oliver@poignant.se>2016-03-06 23:10:30 +0100
committerOliver Poignant <oliver@poignant.se>2016-03-06 23:10:30 +0100
commitde6bc048c63976443fa6fbe00d6b65439bd54a82 (patch)
tree77bccdd71c36ec5ee1e597a995d1e39a309fd6eb /gitautodeploy/gitautodeploy.py
parent9bb81f8c8e75278db86868575fc47394b0541eef (diff)
downloadGit-Auto-Deploy-de6bc048c63976443fa6fbe00d6b65439bd54a82.zip
Git-Auto-Deploy-de6bc048c63976443fa6fbe00d6b65439bd54a82.tar.gz
Git-Auto-Deploy-de6bc048c63976443fa6fbe00d6b65439bd54a82.tar.bz2
Added default config values and the ability to start the server without a config file. Moved HTTP server implementation to separate file.
Diffstat (limited to 'gitautodeploy/gitautodeploy.py')
-rw-r--r--gitautodeploy/gitautodeploy.py215
1 files changed, 20 insertions, 195 deletions
diff --git a/gitautodeploy/gitautodeploy.py b/gitautodeploy/gitautodeploy.py
index b7934c1..7191b8c 100644
--- a/gitautodeploy/gitautodeploy.py
+++ b/gitautodeploy/gitautodeploy.py
@@ -1,5 +1,3 @@
-from BaseHTTPServer import BaseHTTPRequestHandler
-
class LogInterface(object):
"""Interface that functions as a stdout and stderr handler and directs the
output to the logging module, which in turn will output to either console,
@@ -13,184 +11,6 @@ class LogInterface(object):
for line in msg.strip().split("\n"):
self.level(line)
-class WebhookRequestHandler(BaseHTTPRequestHandler):
- """Extends the BaseHTTPRequestHandler class and handles the incoming
- HTTP requests."""
-
- def do_POST(self):
- """Invoked on incoming POST requests"""
- from threading import Timer
- import logging
-
- logger = logging.getLogger()
-
- content_type = self.headers.getheader('content-type')
- content_length = int(self.headers.getheader('content-length'))
- request_body = self.rfile.read(content_length)
-
- # Extract request headers and make all keys to lowercase (makes them easier to compare)
- request_headers = dict(self.headers)
- request_headers = dict((k.lower(), v) for k, v in request_headers.iteritems())
-
- ServiceRequestParser = self.figure_out_service_from_request(request_headers, request_body)
-
- self.send_response(200)
- self.send_header('Content-type', 'text/plain')
- self.end_headers()
-
- # Unable to identify the source of the request
- if not ServiceRequestParser:
- logger.error('Unable to find appropriate handler for request. The source service is not supported.')
- return
-
- # Could be GitHubParser, GitLabParser or other
- repo_configs, ref, action = ServiceRequestParser(self._config).get_repo_params_from_request(request_headers, request_body)
-
- #if success:
- # print "Successfullt handled request using %s" % ServiceHandler.__name__
- #else:
- # print "Unable to handle request using %s" % ServiceHandler.__name__
-
- if len(repo_configs) == 0:
- logger.warning('Unable to find any of the repository URLs in the config: %s' % ', '.join(repo_urls))
- return
-
- # Wait one second before we do git pull (why?)
- Timer(1.0, self.process_repositories, (repo_configs,
- ref,
- action)).start()
-
- def log_message(self, format, *args):
- """Overloads the default message logging method to allow messages to
- go through our custom logger instead."""
- import logging
- logger = logging.getLogger()
- logger.info("%s - - [%s] %s\n" % (self.client_address[0],
- self.log_date_time_string(),
- format%args))
-
- def figure_out_service_from_request(self, request_headers, request_body):
- """Parses the incoming request and attempts to determine whether
- it originates from GitHub, GitLab or any other known service."""
- import json
- import logging
- import parsers
-
- logger = logging.getLogger()
- data = json.loads(request_body)
-
- user_agent = 'user-agent' in request_headers and request_headers['user-agent']
- content_type = 'content-type' in request_headers and request_headers['content-type']
-
- # Assume GitLab if the X-Gitlab-Event HTTP header is set
- if 'x-gitlab-event' in request_headers:
-
- logger.info("Received event from GitLab")
- return parsers.GitLabRequestParser
-
- # Assume GitHub if the X-GitHub-Event HTTP header is set
- elif 'x-github-event' in request_headers:
-
- logger.info("Received event from GitHub")
- return parsers.GitHubRequestParser
-
- # Assume BitBucket if the User-Agent HTTP header is set to
- # 'Bitbucket-Webhooks/2.0' (or something similar)
- elif user_agent and user_agent.lower().find('bitbucket') != -1:
-
- logger.info("Received event from BitBucket")
- return parsers.BitBucketRequestParser
-
- # Special Case for Gitlab CI
- elif content_type == "application/json" and "build_status" in data:
-
- logger.info('Received event from Gitlab CI')
- return parsers.GitLabCIRequestParser
-
- # This handles old GitLab requests and Gogs requests for example.
- elif content_type == "application/json":
-
- logger.info("Received event from unknown origin.")
- return parsers.GenericRequestParser
-
- logger.error("Unable to recognize request origin. Don't know how to handle the request.")
- return
-
-
- def process_repositories(self, repo_configs, ref, action):
- import os
- import time
- import logging
- from wrappers import GitWrapper
- from lock import Lock
- from exceptions import FilterMatchError
-
- logger = logging.getLogger()
-
- # Process each matching repository
- for repo_config in repo_configs:
-
- try:
- # Verify that all filters matches the request if specified
- if 'filters' in repo_config:
- for filter in repo_config['filters']:
- if filter['type'] == 'pull-request-filter':
- if filter['ref'] == ref and filter['action'] == action:
- continue
- raise FilterMatchError()
- else:
- logger.error('Unrecognized filter: ' % filter)
- raise FilterMatchError()
-
- except FilterMatchError as e:
- continue
-
-
- # In case there is no path configured for the repository, no pull will
- # be made.
- if not 'path' in repo_config:
- GitWrapper.deploy(repo_config)
- continue
-
- running_lock = Lock(os.path.join(repo_config['path'], 'status_running'))
- waiting_lock = Lock(os.path.join(repo_config['path'], 'status_waiting'))
- try:
-
- # Attempt to obtain the status_running lock
- while not running_lock.obtain():
-
- # If we're unable, try once to obtain the status_waiting lock
- if not waiting_lock.has_lock() and not waiting_lock.obtain():
- logger.error("Unable to obtain the status_running lock nor the status_waiting lock. Another process is " +
- "already waiting, so we'll ignore the request.")
-
- # If we're unable to obtain the waiting lock, ignore the request
- break
-
- # 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:
- logger.error('Error during \'pull\' or \'deploy\' operation on path: %s' % repo_config['path'])
- logger.error(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()
-
class GitAutoDeploy(object):
_instance = None
@@ -280,12 +100,12 @@ class GitAutoDeploy(object):
# Look for a *conf.json or *config.json
for dir in target_directories:
for item in os.listdir(dir):
- if re.match(r"conf(ig)?\.json$", item):
+ if re.match(r".*conf(ig)?\.json$", item):
path = os.path.realpath(os.path.join(dir, item))
logger.info("Using '%s' as config" % path)
return path
- return './GitAutoDeploy.conf.json'
+ return
def read_json_file(self, file_path):
import json
@@ -323,7 +143,7 @@ class GitAutoDeploy(object):
'url': os.environ['GAD_REPO_URL']
}
- logger.info("Added configuration for '%s' found environment variables" % os.environ['GAD_REPO_URL'])
+ logger.info("Added configuration for '%s' found in environment variables" % os.environ['GAD_REPO_URL'])
if 'GAD_REPO_BRANCH' in os.environ:
repo_config['branch'] = os.environ['GAD_REPO_BRANCH']
@@ -371,7 +191,7 @@ class GitAutoDeploy(object):
repo_config['deploy_commands'] = []
# Check if any global pre deploy commands is specified
- if len(self._config['global_deploy'][0]) is not 0:
+ if 'global_deploy' in self._config and len(self._config['global_deploy'][0]) is not 0:
repo_config['deploy_commands'].insert(0, self._config['global_deploy'][0])
# Check if any repo specific deploy command is specified
@@ -379,7 +199,7 @@ class GitAutoDeploy(object):
repo_config['deploy_commands'].append(repo_config['deploy'])
# Check if any global post deploy command is specified
- if len(self._config['global_deploy'][1]) is not 0:
+ if 'global_deploy' in self._config and len(self._config['global_deploy'][1]) is not 0:
repo_config['deploy_commands'].append(self._config['global_deploy'][1])
# If a Bitbucket repository is configured using the https:// URL, a username is usually
@@ -529,17 +349,18 @@ class GitAutoDeploy(object):
import logging
import argparse
from lock import Lock
+ from httpserver import WebhookRequestHandler
# Attempt to retrieve default config values from environment variables
- default_quiet_value = 'GAD_QUIET' in os.environ
- default_daemon_mode_value = 'GAD_DAEMON_MODE' in os.environ
- default_config_value = 'GAD_CONFIG' in os.environ and os.environ['GAD_CONFIG']
- default_ssh_keygen_value = 'GAD_SSH_KEYGEN' in os.environ
- default_force_value = 'GAD_FORCE' in os.environ
- default_pid_file_value = 'GAD_PID_FILE' in os.environ and os.environ['GAD_PID_FILE']
- default_log_file_value = 'GAD_LOG_FILE' in os.environ and os.environ['GAD_LOG_FILE']
- default_host_value = 'GAD_HOST' in os.environ and os.environ['GAD_HOST']
- default_port_value = 'GAD_PORT' in os.environ and int(os.environ['GAD_PORT'])
+ default_quiet_value = 'GAD_QUIET' in os.environ or False
+ default_daemon_mode_value = 'GAD_DAEMON_MODE' in os.environ or False
+ default_config_value = 'GAD_CONFIG' in os.environ and os.environ['GAD_CONFIG'] or None
+ default_ssh_keygen_value = 'GAD_SSH_KEYGEN' in os.environ or False
+ default_force_value = 'GAD_FORCE' in os.environ or False
+ default_pid_file_value = 'GAD_PID_FILE' in os.environ and os.environ['GAD_PID_FILE'] or '~/.gitautodeploy.pid'
+ default_log_file_value = 'GAD_LOG_FILE' in os.environ and os.environ['GAD_LOG_FILE'] or None
+ default_host_value = 'GAD_HOST' in os.environ and os.environ['GAD_HOST'] or '0.0.0.0'
+ default_port_value = 'GAD_PORT' in os.environ and int(os.environ['GAD_PORT']) or 8001
parser = argparse.ArgumentParser()
@@ -616,7 +437,11 @@ class GitAutoDeploy(object):
config_file_path = self.find_config_file_path()
# Read config data from json file
- config_data = self.read_json_file(config_file_path)
+ if config_file_path:
+ config_data = self.read_json_file(config_file_path)
+ else:
+ logger.info('No configuration file found or specified. Using default values.')
+ config_data = {}
# Configuration options coming from environment or command line will
# override those coming from config file