def _delete_user_linked_data(user): if user.organization: # Delete the organization's teams. with db_transaction(): for team in Team.select().where(Team.organization == user): team.delete_instance(recursive=True) # Delete any OAuth approvals and tokens associated with the user. with db_transaction(): for app in OAuthApplication.select().where(OAuthApplication.organization == user): app.delete_instance(recursive=True) else: # Remove the user from any teams in which they are a member. TeamMember.delete().where(TeamMember.user == user).execute() # Delete any repository buildtriggers where the user is the connected user. with db_transaction(): triggers = RepositoryBuildTrigger.select().where(RepositoryBuildTrigger.connected_user == user) for trigger in triggers: trigger.delete_instance(recursive=True, delete_nullable=False) # Delete any mirrors with robots owned by this user. with db_transaction(): robots = list(list_namespace_robots(user.username)) RepoMirrorConfig.delete().where(RepoMirrorConfig.internal_robot << robots).execute() # Delete any robots owned by this user. with db_transaction(): robots = list(list_namespace_robots(user.username)) for robot in robots: robot.delete_instance(recursive=True, delete_nullable=True) # Null out any service key approvals. We technically lose information here, but its better than # falling and only occurs if a superuser is being deleted. ServiceKeyApproval.update(approver=None).where(ServiceKeyApproval.approver == user).execute()
def test_update_trigger_disable_status(starting_failure_count, starting_error_count, status, expected_reason, initialized_db): test_config = { "SUCCESSIVE_TRIGGER_FAILURE_DISABLE_THRESHOLD": TEST_FAIL_THRESHOLD, "SUCCESSIVE_TRIGGER_INTERNAL_ERROR_DISABLE_THRESHOLD": TEST_INTERNAL_ERROR_THRESHOLD, } trigger = model.build.list_build_triggers("devtable", "building")[0] trigger.successive_failure_count = starting_failure_count trigger.successive_internal_error_count = starting_error_count trigger.enabled = True trigger.save() with patch("data.model.config.app_config", test_config): update_trigger_disable_status(trigger, status) updated_trigger = RepositoryBuildTrigger.get(uuid=trigger.uuid) assert updated_trigger.enabled == (expected_reason is None) if expected_reason is not None: assert updated_trigger.disabled_reason.name == expected_reason else: assert updated_trigger.disabled_reason is None assert updated_trigger.successive_failure_count == 0 assert updated_trigger.successive_internal_error_count == 0
def run_branchregex_migration(): encountered = set() while True: found = list( RepositoryBuildTrigger.select().where( RepositoryBuildTrigger.config ** "%branch_regex%", ~(RepositoryBuildTrigger.config ** "%branchtag_regex%"), ) ) 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) existing_regex = config["branch_regex"] logger.debug("Found branch regex '%s'", existing_regex) sub_regex = existing_regex.split("|") new_regex = "|".join(["heads/" + sub for sub in sub_regex]) config["branchtag_regex"] = new_regex logger.debug("Updating to branchtag regex '%s'", new_regex) update_build_trigger(trigger, config)
def list_build_triggers(namespace_name, repository_name): return (RepositoryBuildTrigger.select( RepositoryBuildTrigger, BuildTriggerService, Repository).join(BuildTriggerService).switch( RepositoryBuildTrigger).join(Repository).join( Namespace, on=(Repository.namespace_user == Namespace.id)).where( Namespace.username == namespace_name, Repository.name == repository_name))
def get_build_trigger(trigger_uuid): try: return ( RepositoryBuildTrigger.select( RepositoryBuildTrigger, BuildTriggerService, Repository, Namespace).join(BuildTriggerService).switch( RepositoryBuildTrigger).join(Repository).join( Namespace, on=(Repository.namespace_user == Namespace.id)). switch(RepositoryBuildTrigger).join( User, on=(RepositoryBuildTrigger.connected_user == User.id)).where( RepositoryBuildTrigger.uuid == trigger_uuid).get()) except RepositoryBuildTrigger.DoesNotExist: msg = "No build trigger with uuid: %s" % trigger_uuid raise InvalidBuildTriggerException(msg)
def create_build_trigger(repo, service_name, auth_token, user, pull_robot=None, config=None): service = BuildTriggerService.get(name=service_name) secure_auth_token = DecryptedValue(auth_token) if auth_token else None trigger = RepositoryBuildTrigger.create( repository=repo, service=service, secure_auth_token=secure_auth_token, connected_user=user, pull_robot=pull_robot, config=json.dumps(config or {}), ) return trigger
def update_trigger_disable_status(trigger, final_phase): """ Updates the disable status of the given build trigger. If the build trigger had a failure, then the counter is increased and, if we've reached the limit, the trigger is automatically disabled. Otherwise, if the trigger succeeded, it's counter is reset. This ensures that triggers that continue to error are eventually automatically disabled. """ with db_transaction(): try: trigger = RepositoryBuildTrigger.get(id=trigger.id) except RepositoryBuildTrigger.DoesNotExist: # Already deleted. return # If the build completed successfully, then reset the successive counters. if final_phase == BUILD_PHASE.COMPLETE: trigger.successive_failure_count = 0 trigger.successive_internal_error_count = 0 trigger.save() return # Otherwise, increment the counters and check for trigger disable. if final_phase == BUILD_PHASE.ERROR: trigger.successive_failure_count = trigger.successive_failure_count + 1 trigger.successive_internal_error_count = 0 elif final_phase == BUILD_PHASE.INTERNAL_ERROR: trigger.successive_internal_error_count = trigger.successive_internal_error_count + 1 # Check if we need to disable the trigger. failure_threshold = config.app_config.get( "SUCCESSIVE_TRIGGER_FAILURE_DISABLE_THRESHOLD") error_threshold = config.app_config.get( "SUCCESSIVE_TRIGGER_INTERNAL_ERROR_DISABLE_THRESHOLD") if failure_threshold and trigger.successive_failure_count >= failure_threshold: toggle_build_trigger(trigger, False, TRIGGER_DISABLE_REASON.BUILD_FALURES) elif error_threshold and trigger.successive_internal_error_count >= error_threshold: toggle_build_trigger(trigger, False, TRIGGER_DISABLE_REASON.INTERNAL_ERRORS) else: # Save the trigger changes. trigger.save()
def create_build_trigger(repo, service_name, auth_token, user, pull_robot=None, config=None): service = BuildTriggerService.get(name=service_name) # TODO(remove-unenc): Remove legacy field. old_auth_token = None if ActiveDataMigration.has_flag(ERTMigrationFlags.WRITE_OLD_FIELDS): old_auth_token = auth_token secure_auth_token = DecryptedValue(auth_token) if auth_token else None trigger = RepositoryBuildTrigger.create( repository=repo, service=service, auth_token=old_auth_token, secure_auth_token=secure_auth_token, connected_user=user, pull_robot=pull_robot, config=json.dumps(config or {}), ) return trigger
def ask_disable_namespace(username, queue_name): user = model.user.get_namespace_user(username) if user is None: raise Exception("Unknown user or organization %s" % username) if not user.enabled: print("NOTE: Namespace %s is already disabled" % username) queue_prefix = "%s/%s/%%" % (queue_name, username) existing_queue_item_count = (QueueItem.select().where( QueueItem.queue_name**queue_prefix).where( QueueItem.available == 1, QueueItem.retries_remaining > 0, QueueItem.processing_expires > datetime.now(), ).count()) repository_trigger_count = ( RepositoryBuildTrigger.select().join(Repository).where( Repository.namespace_user == user).count()) print("=============================================") print("For namespace %s" % username) print("=============================================") print("User %s has email address %s" % (username, user.email)) print("User %s has %s queued builds in their namespace" % (username, existing_queue_item_count)) print("User %s has %s build triggers in their namespace" % (username, repository_trigger_count)) confirm_msg = ( "Would you like to disable this user and delete their triggers and builds? [y/N]> " ) letter = str(input(confirm_msg)) if letter.lower() != "y": print("Action canceled") return print("=============================================") triggers = [] count_removed = 0 with db_transaction(): user.enabled = False user.save() repositories_query = Repository.select().where( Repository.namespace_user == user) if len(repositories_query.clone()): builds = list(RepositoryBuild.select().where( RepositoryBuild.repository << list(repositories_query))) triggers = list(RepositoryBuildTrigger.select().where( RepositoryBuildTrigger.repository << list(repositories_query))) mirrors = list(RepoMirrorConfig.select().where( RepoMirrorConfig.repository << list(repositories_query))) # Delete all builds for the user's repositories. if builds: RepositoryBuild.delete().where( RepositoryBuild.id << builds).execute() # Delete all build triggers for the user's repositories. if triggers: RepositoryBuildTrigger.delete().where( RepositoryBuildTrigger.id << triggers).execute() # Delete all mirrors for the user's repositories. if mirrors: RepoMirrorConfig.delete().where( RepoMirrorConfig.id << mirrors).execute() # Delete all queue items for the user's namespace. dockerfile_build_queue = WorkQueue(queue_name, tf, has_namespace=True) count_removed = dockerfile_build_queue.delete_namespaced_items( user.username) info = (user.username, len(triggers), count_removed, len(mirrors)) print( "Namespace %s disabled, %s triggers deleted, %s queued builds removed, %s mirrors deleted" % info) return user