This repository has been archived on 2020-03-24. You can view files and clone it, but cannot push or open issues or pull requests.
quay/endpoints/trigger.py

208 lines
5.5 KiB
Python
Raw Normal View History

import logging
import io
import os.path
from github import Github, UnknownObjectException, GithubException
from tempfile import SpooledTemporaryFile
from app import app
user_files = app.config['USERFILES']
client = app.config['HTTPCLIENT']
logger = logging.getLogger(__name__)
ZIPBALL = 'application/zip'
CHUNK_SIZE = 512 * 1024
class BuildArchiveException(Exception):
pass
class InvalidServiceException(Exception):
pass
class TriggerActivationException(Exception):
pass
class ValidationRequestException(Exception):
pass
class EmptyRepositoryException(Exception):
pass
class BuildTrigger(object):
def __init__(self):
pass
def list_build_sources(self, auth_token):
"""
Take the auth information for the specific trigger type and load the
list of build sources(repositories).
"""
raise NotImplementedError
def list_build_subdirs(self, auth_token, config):
"""
Take the auth information and the specified config so far and list all of
the possible subdirs containing dockerfiles.
"""
raise NotImplementedError
def handle_trigger_request(self, request, auth_token, config):
"""
Transform the incoming request data into a set of actions.
"""
raise NotImplementedError
def is_active(self, config):
"""
Returns True if the current build trigger is active. Inactive means further
setup is needed.
"""
raise NotImplementedError
def activate(self, trigger_uuid, standard_webhook_url, auth_token, config):
"""
Activates the trigger for the service, with the given new configuration.
"""
raise NotImplementedError
@classmethod
def service_name(cls):
"""
Particular service implemented by subclasses.
"""
raise NotImplementedError
@classmethod
def get_trigger_for_service(cls, service):
for subc in cls.__subclasses__():
if subc.service_name() == service:
return subc()
raise InvalidServiceException('Unable to find service: %s' % service)
def raise_unsupported():
raise io.UnsupportedOperation
class GithubBuildTrigger(BuildTrigger):
@staticmethod
def _get_client(auth_token):
return Github(auth_token, client_id=app.config['GITHUB_CLIENT_ID'],
client_secret=app.config['GITHUB_CLIENT_SECRET'])
@classmethod
def service_name(cls):
return 'github'
def is_active(self, config):
return 'build_source' in config and len(config['build_source']) > 0
def activate(self, trigger_uuid, standard_webhook_url, auth_token, config):
new_build_source = config['build_source']
gh_client = self._get_client(auth_token)
try:
to_add_webhook = gh_client.get_repo(new_build_source)
except UnknownObjectException:
msg = 'Unable to find GitHub repository for source: %s'
raise TriggerActivationException(msg % new_build_source)
webhook_config = {
'url': standard_webhook_url,
'content_type': 'json',
}
try:
to_add_webhook.create_hook('web', webhook_config)
except GithubException:
msg = 'Unable to create webhook on repository: %s'
raise TriggerActivationException(msg % new_build_source)
def list_build_sources(self, auth_token):
gh_client = self._get_client(auth_token)
usr = gh_client.get_user()
personal = {
'personal': True,
'repos': [repo.full_name for repo in usr.get_repos()],
'info': {
'name': usr.login,
'avatar_url': usr.avatar_url,
}
}
repos_by_org = [personal]
for org in usr.get_orgs():
repo_list = []
for repo in org.get_repos(type='member'):
repo_list.append(repo.full_name)
repos_by_org.append({
'personal': False,
'repos': repo_list,
'info': {
'name': org.name,
'avatar_url': org.avatar_url
}
})
return repos_by_org
def list_build_subdirs(self, auth_token, config):
gh_client = self._get_client(auth_token)
source = config['build_source']
try:
repo = gh_client.get_repo(source)
default_commit = repo.get_branch(repo.default_branch).commit
commit_tree = repo.get_git_tree(default_commit.sha, recursive=True)
return [os.path.dirname(elem.path) for elem in commit_tree.tree
if (elem.type == u'blob' and
os.path.basename(elem.path) == u'Dockerfile')]
except GithubException:
msg = 'Unable to list contents of repository: %s' % source
raise EmptyRepositoryException(msg)
def handle_trigger_request(self, request, auth_token, config):
payload = request.get_json()
if 'zen' in payload:
raise ValidationRequestException()
logger.debug('Payload %s', payload)
ref = payload['ref']
commit_id = payload['head_commit']['id'][0:7]
gh_client = self._get_client(auth_token)
repo_full_name = '%s/%s' % (payload['repository']['owner']['name'],
payload['repository']['name'])
repo = gh_client.get_repo(repo_full_name)
logger.debug('Github repo: %s', repo)
# Prepare the download and upload URLs
branch_name = ref.split('/')[-1]
archive_link = repo.get_archive_link('zipball', branch_name)
download_archive = client.get(archive_link, stream=True)
with SpooledTemporaryFile(CHUNK_SIZE) as zipball:
for chunk in download_archive.iter_content(CHUNK_SIZE):
zipball.write(chunk)
dockerfile_id = user_files.store_file(zipball, ZIPBALL)
logger.debug('Successfully prepared job')
return dockerfile_id, branch_name, commit_id