0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-16 21:56:37 -05:00

feat: Get the image LastUpdated timestamp from annotations (#2240)

Fallback to Created field and the History entries in the image config
only if the annotation "org.opencontainers.image.created" is not available

closes #2210

Signed-off-by: Andrei Aaron <aaaron@luxoft.com>
This commit is contained in:
Andrei Aaron 2024-02-14 19:14:24 +02:00 committed by GitHub
parent ec38d39c06
commit d0eb043be5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 92 additions and 16 deletions

View file

@ -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,

View file

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

View file

@ -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,