Skip to content

Commit 4cf082a

Browse files
committed
list,oci_index: automatically add inbuilt annotations on add
Adding instance to list should automatically add special annotations if any. Signed-off-by: Aditya R <arajan@redhat.com>
1 parent 68798a2 commit 4cf082a

File tree

3 files changed

+87
-12
lines changed

3 files changed

+87
-12
lines changed

internal/manifest/list.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package manifest
33
import (
44
"fmt"
55

6+
compression "github.com/containers/image/v5/pkg/compression/types"
67
"github.com/containers/image/v5/types"
78
digest "github.com/opencontainers/go-digest"
89
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
@@ -82,17 +83,21 @@ type ListEdit struct {
8283
ListOperation ListOp
8384

8485
// if Op == ListEditUpdate (basically the previous UpdateInstances). All fields must be set.
85-
UpdateOldDigest digest.Digest
86-
UpdateDigest digest.Digest
87-
UpdateSize int64
88-
UpdateMediaType string
86+
UpdateOldDigest digest.Digest
87+
UpdateDigest digest.Digest
88+
UpdateSize int64
89+
UpdateMediaType string
90+
UpdateAffectAnnotations bool
91+
UpdateAnnotations map[string]string
92+
UpdateCompressionAlgorithms []compression.Algorithm
8993

9094
// If Op = ListEditAdd. All fields must be set.
91-
AddDigest digest.Digest
92-
AddSize int64
93-
AddMediaType string
94-
AddPlatform *imgspecv1.Platform
95-
AddAnnotations map[string]string
95+
AddDigest digest.Digest
96+
AddSize int64
97+
AddMediaType string
98+
AddPlatform *imgspecv1.Platform
99+
AddAnnotations map[string]string
100+
AddCompressionAlgorithms []compression.Algorithm
96101
}
97102

98103
// ListPublicFromBlob parses a list of manifests.

internal/manifest/oci_index.go

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"runtime"
88

99
platform "github.com/containers/image/v5/internal/pkg/platform"
10+
compression "github.com/containers/image/v5/pkg/compression/types"
1011
"github.com/containers/image/v5/types"
1112
"github.com/opencontainers/go-digest"
1213
imgspec "github.com/opencontainers/image-spec/specs-go"
@@ -22,7 +23,8 @@ const (
2223
// That also suggests that this instance benefits from
2324
// Zstd compression, so it can be preferred by compatible consumers over instances that
2425
// use gzip, depending on their local policy.
25-
OCI1InstanceAnnotationCompressionZSTD = "io.github.containers.compression.zstd"
26+
OCI1InstanceAnnotationCompressionZSTD = "io.github.containers.compression.zstd"
27+
OCI1InstanceAnnotationCompressionZSTDValue = "true"
2628
)
2729

2830
// OCI1IndexPublic is just an alias for the OCI index type, but one which we can
@@ -76,8 +78,23 @@ func (index *OCI1IndexPublic) UpdateInstances(updates []ListUpdate) error {
7678
return index.editInstances(editInstances)
7779
}
7880

81+
func addCompressionAnnotations(compressionAlgorithms []compression.Algorithm, annotationsMap map[string]string) {
82+
// TODO: This should also delete the algorithm if map already contains an algorithm and compressionAlgorithm
83+
// list has a different algorithm. To do that, we would need to modify the callers to always provide a reliable
84+
// and full compressionAlghorithms list.
85+
for _, algo := range compressionAlgorithms {
86+
switch algo.Name() {
87+
case compression.ZstdAlgorithmName:
88+
annotationsMap[OCI1InstanceAnnotationCompressionZSTD] = OCI1InstanceAnnotationCompressionZSTDValue
89+
default:
90+
continue
91+
}
92+
}
93+
}
94+
7995
func (index *OCI1IndexPublic) editInstances(editInstances []ListEdit) error {
8096
addedEntries := []imgspecv1.Descriptor{}
97+
updatedAnnotations := false
8198
for i, editInstance := range editInstances {
8299
switch editInstance.ListOperation {
83100
case ListOpUpdate:
@@ -102,19 +119,38 @@ func (index *OCI1IndexPublic) editInstances(editInstances []ListEdit) error {
102119
return fmt.Errorf("update %d of %d passed to OCI1Index.UpdateInstances had no media type (was %q)", i+1, len(editInstances), index.Manifests[i].MediaType)
103120
}
104121
index.Manifests[targetIndex].MediaType = editInstance.UpdateMediaType
122+
if editInstance.UpdateAnnotations != nil {
123+
updatedAnnotations = true
124+
if editInstance.UpdateAffectAnnotations {
125+
index.Manifests[targetIndex].Annotations = maps.Clone(editInstance.UpdateAnnotations)
126+
} else {
127+
if index.Manifests[targetIndex].Annotations == nil {
128+
index.Manifests[targetIndex].Annotations = map[string]string{}
129+
}
130+
maps.Copy(index.Manifests[targetIndex].Annotations, editInstance.UpdateAnnotations)
131+
}
132+
}
133+
addCompressionAnnotations(editInstance.UpdateCompressionAlgorithms, index.Manifests[targetIndex].Annotations)
105134
case ListOpAdd:
135+
annotations := map[string]string{}
136+
if editInstance.AddAnnotations != nil {
137+
annotations = maps.Clone(editInstance.AddAnnotations)
138+
}
139+
addCompressionAnnotations(editInstance.AddCompressionAlgorithms, annotations)
106140
addedEntries = append(addedEntries, imgspecv1.Descriptor{
107141
MediaType: editInstance.AddMediaType,
108142
Size: editInstance.AddSize,
109143
Digest: editInstance.AddDigest,
110144
Platform: editInstance.AddPlatform,
111-
Annotations: editInstance.AddAnnotations})
145+
Annotations: annotations})
112146
default:
113147
return fmt.Errorf("internal error: invalid operation: %d", editInstance.ListOperation)
114148
}
115149
}
116150
if len(addedEntries) != 0 {
117151
index.Manifests = append(index.Manifests, addedEntries...)
152+
}
153+
if len(addedEntries) != 0 || updatedAnnotations {
118154
slices.SortStableFunc(index.Manifests, func(a, b imgspecv1.Descriptor) bool {
119155
return !instanceIsZstd(a) && instanceIsZstd(b)
120156
})

internal/manifest/oci_index_test.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"path/filepath"
77
"testing"
88

9+
"github.com/containers/image/v5/pkg/compression"
910
"github.com/containers/image/v5/types"
1011
"github.com/opencontainers/go-digest"
1112
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
@@ -99,6 +100,23 @@ func TestOCI1EditInstances(t *testing.T) {
99100
AddPlatform: &imgspecv1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"sse4"}},
100101
AddAnnotations: annotations,
101102
ListOperation: ListOpAdd})
103+
// with zstd but with compression, annotation must be added automatically
104+
editInstances = append(editInstances, ListEdit{
105+
AddDigest: "sha256:hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
106+
AddSize: 32,
107+
AddMediaType: "application/vnd.oci.image.manifest.v1+json",
108+
AddPlatform: &imgspecv1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"sse4"}},
109+
AddCompressionAlgorithms: []compression.Algorithm{compression.Zstd},
110+
AddAnnotations: map[string]string{},
111+
ListOperation: ListOpAdd})
112+
// with zstd but with compression, annotation must be added automatically and AddAnnotations is unset
113+
editInstances = append(editInstances, ListEdit{
114+
AddDigest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
115+
AddSize: 32,
116+
AddMediaType: "application/vnd.oci.image.manifest.v1+json",
117+
AddPlatform: &imgspecv1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"sse4"}},
118+
AddCompressionAlgorithms: []compression.Algorithm{compression.Zstd},
119+
ListOperation: ListOpAdd})
102120
// without zstd
103121
editInstances = append(editInstances, ListEdit{
104122
AddDigest: "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
@@ -110,7 +128,23 @@ func TestOCI1EditInstances(t *testing.T) {
110128
require.NoError(t, err)
111129

112130
// Zstd should be kept on lowest priority as compared to the default gzip ones and order of prior elements must be preserved.
113-
assert.Equal(t, list.Instances(), []digest.Digest{digest.Digest("sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"), digest.Digest("sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"), digest.Digest("sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), digest.Digest("sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"), digest.Digest("sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee")})
131+
assert.Equal(t, list.Instances(), []digest.Digest{digest.Digest("sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"), digest.Digest("sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"), digest.Digest("sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), digest.Digest("sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"), digest.Digest("sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"), digest.Digest("sha256:hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"), digest.Digest("sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")})
132+
133+
// Update list and remove zstd annotation from existing instance, and verify if resorting works
134+
editInstances = []ListEdit{}
135+
editInstances = append(editInstances, ListEdit{
136+
UpdateOldDigest: digest.Digest("sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
137+
UpdateDigest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
138+
UpdateSize: 32,
139+
UpdateMediaType: "application/vnd.oci.image.manifest.v1+json",
140+
UpdateAffectAnnotations: true,
141+
UpdateAnnotations: map[string]string{},
142+
ListOperation: ListOpUpdate})
143+
err = list.EditInstances(editInstances)
144+
require.NoError(t, err)
145+
// Digest `ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff` should be re-ordered on update.
146+
assert.Equal(t, list.Instances(), []digest.Digest{digest.Digest("sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"), digest.Digest("sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"), digest.Digest("sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), digest.Digest("sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"), digest.Digest("sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), digest.Digest("sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"), digest.Digest("sha256:hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")})
147+
114148
}
115149

116150
func TestOCI1IndexChooseInstanceByCompression(t *testing.T) {

0 commit comments

Comments
 (0)