Switch get repo API to use a single list tags query
Should make things faster since the join occurs on the database side
This commit is contained in:
parent
d86b0254b3
commit
ab2f044331
4 changed files with 48 additions and 15 deletions
|
@ -83,13 +83,30 @@ def filter_tags_have_repository_event(query, event):
|
||||||
.order_by(RepositoryTag.lifetime_start_ts.desc()))
|
.order_by(RepositoryTag.lifetime_start_ts.desc()))
|
||||||
|
|
||||||
|
|
||||||
def get_tag_manifests(tags):
|
def get_tag_manifest_digests(tags):
|
||||||
""" Returns a map from tag ID to its associated manifest, if any. """
|
""" Returns a map from tag ID to its associated manifest digest, if any. """
|
||||||
if not tags:
|
if not tags:
|
||||||
return dict()
|
return dict()
|
||||||
|
|
||||||
manifests = TagManifest.select().where(TagManifest.tag << [t.id for t in tags])
|
manifests = (TagManifest
|
||||||
return {manifest.tag_id:manifest for manifest in manifests}
|
.select(TagManifest.tag, TagManifest.digest)
|
||||||
|
.where(TagManifest.tag << [t.id for t in tags]))
|
||||||
|
|
||||||
|
return {manifest.tag_id: manifest.digest for manifest in manifests}
|
||||||
|
|
||||||
|
|
||||||
|
def list_active_repo_tags(repo):
|
||||||
|
""" Returns all of the active, non-hidden tags in a repository, joined to they images
|
||||||
|
and (if present), their manifest.
|
||||||
|
"""
|
||||||
|
query = _tag_alive(RepositoryTag
|
||||||
|
.select(RepositoryTag, Image, TagManifest.digest)
|
||||||
|
.join(Image)
|
||||||
|
.where(RepositoryTag.repository == repo)
|
||||||
|
.switch(RepositoryTag)
|
||||||
|
.join(TagManifest, JOIN_LEFT_OUTER))
|
||||||
|
|
||||||
|
return query
|
||||||
|
|
||||||
|
|
||||||
def list_repository_tags(namespace_name, repository_name, include_hidden=False,
|
def list_repository_tags(namespace_name, repository_name, include_hidden=False,
|
||||||
|
@ -320,7 +337,7 @@ def list_repository_tag_history(repo_obj, page=1, size=100, specific_tag=None):
|
||||||
if not tags:
|
if not tags:
|
||||||
return [], {}, False
|
return [], {}, False
|
||||||
|
|
||||||
manifest_map = get_tag_manifests(tags)
|
manifest_map = get_tag_manifest_digests(tags)
|
||||||
return tags[0:size], manifest_map, len(tags) > size
|
return tags[0:size], manifest_map, len(tags) > size
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -322,7 +322,7 @@ class Repository(RepositoryParamResource):
|
||||||
return repo_data
|
return repo_data
|
||||||
|
|
||||||
# Older image-only repo code.
|
# Older image-only repo code.
|
||||||
def tag_view(tag, manifest):
|
def tag_view(tag):
|
||||||
tag_info = {
|
tag_info = {
|
||||||
'name': tag.name,
|
'name': tag.name,
|
||||||
'image_id': tag.image.docker_image_id,
|
'image_id': tag.image.docker_image_id,
|
||||||
|
@ -333,16 +333,14 @@ class Repository(RepositoryParamResource):
|
||||||
last_modified = format_date(datetime.fromtimestamp(tag.lifetime_start_ts))
|
last_modified = format_date(datetime.fromtimestamp(tag.lifetime_start_ts))
|
||||||
tag_info['last_modified'] = last_modified
|
tag_info['last_modified'] = last_modified
|
||||||
|
|
||||||
if manifest is not None:
|
if tag.tagmanifest is not None:
|
||||||
tag_info['manifest_digest'] = manifest.digest
|
tag_info['manifest_digest'] = tag.tagmanifest.digest
|
||||||
|
|
||||||
return tag_info
|
return tag_info
|
||||||
|
|
||||||
stats = None
|
stats = None
|
||||||
tags = model.tag.list_repository_tags(namespace, repository, include_storage=True)
|
tags = model.tag.list_active_repo_tags(repo)
|
||||||
manifests = model.tag.get_tag_manifests(tags)
|
tag_dict = {tag.name: tag_view(tag) for tag in tags}
|
||||||
|
|
||||||
tag_dict = {tag.name: tag_view(tag, manifests.get(tag.id)) for tag in tags}
|
|
||||||
if parsed_args['includeStats']:
|
if parsed_args['includeStats']:
|
||||||
stats = []
|
stats = []
|
||||||
found_dates = {}
|
found_dates = {}
|
||||||
|
@ -419,7 +417,7 @@ class Repository(RepositoryParamResource):
|
||||||
|
|
||||||
# Remove any builds from the queue.
|
# Remove any builds from the queue.
|
||||||
dockerfile_build_queue.delete_namespaced_items(namespace, repository)
|
dockerfile_build_queue.delete_namespaced_items(namespace, repository)
|
||||||
|
|
||||||
log_action('delete_repo', namespace,
|
log_action('delete_repo', namespace,
|
||||||
{'repo': repository, 'namespace': namespace})
|
{'repo': repository, 'namespace': namespace})
|
||||||
return '', 204
|
return '', 204
|
||||||
|
|
|
@ -44,7 +44,7 @@ class ListRepositoryTags(RepositoryParamResource):
|
||||||
tag_info['end_ts'] = tag.lifetime_end_ts
|
tag_info['end_ts'] = tag.lifetime_end_ts
|
||||||
|
|
||||||
if tag.id in manifest_map:
|
if tag.id in manifest_map:
|
||||||
tag_info['manifest_digest'] = manifest_map[tag.id].digest
|
tag_info['manifest_digest'] = manifest_map[tag.id]
|
||||||
|
|
||||||
return tag_info
|
return tag_info
|
||||||
|
|
||||||
|
|
|
@ -2425,6 +2425,24 @@ class TestDeleteRepository(ApiTestCase):
|
||||||
class TestGetRepository(ApiTestCase):
|
class TestGetRepository(ApiTestCase):
|
||||||
PUBLIC_REPO = PUBLIC_USER + '/publicrepo'
|
PUBLIC_REPO = PUBLIC_USER + '/publicrepo'
|
||||||
|
|
||||||
|
def test_get_largerepo(self):
|
||||||
|
self.login(ADMIN_ACCESS_USER)
|
||||||
|
|
||||||
|
# base + repo + is_starred + tags
|
||||||
|
with assert_query_count(BASE_LOGGEDIN_QUERY_COUNT + 4):
|
||||||
|
self.getJsonResponse(Repository,
|
||||||
|
params=dict(repository=ADMIN_ACCESS_USER + '/simple'))
|
||||||
|
|
||||||
|
# base + repo + is_starred + tags
|
||||||
|
with assert_query_count(BASE_LOGGEDIN_QUERY_COUNT + 4):
|
||||||
|
json = self.getJsonResponse(Repository,
|
||||||
|
params=dict(repository=ADMIN_ACCESS_USER + '/gargantuan'))
|
||||||
|
|
||||||
|
self.assertEquals(ADMIN_ACCESS_USER, json['namespace'])
|
||||||
|
self.assertEquals('gargantuan', json['name'])
|
||||||
|
|
||||||
|
self.assertEquals(False, json['is_public'])
|
||||||
|
|
||||||
def test_getrepo_badnames(self):
|
def test_getrepo_badnames(self):
|
||||||
self.login(ADMIN_ACCESS_USER)
|
self.login(ADMIN_ACCESS_USER)
|
||||||
|
|
||||||
|
@ -4540,7 +4558,7 @@ class TestRepositoryImageSecurity(ApiTestCase):
|
||||||
expected_code=200)
|
expected_code=200)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TestSuperUserCustomCertificates(ApiTestCase):
|
class TestSuperUserCustomCertificates(ApiTestCase):
|
||||||
def test_custom_certificates(self):
|
def test_custom_certificates(self):
|
||||||
|
|
Reference in a new issue