Ejemplo n.º 1
0
    def put(self, request, path, pk=None):
        """
        Responds with the actual manifest
        """
        _, repository = self.get_dr_push(request, path)
        # iterate over all the layers and create
        chunk = request.META["wsgi.input"]
        artifact = self.receive_artifact(chunk)
        with storage.open(artifact.file.name) as artifact_file:
            raw_data = artifact_file.read()
        content_data = json.loads(raw_data)
        config_layer = content_data.get("config")
        config_blob = models.Blob.objects.get(
            digest=config_layer.get("digest"))

        manifest = models.Manifest(
            digest="sha256:{id}".format(id=artifact.sha256),
            schema_version=2,
            media_type=request.content_type,
            config_blob=config_blob,
        )
        try:
            manifest.save()
        except IntegrityError:
            manifest = models.Manifest.objects.get(digest=manifest.digest)
        ca = ContentArtifact(artifact=artifact,
                             content=manifest,
                             relative_path=manifest.digest)
        try:
            ca.save()
        except IntegrityError:
            pass
        layers = content_data.get("layers")
        blobs = []
        for layer in layers:
            blobs.append(layer.get("digest"))
        blobs_qs = models.Blob.objects.filter(digest__in=blobs)
        thru = []
        for blob in blobs_qs:
            thru.append(
                models.BlobManifest(manifest=manifest, manifest_blob=blob))
        models.BlobManifest.objects.bulk_create(objs=thru,
                                                ignore_conflicts=True,
                                                batch_size=1000)
        tag = models.Tag(name=pk, tagged_manifest=manifest)
        try:
            tag.save()
        except IntegrityError:
            pass
        with repository.new_version() as new_version:
            new_version.add_content(
                models.Manifest.objects.filter(digest=manifest.digest))
            new_version.remove_content(
                models.Tag.objects.filter(name=tag.name))
            new_version.add_content(
                models.Tag.objects.filter(name=tag.name,
                                          tagged_manifest=manifest))
        return ManifestResponse(manifest, path, request, status=201)
Ejemplo n.º 2
0
    def put(self, request, path, pk=None):
        """
        Responds with the actual manifest
        """
        _, repository = self.get_dr_push(request, path)
        # iterate over all the layers and create
        chunk = request.META["wsgi.input"]
        artifact = self.receive_artifact(chunk)
        with storage.open(artifact.file.name) as artifact_file:
            raw_data = artifact_file.read()
        content_data = json.loads(raw_data)
        config_layer = content_data.get("config")
        config_blob = models.Blob.objects.get(digest=config_layer.get("digest"))

        manifest = models.Manifest(
            digest="sha256:{id}".format(id=artifact.sha256),
            schema_version=2,
            media_type=request.content_type,
            config_blob=config_blob,
        )
        try:
            manifest.save()
        except IntegrityError:
            manifest = models.Manifest.objects.get(digest=manifest.digest)
        ca = ContentArtifact(artifact=artifact, content=manifest, relative_path=manifest.digest)
        try:
            ca.save()
        except IntegrityError:
            pass
        layers = content_data.get("layers")
        blobs = []
        for layer in layers:
            blobs.append(layer.get("digest"))
        blobs_qs = models.Blob.objects.filter(digest__in=blobs)
        thru = []
        for blob in blobs_qs:
            thru.append(models.BlobManifest(manifest=manifest, manifest_blob=blob))
        models.BlobManifest.objects.bulk_create(objs=thru, ignore_conflicts=True, batch_size=1000)
        tag = models.Tag(name=pk, tagged_manifest=manifest)
        try:
            tag.save()
        except IntegrityError:
            tag = models.Tag.objects.get(name=tag.name, tagged_manifest=manifest)

        tags_to_remove = models.Tag.objects.filter(
            pk__in=repository.latest_version().content.all(), name=tag
        ).exclude(tagged_manifest=manifest)
        dispatched_task = dispatch(
            add_and_remove,
            [repository],
            kwargs={
                "repository_pk": str(repository.pk),
                "add_content_units": [str(tag.pk), str(manifest.pk)],
                "remove_content_units": [str(pk) for pk in tags_to_remove.values_list("pk")],
            },
        )

        # Wait a small amount of time
        for dummy in range(3):
            time.sleep(1)
            task = Task.objects.get(pk=dispatched_task.pk)
            if task.state == "completed":
                task.delete()
                return ManifestResponse(manifest, path, request, status=201)
            elif task.state in ["waiting", "running"]:
                continue
            else:
                error = task.error
                task.delete()
                raise Exception(str(error))
        raise Throttled()
Ejemplo n.º 3
0
    def put(self, request, path, pk=None):
        """
        Responds with the actual manifest
        """
        # when a user uploads a manifest list with zero listed manifests (no blobs were uploaded
        # before) and the specified repository has not been created yet, create the repository
        # without raising an error
        create_new_repo = request.content_type in (
            models.MEDIA_TYPE.MANIFEST_LIST,
            models.MEDIA_TYPE.INDEX_OCI,
        )
        _, repository = self.get_dr_push(request, path, create=create_new_repo)
        # iterate over all the layers and create
        chunk = request.META["wsgi.input"]
        artifact = self.receive_artifact(chunk)
        manifest_digest = "sha256:{id}".format(id=artifact.sha256)

        # oci format might not contain mediaType in the manifest.json, docker should
        # hence need to check request content type
        if request.content_type not in (
                models.MEDIA_TYPE.MANIFEST_V2,
                models.MEDIA_TYPE.MANIFEST_OCI,
                models.MEDIA_TYPE.MANIFEST_LIST,
                models.MEDIA_TYPE.INDEX_OCI,
        ):
            raise ManifestInvalid(digest=manifest_digest)

        with storage.open(artifact.file.name) as artifact_file:
            raw_data = artifact_file.read()

        content_data = json.loads(raw_data)

        if request.content_type in (
                models.MEDIA_TYPE.MANIFEST_LIST,
                models.MEDIA_TYPE.INDEX_OCI,
        ):
            manifests = {}
            for manifest in content_data.get("manifests"):
                manifests[manifest["digest"]] = manifest["platform"]

            digests = set(manifests.keys())
            found_manifests = models.Manifest.objects.filter(
                digest__in=digests)

            if (len(manifests) - found_manifests.count()) != 0:
                ManifestInvalid(digest=manifest_digest)

            manifest_list = self._save_manifest(artifact, manifest_digest,
                                                request.content_type)

            manifests_to_list = []
            for manifest in found_manifests:
                platform = manifests[manifest.digest]
                manifest_to_list = models.ManifestListManifest(
                    manifest_list=manifest,
                    image_manifest=manifest_list,
                    architecture=platform["architecture"],
                    os=platform["os"],
                    features=platform.get("features", ""),
                    variant=platform.get("variant", ""),
                    os_version=platform.get("os.version", ""),
                    os_features=platform.get("os.features", ""),
                )
                manifests_to_list.append(manifest_to_list)

            models.ManifestListManifest.objects.bulk_create(
                manifests_to_list, ignore_conflicts=True, batch_size=1000)
            manifest = manifest_list
        else:
            # both docker/oci format should contain config, digest, mediaType, size
            config_layer = content_data.get("config")
            config_media_type = config_layer.get("mediaType")
            config_digest = config_layer.get("digest")
            if config_media_type not in (
                    models.MEDIA_TYPE.CONFIG_BLOB,
                    models.MEDIA_TYPE.CONFIG_BLOB_OCI,
            ):
                raise BlobInvalid(digest=config_digest)

            try:
                config_blob = models.Blob.objects.get(
                    digest=config_digest,
                    pk__in=repository.latest_version().content)
            except models.Blob.DoesNotExist:
                raise BlobInvalid(digest=config_digest)

            # both docker/oci format should contain layers, digest, media_type, size
            layers = content_data.get("layers")
            blobs = set()
            for layer in layers:
                media_type = layer.get("mediaType")
                urls = layer.get("urls")
                digest = layer.get("digest")
                if (media_type in (
                        models.MEDIA_TYPE.FOREIGN_BLOB,
                        models.MEDIA_TYPE.FOREIGN_BLOB_OCI,
                ) and not urls):
                    raise ManifestInvalid(digest=manifest_digest)
                if media_type not in (
                        models.MEDIA_TYPE.REGULAR_BLOB,
                        models.MEDIA_TYPE.REGULAR_BLOB_OCI,
                ):
                    raise BlobInvalid(digest=digest)
                blobs.add(digest)

            blobs_qs = models.Blob.objects.filter(
                digest__in=blobs, pk__in=repository.latest_version().content)
            if (len(blobs) - blobs_qs.count()) != 0:
                raise ManifestInvalid(digest=manifest_digest)

            manifest = self._save_manifest(artifact, manifest_digest,
                                           request.content_type, config_blob)

            thru = []
            for blob in blobs_qs:
                thru.append(
                    models.BlobManifest(manifest=manifest, manifest_blob=blob))
            models.BlobManifest.objects.bulk_create(objs=thru,
                                                    ignore_conflicts=True,
                                                    batch_size=1000)

        tag = models.Tag(name=pk, tagged_manifest=manifest)
        try:
            tag.save()
        except IntegrityError:
            tag = models.Tag.objects.get(name=tag.name,
                                         tagged_manifest=manifest)
            tag.touch()

        tags_to_remove = models.Tag.objects.filter(
            pk__in=repository.latest_version().content.all(),
            name=tag).exclude(tagged_manifest=manifest)
        dispatched_task = dispatch(
            add_and_remove,
            exclusive_resources=[repository],
            kwargs={
                "repository_pk":
                str(repository.pk),
                "add_content_units": [str(tag.pk),
                                      str(manifest.pk)],
                "remove_content_units":
                [str(pk) for pk in tags_to_remove.values_list("pk")],
            },
        )

        if has_task_completed(dispatched_task):
            return ManifestResponse(manifest, path, request, status=201)