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)
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()
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)