import logging import logging.config import json from data.database import (db, db_for_update, BaseModel, CharField, ForeignKeyField, TextField, BooleanField) from app import app from buildtrigger.basehandler import BuildTriggerHandler from util.security.ssh import generate_ssh_keypair from github import GithubException logger = logging.getLogger(__name__) class BuildTriggerService(BaseModel): name = CharField(index=True, unique=True) class Repository(BaseModel): pass class User(BaseModel): pass class AccessToken(BaseModel): pass class RepositoryBuildTrigger(BaseModel): uuid = CharField() 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, null=True, related_name='triggerpullrobot') used_legacy_github = BooleanField(null=True, default=False) def backfill_github_deploykeys(): """ Generates and saves private deploy keys for any GitHub build triggers still relying on the old buildpack behavior. """ logger.setLevel(logging.DEBUG) logger.debug('GitHub deploy key backfill: Began execution') encountered = set() github_service = BuildTriggerService.get(name='github') while True: build_trigger_ids = list(RepositoryBuildTrigger .select(RepositoryBuildTrigger.id) .where(RepositoryBuildTrigger.private_key >> None) .where(RepositoryBuildTrigger.service == github_service) .where(RepositoryBuildTrigger.used_legacy_github >> None) .limit(100)) filtered_ids = [trigger.id for trigger in build_trigger_ids if trigger.id not in encountered] if len(filtered_ids) == 0: # We're done! logger.debug('GitHub deploy key backfill: Backfill completed') return logger.debug('GitHub deploy key backfill: Found %s records to update', len(filtered_ids)) for trigger_id in filtered_ids: encountered.add(trigger_id) logger.debug('Updating build trigger: %s', trigger_id) with app.config['DB_TRANSACTION_FACTORY'](db): try: query = RepositoryBuildTrigger.select().where(RepositoryBuildTrigger.id == trigger_id) trigger = db_for_update(query).get() except RepositoryBuildTrigger.DoesNotExist: logger.debug('Could not find build trigger %s', trigger_id) continue trigger.used_legacy_github = True trigger.save() handler = BuildTriggerHandler.get_handler(trigger) config = handler.config if not 'build_source' in config: logger.debug('Could not find build source for trigger %s', trigger_id) continue build_source = config['build_source'] gh_client = handler._get_client() # Find the GitHub repository. try: gh_repo = gh_client.get_repo(build_source) except GithubException: logger.exception('Cannot find repository %s for trigger %s', build_source, trigger.id) continue # Add a deploy key to the GitHub repository. public_key, private_key = generate_ssh_keypair() config['credentials'] = [ { 'name': 'SSH Public Key', 'value': public_key, }, ] logger.debug('Adding deploy key to build trigger %s', trigger.id) try: deploy_key = gh_repo.create_key('%s Builder' % app.config['REGISTRY_TITLE'], public_key) config['deploy_key_id'] = deploy_key.id except GithubException: logger.exception('Cannot add deploy key to repository %s for trigger %s', build_source, trigger.id) continue logger.debug('Saving deploy key for trigger %s', trigger.id) trigger.used_legacy_github = True trigger.private_key = private_key trigger.config = json.dumps(config) trigger.save() if __name__ == "__main__": logging.getLogger('boto').setLevel(logging.CRITICAL) logging.getLogger('github').setLevel(logging.CRITICAL) logging.config.fileConfig('conf/logging_debug.conf', disable_existing_loggers=False) backfill_github_deploykeys()