package convert import ( "time" godigest "github.com/opencontainers/go-digest" ispec "github.com/opencontainers/image-spec/specs-go/v1" "google.golang.org/protobuf/types/known/timestamppb" "zotregistry.dev/zot/pkg/common" proto_go "zotregistry.dev/zot/pkg/meta/proto/gen" mTypes "zotregistry.dev/zot/pkg/meta/types" ) func GetProtoRepoMeta(repo mTypes.RepoMeta) *proto_go.RepoMeta { return &proto_go.RepoMeta{ Name: repo.Name, Tags: GetProtoTags(repo.Tags), Statistics: GetProtoStatistics(repo.Statistics), Signatures: GetProtoSignatures(repo.Signatures), Referrers: GetProtoReferrers(repo.Referrers), Size: repo.Size, Vendors: repo.Vendors, Platforms: GetProtoPlatforms(repo.Platforms), LastUpdatedImage: GetProtoLastUpdatedImage(repo.LastUpdatedImage), Stars: int32(repo.StarCount), //nolint:gosec // ignore overflow Downloads: int32(repo.DownloadCount), //nolint:gosec // ignore overflow } } func GetProtoImageMeta(imageMeta mTypes.ImageMeta) *proto_go.ImageMeta { switch imageMeta.MediaType { case ispec.MediaTypeImageManifest: if len(imageMeta.Manifests) == 0 { return nil } manifestData := imageMeta.Manifests[0] return GetProtoImageManifestData(manifestData.Manifest, manifestData.Config, manifestData.Size, manifestData.Digest.String()) case ispec.MediaTypeImageIndex: if imageMeta.Index == nil { return nil } return GetProtoImageIndexMeta(*imageMeta.Index, imageMeta.Size, imageMeta.Digest.String()) default: return nil } } func GetProtoImageManifestData(manifestContent ispec.Manifest, configContent ispec.Image, size int64, digest string, ) *proto_go.ImageMeta { return &proto_go.ImageMeta{ MediaType: ispec.MediaTypeImageManifest, Manifests: []*proto_go.ManifestMeta{GetProtoManifestMeta(manifestContent, configContent, size, digest)}, Index: nil, } } func GetProtoManifestMeta(manifestContent ispec.Manifest, configContent ispec.Image, size int64, digest string, ) *proto_go.ManifestMeta { return &proto_go.ManifestMeta{ Digest: digest, Size: size, Manifest: &proto_go.Manifest{ Versioned: &proto_go.Versioned{SchemaVersion: int32(manifestContent.SchemaVersion)}, //nolint:gosec,lll // ignore overflow Config: &proto_go.Descriptor{ Digest: manifestContent.Config.Digest.String(), Size: manifestContent.Config.Size, MediaType: manifestContent.Config.MediaType, }, MediaType: ref(ispec.MediaTypeImageManifest), ArtifactType: &manifestContent.ArtifactType, Layers: getProtoManifestLayers(manifestContent.Layers), Subject: getProtoDesc(manifestContent.Subject), Annotations: manifestContent.Annotations, }, Config: &proto_go.Image{ Created: GetProtoTime(configContent.Created), Author: &configContent.Author, Platform: GetProtoPlatform(&configContent.Platform), Config: &proto_go.ImageConfig{ User: configContent.Config.User, ExposedPorts: getProtoExposedPorts(configContent.Config.ExposedPorts), Env: configContent.Config.Env, Entrypoint: configContent.Config.Entrypoint, Cmd: configContent.Config.Cmd, Volumes: getProtoConfigVolumes(configContent.Config.Volumes), WorkingDir: &configContent.Config.WorkingDir, Labels: configContent.Config.Labels, StopSignal: &configContent.Config.StopSignal, }, RootFS: &proto_go.RootFS{ Type: configContent.RootFS.Type, DiffIDs: getProtoDiffIDs(configContent.RootFS.DiffIDs), }, History: getProtoHistory(configContent.History), }, } } func GetProtoImageIndexMeta(indexContent ispec.Index, size int64, digest string) *proto_go.ImageMeta { return &proto_go.ImageMeta{ MediaType: ispec.MediaTypeImageIndex, Index: &proto_go.IndexMeta{ Size: size, Digest: digest, Index: &proto_go.Index{ Versioned: &proto_go.Versioned{SchemaVersion: int32(indexContent.Versioned.SchemaVersion)}, //nolint:gosec,lll // ignore overflow MediaType: ref(ispec.MediaTypeImageIndex), ArtifactType: ref(common.GetIndexArtifactType(indexContent)), Manifests: getProtoManifestList(indexContent.Manifests), Subject: getProtoDesc(indexContent.Subject), Annotations: indexContent.Annotations, }, }, } } func GetProtoStatistics(stats map[mTypes.ImageDigest]mTypes.DescriptorStatistics, ) map[mTypes.ImageDigest]*proto_go.DescriptorStatistics { results := map[mTypes.ImageDigest]*proto_go.DescriptorStatistics{} for digest, stat := range stats { results[digest] = &proto_go.DescriptorStatistics{ DownloadCount: int32(stat.DownloadCount), //nolint:gosec // ignore overflow LastPullTimestamp: timestamppb.New(stat.LastPullTimestamp), PushTimestamp: timestamppb.New(stat.PushTimestamp), PushedBy: stat.PushedBy, } } return results } func GetProtoPlatforms(platforms []ispec.Platform) []*proto_go.Platform { result := []*proto_go.Platform{} for i := range platforms { result = append(result, &proto_go.Platform{ OS: platforms[i].OS, Architecture: platforms[i].Architecture, }) } return result } func GetProtoReferrers(refs map[string][]mTypes.ReferrerInfo) map[string]*proto_go.ReferrersInfo { results := map[string]*proto_go.ReferrersInfo{} for digest, ref := range refs { referrersInfoList := []*proto_go.ReferrerInfo{} for _, dbRef := range ref { referrersInfoList = append(referrersInfoList, GetProtoReferrerInfo(dbRef)) } results[digest] = &proto_go.ReferrersInfo{List: referrersInfoList} } return results } func GetProtoSignatures(sigs map[string]mTypes.ManifestSignatures) map[string]*proto_go.ManifestSignatures { results := map[string]*proto_go.ManifestSignatures{} for digest, dbSignatures := range sigs { imageSignatures := &proto_go.ManifestSignatures{Map: map[string]*proto_go.SignaturesInfo{}} for signatureName, signatureInfo := range dbSignatures { imageSignatures.Map[signatureName] = &proto_go.SignaturesInfo{List: GetProtoSignaturesInfo(signatureInfo)} } results[digest] = imageSignatures } return results } func GetProtoSignaturesInfo(sigsInfo []mTypes.SignatureInfo) []*proto_go.SignatureInfo { results := []*proto_go.SignatureInfo{} for _, sigInfo := range sigsInfo { results = append(results, &proto_go.SignatureInfo{ SignatureManifestDigest: sigInfo.SignatureManifestDigest, LayersInfo: GetProtoLayersInfo(sigInfo.LayersInfo), }) } return results } func GetProtoLayersInfo(layersInfo []mTypes.LayerInfo) []*proto_go.LayersInfo { result := make([]*proto_go.LayersInfo, 0, len(layersInfo)) for _, layerInfo := range layersInfo { result = append(result, &proto_go.LayersInfo{ LayerDigest: layerInfo.LayerDigest, LayerContent: layerInfo.LayerContent, SignatureKey: layerInfo.SignatureKey, Signer: layerInfo.Signer, Date: timestamppb.New(layerInfo.Date), }) } return result } func getProtoManifestLayers(layers []ispec.Descriptor) []*proto_go.Descriptor { protoLayers := []*proto_go.Descriptor{} for _, layer := range layers { layer := layer protoLayers = append(protoLayers, getProtoDesc(&layer)) } return protoLayers } func getProtoDesc(descriptor *ispec.Descriptor) *proto_go.Descriptor { if descriptor == nil { return nil } return &proto_go.Descriptor{ MediaType: descriptor.MediaType, Digest: descriptor.Digest.String(), Size: descriptor.Size, URLs: descriptor.URLs, Annotations: descriptor.Annotations, Data: descriptor.Data, Platform: GetProtoPlatform(descriptor.Platform), ArtifactType: &descriptor.ArtifactType, } } func getProtoManifestList(manifests []ispec.Descriptor) []*proto_go.Descriptor { result := make([]*proto_go.Descriptor, 0, len(manifests)) for _, manifest := range manifests { result = append(result, &proto_go.Descriptor{ MediaType: manifest.MediaType, Digest: manifest.Digest.String(), Size: manifest.Size, URLs: manifest.URLs, Annotations: manifest.Annotations, Data: manifest.Data, Platform: GetProtoPlatform(manifest.Platform), ArtifactType: ref(manifest.ArtifactType), }) } return result } func GetProtoPlatform(platform *ispec.Platform) *proto_go.Platform { if platform == nil { return nil } return &proto_go.Platform{ Architecture: platform.Architecture, OS: platform.OS, OSVersion: ref(platform.OSVersion), OSFeatures: platform.OSFeatures, Variant: ref(platform.Variant), } } func getProtoHistory(historySlice []ispec.History) []*proto_go.History { protoHistory := []*proto_go.History{} for _, history := range historySlice { history := history protoHistory = append(protoHistory, &proto_go.History{ Created: GetProtoTime(history.Created), CreatedBy: &history.CreatedBy, Author: &history.Author, Comment: &history.Comment, EmptyLayer: &history.EmptyLayer, }) } return protoHistory } func getProtoDiffIDs(digests []godigest.Digest) []string { digestsStr := []string{} for _, digest := range digests { digestsStr = append(digestsStr, digest.String()) } return digestsStr } func getProtoExposedPorts(exposedPorts map[string]struct{}) map[string]*proto_go.EmptyMessage { protoPorts := map[string]*proto_go.EmptyMessage{} for i := range exposedPorts { protoPorts[i] = &proto_go.EmptyMessage{} } return protoPorts } func getProtoConfigVolumes(volumes map[string]struct{}) map[string]*proto_go.EmptyMessage { protoVolumes := map[string]*proto_go.EmptyMessage{} for i := range volumes { protoVolumes[i] = &proto_go.EmptyMessage{} } return protoVolumes } func GetProtoReferrerInfo(referrer mTypes.ReferrerInfo) *proto_go.ReferrerInfo { return &proto_go.ReferrerInfo{ Digest: referrer.Digest, MediaType: referrer.MediaType, ArtifactType: referrer.ArtifactType, Size: int64(referrer.Size), Annotations: referrer.Annotations, } } func GetProtoTime(time *time.Time) *timestamppb.Timestamp { if time == nil { return nil } return timestamppb.New(*time) } func GetProtoTags(tags map[mTypes.Tag]mTypes.Descriptor) map[mTypes.Tag]*proto_go.TagDescriptor { resultMap := map[mTypes.Tag]*proto_go.TagDescriptor{} for tag, tagDescriptor := range tags { resultMap[tag] = &proto_go.TagDescriptor{ Digest: tagDescriptor.Digest, MediaType: tagDescriptor.MediaType, } } return resultMap } func GetProtoLastUpdatedImage(lastUpdatedImage *mTypes.LastUpdatedImage) *proto_go.RepoLastUpdatedImage { if lastUpdatedImage == nil { return nil } return &proto_go.RepoLastUpdatedImage{ LastUpdated: GetProtoTime(lastUpdatedImage.LastUpdated), MediaType: lastUpdatedImage.MediaType, Digest: lastUpdatedImage.Digest, Tag: lastUpdatedImage.Tag, } } func GetProtoEarlierUpdatedImage(repoLastImage *proto_go.RepoLastUpdatedImage, lastImage *proto_go.RepoLastUpdatedImage, ) *proto_go.RepoLastUpdatedImage { if repoLastImage == nil { return lastImage } if lastImage == nil || lastImage.LastUpdated == nil { return repoLastImage } if repoLastImage.LastUpdated == nil { return lastImage } if repoLastImage.LastUpdated.AsTime().Before(lastImage.LastUpdated.AsTime()) { return lastImage } return repoLastImage } func ref[T any](input T) *T { ref := input return &ref }