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 @@
-
- - {{ image.id.substr(0, 12) }} - -
-
+
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) }} +