Skip to content

Commit 6ce3ae5

Browse files
committed
Don't add the zstd annotation if we can't edit the OCI index
In that case, just copy the image as is, giving up on enabling the zstd prioritization. Signed-off-by: Miloslav Trmač <[email protected]>
1 parent 528cbf7 commit 6ce3ae5

File tree

6 files changed

+70
-22
lines changed

6 files changed

+70
-22
lines changed

image/copy/multiple.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ func (c *copier) copyMultipleImages(ctx context.Context) (copiedManifest []byte,
284284
}
285285

286286
// Now reset the digest/size/types of the manifests in the list to account for any conversions that we made.
287-
if err = updatedList.EditInstances(instanceEdits); err != nil {
287+
if err = updatedList.EditInstances(instanceEdits, cannotModifyManifestListReason != ""); err != nil {
288288
return nil, fmt.Errorf("updating manifest list: %w", err)
289289
}
290290

image/internal/manifest/docker_schema2_list.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,13 @@ func (list *Schema2ListPublic) UpdateInstances(updates []ListUpdate) error {
8585
ListOperation: ListOpUpdate,
8686
})
8787
}
88-
return list.editInstances(editInstances)
88+
return list.editInstances(editInstances, false)
8989
}
9090

91-
func (list *Schema2ListPublic) editInstances(editInstances []ListEdit) error {
91+
// editInstances edits information about the list's instances, based on instructions in editInstances.
92+
// If cannotModifyManifest, avoidable updates should be skipped.
93+
func (list *Schema2ListPublic) editInstances(editInstances []ListEdit, cannotModifyManifest bool) error {
94+
_ = cannotModifyManifest // None of the edits we make are avoidable.
9295
addedEntries := []Schema2ManifestDescriptor{}
9396
for i, editInstance := range editInstances {
9497
switch editInstance.ListOperation {
@@ -141,8 +144,10 @@ func (list *Schema2ListPublic) editInstances(editInstances []ListEdit) error {
141144
return nil
142145
}
143146

144-
func (list *Schema2List) EditInstances(editInstances []ListEdit) error {
145-
return list.editInstances(editInstances)
147+
// EditInstances edits information about the list's instances, based on instructions in editInstances.
148+
// If cannotModifyManifest, avoidable updates should be skipped.
149+
func (list *Schema2List) EditInstances(editInstances []ListEdit, cannotModifyManifest bool) error {
150+
return list.editInstances(editInstances, cannotModifyManifest)
146151
}
147152

148153
func (list *Schema2ListPublic) ChooseInstanceByCompression(ctx *types.SystemContext, preferGzip types.OptionalBool) (digest.Digest, error) {

image/internal/manifest/docker_schema2_list_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func TestSchema2ListEditInstances(t *testing.T) {
4747
UpdateMediaType: "something",
4848
ListOperation: ListOpUpdate,
4949
})
50-
err = list.EditInstances(editInstances)
50+
err = list.EditInstances(editInstances, false)
5151
require.NoError(t, err)
5252

5353
expectedDigests[0] = editInstances[0].UpdateDigest
@@ -82,7 +82,7 @@ func TestSchema2ListEditInstances(t *testing.T) {
8282
AddPlatform: &imgspecv1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"sse4"}},
8383
ListOperation: ListOpAdd,
8484
})
85-
err = list.EditInstances(editInstances)
85+
err = list.EditInstances(editInstances, false)
8686
require.NoError(t, err)
8787

8888
// Verify new elements are added to the end of old list

image/internal/manifest/list.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,9 @@ type List interface {
5656
// SystemContext ( or for the current platform if the SystemContext doesn't specify any detail ) and preferGzip for compression which
5757
// when configured to OptionalBoolTrue and chooses best available compression when it is OptionalBoolFalse or left OptionalBoolUndefined.
5858
ChooseInstanceByCompression(ctx *types.SystemContext, preferGzip types.OptionalBool) (digest.Digest, error)
59-
// Edit information about the list's instances. Contains Slice of ListEdit where each element
60-
// is responsible for either Modifying or Adding a new instance to the Manifest. Operation is
61-
// selected on the basis of configured ListOperation field.
62-
EditInstances([]ListEdit) error
59+
// EditInstances edits information about the list's instances, based on instructions in editInstances.
60+
// If cannotModifyManifest, avoidable updates should be skipped.
61+
EditInstances(editInstances []ListEdit, cannotModifyManifest bool) error
6362
}
6463

6564
// ListUpdate includes the fields which a List's UpdateInstances() method will modify.

image/internal/manifest/oci_index.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func (index *OCI1IndexPublic) UpdateInstances(updates []ListUpdate) error {
8282
ListOperation: ListOpUpdate,
8383
})
8484
}
85-
return index.editInstances(editInstances)
85+
return index.editInstances(editInstances, false)
8686
}
8787

8888
func annotationsToCompressionAlgorithmNames(annotations map[string]string) []string {
@@ -97,7 +97,12 @@ func annotationsToCompressionAlgorithmNames(annotations map[string]string) []str
9797
return result
9898
}
9999

100-
func addCompressionAnnotations(compressionAlgorithms []compression.Algorithm, annotationsMap *map[string]string) {
100+
// addCompressionAnnotations updates annotationsMap for a manifest, based on compressionAlgorithms known to be used in that instance.
101+
// If cannotModifyManifest, avoidable updates should be skipped (i.e. this does nothing, because the algorithm annotation is “only” used for prioritization).
102+
func addCompressionAnnotations(compressionAlgorithms []compression.Algorithm, annotationsMap *map[string]string, cannotModifyManifest bool) {
103+
if cannotModifyManifest {
104+
return
105+
}
101106
// TODO: This should also delete the algorithm if map already contains an algorithm and compressionAlgorithm
102107
// list has a different algorithm. To do that, we would need to modify the callers to always provide a reliable
103108
// and full compressionAlghorithms list.
@@ -114,7 +119,9 @@ func addCompressionAnnotations(compressionAlgorithms []compression.Algorithm, an
114119
}
115120
}
116121

117-
func (index *OCI1IndexPublic) editInstances(editInstances []ListEdit) error {
122+
// editInstances edits information about the list's instances, based on instructions in editInstances.
123+
// If cannotModifyManifest, avoidable updates should be skipped.
124+
func (index *OCI1IndexPublic) editInstances(editInstances []ListEdit, cannotModifyManifest bool) error {
118125
addedEntries := []imgspecv1.Descriptor{}
119126
updatedAnnotations := false
120127
for i, editInstance := range editInstances {
@@ -152,13 +159,13 @@ func (index *OCI1IndexPublic) editInstances(editInstances []ListEdit) error {
152159
maps.Copy(index.Manifests[targetIndex].Annotations, editInstance.UpdateAnnotations)
153160
}
154161
}
155-
addCompressionAnnotations(editInstance.UpdateCompressionAlgorithms, &index.Manifests[targetIndex].Annotations)
162+
addCompressionAnnotations(editInstance.UpdateCompressionAlgorithms, &index.Manifests[targetIndex].Annotations, cannotModifyManifest)
156163
case ListOpAdd:
157164
annotations := map[string]string{}
158165
if editInstance.AddAnnotations != nil {
159166
annotations = maps.Clone(editInstance.AddAnnotations)
160167
}
161-
addCompressionAnnotations(editInstance.AddCompressionAlgorithms, &annotations)
168+
addCompressionAnnotations(editInstance.AddCompressionAlgorithms, &annotations, false) // We are adding a full new entry, so skipping the annotation would still not allow preserving the original manifest.
162169
addedEntries = append(addedEntries, imgspecv1.Descriptor{
163170
MediaType: editInstance.AddMediaType,
164171
ArtifactType: editInstance.AddArtifactType,
@@ -195,8 +202,10 @@ func (index *OCI1IndexPublic) editInstances(editInstances []ListEdit) error {
195202
return nil
196203
}
197204

198-
func (index *OCI1Index) EditInstances(editInstances []ListEdit) error {
199-
return index.editInstances(editInstances)
205+
// EditInstances edits information about the list's instances, based on instructions in editInstances.
206+
// If cannotModifyManifest, avoidable updates should be skipped.
207+
func (index *OCI1Index) EditInstances(editInstances []ListEdit, cannotModifyManifest bool) error {
208+
return index.editInstances(editInstances, cannotModifyManifest)
200209
}
201210

202211
// instanceIsZstd returns true if instance is a zstd instance otherwise false.

image/internal/manifest/oci_index_test.go

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func TestOCI1EditInstances(t *testing.T) {
6868
UpdateMediaType: "something",
6969
ListOperation: ListOpUpdate,
7070
})
71-
err = list.EditInstances(editInstances)
71+
err = list.EditInstances(editInstances, false)
7272
require.NoError(t, err)
7373

7474
expectedDigests[0] = editInstances[0].UpdateDigest
@@ -135,7 +135,7 @@ func TestOCI1EditInstances(t *testing.T) {
135135
AddPlatform: &imgspecv1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"sse4"}},
136136
ListOperation: ListOpAdd,
137137
})
138-
err = list.EditInstances(editInstances)
138+
err = list.EditInstances(editInstances, false)
139139
require.NoError(t, err)
140140

141141
// Zstd should be kept on lowest priority as compared to the default gzip ones and order of prior elements must be preserved.
@@ -162,7 +162,7 @@ func TestOCI1EditInstances(t *testing.T) {
162162
UpdateAnnotations: map[string]string{},
163163
ListOperation: ListOpUpdate,
164164
})
165-
err = list.EditInstances(editInstances)
165+
err = list.EditInstances(editInstances, false)
166166
require.NoError(t, err)
167167
// Digest `ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff` should be re-ordered on update.
168168
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")})
@@ -187,12 +187,47 @@ func TestOCI1EditInstances(t *testing.T) {
187187
UpdateAffectAnnotations: true,
188188
UpdateAnnotations: map[string]string{},
189189
}}
190-
err = list.EditInstances(editInstances)
190+
err = list.EditInstances(editInstances, false)
191191
require.NoError(t, err)
192192
// Verify that the artifactType wasn't lost.
193193
instance, err = list.Instance(digest.Digest("sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
194194
require.NoError(t, err)
195195
assert.Equal(t, "application/x-tar", instance.ReadOnly.ArtifactType)
196+
197+
// Updating the zstd annotation for an existing instance works
198+
for _, cannotModifyManifest := range []bool{false, true} {
199+
list, err = ListFromBlob(validManifest, GuessMIMEType(validManifest))
200+
require.NoError(t, err)
201+
originalInstance, err := list.Instance(list.Instances()[0])
202+
require.NoError(t, err)
203+
editInstances = []ListEdit{{
204+
ListOperation: ListOpUpdate,
205+
UpdateOldDigest: originalInstance.Digest,
206+
UpdateDigest: originalInstance.Digest,
207+
UpdateSize: originalInstance.Size,
208+
UpdateMediaType: originalInstance.MediaType,
209+
UpdateAffectAnnotations: false,
210+
UpdateAnnotations: nil,
211+
UpdateCompressionAlgorithms: []compression.Algorithm{compression.Zstd},
212+
}}
213+
err = list.EditInstances(editInstances, cannotModifyManifest)
214+
require.NoError(t, err)
215+
instance, err = list.Instance(list.Instances()[0])
216+
require.NoError(t, err)
217+
218+
if cannotModifyManifest {
219+
assert.Equal(t, originalInstance, instance) // No changes
220+
} else {
221+
assert.Equal(t, "true", instance.ReadOnly.Annotations["io.github.containers.compression.zstd"])
222+
assert.Equal(t, []string{compressionTypes.ZstdAlgorithmName}, instance.ReadOnly.CompressionAlgorithmNames)
223+
// These are the only changes:
224+
delete(instance.ReadOnly.Annotations, "io.github.containers.compression.zstd")
225+
require.Empty(t, instance.ReadOnly.Annotations)
226+
instance.ReadOnly.Annotations = nil
227+
instance.ReadOnly.CompressionAlgorithmNames = []string{compressionTypes.GzipAlgorithmName}
228+
assert.Equal(t, originalInstance, instance)
229+
}
230+
}
196231
}
197232

198233
func TestOCI1IndexChooseInstanceByCompression(t *testing.T) {

0 commit comments

Comments
 (0)