import logging import json from app import app from data.database import configure, BaseModel, uuid_generator from peewee import * from bitbucket import BitBucket from endpoints.trigger import BitbucketBuildTrigger configure(app.config) logger = logging.getLogger(__name__) # Note: We vendor the RepositoryBuildTrigger and its dependencies here class Repository(BaseModel): pass class BuildTriggerService(BaseModel): name = CharField(index=True, unique=True) class AccessToken(BaseModel): pass class User(BaseModel): pass class RepositoryBuildTrigger(BaseModel): uuid = CharField(default=uuid_generator) service = ForeignKeyField(BuildTriggerService, index=True) repository = ForeignKeyField(Repository, index=True) connected_user = ForeignKeyField(User) auth_token = CharField(null=True) private_key = TextField(null=True) config = TextField(default='{}') write_token = ForeignKeyField(AccessToken, null=True) pull_robot = ForeignKeyField(User, related_name='triggerpullrobot') def run_bitbucket_migration(): bitbucket_trigger = BuildTriggerService.get(BuildTriggerService.name == "bitbucket") encountered = set() while True: found = list(RepositoryBuildTrigger.select().where( RepositoryBuildTrigger.service == bitbucket_trigger, RepositoryBuildTrigger.config ** "%\"hook_id%")) found = [f for f in found if not f.uuid in encountered] if not found: logger.debug('No additional records found') return logger.debug('Found %s records to be changed', len(found)) for trigger in found: encountered.add(trigger.uuid) try: config = json.loads(trigger.config) except: logging.error("Cannot parse config for trigger %s", trigger.uuid) continue logger.debug("Checking trigger %s", trigger.uuid) if 'hook_id' in config: logger.debug("Updating trigger %s to a webhook", trigger.uuid) trigger_handler = BitbucketBuildTrigger(trigger) client = trigger_handler._get_repository_client() hook_id = config['hook_id'] # Lookup the old service hook. logger.debug("Looking up old service URL for trigger %s", trigger.uuid) (result, hook_data, err_msg) = client.services().get(hook_id) if not result or not hook_data: logger.error('Error when retrieving service hook for trigger %s: %s', trigger.uuid, err_msg) continue if not 'webhook_id' in config: hook_data = hook_data[0]['service'] webhook_url = [f for f in hook_data['fields'] if f['name'] == 'URL'][0]['value'] logger.debug("Adding webhook for trigger %s: %s", trigger.uuid, webhook_url) # Add the new web hook. description = 'Webhook for invoking builds on %s' % app.config['REGISTRY_TITLE_SHORT'] webhook_events = ['repo:push'] (result, data, err_msg) = client.webhooks().create(description, webhook_url, webhook_events) if not result: logger.error('Error when adding webhook for trigger %s: %s', trigger.uuid, err_msg) continue config['webhook_id'] = data['uuid'] trigger.config = json.dumps(config) trigger.save() # Remove the old service hook. logger.debug("Deleting old service URL for trigger %s", trigger.uuid) (result, _, err_msg) = client.services().delete(hook_id) if not result: logger.error('Error when deleting service hook for trigger %s: %s', trigger.uuid, err_msg) continue del config['hook_id'] # Update the config. trigger.config = json.dumps(config) trigger.save() logger.debug("Trigger %s updated to a webhook", trigger.uuid) if __name__ == "__main__": logging.basicConfig(level=logging.DEBUG) logging.getLogger('boto').setLevel(logging.CRITICAL) run_bitbucket_migration()