Beispiel #1
0
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,
            ),
        },
    )
Beispiel #2
0
    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)
Beispiel #3
0
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)})
Beispiel #4
0
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
Beispiel #5
0
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
Beispiel #6
0
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,
        },
    )
Beispiel #7
0
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})
Beispiel #8
0
    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)
Beispiel #9
0
    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)
Beispiel #10
0
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,},
    )