Skip parsing the manifest where applicable

Instead of always parsing (like we did previously), we now only parse the manifest if conversion is necessary. This should save significant CPU.
This commit is contained in:
Joseph Schorr 2019-01-11 15:24:21 -05:00
parent 396ce21020
commit 7f068c3fc3

View file

@ -57,15 +57,9 @@ def fetch_manifest_by_tagname(namespace_name, repo_name, manifest_ref):
# Something went wrong. # Something went wrong.
raise ManifestInvalid() raise ManifestInvalid()
try: manifest_bytes, manifest_digest, manifest_media_type = _rewrite_schema_if_necessary(
parsed = manifest.get_parsed_manifest() namespace_name, repo_name, manifest_ref, manifest)
except ManifestException: if manifest_bytes is None:
logger.exception('Got exception when trying to parse manifest `%s`', manifest_ref)
raise ManifestInvalid()
supported = _rewrite_schema_if_necessary(namespace_name, repo_name, manifest_ref, manifest,
parsed)
if supported is None:
raise ManifestUnknown() raise ManifestUnknown()
track_and_log('pull_repo', repository_ref, analytics_name='pull_repo_100x', analytics_sample=0.01, track_and_log('pull_repo', repository_ref, analytics_name='pull_repo_100x', analytics_sample=0.01,
@ -73,11 +67,11 @@ def fetch_manifest_by_tagname(namespace_name, repo_name, manifest_ref):
metric_queue.repository_pull.Inc(labelvalues=[namespace_name, repo_name, 'v2', True]) metric_queue.repository_pull.Inc(labelvalues=[namespace_name, repo_name, 'v2', True])
return Response( return Response(
supported.bytes.as_unicode(), manifest_bytes.as_unicode(),
status=200, status=200,
headers={ headers={
'Content-Type': '%s; charset=utf-8' % supported.media_type, 'Content-Type': '%s; charset=utf-8' % manifest_media_type,
'Docker-Content-Digest': supported.digest, 'Docker-Content-Digest': manifest_digest,
}, },
) )
@ -96,43 +90,43 @@ def fetch_manifest_by_digest(namespace_name, repo_name, manifest_ref):
if manifest is None: if manifest is None:
raise ManifestUnknown() raise ManifestUnknown()
try: manifest_bytes, manifest_digest, manifest_media_type = _rewrite_schema_if_necessary(
parsed = manifest.get_parsed_manifest() namespace_name, repo_name, '$digest', manifest)
except ManifestException: if manifest_digest is None:
logger.exception('Got exception when trying to parse manifest `%s`', manifest_ref)
raise ManifestInvalid()
supported = _rewrite_schema_if_necessary(namespace_name, repo_name, '$digest', manifest,
parsed)
if supported is None:
raise ManifestUnknown() raise ManifestUnknown()
track_and_log('pull_repo', repository_ref, manifest_digest=manifest_ref) track_and_log('pull_repo', repository_ref, manifest_digest=manifest_ref)
metric_queue.repository_pull.Inc(labelvalues=[namespace_name, repo_name, 'v2', True]) metric_queue.repository_pull.Inc(labelvalues=[namespace_name, repo_name, 'v2', True])
return Response(supported.bytes.as_unicode(), status=200, headers={ return Response(manifest_bytes.as_unicode(), status=200, headers={
'Content-Type': '%s; charset=utf-8' % supported.media_type, 'Content-Type': '%s; charset=utf-8' % manifest_media_type,
'Docker-Content-Digest': supported.digest, 'Docker-Content-Digest': manifest_digest,
}) })
def _rewrite_schema_if_necessary(namespace_name, repo_name, tag_name, manifest, parsed): def _rewrite_schema_if_necessary(namespace_name, repo_name, tag_name, manifest):
# As per the Docker protocol, if the manifest is not schema version 1 and the manifest's # As per the Docker protocol, if the manifest is not schema version 1 and the manifest's
# media type is not in the Accept header, we return a schema 1 version of the manifest for # media type is not in the Accept header, we return a schema 1 version of the manifest for
# the amd64+linux platform, if any, or None if none. # the amd64+linux platform, if any, or None if none.
# See: https://docs.docker.com/registry/spec/manifest-v2-2 # See: https://docs.docker.com/registry/spec/manifest-v2-2
mimetypes = [mimetype for mimetype, _ in request.accept_mimetypes] mimetypes = [mimetype for mimetype, _ in request.accept_mimetypes]
if parsed.media_type in mimetypes: if manifest.media_type in mimetypes:
return parsed return manifest.internal_manifest_bytes, manifest.digest, manifest.media_type
logger.debug('Manifest `%s` not compatible against %s; checking for conversion', manifest.digest,
request.accept_mimetypes)
converted = registry_model.convert_manifest(manifest, namespace_name, repo_name, tag_name, converted = registry_model.convert_manifest(manifest, namespace_name, repo_name, tag_name,
mimetypes, storage) mimetypes, storage)
if converted is not None: if converted is not None:
return converted return converted.bytes, converted.digest, converted.media_type
# For back-compat, we always default to schema 1 if the manifest could not be converted. # For back-compat, we always default to schema 1 if the manifest could not be converted.
return registry_model.get_schema1_parsed_manifest(manifest, namespace_name, repo_name, tag_name, schema1 = registry_model.get_schema1_parsed_manifest(manifest, namespace_name, repo_name,
storage) tag_name, storage)
if schema1 is None:
return None, None, None
return schema1.bytes, schema1.digest, schema1.media_type
def _reject_manifest2_schema2(func): def _reject_manifest2_schema2(func):