diff --git a/data/model.py b/data/model.py index 42dac1ea1..9846426b6 100644 --- a/data/model.py +++ b/data/model.py @@ -148,6 +148,13 @@ def get_repository_images(namespace_name, repository_name): return joined.where(Repository.name == repository_name, Repository.namespace == namespace_name) +def get_tag_images(namespace_name, repository_name, tag_name): + joined = Image.select().join(RepositoryTag).join(Repository) + fetched = list(joined.where(Repository.name == repository_name, + Repository.namespace == namespace_name, + RepositoryTag.name == tag_name)) + + return fetched def list_repository_tags(namespace_name, repository_name): select = RepositoryTag.select(RepositoryTag, Image) @@ -158,11 +165,7 @@ def list_repository_tags(namespace_name, repository_name): def get_tag_image(namespace_name, repository_name, tag_name): - joined = Image.select().join(RepositoryTag).join(Repository) - fetched = list(joined.where(Repository.name == repository_name, - Repository.namespace == namespace_name, - RepositoryTag.name == tag_name)) - + fetched = get_tag_images(namespace_name, repository_name, tag_name) if not fetched: raise DataModelException('Unable to find image for tag.') diff --git a/endpoints/api.py b/endpoints/api.py index a925d34ff..c858086cf 100644 --- a/endpoints/api.py +++ b/endpoints/api.py @@ -79,18 +79,20 @@ def update_repo_api(namespace, repository): abort(404) +def image_view(image): + return { + 'id': image.image_id, + 'created': image.created, + 'comment': image.comment, + } + + @app.route('/api/repository/', methods=['GET']) @login_required @parse_repository_name def get_repo_api(namespace, repository): logger.debug('Get repo: %s/%s' % (namespace, repository)) - def image_view(image): - return { - 'id': image.image_id, - 'created': image.created, - 'comment': image.comment, - } - + def tag_view(tag): image = model.get_tag_image(namespace, repository, tag.name) if not image: @@ -128,6 +130,21 @@ def role_view(repo_perm_obj): } +@app.route('/api/repository//tag//images', methods=['GET']) +@login_required +@parse_repository_name +def list_tag_images(namespace, repository, tag): + permission = ReadRepositoryPermission(namespace, repository) + if permission.can(): + images = model.get_tag_images(namespace, repository, tag) + + return jsonify({ + 'images': [image_view(image) for image in images] + }) + + abort(403) # Permission denied + + @app.route('/api/repository//permissions/', methods=['GET']) @login_required @parse_repository_name diff --git a/static/css/quay.css b/static/css/quay.css index 5610c1fea..7748bd8d2 100644 --- a/static/css/quay.css +++ b/static/css/quay.css @@ -182,11 +182,15 @@ p.editable:hover i { min-width: 300px; } -.repo-admin thead td { +.repo thead td { padding: 4px; color: #999; } -.repo-admin td { +.repo td { padding: 6px; +} + +.repo .images { + margin: 10px; } \ No newline at end of file diff --git a/static/js/controllers.js b/static/js/controllers.js index 365b695e6..c0a295af7 100644 --- a/static/js/controllers.js +++ b/static/js/controllers.js @@ -16,8 +16,24 @@ function LandingCtrl($scope) { } function RepoCtrl($scope, Restangular, $routeParams, $rootScope) { + var tabs = ['current-image', 'image-history']; + $rootScope.title = 'Loading...'; + $scope.showTab = function(tabName) { + for (var i = 0; i < tabs.length; ++i) { + $('#' + tabs[i]).hide(); + $('#' + tabs[i] + '-tab').removeClass('active'); + } + + $('#' + tabName).show(); + $('#' + tabName + '-tab').addClass('active'); + + if (tabName == 'image-history') { + $scope.listImages(); + } + }; + $scope.editDescription = function() { if (!$scope.repo.can_write) { return; } $('#descriptionEdit')[0].value = $scope.repo.description || ''; @@ -33,6 +49,15 @@ function RepoCtrl($scope, Restangular, $routeParams, $rootScope) { $scope.parseDate = function(dateString) { return Date.parse(dateString); }; + + $scope.listImages = function() { + if ($scope.imageHistory) { return; } + + var imageFetch = Restangular.one('repository/' + namespace + '/' + name + '/tag/' + $scope.currentTag.name + '/images'); + imageFetch.get().then(function(resp) { + $scope.imageHistory = resp.images; + }); + }; var namespace = $routeParams.namespace; var name = $routeParams.name; diff --git a/static/partials/view-repo.html b/static/partials/view-repo.html index de81596f5..6e2199a49 100644 --- a/static/partials/view-repo.html +++ b/static/partials/view-repo.html @@ -52,8 +52,8 @@ -
  • Current Image
  • -
  • Image History
  • +
  • Current Image
  • +
  • Image History
  • @@ -73,6 +73,30 @@
    + +