From d41dcaae231b3e447ebd800cf499027928f6ee74 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 28 Mar 2018 13:56:09 -0400 Subject: [PATCH 1/2] Rename image-link into manifest-link, and change to typescript, in prep for the UI changes to link to manifests --- .../ui/{image-link.css => manifest-link.css} | 8 ++-- static/css/directives/ui/repo-tag-history.css | 2 +- static/directives/image-feature-view.html | 7 +-- static/directives/image-link.html | 37 -------------- .../directives/image-vulnerability-view.html | 11 ++--- static/directives/repo-tag-history.html | 36 +++++++------- .../directives/repo-view/repo-panel-tags.html | 6 +-- static/directives/tag-operations-dialog.html | 6 +-- static/js/directives/ui/image-link.js | 47 ------------------ .../manifest-link.component.html | 37 ++++++++++++++ .../manifest-link/manifest-link.component.ts | 48 +++++++++++++++++++ static/js/quay.module.ts | 2 + 12 files changed, 122 insertions(+), 125 deletions(-) rename static/css/directives/ui/{image-link.css => manifest-link.css} (82%) delete mode 100644 static/directives/image-link.html delete mode 100644 static/js/directives/ui/image-link.js create mode 100644 static/js/directives/ui/manifest-link/manifest-link.component.html create mode 100644 static/js/directives/ui/manifest-link/manifest-link.component.ts diff --git a/static/css/directives/ui/image-link.css b/static/css/directives/ui/manifest-link.css similarity index 82% rename from static/css/directives/ui/image-link.css rename to static/css/directives/ui/manifest-link.css index 9e9b38913..6996642c1 100644 --- a/static/css/directives/ui/image-link.css +++ b/static/css/directives/ui/manifest-link.css @@ -1,16 +1,16 @@ -.image-link { +.manifest-link { display: inline-block; white-space: nowrap; width: 120px; } -.image-link a { +.manifest-link a { font-family: Consolas, "Lucida Console", Monaco, monospace; font-size: 12px; text-decoration: none; } -.image-link .id-label { +.manifest-link .id-label { font-size: 10px; cursor: pointer; padding: 2px; @@ -24,6 +24,6 @@ display: inline-block; } -.image-link .id-label.cas { +.manifest-link .id-label.cas { background-color: #e8f1f6; } \ No newline at end of file diff --git a/static/css/directives/ui/repo-tag-history.css b/static/css/directives/ui/repo-tag-history.css index 6731f6af1..68357eb47 100644 --- a/static/css/directives/ui/repo-tag-history.css +++ b/static/css/directives/ui/repo-tag-history.css @@ -252,7 +252,7 @@ background: #F6FCFF; } -.repo-tag-history-element .history-entry .image-link { +.repo-tag-history-element .history-entry .manifest-link { margin-left: 6px; } diff --git a/static/directives/image-feature-view.html b/static/directives/image-feature-view.html index 3ae932275..b4faae903 100644 --- a/static/directives/image-feature-view.html +++ b/static/directives/image-feature-view.html @@ -159,17 +159,14 @@ + log-base="2"> - - - - + (No Command) diff --git a/static/directives/image-link.html b/static/directives/image-link.html deleted file mode 100644 index 88d66add0..000000000 --- a/static/directives/image-link.html +++ /dev/null @@ -1,37 +0,0 @@ - - V1ID - - SHA256 - - - {{ imageId.substr(0, 12) }} - {{ getShortDigest(manifestDigest) }} - - - - diff --git a/static/directives/image-vulnerability-view.html b/static/directives/image-vulnerability-view.html index 3f127b3cf..1e1977cfa 100644 --- a/static/directives/image-vulnerability-view.html +++ b/static/directives/image-vulnerability-view.html @@ -152,12 +152,9 @@ - - - - - + + (No Command) @@ -181,8 +178,8 @@ - Introduced in Image: - + Introduced in Layer: + {{ ::vuln.imageId }} diff --git a/static/directives/repo-tag-history.html b/static/directives/repo-tag-history.html index 7b4c677d0..7e7d8fa31 100644 --- a/static/directives/repo-tag-history.html +++ b/static/directives/repo-tag-history.html @@ -38,38 +38,38 @@ was recreated pointing to - + manifest-digest="entry.manifest_digest"> was created pointing to - + manifest-digest="entry.manifest_digest"> was deleted was moved to - + manifest-digest="entry.manifest_digest"> from - + manifest-digest="entry.old_manifest_digest"> was reverted to - + manifest-digest="entry.manifest_digest"> from - + manifest-digest="entry.old_manifest_digest"> @@ -87,21 +87,21 @@ Restore {{ entry.tag_name }} to - + manifest-digest="entry.manifest_digest"> Revert {{ entry.tag_name }} to - + manifest-digest="entry.old_manifest_digest"> Restore {{ entry.tag_name }} to - + manifest-digest="entry.old_manifest_digest"> diff --git a/static/directives/repo-view/repo-panel-tags.html b/static/directives/repo-view/repo-panel-tags.html index 450b4f802..32c485386 100644 --- a/static/directives/repo-view/repo-panel-tags.html +++ b/static/directives/repo-view/repo-panel-tags.html @@ -237,9 +237,9 @@ - + - + @@ -293,7 +293,7 @@
- +
diff --git a/static/directives/tag-operations-dialog.html b/static/directives/tag-operations-dialog.html index 12e1b7d21..40a1c6555 100644 --- a/static/directives/tag-operations-dialog.html +++ b/static/directives/tag-operations-dialog.html @@ -162,9 +162,9 @@ Are you sure you want to restore tag {{ restoreTagInfo.tag.name }} to image - ? + ? diff --git a/static/js/directives/ui/image-link.js b/static/js/directives/ui/image-link.js deleted file mode 100644 index 9f8d9fb53..000000000 --- a/static/js/directives/ui/image-link.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * An element which displays a link to a repository image. - */ -angular.module('quay').directive('imageLink', function () { - var directiveDefinitionObject = { - priority: 0, - templateUrl: '/static/directives/image-link.html', - replace: false, - transclude: true, - restrict: 'C', - scope: { - 'repository': '=repository', - 'imageId': '=imageId', - 'manifestDigest': '=?manifestDigest' - }, - controller: function($scope, $element, $timeout) { - $scope.showingCopyBox = false; - - $scope.hasSHA256 = function(digest) { - return digest && digest.indexOf('sha256:') == 0; - }; - - $scope.getShortDigest = function(digest) { - return digest.substr('sha256:'.length).substr(0, 12); - }; - - $scope.showCopyBox = function() { - $scope.showingCopyBox = true; - - // Necessary to wait for digest cycle to complete. - $timeout(function() { - $element.find('.modal').modal('show'); - }, 10); - }; - - $scope.hideCopyBox = function() { - $element.find('.modal').modal('hide'); - - // Wait for the modal to hide before removing from the DOM. - $timeout(function() { - $scope.showingCopyBox = false; - }, 10); - }; - } - }; - return directiveDefinitionObject; -}); \ No newline at end of file diff --git a/static/js/directives/ui/manifest-link/manifest-link.component.html b/static/js/directives/ui/manifest-link/manifest-link.component.html new file mode 100644 index 000000000..1b9425aee --- /dev/null +++ b/static/js/directives/ui/manifest-link/manifest-link.component.html @@ -0,0 +1,37 @@ + + V1ID + + SHA256 + + + {{ $ctrl.imageId.substr(0, 12) }} + {{ $ctrl.getShortDigest($ctrl.manifestDigest) }} + + + + diff --git a/static/js/directives/ui/manifest-link/manifest-link.component.ts b/static/js/directives/ui/manifest-link/manifest-link.component.ts new file mode 100644 index 000000000..fbdfc3b01 --- /dev/null +++ b/static/js/directives/ui/manifest-link/manifest-link.component.ts @@ -0,0 +1,48 @@ +import { Input, Component, Inject } from 'ng-metadata/core'; +import { Repository } from '../../../types/common.types'; + + +/** + * A component that links to a manifest view. + */ +@Component({ + selector: 'manifest-link', + templateUrl: '/static/js/directives/ui/manifest-link/manifest-link.component.html' +}) +export class ManifestLinkComponent { + + @Input('<') public repository: Repository; + @Input('<') public manifestDigest: string; + @Input('<') public imageId: string; + + private showingCopyBox: boolean = false; + + constructor(@Inject('$timeout') private $timeout, @Inject('$element') private $element) { + } + + private hasSHA256(digest: string) { + return digest && digest.indexOf('sha256:') == 0; + } + + private getShortDigest(digest: string) { + return digest.substr('sha256:'.length).substr(0, 12); + } + + private showCopyBox() { + this.showingCopyBox = true; + + // Necessary to wait for digest cycle to complete. + this.$timeout(() => { + this.$element.find('.modal').modal('show'); + }, 10); + }; + + private hideCopyBox() { + this.$element.find('.modal').modal('hide'); + + // Wait for the modal to hide before removing from the DOM. + this.$timeout(() => { + this.showingCopyBox = false; + }, 10); + }; +} diff --git a/static/js/quay.module.ts b/static/js/quay.module.ts index 682169e8a..d17c399bc 100644 --- a/static/js/quay.module.ts +++ b/static/js/quay.module.ts @@ -40,6 +40,7 @@ import { TriggerDescriptionComponent } from './directives/ui/trigger-description import { TimeAgoComponent } from './directives/ui/time-ago/time-ago.component'; import { TimeDisplayComponent } from './directives/ui/time-display/time-display.component'; import { AppSpecificTokenManagerComponent } from './directives/ui/app-specific-token-manager/app-specific-token-manager.component'; +import { ManifestLinkComponent } from './directives/ui/manifest-link/manifest-link.component'; import { MarkdownModule } from './directives/ui/markdown/markdown.module'; import * as Clipboard from 'clipboard'; @@ -85,6 +86,7 @@ import * as Clipboard from 'clipboard'; TimeAgoComponent, TimeDisplayComponent, AppSpecificTokenManagerComponent, + ManifestLinkComponent, ], providers: [ ViewArrayImpl, From fc6eb71ab15962c61821abc99706213777a64ba7 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 28 Mar 2018 16:03:18 -0400 Subject: [PATCH 2/2] Switch from an image view UI to a manifest view UI We no longer allow viewing individual images, but instead only manifests. This will help with the transition to Clair V3 (which is manifest based) and, eventually, the the new data model (which will also be manifest based) --- endpoints/api/manifest.py | 17 +++++ endpoints/api/manifest_models_interface.py | 20 ++++++ endpoints/api/manifest_models_pre_oci.py | 19 ++++- endpoints/api/test/test_manifest.py | 22 ++++++ endpoints/api/test/test_security.py | 7 ++ static/css/directives/ui/image-view-layer.css | 36 +--------- ...ure-view.css => manifest-feature-view.css} | 46 ++++++------ static/css/directives/ui/manifest-link.css | 2 +- ...ew.css => manifest-vulnerability-view.css} | 72 +++++++++---------- .../{image-view.css => manifest-view.css} | 28 ++++---- static/directives/image-view-layer.html | 7 +- ...e-view.html => manifest-feature-view.html} | 14 ++-- ....html => manifest-vulnerability-view.html} | 20 +++--- .../directives/repo-view/repo-panel-tags.html | 14 +++- .../directives/repo-view/repo-panel-tags.js | 4 ++ ...ature-view.js => manifest-feature-view.js} | 28 ++++---- .../manifest-link.component.html | 12 ++-- .../manifest-link/manifest-link.component.ts | 1 + ...view.js => manifest-vulnerability-view.js} | 28 ++++---- static/js/pages/image-view.js | 69 ------------------ static/js/pages/manifest-view.js | 60 ++++++++++++++++ static/js/quay-routes.module.ts | 2 +- static/js/services/vulnerability-service.js | 20 +++--- .../{image-view.html => manifest-view.html} | 24 +++---- 24 files changed, 312 insertions(+), 260 deletions(-) create mode 100644 endpoints/api/test/test_manifest.py rename static/css/directives/ui/{image-feature-view.css => manifest-feature-view.css} (50%) rename static/css/directives/ui/{image-vulnerability-view.css => manifest-vulnerability-view.css} (51%) rename static/css/pages/{image-view.css => manifest-view.css} (54%) rename static/directives/{image-feature-view.html => manifest-feature-view.html} (93%) rename static/directives/{image-vulnerability-view.html => manifest-vulnerability-view.html} (93%) rename static/js/directives/ui/{image-feature-view.js => manifest-feature-view.js} (75%) rename static/js/directives/ui/{image-vulnerability-view.js => manifest-vulnerability-view.js} (81%) delete mode 100644 static/js/pages/image-view.js create mode 100644 static/js/pages/manifest-view.js rename static/partials/{image-view.html => manifest-view.html} (69%) diff --git a/endpoints/api/manifest.py b/endpoints/api/manifest.py index 7eaad827b..b1546c8c5 100644 --- a/endpoints/api/manifest.py +++ b/endpoints/api/manifest.py @@ -1,4 +1,5 @@ """ Manage the manifests of a repository. """ +import json from app import label_validator from flask import request @@ -18,6 +19,22 @@ MANIFEST_DIGEST_ROUTE = BASE_MANIFEST_ROUTE.format(digest_tools.DIGEST_PATTERN) ALLOWED_LABEL_MEDIA_TYPES = ['text/plain', 'application/json'] +@resource(MANIFEST_DIGEST_ROUTE) +@path_param('repository', 'The full path of the repository. e.g. namespace/name') +@path_param('manifestref', 'The digest of the manifest') +class RepositoryManifest(RepositoryParamResource): + """ Resource for retrieving a specific repository manifest. """ + @require_repo_read + @nickname('getRepoManifest') + @disallow_for_app_repositories + def get(self, namespace_name, repository_name, manifestref): + manifest = model.get_repository_manifest(namespace_name, repository_name, manifestref) + if manifest is None: + raise NotFound() + + return manifest.to_dict() + + @resource(MANIFEST_DIGEST_ROUTE + '/labels') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('manifestref', 'The digest of the manifest') diff --git a/endpoints/api/manifest_models_interface.py b/endpoints/api/manifest_models_interface.py index 618e434b7..03615b208 100644 --- a/endpoints/api/manifest_models_interface.py +++ b/endpoints/api/manifest_models_interface.py @@ -28,7 +28,21 @@ class ManifestLabel( 'source_type': self.source_type_name, 'media_type': self.media_type_name, } + +class ManifestAndImage( + namedtuple('ManifestAndImage', [ + 'digest', + 'manifest_data', + 'image', + ])): + def to_dict(self): + return { + 'digest': self.digest, + 'manifest_data': self.manifest_data, + 'image': self.image.to_dict(), + } + @add_metaclass(ABCMeta) class ManifestLabelInterface(object): @@ -95,3 +109,9 @@ class ManifestLabelInterface(object): Returns: ManifestLabel or None """ + + @abstractmethod + def get_repository_manifest(self, namespace_name, repository_name, digest): + """ + Returns the manifest and image for the manifest with the specified digest, if any. + """ diff --git a/endpoints/api/manifest_models_pre_oci.py b/endpoints/api/manifest_models_pre_oci.py index 9cc44ce93..b7abd45e2 100644 --- a/endpoints/api/manifest_models_pre_oci.py +++ b/endpoints/api/manifest_models_pre_oci.py @@ -1,5 +1,8 @@ -from manifest_models_interface import ManifestLabel, ManifestLabelInterface +import json + +from manifest_models_interface import ManifestLabel, ManifestLabelInterface, ManifestAndImage from data import model +from image_models_pre_oci import pre_oci_model as image_models class ManifestLabelPreOCI(ManifestLabelInterface): @@ -36,6 +39,20 @@ class ManifestLabelPreOCI(ManifestLabelInterface): return self._label(model.label.delete_manifest_label(label_uuid, tag_manifest)) + def get_repository_manifest(self, namespace_name, repository_name, digest): + try: + tag_manifest = model.tag.load_manifest_by_digest(namespace_name, repository_name, digest) + except model.DataModelException: + return None + + # TODO: remove this dependency on image once we've moved to the new data model. + image = image_models.get_repository_image(namespace_name, repository_name, + tag_manifest.tag.image.docker_image_id) + + manifest_data = json.loads(tag_manifest.json_data) + return ManifestAndImage(digest=digest, manifest_data=manifest_data, image=image) + + def _label(self, label_obj): if not label_obj: return None diff --git a/endpoints/api/test/test_manifest.py b/endpoints/api/test/test_manifest.py new file mode 100644 index 000000000..906ee07d2 --- /dev/null +++ b/endpoints/api/test/test_manifest.py @@ -0,0 +1,22 @@ +import pytest + +from data import model +from endpoints.api.manifest import RepositoryManifest +from endpoints.api.test.shared import conduct_api_call +from endpoints.test.shared import client_with_identity +from test.fixtures import * + +def test_repository_manifest(client): + with client_with_identity('devtable', client) as cl: + tags = model.tag.list_repository_tags('devtable', 'simple') + digests = model.tag.get_tag_manifest_digests(tags) + for tag in tags: + manifest = digests[tag.id] + params = { + 'repository': 'devtable/simple', + 'manifestref': manifest, + } + result = conduct_api_call(cl, RepositoryManifest, 'GET', params, None, 200).json + assert result['digest'] == manifest + assert result['manifest_data'] + assert result['image'] diff --git a/endpoints/api/test/test_security.py b/endpoints/api/test/test_security.py index 95d9b3acd..e644063a1 100644 --- a/endpoints/api/test/test_security.py +++ b/endpoints/api/test/test_security.py @@ -15,6 +15,7 @@ from endpoints.api.search import ConductRepositorySearch from endpoints.api.superuser import SuperUserRepositoryBuildLogs, SuperUserRepositoryBuildResource from endpoints.api.superuser import SuperUserRepositoryBuildStatus from endpoints.api.appspecifictokens import AppTokens, AppToken +from endpoints.api.manifest import RepositoryManifest from endpoints.api.trigger import BuildTrigger from endpoints.test.shared import client_with_identity, toggle_feature @@ -28,6 +29,7 @@ SEARCH_PARAMS = {'query': ''} NOTIFICATION_PARAMS = {'namespace': 'devtable', 'repository': 'devtable/simple', 'uuid': 'some uuid'} TOKEN_PARAMS = {'token_uuid': 'someuuid'} TRIGGER_PARAMS = {'repository': 'devtable/simple', 'trigger_uuid': 'someuuid'} +MANIFEST_PARAMS = {'repository': 'devtable/simple', 'manifestref': 'sha256:deadbeef'} @pytest.mark.parametrize('resource,method,params,body,identity,expected', [ (AppTokens, 'GET', {}, {}, None, 401), @@ -50,6 +52,11 @@ TRIGGER_PARAMS = {'repository': 'devtable/simple', 'trigger_uuid': 'someuuid'} (AppToken, 'DELETE', TOKEN_PARAMS, {}, 'reader', 404), (AppToken, 'DELETE', TOKEN_PARAMS, {}, 'devtable', 404), + (RepositoryManifest, 'GET', MANIFEST_PARAMS, {}, None, 401), + (RepositoryManifest, 'GET', MANIFEST_PARAMS, {}, 'freshuser', 403), + (RepositoryManifest, 'GET', MANIFEST_PARAMS, {}, 'reader', 403), + (RepositoryManifest, 'GET', MANIFEST_PARAMS, {}, 'devtable', 404), + (OrganizationCollaboratorList, 'GET', ORG_PARAMS, None, None, 401), (OrganizationCollaboratorList, 'GET', ORG_PARAMS, None, 'freshuser', 403), (OrganizationCollaboratorList, 'GET', ORG_PARAMS, None, 'reader', 403), diff --git a/static/css/directives/ui/image-view-layer.css b/static/css/directives/ui/image-view-layer.css index d86d3cc2a..4c1853520 100644 --- a/static/css/directives/ui/image-view-layer.css +++ b/static/css/directives/ui/image-view-layer.css @@ -1,29 +1,7 @@ .image-view-layer-element { position: relative; padding: 10px; - padding-left: 170px; -} - -.image-view-layer-element .image-id { - font-family: monospace; - position: absolute; - top: 10px; - left: 10px; - width: 110px; - text-align: right; -} - -.image-view-layer-element .image-id a { - color: #aaa; -} - -.image-view-layer-element.first .image-id { - font-weight: bold; - font-size: 110%; -} - -.image-view-layer-element.first .image-id a { - color: black; + padding-left: 40px; } .image-view-layer-element .image-comment { @@ -47,7 +25,7 @@ position: absolute; top: 0px; bottom: 0px; - left: 140px; + left: 10px; border-left: 2px solid #428bca; width: 0px; @@ -64,7 +42,7 @@ .image-view-layer-element .image-layer-dot { position: absolute; top: 14px; - left: 135px; + left: 5px; border: 2px solid #428bca; border-radius: 50%; width: 12px; @@ -79,14 +57,6 @@ } @media (max-width: 767px) { - .image-view-layer-element { - margin-left: -140px; - } - - .image-view-layer-element .image-id { - display: none; - } - .image-view-layer-element .dockerfile-command-element .label { position: relative; display: block; diff --git a/static/css/directives/ui/image-feature-view.css b/static/css/directives/ui/manifest-feature-view.css similarity index 50% rename from static/css/directives/ui/image-feature-view.css rename to static/css/directives/ui/manifest-feature-view.css index c2fae532d..b2e17aef3 100644 --- a/static/css/directives/ui/image-feature-view.css +++ b/static/css/directives/ui/manifest-feature-view.css @@ -1,4 +1,4 @@ -.image-feature-view-element .donut-icon { +.manifest-feature-view-element .donut-icon { position: absolute; top: 60px; left: 95px; @@ -8,26 +8,26 @@ margin-left: -6px; } -.image-feature-view-element > .empty { +.manifest-feature-view-element > .empty { margin-top: 20px; } -.image-feature-view-element .no-vulns { +.manifest-feature-view-element .no-vulns { color: #2FC98E; } -.image-feature-view-element .no-vulns i.fa { +.manifest-feature-view-element .no-vulns i.fa { margin-right: 6px; } -.image-feature-view-element .security-header { +.manifest-feature-view-element .security-header { margin-top: -4px; margin-bottom: 30px; padding-bottom: 30px; border-bottom: 1px solid #eee; } -.image-feature-view-element .donut-col { +.manifest-feature-view-element .donut-col { padding-top: 20px; text-align: center; max-width: 250px; @@ -36,75 +36,75 @@ } -.image-feature-view-element #featureDonutChart { +.manifest-feature-view-element #featureDonutChart { display: inline-block; } -.image-feature-view-element .summary-col { +.manifest-feature-view-element .summary-col { font-size: 18px; display: inline-block; vertical-align: top; padding-top: 30px; } -.image-feature-view-element .summary-col .title-item { +.manifest-feature-view-element .summary-col .title-item { font-size: 24px; margin-bottom: 30px; } -.image-feature-view-element .summary-list { +.manifest-feature-view-element .summary-list { text-align: left; list-style: none; } -.image-feature-view-element .summary-list i.fa { +.manifest-feature-view-element .summary-list i.fa { margin-right: 10px; } -.image-feature-view-element .summary-list .package-item strong { +.manifest-feature-view-element .summary-list .package-item strong { text-align: right; width: 40px; display: inline-block; margin-right: 6px; } -.image-feature-view-element .co-table .empty { +.manifest-feature-view-element .co-table .empty { color: #ddd; } -.image-feature-view-element .co-table .single-col { +.manifest-feature-view-element .co-table .single-col { width: 12.5%; } -.image-feature-view-element .co-table .double-col { +.manifest-feature-view-element .co-table .double-col { width: 25%; } -.image-feature-view-element .co-table .impact-col { +.manifest-feature-view-element .co-table .impact-col { text-align: center; width: 130px; } -.image-feature-view-element .co-table .image-col { +.manifest-feature-view-element .co-table .image-col { white-space: nowrap; } -.image-feature-view-element .co-table .image-col .fa { +.manifest-feature-view-element .co-table .image-col .fa { margin-left: 6px; opacity: 0.5; } @media (max-width: 767px) { - .image-feature-view-element .co-table .single-col { + .manifest-feature-view-element .co-table .single-col { width: auto !important; } } -.image-feature-view-element .dockerfile-command { +.manifest-feature-view-element .dockerfile-command { cursor: default; } -.image-feature-view-element .dockerfile-command .command-title { +.manifest-feature-view-element .dockerfile-command .command-title { font-size: 12px; max-width: 297px; overflow: hidden; @@ -114,11 +114,11 @@ vertical-align: middle; } -.image-feature-view-element .vuln-summary i.fa { +.manifest-feature-view-element .vuln-summary i.fa { margin-right: 6px; } -.image-feature-view-element .defcon1 { +.manifest-feature-view-element .defcon1 { background-color: #FB5151; color: white; } diff --git a/static/css/directives/ui/manifest-link.css b/static/css/directives/ui/manifest-link.css index 6996642c1..eaf151933 100644 --- a/static/css/directives/ui/manifest-link.css +++ b/static/css/directives/ui/manifest-link.css @@ -6,7 +6,7 @@ .manifest-link a { font-family: Consolas, "Lucida Console", Monaco, monospace; - font-size: 12px; + font-size: 10px; text-decoration: none; } diff --git a/static/css/directives/ui/image-vulnerability-view.css b/static/css/directives/ui/manifest-vulnerability-view.css similarity index 51% rename from static/css/directives/ui/image-vulnerability-view.css rename to static/css/directives/ui/manifest-vulnerability-view.css index 3e92e1761..07a2b7d80 100644 --- a/static/css/directives/ui/image-vulnerability-view.css +++ b/static/css/directives/ui/manifest-vulnerability-view.css @@ -1,4 +1,4 @@ -.image-vulnerability-view-element .donut-icon { +.manifest-vulnerability-view-element .donut-icon { position: absolute; top: 70px; left: 95px; @@ -8,18 +8,18 @@ margin-left: -6px; } -.image-vulnerability-view-element > .empty { +.manifest-vulnerability-view-element > .empty { margin-top: 20px; } -.image-vulnerability-view-element .security-header { +.manifest-vulnerability-view-element .security-header { margin-top: -4px; margin-bottom: 30px; padding-bottom: 30px; border-bottom: 1px solid #eee; } -.image-vulnerability-view-element .donut-col { +.manifest-vulnerability-view-element .donut-col { padding-top: 20px; text-align: center; max-width: 250px; @@ -27,48 +27,48 @@ vertical-align: top; } -.image-vulnerability-view-element #vulnDonutChart { +.manifest-vulnerability-view-element #vulnDonutChart { display: inline-block; } -.image-vulnerability-view-element .summary-col { +.manifest-vulnerability-view-element .summary-col { font-size: 18px; display: inline-block; vertical-align: top; padding-top: 30px; } -.image-vulnerability-view-element .summary-col .title-item { +.manifest-vulnerability-view-element .summary-col .title-item { font-size: 24px; margin-bottom: 6px; } -.image-vulnerability-view-element .summary-col .subtitle-item { +.manifest-vulnerability-view-element .summary-col .subtitle-item { font-size: 22px; margin-bottom: 6px; } -.image-vulnerability-view-element .summary-list { +.manifest-vulnerability-view-element .summary-list { text-align: left; list-style: none; } -.image-vulnerability-view-element .summary-list i.fa { +.manifest-vulnerability-view-element .summary-list i.fa { margin-right: 10px; } -.image-vulnerability-view-element .summary-list li.severity-item strong { +.manifest-vulnerability-view-element .summary-list li.severity-item strong { text-align: right; width: 40px; display: inline-block; margin-right: 6px; } -.image-vulnerability-view-element .dockerfile-command { +.manifest-vulnerability-view-element .dockerfile-command { cursor: default; } -.image-vulnerability-view-element .dockerfile-command .command-title { +.manifest-vulnerability-view-element .dockerfile-command .command-title { font-size: 12px; max-width: 297px; overflow: hidden; @@ -78,53 +78,53 @@ vertical-align: middle; } -.image-vulnerability-view-element .co-table .empty { +.manifest-vulnerability-view-element .co-table .empty { color: #ddd; } -.image-vulnerability-view-element .co-table .single-col { +.manifest-vulnerability-view-element .co-table .single-col { width: 15%; } -.image-vulnerability-view-element .co-table .double-col { +.manifest-vulnerability-view-element .co-table .double-col { width: 30%; } -.image-vulnerability-view-element .co-table .impact-col { +.manifest-vulnerability-view-element .co-table .impact-col { text-align: center; width: 130px; } -.image-vulnerability-view-element .co-table .nowrap-col { +.manifest-vulnerability-view-element .co-table .nowrap-col { white-space: nowrap; } -.image-vulnerability-view-element .co-table .image-col { +.manifest-vulnerability-view-element .co-table .image-col { white-space: nowrap; } -.image-vulnerability-view-element .co-table .image-col .fa { +.manifest-vulnerability-view-element .co-table .image-col .fa { margin-left: 6px; opacity: 0.5; } @media (max-width: 767px) { - .image-vulnerability-view-element .co-table .single-col { + .manifest-vulnerability-view-element .co-table .single-col { width: auto !important; } } -.image-vulnerability-view-element .fixed-in-version:before { +.manifest-vulnerability-view-element .fixed-in-version:before { font-family: FontAwesome; content: '\f0a9'; margin-right: 6px; } -.image-vulnerability-view-element .fixed-in-version { +.manifest-vulnerability-view-element .fixed-in-version { color: rgb(47, 201, 142); } -.image-vulnerability-view-element .cvss-text { +.manifest-vulnerability-view-element .cvss-text { display: inline-block; width: 40px; text-align: right; @@ -135,11 +135,11 @@ color: #ccc; } -.image-vulnerability-view-element .vulnerability-priority-view { +.manifest-vulnerability-view-element .vulnerability-priority-view { margin-left: 10px; } -.image-vulnerability-view-element .cvss { +.manifest-vulnerability-view-element .cvss { display: inline-block; width: 100px; height: 10px; @@ -148,7 +148,7 @@ position: relative;; } -.image-vulnerability-view-element .cvss span { +.manifest-vulnerability-view-element .cvss span { display: inline-block; position: absolute; top: 0px; @@ -156,12 +156,12 @@ bottom: 0px; } -.image-vulnerability-view-element .expansion-col { +.manifest-vulnerability-view-element .expansion-col { padding-top: 20px; padding-bottom: 20px; } -.image-vulnerability-view-element .subtitle { +.manifest-vulnerability-view-element .subtitle { color: #999; font-size: 90%; text-transform: uppercase; @@ -172,37 +172,37 @@ } -.image-vulnerability-view-element .expand-link { +.manifest-vulnerability-view-element .expand-link { color: black !important; } -.image-vulnerability-view-element .external-link { +.manifest-vulnerability-view-element .external-link { margin-left: 10px; font-size: 12px; } -.image-vulnerability-view-element .description { +.manifest-vulnerability-view-element .description { display: inline-block; max-width: 1000px; } -.image-vulnerability-view-element .asterisk { +.manifest-vulnerability-view-element .asterisk { vertical-align: super; font-size: 9px; margin-left: 2px; } -.image-vulnerability-view-element .severity-note { +.manifest-vulnerability-view-element .severity-note { margin-bottom: 10px; } -.image-vulnerability-view-element .severity-note .vulnerability-priority-view { +.manifest-vulnerability-view-element .severity-note .vulnerability-priority-view { margin: 0px; margin-left: 2px; margin-right: 2px; } -.image-vulnerability-view-element .defcon1 { +.manifest-vulnerability-view-element .defcon1 { background-color: #FB5151; color: white; } \ No newline at end of file diff --git a/static/css/pages/image-view.css b/static/css/pages/manifest-view.css similarity index 54% rename from static/css/pages/image-view.css rename to static/css/pages/manifest-view.css index a1cb55bab..8bc5f41c1 100644 --- a/static/css/pages/image-view.css +++ b/static/css/pages/manifest-view.css @@ -1,70 +1,70 @@ -.image-view .image-view-header { +.manifest-view .manifest-view-header { padding: 10px; background: #e8f1f6; margin: -10px; margin-bottom: 20px; } -.image-view .image-view-header .section-icon { +.manifest-view .manifest-view-header .section-icon { margin-right: 6px; } -.image-view .image-view-header .section { +.manifest-view .manifest-view-header .section { padding: 4px; display: inline-block; margin-right: 20px; } -.image-view .co-tab-content { +.manifest-view .co-tab-content { padding: 20px; padding-top: 10px; } -.image-view .co-tab-content h3 { +.manifest-view .co-tab-content h3 { margin-bottom: 30px; } -.image-view .fa-bug { +.manifest-view .fa-bug { margin-right: 4px; } -.image-view .co-filter-box { +.manifest-view .co-filter-box { float: right; min-width: 300px; margin-bottom: 10px; } -.image-view .co-filter-box .current-filtered { +.manifest-view .co-filter-box .current-filtered { display: inline-block; margin-right: 10px; color: #999; } -.image-view .co-filter-box input { +.manifest-view .co-filter-box input { display: inline-block; } -.image-view .level-col h4 { +.manifest-view .level-col h4 { margin-top: 0px; margin-bottom: 20px; } -.image-view .levels { +.manifest-view .levels { list-style: none; padding: 0px; margin: 0px; } -.image-view .levels li { +.manifest-view .levels li { margin-bottom: 20px; } -.image-view .levels li .description { +.manifest-view .levels li .description { margin-top: 6px; font-size: 14px; color: #999; } -.image-view .level-col { +.manifest-view .level-col { padding: 20px; } \ No newline at end of file diff --git a/static/directives/image-view-layer.html b/static/directives/image-view-layer.html index 23369587e..47376f320 100644 --- a/static/directives/image-view-layer.html +++ b/static/directives/image-view-layer.html @@ -1,10 +1,5 @@
- -
+
diff --git a/static/directives/image-feature-view.html b/static/directives/manifest-feature-view.html similarity index 93% rename from static/directives/image-feature-view.html rename to static/directives/manifest-feature-view.html index b4faae903..8065accea 100644 --- a/static/directives/image-feature-view.html +++ b/static/directives/manifest-feature-view.html @@ -1,4 +1,4 @@ -
+
@@ -15,7 +15,7 @@
-
This image has not been indexed yet
+
This manifest has not been indexed yet
Please try again in a few minutes.
@@ -26,9 +26,9 @@
-
This image could not be indexed
+
This manifest could not be indexed
- Quay security scanner was unable to index this image. + Quay security scanner was unable to index this manifest.
@@ -38,9 +38,9 @@
-
Image is not supported by Quay Security Scanner
+
Manifest is not supported by Quay Security Scanner
- This image has an operating system or package manager unsupported by Quay Security Scanner. + This manifest has an operating system or package manager unsupported by Quay Security Scanner.
@@ -79,7 +79,7 @@ -

Image Packages

+

Packages

diff --git a/static/directives/image-vulnerability-view.html b/static/directives/manifest-vulnerability-view.html similarity index 93% rename from static/directives/image-vulnerability-view.html rename to static/directives/manifest-vulnerability-view.html index 1e1977cfa..20b34c620 100644 --- a/static/directives/image-vulnerability-view.html +++ b/static/directives/manifest-vulnerability-view.html @@ -1,4 +1,4 @@ -
+
@@ -15,7 +15,7 @@
-
This image has not been indexed yet
+
This manifest has not been indexed yet
Please try again in a few minutes.
@@ -26,9 +26,9 @@
-
This image could not be indexed
+
This manifest could not be indexed
- Quay security scanner was unable to index this image. + Quay security scanner was unable to index this manifest.
@@ -38,9 +38,9 @@
-
Image is not supported by Quay Security Scanner
+
Manifest is not supported by Quay Security Scanner
- This image has an operating system or package manager unsupported by Quay Security Scanner. + This manifest has an operating system or package manager unsupported by Quay Security Scanner.
@@ -72,7 +72,7 @@
- Quay Security Scanner has detected no vulnerabilities in this image. + Quay Security Scanner has detected no vulnerabilities in this manifest.
@@ -87,13 +87,13 @@ -

Image Vulnerabilities

+

Vulnerabilities

No vulnerabilities found.
-
Quay Security Scanner has detected no vulnerabilities in this image.
+
Quay Security Scanner has detected no vulnerabilities in this manifest.
@@ -111,7 +111,7 @@ - + diff --git a/static/directives/repo-view/repo-panel-tags.html b/static/directives/repo-view/repo-panel-tags.html index 32c485386..081e801eb 100644 --- a/static/directives/repo-view/repo-panel-tags.html +++ b/static/directives/repo-view/repo-panel-tags.html @@ -161,6 +161,14 @@ + + + + Unsupported + + - + Passed @@ -204,7 +212,7 @@ ng-class="getTagVulnerabilities(tag).highestVulnerability.Priority" class="has-vulns" bindonce> - @@ -218,7 +226,7 @@ · - + {{ getTagVulnerabilities(tag).vulnerabilitiesInfo.fixable.length }} fixable diff --git a/static/js/directives/repo-view/repo-panel-tags.js b/static/js/directives/repo-view/repo-panel-tags.js index 73dd48d99..d9182eaaa 100644 --- a/static/js/directives/repo-view/repo-panel-tags.js +++ b/static/js/directives/repo-view/repo-panel-tags.js @@ -262,6 +262,10 @@ angular.module('quay').directive('repoPanelTags', function () { }; $scope.getTagVulnerabilities = function(tag) { + if (!tag.manifest_digest) { + return 'nodigest'; + } + return $scope.getImageVulnerabilities(tag.image_id); }; diff --git a/static/js/directives/ui/image-feature-view.js b/static/js/directives/ui/manifest-feature-view.js similarity index 75% rename from static/js/directives/ui/image-feature-view.js rename to static/js/directives/ui/manifest-feature-view.js index f9bda9624..1fc52e438 100644 --- a/static/js/directives/ui/image-feature-view.js +++ b/static/js/directives/ui/manifest-feature-view.js @@ -1,16 +1,16 @@ /** - * An element which displays the features of an image. + * An element which displays the features of a manifest. */ -angular.module('quay').directive('imageFeatureView', function () { +angular.module('quay').directive('manifestFeatureView', function () { var directiveDefinitionObject = { priority: 0, - templateUrl: '/static/directives/image-feature-view.html', + templateUrl: '/static/directives/manifest-feature-view.html', replace: false, transclude: true, restrict: 'C', scope: { 'repository': '=repository', - 'image': '=image', + 'manifest': '=manifest', 'isEnabled': '=isEnabled' }, controller: function($scope, $element, Config, ApiService, VulnerabilityService, ViewArray, TableService) { @@ -64,15 +64,15 @@ angular.module('quay').directive('imageFeatureView', function () { }); }; - var loadImageVulnerabilities = function() { + var loadManifestVulnerabilities = function() { if ($scope.loading) { return; } $scope.loading = true; - VulnerabilityService.loadImageVulnerabilities($scope.repository, $scope.image.id, function(resp) { + VulnerabilityService.loadManifestVulnerabilities($scope.repository, $scope.manifest.digest, function(resp) { $scope.securityStatus = resp.status; - $scope.featuresInfo = VulnerabilityService.buildFeaturesInfo($scope.image, resp); + $scope.featuresInfo = VulnerabilityService.buildFeaturesInfo($scope.manifest.image, resp); buildOrderedFeatures(); buildChart(); @@ -87,20 +87,20 @@ angular.module('quay').directive('imageFeatureView', function () { $scope.$watch('options.filter', buildOrderedFeatures); $scope.$watch('repository', function(repository) { - if ($scope.isEnabled && $scope.repository && $scope.image) { - loadImageVulnerabilities(); + if ($scope.isEnabled && $scope.repository && $scope.manifest) { + loadManifestVulnerabilities(); } }); - $scope.$watch('image', function(image) { - if ($scope.isEnabled && $scope.repository && $scope.image) { - loadImageVulnerabilities(); + $scope.$watch('manifest', function(manifest) { + if ($scope.isEnabled && $scope.repository && $scope.manifest) { + loadManifestVulnerabilities(); } }); $scope.$watch('isEnabled', function(isEnabled) { - if ($scope.isEnabled && $scope.repository && $scope.image) { - loadImageVulnerabilities(); + if ($scope.isEnabled && $scope.repository && $scope.manifest) { + loadManifestVulnerabilities(); } }); } diff --git a/static/js/directives/ui/manifest-link/manifest-link.component.html b/static/js/directives/ui/manifest-link/manifest-link.component.html index 1b9425aee..af5400a87 100644 --- a/static/js/directives/ui/manifest-link/manifest-link.component.html +++ b/static/js/directives/ui/manifest-link/manifest-link.component.html @@ -1,22 +1,22 @@ - V1ID - SHA256 - - {{ $ctrl.imageId.substr(0, 12) }} - {{ $ctrl.getShortDigest($ctrl.manifestDigest) }} + + {{ $ctrl.getShortDigest($ctrl.manifestDigest) }} + {{ ::$ctrl.imageId.substr(0, 12) }} +