@@ -11,15 +11,33 @@ import (
1111 "testing"
1212 "time"
1313
14+ "github.com/containers/image/v5/manifest"
15+ ociLayout "github.com/containers/image/v5/oci/layout"
1416 imageStorage "github.com/containers/image/v5/storage"
17+ "github.com/containers/image/v5/types"
1518 "github.com/containers/storage"
19+ "github.com/containers/storage/pkg/archive"
1620 storageTypes "github.com/containers/storage/types"
1721 v1 "github.com/opencontainers/image-spec/specs-go/v1"
1822 rspec "github.com/opencontainers/runtime-spec/specs-go"
1923 "github.com/stretchr/testify/assert"
2024 "github.com/stretchr/testify/require"
2125)
2226
27+ func makeFile (t * testing.T , base string , size int64 ) string {
28+ t .Helper ()
29+ fn := filepath .Join (t .TempDir (), base )
30+ f , err := os .Create (fn )
31+ require .NoError (t , err )
32+ defer f .Close ()
33+ if size == 0 {
34+ size = 512
35+ }
36+ _ , err = io .CopyN (f , rand .Reader , size )
37+ require .NoErrorf (t , err , "writing payload file %d" , base )
38+ return f .Name ()
39+ }
40+
2341func TestCommitLinkedLayers (t * testing.T ) {
2442 // This test cannot be parallized as this uses NewBuilder()
2543 // which eventually and indirectly accesses a global variable
@@ -34,6 +52,7 @@ func TestCommitLinkedLayers(t *testing.T) {
3452 if graphDriverName == "" {
3553 graphDriverName = "vfs"
3654 }
55+ t .Logf ("using storage driver %q" , graphDriverName )
3756 store , err := storage .GetStore (storageTypes.StoreOptions {
3857 RunRoot : t .TempDir (),
3958 GraphRoot : t .TempDir (),
@@ -44,17 +63,7 @@ func TestCommitLinkedLayers(t *testing.T) {
4463
4564 imageName := func (i int ) string { return fmt .Sprintf ("image%d" , i ) }
4665 makeFile := func (base string , size int64 ) string {
47- t .Helper ()
48- fn := filepath .Join (t .TempDir (), base )
49- f , err := os .Create (fn )
50- require .NoError (t , err )
51- defer f .Close ()
52- if size == 0 {
53- size = 512
54- }
55- _ , err = io .CopyN (f , rand .Reader , size )
56- require .NoErrorf (t , err , "writing payload file %d" , base )
57- return f .Name ()
66+ return makeFile (t , base , size )
5867 }
5968 makeArchive := func (base string , size int64 ) string {
6069 t .Helper ()
@@ -242,3 +251,100 @@ func TestCommitLinkedLayers(t *testing.T) {
242251 }()
243252 }
244253}
254+
255+ func TestCommitCompression (t * testing.T ) {
256+ // This test cannot be parallized as this uses NewBuilder()
257+ // which eventually and indirectly accesses a global variable
258+ // defined in `go-selinux`, this must be fixed at `go-selinux`
259+ // or builder must enable sometime of locking mechanism i.e if
260+ // routine is creating Builder other's must wait for it.
261+ // Tracked here: https://github.com/containers/buildah/issues/5967
262+ ctx := context .TODO ()
263+
264+ graphDriverName := os .Getenv ("STORAGE_DRIVER" )
265+ if graphDriverName == "" {
266+ graphDriverName = "vfs"
267+ }
268+ t .Logf ("using storage driver %q" , graphDriverName )
269+ store , err := storage .GetStore (storageTypes.StoreOptions {
270+ RunRoot : t .TempDir (),
271+ GraphRoot : t .TempDir (),
272+ GraphDriverName : graphDriverName ,
273+ })
274+ require .NoError (t , err , "initializing storage" )
275+ t .Cleanup (func () { _ , err := store .Shutdown (true ); assert .NoError (t , err ) })
276+
277+ builderOptions := BuilderOptions {
278+ FromImage : "scratch" ,
279+ NamespaceOptions : []NamespaceOption {{
280+ Name : string (rspec .NetworkNamespace ),
281+ Host : true ,
282+ }},
283+ SystemContext : & testSystemContext ,
284+ }
285+ b , err := NewBuilder (ctx , store , builderOptions )
286+ require .NoError (t , err , "creating builder" )
287+ payload := makeFile (t , "file0" , 0 )
288+ b .SetCreatedBy ("ADD file0 in /" )
289+ err = b .Add ("/" , false , AddAndCopyOptions {}, payload )
290+ require .NoError (t , err , "adding" , payload )
291+ for _ , compressor := range []struct {
292+ compression archive.Compression
293+ name string
294+ expectError bool
295+ layerMediaType string
296+ }{
297+ {archive .Uncompressed , "uncompressed" , false , v1 .MediaTypeImageLayer },
298+ {archive .Gzip , "gzip" , false , v1 .MediaTypeImageLayerGzip },
299+ {archive .Bzip2 , "bz2" , true , "" },
300+ {archive .Xz , "xz" , true , "" },
301+ {archive .Zstd , "zstd" , false , v1 .MediaTypeImageLayerZstd },
302+ } {
303+ t .Run (compressor .name , func (t * testing.T ) {
304+ var ref types.ImageReference
305+ commitOptions := CommitOptions {
306+ PreferredManifestType : v1 .MediaTypeImageManifest ,
307+ SystemContext : & testSystemContext ,
308+ Compression : compressor .compression ,
309+ }
310+ imageName := compressor .name
311+ ref , err := imageStorage .Transport .ParseStoreReference (store , imageName )
312+ require .NoErrorf (t , err , "parsing reference for to-be-committed local image %q" , imageName )
313+ _ , _ , _ , err = b .Commit (ctx , ref , commitOptions )
314+ if compressor .expectError {
315+ require .Errorf (t , err , "committing local image %q" , imageName )
316+ } else {
317+ require .NoErrorf (t , err , "committing local image %q" , imageName )
318+ }
319+ imageName = t .TempDir ()
320+ ref , err = ociLayout .Transport .ParseReference (imageName )
321+ require .NoErrorf (t , err , "parsing reference for to-be-committed oci layout %q" , imageName )
322+ _ , _ , _ , err = b .Commit (ctx , ref , commitOptions )
323+ if compressor .expectError {
324+ require .Errorf (t , err , "committing oci layout %q" , imageName )
325+ return
326+ } else {
327+ require .NoErrorf (t , err , "committing oci layout %q" , imageName )
328+ }
329+ src , err := ref .NewImageSource (ctx , & testSystemContext )
330+ require .NoErrorf (t , err , "reading oci layout %q" , imageName )
331+ defer src .Close ()
332+ manifestBytes , manifestType , err := src .GetManifest (ctx , nil )
333+ require .NoErrorf (t , err , "reading manifest from oci layout %q" , imageName )
334+ require .Equalf (t , v1 .MediaTypeImageManifest , manifestType , "manifest type from oci layout %q looked wrong" , imageName )
335+ parsedManifest , err := manifest .OCI1FromManifest (manifestBytes )
336+ require .NoErrorf (t , err , "parsing manifest from oci layout %q" , imageName )
337+ require .Lenf (t , parsedManifest .Layers , 1 , "expected exactly one layer in oci layout %q" , imageName )
338+ require .Equalf (t , compressor .layerMediaType , parsedManifest .Layers [0 ].MediaType , "expected the layer media type to reflect compression in oci layout %q" , imageName )
339+ blobReadCloser , _ , err := src .GetBlob (ctx , types.BlobInfo {
340+ Digest : parsedManifest .Layers [0 ].Digest ,
341+ MediaType : parsedManifest .Layers [0 ].MediaType ,
342+ }, nil )
343+ require .NoErrorf (t , err , "reading the first layer from oci layout %q" , imageName )
344+ defer blobReadCloser .Close ()
345+ blob , err := io .ReadAll (blobReadCloser )
346+ require .NoErrorf (t , err , "consuming the first layer from oci layout %q" , imageName )
347+ require .Equalf (t , compressor .compression , archive .DetectCompression (blob ), "detected compression looks wrong for layer in oci layout %q" )
348+ })
349+ }
350+ }
0 commit comments