diff --git a/endpoints/api/__init__.py b/endpoints/api/__init__.py index 3d1b6782c..a2f3f5793 100644 --- a/endpoints/api/__init__.py +++ b/endpoints/api/__init__.py @@ -503,7 +503,6 @@ import endpoints.api.build import endpoints.api.discovery import endpoints.api.error import endpoints.api.globalmessages -import endpoints.api.image import endpoints.api.logs import endpoints.api.manifest import endpoints.api.organization diff --git a/endpoints/api/image.py b/endpoints/api/image.py deleted file mode 100644 index 41c6f7f2d..000000000 --- a/endpoints/api/image.py +++ /dev/null @@ -1,120 +0,0 @@ -""" -List and lookup repository images. -""" -import json - -from collections import defaultdict -from datetime import datetime - -from app import storage -from data.registry_model import registry_model -from endpoints.api import ( - resource, - nickname, - require_repo_read, - RepositoryParamResource, - path_param, - disallow_for_app_repositories, - format_date, - deprecated, -) -from endpoints.exception import NotFound - - -def image_dict(image): - parsed_command = None - if image.command: - try: - parsed_command = json.loads(image.command) - except (ValueError, TypeError): - parsed_command = {"error": "Could not parse command"} - - image_data = { - "id": image.docker_image_id, - "created": format_date(image.created), - "comment": image.comment, - "command": parsed_command, - "size": image.image_size, - "uploading": False, - "sort_index": 0, - } - - image_data["ancestors"] = "/{0}/".format("/".join(image.ancestor_ids)) - return image_data - - -@resource("/v1/repository//image/") -@path_param("repository", "The full path of the repository. e.g. namespace/name") -class RepositoryImageList(RepositoryParamResource): - """ - Resource for listing repository images. - """ - - @require_repo_read - @nickname("listRepositoryImages") - @disallow_for_app_repositories - @deprecated() - def get(self, namespace, repository): - """ - List the images for the specified repository. - """ - repo_ref = registry_model.lookup_repository(namespace, repository) - if repo_ref is None: - raise NotFound() - - tags = registry_model.list_all_active_repository_tags(repo_ref) - images_with_tags = defaultdict(list) - for tag in tags: - legacy_image_id = tag.manifest.legacy_image_root_id - if legacy_image_id is not None: - images_with_tags[legacy_image_id].append(tag) - - # NOTE: This is replicating our older response for this endpoint, but - # returns empty for the metadata fields. This is to ensure back-compat - # for callers still using the deprecated API, while not having to load - # all the manifests from storage. - return { - "images": [ - { - "id": image_id, - "created": format_date( - datetime.utcfromtimestamp((min([tag.lifetime_start_ts for tag in tags]))) - ), - "comment": "", - "command": "", - "size": 0, - "uploading": False, - "sort_index": 0, - "tags": [tag.name for tag in tags], - "ancestors": "", - } - for image_id, tags in images_with_tags.items() - ] - } - - -@resource("/v1/repository//image/") -@path_param("repository", "The full path of the repository. e.g. namespace/name") -@path_param("image_id", "The Docker image ID") -class RepositoryImage(RepositoryParamResource): - """ - Resource for handling repository images. - """ - - @require_repo_read - @nickname("getImage") - @disallow_for_app_repositories - @deprecated() - def get(self, namespace, repository, image_id): - """ - Get the information available for the specified image. - """ - repo_ref = registry_model.lookup_repository(namespace, repository) - if repo_ref is None: - raise NotFound() - - image = registry_model.get_legacy_image(repo_ref, image_id, storage) - if image is None: - raise NotFound() - - return image_dict(image) diff --git a/endpoints/api/manifest.py b/endpoints/api/manifest.py index ed3c256d8..6751a1c78 100644 --- a/endpoints/api/manifest.py +++ b/endpoints/api/manifest.py @@ -29,7 +29,6 @@ from endpoints.api import ( format_date, disallow_for_non_normal_repositories, ) -from endpoints.api.image import image_dict from endpoints.exception import NotFound from util.validation import VALID_LABEL_KEY_REGEX @@ -83,22 +82,6 @@ def _manifest_dict(manifest): logger.debug("Missing layers for manifest `%s`", manifest.digest) abort(404) - image = None - if manifest.legacy_image_root_id: - # NOTE: This is replicating our older response for this endpoint, but - # returns empty for the metadata fields. This is to ensure back-compat - # for callers still using the deprecated API. - image = { - "id": manifest.legacy_image_root_id, - "created": format_date(datetime.utcnow()), - "comment": "", - "command": "", - "size": 0, - "uploading": False, - "sort_index": 0, - "ancestors": "", - } - return { "digest": manifest.digest, "is_manifest_list": manifest.is_manifest_list, @@ -107,7 +90,6 @@ def _manifest_dict(manifest): "layers": ( [_layer_dict(lyr.layer_info, idx) for idx, lyr in enumerate(layers)] if layers else None ), - "image": image, } diff --git a/endpoints/api/repository_models_interface.py b/endpoints/api/repository_models_interface.py index f8c20ec1e..ca54f09ca 100644 --- a/endpoints/api/repository_models_interface.py +++ b/endpoints/api/repository_models_interface.py @@ -201,7 +201,6 @@ class Tag( "Tag", [ "name", - "image_docker_image_id", "image_aggregate_size", "lifetime_start_ts", "tag_manifest_digest", @@ -211,7 +210,6 @@ class Tag( ): """ :type name: string - :type image_docker_image_id: string :type image_aggregate_size: int :type lifetime_start_ts: int :type lifetime_end_ts: int|None @@ -222,7 +220,6 @@ class Tag( def to_dict(self): tag_info = { "name": self.name, - "image_id": self.image_docker_image_id, "size": self.image_aggregate_size, } diff --git a/endpoints/api/repository_models_pre_oci.py b/endpoints/api/repository_models_pre_oci.py index 2e7c73f33..321673c61 100644 --- a/endpoints/api/repository_models_pre_oci.py +++ b/endpoints/api/repository_models_pre_oci.py @@ -268,7 +268,6 @@ class PreOCIModel(RepositoryDataInterface): tags = [ Tag( tag.name, - tag.manifest.legacy_image_root_id, tag.manifest_layers_size, tag.lifetime_start_ts, tag.manifest_digest, diff --git a/endpoints/api/secscan.py b/endpoints/api/secscan.py index e986a18ba..65aed1d50 100644 --- a/endpoints/api/secscan.py +++ b/endpoints/api/secscan.py @@ -75,42 +75,6 @@ def _security_info(manifest_or_legacy_image, include_vulnerabilities=True): } -@resource("/v1/repository//image//security") -@show_if(features.SECURITY_SCANNER) -@path_param("repository", "The full path of the repository. e.g. namespace/name") -@path_param("imageid", "The image ID") -class RepositoryImageSecurity(RepositoryParamResource): - """ - Operations for managing the vulnerabilities in a repository image. - - DEPRECATED: Please retrieve security by manifest . - """ - - @process_basic_auth_no_pass - @anon_allowed - @require_repo_read - @nickname("getRepoImageSecurity") - @deprecated() - @disallow_for_app_repositories - @parse_args() - @query_param( - "vulnerabilities", "Include vulnerabilities information", type=truthy_bool, default=False - ) - def get(self, namespace, repository, imageid, parsed_args): - """ - Fetches the features and vulnerabilities (if any) for a repository image. - """ - repo_ref = registry_model.lookup_repository(namespace, repository) - if repo_ref is None: - raise NotFound() - - legacy_image = registry_model.get_legacy_image(repo_ref, imageid, storage) - if legacy_image is None: - raise NotFound() - - return _security_info(legacy_image, parsed_args.vulnerabilities) - - @resource(MANIFEST_DIGEST_ROUTE + "/security") @show_if(features.SECURITY_SCANNER) @path_param("repository", "The full path of the repository. e.g. namespace/name") diff --git a/endpoints/api/tag.py b/endpoints/api/tag.py index 314ab6f4b..d0fc4fb6f 100644 --- a/endpoints/api/tag.py +++ b/endpoints/api/tag.py @@ -23,7 +23,6 @@ from endpoints.api import ( format_date, disallow_for_non_normal_repositories, ) -from endpoints.api.image import image_dict from endpoints.exception import NotFound, InvalidRequest from util.names import TAG_ERROR, TAG_REGEX from util.parsing import truthy_bool @@ -44,8 +43,6 @@ def _tag_dict(tag): tag_info["manifest_digest"] = tag.manifest_digest tag_info["is_manifest_list"] = tag.manifest.is_manifest_list tag_info["size"] = tag.manifest_layers_size - tag_info["docker_image_id"] = tag.manifest.legacy_image_root_id - tag_info["image_id"] = tag.manifest.legacy_image_root_id if tag.lifetime_start_ts and tag.lifetime_start_ts > 0: last_modified = format_date(datetime.utcfromtimestamp(tag.lifetime_start_ts)) @@ -112,10 +109,6 @@ class RepositoryTag(RepositoryParamResource): "type": "object", "description": "Makes changes to a specific tag", "properties": { - "image": { - "type": ["string", "null"], - "description": "(Deprecated: Use `manifest_digest`) Image to which the tag should point.", - }, "manifest_digest": { "type": ["string", "null"], "description": "(If specified) The manifest digest to which the tag should point", @@ -181,23 +174,17 @@ class RepositoryTag(RepositoryParamResource): else: raise InvalidRequest("Could not update tag expiration; Tag has probably changed") - if "image" in request.get_json() or "manifest_digest" in request.get_json(): + if "manifest_digest" in request.get_json(): existing_tag = registry_model.get_repo_tag(repo_ref, tag) - manifest_or_image = None - image_id = None manifest_digest = None - if "manifest_digest" in request.get_json(): - manifest_digest = request.get_json()["manifest_digest"] - manifest_or_image = registry_model.lookup_manifest_by_digest( - repo_ref, manifest_digest, require_available=True - ) - else: - image_id = request.get_json()["image"] - manifest_or_image = registry_model.get_legacy_image(repo_ref, image_id, storage) + manifest_digest = request.get_json()["manifest_digest"] + manifest = registry_model.lookup_manifest_by_digest( + repo_ref, manifest_digest, require_available=True + ) - if manifest_or_image is None: + if manifest is None: raise NotFound() existing_manifest = ( @@ -206,7 +193,7 @@ class RepositoryTag(RepositoryParamResource): existing_manifest_digest = existing_manifest.digest if existing_manifest else None if not registry_model.retarget_tag( - repo_ref, tag, manifest_or_image, storage, docker_v2_signing_key + repo_ref, tag, manifest, storage, docker_v2_signing_key ): raise InvalidRequest("Could not move tag") @@ -220,7 +207,6 @@ class RepositoryTag(RepositoryParamResource): "repo": repository, "tag": tag, "namespace": namespace, - "image": image_id, "manifest_digest": manifest_digest, "original_manifest_digest": existing_manifest_digest, }, @@ -254,72 +240,6 @@ class RepositoryTag(RepositoryParamResource): return "", 204 -@resource("/v1/repository//tag//images") -@path_param("repository", "The full path of the repository. e.g. namespace/name") -@path_param("tag", "The name of the tag") -class RepositoryTagImages(RepositoryParamResource): - """ - Resource for listing the images in a specific repository tag. - """ - - @require_repo_read - @nickname("listTagImages") - @disallow_for_app_repositories - @parse_args() - @deprecated() - @query_param( - "owned", - "If specified, only images wholely owned by this tag are returned.", - type=truthy_bool, - default=False, - ) - def get(self, namespace, repository, tag, parsed_args): - """ - List the images for the specified repository tag. - """ - repo_ref = registry_model.lookup_repository(namespace, repository) - if repo_ref is None: - raise NotFound() - - tag_ref = registry_model.get_repo_tag(repo_ref, tag) - if tag_ref is None: - raise NotFound() - - if parsed_args["owned"]: - # NOTE: This is deprecated, so we just return empty now. - return {"images": []} - - manifest = registry_model.get_manifest_for_tag(tag_ref) - if manifest is None: - raise NotFound() - - legacy_image = registry_model.get_legacy_image( - repo_ref, manifest.legacy_image_root_id, storage - ) - if legacy_image is None: - raise NotFound() - - # NOTE: This is replicating our older response for this endpoint, but - # returns empty for the metadata fields. This is to ensure back-compat - # for callers still using the deprecated API, while not having to load - # all the manifests from storage. - return { - "images": [ - { - "id": image_id, - "created": format_date(datetime.utcfromtimestamp(tag_ref.lifetime_start_ts)), - "comment": "", - "command": "", - "size": 0, - "uploading": False, - "sort_index": 0, - "ancestors": "", - } - for image_id in legacy_image.full_image_id_chain - ] - } - - @resource("/v1/repository//tag//restore") @path_param("repository", "The full path of the repository. e.g. namespace/name") @path_param("tag", "The name of the tag") @@ -333,10 +253,6 @@ class RestoreTag(RepositoryParamResource): "type": "object", "description": "Restores a tag to a specific image", "properties": { - "image": { - "type": "string", - "description": "(Deprecated: use `manifest_digest`) Image to which the tag should point", - }, "manifest_digest": { "type": "string", "description": "If specified, the manifest digest that should be used", @@ -359,10 +275,9 @@ class RestoreTag(RepositoryParamResource): raise NotFound() # Restore the tag back to the previous image. - image_id = request.get_json().get("image", None) manifest_digest = request.get_json().get("manifest_digest", None) - if image_id is None and manifest_digest is None: + if manifest_digest is None: raise InvalidRequest("Missing manifest_digest") # Data for logging the reversion/restoration. @@ -371,25 +286,20 @@ class RestoreTag(RepositoryParamResource): "username": username, "repo": repository, "tag": tag, - "image": image_id, "manifest_digest": manifest_digest, } - manifest_or_legacy_image = None - if manifest_digest is not None: - manifest_or_legacy_image = registry_model.lookup_manifest_by_digest( - repo_ref, manifest_digest, allow_dead=True, require_available=True - ) - elif image_id is not None: - manifest_or_legacy_image = registry_model.get_legacy_image(repo_ref, image_id, storage) + manifest = registry_model.lookup_manifest_by_digest( + repo_ref, manifest_digest, allow_dead=True, require_available=True + ) - if manifest_or_legacy_image is None: + if manifest is None: raise NotFound() if not registry_model.retarget_tag( repo_ref, tag, - manifest_or_legacy_image, + manifest, storage, docker_v2_signing_key, is_reversion=True, diff --git a/endpoints/api/test/test_deprecated_route.py b/endpoints/api/test/test_deprecated_route.py deleted file mode 100644 index 361433021..000000000 --- a/endpoints/api/test/test_deprecated_route.py +++ /dev/null @@ -1,26 +0,0 @@ -import pytest - -from data import model -from data.model.oci import shared -from data.registry_model import registry_model -from endpoints.api.secscan import RepositoryImageSecurity -from endpoints.api.test.shared import conduct_api_call -from endpoints.test.shared import client_with_identity -from test.fixtures import * - - -def test_deprecated_route(client): - repository_ref = registry_model.lookup_repository("devtable", "simple") - tag = registry_model.get_repo_tag(repository_ref, "latest") - manifest = registry_model.get_manifest_for_tag(tag) - - with client_with_identity("devtable", client) as cl: - resp = conduct_api_call( - cl, - RepositoryImageSecurity, - "get", - {"repository": "devtable/simple", "imageid": manifest.legacy_image_root_id}, - expected_code=200, - ) - - assert resp.headers["Deprecation"] == "true" diff --git a/endpoints/api/test/test_disallow_for_apps.py b/endpoints/api/test/test_disallow_for_apps.py index a26103f3e..469d3e1f2 100644 --- a/endpoints/api/test/test_disallow_for_apps.py +++ b/endpoints/api/test/test_disallow_for_apps.py @@ -8,16 +8,15 @@ from endpoints.api.build import ( RepositoryBuildStatus, RepositoryBuildLogs, ) -from endpoints.api.image import RepositoryImageList, RepositoryImage from endpoints.api.manifest import RepositoryManifestLabels, ManageRepositoryManifestLabel from endpoints.api.repositorynotification import ( RepositoryNotification, RepositoryNotificationList, TestRepositoryNotification, ) -from endpoints.api.secscan import RepositoryImageSecurity, RepositoryManifestSecurity +from endpoints.api.secscan import RepositoryManifestSecurity from endpoints.api.signing import RepositorySignatures -from endpoints.api.tag import ListRepositoryTags, RepositoryTag, RepositoryTagImages, RestoreTag +from endpoints.api.tag import ListRepositoryTags, RepositoryTag, RestoreTag from endpoints.api.trigger import ( BuildTriggerList, BuildTrigger, @@ -53,8 +52,6 @@ FIELD_ARGS = {"trigger_uuid": "1234", "field_name": "foobar"} (RepositoryBuildResource, "delete", BUILD_ARGS), (RepositoryBuildStatus, "get", BUILD_ARGS), (RepositoryBuildLogs, "get", BUILD_ARGS), - (RepositoryImageList, "get", None), - (RepositoryImage, "get", IMAGE_ARGS), (RepositoryManifestLabels, "get", MANIFEST_ARGS), (RepositoryManifestLabels, "post", MANIFEST_ARGS), (ManageRepositoryManifestLabel, "get", LABEL_ARGS), @@ -65,13 +62,11 @@ FIELD_ARGS = {"trigger_uuid": "1234", "field_name": "foobar"} (RepositoryNotification, "delete", NOTIFICATION_ARGS), (RepositoryNotification, "post", NOTIFICATION_ARGS), (TestRepositoryNotification, "post", NOTIFICATION_ARGS), - (RepositoryImageSecurity, "get", IMAGE_ARGS), (RepositoryManifestSecurity, "get", MANIFEST_ARGS), (RepositorySignatures, "get", None), (ListRepositoryTags, "get", None), (RepositoryTag, "put", TAG_ARGS), (RepositoryTag, "delete", TAG_ARGS), - (RepositoryTagImages, "get", TAG_ARGS), (RestoreTag, "post", TAG_ARGS), (BuildTriggerList, "get", None), (BuildTrigger, "get", TRIGGER_ARGS), diff --git a/endpoints/api/test/test_manifest.py b/endpoints/api/test/test_manifest.py index 85e19f929..f62b93ad5 100644 --- a/endpoints/api/test/test_manifest.py +++ b/endpoints/api/test/test_manifest.py @@ -22,4 +22,3 @@ def test_repository_manifest(client): result = conduct_api_call(cl, RepositoryManifest, "GET", params, None, 200).json assert result["digest"] == manifest_digest assert result["manifest_data"] - assert result["image"] diff --git a/endpoints/api/test/test_secscan.py b/endpoints/api/test/test_secscan.py index 55904c14e..6a4e21704 100644 --- a/endpoints/api/test/test_secscan.py +++ b/endpoints/api/test/test_secscan.py @@ -6,7 +6,7 @@ from mock import patch from data.registry_model import registry_model from endpoints.test.shared import gen_basic_auth from endpoints.api.test.shared import conduct_api_call -from endpoints.api.secscan import RepositoryImageSecurity, RepositoryManifestSecurity +from endpoints.api.secscan import RepositoryManifestSecurity from test.fixtures import * @@ -14,14 +14,10 @@ from test.fixtures import * @pytest.mark.parametrize( "endpoint, anonymous_allowed, auth_headers, expected_code", [ - pytest.param(RepositoryImageSecurity, True, gen_basic_auth("devtable", "password"), 200), - pytest.param(RepositoryImageSecurity, False, gen_basic_auth("devtable", "password"), 200), pytest.param(RepositoryManifestSecurity, True, gen_basic_auth("devtable", "password"), 200), pytest.param( RepositoryManifestSecurity, False, gen_basic_auth("devtable", "password"), 200 ), - pytest.param(RepositoryImageSecurity, True, None, 401), - pytest.param(RepositoryImageSecurity, False, None, 401), pytest.param(RepositoryManifestSecurity, True, None, 401), pytest.param(RepositoryManifestSecurity, False, None, 401), ], @@ -36,7 +32,6 @@ def test_get_security_info_with_pull_secret( params = { "repository": "devtable/simple", - "imageid": tag.manifest.legacy_image_root_id, "manifestref": manifest.digest, } diff --git a/endpoints/api/test/test_security.py b/endpoints/api/test/test_security.py index 494781f09..5c904d8b5 100644 --- a/endpoints/api/test/test_security.py +++ b/endpoints/api/test/test_security.py @@ -14,7 +14,6 @@ from endpoints.api.billing import * from endpoints.api.build import * from endpoints.api.discovery import * from endpoints.api.globalmessages import * -from endpoints.api.image import * from endpoints.api.logs import * from endpoints.api.manifest import * from endpoints.api.organization import * @@ -2648,95 +2647,6 @@ SECURITY_TESTS: List[ "reader", 200, ), - ( - RepositoryTagImages, - "GET", - {"tag": "TN96", "repository": "public/publicrepo"}, - None, - None, - 404, - ), - ( - RepositoryTagImages, - "GET", - {"tag": "TN96", "repository": "public/publicrepo"}, - None, - "devtable", - 404, - ), - ( - RepositoryTagImages, - "GET", - {"tag": "TN96", "repository": "public/publicrepo"}, - None, - "freshuser", - 404, - ), - ( - RepositoryTagImages, - "GET", - {"tag": "TN96", "repository": "public/publicrepo"}, - None, - "reader", - 404, - ), - (RepositoryTagImages, "GET", {"tag": "TN96", "repository": "devtable/shared"}, None, None, 401), - ( - RepositoryTagImages, - "GET", - {"tag": "TN96", "repository": "devtable/shared"}, - None, - "devtable", - 404, - ), - ( - RepositoryTagImages, - "GET", - {"tag": "TN96", "repository": "devtable/shared"}, - None, - "freshuser", - 403, - ), - ( - RepositoryTagImages, - "GET", - {"tag": "TN96", "repository": "devtable/shared"}, - None, - "reader", - 404, - ), - ( - RepositoryTagImages, - "GET", - {"tag": "TN96", "repository": "buynlarge/orgrepo"}, - None, - None, - 401, - ), - ( - RepositoryTagImages, - "GET", - {"tag": "TN96", "repository": "buynlarge/orgrepo"}, - None, - "devtable", - 404, - ), - ( - RepositoryTagImages, - "GET", - {"tag": "TN96", "repository": "buynlarge/orgrepo"}, - None, - "freshuser", - 403, - ), - ( - RepositoryTagImages, - "GET", - {"tag": "TN96", "repository": "buynlarge/orgrepo"}, - None, - "reader", - 404, - ), ( PermissionPrototype, "DELETE", @@ -3743,107 +3653,11 @@ SECURITY_TESTS: List[ "reader", 403, ), - ( - RepositoryImage, - "GET", - {"image_id": "5AVQ", "repository": "public/publicrepo"}, - None, - None, - 404, - ), - ( - RepositoryImage, - "GET", - {"image_id": "5AVQ", "repository": "public/publicrepo"}, - None, - "devtable", - 404, - ), - ( - RepositoryImage, - "GET", - {"image_id": "5AVQ", "repository": "public/publicrepo"}, - None, - "freshuser", - 404, - ), - ( - RepositoryImage, - "GET", - {"image_id": "5AVQ", "repository": "public/publicrepo"}, - None, - "reader", - 404, - ), - ( - RepositoryImage, - "GET", - {"image_id": "5AVQ", "repository": "devtable/shared"}, - None, - None, - 401, - ), - ( - RepositoryImage, - "GET", - {"image_id": "5AVQ", "repository": "devtable/shared"}, - None, - "devtable", - 404, - ), - ( - RepositoryImage, - "GET", - {"image_id": "5AVQ", "repository": "devtable/shared"}, - None, - "freshuser", - 403, - ), - ( - RepositoryImage, - "GET", - {"image_id": "5AVQ", "repository": "devtable/shared"}, - None, - "reader", - 404, - ), - ( - RepositoryImage, - "GET", - {"image_id": "5AVQ", "repository": "buynlarge/orgrepo"}, - None, - None, - 401, - ), - ( - RepositoryImage, - "GET", - {"image_id": "5AVQ", "repository": "buynlarge/orgrepo"}, - None, - "devtable", - 404, - ), - ( - RepositoryImage, - "GET", - {"image_id": "5AVQ", "repository": "buynlarge/orgrepo"}, - None, - "freshuser", - 403, - ), - ( - RepositoryImage, - "GET", - {"image_id": "5AVQ", "repository": "buynlarge/orgrepo"}, - None, - "reader", - 404, - ), ( RestoreTag, "POST", {"tag": "HP8R", "repository": "public/publicrepo"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, None, 401, ), @@ -3851,7 +3665,7 @@ SECURITY_TESTS: List[ RestoreTag, "POST", {"tag": "HP8R", "repository": "public/publicrepo"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, "devtable", 403, ), @@ -3859,7 +3673,7 @@ SECURITY_TESTS: List[ RestoreTag, "POST", {"tag": "HP8R", "repository": "public/publicrepo"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, "freshuser", 403, ), @@ -3867,7 +3681,7 @@ SECURITY_TESTS: List[ RestoreTag, "POST", {"tag": "HP8R", "repository": "public/publicrepo"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, "reader", 403, ), @@ -3875,7 +3689,7 @@ SECURITY_TESTS: List[ RestoreTag, "POST", {"tag": "HP8R", "repository": "devtable/shared"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, None, 401, ), @@ -3883,7 +3697,7 @@ SECURITY_TESTS: List[ RestoreTag, "POST", {"tag": "HP8R", "repository": "devtable/shared"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, "devtable", 404, ), @@ -3891,7 +3705,7 @@ SECURITY_TESTS: List[ RestoreTag, "POST", {"tag": "HP8R", "repository": "devtable/shared"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, "freshuser", 403, ), @@ -3899,7 +3713,7 @@ SECURITY_TESTS: List[ RestoreTag, "POST", {"tag": "HP8R", "repository": "devtable/shared"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, "reader", 403, ), @@ -3907,7 +3721,7 @@ SECURITY_TESTS: List[ RestoreTag, "POST", {"tag": "HP8R", "repository": "buynlarge/orgrepo"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, None, 401, ), @@ -3915,7 +3729,7 @@ SECURITY_TESTS: List[ RestoreTag, "POST", {"tag": "HP8R", "repository": "buynlarge/orgrepo"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, "devtable", 404, ), @@ -3923,7 +3737,7 @@ SECURITY_TESTS: List[ RestoreTag, "POST", {"tag": "HP8R", "repository": "buynlarge/orgrepo"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, "freshuser", 403, ), @@ -3931,7 +3745,7 @@ SECURITY_TESTS: List[ RestoreTag, "POST", {"tag": "HP8R", "repository": "buynlarge/orgrepo"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, "reader", 403, ), @@ -3964,7 +3778,7 @@ SECURITY_TESTS: List[ RepositoryTag, "PUT", {"tag": "HP8R", "repository": "public/publicrepo"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, None, 401, ), @@ -3972,7 +3786,7 @@ SECURITY_TESTS: List[ RepositoryTag, "PUT", {"tag": "HP8R", "repository": "public/publicrepo"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, "devtable", 403, ), @@ -3980,7 +3794,7 @@ SECURITY_TESTS: List[ RepositoryTag, "PUT", {"tag": "HP8R", "repository": "public/publicrepo"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, "freshuser", 403, ), @@ -3988,7 +3802,7 @@ SECURITY_TESTS: List[ RepositoryTag, "PUT", {"tag": "HP8R", "repository": "public/publicrepo"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, "reader", 403, ), @@ -4021,7 +3835,7 @@ SECURITY_TESTS: List[ RepositoryTag, "PUT", {"tag": "HP8R", "repository": "devtable/shared"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, None, 401, ), @@ -4029,7 +3843,7 @@ SECURITY_TESTS: List[ RepositoryTag, "PUT", {"tag": "HP8R", "repository": "devtable/shared"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, "devtable", 404, ), @@ -4037,7 +3851,7 @@ SECURITY_TESTS: List[ RepositoryTag, "PUT", {"tag": "HP8R", "repository": "devtable/shared"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, "freshuser", 403, ), @@ -4045,7 +3859,7 @@ SECURITY_TESTS: List[ RepositoryTag, "PUT", {"tag": "HP8R", "repository": "devtable/shared"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, "reader", 403, ), @@ -4078,7 +3892,7 @@ SECURITY_TESTS: List[ RepositoryTag, "PUT", {"tag": "HP8R", "repository": "buynlarge/orgrepo"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, None, 401, ), @@ -4086,7 +3900,7 @@ SECURITY_TESTS: List[ RepositoryTag, "PUT", {"tag": "HP8R", "repository": "buynlarge/orgrepo"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, "devtable", 404, ), @@ -4094,7 +3908,7 @@ SECURITY_TESTS: List[ RepositoryTag, "PUT", {"tag": "HP8R", "repository": "buynlarge/orgrepo"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, "freshuser", 403, ), @@ -4102,7 +3916,7 @@ SECURITY_TESTS: List[ RepositoryTag, "PUT", {"tag": "HP8R", "repository": "buynlarge/orgrepo"}, - {"image": "WXNG"}, + {"manifest_digest": "WXNG"}, "reader", 403, ), @@ -4744,18 +4558,6 @@ SECURITY_TESTS: List[ "reader", 403, ), - (RepositoryImageList, "GET", {"repository": "public/publicrepo"}, None, None, 200), - (RepositoryImageList, "GET", {"repository": "public/publicrepo"}, None, "devtable", 200), - (RepositoryImageList, "GET", {"repository": "public/publicrepo"}, None, "freshuser", 200), - (RepositoryImageList, "GET", {"repository": "public/publicrepo"}, None, "reader", 200), - (RepositoryImageList, "GET", {"repository": "devtable/shared"}, None, None, 401), - (RepositoryImageList, "GET", {"repository": "devtable/shared"}, None, "devtable", 200), - (RepositoryImageList, "GET", {"repository": "devtable/shared"}, None, "freshuser", 403), - (RepositoryImageList, "GET", {"repository": "devtable/shared"}, None, "reader", 200), - (RepositoryImageList, "GET", {"repository": "buynlarge/orgrepo"}, None, None, 401), - (RepositoryImageList, "GET", {"repository": "buynlarge/orgrepo"}, None, "devtable", 200), - (RepositoryImageList, "GET", {"repository": "buynlarge/orgrepo"}, None, "freshuser", 403), - (RepositoryImageList, "GET", {"repository": "buynlarge/orgrepo"}, None, "reader", 200), (RepositoryLogs, "GET", {"repository": "public/publicrepo"}, None, None, 401), (RepositoryLogs, "GET", {"repository": "public/publicrepo"}, None, "devtable", 403), (RepositoryLogs, "GET", {"repository": "public/publicrepo"}, None, "freshuser", 403), @@ -5393,38 +5195,6 @@ SECURITY_TESTS: List[ "reader", 403, ), - ( - RepositoryImageSecurity, - "GET", - {"repository": "devtable/simple", "imageid": "fake"}, - None, - None, - 401, - ), - ( - RepositoryImageSecurity, - "GET", - {"repository": "devtable/simple", "imageid": "fake"}, - None, - "devtable", - 404, - ), - ( - RepositoryImageSecurity, - "GET", - {"repository": "devtable/simple", "imageid": "fake"}, - None, - "freshuser", - 403, - ), - ( - RepositoryImageSecurity, - "GET", - {"repository": "devtable/simple", "imageid": "fake"}, - None, - "reader", - 403, - ), ( RepositoryManifestSecurity, "GET", diff --git a/endpoints/api/test/test_tag.py b/endpoints/api/test/test_tag.py index 132a5aba2..ab4f57f26 100644 --- a/endpoints/api/test/test_tag.py +++ b/endpoints/api/test/test_tag.py @@ -7,7 +7,7 @@ from data.database import Manifest from endpoints.api.test.shared import conduct_api_call from endpoints.test.shared import client_with_identity -from endpoints.api.tag import RepositoryTag, RestoreTag, ListRepositoryTags, RepositoryTagImages +from endpoints.api.tag import RepositoryTag, RestoreTag, ListRepositoryTags from test.fixtures import * @@ -55,7 +55,7 @@ def test_change_tag_expiration(client, app): @pytest.mark.parametrize( - "image_exists,test_tag,expected_status", + "manifest_exists,test_tag,expected_status", [ (True, "-INVALID-TAG-NAME", 400), (True, ".INVALID-TAG-NAME", 400), @@ -70,18 +70,18 @@ def test_change_tag_expiration(client, app): (True, "newtag", 201), ], ) -def test_move_tag(image_exists, test_tag, expected_status, client, app): +def test_move_tag(manifest_exists, test_tag, expected_status, client, app): with client_with_identity("devtable", client) as cl: test_image = "unknown" - if image_exists: + if manifest_exists: repo_ref = registry_model.lookup_repository("devtable", "simple") tag_ref = registry_model.get_repo_tag(repo_ref, "latest") assert tag_ref - test_image = tag_ref.manifest.legacy_image_root_id + test_image = tag_ref.manifest.digest params = {"repository": "devtable/simple", "tag": test_tag} - request_body = {"image": test_image} + request_body = {"manifest_digest": test_image} if expected_status is None: with pytest.raises(Exception): conduct_api_call(cl, RepositoryTag, "put", params, request_body, expected_status) @@ -112,18 +112,3 @@ def test_list_repo_tags(repo_namespace, repo_name, client, query_count, app): repo_ref = registry_model.lookup_repository(repo_namespace, repo_name) history, _ = registry_model.list_repository_tag_history(repo_ref) assert len(tags) == len(history) - - -@pytest.mark.parametrize( - "repository, tag, expect_images", - [ - ("devtable/simple", "prod", True), - ("devtable/simple", "latest", True), - ("devtable/complex", "prod", True), - ], -) -def test_list_tag_images(repository, tag, expect_images, client, app): - with client_with_identity("devtable", client) as cl: - params = {"repository": repository, "tag": tag} - result = conduct_api_call(cl, RepositoryTagImages, "get", params, None, 200).json - assert bool(result["images"]) == expect_images diff --git a/test/test_api_usage.py b/test/test_api_usage.py index 6cc01b8b6..0e2188411 100644 --- a/test/test_api_usage.py +++ b/test/test_api_usage.py @@ -48,9 +48,8 @@ from endpoints.api.team import ( TeamPermissions, InviteTeamMember, ) -from endpoints.api.tag import RepositoryTagImages, RepositoryTag, RestoreTag, ListRepositoryTags +from endpoints.api.tag import RepositoryTag, RestoreTag, ListRepositoryTags from endpoints.api.search import EntitySearch, ConductSearch -from endpoints.api.image import RepositoryImage, RepositoryImageList from endpoints.api.build import RepositoryBuildStatus, RepositoryBuildList, RepositoryBuildResource from endpoints.api.robot import ( UserRobotList, @@ -139,7 +138,7 @@ from endpoints.api.globalmessages import ( GlobalUserMessage, GlobalUserMessages, ) -from endpoints.api.secscan import RepositoryImageSecurity, RepositoryManifestSecurity +from endpoints.api.secscan import RepositoryManifestSecurity from endpoints.api.manifest import RepositoryManifestLabels, ManageRepositoryManifestLabel from util.morecollections import AttrDict @@ -3119,51 +3118,6 @@ class TestRepositoryNotifications(ApiTestCase): self.assertEqual("Some Notification", json["title"]) -class TestListAndGetImage(ApiTestCase): - def test_listandgetimages(self): - self.login(ADMIN_ACCESS_USER) - - json = self.getJsonResponse( - RepositoryImageList, params=dict(repository=ADMIN_ACCESS_USER + "/simple") - ) - - assert len(json["images"]) > 0 - - for image in json["images"]: - assert "id" in image - assert "tags" in image - assert "created" in image - assert "comment" in image - assert "command" in image - assert "ancestors" in image - assert "size" in image - - ijson = self.getJsonResponse( - RepositoryImage, - params=dict(repository=ADMIN_ACCESS_USER + "/simple", image_id=image["id"]), - ) - - self.assertEqual(image["id"], ijson["id"]) - - -class TestGetImageChanges(ApiTestCase): - def test_getimagechanges(self): - self.login(ADMIN_ACCESS_USER) - - # Find an image to check. - json = self.getJsonResponse( - RepositoryImageList, params=dict(repository=ADMIN_ACCESS_USER + "/simple") - ) - - image_id = json["images"][0]["id"] - - # Lookup the image's changes. - # TODO: Fix me once we can get fake changes into the test data - # self.getJsonResponse(RepositoryImageChanges, - # params=dict(repository=ADMIN_ACCESS_USER + '/simple', - # image_id=image_id)) - - class TestRestoreTag(ApiTestCase): def test_restoretag_invalidtag(self): self.login(ADMIN_ACCESS_USER) @@ -3171,7 +3125,7 @@ class TestRestoreTag(ApiTestCase): self.postResponse( RestoreTag, params=dict(repository=ADMIN_ACCESS_USER + "/history", tag="invalidtag"), - data=dict(image="invalid_image"), + data=dict(manifest_digest="invalid_image"), expected_code=404, ) @@ -3181,7 +3135,7 @@ class TestRestoreTag(ApiTestCase): self.postResponse( RestoreTag, params=dict(repository=ADMIN_ACCESS_USER + "/history", tag="latest"), - data=dict(image="invalid_image"), + data=dict(manifest_digest="invalid_image"), expected_code=404, ) @@ -3205,12 +3159,12 @@ class TestRestoreTag(ApiTestCase): self.assertEqual(2, len(json["tags"])) self.assertFalse("end_ts" in json["tags"][0]) - previous_image_id = json["tags"][1]["docker_image_id"] + previous_image_id = json["tags"][1]["manifest_digest"] self.postJsonResponse( RestoreTag, params=dict(repository=ADMIN_ACCESS_USER + "/history", tag="latest"), - data=dict(image=previous_image_id), + data=dict(manifest_digest=previous_image_id), ) json = self.getJsonResponse( @@ -3218,7 +3172,7 @@ class TestRestoreTag(ApiTestCase): ) self.assertEqual(3, len(json["tags"])) self.assertFalse("end_ts" in json["tags"][0]) - self.assertEqual(previous_image_id, json["tags"][0]["docker_image_id"]) + self.assertEqual(previous_image_id, json["tags"][0]["manifest_digest"]) def test_restoretag_to_digest(self): self.login(ADMIN_ACCESS_USER) @@ -3235,7 +3189,7 @@ class TestRestoreTag(ApiTestCase): self.postJsonResponse( RestoreTag, params=dict(repository=ADMIN_ACCESS_USER + "/history", tag="latest"), - data=dict(image="foo", manifest_digest=previous_manifest), + data=dict(manifest_digest=previous_manifest), ) json = self.getJsonResponse( @@ -3252,24 +3206,28 @@ class TestListAndDeleteTag(ApiTestCase): # List the images for staging. json = self.getJsonResponse( - RepositoryTagImages, - params=dict(repository=ADMIN_ACCESS_USER + "/complex", tag="staging"), + ListRepositoryTags, + params=dict( + repository=ADMIN_ACCESS_USER + "/complex", + specificTag="staging", + onlyActiveTags=True, + ), ) - staging_images = json["images"] + staging_images = json["tags"] # Try to add some invalid tags. self.putResponse( RepositoryTag, params=dict(repository=ADMIN_ACCESS_USER + "/complex", tag="-fail"), - data=dict(image=staging_images[0]["id"]), + data=dict(manifest_digest=staging_images[0]["manifest_digest"]), expected_code=400, ) self.putResponse( RepositoryTag, params=dict(repository=ADMIN_ACCESS_USER + "/complex", tag="北京"), - data=dict(image=staging_images[0]["id"]), + data=dict(manifest_digest=staging_images[0]["manifest_digest"]), expected_code=400, ) @@ -3278,20 +3236,27 @@ class TestListAndDeleteTag(ApiTestCase): # List the images for prod. json = self.getJsonResponse( - RepositoryTagImages, params=dict(repository=ADMIN_ACCESS_USER + "/complex", tag="prod") + ListRepositoryTags, + params=dict( + repository=ADMIN_ACCESS_USER + "/complex", specificTag="prod", onlyActiveTags=True + ), ) - prod_images = json["images"] + prod_images = json["tags"] assert len(prod_images) > 0 # List the images for staging. json = self.getJsonResponse( - RepositoryTagImages, - params=dict(repository=ADMIN_ACCESS_USER + "/complex", tag="staging"), + ListRepositoryTags, + params=dict( + repository=ADMIN_ACCESS_USER + "/complex", + specificTag="staging", + onlyActiveTags=True, + ), ) - staging_images = json["images"] - assert len(prod_images) == len(staging_images) + 2 + staging_images = json["tags"] + assert len(prod_images) == len(staging_images) # Delete prod. self.deleteEmptyResponse( @@ -3301,25 +3266,30 @@ class TestListAndDeleteTag(ApiTestCase): ) # Make sure the tag is gone. - self.getResponse( - RepositoryTagImages, - params=dict(repository=ADMIN_ACCESS_USER + "/complex", tag="prod"), - expected_code=404, + json = self.getJsonResponse( + ListRepositoryTags, + params=dict( + repository=ADMIN_ACCESS_USER + "/complex", specificTag="prod", onlyActiveTags=True + ), + expected_code=200, ) + assert len(json["tags"]) == 0 # Make the sure the staging images are still there. json = self.getJsonResponse( - RepositoryTagImages, - params=dict(repository=ADMIN_ACCESS_USER + "/complex", tag="staging"), + ListRepositoryTags, + params=dict( + repository=ADMIN_ACCESS_USER + "/complex", + specificTag="staging", + onlyActiveTags=True, + ), ) - self.assertEqual(staging_images, json["images"]) - # Require a valid tag name. self.putResponse( RepositoryTag, params=dict(repository=ADMIN_ACCESS_USER + "/complex", tag="-fail"), - data=dict(image=staging_images[0]["id"]), + data=dict(manifest_digest=json["tags"][0]["manifest_digest"]), expected_code=400, ) @@ -3327,44 +3297,55 @@ class TestListAndDeleteTag(ApiTestCase): self.putResponse( RepositoryTag, params=dict(repository=ADMIN_ACCESS_USER + "/complex", tag="sometag"), - data=dict(image=staging_images[0]["id"]), + data=dict(manifest_digest=json["tags"][0]["manifest_digest"]), expected_code=201, ) # Make sure the tag is present. json = self.getJsonResponse( - RepositoryTagImages, - params=dict(repository=ADMIN_ACCESS_USER + "/complex", tag="sometag"), + ListRepositoryTags, + params=dict( + repository=ADMIN_ACCESS_USER + "/complex", + specificTag="sometag", + onlyActiveTags=True, + ), ) - assert json["images"] + assert len(json["tags"]) > 0 # Move the tag. self.putResponse( RepositoryTag, params=dict(repository=ADMIN_ACCESS_USER + "/complex", tag="sometag"), - data=dict(image=staging_images[-1]["id"]), + data=dict(manifest_digest=json["tags"][0]["manifest_digest"]), expected_code=201, ) # Make sure the tag has moved. json = self.getJsonResponse( - RepositoryTagImages, - params=dict(repository=ADMIN_ACCESS_USER + "/complex", tag="sometag"), + ListRepositoryTags, + params=dict( + repository=ADMIN_ACCESS_USER + "/complex", + specificTag="sometag", + onlyActiveTags=True, + ), ) - sometag_new_images = json["images"] - assert sometag_new_images + sometag_new_images = json["tags"] + assert len(sometag_new_images) > 0 def test_deletesubtag(self): self.login(ADMIN_ACCESS_USER) # List the images for prod. json = self.getJsonResponse( - RepositoryTagImages, params=dict(repository=ADMIN_ACCESS_USER + "/complex", tag="prod") + ListRepositoryTags, + params=dict( + repository=ADMIN_ACCESS_USER + "/complex", specificTag="prod", onlyActiveTags=True + ), ) - prod_images = json["images"] + prod_images = json["tags"] assert len(prod_images) > 0 # Delete staging. @@ -3376,10 +3357,13 @@ class TestListAndDeleteTag(ApiTestCase): # Make sure the prod images are still around. json = self.getJsonResponse( - RepositoryTagImages, params=dict(repository=ADMIN_ACCESS_USER + "/complex", tag="prod") + ListRepositoryTags, + params=dict( + repository=ADMIN_ACCESS_USER + "/complex", specificTag="prod", onlyActiveTags=True + ), ) - self.assertEqual(prod_images, json["images"]) + self.assertEqual(prod_images, json["tags"]) def test_listtag_digest(self): self.login(ADMIN_ACCESS_USER)