Skip to content

Commit 243b49d

Browse files
committed
Extend private.ReusedBlob to allow zstd:chunked reuses
- Add a CompressionAnnotations field - Allow turning a known-zstd blob into a zstd:chunked one if we know the right annotations This just adds the fields, nothing sets them yet, should not change behavior. Signed-off-by: Miloslav Trmač <[email protected]>
1 parent 76af27c commit 243b49d

File tree

4 files changed

+54
-13
lines changed

4 files changed

+54
-13
lines changed

copy/single.go

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"errors"
77
"fmt"
88
"io"
9+
"maps"
910
"reflect"
1011
"slices"
1112
"strings"
@@ -888,21 +889,33 @@ func updatedBlobInfoFromReuse(inputInfo types.BlobInfo, reusedBlob private.Reuse
888889
// Handling of compression, encryption, and the related MIME types and the like are all the responsibility
889890
// of the generic code in this package.
890891
res := types.BlobInfo{
891-
Digest: reusedBlob.Digest,
892-
Size: reusedBlob.Size,
893-
URLs: nil, // This _must_ be cleared if Digest changes; clear it in other cases as well, to preserve previous behavior.
894-
Annotations: inputInfo.Annotations, // FIXME: This should remove zstd:chunked annotations (but those annotations being left with incorrect values should not break pulls)
895-
MediaType: inputInfo.MediaType, // Mostly irrelevant, MediaType is updated based on Compression*/CryptoOperation.
892+
Digest: reusedBlob.Digest,
893+
Size: reusedBlob.Size,
894+
URLs: nil, // This _must_ be cleared if Digest changes; clear it in other cases as well, to preserve previous behavior.
895+
// FIXME: This should remove zstd:chunked annotations IF the original was chunked and the new one isn’t
896+
// (but those annotations being left with incorrect values should not break pulls).
897+
Annotations: maps.Clone(inputInfo.Annotations),
898+
MediaType: inputInfo.MediaType, // Mostly irrelevant, MediaType is updated based on Compression*/CryptoOperation.
896899
CompressionOperation: reusedBlob.CompressionOperation,
897900
CompressionAlgorithm: reusedBlob.CompressionAlgorithm,
898901
CryptoOperation: inputInfo.CryptoOperation, // Expected to be unset anyway.
899902
}
900903
// The transport is only expected to fill CompressionOperation and CompressionAlgorithm
901-
// if the blob was substituted; otherwise, fill it in based
904+
// if the blob was substituted; otherwise, it is optional, and if not set, fill it in based
902905
// on what we know from the srcInfos we were given.
903906
if reusedBlob.Digest == inputInfo.Digest {
904-
res.CompressionOperation = inputInfo.CompressionOperation
905-
res.CompressionAlgorithm = inputInfo.CompressionAlgorithm
907+
if res.CompressionOperation == types.PreserveOriginal {
908+
res.CompressionOperation = inputInfo.CompressionOperation
909+
}
910+
if res.CompressionAlgorithm == nil {
911+
res.CompressionAlgorithm = inputInfo.CompressionAlgorithm
912+
}
913+
}
914+
if len(reusedBlob.CompressionAnnotations) != 0 {
915+
if res.Annotations == nil {
916+
res.Annotations = map[string]string{}
917+
}
918+
maps.Copy(res.Annotations, reusedBlob.CompressionAnnotations)
906919
}
907920
return res
908921
}

copy/single_test.go

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,22 +55,42 @@ func TestUpdatedBlobInfoFromReuse(t *testing.T) {
5555
},
5656
{ // Reuse with substitution
5757
reused: private.ReusedBlob{
58-
Digest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
59-
Size: 513543640,
60-
CompressionOperation: types.Decompress,
61-
CompressionAlgorithm: nil,
58+
Digest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
59+
Size: 513543640,
60+
CompressionOperation: types.Decompress,
61+
CompressionAlgorithm: nil,
62+
CompressionAnnotations: map[string]string{"decompressed": "value"},
6263
},
6364
expected: types.BlobInfo{
6465
Digest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
6566
Size: 513543640,
6667
URLs: nil,
67-
Annotations: map[string]string{"test-annotation-2": "two"},
68+
Annotations: map[string]string{"test-annotation-2": "two", "decompressed": "value"},
6869
MediaType: imgspecv1.MediaTypeImageLayerGzip,
6970
CompressionOperation: types.Decompress,
7071
CompressionAlgorithm: nil,
7172
// CryptoOperation is set to the zero value
7273
},
7374
},
75+
{ // Reuse turning zstd into zstd:chunked
76+
reused: private.ReusedBlob{
77+
Digest: "sha256:6a5a5368e0c2d3e5909184fa28ddfd56072e7ff3ee9a945876f7eee5896ef5bb",
78+
Size: 51354364,
79+
CompressionOperation: types.Compress,
80+
CompressionAlgorithm: &compression.ZstdChunked,
81+
CompressionAnnotations: map[string]string{"zstd-toc": "value"},
82+
},
83+
expected: types.BlobInfo{
84+
Digest: "sha256:6a5a5368e0c2d3e5909184fa28ddfd56072e7ff3ee9a945876f7eee5896ef5bb",
85+
Size: 51354364,
86+
URLs: nil,
87+
Annotations: map[string]string{"test-annotation-2": "two", "zstd-toc": "value"},
88+
MediaType: imgspecv1.MediaTypeImageLayerGzip,
89+
CompressionOperation: types.Compress,
90+
CompressionAlgorithm: &compression.ZstdChunked,
91+
// CryptoOperation is set to the zero value
92+
},
93+
},
7494
} {
7595
res := updatedBlobInfoFromReuse(srcInfo, c.reused)
7696
assert.Equal(t, c.expected, res, fmt.Sprintf("%#v", c.reused))

internal/imagedestination/wrapper.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ func (w *wrapped) TryReusingBlobWithOptions(ctx context.Context, info types.Blob
7676
Size: blob.Size,
7777
CompressionOperation: blob.CompressionOperation,
7878
CompressionAlgorithm: blob.CompressionAlgorithm,
79+
// CompressionAnnotations could be set to blob.Annotations, but that may contain unrelated
80+
// annotations, and we didn’t use the blob.Annotations field previously, so we’ll
81+
// continue not using it.
7982
}, nil
8083
}
8184

internal/private/private.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,14 @@ type ReusedBlob struct {
134134
Size int64 // Must be provided
135135
// The following compression fields should be set when the reuse substitutes
136136
// a differently-compressed blob.
137+
// They may be set also to change from a base variant to a specific variant of an algorithm.
137138
CompressionOperation types.LayerCompression // Compress/Decompress, matching the reused blob; PreserveOriginal if N/A
138139
CompressionAlgorithm *compression.Algorithm // Algorithm if compressed, nil if decompressed or N/A
139140

141+
// Annotations that should be added, for CompressionAlgorithm. Note that they might need to be
142+
// added even if the digest doesn’t change (if we found the annotations in a cache).
143+
CompressionAnnotations map[string]string
144+
140145
MatchedByTOCDigest bool // Whether the layer was reused/matched by TOC digest. Used only for UI purposes.
141146
}
142147

0 commit comments

Comments
 (0)