use kwargs for parse_repository_name
This commit is contained in:
parent
3b52a255b2
commit
bb46cc933d
15 changed files with 285 additions and 270 deletions
|
@ -1,18 +1,22 @@
|
|||
import logging
|
||||
import jwt.utils
|
||||
import json
|
||||
import features
|
||||
import hashlib
|
||||
|
||||
from peewee import IntegrityError
|
||||
from flask import make_response, request, url_for
|
||||
from collections import namedtuple, OrderedDict
|
||||
from jwkest.jws import SIGNER_ALGS, keyrep
|
||||
from datetime import datetime
|
||||
from functools import wraps
|
||||
|
||||
import jwt.utils
|
||||
|
||||
from peewee import IntegrityError
|
||||
from flask import make_response, request, url_for
|
||||
from jwkest.jws import SIGNER_ALGS, keyrep
|
||||
|
||||
import features
|
||||
|
||||
from app import docker_v2_signing_key, app
|
||||
from auth.registry_jwt_auth import process_registry_jwt_auth
|
||||
from endpoints.common import parse_repository_name
|
||||
from endpoints.decorators import anon_protect
|
||||
from endpoints.v2 import v2_bp, require_repo_read, require_repo_write
|
||||
from endpoints.v2.errors import (BlobUnknown, ManifestInvalid, ManifestUnknown, TagInvalid,
|
||||
|
@ -22,7 +26,6 @@ from endpoints.notificationhelper import spawn_notification
|
|||
from digest import digest_tools
|
||||
from data import model
|
||||
from data.database import RepositoryTag
|
||||
from endpoints.common import parse_repository_name
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -84,7 +87,8 @@ class SignedManifest(object):
|
|||
|
||||
def _validate(self):
|
||||
for signature in self._signatures:
|
||||
bytes_to_verify = '{0}.{1}'.format(signature['protected'], jwt.utils.base64url_encode(self.payload))
|
||||
bytes_to_verify = '{0}.{1}'.format(signature['protected'],
|
||||
jwt.utils.base64url_encode(self.payload))
|
||||
signer = SIGNER_ALGS[signature['header']['alg']]
|
||||
key = keyrep(signature['header']['jwk'])
|
||||
gk = key.get_key()
|
||||
|
@ -163,9 +167,9 @@ class SignedManifest(object):
|
|||
class SignedManifestBuilder(object):
|
||||
""" Class which represents a manifest which is currently being built.
|
||||
"""
|
||||
def __init__(self, namespace, repo_name, tag, architecture='amd64', schema_ver=1):
|
||||
repo_name_key = '{0}/{1}'.format(namespace, repo_name)
|
||||
if namespace == '':
|
||||
def __init__(self, namespace_name, repo_name, tag, architecture='amd64', schema_ver=1):
|
||||
repo_name_key = '{0}/{1}'.format(namespace_name, repo_name)
|
||||
if namespace_name == '':
|
||||
repo_name_key = repo_name
|
||||
|
||||
self._base_payload = {
|
||||
|
@ -238,26 +242,26 @@ class SignedManifestBuilder(object):
|
|||
|
||||
@v2_bp.route(MANIFEST_TAGNAME_ROUTE, methods=['GET'])
|
||||
@process_registry_jwt_auth
|
||||
@parse_repository_name
|
||||
@parse_repository_name()
|
||||
@require_repo_read
|
||||
@anon_protect
|
||||
def fetch_manifest_by_tagname(namespace, repo_name, manifest_ref):
|
||||
def fetch_manifest_by_tagname(namespace_name, repo_name, manifest_ref):
|
||||
try:
|
||||
manifest = model.tag.load_tag_manifest(namespace, repo_name, manifest_ref)
|
||||
manifest = model.tag.load_tag_manifest(namespace_name, repo_name, manifest_ref)
|
||||
except model.InvalidManifestException:
|
||||
try:
|
||||
model.tag.get_active_tag(namespace, repo_name, manifest_ref)
|
||||
model.tag.get_active_tag(namespace_name, repo_name, manifest_ref)
|
||||
except RepositoryTag.DoesNotExist:
|
||||
raise ManifestUnknown()
|
||||
raise ManifestUnknown()
|
||||
|
||||
try:
|
||||
manifest = _generate_and_store_manifest(namespace, repo_name, manifest_ref)
|
||||
manifest = _generate_and_store_manifest(namespace_name, repo_name, manifest_ref)
|
||||
except model.DataModelException:
|
||||
logger.exception('Exception when generating manifest for %s/%s:%s', namespace, repo_name,
|
||||
logger.exception('Exception when generating manifest for %s/%s:%s', namespace_name, repo_name,
|
||||
manifest_ref)
|
||||
raise ManifestUnknown()
|
||||
|
||||
repo = model.repository.get_repository(namespace, repo_name)
|
||||
repo = model.repository.get_repository(namespace_name, repo_name)
|
||||
if repo is not None:
|
||||
track_and_log('pull_repo', repo, analytics_name='pull_repo_100x', analytics_sample=0.01)
|
||||
|
||||
|
@ -269,17 +273,17 @@ def fetch_manifest_by_tagname(namespace, repo_name, manifest_ref):
|
|||
|
||||
@v2_bp.route(MANIFEST_DIGEST_ROUTE, methods=['GET'])
|
||||
@process_registry_jwt_auth
|
||||
@parse_repository_name
|
||||
@parse_repository_name()
|
||||
@require_repo_read
|
||||
@anon_protect
|
||||
def fetch_manifest_by_digest(namespace, repo_name, manifest_ref):
|
||||
def fetch_manifest_by_digest(namespace_name, repo_name, manifest_ref):
|
||||
try:
|
||||
manifest = model.tag.load_manifest_by_digest(namespace, repo_name, manifest_ref)
|
||||
manifest = model.tag.load_manifest_by_digest(namespace_name, repo_name, manifest_ref)
|
||||
except model.InvalidManifestException:
|
||||
# Without a tag name to reference, we can't make an attempt to generate the manifest
|
||||
raise ManifestUnknown()
|
||||
|
||||
repo = model.repository.get_repository(namespace, repo_name)
|
||||
repo = model.repository.get_repository(namespace_name, repo_name)
|
||||
if repo is not None:
|
||||
track_and_log('pull_repo', repo)
|
||||
|
||||
|
@ -301,11 +305,11 @@ def _reject_manifest2_schema2(func):
|
|||
|
||||
@v2_bp.route(MANIFEST_TAGNAME_ROUTE, methods=['PUT'])
|
||||
@process_registry_jwt_auth
|
||||
@parse_repository_name
|
||||
@parse_repository_name()
|
||||
@require_repo_write
|
||||
@anon_protect
|
||||
@_reject_manifest2_schema2
|
||||
def write_manifest_by_tagname(namespace, repo_name, manifest_ref):
|
||||
def write_manifest_by_tagname(namespace_name, repo_name, manifest_ref):
|
||||
try:
|
||||
manifest = SignedManifest(request.data)
|
||||
except ValueError:
|
||||
|
@ -314,16 +318,16 @@ def write_manifest_by_tagname(namespace, repo_name, manifest_ref):
|
|||
if manifest.tag != manifest_ref:
|
||||
raise TagInvalid()
|
||||
|
||||
return _write_manifest(namespace, repo_name, manifest)
|
||||
return _write_manifest(namespace_name, repo_name, manifest)
|
||||
|
||||
|
||||
@v2_bp.route(MANIFEST_DIGEST_ROUTE, methods=['PUT'])
|
||||
@process_registry_jwt_auth
|
||||
@parse_repository_name
|
||||
@parse_repository_name()
|
||||
@require_repo_write
|
||||
@anon_protect
|
||||
@_reject_manifest2_schema2
|
||||
def write_manifest_by_digest(namespace, repo_name, manifest_ref):
|
||||
def write_manifest_by_digest(namespace_name, repo_name, manifest_ref):
|
||||
try:
|
||||
manifest = SignedManifest(request.data)
|
||||
except ValueError:
|
||||
|
@ -332,7 +336,7 @@ def write_manifest_by_digest(namespace, repo_name, manifest_ref):
|
|||
if manifest.digest != manifest_ref:
|
||||
raise ManifestInvalid(detail={'message': 'manifest digest mismatch'})
|
||||
|
||||
return _write_manifest(namespace, repo_name, manifest)
|
||||
return _write_manifest(namespace_name, repo_name, manifest)
|
||||
|
||||
|
||||
def _updated_v1_metadata(v1_metadata_json, updated_id_map):
|
||||
|
@ -350,21 +354,21 @@ def _updated_v1_metadata(v1_metadata_json, updated_id_map):
|
|||
return json.dumps(parsed)
|
||||
|
||||
|
||||
def _write_manifest(namespace, repo_name, manifest):
|
||||
def _write_manifest(namespace_name, repo_name, manifest):
|
||||
# Ensure that the manifest is for this repository. If the manifest's namespace is empty, then
|
||||
# it is for the library namespace and we need an extra check.
|
||||
if (manifest.namespace == '' and features.LIBRARY_SUPPORT and
|
||||
namespace == app.config['LIBRARY_NAMESPACE']):
|
||||
namespace_name == app.config['LIBRARY_NAMESPACE']):
|
||||
# This is a library manifest. All good.
|
||||
pass
|
||||
elif manifest.namespace != namespace:
|
||||
elif manifest.namespace != namespace_name:
|
||||
raise NameInvalid()
|
||||
|
||||
if manifest.repo_name != repo_name:
|
||||
raise NameInvalid()
|
||||
|
||||
# Ensure that the repository exists.
|
||||
repo = model.repository.get_repository(namespace, repo_name)
|
||||
repo = model.repository.get_repository(namespace_name, repo_name)
|
||||
if repo is None:
|
||||
raise NameInvalid()
|
||||
|
||||
|
@ -411,8 +415,8 @@ def _write_manifest(namespace, repo_name, manifest):
|
|||
|
||||
v1_metadata_str = mdata.v1_metadata_str.encode('utf-8')
|
||||
working_docker_id = hashlib.sha256(v1_metadata_str + '@' + digest_str).hexdigest()
|
||||
logger.debug('Rewriting docker_id %s/%s %s -> %s', namespace, repo_name, v1_mdata.docker_id,
|
||||
working_docker_id)
|
||||
logger.debug('Rewriting docker_id %s/%s %s -> %s', namespace_name, repo_name,
|
||||
v1_mdata.docker_id, working_docker_id)
|
||||
has_rewritten_ids = True
|
||||
|
||||
# Store the new docker id in the map
|
||||
|
@ -447,7 +451,7 @@ def _write_manifest(namespace, repo_name, manifest):
|
|||
# Store the manifest pointing to the tag.
|
||||
manifest_digest = manifest.digest
|
||||
leaf_layer_id = images_map[layers[-1].v1_metadata.docker_id].docker_image_id
|
||||
model.tag.store_tag_manifest(namespace, repo_name, tag_name, leaf_layer_id, manifest_digest,
|
||||
model.tag.store_tag_manifest(namespace_name, repo_name, tag_name, leaf_layer_id, manifest_digest,
|
||||
manifest.bytes)
|
||||
|
||||
# Spawn the repo_push event.
|
||||
|
@ -461,29 +465,29 @@ def _write_manifest(namespace, repo_name, manifest):
|
|||
response = make_response('OK', 202)
|
||||
response.headers['Docker-Content-Digest'] = manifest_digest
|
||||
response.headers['Location'] = url_for('v2.fetch_manifest_by_digest',
|
||||
repository='%s/%s' % (namespace, repo_name),
|
||||
repository='%s/%s' % (namespace_name, repo_name),
|
||||
manifest_ref=manifest_digest)
|
||||
return response
|
||||
|
||||
|
||||
@v2_bp.route(MANIFEST_DIGEST_ROUTE, methods=['DELETE'])
|
||||
@process_registry_jwt_auth
|
||||
@parse_repository_name
|
||||
@parse_repository_name()
|
||||
@require_repo_write
|
||||
@anon_protect
|
||||
def delete_manifest_by_digest(namespace, repo_name, manifest_ref):
|
||||
def delete_manifest_by_digest(namespace_name, repo_name, manifest_ref):
|
||||
""" Delete the manifest specified by the digest. Note: there is no equivalent
|
||||
method for deleting by tag name because it is forbidden by the spec.
|
||||
"""
|
||||
try:
|
||||
manifest = model.tag.load_manifest_by_digest(namespace, repo_name, manifest_ref)
|
||||
manifest = model.tag.load_manifest_by_digest(namespace_name, repo_name, manifest_ref)
|
||||
except model.InvalidManifestException:
|
||||
# Without a tag name to reference, we can't make an attempt to generate the manifest
|
||||
raise ManifestUnknown()
|
||||
|
||||
# Mark the tag as no longer alive.
|
||||
try:
|
||||
model.tag.delete_tag(namespace, repo_name, manifest.tag.name)
|
||||
model.tag.delete_tag(namespace_name, repo_name, manifest.tag.name)
|
||||
except model.DataModelException:
|
||||
# Tag is not alive.
|
||||
raise ManifestUnknown()
|
||||
|
@ -494,15 +498,15 @@ def delete_manifest_by_digest(namespace, repo_name, manifest_ref):
|
|||
return make_response('', 202)
|
||||
|
||||
|
||||
def _generate_and_store_manifest(namespace, repo_name, tag_name):
|
||||
def _generate_and_store_manifest(namespace_name, repo_name, tag_name):
|
||||
# First look up the tag object and its ancestors
|
||||
image = model.tag.get_tag_image(namespace, repo_name, tag_name)
|
||||
parents = model.image.get_parent_images(namespace, repo_name, image)
|
||||
image = model.tag.get_tag_image(namespace_name, repo_name, tag_name)
|
||||
parents = model.image.get_parent_images(namespace_name, repo_name, image)
|
||||
|
||||
# If the manifest is being generated under the library namespace, then we make its namespace
|
||||
# empty.
|
||||
manifest_namespace = namespace
|
||||
if features.LIBRARY_SUPPORT and namespace == app.config['LIBRARY_NAMESPACE']:
|
||||
manifest_namespace = namespace_name
|
||||
if features.LIBRARY_SUPPORT and namespace_name == app.config['LIBRARY_NAMESPACE']:
|
||||
manifest_namespace = ''
|
||||
|
||||
# Create and populate the manifest builder
|
||||
|
@ -520,13 +524,12 @@ def _generate_and_store_manifest(namespace, repo_name, tag_name):
|
|||
# Write the manifest to the DB. If an existing manifest already exists, return the
|
||||
# one found.
|
||||
try:
|
||||
return model.tag.associate_generated_tag_manifest(namespace, repo_name, tag_name,
|
||||
return model.tag.associate_generated_tag_manifest(namespace_name, repo_name, tag_name,
|
||||
manifest.digest, manifest.bytes)
|
||||
except IntegrityError as ie:
|
||||
logger.debug('Got integrity error: %s', ie)
|
||||
try:
|
||||
return model.tag.load_tag_manifest(namespace, repo_name, tag_name)
|
||||
return model.tag.load_tag_manifest(namespace_name, repo_name, tag_name)
|
||||
except model.InvalidManifestException:
|
||||
logger.exception('Exception when generating manifest')
|
||||
raise model.DataModelException('Could not load or generate manifest')
|
||||
|
||||
|
|
Reference in a new issue