mirror of
https://github.com/quay/quay.git
synced 2025-07-28 20:22:05 +03:00
api: fully deprecate image api endpoints (PROJQUAY-3418) (#1164)
This commit is contained in:
committed by
GitHub
parent
ba652e0b98
commit
02dfc63f42
@ -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
|
||||
|
@ -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/<apirepopath: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/<apirepopath:repository>/image/<image_id>")
|
||||
@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)
|
@ -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,
|
||||
}
|
||||
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -75,42 +75,6 @@ def _security_info(manifest_or_legacy_image, include_vulnerabilities=True):
|
||||
}
|
||||
|
||||
|
||||
@resource("/v1/repository/<apirepopath:repository>/image/<imageid>/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")
|
||||
|
@ -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/<apirepopath:repository>/tag/<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/<apirepopath:repository>/tag/<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,
|
||||
|
@ -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"
|
@ -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),
|
||||
|
@ -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"]
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
Reference in New Issue
Block a user