Merge pull request #255 from coreos-inc/betterbb

Change to use the new BitBucket webhooks
This commit is contained in:
Jimmy Zelinskie 2015-07-23 13:36:07 -04:00
commit fee8bf8607

View file

@ -318,7 +318,7 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
return True
def is_active(self):
return 'hook_id' in self.config
return 'webhook_id' in self.config
def activate(self, standard_webhook_url):
config = self.config
@ -333,51 +333,58 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
]
repository = self._get_repository_client()
(result, data, err_msg) = repository.deploykeys().create(
(result, created_deploykey, err_msg) = repository.deploykeys().create(
app.config['REGISTRY_TITLE'] + ' webhook key', public_key)
if not result:
msg = 'Unable to add deploy key to repository: %s' % err_msg
raise TriggerActivationException(msg)
config['deploy_key_id'] = data['pk']
config['deploy_key_id'] = created_deploykey['pk']
# Add a webhook callback.
(result, data, err_msg) = repository.services().create('POST', URL=standard_webhook_url)
description = 'Webhook for invoking builds on %s' % app.config['REGISTRY_TITLE_SHORT']
webhook_events = ['repo:push']
(result, created_webhook, err_msg) = repository.webhooks().create(
description, standard_webhook_url, webhook_events)
if not result:
msg = 'Unable to add webhook to repository: %s' % err_msg
raise TriggerActivationException(msg)
config['hook_id'] = data['id']
config['webhook_id'] = created_webhook['uuid']
self.config = config
return config, {'private_key': private_key}
def deactivate(self):
config = self.config
hook_id = config.pop('hook_id', None)
# TODO(jschorr): Remove the old hook removal code once everyone is migrated.
old_hook_id = config.pop('hook_id', None)
webhook_id = config.pop('webhook_id', None)
deploy_key_id = config.pop('deploy_key_id', None)
repository = self._get_repository_client()
try:
repository = self._get_repository_client()
# Remove the old service hook.
if old_hook_id is not None:
(result, _, err_msg) = repository.services().delete(old_hook_id)
if not result:
msg = 'Unable to remove service hook from repository: %s' % err_msg
raise TriggerDeactivationException(msg)
# Remove the webhook link.
if hook_id is not None:
(result, _, err_msg) = repository.services().delete(hook_id)
if not result:
msg = 'Unable to remove webhook from repository: %s' % err_msg
raise TriggerDeactivationException(msg)
# Remove the webhook.
if webhook_id is not None:
(result, _, err_msg) = repository.webhooks().delete(webhook_id)
if not result:
msg = 'Unable to remove webhook from repository: %s' % err_msg
raise TriggerDeactivationException(msg)
# Remove the public key.
if deploy_key_id is not None:
(result, _, err_msg) = repository.deploykeys().delete(deploy_key_id)
if not result:
msg = 'Unable to remove deploy key from repository: %s' % err_msg
raise TriggerDeactivationException(msg)
except GitHubBadCredentialsException:
msg = ('Unable to remove trigger as credentials are invalid. ' +
'Please manually remove the webhook and deploy key.')
raise TriggerDeactivationException(msg)
# Remove the public key.
if deploy_key_id is not None:
(result, _, err_msg) = repository.deploykeys().delete(deploy_key_id)
if not result:
msg = 'Unable to remove deploy key from repository: %s' % err_msg
raise TriggerDeactivationException(msg)
return config
@ -470,7 +477,17 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
return None
def _prepare_build(self, commit_sha, ref, is_manual):
_BITBUCKET_COMMIT_URL = 'https://bitbucket.org/%s/%s/commits/%s'
def _prepare_build(self, commit_sha, ref, is_manual, target=None, actor=None):
def _build_user_block(info):
return {
'username': info['username'],
'url': info['links']['html']['href'],
'avatar_url': info['links']['avatar']['href'],
'display_name': info['display_name']
}
config = self.config
repository = self._get_repository_client()
@ -478,23 +495,33 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
# the tags.
default_branch = self._get_default_branch(repository)
# Lookup the commit sha.
(result, data, _) = repository.changesets().get(commit_sha)
if not result:
raise TriggerStartException('Could not lookup commit SHA')
# Lookup the commit sha (if necessary)
data = {}
if target is None:
(result, data, _) = repository.changesets().get(commit_sha)
if not result:
raise TriggerStartException('Could not lookup commit SHA')
namespace = repository.namespace
name = repository.repository_name
# Build the commit information.
commit_url = self._BITBUCKET_COMMIT_URL % (namespace, name, commit_sha)
if target is not None and 'links' in target:
commit_url = target['links']['html']['href']
commit_info = {
'url': 'https://bitbucket.org/%s/%s/commits/%s' % (namespace, name, commit_sha),
'message': data['message'],
'date': data['timestamp']
'url': commit_url,
'message': target['message'] if target else data['message'],
'date': target['date'] if target else data['timestamp']
}
# Try to lookup the author by email address. The raw_author field (if it exists) is returned
# in the form: "Joseph Schorr <joseph.schorr@coreos.com>"
if data.get('raw_author'):
# Add the commit's author.
if target.get('author') and 'user' in target['author']:
commit_info['author'] = _build_user_block(target['author']['user'])
elif data.get('raw_author'):
# Try to lookup the author by email address. The raw_author field (if it exists) is returned
# in the form: "Joseph Schorr <joseph.schorr@coreos.com>"
match = re.compile(r'.*<(.+)>').match(data['raw_author'])
if match:
email_address = match.group(1)
@ -507,6 +534,10 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
'avatar_url': data['user']['avatar']
}
# Add the commit's actor (committer).
if actor is not None:
commit_info['committer'] = _build_user_block(actor)
metadata = {
'commit': commit_sha,
'ref': ref,
@ -524,8 +555,61 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
return prepared
def handle_trigger_request(self, request):
# Check for a payload field. If found, this is a V1 service request.
payload_json = request.form.get('payload')
if payload_json:
return self.handle_V1_trigger_request(request)
# Otherwise, this is a V2 webhook request.
return self.handle_V2_trigger_request(request)
def handle_V2_trigger_request(self, request):
payload = request.get_json()
if not 'push' in payload:
logger.debug('Skipping BitBucket request due to missing push data in payload')
raise SkipRequestException()
push_payload = payload['push']
if not 'changes' in push_payload or not push_payload['changes']:
logger.debug('Skipping BitBucket request due to empty changes list')
raise SkipRequestException()
# Make sure we have a new change.
changes = push_payload['changes']
last_change = changes[-1]
if not last_change.get('new'):
logger.debug('Skipping BitBucket request due to change being a deletion')
raise SkipRequestException()
change_info = last_change['new']
change_target = change_info.get('target')
if not change_target:
logger.debug('Skipping BitBucket request due to missing change target')
raise SkipRequestException()
# Check if this build should be skipped by commit message.
commit_message = change_target.get('message', '')
if should_skip_commit(commit_message):
logger.debug('Skipping BitBucket request due to commit message request')
raise SkipRequestException()
# Check to see if this build should be skipped by ref.
ref = ('refs/heads/' + change_info['name'] if change_info['type'] == 'branch'
else 'refs/tags/' + change_info['name'])
logger.debug('Checking BitBucket request: %s', ref)
raise_if_skipped(self.config, ref)
# Prepare the build.
commit_sha = change_target['hash']
return self._prepare_build(commit_sha, ref, False, target=change_target,
actor=payload.get('actor'))
def handle_V1_trigger_request(self, request):
# TODO(jschorr): Delete this code after the migration has successfully completed.
# Parse the JSON payload.
payload_json = request.form.get('payload')
if not payload_json: