Performance improvements for the repo API and the new repo UI
This commit is contained in:
parent
bc6baae050
commit
ab2331a486
9 changed files with 119 additions and 100 deletions
|
@ -1146,6 +1146,9 @@ def get_repo_image_extended(namespace_name, repository_name, docker_image_id):
|
|||
|
||||
return images[0]
|
||||
|
||||
def is_repository_public(repository):
|
||||
return repository.visibility == _get_public_repo_visibility()
|
||||
|
||||
def repository_is_public(namespace_name, repository_name):
|
||||
try:
|
||||
(Repository
|
||||
|
@ -1579,9 +1582,15 @@ def _tag_alive(query):
|
|||
(RepositoryTag.lifetime_end_ts > int(time.time())))
|
||||
|
||||
|
||||
def list_repository_tags(namespace_name, repository_name, include_hidden=False):
|
||||
def list_repository_tags(namespace_name, repository_name, include_hidden=False,
|
||||
include_storage=False):
|
||||
|
||||
toSelect = (RepositoryTag, Image)
|
||||
if include_storage:
|
||||
toSelect = (RepositoryTag, Image, ImageStorage)
|
||||
|
||||
query = _tag_alive(RepositoryTag
|
||||
.select(RepositoryTag, Image)
|
||||
.select(*toSelect)
|
||||
.join(Repository)
|
||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||
.switch(RepositoryTag)
|
||||
|
@ -1592,6 +1601,9 @@ def list_repository_tags(namespace_name, repository_name, include_hidden=False):
|
|||
if not include_hidden:
|
||||
query = query.where(RepositoryTag.hidden == False)
|
||||
|
||||
if include_storage:
|
||||
query = query.switch(Image).join(ImageStorage)
|
||||
|
||||
return query
|
||||
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ def image_view(image, image_map):
|
|||
command = extended_props.command
|
||||
|
||||
def docker_id(aid):
|
||||
if not aid:
|
||||
if not aid or not aid in image_map:
|
||||
return ''
|
||||
|
||||
return image_map[aid]
|
||||
|
@ -51,19 +51,26 @@ class RepositoryImageList(RepositoryParamResource):
|
|||
all_tags = model.list_repository_tags(namespace, repository)
|
||||
|
||||
tags_by_image_id = defaultdict(list)
|
||||
found_image_ids = set()
|
||||
|
||||
for tag in all_tags:
|
||||
tags_by_image_id[tag.image.docker_image_id].append(tag.name)
|
||||
found_image_ids.add(str(tag.image.id))
|
||||
found_image_ids.update(tag.image.ancestors.split('/')[1:-1])
|
||||
|
||||
image_map = {}
|
||||
filtered_images = []
|
||||
for image in all_images:
|
||||
image_map[str(image.id)] = image.docker_image_id
|
||||
if str(image.id) in found_image_ids:
|
||||
image_map[str(image.id)] = image.docker_image_id
|
||||
filtered_images.append(image)
|
||||
|
||||
def add_tags(image_json):
|
||||
image_json['tags'] = tags_by_image_id[image_json['id']]
|
||||
return image_json
|
||||
|
||||
return {
|
||||
'images': [add_tags(image_view(image, image_map)) for image in all_images]
|
||||
'images': [add_tags(image_view(image, image_map)) for image in filtered_images]
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -188,16 +188,9 @@ class Repository(RepositoryParamResource):
|
|||
|
||||
return tag_info
|
||||
|
||||
organization = None
|
||||
try:
|
||||
organization = model.get_organization(namespace)
|
||||
except model.InvalidOrganizationException:
|
||||
pass
|
||||
|
||||
is_public = model.repository_is_public(namespace, repository)
|
||||
repo = model.get_repository(namespace, repository)
|
||||
if repo:
|
||||
tags = model.list_repository_tags(namespace, repository)
|
||||
tags = model.list_repository_tags(namespace, repository, include_storage=True)
|
||||
tag_dict = {tag.name: tag_view(tag) for tag in tags}
|
||||
can_write = ModifyRepositoryPermission(namespace, repository).can()
|
||||
can_admin = AdministerRepositoryPermission(namespace, repository).can()
|
||||
|
@ -206,6 +199,7 @@ class Repository(RepositoryParamResource):
|
|||
|
||||
is_starred = (model.repository_is_starred(get_authenticated_user(), repo)
|
||||
if get_authenticated_user() else False)
|
||||
is_public = model.is_repository_public(repo)
|
||||
|
||||
return {
|
||||
'namespace': namespace,
|
||||
|
@ -216,7 +210,7 @@ class Repository(RepositoryParamResource):
|
|||
'can_admin': can_admin,
|
||||
'is_public': is_public,
|
||||
'is_building': len(list(active_builds)) > 0,
|
||||
'is_organization': bool(organization),
|
||||
'is_organization': repo.namespace_user.organization,
|
||||
'is_starred': is_starred,
|
||||
'status_token': repo.badge_token if not is_public else '',
|
||||
'stats': {
|
||||
|
|
|
@ -1,61 +1,64 @@
|
|||
<div class="repo-panel-changes-element">
|
||||
<!-- No Tags Selected -->
|
||||
<div class="empty" ng-if="!selectedTags.length">
|
||||
<div class="empty-primary-msg">No tags selected to view</div>
|
||||
<div class="empty-secondary-msg">
|
||||
Please select one or more tags in the <i class="fa fa-tags" style="margin-left: 4px; margin-right: 4px;"></i> Tags tab to visualize.
|
||||
<div class="resource-view" resource="imagesResource"
|
||||
error-message="'Could not load repository images'">
|
||||
<!-- No Tags Selected -->
|
||||
<div class="empty" ng-if="!selectedTags.length">
|
||||
<div class="empty-primary-msg">No tags selected to view</div>
|
||||
<div class="empty-secondary-msg">
|
||||
Please select one or more tags in the <i class="fa fa-tags" style="margin-left: 4px; margin-right: 4px;"></i> Tags tab to visualize.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tags Selected -->
|
||||
<div ng-if="selectedTags.length > 0">
|
||||
<h3 class="tab-header">
|
||||
Visualize Tags:
|
||||
<span class="visualized-tag" ng-repeat="tag in selectedTags">
|
||||
<i class="fa fa-tag"></i>{{ tag }}
|
||||
</span>
|
||||
</h3>
|
||||
<!-- Tags Selected -->
|
||||
<div ng-if="selectedTags.length > 0">
|
||||
<h3 class="tab-header">
|
||||
Visualize Tags:
|
||||
<span class="visualized-tag" ng-repeat="tag in selectedTags">
|
||||
<i class="fa fa-tag"></i>{{ tag }}
|
||||
</span>
|
||||
</h3>
|
||||
|
||||
<div id="image-history row" class="resource-view" resource="imagesResource"
|
||||
error-message="'Cannot load repository images'">
|
||||
<div id="image-history row" class="resource-view" resource="imagesResource"
|
||||
error-message="'Cannot load repository images'">
|
||||
|
||||
<!-- Tree View container -->
|
||||
<div class="col-md-8">
|
||||
<div class="panel panel-default">
|
||||
<!-- Image history tree -->
|
||||
<div id="image-history-container" onresize="tree.notifyResized()"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Side Panel -->
|
||||
<div class="col-md-4">
|
||||
<div class="side-panel-title" ng-if="currentTag">
|
||||
<i class="fa fa-tag"></i>{{ currentTag }}
|
||||
</div>
|
||||
<div class="side-panel-title" ng-if="currentImage">
|
||||
<i class="fa fa-archive"></i>{{ currentImage.substr(0, 12) }}
|
||||
</div>
|
||||
|
||||
<div class="side-panel">
|
||||
<!-- Tag Info -->
|
||||
<div class="tag-info-sidebar"
|
||||
tracker="tracker"
|
||||
tag="currentTag"
|
||||
image-selected="setImage(image)"
|
||||
delete-tag-requested="tagActionHandler.askDeleteTag(tag)"
|
||||
ng-if="currentTag">
|
||||
</div>
|
||||
|
||||
<!-- Image Info -->
|
||||
<div class="image-info-sidebar"
|
||||
tracker="tracker"
|
||||
image="currentImage"
|
||||
tag-selected="setTag(tag)"
|
||||
add-tag-requested="tagActionHandler.askAddTag(image)"
|
||||
ng-if="currentImage">
|
||||
<!-- Tree View container -->
|
||||
<div class="col-md-8">
|
||||
<div class="panel panel-default">
|
||||
<!-- Image history tree -->
|
||||
<div id="image-history-container" onresize="tree.notifyResized()"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Side Panel -->
|
||||
<div class="col-md-4">
|
||||
<div class="side-panel-title" ng-if="currentTag">
|
||||
<i class="fa fa-tag"></i>{{ currentTag }}
|
||||
</div>
|
||||
<div class="side-panel-title" ng-if="currentImage">
|
||||
<i class="fa fa-archive"></i>{{ currentImage.substr(0, 12) }}
|
||||
</div>
|
||||
|
||||
<div class="side-panel">
|
||||
<!-- Tag Info -->
|
||||
<div class="tag-info-sidebar"
|
||||
tracker="tracker"
|
||||
tag="currentTag"
|
||||
image-selected="setImage(image)"
|
||||
delete-tag-requested="tagActionHandler.askDeleteTag(tag)"
|
||||
ng-if="currentTag">
|
||||
</div>
|
||||
|
||||
<!-- Image Info -->
|
||||
<div class="image-info-sidebar"
|
||||
tracker="tracker"
|
||||
image="currentImage"
|
||||
tag-selected="setTag(tag)"
|
||||
add-tag-requested="tagActionHandler.askAddTag(image)"
|
||||
ng-if="currentImage">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<div class="repo-panel-tags-element">
|
||||
<h3 class="tab-header">Repository Tags</h3>
|
||||
<div class="resource-view" resource="imagesResource" error-message="'Could not load images'">
|
||||
|
||||
<div class="co-check-bar">
|
||||
<span class="cor-checkable-menu" controller="checkedTags">
|
||||
<div class="cor-checkable-menu-item" item-filter="allTagFilter">
|
||||
|
|
|
@ -89,6 +89,10 @@ angular.module('quay').directive('repoPanelChanges', function () {
|
|||
scope: {
|
||||
'repository': '=repository',
|
||||
'selectedTags': '=selectedTags',
|
||||
|
||||
'imagesResource': '=imagesResource',
|
||||
'images': '=images',
|
||||
|
||||
'isEnabled': '=isEnabled'
|
||||
},
|
||||
controller: function($scope, $element, $timeout, ApiService, UtilService, ImageMetadataService) {
|
||||
|
@ -99,13 +103,24 @@ angular.module('quay').directive('repoPanelChanges', function () {
|
|||
$scope.currentImage = null;
|
||||
$scope.currentTag = null;
|
||||
|
||||
if (!$scope.imagesResource) {
|
||||
loadImages();
|
||||
if (!$scope.tracker) {
|
||||
updateImages();
|
||||
}
|
||||
};
|
||||
|
||||
var updateImages = function() {
|
||||
if (!$scope.repository || !$scope.images) { return; }
|
||||
|
||||
$scope.tracker = new RepositoryImageTracker($scope.repository, $scope.images);
|
||||
|
||||
if ($scope.selectedTags && $scope.selectedTags.length) {
|
||||
refreshTree();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.$watch('selectedTags', update)
|
||||
$scope.$watch('repository', update);
|
||||
$scope.$watch('images', updateImages);
|
||||
|
||||
$scope.$watch('isEnabled', function(isEnabled) {
|
||||
if (isEnabled) {
|
||||
|
@ -147,23 +162,6 @@ angular.module('quay').directive('repoPanelChanges', function () {
|
|||
}
|
||||
};
|
||||
|
||||
var loadImages = function(opt_callback) {
|
||||
var params = {
|
||||
'repository': $scope.repository.namespace + '/' + $scope.repository.name
|
||||
};
|
||||
|
||||
$scope.imagesResource = ApiService.listRepositoryImagesAsResource(params).get(function(resp) {
|
||||
$scope.images = resp.images;
|
||||
$scope.tracker = new RepositoryImageTracker($scope.repository, $scope.images);
|
||||
|
||||
if ($scope.selectedTags && $scope.selectedTags.length) {
|
||||
refreshTree();
|
||||
}
|
||||
|
||||
opt_callback && opt_callback();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.setImage = function(image_id) {
|
||||
$scope.currentTag = null;
|
||||
$scope.currentImage = image_id;
|
||||
|
|
|
@ -10,7 +10,9 @@ angular.module('quay').directive('repoPanelTags', function () {
|
|||
restrict: 'C',
|
||||
scope: {
|
||||
'repository': '=repository',
|
||||
'selectedTags': '=selectedTags'
|
||||
'selectedTags': '=selectedTags',
|
||||
'imagesResource': '=imagesResource',
|
||||
'images': '=images',
|
||||
},
|
||||
controller: function($scope, $element, $filter, $location, ApiService, UIService) {
|
||||
var orderBy = $filter('orderBy');
|
||||
|
@ -24,16 +26,6 @@ angular.module('quay').directive('repoPanelTags', function () {
|
|||
$scope.iterationState = {};
|
||||
$scope.tagActionHandler = null;
|
||||
|
||||
var loadImages = function() {
|
||||
var params = {
|
||||
'repository': $scope.repository.namespace + '/' + $scope.repository.name
|
||||
};
|
||||
|
||||
$scope.imagesResource = ApiService.listRepositoryImagesAsResource(params).get(function(resp) {
|
||||
$scope.images = resp.images;
|
||||
});
|
||||
};
|
||||
|
||||
var setTagState = function() {
|
||||
if (!$scope.repository || !$scope.selectedTags) { return; }
|
||||
|
||||
|
@ -124,9 +116,6 @@ angular.module('quay').directive('repoPanelTags', function () {
|
|||
$scope.$watch('repository', function(repository) {
|
||||
if (!repository) { return; }
|
||||
|
||||
// Load the repository's images.
|
||||
loadImages();
|
||||
|
||||
// Process each of the tags.
|
||||
setTagState();
|
||||
});
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
$scope.viewScope = {
|
||||
'selectedTags': [],
|
||||
'repository': null,
|
||||
'images': null,
|
||||
'imagesResource': null,
|
||||
'builds': null,
|
||||
'changesVisible': false
|
||||
};
|
||||
|
@ -70,6 +72,16 @@
|
|||
});
|
||||
};
|
||||
|
||||
var loadImages = function() {
|
||||
var params = {
|
||||
'repository': $scope.namespace + '/' + $scope.name
|
||||
};
|
||||
|
||||
$scope.viewScope.imagesResource = ApiService.listRepositoryImagesAsResource(params).get(function(resp) {
|
||||
$scope.viewScope.images = resp.images;
|
||||
});
|
||||
};
|
||||
|
||||
var loadRepositoryBuilds = function(callback) {
|
||||
var params = {
|
||||
'repository': $scope.namespace + '/' + $scope.name,
|
||||
|
@ -86,8 +98,9 @@
|
|||
}, errorHandler);
|
||||
};
|
||||
|
||||
// Load the repository.
|
||||
// Load the repository and images.
|
||||
loadRepository();
|
||||
loadImages();
|
||||
|
||||
$scope.setTags = function(tagNames) {
|
||||
if (!tagNames) {
|
||||
|
|
|
@ -59,6 +59,8 @@
|
|||
<div id="tags" class="tab-pane">
|
||||
<div class="repo-panel-tags"
|
||||
repository="viewScope.repository"
|
||||
images="viewScope.images"
|
||||
images-resource="viewScope.imagesResource"
|
||||
selected-tags="viewScope.selectedTags"></div>
|
||||
</div>
|
||||
|
||||
|
@ -73,6 +75,8 @@
|
|||
<div id="changes" class="tab-pane">
|
||||
<div class="repo-panel-changes"
|
||||
repository="viewScope.repository"
|
||||
images="viewScope.images"
|
||||
images-resource="viewScope.imagesResource"
|
||||
selected-tags="viewScope.selectedTags"
|
||||
is-enabled="viewScope.changesVisible"></div>
|
||||
</div>
|
||||
|
|
Reference in a new issue