Merge branch 'rustedbuilds' of https://bitbucket.org/yackob03/quay into rustedbuilds

This commit is contained in:
Joseph Schorr 2014-02-24 20:38:57 -05:00
commit ef02e769fb
10 changed files with 175 additions and 70 deletions

View file

@ -29,7 +29,9 @@ from auth.permissions import (ReadRepositoryPermission,
ViewTeamPermission,
UserPermission)
from endpoints.common import common_login, get_route_data, truthy_param
from endpoints.trigger import BuildTrigger, TriggerActivationException, EmptyRepositoryException
from endpoints.trigger import (BuildTrigger, TriggerActivationException,
TriggerDeactivationException, EmptyRepositoryException)
from util.cache import cache_control
from datetime import datetime, timedelta
@ -1144,7 +1146,7 @@ def build_status_view(build_obj, can_write=False):
'started': build_obj.started,
'display_name': build_obj.display_name,
'status': status,
'resource_key': build_obj.resource_key if can_write else None,
'job_config': json.loads(build_obj.job_config) if can_write else None,
'is_writer': can_write,
'trigger': trigger_view(build_obj.trigger),
}
@ -1234,11 +1236,13 @@ def request_repo_build(namespace, repository):
logger.debug('User requested repository initialization.')
dockerfile_id = request.get_json()['file_id']
# Check if the dockerfile resource has already been used. If so, then it can only be reused if the
# user has access to the repository for which it was used.
# Check if the dockerfile resource has already been used. If so, then it
# can only be reused if the user has access to the repository for which it
# was used.
associated_repository = model.get_repository_for_resource(dockerfile_id)
if associated_repository:
if not ModifyRepositoryPermission(associated_repository.namespace, associated_repository.name):
if not ModifyRepositoryPermission(associated_repository.namespace,
associated_repository.name):
abort(403)
# Start the build.
@ -1248,9 +1252,15 @@ def request_repo_build(namespace, repository):
logger.debug('**********Md5: %s' % display_name)
host = urlparse.urlparse(request.url).netloc
tag = '%s/%s/%s' % (host, repo.namespace, repo.name)
build_request = model.create_repository_build(repo, token, dockerfile_id,
tag, display_name)
repo = '%s/%s/%s' % (host, repo.namespace, repo.name)
job_config = {
'docker_tags': ['latest'],
'build_subdir': '',
'repository': repo,
'resource_key': dockerfile_id,
}
build_request = model.create_repository_build(repo, token, job_config,
display_name)
dockerfile_build_queue.put(json.dumps({
'build_uuid': build_request.uuid,
'namespace': namespace,
@ -1438,15 +1448,15 @@ def activate_build_trigger(namespace, repository, trigger_uuid):
token.code, app.config['URL_HOST'],
path)
handler.activate(trigger.uuid, authed_url, trigger.auth_token,
new_config_dict)
final_config = handler.activate(trigger.uuid, authed_url,
trigger.auth_token, new_config_dict)
except TriggerActivationException as e:
token.delete_instance()
abort(400, message = e.msg)
return
# Save the updated config.
trigger.config = json.dumps(new_config_dict)
trigger.config = json.dumps(final_config)
trigger.write_token = token
trigger.save()
@ -1517,7 +1527,22 @@ def list_build_triggers(namespace, repository):
def delete_build_trigger(namespace, repository, trigger_uuid):
permission = AdministerRepositoryPermission(namespace, repository)
if permission.can():
model.delete_build_trigger(namespace, repository, trigger_uuid)
try:
trigger = model.get_build_trigger(namespace, repository, trigger_uuid)
except model.InvalidBuildTriggerException:
abort(404)
return
handler = BuildTrigger.get_trigger_for_service(trigger.service.name)
config_dict = json.loads(trigger.config)
if handler.is_active(config_dict):
try:
handler.deactivate(trigger.auth_token, config_dict)
except TriggerDeactivationException as ex:
# We are just going to eat this error
logger.warning('Trigger deactivation problem.', ex)
trigger.delete_instance()
log_action('delete_repo_trigger', namespace,
{'repo': repository, 'trigger_id': trigger_uuid},
repo=model.get_repository(namespace, repository))

View file

@ -120,6 +120,11 @@ def get_user():
'username': get_authenticated_user().username,
'email': get_authenticated_user().email,
})
elif get_validated_token():
return jsonify({
'username': '$token',
'email': None,
})
abort(404)

View file

@ -28,6 +28,9 @@ class InvalidServiceException(Exception):
class TriggerActivationException(Exception):
pass
class TriggerDeactivationException(Exception):
pass
class ValidationRequestException(Exception):
pass
@ -55,7 +58,8 @@ class BuildTrigger(object):
def handle_trigger_request(self, request, auth_token, config):
"""
Transform the incoming request data into a set of actions.
Transform the incoming request data into a set of actions. Returns a tuple
of usefiles resource id, docker tags, build name, and resource subdir.
"""
raise NotImplementedError
@ -69,6 +73,15 @@ class BuildTrigger(object):
def activate(self, trigger_uuid, standard_webhook_url, auth_token, config):
"""
Activates the trigger for the service, with the given new configuration.
Returns new configuration that should be stored if successful.
"""
raise NotImplementedError
def deactivate(self, auth_token, config):
"""
Deactivates the trigger for the service, removing any hooks installed in
the remote service. Returns the new config that should be stored if this
trigger is going to be re-activated.
"""
raise NotImplementedError
@ -103,7 +116,7 @@ class GithubBuildTrigger(BuildTrigger):
return 'github'
def is_active(self, config):
return 'build_source' in config and len(config['build_source']) > 0
return 'hook_id' in config
def activate(self, trigger_uuid, standard_webhook_url, auth_token, config):
new_build_source = config['build_source']
@ -121,11 +134,30 @@ class GithubBuildTrigger(BuildTrigger):
}
try:
to_add_webhook.create_hook('web', webhook_config)
hook = to_add_webhook.create_hook('web', webhook_config)
config['hook_id'] = hook.id
except GithubException:
msg = 'Unable to create webhook on repository: %s'
raise TriggerActivationException(msg % new_build_source)
return config
def deactivate(self, auth_token, config):
gh_client = self._get_client(auth_token)
try:
repo = gh_client.get_repo(config['build_source'])
to_delete = repo.get_hook(config['hook_id'])
to_delete.delete()
except GithubException:
msg = 'Unable to remove hook: %s' % config['hook_id']
raise TriggerDeactivationException(msg)
config.pop('hook_id', None)
return config
def list_build_sources(self, auth_token):
gh_client = self._get_client(auth_token)
usr = gh_client.get_user()
@ -163,7 +195,7 @@ class GithubBuildTrigger(BuildTrigger):
try:
repo = gh_client.get_repo(source)
default_commit = repo.get_branch(repo.default_branch).commit
default_commit = repo.get_branch(repo.master_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
@ -181,7 +213,8 @@ class GithubBuildTrigger(BuildTrigger):
logger.debug('Payload %s', payload)
ref = payload['ref']
commit_id = payload['head_commit']['id'][0:7]
commit_sha = payload['head_commit']['id']
short_sha = commit_sha[0:7]
gh_client = self._get_client(auth_token)
@ -192,8 +225,7 @@ class GithubBuildTrigger(BuildTrigger):
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)
archive_link = repo.get_archive_link('zipball', short_sha)
download_archive = client.get(archive_link, stream=True)
with SpooledTemporaryFile(CHUNK_SIZE) as zipball:
@ -204,4 +236,17 @@ class GithubBuildTrigger(BuildTrigger):
logger.debug('Successfully prepared job')
return dockerfile_id, branch_name, commit_id
# compute the tag(s)
pushed_branch = ref.split('/')[-1]
tags = {pushed_branch}
if pushed_branch == repo.master_branch:
tags.add('latest')
logger.debug('Pushing to tags: %s' % tags)
# compute the subdir
repo_subdir = config['subdir']
zipball_subdir = '%s-%s-%s' % (repo.owner.login, repo.name, short_sha)
joined_subdir = os.path.join(zipball_subdir, repo_subdir)
logger.debug('Final subdir: %s' % joined_subdir)
return dockerfile_id, list(tags), short_sha, joined_subdir

View file

@ -1,10 +1,12 @@
import logging
import stripe
import urlparse
import json
from flask import request, make_response, Blueprint
from data import model
from data.queue import dockerfile_build_queue
from auth.auth import process_auth
from auth.permissions import ModifyRepositoryPermission
from util.invoice import renderInvoiceToHtml
@ -62,22 +64,36 @@ def build_trigger_webhook(namespace, repository, trigger_uuid):
logger.debug('Passing webhook request to handler %s', handler)
try:
df_id, tag, name = handler.handle_trigger_request(request,
trigger.auth_token,
trigger.config)
specs = handler.handle_trigger_request(request, trigger.auth_token,
json.loads(trigger.config))
dockerfile_id, tags, name, subdir = specs
except ValidationRequestException:
# This was just a validation request, don't need to build anything
# This was just a validation request, we don't need to build anything
return make_response('Okay')
host = urlparse.urlparse(request.url).netloc
full_tag = '%s/%s/%s:%s' % (host, trigger.repository.namespace,
trigger.repository.name, tag)
repo = '%s/%s/%s' % (host, trigger.repository.namespace,
trigger.repository.name)
token = model.create_access_token(trigger.repository, 'write')
logger.debug('Creating build %s with full_tag %s and dockerfile_id %s',
name, full_tag, df_id)
model.create_repository_build(trigger.repository, token, df_id, full_tag,
name)
logger.debug('Creating build %s with repo %s tags %s and dockerfile_id %s',
name, repo, tags, dockerfile_id)
job_config = {
'docker_tags': tags,
'repository': repo,
'build_subdir': subdir,
'resource_key': dockerfile_id,
}
build_request = model.create_repository_build(trigger.repository, token,
job_config, name)
dockerfile_build_queue.put(json.dumps({
'build_uuid': build_request.uuid,
'namespace': namespace,
'repository': repository,
}), retries_remaining=1)
return make_response('Okay')