def change_username(user_id, new_username): (username_valid, username_issue) = validate_username(new_username) if not username_valid: raise InvalidUsernameException("Invalid username %s: %s" % (new_username, username_issue)) with db_transaction(): # Reload the user for update user = db_for_update(User.select().where(User.id == user_id)).get() # Rename the robots for robot in db_for_update( _list_entity_robots(user.username, include_metadata=False, include_token=False)): _, robot_shortname = parse_robot_username(robot.username) new_robot_name = format_robot_username(new_username, robot_shortname) robot.username = new_robot_name robot.save() # Rename the user user.username = new_username user.save() # Remove any prompts for username. remove_user_prompt(user, "confirm_username") return user
def _purge_pre_oci_tag(tag, context, allow_non_expired=False): assert tag.repository_id == context.repository.id if not allow_non_expired: assert tag.lifetime_end_ts is not None assert tag.lifetime_end_ts <= pre_oci_tag.get_epoch_timestamp() # If it exists, GC the tag manifest. try: tag_manifest = TagManifest.select().where(TagManifest.tag == tag).get() _garbage_collect_legacy_manifest(tag_manifest.id, context) except TagManifest.DoesNotExist: pass # Add the tag's legacy image to be GCed. context.add_legacy_image_id(tag.image_id) with db_transaction(): # Reload the tag and verify its lifetime_end_ts has not changed. try: reloaded_tag = db_for_update(RepositoryTag.select().where(RepositoryTag.id == tag.id)).get() except RepositoryTag.DoesNotExist: return False assert reloaded_tag.id == tag.id assert reloaded_tag.repository_id == context.repository.id if reloaded_tag.lifetime_end_ts != tag.lifetime_end_ts: return False # Delete mapping rows. TagToRepositoryTag.delete().where(TagToRepositoryTag.repository_tag == reloaded_tag).execute() # Delete the tag. reloaded_tag.delete_instance()
def _purge_oci_tag(tag, context, allow_non_expired=False): assert tag.repository_id == context.repository.id if not allow_non_expired: assert tag.lifetime_end_ms is not None assert tag.lifetime_end_ms <= oci_tag.get_epoch_timestamp_ms() # Add the manifest to be GCed. context.add_manifest_id(tag.manifest_id) with db_transaction(): # Reload the tag and verify its lifetime_end_ms has not changed. try: reloaded_tag = db_for_update(Tag.select().where(Tag.id == tag.id)).get() except Tag.DoesNotExist: return False assert reloaded_tag.id == tag.id assert reloaded_tag.repository_id == context.repository.id if reloaded_tag.lifetime_end_ms != tag.lifetime_end_ms: return False # Delete mapping rows. TagToRepositoryTag.delete().where(TagToRepositoryTag.tag == tag).execute() # Delete the tag. tag.delete_instance()
def replace_service_key(old_kid, kid, jwk, metadata, expiration_date): try: with db_transaction(): key = db_for_update( ServiceKey.select().where(ServiceKey.kid == old_kid)).get() key.metadata.update(metadata) ServiceKey.create( name=key.name, kid=kid, service=key.service, jwk=jwk, metadata=key.metadata, expiration_date=expiration_date, rotation_duration=key.rotation_duration, approval=key.approval, ) key.delete_instance() except ServiceKey.DoesNotExist: raise ServiceKeyDoesNotExist _notify_superusers(key) delete_all_notifications_by_path_prefix( "/service_key_approval/{0}".format(old_kid)) _gc_expired(key.service)
def mark_namespace_for_deletion(user, queues, namespace_gc_queue, force=False): """ Marks a namespace (as referenced by the given user) for deletion. A queue item will be added to delete the namespace's repositories and storage, while the namespace itself will be renamed, disabled, and delinked from other tables. """ if not user.enabled: return None if not force and not user.organization: # Ensure that the user is not the sole admin for any organizations. If so, then the user # cannot be deleted before those organizations are deleted or reassigned. organizations = get_solely_admined_organizations(user) if len(organizations) > 0: message = ( "Cannot delete %s as you are the only admin for organizations: " % user.username) for index, org in enumerate(organizations): if index > 0: message = message + ", " message = message + org.username raise DataModelException(message) # Delete all queue items for the user. for queue in queues: queue.delete_namespaced_items(user.username) # Delete non-repository related items. This operation is very quick, so we can do so here. _delete_user_linked_data(user) with db_transaction(): original_username = user.username user = db_for_update(User.select().where(User.id == user.id)).get() # Mark the namespace as deleted and ready for GC. try: marker = DeletedNamespace.create( namespace=user, original_username=original_username, original_email=user.email) except IntegrityError: return # Disable the namespace itself, and replace its various unique fields with UUIDs. user.enabled = False user.username = str(uuid4()) user.email = str(uuid4()) user.save() # Add a queueitem to delete the namespace. marker.queue_id = namespace_gc_queue.put( [str(user.id)], json.dumps({ "marker_id": marker.id, "original_username": original_username, }), ) marker.save() return marker.id
def delete_tag(namespace_name, repository_name, tag_name, now_ms=None): now_ms = now_ms or get_epoch_timestamp_ms() now_ts = int(now_ms / 1000) with db_transaction(): try: query = _tag_alive( RepositoryTag.select( RepositoryTag, Repository).join(Repository).join( Namespace, on=(Repository.namespace_user == Namespace.id)).where( Repository.name == repository_name, Namespace.username == namespace_name, RepositoryTag.name == tag_name, ), now_ts, ) found = db_for_update(query).get() except RepositoryTag.DoesNotExist: msg = "Invalid repository tag '%s' on repository '%s/%s'" % ( tag_name, namespace_name, repository_name, ) raise DataModelException(msg) found.lifetime_end_ts = now_ts found.save() try: oci_tag_query = TagToRepositoryTag.select().where( TagToRepositoryTag.repository_tag == found) oci_tag = db_for_update(oci_tag_query).get().tag oci_tag.lifetime_end_ms = now_ms oci_tag.save() except TagToRepositoryTag.DoesNotExist: pass return found
def update_service_key(kid, name=None, metadata=None): try: with db_transaction(): key = db_for_update(ServiceKey.select().where(ServiceKey.kid == kid)).get() if name is not None: key.name = name if metadata is not None: key.metadata.update(metadata) key.save() except ServiceKey.DoesNotExist: raise ServiceKeyDoesNotExist
def approve_service_key(kid, approval_type, approver=None, notes=''): try: with db_transaction(): key = db_for_update(ServiceKey.select().where(ServiceKey.kid == kid)).get() if key.approval is not None: raise ServiceKeyAlreadyApproved approval = ServiceKeyApproval.create(approver=approver, approval_type=approval_type, notes=notes) key.approval = approval key.save() except ServiceKey.DoesNotExist: raise ServiceKeyDoesNotExist delete_all_notifications_by_path_prefix('/service_key_approval/{0}'.format(kid)) return key
def get_existing_repository(namespace_name, repository_name, for_update=False, kind_filter=None): query = (Repository.select(Repository, Namespace).join( Namespace, on=(Repository.namespace_user == Namespace.id)).where( Namespace.username == namespace_name, Repository.name == repository_name)) if kind_filter: query = (query.switch(Repository).join(RepositoryKind).where( RepositoryKind.name == kind_filter)) if for_update: query = db_for_update(query) return query.get()
def create_or_update_tag(repo, tag_name, models_ref, manifest_list=None, linked_tag=None, tag_kind="release"): Tag = models_ref.Tag now_ts = get_epoch_timestamp_ms() tag_kind_id = Tag.tag_kind.get_id(tag_kind) with db_transaction(): try: tag = db_for_update( tag_is_alive( Tag.select().where(Tag.repository == repo, Tag.name == tag_name, Tag.tag_kind == tag_kind_id), Tag, now_ts, )).get() if tag.manifest_list == manifest_list and tag.linked_tag == linked_tag: return tag tag.lifetime_end = now_ts tag.save() except Tag.DoesNotExist: pass try: return Tag.create( repository=repo, manifest_list=manifest_list, linked_tag=linked_tag, name=tag_name, lifetime_start=now_ts, lifetime_end=None, tag_kind=tag_kind_id, ) except IntegrityError: msg = "Tag with name %s and lifetime start %s under repository %s/%s already exists" raise TagAlreadyCreatedException( msg % (tag_name, now_ts, repo.namespace_user, repo.name))
def _item_by_id_for_update(queue_id): return db_for_update( QueueItem.select().where(QueueItem.id == queue_id)).get()
def create_or_update_tag_for_repo(repository_id, tag_name, tag_docker_image_id, reversion=False, oci_manifest=None, now_ms=None): now_ms = now_ms or get_epoch_timestamp_ms() now_ts = int(now_ms / 1000) with db_transaction(): try: tag = db_for_update( _tag_alive( RepositoryTag.select().where( RepositoryTag.repository == repository_id, RepositoryTag.name == tag_name), now_ts, )).get() tag.lifetime_end_ts = now_ts tag.save() # Check for an OCI tag. try: oci_tag = db_for_update( Tag.select().join(TagToRepositoryTag).where( TagToRepositoryTag.repository_tag == tag)).get() oci_tag.lifetime_end_ms = now_ms oci_tag.save() except Tag.DoesNotExist: pass except RepositoryTag.DoesNotExist: pass except IntegrityError: msg = "Tag with name %s was stale when we tried to update it; Please retry the push" raise StaleTagException(msg % tag_name) try: image_obj = Image.get(Image.docker_image_id == tag_docker_image_id, Image.repository == repository_id) except Image.DoesNotExist: raise DataModelException("Invalid image with id: %s" % tag_docker_image_id) try: created = RepositoryTag.create( repository=repository_id, image=image_obj, name=tag_name, lifetime_start_ts=now_ts, reversion=reversion, ) if oci_manifest: # Create the OCI tag as well. oci_tag = Tag.create( repository=repository_id, manifest=oci_manifest, name=tag_name, lifetime_start_ms=now_ms, reversion=reversion, tag_kind=Tag.tag_kind.get_id("tag"), ) TagToRepositoryTag.create(tag=oci_tag, repository_tag=created, repository=repository_id) return created except IntegrityError: msg = "Tag with name %s and lifetime start %s already exists" raise TagAlreadyCreatedException(msg % (tag_name, now_ts))