def write_manifest_by_digest(namespace_name, repo_name, manifest_ref): parsed = _parse_manifest() if parsed.digest != manifest_ref: raise ManifestInvalid(detail={"message": "manifest digest mismatch"}) if parsed.schema_version != 2: return _write_manifest_and_log(namespace_name, repo_name, parsed.tag, parsed) # If the manifest is schema version 2, then this cannot be a normal tag-based push, as the # manifest does not contain the tag and this call was not given a tag name. Instead, we write the # manifest with a temporary tag, as it is being pushed as part of a call for a manifest list. repository_ref = registry_model.lookup_repository(namespace_name, repo_name) if repository_ref is None: raise NameUnknown() expiration_sec = app.config["PUSH_TEMP_TAG_EXPIRATION_SEC"] manifest = registry_model.create_manifest_with_temp_tag( repository_ref, parsed, expiration_sec, storage ) if manifest is None: raise ManifestInvalid() return Response( "OK", status=202, headers={ "Docker-Content-Digest": manifest.digest, "Location": url_for( "v2.fetch_manifest_by_digest", repository="%s/%s" % (namespace_name, repo_name), manifest_ref=manifest.digest, ), }, )
def wrapped(*args, **kwargs): namespace_name = kwargs["namespace_name"] if (request.content_type and request.content_type != "application/json" and request.content_type not in DOCKER_SCHEMA1_CONTENT_TYPES | DOCKER_SCHEMA2_CONTENT_TYPES | OCI_CONTENT_TYPES): raise ManifestInvalid( detail={"message": "manifest schema version not supported"}, http_status_code=415) if namespace_name not in app.config.get("V22_NAMESPACE_BLACKLIST", []): if request.content_type in OCI_CONTENT_TYPES: if (namespace_name not in app.config.get( "OCI_NAMESPACE_WHITELIST", []) and not features.GENERAL_OCI_SUPPORT): raise ManifestInvalid( detail={ "message": "manifest schema version not supported" }, http_status_code=415, ) return func(*args, **kwargs) 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"}, http_status_code=415) return func(*args, **kwargs)
def _validate_schema1_manifest(namespace: str, repo: str, manifest: DockerSchema1Manifest): # NOTE: These extra checks are needed for schema version 1 because the manifests # contain the repo namespace, name and tag name. if manifest.schema_version != 1: return if ( manifest.namespace == "" and features.LIBRARY_SUPPORT and namespace == app.config["LIBRARY_NAMESPACE"] ): pass elif manifest.namespace != namespace: raise NameInvalid( message="namespace name does not match manifest", detail={ "message": "namespace name `%s` does not match `%s` in manifest" % (namespace, manifest.namespace), }, ) if manifest.repo_name != repo: raise NameInvalid( message="repository name does not match manifest", detail={ "message": "repository name `%s` does not match `%s` in manifest" % (repo, manifest.repo_name), }, ) try: if not manifest.layers: raise ManifestInvalid(detail={"message": "manifest does not reference any layers"}) except ManifestException as me: raise ManifestInvalid(detail={"message": str(me)})
def _write_manifest( namespace_name, repo_name, tag_name, manifest_impl, registry_model=registry_model ): # Ensure that the repository exists. repository_ref = registry_model.lookup_repository(namespace_name, repo_name) if repository_ref is None: raise NameUnknown("repository not found") # Create the manifest(s) and retarget the tag to point to it. try: manifest, tag = registry_model.create_manifest_and_retarget_tag( repository_ref, manifest_impl, tag_name, storage, raise_on_error=True ) except CreateManifestException as cme: raise ManifestInvalid(detail={"message": str(cme)}) except RetargetTagException as rte: raise ManifestInvalid(detail={"message": str(rte)}) if manifest is None: raise ManifestInvalid() if app.config.get("FEATURE_QUOTA_MANAGEMENT", False): quota = namespacequota.verify_namespace_quota_force_cache(repository_ref) if quota["severity_level"] == "Warning": namespacequota.notify_organization_admins(repository_ref, "quota_warning") elif quota["severity_level"] == "Reject": namespacequota.notify_organization_admins(repository_ref, "quota_error") raise QuotaExceeded() return repository_ref, manifest, tag
def _write_manifest(namespace_name, repo_name, tag_name, manifest_impl): # NOTE: These extra checks are needed for schema version 1 because the manifests # contain the repo namespace, name and tag name. if manifest_impl.schema_version == 1: if (manifest_impl.namespace == "" and features.LIBRARY_SUPPORT and namespace_name == app.config["LIBRARY_NAMESPACE"]): pass elif manifest_impl.namespace != namespace_name: raise NameInvalid( message="namespace name does not match manifest", detail={ "namespace name `%s` does not match `%s` in manifest" % (namespace_name, manifest_impl.namespace) }, ) if manifest_impl.repo_name != repo_name: raise NameInvalid( message="repository name does not match manifest", detail={ "repository name `%s` does not match `%s` in manifest" % (repo_name, manifest_impl.repo_name) }, ) try: if not manifest_impl.layers: raise ManifestInvalid( detail={ "message": "manifest does not reference any layers" }) except ManifestException as me: raise ManifestInvalid(detail={"message": str(me)}) # Ensure that the repository exists. repository_ref = registry_model.lookup_repository(namespace_name, repo_name) if repository_ref is None: raise NameUnknown() # Create the manifest(s) and retarget the tag to point to it. try: manifest, tag = registry_model.create_manifest_and_retarget_tag( repository_ref, manifest_impl, tag_name, storage, raise_on_error=True) except CreateManifestException as cme: raise ManifestInvalid(detail={"message": str(cme)}) except RetargetTagException as rte: raise ManifestInvalid(detail={"message": str(rte)}) if manifest is None: raise ManifestInvalid() return repository_ref, manifest, tag
def fetch_manifest_by_tagname(namespace_name, repo_name, manifest_ref, registry_model): try: repository_ref = registry_model.lookup_repository( namespace_name, repo_name, raise_on_error=True, manifest_ref=manifest_ref ) except RepositoryDoesNotExist as e: image_pulls.labels("v2", "tag", 404).inc() raise NameUnknown("repository not found") try: tag = registry_model.get_repo_tag(repository_ref, manifest_ref, raise_on_error=True) except TagDoesNotExist as e: if registry_model.has_expired_tag(repository_ref, manifest_ref): logger.debug( "Found expired tag %s for repository %s/%s", manifest_ref, namespace_name, repo_name ) msg = ( "Tag %s was deleted or has expired. To pull, revive via time machine" % manifest_ref ) image_pulls.labels("v2", "tag", 404).inc() raise TagExpired(msg) image_pulls.labels("v2", "tag", 404).inc() raise ManifestUnknown(str(e)) manifest = registry_model.get_manifest_for_tag(tag) if manifest is None: # Something went wrong. image_pulls.labels("v2", "tag", 400).inc() raise ManifestInvalid() try: manifest_bytes, manifest_digest, manifest_media_type = _rewrite_schema_if_necessary( namespace_name, repo_name, manifest_ref, manifest, registry_model ) except (ManifestException, ManifestDoesNotExist) as e: image_pulls.labels("v2", "tag", 404).inc() raise ManifestUnknown(str(e)) if manifest_bytes is None: image_pulls.labels("v2", "tag", 404).inc() raise ManifestUnknown() track_and_log( "pull_repo", repository_ref, analytics_name="pull_repo_100x", analytics_sample=0.01, tag=manifest_ref, ) image_pulls.labels("v2", "tag", 200).inc() return Response( manifest_bytes.as_unicode(), status=200, headers={ "Content-Type": manifest_media_type, "Docker-Content-Digest": manifest_digest, }, )
def _parse_manifest(): content_type = request.content_type or DOCKER_SCHEMA1_MANIFEST_CONTENT_TYPE if content_type == "application/json": # For back-compat. content_type = DOCKER_SCHEMA1_MANIFEST_CONTENT_TYPE try: return parse_manifest_from_bytes(Bytes.for_string_or_unicode(request.data), content_type) except ManifestException as me: logger.exception("failed to parse manifest when writing by tagname") raise ManifestInvalid(detail={"message": "failed to parse manifest: %s" % me})
def wrapped(*args, **kwargs): namespace_name = kwargs['namespace_name'] if registry_model.supports_schema2(namespace_name): return func(*args, **kwargs) 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'}, http_status_code=415) return func(*args, **kwargs)
def wrapped(*args, **kwargs): namespace_name = kwargs["namespace_name"] if registry_model.supports_schema2(namespace_name) and namespace_name not in app.config.get( "V22_NAMESPACE_BLACKLIST", [] ): return func(*args, **kwargs) 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"}, http_status_code=415 ) return func(*args, **kwargs)
def fetch_manifest_by_tagname(namespace_name, repo_name, manifest_ref): repository_ref = registry_model.lookup_repository(namespace_name, repo_name) if repository_ref is None: raise NameUnknown() tag = registry_model.get_repo_tag(repository_ref, manifest_ref) if tag is None: if registry_model.has_expired_tag(repository_ref, manifest_ref): logger.debug( "Found expired tag %s for repository %s/%s", manifest_ref, namespace_name, repo_name ) msg = ( "Tag %s was deleted or has expired. To pull, revive via time machine" % manifest_ref ) raise TagExpired(msg) raise ManifestUnknown() manifest = registry_model.get_manifest_for_tag(tag, backfill_if_necessary=True) if manifest is None: # Something went wrong. raise ManifestInvalid() manifest_bytes, manifest_digest, manifest_media_type = _rewrite_schema_if_necessary( namespace_name, repo_name, manifest_ref, manifest ) if manifest_bytes is None: raise ManifestUnknown() track_and_log( "pull_repo", repository_ref, analytics_name="pull_repo_100x", analytics_sample=0.01, tag=manifest_ref, ) metric_queue.repository_pull.Inc(labelvalues=[namespace_name, repo_name, "v2", True]) return Response( manifest_bytes.as_unicode(), status=200, headers={"Content-Type": manifest_media_type, "Docker-Content-Digest": manifest_digest,}, )