diff --git a/buildtrigger/customhandler.py b/buildtrigger/customhandler.py index c60d751ea..b3b1b01ba 100644 --- a/buildtrigger/customhandler.py +++ b/buildtrigger/customhandler.py @@ -1,7 +1,7 @@ import logging import json -from jsonschema import validate +from jsonschema import validate, ValidationError from buildtrigger.triggerutil import (RepositoryReadException, TriggerActivationException, TriggerStartException, ValidationRequestException, InvalidPayloadException, @@ -10,11 +10,60 @@ from buildtrigger.triggerutil import (RepositoryReadException, TriggerActivation from buildtrigger.basehandler import BuildTriggerHandler +from buildtrigger.bitbuckethandler import (BITBUCKET_WEBHOOK_PAYLOAD_SCHEMA as bb_schema, + get_transformed_webhook_payload as bb_payload) + +from buildtrigger.githubhandler import (GITHUB_WEBHOOK_PAYLOAD_SCHEMA as gh_schema, + get_transformed_webhook_payload as gh_payload) + +from buildtrigger.bitbuckethandler import (BITBUCKET_WEBHOOK_PAYLOAD_SCHEMA as bb_schema, + get_transformed_webhook_payload as bb_payload) + +from buildtrigger.gitlabhandler import (GITLAB_WEBHOOK_PAYLOAD_SCHEMA as gl_schema, + get_transformed_webhook_payload as gl_payload) + from util.security.ssh import generate_ssh_keypair logger = logging.getLogger(__name__) +# Defines an ordered set of tuples of the schemas and associated transformation functions +# for incoming webhook payloads. +SCHEMA_AND_HANDLERS = [ + (gh_schema, gh_payload), + (bb_schema, bb_payload), + (gl_schema, gl_payload), +] + + +def custom_trigger_payload(metadata, git_url): + # First try the customhandler schema. If it matches, nothing more to do. + custom_handler_validation_error = None + try: + validate(metadata, CustomBuildTrigger.payload_schema) + except ValidationError as vex: + custom_handler_validation_error = vex + + # Otherwise, try the defined schemas, in order, until we find a match. + for schema, handler in SCHEMA_AND_HANDLERS: + try: + validate(metadata, schema) + except ValidationError: + continue + + result = handler(metadata) + result['git_url'] = git_url + return result + + # If we have reached this point and no other schemas validated, then raise the error for the + # custom schema. + if custom_handler_validation_error is not None: + raise InvalidPayloadException(custom_handler_validation_error.message) + + metadata['git_url'] = git_url + return metadata + + class CustomBuildTrigger(BuildTriggerHandler): payload_schema = { 'type': 'object', @@ -101,13 +150,14 @@ class CustomBuildTrigger(BuildTriggerHandler): def is_active(self): return self.config.has_key('credentials') - def _metadata_from_payload(self, payload): + def _metadata_from_payload(self, payload, git_url): + # Parse the JSON payload. try: metadata = json.loads(payload) - validate(metadata, self.payload_schema) - except Exception as e: - raise InvalidPayloadException(e.message) - return metadata + except ValueError as vex: + raise InvalidPayloadException(vex.message) + + return custom_trigger_payload(metadata, git_url) def handle_trigger_request(self, request): payload = request.data @@ -116,9 +166,7 @@ class CustomBuildTrigger(BuildTriggerHandler): logger.debug('Payload %s', payload) - metadata = self._metadata_from_payload(payload) - metadata['git_url'] = self.config['build_source'] - + metadata = self._metadata_from_payload(payload, self.config['build_source']) prepared = self.prepare_build(metadata) # Check if we should skip this build. diff --git a/test/test_prepare_trigger.py b/test/test_prepare_trigger.py index 54c00454d..e47e7f859 100644 --- a/test/test_prepare_trigger.py +++ b/test/test_prepare_trigger.py @@ -2,6 +2,7 @@ import unittest import json from jsonschema import validate, ValidationError +from buildtrigger.customhandler import custom_trigger_payload from buildtrigger.basehandler import METADATA_SCHEMA from buildtrigger.bitbuckethandler import get_transformed_webhook_payload as bb_webhook from buildtrigger.bitbuckethandler import get_transformed_commit_info as bb_commit @@ -21,6 +22,93 @@ class TestPrepareTrigger(unittest.TestCase): validate(created, METADATA_SCHEMA) + def test_custom_custom(self): + expected = { + u'commit':u'1c002dd', + u'commit_info': { + u'url': u'gitsoftware.com/repository/commits/1234567', + u'date': u'timestamp', + u'message': u'initial commit', + u'committer': { + u'username': u'user', + u'url': u'gitsoftware.com/users/user', + u'avatar_url': u'gravatar.com/user.png' + }, + u'author': { + u'username': u'user', + u'url': u'gitsoftware.com/users/user', + u'avatar_url': u'gravatar.com/user.png' + } + }, + u'ref': u'refs/heads/master', + u'default_branch': u'master', + u'git_url': u'foobar', + } + + self.assertSchema('custom_webhook', expected, custom_trigger_payload, git_url='foobar') + + + def test_custom_gitlab(self): + expected = { + 'commit': u'fb88379ee45de28a0a4590fddcbd8eff8b36026e', + 'ref': u'refs/heads/master', + 'git_url': u'git@gitlab.com:jzelinskie/www-gitlab-com.git', + 'commit_info': { + 'url': u'https://gitlab.com/jzelinskie/www-gitlab-com/commit/fb88379ee45de28a0a4590fddcbd8eff8b36026e', + 'date': u'2015-08-13T19:33:18+00:00', + 'message': u'Fix link\n', + }, + } + + self.assertSchema('gitlab_webhook', expected, custom_trigger_payload, git_url='git@gitlab.com:jzelinskie/www-gitlab-com.git') + + + def test_custom_github(self): + expected = { + 'commit': u'410f4cdf8ff09b87f245b13845e8497f90b90a4c', + 'ref': u'refs/heads/master', + 'git_url': u'git@github.com:josephschorr/anothertest.git', + 'commit_info': { + 'url': u'https://github.com/josephschorr/anothertest/commit/410f4cdf8ff09b87f245b13845e8497f90b90a4c', + 'date': u'2015-09-11T14:26:16-04:00', + 'message': u'Update Dockerfile', + 'committer': { + 'username': u'josephschorr', + }, + 'author': { + 'username': u'josephschorr', + }, + }, + } + + self.assertSchema('github_webhook', expected, custom_trigger_payload, git_url='git@github.com:josephschorr/anothertest.git') + + + def test_custom_bitbucket(self): + expected = { + "commit": u"af64ae7188685f8424040b4735ad12941b980d75", + "ref": u"refs/heads/master", + "git_url": u"git@bitbucket.org:jscoreos/another-repo.git", + "commit_info": { + "url": u"https://bitbucket.org/jscoreos/another-repo/commits/af64ae7188685f8424040b4735ad12941b980d75", + "date": u"2015-09-10T20:40:54+00:00", + "message": u"Dockerfile edited online with Bitbucket", + "author": { + "username": u"jscoreos", + "url": u"https://bitbucket.org/jscoreos/", + "avatar_url": u"https://bitbucket.org/account/jscoreos/avatar/32/", + }, + "committer": { + "username": u"jscoreos", + "url": u"https://bitbucket.org/jscoreos/", + "avatar_url": u"https://bitbucket.org/account/jscoreos/avatar/32/", + }, + }, + } + + self.assertSchema('bitbucket_webhook', expected, custom_trigger_payload, git_url='git@bitbucket.org:jscoreos/another-repo.git') + + def test_bitbucket_customer_payload_noauthor(self): expected = { "commit": "a0ec139843b2bb281ab21a433266ddc498e605dc", diff --git a/test/triggerjson/custom_webhook.json b/test/triggerjson/custom_webhook.json new file mode 100644 index 000000000..61fb7adb6 --- /dev/null +++ b/test/triggerjson/custom_webhook.json @@ -0,0 +1,20 @@ +{ + "commit": "1c002dd", + "ref": "refs/heads/master", + "default_branch": "master", + "commit_info": { + "url": "gitsoftware.com/repository/commits/1234567", + "message": "initial commit", + "date": "timestamp", + "author": { + "username": "user", + "avatar_url": "gravatar.com/user.png", + "url": "gitsoftware.com/users/user" + }, + "committer": { + "username": "user", + "avatar_url": "gravatar.com/user.png", + "url": "gitsoftware.com/users/user" + } + } +} \ No newline at end of file