Skip to content

Commit 873e545

Browse files
nalindAaron Lehmann
authored andcommitted
Add a unit test for compression types in OCI images
Add a unit test that commits OCI layouts with various types of compression specified, and verifies that the layers end up written with the desired compression and media type descriptors. Signed-off-by: Nalin Dahyabhai <[email protected]>
1 parent aa84d9c commit 873e545

File tree

1 file changed

+117
-11
lines changed

1 file changed

+117
-11
lines changed

commit_test.go

Lines changed: 117 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
2341
func 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

Comments
 (0)