f9e6110f73
Adds support for creating app repos, viewing app repos and seeing the list of app repos in the Quay UI.
138 lines
4.7 KiB
Python
138 lines
4.7 KiB
Python
from peewee import fn
|
|
from cachetools import lru_cache
|
|
|
|
from data.model import DataModelException
|
|
from data.database import (Repository, User, Team, TeamMember, RepositoryPermission, TeamRole,
|
|
Namespace, Visibility, ImageStorage, Image, RepositoryKind,
|
|
db_for_update)
|
|
|
|
|
|
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()
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def get_public_repo_visibility():
|
|
return Visibility.get(name='public')
|
|
|
|
|
|
@lru_cache(maxsize=3)
|
|
def _lookup_team_role(name):
|
|
return TeamRole.get(name=name)
|
|
|
|
|
|
def filter_to_repos_for_user(query, username=None, namespace=None, repo_kind='image',
|
|
include_public=True, start_id=None):
|
|
if not include_public and not username:
|
|
return Repository.select().where(Repository.id == '-1')
|
|
|
|
# Filter on the type of repository.
|
|
try:
|
|
query = query.where(Repository.kind == Repository.kind.get_id(repo_kind))
|
|
except RepositoryKind.DoesNotExist:
|
|
raise DataModelException('Unknown repository kind')
|
|
|
|
# Add the start ID if necessary.
|
|
if start_id is not None:
|
|
query = query.where(Repository.id >= start_id)
|
|
|
|
# Build a set of queries that, when unioned together, return the full set of visible repositories
|
|
# for the filters specified.
|
|
queries = []
|
|
|
|
where_clause = (True)
|
|
if namespace:
|
|
where_clause = (Namespace.username == namespace)
|
|
|
|
if include_public:
|
|
queries.append(query
|
|
.clone()
|
|
.where(Repository.visibility == get_public_repo_visibility(), where_clause))
|
|
|
|
if username:
|
|
UserThroughTeam = User.alias()
|
|
Org = User.alias()
|
|
AdminTeam = Team.alias()
|
|
AdminTeamMember = TeamMember.alias()
|
|
AdminUser = User.alias()
|
|
|
|
# Add repositories in which the user has permission.
|
|
queries.append(query
|
|
.clone()
|
|
.switch(RepositoryPermission)
|
|
.join(User)
|
|
.where(User.username == username, where_clause))
|
|
|
|
# Add repositories in which the user is a member of a team that has permission.
|
|
queries.append(query
|
|
.clone()
|
|
.switch(RepositoryPermission)
|
|
.join(Team)
|
|
.join(TeamMember)
|
|
.join(UserThroughTeam, on=(UserThroughTeam.id == TeamMember.user))
|
|
.where(UserThroughTeam.username == username, where_clause))
|
|
|
|
# Add repositories under namespaces in which the user is the org admin.
|
|
queries.append(query
|
|
.clone()
|
|
.switch(Repository)
|
|
.join(Org, on=(Repository.namespace_user == Org.id))
|
|
.join(AdminTeam, on=(Org.id == AdminTeam.organization))
|
|
.where(AdminTeam.role == _lookup_team_role('admin'))
|
|
.switch(AdminTeam)
|
|
.join(AdminTeamMember, on=(AdminTeam.id == AdminTeamMember.team))
|
|
.join(AdminUser, on=(AdminTeamMember.user == AdminUser.id))
|
|
.where(AdminUser.username == username, where_clause))
|
|
|
|
return reduce(lambda l, r: l | r, queries)
|
|
|
|
|
|
def get_user_organizations(username):
|
|
UserAlias = User.alias()
|
|
return (User
|
|
.select()
|
|
.distinct()
|
|
.join(Team)
|
|
.join(TeamMember)
|
|
.join(UserAlias, on=(UserAlias.id == TeamMember.user))
|
|
.where(User.organization == True, UserAlias.username == username))
|
|
|
|
|
|
def calculate_image_aggregate_size(ancestors_str, image_size, parent_image):
|
|
ancestors = ancestors_str.split('/')[1:-1]
|
|
if not ancestors:
|
|
return image_size
|
|
|
|
if parent_image is None:
|
|
raise DataModelException('Could not load parent image')
|
|
|
|
ancestor_size = parent_image.aggregate_size
|
|
if ancestor_size is not None:
|
|
return ancestor_size + image_size
|
|
|
|
# Fallback to a slower path if the parent doesn't have an aggregate size saved.
|
|
# TODO: remove this code if/when we do a full backfill.
|
|
ancestor_size = (ImageStorage
|
|
.select(fn.Sum(ImageStorage.image_size))
|
|
.join(Image)
|
|
.where(Image.id << ancestors)
|
|
.scalar())
|
|
if ancestor_size is None:
|
|
return None
|
|
|
|
return ancestor_size + image_size
|