Merge pull request #3195 from jzelinskie/v2-accept-headers
V2 accept headers
This commit is contained in:
commit
7edf679670
6 changed files with 41 additions and 7 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -26,5 +26,4 @@ build/
|
||||||
.vscode
|
.vscode
|
||||||
*.iml
|
*.iml
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.pytest_cache/v/cache/lastfailed
|
.pytest_cache/*
|
||||||
.pytest_cache/v/cache/nodeids
|
|
||||||
|
|
|
@ -47,13 +47,13 @@ def _get_repository_blob(namespace_name, repo_name, digest):
|
||||||
blob = model.get_blob_by_digest(namespace_name, repo_name, digest)
|
blob = model.get_blob_by_digest(namespace_name, repo_name, digest)
|
||||||
if blob is None:
|
if blob is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return blob._asdict()
|
return blob._asdict()
|
||||||
|
|
||||||
blob_cache_key = cache_key.for_repository_blob(namespace_name, repo_name, digest)
|
blob_cache_key = cache_key.for_repository_blob(namespace_name, repo_name, digest)
|
||||||
blob_dict = model_cache.retrieve(blob_cache_key, load_blob)
|
blob_dict = model_cache.retrieve(blob_cache_key, load_blob)
|
||||||
return Blob(**blob_dict) if blob_dict is not None else None
|
return Blob(**blob_dict) if blob_dict is not None else None
|
||||||
|
|
||||||
|
|
||||||
@v2_bp.route(BLOB_DIGEST_ROUTE, methods=['HEAD'])
|
@v2_bp.route(BLOB_DIGEST_ROUTE, methods=['HEAD'])
|
||||||
@parse_repository_name()
|
@parse_repository_name()
|
||||||
|
|
|
@ -17,7 +17,8 @@ from endpoints.v2.errors import (BlobUnknown, ManifestInvalid, ManifestUnknown,
|
||||||
NameInvalid, TagExpired)
|
NameInvalid, TagExpired)
|
||||||
from endpoints.v2.labelhandlers import handle_label
|
from endpoints.v2.labelhandlers import handle_label
|
||||||
from image.docker import ManifestException
|
from image.docker import ManifestException
|
||||||
from image.docker.schema1 import DockerSchema1Manifest, DockerSchema1ManifestBuilder
|
from image.docker.schema1 import (DOCKER_SCHEMA1_MANIFEST_CONTENT_TYPE,
|
||||||
|
DockerSchema1Manifest, DockerSchema1ManifestBuilder)
|
||||||
from image.docker.schema2 import DOCKER_SCHEMA2_CONTENT_TYPES, OCI_CONTENT_TYPES
|
from image.docker.schema2 import DOCKER_SCHEMA2_CONTENT_TYPES, OCI_CONTENT_TYPES
|
||||||
from notifications import spawn_notification
|
from notifications import spawn_notification
|
||||||
from util.audit import track_and_log
|
from util.audit import track_and_log
|
||||||
|
@ -92,7 +93,8 @@ def fetch_manifest_by_digest(namespace_name, repo_name, manifest_ref):
|
||||||
def _reject_manifest2_schema2(func):
|
def _reject_manifest2_schema2(func):
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapped(*args, **kwargs):
|
def wrapped(*args, **kwargs):
|
||||||
if request.content_type in (DOCKER_SCHEMA2_CONTENT_TYPES | OCI_CONTENT_TYPES):
|
if _doesnt_accept_schema_v1() or \
|
||||||
|
request.content_type in DOCKER_SCHEMA2_CONTENT_TYPES | OCI_CONTENT_TYPES:
|
||||||
raise ManifestInvalid(detail={'message': 'manifest schema version not supported'},
|
raise ManifestInvalid(detail={'message': 'manifest schema version not supported'},
|
||||||
http_status_code=415)
|
http_status_code=415)
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
@ -100,6 +102,12 @@ def _reject_manifest2_schema2(func):
|
||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
|
def _doesnt_accept_schema_v1():
|
||||||
|
# If the client doesn't specify anything, still give them Schema v1.
|
||||||
|
return len(request.accept_mimetypes) != 0 and \
|
||||||
|
DOCKER_SCHEMA1_MANIFEST_CONTENT_TYPE not in request.accept_mimetypes
|
||||||
|
|
||||||
|
|
||||||
@v2_bp.route(MANIFEST_TAGNAME_ROUTE, methods=['PUT'])
|
@v2_bp.route(MANIFEST_TAGNAME_ROUTE, methods=['PUT'])
|
||||||
@_reject_manifest2_schema2
|
@_reject_manifest2_schema2
|
||||||
@parse_repository_name()
|
@parse_repository_name()
|
||||||
|
|
|
@ -136,6 +136,7 @@ class V2Protocol(RegistryProtocol):
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
'Authorization': 'Bearer ' + token,
|
'Authorization': 'Bearer ' + token,
|
||||||
|
'Accept': options.accept_mimetypes,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Build fake manifests.
|
# Build fake manifests.
|
||||||
|
|
|
@ -64,6 +64,7 @@ class ProtocolOptions(object):
|
||||||
self.chunks_for_upload = None
|
self.chunks_for_upload = None
|
||||||
self.skip_head_checks = False
|
self.skip_head_checks = False
|
||||||
self.manifest_content_type = None
|
self.manifest_content_type = None
|
||||||
|
self.accept_mimetypes = '*/*'
|
||||||
self.mount_blobs = None
|
self.mount_blobs = None
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@ import binascii
|
||||||
import bencode
|
import bencode
|
||||||
import resumablehashlib
|
import resumablehashlib
|
||||||
|
|
||||||
|
from werkzeug.datastructures import Accept
|
||||||
|
|
||||||
from test.fixtures import *
|
from test.fixtures import *
|
||||||
from test.registry.liveserverfixture import *
|
from test.registry.liveserverfixture import *
|
||||||
from test.registry.fixtures import *
|
from test.registry.fixtures import *
|
||||||
|
@ -17,6 +19,7 @@ from test.registry.protocols import Failures, Image, layer_bytes_for_contents, P
|
||||||
|
|
||||||
from app import instance_keys
|
from app import instance_keys
|
||||||
from data.model.tag import list_repository_tags
|
from data.model.tag import list_repository_tags
|
||||||
|
from image.docker.schema1 import DOCKER_SCHEMA1_MANIFEST_CONTENT_TYPE
|
||||||
from util.security.registry_jwt import decode_bearer_header
|
from util.security.registry_jwt import decode_bearer_header
|
||||||
from util.timedeltastring import convert_to_timedelta
|
from util.timedeltastring import convert_to_timedelta
|
||||||
|
|
||||||
|
@ -341,7 +344,7 @@ def test_push_library_with_support_disabled(pusher, basic_images, liveserver_ses
|
||||||
should fail.
|
should fail.
|
||||||
"""
|
"""
|
||||||
credentials = ('devtable', 'password')
|
credentials = ('devtable', 'password')
|
||||||
|
|
||||||
with FeatureFlagValue('LIBRARY_SUPPORT', False, registry_server_executor.on(liveserver)):
|
with FeatureFlagValue('LIBRARY_SUPPORT', False, registry_server_executor.on(liveserver)):
|
||||||
# Attempt to push a new repository.
|
# Attempt to push a new repository.
|
||||||
pusher.push(liveserver_session, '', 'newrepo', 'latest', basic_images,
|
pusher.push(liveserver_session, '', 'newrepo', 'latest', basic_images,
|
||||||
|
@ -543,6 +546,28 @@ def test_unsupported_manifest_content_type(content_type, manifest_protocol, basi
|
||||||
expected_failure=Failures.UNSUPPORTED_CONTENT_TYPE)
|
expected_failure=Failures.UNSUPPORTED_CONTENT_TYPE)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('accept_mimetypes', [
|
||||||
|
[('application/vnd.oci.image.manifest.v1+json', 1)],
|
||||||
|
[('application/vnd.docker.distribution.manifest.v2+json', 1),
|
||||||
|
('application/vnd.docker.distribution.manifest.list.v2+json', 1)],
|
||||||
|
[('application/vnd.foo.bar', 1)],
|
||||||
|
])
|
||||||
|
def test_unsupported_manifest_accept_headers(accept_mimetypes, manifest_protocol, basic_images,
|
||||||
|
liveserver_session, app_reloader):
|
||||||
|
""" Test: Attempt to push a manifest with an unsupported accept headers. """
|
||||||
|
credentials = ('devtable', 'password')
|
||||||
|
|
||||||
|
options = ProtocolOptions()
|
||||||
|
options.manifest_content_type = DOCKER_SCHEMA1_MANIFEST_CONTENT_TYPE
|
||||||
|
options.accept_mimetypes = str(Accept(accept_mimetypes))
|
||||||
|
|
||||||
|
# Attempt to push a new repository.
|
||||||
|
manifest_protocol.push(liveserver_session, 'devtable', 'newrepo', 'latest', basic_images,
|
||||||
|
credentials=credentials,
|
||||||
|
options=options,
|
||||||
|
expected_failure=Failures.UNSUPPORTED_CONTENT_TYPE)
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_blob_reference(manifest_protocol, basic_images, liveserver_session, app_reloader):
|
def test_invalid_blob_reference(manifest_protocol, basic_images, liveserver_session, app_reloader):
|
||||||
""" Test: Attempt to push a manifest with an invalid blob reference. """
|
""" Test: Attempt to push a manifest with an invalid blob reference. """
|
||||||
credentials = ('devtable', 'password')
|
credentials = ('devtable', 'password')
|
||||||
|
|
Reference in a new issue