/* * Copyright 2026 Oleg Borodin * * This work is published and licensed under a Creative Commons * Attribution-NonCommercial-NoDerivatives 4.0 International License. * * Distribution of this work is permitted, but commercial use and * modifications are strictly prohibited. */ package imageoper import ( "encoding/json" "mstore/pkg/auxoci" "mstore/pkg/auxtool" "mstore/pkg/auxuuid" "mstore/pkg/descr" ocidigest "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) const ( oiiMediaType = "application/vnd.oci.image.index.v1+json" oimMediaType = "application/vnd.oci.image.manifest.v1+json" ) func indexFromManigestDescrs(manifestDescrs []descr.Manifest) (ocispec.Index, string, error) { var err error var payload string index := ocispec.Index{ MediaType: oiiMediaType, Manifests: make([]ocispec.Descriptor, 0), } index.Versioned.SchemaVersion = 2 for _, manifestDescr := range manifestDescrs { var ociManifest ocispec.Manifest ociManifest.Subject = &ocispec.Descriptor{} ociManifest.Subject.Platform = &ocispec.Platform{} ociManifest.Config = ocispec.Descriptor{} ociManifest.Config.Platform = &ocispec.Platform{} err = json.Unmarshal([]byte(manifestDescr.Payload), &ociManifest) if err != nil { return index, payload, err } descriptor := ocispec.Descriptor{ MediaType: oimMediaType, Digest: auxoci.SHA256DigestFromString(manifestDescr.Payload), Size: int64(len(manifestDescr.Payload)), Platform: ociManifest.Subject.Platform, } index.Manifests = append(index.Manifests, descriptor) } indexBytes, err := json.Marshal(index) if err != nil { return index, payload, err } payload = string(indexBytes) return index, payload, err } func descrsFromManifest(name, reference string, manifest *ocispec.Manifest, rawManifest []byte) (descr.Manifest, []descr.Blob, error) { var err error manifestDescr := descr.Manifest{} //configDescr := descr.Blob{} blobDescrs := make([]descr.Blob, 0) timestamp := auxtool.TimeNow() // Make manifest descriptor manifestDigest := ocidigest.SHA256.FromBytes(rawManifest).String() manifestDescr = descr.Manifest{ ID: auxuuid.NewUUID(), Name: name, Reference: reference, Digest: manifestDigest, ContentType: manifest.MediaType, Payload: string(rawManifest), CreatedAt: timestamp, UpdatedAt: timestamp, } // Make config descriptor ociConfig := manifest.Config configDescr := descr.Blob{ ID: auxuuid.NewUUID(), Name: name, Reference: reference, MediaType: ociConfig.MediaType, Digest: string(ociConfig.Digest), Size: ociConfig.Size, CreatedAt: timestamp, UpdatedAt: timestamp, } blobDescrs = append(blobDescrs, configDescr) // Make blob descriptions layerMap := make(map[string]bool) for _, layer := range manifest.Layers { blobDescr := descr.Blob{ ID: auxuuid.NewUUID(), Name: name, Reference: reference, MediaType: layer.MediaType, Digest: string(layer.Digest), Size: layer.Size, CreatedAt: timestamp, UpdatedAt: timestamp, } _, alreadyAdded := layerMap[string(layer.Digest)] if !alreadyAdded { blobDescrs = append(blobDescrs, blobDescr) layerMap[string(layer.Digest)] = true } } return manifestDescr, blobDescrs, err } func layersDiff(name, reference string, existingManifest, incomingManifest *ocispec.Manifest, rawManifest []byte) ([]descr.Blob, []descr.Blob, error) { var err error newBlobDescrs := make([]descr.Blob, 0) delBlobDescrs := make([]descr.Blob, 0) // Calculating old layers delLayers := make([]ocispec.Descriptor, 0) for _, oldLayer := range existingManifest.Layers { uniq := true for _, newLayer := range incomingManifest.Layers { if oldLayer.Digest == newLayer.Digest { uniq = false } } if uniq { delLayers = append(delLayers, oldLayer) } } // Calculating new layers newLayers := make([]ocispec.Descriptor, 0) for _, newLayer := range incomingManifest.Layers { uniq := true for _, oldLayer := range existingManifest.Layers { if oldLayer.Digest == newLayer.Digest { uniq = false } } if uniq { newLayers = append(newLayers, newLayer) } } if incomingManifest.Config.Digest != existingManifest.Config.Digest { delLayers = append(delLayers, existingManifest.Config) newLayers = append(newLayers, incomingManifest.Config) } // Converting to new blob timestamp := auxtool.TimeNow() for _, layer := range newLayers { blobDescr := descr.Blob{ ID: auxuuid.NewUUID(), Name: name, Reference: reference, MediaType: layer.MediaType, Digest: string(layer.Digest), Size: layer.Size, CreatedAt: timestamp, UpdatedAt: timestamp, } newBlobDescrs = append(newBlobDescrs, blobDescr) } // Converting to old blobs for _, layer := range delLayers { blobDescr := descr.Blob{ ID: auxuuid.NewUUID(), Name: name, Reference: reference, MediaType: layer.MediaType, Digest: string(layer.Digest), Size: layer.Size, } delBlobDescrs = append(delBlobDescrs, blobDescr) } return newBlobDescrs, delBlobDescrs, err }