Spaces:
Paused
Paused
| import collections | |
| from ..wrappers import GitWrapper | |
| from ..lock import Lock | |
| from ..wrappers import GitWrapper | |
| from ..events import DeployEvent | |
| class Project(collections.MutableMapping): | |
| """A dictionary that applies an arbitrary key-altering | |
| function before accessing the keys""" | |
| def __init__(self, *args, **kwargs): | |
| self.store = dict() | |
| self.update(dict(*args, **kwargs)) # use the free update to set keys | |
| def __getitem__(self, key): | |
| return self.store[self.__keytransform__(key)] | |
| def __setitem__(self, key, value): | |
| self.store[self.__keytransform__(key)] = value | |
| def __delitem__(self, key): | |
| del self.store[self.__keytransform__(key)] | |
| def __iter__(self): | |
| return iter(self.store) | |
| def __len__(self): | |
| return len(self.store) | |
| def __keytransform__(self, key): | |
| return key | |
| def get_name(self): | |
| return self['url'].split('/')[-1].split('.git')[0] | |
| def passes_payload_filter(self, payload, action): | |
| # At least one filter must match | |
| for filter in self['payload-filter']: | |
| # All options specified in the filter must match | |
| for filter_key, filter_value in filter.items(): | |
| # Ignore filters with value None (let them pass) | |
| if filter_value == None: | |
| continue | |
| # Interpret dots in filter name as path notations | |
| node_value = payload | |
| for node_key in filter_key.split('.'): | |
| # If the path is not valid the filter does not match | |
| if not node_key in node_value: | |
| action.log_info("Filter '%s' does not match since the path is invalid" % (filter_key)) | |
| # Filter does not match, do not process this repo config | |
| return False | |
| node_value = node_value[node_key] | |
| if filter_value == node_value: | |
| continue | |
| # If the filter value is set to True. the filter | |
| # will pass regardless of the actual value | |
| if filter_value == True: | |
| continue | |
| action.log_debug("Filter '%s' does not match ('%s' != '%s')" % (filter_key, filter_value, (str(node_value)[:75] + '..') if len(str(node_value)) > 75 else str(node_value))) | |
| # Filter does not match, do not process this repo config | |
| return False | |
| # Filter does match, proceed | |
| return True | |
| def passes_header_filter(self, request_headers): | |
| # At least one filter must match | |
| for key in self['header-filter']: | |
| # Verify that the request has the required header attribute | |
| if key.lower() not in request_headers: | |
| return False | |
| # "True" indicates that any header value is accepted | |
| if self['header-filter'][key] is True: | |
| continue | |
| # Verify that the request has the required header value | |
| if self['header-filter'][key] != request_headers[key.lower()]: | |
| return False | |
| # Filter does match, proceed | |
| return True | |
| def apply_filters(self, request_headers, request_body, action): | |
| """Verify that the suggested repositories has matching settings and | |
| issue git pull and/or deploy commands.""" | |
| import os | |
| import time | |
| import json | |
| payload = json.loads(request_body) | |
| # Verify that all payload filters matches the request (if any payload filters are specified) | |
| if 'payload-filter' in self and not self.passes_payload_filter(payload, action): | |
| # Filter does not match, do not process this repo config | |
| return False | |
| # Verify that all header filters matches the request (if any header filters are specified) | |
| if 'header-filter' in self and not self.passes_header_filter(request_headers): | |
| # Filter does not match, do not process this repo config | |
| return False | |
| return True | |
| def execute_webhook(self, event_store): | |
| """Verify that the suggested repositories has matching settings and | |
| issue git pull and/or deploy commands.""" | |
| import os | |
| import time | |
| import json | |
| event = DeployEvent(self) | |
| 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 self: | |
| res = GitWrapper.deploy(self) | |
| 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(self['path']): | |
| event.log_error("The repository '%s' does not exist locally. Make sure it was pulled properly without errors by reviewing the log." % self['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(self['path'], os.W_OK): | |
| event.log_error("The path '%s' is not writable. Make sure that GAD has write access to that path." % self['path']) | |
| event.set_waiting(False) | |
| event.set_success(False) | |
| return | |
| running_lock = Lock(os.path.join(self['path'], 'status_running')) | |
| waiting_lock = Lock(os.path.join(self['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(self) | |
| # Return code indicating success? | |
| if res == 0: | |
| break | |
| n -= 1 | |
| if 0 < n: | |
| res = GitWrapper.deploy(self) | |
| #except Exception as e: | |
| # logger.error('Error during \'pull\' or \'deploy\' operation on path: %s' % self['path']) | |
| # logger.error(e) | |
| # raise 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() | |
| event.log_info("Deploy commands were executed") | |
| event.set_waiting(False) | |
| event.set_success(True) | |