You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ELAB-partsearch/deploy/Git-Auto-Deploy/gitautodeploy/models/project.py

211 lines
7.2 KiB

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)