summaryrefslogtreecommitdiffstats
path: root/gitautodeploy
diff options
context:
space:
mode:
Diffstat (limited to 'gitautodeploy')
-rw-r--r--gitautodeploy/cli/config.py9
-rw-r--r--gitautodeploy/events.py18
-rw-r--r--gitautodeploy/gitautodeploy.py6
-rw-r--r--gitautodeploy/httpserver.py46
-rw-r--r--gitautodeploy/webhook.py162
5 files changed, 140 insertions, 101 deletions
diff --git a/gitautodeploy/cli/config.py b/gitautodeploy/cli/config.py
index 2d2e301..3af4e3d 100644
--- a/gitautodeploy/cli/config.py
+++ b/gitautodeploy/cli/config.py
@@ -44,6 +44,7 @@ def get_config_defaults():
config['web-ui-whitelist'] = ['127.0.0.1']
config['web-ui-require-https'] = True
config['web-ui-auth-enabled'] = True
+ config['web-ui-prevent-root'] = True
# Record all log levels by default
config['log-level'] = 'NOTSET'
@@ -51,6 +52,7 @@ def get_config_defaults():
# Other options
config['intercept-stdout'] = True
config['ssh-keyscan'] = False
+ config['allow-root-user'] = False
# Include details with deploy command return codes in HTTP response. Causes
# to await any git pull or deploy command actions before it sends the
@@ -232,6 +234,13 @@ def get_config_from_argv(argv):
dest="ssl-cert",
type=str)
+ parser.add_argument("--allow-root-user",
+ help="allow running as root user",
+ dest="allow-root-user",
+ default=None,
+ action="store_true")
+
+
config = vars(parser.parse_args(argv))
# Delete entries for unprovided arguments
diff --git a/gitautodeploy/events.py b/gitautodeploy/events.py
index 3a86700..5a4cef5 100644
--- a/gitautodeploy/events.py
+++ b/gitautodeploy/events.py
@@ -95,6 +95,24 @@ class WebhookAction(SystemEvent):
return data
+class DeployEvent(SystemEvent):
+
+ def __init__(self, repo_config):
+ self.repo_config = repo_config
+ super(DeployEvent, self).__init__()
+
+ def __repr__(self):
+ return "<WebhookAction>"
+
+ def dict_repr(self):
+ data = super(DeployEvent, self).dict_repr()
+ data['name'] = self.get_name()
+ return data
+
+ def get_name(self):
+ return self.repo_config['url'].split('/')[-1].split('.git')[0]
+
+
class StartupEvent(SystemEvent):
def __init__(self, http_address=None, http_port=None, ws_address=None, ws_port=None):
diff --git a/gitautodeploy/gitautodeploy.py b/gitautodeploy/gitautodeploy.py
index 6bf8693..31e1696 100644
--- a/gitautodeploy/gitautodeploy.py
+++ b/gitautodeploy/gitautodeploy.py
@@ -172,6 +172,7 @@ class GitAutoDeploy(object):
import logging
import base64
from .lock import Lock
+ import getpass
# This solves https://github.com/olipo186/Git-Auto-Deploy/issues/118
try:
@@ -218,6 +219,11 @@ class GitAutoDeploy(object):
fileHandler.setFormatter(logFormatter)
logger.addHandler(fileHandler)
+ # Display a warning when trying to run as root
+ if not self._config['allow-root-user'] and getpass.getuser() == 'root':
+ logger.critical("Refusing to start. This application shouldn't run as root. Please run it as a different user. To disregard this warning and start anyway, set the config option \"allow-root-user\" to true, or use the command line argument --allow-root-user")
+ sys.exit()
+
if 'ssh-keyscan' in self._config and self._config['ssh-keyscan']:
self._startup_event.log_info('Scanning repository hosts for ssh keys...')
self.ssh_key_scan()
diff --git a/gitautodeploy/httpserver.py b/gitautodeploy/httpserver.py
index 65ccc3d..857f5f3 100644
--- a/gitautodeploy/httpserver.py
+++ b/gitautodeploy/httpserver.py
@@ -104,7 +104,7 @@ def WebhookRequestHandlerFactory(config, event_store, server_status, is_https=Fa
request_headers = dict((k.lower(), v) for k, v in request_headers.items())
action = WebhookAction(self.client_address, request_headers, request_body)
- event_store.register_action(action)
+ self._event_store.register_action(action)
action.set_waiting(True)
action.log_info('Incoming request from %s:%s' % (self.client_address[0], self.client_address[1]))
@@ -129,6 +129,8 @@ def WebhookRequestHandlerFactory(config, event_store, server_status, is_https=Fa
self.send_error(400, 'Unrecognized service')
test_case['expected']['status'] = 400
action.log_error("Unable to find appropriate handler for request. The source service is not supported")
+ action.set_waiting(False)
+ action.set_success(False)
return
service_handler = ServiceRequestHandler(self._config)
@@ -138,21 +140,29 @@ def WebhookRequestHandlerFactory(config, event_store, server_status, is_https=Fa
# Could be GitHubParser, GitLabParser or other
repo_configs = service_handler.get_repo_configs(request_headers, request_body, action)
+ action.log_info("%s candidates found" % len(repo_configs))
+
request_filter = WebhookRequestFilter()
if len(repo_configs) == 0:
self.send_error(400, 'Bad request')
test_case['expected']['status'] = 400
action.log_error("No matching repository config")
+ action.set_waiting(False)
+ action.set_success(False)
return
# Apply filters
repo_configs = request_filter.apply_filters(repo_configs, request_headers, request_body, action)
+ action.log_info("%s candidates matches the filters" % len(repo_configs))
+
if not service_handler.validate_request(request_headers, repo_configs, action):
self.send_error(400, 'Bad request')
test_case['expected']['status'] = 400
- action.log_warning("Request not valid")
+ action.log_warning("Request is not valid")
+ action.set_waiting(False)
+ action.set_success(False)
return
test_case['expected']['status'] = 200
@@ -162,22 +172,27 @@ def WebhookRequestHandlerFactory(config, event_store, server_status, is_https=Fa
self.end_headers()
if len(repo_configs) == 0:
- action.log_info("Filter does not match")
+ action.set_waiting(False)
+ action.set_success(False)
return
- action.log_info("Executing deploy commands")
+ action.log_info("Proceeding with %s candidates" % len(repo_configs))
+ action.set_waiting(False)
+ action.set_success(True)
+
+ for repo_config in repo_configs:
- # Schedule the execution of the webhook (git pull and trigger deploy etc)
- thread = threading.Thread(target=request_processor.execute_webhook, args=[repo_configs, request_headers, request_body, action])
- thread.start()
+ # Schedule the execution of the webhook (git pull and trigger deploy etc)
+ thread = threading.Thread(target=request_processor.execute_webhook, args=[repo_config, self._event_store])
+ thread.start()
- # Add additional test case data
- test_case['config'] = {
- 'url': 'url' in repo_configs[0] and repo_configs[0]['url'],
- 'branch': 'branch' in repo_configs[0] and repo_configs[0]['branch'],
- 'remote': 'remote' in repo_configs[0] and repo_configs[0]['remote'],
- 'deploy': 'echo test!'
- }
+ # Add additional test case data
+ test_case['config'] = {
+ 'url': 'url' in repo_config and repo_config['url'],
+ 'branch': 'branch' in repo_config and repo_config['branch'],
+ 'remote': 'remote' in repo_config and repo_config['remote'],
+ 'deploy': 'echo test!'
+ }
except ValueError as e:
self.send_error(400, 'Unprocessable request')
@@ -210,8 +225,7 @@ def WebhookRequestHandlerFactory(config, event_store, server_status, is_https=Fa
go through our custom logger instead."""
import logging
logger = logging.getLogger()
- logger.info("%s - %s" % (self.client_address[0],
- format%args))
+ logger.info("%s - %s" % (self.client_address[0], format%args))
def save_test_case(self, test_case):
"""Log request information in a way it can be used as a test case."""
diff --git a/gitautodeploy/webhook.py b/gitautodeploy/webhook.py
index 5c027e4..752717e 100644
--- a/gitautodeploy/webhook.py
+++ b/gitautodeploy/webhook.py
@@ -51,105 +51,97 @@ class WebbhookRequestProcessor(object):
action.log_error("Unable to recognize request origin. Don't know how to handle the request.")
return
- def execute_webhook(self, repo_configs, request_headers, request_body, action):
+ def execute_webhook(self, repo_config, event_store):
"""Verify that the suggested repositories has matching settings and
issue git pull and/or deploy commands."""
import os
import time
- import logging
from .wrappers import GitWrapper
+ from .events import DeployEvent
from .lock import Lock
import json
- logger = logging.getLogger()
- payload = json.loads(request_body)
-
- result = []
-
- # Process each matching repository
- for repo_config in repo_configs:
-
- repo_result = {}
-
- # In case there is no path configured for the repository, no pull will
- # be made.
- if 'path' not in repo_config:
+ event = DeployEvent(repo_config)
+ event_store.register_action(event)
+ event.set_waiting(True)
+ event.log_info("Running deploy commands")
+
+ # In case there is no path configured for the repository, no pull will
+ # be made.
+ if 'path' not in repo_config:
+ res = GitWrapper.deploy(repo_config)
+ event.log_info("%s" % res)
+ event.set_waiting(False)
+ event.set_success(True)
+ return
+
+ # If the path does not exist, a warning will be raised and no pull or
+ # deploy will be made.
+ if not os.path.isdir(repo_config['path']):
+ event.log_error("The repository '%s' does not exist locally. Make sure it was pulled properly without errors by reviewing the log." % repo_config['path'])
+ event.set_waiting(False)
+ event.set_success(False)
+ return
+
+ # If the path is not writable, a warning will be raised and no pull or
+ # deploy will be made.
+ if not os.access(repo_config['path'], os.W_OK):
+ event.log_error("The path '%s' is not writable. Make sure that GAD has write access to that path." % repo_config['path'])
+ event.set_waiting(False)
+ event.set_success(False)
+ return
+
+ 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():
+ event.log_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
+ res = None
+ while n > 0:
+
+ # Attempt to pull up a maximum of 4 times
+ res = GitWrapper.pull(repo_config)
+
+ # Return code indicating success?
+ if res == 0:
+ break
+
+ n -= 1
+
+ if 0 < n:
res = GitWrapper.deploy(repo_config)
- repo_result['deploy'] = res
- result.append(repo_result)
- continue
-
- # If the path does not exist, a warning will be raised and no pull or
- # deploy will be made.
- if not os.path.isdir(repo_config['path']):
- action.log_error("The repository '%s' does not exist locally. Make sure it was pulled properly without errors by reviewing the log." % repo_config['path'])
- result.append(repo_result)
- continue
-
- # If the path is not writable, a warning will be raised and no pull or
- # deploy will be made.
- if not os.access(repo_config['path'], os.W_OK):
- action.log_error("The path '%s' is not writable. Make sure that GAD has write access to that path." % repo_config['path'])
- result.append(repo_result)
- 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():
- action.log_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
- res = None
- while n > 0:
-
- # Attempt to pull up a maximum of 4 times
- res = GitWrapper.pull(repo_config)
- repo_result['git pull'] = res
-
- # Return code indicating success?
- if res == 0:
- break
-
- n -= 1
-
- if 0 < n:
- res = GitWrapper.deploy(repo_config)
- repo_result['deploy'] = res
-
- #except Exception as e:
- # logger.error('Error during \'pull\' or \'deploy\' operation on path: %s' % repo_config['path'])
- # logger.error(e)
- # raise e
-
- finally:
- # Release the lock if it's ours
- if running_lock.has_lock():
- running_lock.release()
+ #except Exception as e:
+ # logger.error('Error during \'pull\' or \'deploy\' operation on path: %s' % repo_config['path'])
+ # logger.error(e)
+ # raise e
- # Release the lock if it's ours
- if waiting_lock.has_lock():
- waiting_lock.release()
+ finally:
- result.append(repo_result)
+ # Release the lock if it's ours
+ if running_lock.has_lock():
+ running_lock.release()
- action.log_info("Deploy commands were executed")
- action.set_waiting(False)
- action.set_success(True)
+ # Release the lock if it's ours
+ if waiting_lock.has_lock():
+ waiting_lock.release()
- return result
+ event.log_info("Deploy commands were executed")
+ event.set_waiting(False)
+ event.set_success(True)
class WebhookRequestFilter(object):