Phase 2 of migrating repo namespaces to referencing user objects, backfilling the rows without a value for namespace_user, and changing all accesses to go through the namespace_user object. All tests are passing, manual testing still required.
This commit is contained in:
parent
6070c251ae
commit
03190efde3
19 changed files with 373 additions and 305 deletions
|
@ -25,6 +25,9 @@ EXPONENTIAL_BACKOFF_SCALE = timedelta(seconds=1)
|
|||
PRESUMED_DEAD_BUILD_AGE = timedelta(days=15)
|
||||
|
||||
|
||||
Namespace = User.alias()
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -100,13 +103,24 @@ class TooManyLoginAttemptsException(Exception):
|
|||
super(TooManyLoginAttemptsException, self).__init__(message)
|
||||
self.retry_after = retry_after
|
||||
|
||||
|
||||
def _get_repository(namespace_name, repository_name):
|
||||
return (Repository
|
||||
.select(Repository, Namespace)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.where(Namespace.username == namespace_name, Repository.name == repository_name)
|
||||
.get())
|
||||
|
||||
|
||||
def hash_password(password, salt=None):
|
||||
salt = salt or bcrypt.gensalt()
|
||||
return bcrypt.hashpw(password.encode('utf-8'), salt)
|
||||
|
||||
|
||||
def is_create_user_allowed():
|
||||
return True
|
||||
|
||||
|
||||
def create_user(username, password, email, auto_verify=False):
|
||||
""" Creates a regular user, if allowed. """
|
||||
if not validate_password(password):
|
||||
|
@ -122,6 +136,7 @@ def create_user(username, password, email, auto_verify=False):
|
|||
|
||||
return created
|
||||
|
||||
|
||||
def _create_user(username, email):
|
||||
if not validate_email(email):
|
||||
raise InvalidEmailAddressException('Invalid email address: %s' % email)
|
||||
|
@ -733,7 +748,7 @@ def get_visible_repositories(username=None, include_public=True, page=None,
|
|||
limit=None, sort=False, namespace=None):
|
||||
query = _visible_repository_query(username=username, include_public=include_public, page=page,
|
||||
limit=limit, namespace=namespace,
|
||||
select_models=[Repository, Visibility])
|
||||
select_models=[Repository, Namespace, Visibility])
|
||||
|
||||
if sort:
|
||||
query = query.order_by(Repository.description.desc())
|
||||
|
@ -747,11 +762,13 @@ def get_visible_repositories(username=None, include_public=True, page=None,
|
|||
def _visible_repository_query(username=None, include_public=True, limit=None,
|
||||
page=None, namespace=None, select_models=[]):
|
||||
query = (Repository
|
||||
.select(*select_models) # Note: We need to leave this blank for the get_count case. Otherwise, MySQL/RDS complains.
|
||||
.distinct()
|
||||
.join(Visibility)
|
||||
.switch(Repository)
|
||||
.join(RepositoryPermission, JOIN_LEFT_OUTER))
|
||||
.select(*select_models) # MySQL/RDS complains is there are selected models for counts.
|
||||
.distinct()
|
||||
.join(Visibility)
|
||||
.switch(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.switch(Repository)
|
||||
.join(RepositoryPermission, JOIN_LEFT_OUTER))
|
||||
|
||||
query = _filter_to_repos_for_user(query, username, namespace, include_public)
|
||||
|
||||
|
@ -782,26 +799,20 @@ def _filter_to_repos_for_user(query, username=None, namespace=None,
|
|||
.switch(RepositoryPermission)
|
||||
.join(Team, JOIN_LEFT_OUTER)
|
||||
.join(TeamMember, JOIN_LEFT_OUTER)
|
||||
.join(UserThroughTeam, JOIN_LEFT_OUTER, on=(UserThroughTeam.id ==
|
||||
TeamMember.user))
|
||||
.join(UserThroughTeam, JOIN_LEFT_OUTER, on=(UserThroughTeam.id == TeamMember.user))
|
||||
.switch(Repository)
|
||||
.join(Org, JOIN_LEFT_OUTER, on=(Org.username == Repository.namespace))
|
||||
.join(AdminTeam, JOIN_LEFT_OUTER, on=(Org.id ==
|
||||
AdminTeam.organization))
|
||||
.join(Org, JOIN_LEFT_OUTER, on=(Repository.namespace_user == Org.id))
|
||||
.join(AdminTeam, JOIN_LEFT_OUTER, on=(Org.id == AdminTeam.organization))
|
||||
.join(TeamRole, JOIN_LEFT_OUTER, on=(AdminTeam.role == TeamRole.id))
|
||||
.switch(AdminTeam)
|
||||
.join(AdminTeamMember, JOIN_LEFT_OUTER, on=(AdminTeam.id ==
|
||||
AdminTeamMember.team))
|
||||
.join(AdminUser, JOIN_LEFT_OUTER, on=(AdminTeamMember.user ==
|
||||
AdminUser.id)))
|
||||
.join(AdminTeamMember, JOIN_LEFT_OUTER, on=(AdminTeam.id == AdminTeamMember.team))
|
||||
.join(AdminUser, JOIN_LEFT_OUTER, on=(AdminTeamMember.user == AdminUser.id)))
|
||||
|
||||
where_clause = ((User.username == username) |
|
||||
(UserThroughTeam.username == username) |
|
||||
((AdminUser.username == username) &
|
||||
(TeamRole.name == 'admin')))
|
||||
where_clause = ((User.username == username) | (UserThroughTeam.username == username) |
|
||||
((AdminUser.username == username) & (TeamRole.name == 'admin')))
|
||||
|
||||
if namespace:
|
||||
where_clause = where_clause & (Repository.namespace == namespace)
|
||||
where_clause = where_clause & (Namespace.username == namespace)
|
||||
|
||||
if include_public:
|
||||
new_clause = (Visibility.name == 'public')
|
||||
|
@ -820,7 +831,7 @@ def get_matching_repositories(repo_term, username=None):
|
|||
visible = get_visible_repositories(username)
|
||||
|
||||
search_clauses = (Repository.name ** ('%' + name_term + '%') |
|
||||
Repository.namespace ** ('%' + namespace_term + '%'))
|
||||
Namespace.username ** ('%' + namespace_term + '%'))
|
||||
|
||||
# Handle the case where the user has already entered a namespace path.
|
||||
if repo_term.find('/') > 0:
|
||||
|
@ -829,7 +840,7 @@ def get_matching_repositories(repo_term, username=None):
|
|||
name_term = parts[-1]
|
||||
|
||||
search_clauses = (Repository.name ** ('%' + name_term + '%') &
|
||||
Repository.namespace ** ('%' + namespace_term + '%'))
|
||||
Namespace.username ** ('%' + namespace_term + '%'))
|
||||
|
||||
final = visible.where(search_clauses).limit(10)
|
||||
return list(final)
|
||||
|
@ -859,22 +870,20 @@ def update_email(user, new_email, auto_verify=False):
|
|||
|
||||
|
||||
def get_all_user_permissions(user):
|
||||
select = RepositoryPermission.select(RepositoryPermission, Role, Repository)
|
||||
with_role = select.join(Role)
|
||||
with_repo = with_role.switch(RepositoryPermission).join(Repository)
|
||||
through_user = with_repo.switch(RepositoryPermission).join(User,
|
||||
JOIN_LEFT_OUTER)
|
||||
as_perm = through_user.switch(RepositoryPermission)
|
||||
through_team = as_perm.join(Team, JOIN_LEFT_OUTER).join(TeamMember,
|
||||
JOIN_LEFT_OUTER)
|
||||
|
||||
UserThroughTeam = User.alias()
|
||||
with_team_member = through_team.join(UserThroughTeam, JOIN_LEFT_OUTER,
|
||||
on=(UserThroughTeam.id ==
|
||||
TeamMember.user))
|
||||
|
||||
return with_team_member.where((User.id == user) |
|
||||
(UserThroughTeam.id == user))
|
||||
return (RepositoryPermission
|
||||
.select(RepositoryPermission, Role, Repository, Namespace)
|
||||
.join(Role)
|
||||
.switch(RepositoryPermission)
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.switch(RepositoryPermission)
|
||||
.join(User, JOIN_LEFT_OUTER)
|
||||
.switch(RepositoryPermission)
|
||||
.join(Team, JOIN_LEFT_OUTER).join(TeamMember, JOIN_LEFT_OUTER)
|
||||
.join(UserThroughTeam, JOIN_LEFT_OUTER, on=(UserThroughTeam.id == TeamMember.user))
|
||||
.where((User.id == user) | (UserThroughTeam.id == user)))
|
||||
|
||||
|
||||
def delete_prototype_permission(org, uid):
|
||||
|
@ -939,33 +948,37 @@ def get_org_wide_permissions(user):
|
|||
|
||||
|
||||
def get_all_repo_teams(namespace_name, repository_name):
|
||||
select = RepositoryPermission.select(Team.name.alias('team_name'),
|
||||
Role.name, RepositoryPermission)
|
||||
with_team = select.join(Team)
|
||||
with_role = with_team.switch(RepositoryPermission).join(Role)
|
||||
with_repo = with_role.switch(RepositoryPermission).join(Repository)
|
||||
return with_repo.where(Repository.namespace == namespace_name,
|
||||
Repository.name == repository_name)
|
||||
return (RepositoryPermission.select(Team.name.alias('team_name'), Role.name, RepositoryPermission)
|
||||
.join(Team)
|
||||
.switch(RepositoryPermission)
|
||||
.join(Role)
|
||||
.switch(RepositoryPermission)
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.where(Namespace.username == namespace_name, Repository.name == repository_name))
|
||||
|
||||
|
||||
def get_all_repo_users(namespace_name, repository_name):
|
||||
select = RepositoryPermission.select(User.username, User.robot, Role.name,
|
||||
RepositoryPermission)
|
||||
with_user = select.join(User)
|
||||
with_role = with_user.switch(RepositoryPermission).join(Role)
|
||||
with_repo = with_role.switch(RepositoryPermission).join(Repository)
|
||||
return with_repo.where(Repository.namespace == namespace_name,
|
||||
Repository.name == repository_name)
|
||||
return (RepositoryPermission.select(User.username, User.robot, Role.name, RepositoryPermission)
|
||||
.join(User)
|
||||
.switch(RepositoryPermission)
|
||||
.join(Role)
|
||||
.switch(RepositoryPermission)
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.where(Namespace.username == namespace_name, Repository.name == repository_name))
|
||||
|
||||
|
||||
def get_all_repo_users_transitive_via_teams(namespace_name, repository_name):
|
||||
select = User.select().distinct()
|
||||
with_team_member = select.join(TeamMember)
|
||||
with_team = with_team_member.join(Team)
|
||||
with_perm = with_team.join(RepositoryPermission)
|
||||
with_repo = with_perm.join(Repository)
|
||||
return with_repo.where(Repository.namespace == namespace_name,
|
||||
Repository.name == repository_name)
|
||||
return (User
|
||||
.select()
|
||||
.distinct()
|
||||
.join(TeamMember)
|
||||
.join(Team)
|
||||
.join(RepositoryPermission)
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.where(Namespace.username == namespace_name, Repository.name == repository_name))
|
||||
|
||||
|
||||
def get_all_repo_users_transitive(namespace_name, repository_name):
|
||||
|
@ -989,10 +1002,12 @@ def get_all_repo_users_transitive(namespace_name, repository_name):
|
|||
def get_repository_for_resource(resource_key):
|
||||
try:
|
||||
return (Repository
|
||||
.select()
|
||||
.join(RepositoryBuild)
|
||||
.where(RepositoryBuild.resource_key == resource_key)
|
||||
.get())
|
||||
.select(Repository, Namespace)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.switch(Repository)
|
||||
.join(RepositoryBuild)
|
||||
.where(RepositoryBuild.resource_key == resource_key)
|
||||
.get())
|
||||
except Repository.DoesNotExist:
|
||||
return None
|
||||
|
||||
|
@ -1006,8 +1021,7 @@ def lookup_repository(repo_id):
|
|||
|
||||
def get_repository(namespace_name, repository_name):
|
||||
try:
|
||||
return Repository.get(Repository.name == repository_name,
|
||||
Repository.namespace == namespace_name)
|
||||
return _get_repository(namespace_name, repository_name)
|
||||
except Repository.DoesNotExist:
|
||||
return None
|
||||
|
||||
|
@ -1024,11 +1038,18 @@ def get_repo_image(namespace_name, repository_name, image_id):
|
|||
|
||||
|
||||
def repository_is_public(namespace_name, repository_name):
|
||||
joined = Repository.select().join(Visibility)
|
||||
query = joined.where(Repository.namespace == namespace_name,
|
||||
Repository.name == repository_name,
|
||||
Visibility.name == 'public')
|
||||
return len(list(query)) > 0
|
||||
try:
|
||||
(Repository
|
||||
.select()
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.switch(Repository)
|
||||
.join(Visibility)
|
||||
.where(Namespace.username == namespace_name, Repository.name == repository_name,
|
||||
Visibility.name == 'public')
|
||||
.get())
|
||||
return True
|
||||
except Repository.DoesNotExist:
|
||||
return False
|
||||
|
||||
|
||||
def set_repository_visibility(repo, visibility):
|
||||
|
@ -1128,7 +1149,7 @@ def __translate_ancestry(old_ancestry, translations, repository, username, prefe
|
|||
def find_create_or_link_image(docker_image_id, repository, username, translations,
|
||||
preferred_location):
|
||||
with config.app_config['DB_TRANSACTION_FACTORY'](db):
|
||||
repo_image = get_repo_image(repository.namespace, repository.name,
|
||||
repo_image = get_repo_image(repository.namespace_user.username, repository.name,
|
||||
docker_image_id)
|
||||
if repo_image:
|
||||
return repo_image
|
||||
|
@ -1142,6 +1163,8 @@ def find_create_or_link_image(docker_image_id, repository, username, translation
|
|||
.join(Visibility)
|
||||
.switch(Repository)
|
||||
.join(RepositoryPermission, JOIN_LEFT_OUTER)
|
||||
.switch(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.where(ImageStorage.uploading == False))
|
||||
|
||||
query = (_filter_to_repos_for_user(query, username)
|
||||
|
@ -1186,11 +1209,11 @@ def find_create_or_link_image(docker_image_id, repository, username, translation
|
|||
|
||||
def get_storage_by_uuid(storage_uuid):
|
||||
placements = list(ImageStoragePlacement
|
||||
.select(ImageStoragePlacement, ImageStorage, ImageStorageLocation)
|
||||
.join(ImageStorageLocation)
|
||||
.switch(ImageStoragePlacement)
|
||||
.join(ImageStorage)
|
||||
.where(ImageStorage.uuid == storage_uuid))
|
||||
.select(ImageStoragePlacement, ImageStorage, ImageStorageLocation)
|
||||
.join(ImageStorageLocation)
|
||||
.switch(ImageStoragePlacement)
|
||||
.join(ImageStorage)
|
||||
.where(ImageStorage.uuid == storage_uuid))
|
||||
|
||||
if not placements:
|
||||
raise InvalidImageException('No storage found with uuid: %s', storage_uuid)
|
||||
|
@ -1205,14 +1228,14 @@ def set_image_size(docker_image_id, namespace_name, repository_name,
|
|||
image_size):
|
||||
try:
|
||||
image = (Image
|
||||
.select(Image, ImageStorage)
|
||||
.join(Repository)
|
||||
.switch(Image)
|
||||
.join(ImageStorage, JOIN_LEFT_OUTER)
|
||||
.where(Repository.name == repository_name,
|
||||
Repository.namespace == namespace_name,
|
||||
Image.docker_image_id == docker_image_id)
|
||||
.get())
|
||||
.select(Image, ImageStorage)
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.switch(Image)
|
||||
.join(ImageStorage, JOIN_LEFT_OUTER)
|
||||
.where(Repository.name == repository_name, Namespace.username == namespace_name,
|
||||
Image.docker_image_id == docker_image_id)
|
||||
.get())
|
||||
|
||||
except Image.DoesNotExist:
|
||||
raise DataModelException('No image with specified id and repository')
|
||||
|
@ -1231,13 +1254,13 @@ def set_image_metadata(docker_image_id, namespace_name, repository_name, created
|
|||
command, uncompressed_size, parent=None):
|
||||
with config.app_config['DB_TRANSACTION_FACTORY'](db):
|
||||
query = (Image
|
||||
.select(Image, ImageStorage)
|
||||
.join(Repository)
|
||||
.switch(Image)
|
||||
.join(ImageStorage)
|
||||
.where(Repository.name == repository_name,
|
||||
Repository.namespace == namespace_name,
|
||||
Image.docker_image_id == docker_image_id))
|
||||
.select(Image, ImageStorage)
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.switch(Image)
|
||||
.join(ImageStorage)
|
||||
.where(Repository.name == repository_name, Namespace.username == namespace_name,
|
||||
Image.docker_image_id == docker_image_id))
|
||||
|
||||
try:
|
||||
fetched = query.get()
|
||||
|
@ -1248,7 +1271,7 @@ def set_image_metadata(docker_image_id, namespace_name, repository_name, created
|
|||
fetched.storage.checksum = None
|
||||
fetched.storage.created = dateutil.parser.parse(created_date_str).replace(tzinfo=None)
|
||||
fetched.storage.comment = comment
|
||||
fetched.storage.command = command
|
||||
fetched.storage.command = command
|
||||
fetched.storage.uncompressed_size = uncompressed_size
|
||||
|
||||
if parent:
|
||||
|
@ -1261,14 +1284,14 @@ def set_image_metadata(docker_image_id, namespace_name, repository_name, created
|
|||
|
||||
def _get_repository_images_base(namespace_name, repository_name, query_modifier):
|
||||
query = (ImageStoragePlacement
|
||||
.select(ImageStoragePlacement, Image, ImageStorage, ImageStorageLocation)
|
||||
.join(ImageStorageLocation)
|
||||
.switch(ImageStoragePlacement)
|
||||
.join(ImageStorage, JOIN_LEFT_OUTER)
|
||||
.join(Image)
|
||||
.join(Repository)
|
||||
.where(Repository.name == repository_name,
|
||||
Repository.namespace == namespace_name))
|
||||
.select(ImageStoragePlacement, Image, ImageStorage, ImageStorageLocation)
|
||||
.join(ImageStorageLocation)
|
||||
.switch(ImageStoragePlacement)
|
||||
.join(ImageStorage, JOIN_LEFT_OUTER)
|
||||
.join(Image)
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.where(Repository.name == repository_name, Namespace.username == namespace_name))
|
||||
|
||||
query = query_modifier(query)
|
||||
|
||||
|
@ -1299,24 +1322,26 @@ def get_repository_images(namespace_name, repository_name):
|
|||
|
||||
|
||||
def list_repository_tags(namespace_name, repository_name):
|
||||
select = RepositoryTag.select(RepositoryTag, Image)
|
||||
with_repo = select.join(Repository)
|
||||
with_image = with_repo.switch(RepositoryTag).join(Image)
|
||||
return with_image.where(Repository.name == repository_name,
|
||||
Repository.namespace == namespace_name)
|
||||
return (RepositoryTag
|
||||
.select(RepositoryTag, Image)
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.switch(RepositoryTag)
|
||||
.join(Image)
|
||||
.where(Repository.name == repository_name, Namespace.username == namespace_name))
|
||||
|
||||
|
||||
def garbage_collect_repository(namespace_name, repository_name):
|
||||
with config.app_config['DB_TRANSACTION_FACTORY'](db):
|
||||
# Get a list of all images used by tags in the repository
|
||||
tag_query = (RepositoryTag
|
||||
.select(RepositoryTag, Image, ImageStorage)
|
||||
.join(Image)
|
||||
.join(ImageStorage, JOIN_LEFT_OUTER)
|
||||
.switch(RepositoryTag)
|
||||
.join(Repository)
|
||||
.where(Repository.name == repository_name,
|
||||
Repository.namespace == namespace_name))
|
||||
.select(RepositoryTag, Image, ImageStorage)
|
||||
.join(Image)
|
||||
.join(ImageStorage, JOIN_LEFT_OUTER)
|
||||
.switch(RepositoryTag)
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.where(Repository.name == repository_name, Namespace.username == namespace_name))
|
||||
|
||||
referenced_anscestors = set()
|
||||
for tag in tag_query:
|
||||
|
@ -1344,11 +1369,11 @@ def garbage_collect_repository(namespace_name, repository_name):
|
|||
|
||||
if uuids_to_check_for_gc:
|
||||
storage_to_remove = (ImageStorage
|
||||
.select()
|
||||
.join(Image, JOIN_LEFT_OUTER)
|
||||
.group_by(ImageStorage)
|
||||
.where(ImageStorage.uuid << list(uuids_to_check_for_gc))
|
||||
.having(fn.Count(Image.id) == 0))
|
||||
.select()
|
||||
.join(Image, JOIN_LEFT_OUTER)
|
||||
.group_by(ImageStorage)
|
||||
.where(ImageStorage.uuid << list(uuids_to_check_for_gc))
|
||||
.having(fn.Count(Image.id) == 0))
|
||||
|
||||
for storage in storage_to_remove:
|
||||
logger.debug('Garbage collecting image storage: %s', storage.uuid)
|
||||
|
@ -1367,9 +1392,9 @@ def garbage_collect_repository(namespace_name, repository_name):
|
|||
def get_tag_image(namespace_name, repository_name, tag_name):
|
||||
def limit_to_tag(query):
|
||||
return (query
|
||||
.switch(Image)
|
||||
.join(RepositoryTag)
|
||||
.where(RepositoryTag.name == tag_name))
|
||||
.switch(Image)
|
||||
.join(RepositoryTag)
|
||||
.where(RepositoryTag.name == tag_name))
|
||||
|
||||
images = _get_repository_images_base(namespace_name, repository_name, limit_to_tag)
|
||||
if not images:
|
||||
|
@ -1407,22 +1432,17 @@ def get_parent_images(namespace_name, repository_name, image_obj):
|
|||
def create_or_update_tag(namespace_name, repository_name, tag_name,
|
||||
tag_docker_image_id):
|
||||
try:
|
||||
repo = Repository.get(Repository.name == repository_name,
|
||||
Repository.namespace == namespace_name)
|
||||
repo = _get_repository(namespace_name, repository_name)
|
||||
except Repository.DoesNotExist:
|
||||
raise DataModelException('Invalid repository %s/%s' %
|
||||
(namespace_name, repository_name))
|
||||
raise DataModelException('Invalid repository %s/%s' % (namespace_name, repository_name))
|
||||
|
||||
try:
|
||||
image = Image.get(Image.docker_image_id == tag_docker_image_id,
|
||||
Image.repository == repo)
|
||||
image = Image.get(Image.docker_image_id == tag_docker_image_id, Image.repository == repo)
|
||||
except Image.DoesNotExist:
|
||||
raise DataModelException('Invalid image with id: %s' %
|
||||
tag_docker_image_id)
|
||||
raise DataModelException('Invalid image with id: %s' % tag_docker_image_id)
|
||||
|
||||
try:
|
||||
tag = RepositoryTag.get(RepositoryTag.repository == repo,
|
||||
RepositoryTag.name == tag_name)
|
||||
tag = RepositoryTag.get(RepositoryTag.repository == repo, RepositoryTag.name == tag_name)
|
||||
tag.image = image
|
||||
tag.save()
|
||||
except RepositoryTag.DoesNotExist:
|
||||
|
@ -1432,41 +1452,46 @@ def create_or_update_tag(namespace_name, repository_name, tag_name,
|
|||
|
||||
|
||||
def delete_tag(namespace_name, repository_name, tag_name):
|
||||
joined = RepositoryTag.select().join(Repository)
|
||||
found = list(joined.where(Repository.name == repository_name,
|
||||
Repository.namespace == namespace_name,
|
||||
RepositoryTag.name == tag_name))
|
||||
try:
|
||||
found = (RepositoryTag
|
||||
.select()
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.where(Repository.name == repository_name, Namespace.username == namespace_name,
|
||||
RepositoryTag.name == tag_name)
|
||||
.get())
|
||||
|
||||
if not found:
|
||||
except RepositoryTag.DoesNotExist:
|
||||
msg = ('Invalid repository tag \'%s\' on repository \'%s/%s\'' %
|
||||
(tag_name, namespace_name, repository_name))
|
||||
raise DataModelException(msg)
|
||||
|
||||
found[0].delete_instance()
|
||||
found.delete_instance()
|
||||
|
||||
|
||||
def delete_all_repository_tags(namespace_name, repository_name):
|
||||
try:
|
||||
repo = Repository.get(Repository.name == repository_name,
|
||||
Repository.namespace == namespace_name)
|
||||
repo = _get_repository(namespace_name, repository_name)
|
||||
except Repository.DoesNotExist:
|
||||
raise DataModelException('Invalid repository \'%s/%s\'' %
|
||||
(namespace_name, repository_name))
|
||||
RepositoryTag.delete().where(RepositoryTag.repository == repo.id).execute()
|
||||
|
||||
|
||||
def __entity_permission_repo_query(entity_id, entity_table,
|
||||
entity_id_property, namespace_name,
|
||||
def __entity_permission_repo_query(entity_id, entity_table, entity_id_property, namespace_name,
|
||||
repository_name):
|
||||
""" This method works for both users and teams. """
|
||||
selected = RepositoryPermission.select(entity_table, Repository, Role,
|
||||
RepositoryPermission)
|
||||
with_user = selected.join(entity_table)
|
||||
with_role = with_user.switch(RepositoryPermission).join(Role)
|
||||
with_repo = with_role.switch(RepositoryPermission).join(Repository)
|
||||
return with_repo.where(Repository.name == repository_name,
|
||||
Repository.namespace == namespace_name,
|
||||
entity_id_property == entity_id)
|
||||
|
||||
return (RepositoryPermission
|
||||
.select(entity_table, Repository, Namespace, Role, RepositoryPermission)
|
||||
.join(entity_table)
|
||||
.switch(RepositoryPermission)
|
||||
.join(Role)
|
||||
.switch(RepositoryPermission)
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.where(Repository.name == repository_name, Namespace.username == namespace_name,
|
||||
entity_id_property == entity_id))
|
||||
|
||||
|
||||
def get_user_reponame_permission(username, namespace_name, repository_name):
|
||||
|
@ -1514,8 +1539,7 @@ def delete_team_permission(team_name, namespace_name, repository_name):
|
|||
|
||||
def __set_entity_repo_permission(entity, permission_entity_property,
|
||||
namespace_name, repository_name, role_name):
|
||||
repo = Repository.get(Repository.name == repository_name,
|
||||
Repository.namespace == namespace_name)
|
||||
repo = _get_repository(namespace_name, repository_name)
|
||||
new_role = Role.get(Role.name == role_name)
|
||||
|
||||
# Fetch any existing permission for this entity on the repo
|
||||
|
@ -1566,15 +1590,18 @@ def purge_repository(namespace_name, repository_name):
|
|||
garbage_collect_repository(namespace_name, repository_name)
|
||||
|
||||
# Delete the rest of the repository metadata
|
||||
fetched = Repository.get(Repository.name == repository_name,
|
||||
Repository.namespace == namespace_name)
|
||||
fetched = _get_repository(namespace_name, repository_name)
|
||||
fetched.delete_instance(recursive=True)
|
||||
|
||||
|
||||
def get_private_repo_count(username):
|
||||
joined = Repository.select().join(Visibility)
|
||||
return joined.where(Repository.namespace == username,
|
||||
Visibility.name == 'private').count()
|
||||
return (Repository
|
||||
.select()
|
||||
.join(Visibility)
|
||||
.switch(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.where(Namespace.username == username, Visibility.name == 'private')
|
||||
.count())
|
||||
|
||||
|
||||
def create_access_token(repository, role):
|
||||
|
@ -1587,22 +1614,23 @@ def create_access_token(repository, role):
|
|||
def create_delegate_token(namespace_name, repository_name, friendly_name,
|
||||
role='read'):
|
||||
read_only = Role.get(name=role)
|
||||
repo = Repository.get(Repository.name == repository_name,
|
||||
Repository.namespace == namespace_name)
|
||||
repo = _get_repository(namespace_name, repository_name)
|
||||
new_token = AccessToken.create(repository=repo, role=read_only,
|
||||
friendly_name=friendly_name, temporary=False)
|
||||
return new_token
|
||||
|
||||
|
||||
def get_repository_delegate_tokens(namespace_name, repository_name):
|
||||
return (AccessToken.select(AccessToken, Role)
|
||||
.join(Repository)
|
||||
.switch(AccessToken)
|
||||
.join(Role)
|
||||
.switch(AccessToken)
|
||||
.join(RepositoryBuildTrigger, JOIN_LEFT_OUTER)
|
||||
.where(Repository.name == repository_name, Repository.namespace == namespace_name,
|
||||
AccessToken.temporary == False, RepositoryBuildTrigger.uuid >> None))
|
||||
return (AccessToken
|
||||
.select(AccessToken, Role)
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.switch(AccessToken)
|
||||
.join(Role)
|
||||
.switch(AccessToken)
|
||||
.join(RepositoryBuildTrigger, JOIN_LEFT_OUTER)
|
||||
.where(Repository.name == repository_name, Namespace.username == namespace_name,
|
||||
AccessToken.temporary == False, RepositoryBuildTrigger.uuid >> None))
|
||||
|
||||
|
||||
def get_repo_delegate_token(namespace_name, repository_name, code):
|
||||
|
@ -1636,14 +1664,17 @@ def delete_delegate_token(namespace_name, repository_name, code):
|
|||
|
||||
def load_token_data(code):
|
||||
""" Load the permissions for any token by code. """
|
||||
selected = AccessToken.select(AccessToken, Repository, Role)
|
||||
with_role = selected.join(Role)
|
||||
with_repo = with_role.switch(AccessToken).join(Repository)
|
||||
fetched = list(with_repo.where(AccessToken.code == code))
|
||||
try:
|
||||
return (AccessToken
|
||||
.select(AccessToken, Repository, Namespace, Role)
|
||||
.join(Role)
|
||||
.switch(AccessToken)
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.where(AccessToken.code == code)
|
||||
.get())
|
||||
|
||||
if fetched:
|
||||
return fetched[0]
|
||||
else:
|
||||
except AccessToken.DoesNotExist:
|
||||
raise InvalidTokenException('Invalid delegate token code: %s' % code)
|
||||
|
||||
|
||||
|
@ -1660,15 +1691,15 @@ def get_repository_build(namespace_name, repository_name, build_uuid):
|
|||
def list_repository_builds(namespace_name, repository_name, limit,
|
||||
include_inactive=True):
|
||||
query = (RepositoryBuild
|
||||
.select(RepositoryBuild, RepositoryBuildTrigger, BuildTriggerService)
|
||||
.join(Repository)
|
||||
.switch(RepositoryBuild)
|
||||
.join(RepositoryBuildTrigger, JOIN_LEFT_OUTER)
|
||||
.join(BuildTriggerService, JOIN_LEFT_OUTER)
|
||||
.where(Repository.name == repository_name,
|
||||
Repository.namespace == namespace_name)
|
||||
.order_by(RepositoryBuild.started.desc())
|
||||
.limit(limit))
|
||||
.select(RepositoryBuild, RepositoryBuildTrigger, BuildTriggerService)
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.switch(RepositoryBuild)
|
||||
.join(RepositoryBuildTrigger, JOIN_LEFT_OUTER)
|
||||
.join(BuildTriggerService, JOIN_LEFT_OUTER)
|
||||
.where(Repository.name == repository_name, Namespace.username == namespace_name)
|
||||
.order_by(RepositoryBuild.started.desc())
|
||||
.limit(limit))
|
||||
|
||||
if not include_inactive:
|
||||
query = query.where(RepositoryBuild.phase != 'error',
|
||||
|
@ -1732,16 +1763,17 @@ def create_repo_notification(repo, event_name, method_name, config):
|
|||
|
||||
|
||||
def get_repo_notification(namespace_name, repository_name, uuid):
|
||||
joined = RepositoryNotification.select().join(Repository)
|
||||
found = list(joined.where(Repository.namespace == namespace_name,
|
||||
Repository.name == repository_name,
|
||||
RepositoryNotification.uuid == uuid))
|
||||
|
||||
if not found:
|
||||
try:
|
||||
return (RepositoryNotification
|
||||
.select(RepositoryNotification, Repository, Namespace)
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.where(Namespace.username == namespace_name, Repository.name == repository_name,
|
||||
RepositoryNotification.uuid == uuid)
|
||||
.get())
|
||||
except RepositoryNotification.DoesNotExist:
|
||||
raise InvalidNotificationException('No repository notification found with id: %s' % uuid)
|
||||
|
||||
return found[0]
|
||||
|
||||
|
||||
def delete_repo_notification(namespace_name, repository_name, uuid):
|
||||
found = get_repo_notification(namespace_name, repository_name, uuid)
|
||||
|
@ -1750,15 +1782,19 @@ def delete_repo_notification(namespace_name, repository_name, uuid):
|
|||
|
||||
|
||||
def list_repo_notifications(namespace_name, repository_name, event_name=None):
|
||||
joined = RepositoryNotification.select().join(Repository)
|
||||
where = joined.where(Repository.namespace == namespace_name,
|
||||
Repository.name == repository_name)
|
||||
query = (RepositoryNotification
|
||||
.select(RepositoryNotification, Repository, Namespace)
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.where(Namespace.username == namespace_name, Repository.name == repository_name))
|
||||
|
||||
if event_name:
|
||||
event = ExternalNotificationEvent.get(ExternalNotificationEvent.name == event_name)
|
||||
where = where.where(RepositoryNotification.event == event)
|
||||
query = (query
|
||||
.switch(RepositoryNotification)
|
||||
.join(ExternalNotificationEvent)
|
||||
.where(ExternalNotificationEvent.name == event_name))
|
||||
|
||||
return where
|
||||
return query
|
||||
|
||||
|
||||
def list_logs(start_time, end_time, performer=None, repository=None, namespace=None):
|
||||
|
@ -1802,16 +1838,17 @@ def create_build_trigger(repo, service_name, auth_token, user, pull_robot=None):
|
|||
def get_build_trigger(namespace_name, repository_name, trigger_uuid):
|
||||
try:
|
||||
return (RepositoryBuildTrigger
|
||||
.select(RepositoryBuildTrigger, BuildTriggerService, Repository)
|
||||
.join(BuildTriggerService)
|
||||
.switch(RepositoryBuildTrigger)
|
||||
.join(Repository)
|
||||
.switch(RepositoryBuildTrigger)
|
||||
.join(User)
|
||||
.where(RepositoryBuildTrigger.uuid == trigger_uuid,
|
||||
Repository.namespace == namespace_name,
|
||||
Repository.name == repository_name)
|
||||
.get())
|
||||
.select(RepositoryBuildTrigger, BuildTriggerService, Repository, Namespace)
|
||||
.join(BuildTriggerService)
|
||||
.switch(RepositoryBuildTrigger)
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.switch(RepositoryBuildTrigger)
|
||||
.join(User)
|
||||
.where(RepositoryBuildTrigger.uuid == trigger_uuid,
|
||||
Namespace.username == namespace_name,
|
||||
Repository.name == repository_name)
|
||||
.get())
|
||||
except RepositoryBuildTrigger.DoesNotExist:
|
||||
msg = 'No build trigger with uuid: %s' % trigger_uuid
|
||||
raise InvalidBuildTriggerException(msg)
|
||||
|
@ -1819,12 +1856,12 @@ def get_build_trigger(namespace_name, repository_name, trigger_uuid):
|
|||
|
||||
def list_build_triggers(namespace_name, repository_name):
|
||||
return (RepositoryBuildTrigger
|
||||
.select(RepositoryBuildTrigger, BuildTriggerService, Repository)
|
||||
.join(BuildTriggerService)
|
||||
.switch(RepositoryBuildTrigger)
|
||||
.join(Repository)
|
||||
.where(Repository.namespace == namespace_name,
|
||||
Repository.name == repository_name))
|
||||
.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 list_trigger_builds(namespace_name, repository_name, trigger_uuid,
|
||||
|
@ -1913,6 +1950,7 @@ def delete_notifications_by_kind(target, kind_name):
|
|||
Notification.delete().where(Notification.target == target,
|
||||
Notification.kind == kind_ref).execute()
|
||||
|
||||
|
||||
def delete_matching_notifications(target, kind_name, **kwargs):
|
||||
kind_ref = NotificationKind.get(name=kind_name)
|
||||
|
||||
|
@ -1943,6 +1981,7 @@ def delete_matching_notifications(target, kind_name, **kwargs):
|
|||
def get_active_users():
|
||||
return User.select().where(User.organization == False, User.robot == False)
|
||||
|
||||
|
||||
def get_active_user_count():
|
||||
return get_active_users().count()
|
||||
|
||||
|
@ -1956,11 +1995,13 @@ def detach_external_login(user, service_name):
|
|||
FederatedLogin.delete().where(FederatedLogin.user == user,
|
||||
FederatedLogin.service == service).execute()
|
||||
|
||||
|
||||
def delete_user(user):
|
||||
user.delete_instance(recursive=True, delete_nullable=True)
|
||||
|
||||
# TODO: also delete any repository data associated
|
||||
|
||||
|
||||
def check_health():
|
||||
# We will connect to the db, check that it contains some log entry kinds
|
||||
try:
|
||||
|
@ -1969,24 +2010,23 @@ def check_health():
|
|||
except:
|
||||
return False
|
||||
|
||||
def get_email_authorized_for_repo(namespace, repository, email):
|
||||
found = list(RepositoryAuthorizedEmail.select()
|
||||
.join(Repository)
|
||||
.where(Repository.namespace == namespace,
|
||||
Repository.name == repository,
|
||||
RepositoryAuthorizedEmail.email == email)
|
||||
.switch(RepositoryAuthorizedEmail)
|
||||
.limit(1))
|
||||
if not found or len(found) < 1:
|
||||
return None
|
||||
|
||||
return found[0]
|
||||
def get_email_authorized_for_repo(namespace, repository, email):
|
||||
try:
|
||||
return (RepositoryAuthorizedEmail
|
||||
.select(RepositoryAuthorizedEmail, Repository, Namespace)
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.where(Namespace.username == namespace, Repository.name == repository,
|
||||
RepositoryAuthorizedEmail.email == email)
|
||||
.get())
|
||||
except RepositoryAuthorizedEmail.DoesNotExist:
|
||||
return None
|
||||
|
||||
|
||||
def create_email_authorization_for_repo(namespace_name, repository_name, email):
|
||||
try:
|
||||
repo = Repository.get(Repository.name == repository_name,
|
||||
Repository.namespace == namespace_name)
|
||||
repo = _get_repository(namespace_name, repository_name)
|
||||
except Repository.DoesNotExist:
|
||||
raise DataModelException('Invalid repository %s/%s' %
|
||||
(namespace_name, repository_name))
|
||||
|
@ -1996,7 +2036,11 @@ def create_email_authorization_for_repo(namespace_name, repository_name, email):
|
|||
|
||||
def confirm_email_authorization_for_repo(code):
|
||||
try:
|
||||
found = RepositoryAuthorizedEmail.get(RepositoryAuthorizedEmail.code == code)
|
||||
found = (RepositoryAuthorizedEmail
|
||||
.select(RepositoryAuthorizedEmail, Repository, Namespace)
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.get(RepositoryAuthorizedEmail.code == code))
|
||||
except RepositoryAuthorizedEmail.DoesNotExist:
|
||||
raise DataModelException('Invalid confirmation code.')
|
||||
|
||||
|
|
Reference in a new issue