diff --git a/pkg/extensions/search/convert/annotations.go b/pkg/extensions/search/convert/annotations.go index 1ebb2cff..66f3eb0b 100644 --- a/pkg/extensions/search/convert/annotations.go +++ b/pkg/extensions/search/convert/annotations.go @@ -1,6 +1,8 @@ package convert import ( + "time" + ispec "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -18,6 +20,7 @@ const ( type ImageAnnotations struct { Description string + Created *time.Time Licenses string Title string Documentation string @@ -45,6 +48,17 @@ func GetAnnotationValue(annotations map[string]string, annotationKey, labelKey s return value } +func GetCreated(annotations map[string]string) *time.Time { + createdStr := GetAnnotationValue(annotations, ispec.AnnotationCreated, LabelAnnotationCreated) + + created, err := time.Parse(time.RFC3339, createdStr) + if err != nil { + return nil + } + + return &created +} + func GetDescription(annotations map[string]string) string { return GetAnnotationValue(annotations, ispec.AnnotationDescription, LabelAnnotationDescription) } @@ -82,6 +96,11 @@ func GetCategories(labels map[string]string) string { } func GetAnnotations(annotations, labels map[string]string) ImageAnnotations { + created := GetCreated(annotations) + if created == nil { + created = GetCreated(labels) + } + description := GetDescription(annotations) if description == "" { description = GetDescription(labels) @@ -123,6 +142,7 @@ func GetAnnotations(annotations, labels map[string]string) ImageAnnotations { } return ImageAnnotations{ + Created: created, Description: description, Title: title, Documentation: documentation, @@ -138,6 +158,11 @@ func GetIndexAnnotations( indexAnnotations map[string]string, annotationsFromManifest *ImageAnnotations, ) ImageAnnotations { + created := GetCreated(indexAnnotations) + if created == nil { + created = annotationsFromManifest.Created + } + description := GetDescription(indexAnnotations) if description == "" { description = annotationsFromManifest.Description @@ -179,6 +204,7 @@ func GetIndexAnnotations( } return ImageAnnotations{ + Created: created, Description: description, Title: title, Documentation: documentation, diff --git a/pkg/extensions/search/convert/convert_test.go b/pkg/extensions/search/convert/convert_test.go index 9f5160da..9e4106c9 100644 --- a/pkg/extensions/search/convert/convert_test.go +++ b/pkg/extensions/search/convert/convert_test.go @@ -69,6 +69,9 @@ func TestLabels(t *testing.T) { // Test various labels labels := make(map[string]string) + created := convert.GetCreated(labels) + So(created, ShouldBeNil) + desc := convert.GetDescription(labels) So(desc, ShouldEqual, "") @@ -81,11 +84,16 @@ func TestLabels(t *testing.T) { categories := convert.GetCategories(labels) So(categories, ShouldEqual, "") + expectedCreatedTime := time.Date(2010, 1, 1, 12, 0, 0, 0, time.UTC) + labels[ispec.AnnotationCreated] = expectedCreatedTime.Format(time.RFC3339) labels[ispec.AnnotationVendor] = "zot" labels[ispec.AnnotationDescription] = "zot-desc" labels[ispec.AnnotationLicenses] = "zot-license" labels[convert.AnnotationLabels] = "zot-labels" + created = convert.GetCreated(labels) + So(*created, ShouldEqual, expectedCreatedTime) + desc = convert.GetDescription(labels) So(desc, ShouldEqual, "zot-desc") @@ -101,10 +109,14 @@ func TestLabels(t *testing.T) { labels = make(map[string]string) // Use diff key + labels[convert.LabelAnnotationCreated] = expectedCreatedTime.Format(time.RFC3339) labels[convert.LabelAnnotationVendor] = "zot-vendor" labels[convert.LabelAnnotationDescription] = "zot-label-desc" labels[ispec.AnnotationLicenses] = "zot-label-license" + created = convert.GetCreated(labels) + So(*created, ShouldEqual, expectedCreatedTime) + desc = convert.GetDescription(labels) So(desc, ShouldEqual, "zot-label-desc") @@ -113,6 +125,14 @@ func TestLabels(t *testing.T) { vendor = convert.GetVendor(labels) So(vendor, ShouldEqual, "zot-vendor") + + labels = make(map[string]string) + + // Handle conversion errors + labels[ispec.AnnotationCreated] = "asd" + + created = convert.GetCreated(labels) + So(created, ShouldBeNil) }) } @@ -568,7 +588,13 @@ func TestIndexAnnotations(t *testing.T) { metaDB, err := boltdb.New(driver, log.NewLogger("debug", "")) So(err, ShouldBeNil) + defaultCreatedTime := *DefaultTimeRef() + configCreatedTime := time.Date(2009, 1, 1, 12, 0, 0, 0, time.UTC) + manifestCreatedTime := time.Date(2010, 1, 1, 12, 0, 0, 0, time.UTC) + indexCreatedTime := time.Date(2011, 1, 1, 12, 0, 0, 0, time.UTC) + configLabels := map[string]string{ + ispec.AnnotationCreated: configCreatedTime.Format(time.RFC3339), ispec.AnnotationDescription: "ConfigDescription", ispec.AnnotationLicenses: "ConfigLicenses", ispec.AnnotationVendor: "ConfigVendor", @@ -579,6 +605,7 @@ func TestIndexAnnotations(t *testing.T) { } manifestAnnotations := map[string]string{ + ispec.AnnotationCreated: manifestCreatedTime.Format(time.RFC3339), ispec.AnnotationDescription: "ManifestDescription", ispec.AnnotationLicenses: "ManifestLicenses", ispec.AnnotationVendor: "ManifestVendor", @@ -589,6 +616,7 @@ func TestIndexAnnotations(t *testing.T) { } indexAnnotations := map[string]string{ + ispec.AnnotationCreated: indexCreatedTime.Format(time.RFC3339), ispec.AnnotationDescription: "IndexDescription", ispec.AnnotationLicenses: "IndexLicenses", ispec.AnnotationVendor: "IndexVendor", @@ -634,6 +662,7 @@ func TestIndexAnnotations(t *testing.T) { imageSummary, _, err := convert.ImageIndex2ImageSummary(ctx, convert.GetFullImageMeta("tag", repoMeta, imageMeta[indexWithAnnotations.DigestStr()])) So(err, ShouldBeNil) + So(*imageSummary.LastUpdated, ShouldEqual, indexCreatedTime) So(*imageSummary.Description, ShouldResemble, "IndexDescription") So(*imageSummary.Licenses, ShouldResemble, "IndexLicenses") So(*imageSummary.Title, ShouldResemble, "IndexTitle") @@ -667,6 +696,7 @@ func TestIndexAnnotations(t *testing.T) { imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, convert.GetFullImageMeta("tag", repoMeta, imageMeta[digest])) So(err, ShouldBeNil) + So(*imageSummary.LastUpdated, ShouldEqual, manifestCreatedTime) So(*imageSummary.Description, ShouldResemble, "ManifestDescription") So(*imageSummary.Licenses, ShouldResemble, "ManifestLicenses") So(*imageSummary.Title, ShouldResemble, "ManifestTitle") @@ -700,6 +730,7 @@ func TestIndexAnnotations(t *testing.T) { imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, convert.GetFullImageMeta("tag", repoMeta, imageMeta[digest])) So(err, ShouldBeNil) + So(*imageSummary.LastUpdated, ShouldEqual, configCreatedTime) So(*imageSummary.Description, ShouldResemble, "ConfigDescription") So(*imageSummary.Licenses, ShouldResemble, "ConfigLicenses") So(*imageSummary.Title, ShouldResemble, "ConfigTitle") @@ -715,6 +746,7 @@ func TestIndexAnnotations(t *testing.T) { indexWithMixAnnotations := CreateMultiarchWith().Images( []Image{ CreateImageWith().DefaultLayers().ImageConfig(ispec.Image{ + Created: &defaultCreatedTime, Config: ispec.ImageConfig{ Labels: map[string]string{ ispec.AnnotationDescription: "ConfigDescription", @@ -730,6 +762,7 @@ func TestIndexAnnotations(t *testing.T) { }, ).Annotations( map[string]string{ + ispec.AnnotationCreated: indexCreatedTime.Format(time.RFC3339), ispec.AnnotationTitle: "IndexTitle", ispec.AnnotationDocumentation: "IndexDocumentation", ispec.AnnotationSource: "IndexSource", @@ -754,6 +787,7 @@ func TestIndexAnnotations(t *testing.T) { imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, convert.GetFullImageMeta("tag", repoMeta, imageMeta[digest])) So(err, ShouldBeNil) + So(*imageSummary.LastUpdated, ShouldEqual, indexCreatedTime) So(*imageSummary.Description, ShouldResemble, "ConfigDescription") So(*imageSummary.Licenses, ShouldResemble, "ConfigLicenses") So(*imageSummary.Vendor, ShouldResemble, "ManifestVendor") @@ -765,7 +799,12 @@ func TestIndexAnnotations(t *testing.T) { err = metaDB.ResetDB() So(err, ShouldBeNil) //-------------------------------------------------------- - indexWithNoAnnotations := CreateRandomMultiarch() + indexWithNoAnnotations := CreateMultiarchWith().Images( + []Image{ + CreateImageWith().RandomLayers(1, 10).DefaultConfig().Build(), + CreateImageWith().RandomLayers(1, 10).DefaultConfig().Build(), + }, + ).Build() ctx, err = ociutils.InitializeTestMetaDB(ctx, metaDB, ociutils.Repo{ Name: "repo", @@ -785,6 +824,7 @@ func TestIndexAnnotations(t *testing.T) { imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, convert.GetFullImageMeta("tag", repoMeta, imageMeta[digest])) So(err, ShouldBeNil) + So(*imageSummary.LastUpdated, ShouldEqual, defaultCreatedTime) So(*imageSummary.Description, ShouldBeBlank) So(*imageSummary.Licenses, ShouldBeBlank) So(*imageSummary.Vendor, ShouldBeBlank) diff --git a/pkg/extensions/search/convert/metadb.go b/pkg/extensions/search/convert/metadb.go index 6dc9e68d..90faf7f6 100644 --- a/pkg/extensions/search/convert/metadb.go +++ b/pkg/extensions/search/convert/metadb.go @@ -463,13 +463,18 @@ func ImageIndex2ImageSummary(ctx context.Context, fullImageMeta mTypes.FullImage annotations := GetIndexAnnotations(fullImageMeta.Index.Annotations, manifestAnnotations) + imageLastUpdated := annotations.Created + if imageLastUpdated == nil { + imageLastUpdated = &indexLastUpdated + } + indexSummary := gql_generated.ImageSummary{ RepoName: &repo, Tag: &tag, Digest: &indexDigestStr, MediaType: &indexMediaType, Manifests: manifestSummaries, - LastUpdated: &indexLastUpdated, + LastUpdated: imageLastUpdated, IsSigned: &isSigned, SignatureInfo: signaturesInfo, Size: ref(strconv.FormatInt(indexSize, 10)), @@ -493,18 +498,17 @@ func ImageManifest2ImageSummary(ctx context.Context, fullImageMeta mTypes.FullIm manifest := fullImageMeta.Manifests[0] var ( - repoName = fullImageMeta.Repo - tag = fullImageMeta.Tag - configDigest = manifest.Manifest.Config.Digest.String() - configSize = manifest.Manifest.Config.Size - manifestDigest = manifest.Digest.String() - manifestSize = manifest.Size - mediaType = manifest.Manifest.MediaType - artifactType = zcommon.GetManifestArtifactType(fullImageMeta.Manifests[0].Manifest) - platform = getPlatform(manifest.Config.Platform) - imageLastUpdated = zcommon.GetImageLastUpdated(manifest.Config) - downloadCount = fullImageMeta.Statistics.DownloadCount - isSigned = isImageSigned(fullImageMeta.Signatures) + repoName = fullImageMeta.Repo + tag = fullImageMeta.Tag + configDigest = manifest.Manifest.Config.Digest.String() + configSize = manifest.Manifest.Config.Size + manifestDigest = manifest.Digest.String() + manifestSize = manifest.Size + mediaType = manifest.Manifest.MediaType + artifactType = zcommon.GetManifestArtifactType(fullImageMeta.Manifests[0].Manifest) + platform = getPlatform(manifest.Config.Platform) + downloadCount = fullImageMeta.Statistics.DownloadCount + isSigned = isImageSigned(fullImageMeta.Signatures) ) imageSize, imageBlobsMap := getImageBlobsInfo(manifestDigest, manifestSize, configDigest, configSize, @@ -517,6 +521,12 @@ func ImageManifest2ImageSummary(ctx context.Context, fullImageMeta mTypes.FullIm authors = manifest.Config.Author } + imageLastUpdated := annotations.Created + if imageLastUpdated == nil { + configCreated := zcommon.GetImageLastUpdated(manifest.Config) + imageLastUpdated = &configCreated + } + historyEntries, err := getAllHistory(manifest.Manifest, manifest.Config) if err != nil { graphql.AddError(ctx, gqlerror.Errorf("error generating history on tag %s in repo %s: "+ @@ -528,7 +538,7 @@ func ImageManifest2ImageSummary(ctx context.Context, fullImageMeta mTypes.FullIm manifestSummary := gql_generated.ManifestSummary{ Digest: &manifestDigest, ConfigDigest: &configDigest, - LastUpdated: &imageLastUpdated, + LastUpdated: imageLastUpdated, Size: &imageSizeStr, IsSigned: &isSigned, SignatureInfo: signaturesInfo, @@ -546,7 +556,7 @@ func ImageManifest2ImageSummary(ctx context.Context, fullImageMeta mTypes.FullIm Digest: &manifestDigest, MediaType: &mediaType, Manifests: []*gql_generated.ManifestSummary{&manifestSummary}, - LastUpdated: &imageLastUpdated, + LastUpdated: imageLastUpdated, IsSigned: &isSigned, SignatureInfo: signaturesInfo, Size: &imageSizeStr,