diff options
Diffstat (limited to 'pkg/v1/tarball/layer_test.go')
-rw-r--r-- | pkg/v1/tarball/layer_test.go | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/pkg/v1/tarball/layer_test.go b/pkg/v1/tarball/layer_test.go new file mode 100644 index 0000000..5d93360 --- /dev/null +++ b/pkg/v1/tarball/layer_test.go @@ -0,0 +1,381 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tarball + +import ( + "bytes" + "compress/gzip" + "io" + "os" + "testing" + + "github.com/containerd/stargz-snapshotter/estargz" + "github.com/google/go-containerregistry/internal/compare" + "github.com/google/go-containerregistry/pkg/compression" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/types" + "github.com/google/go-containerregistry/pkg/v1/validate" +) + +func TestLayerFromFile(t *testing.T) { + setupFixtures(t) + defer teardownFixtures(t) + + tarLayer, err := LayerFromFile("testdata/content.tar") + if err != nil { + t.Fatalf("Unable to create layer from tar file: %v", err) + } + + tarGzLayer, err := LayerFromFile("gzip_content.tgz") + if err != nil { + t.Fatalf("Unable to create layer from compressed tar file: %v", err) + } + + tarZstdLayer, err := LayerFromFile("zstd_content.tar.zst") + if err != nil { + t.Fatalf("Unable to create layer from compressed tar file: %v", err) + } + + if err := compare.Layers(tarLayer, tarGzLayer); err != nil { + t.Errorf("compare.Layers: %v", err) + } + + if err := compare.Layers(tarLayer, tarZstdLayer); err != nil { + t.Errorf("compare.Layers: %v", err) + } + + if err := validate.Layer(tarLayer); err != nil { + t.Errorf("validate.Layer(tarLayer): %v", err) + } + + if err := validate.Layer(tarGzLayer); err != nil { + t.Errorf("validate.Layer(tarGzLayer): %v", err) + } + + if err := validate.Layer(tarZstdLayer); err != nil { + t.Errorf("validate.Layer(tarZstdLayer): %v", err) + } + + getTestDigest := func(testName string, opts ...LayerOption) v1.Hash { + layer, err := LayerFromFile("testdata/content.tar", opts...) + if err != nil { + t.Fatalf("Unable to create layer with '%s' compression from tar file: %v", testName, err) + } + + digest, err := layer.Digest() + if err != nil { + t.Fatalf("Unable to generate digest with '%s' compression: %v", testName, err) + } + + return digest + } + + defaultDigest := getTestDigest("Gzip Default", WithCompressionLevel(gzip.DefaultCompression)) + speedDigest := getTestDigest("Gzip BestSpeed", WithCompressionLevel(gzip.BestSpeed)) + zstdDigest := getTestDigest("Zstd Default", WithCompression(compression.ZStd)) + zstdDigest1 := getTestDigest("Zstd BestSpeed", WithCompression(compression.ZStd), WithCompressionLevel(1)) + + if defaultDigest.String() == speedDigest.String() { + t.Errorf("expected digests to differ: %s", defaultDigest.String()) + } + + if defaultDigest.String() == zstdDigest.String() { + t.Errorf("expected digests to differ: %s", defaultDigest.String()) + } + + if defaultDigest.String() == zstdDigest1.String() { + t.Errorf("expected digests to differ: %s", defaultDigest.String()) + } +} + +func TestLayerFromFileEstargz(t *testing.T) { + setupFixtures(t) + defer teardownFixtures(t) + + tarLayer, err := LayerFromFile("testdata/content.tar", WithEstargz) + if err != nil { + t.Fatalf("Unable to create layer from tar file: %v", err) + } + + if err := validate.Layer(tarLayer); err != nil { + t.Errorf("validate.Layer(tarLayer): %v", err) + } + + tarLayerDefaultCompression, err := LayerFromFile("testdata/content.tar", WithEstargz, WithCompressionLevel(gzip.DefaultCompression)) + if err != nil { + t.Fatalf("Unable to create layer with 'Default' compression from tar file: %v", err) + } + descriptorDefaultCompression, err := tarLayerDefaultCompression.(*layer).Descriptor() + if err != nil { + t.Fatalf("Descriptor() = %v", err) + } else if len(descriptorDefaultCompression.Annotations) != 1 { + t.Errorf("Annotations = %#v, wanted 1 annotation", descriptorDefaultCompression.Annotations) + } + + defaultDigest, err := tarLayerDefaultCompression.Digest() + if err != nil { + t.Fatal("Unable to generate digest with 'Default' compression", err) + } + + tarLayerSpeedCompression, err := LayerFromFile("testdata/content.tar", WithEstargz, WithCompressionLevel(gzip.BestSpeed)) + if err != nil { + t.Fatalf("Unable to create layer with 'BestSpeed' compression from tar file: %v", err) + } + descriptorSpeedCompression, err := tarLayerSpeedCompression.(*layer).Descriptor() + if err != nil { + t.Fatalf("Descriptor() = %v", err) + } else if len(descriptorSpeedCompression.Annotations) != 1 { + t.Errorf("Annotations = %#v, wanted 1 annotation", descriptorSpeedCompression.Annotations) + } + + speedDigest, err := tarLayerSpeedCompression.Digest() + if err != nil { + t.Fatal("Unable to generate digest with 'BestSpeed' compression", err) + } + + if defaultDigest.String() == speedDigest.String() { + t.Errorf("expected digests to differ: %s", defaultDigest.String()) + } + + if descriptorDefaultCompression.Annotations[estargz.TOCJSONDigestAnnotation] == descriptorSpeedCompression.Annotations[estargz.TOCJSONDigestAnnotation] { + t.Errorf("wanted different toc digests got default: %s, speed: %s", + descriptorDefaultCompression.Annotations[estargz.TOCJSONDigestAnnotation], + descriptorSpeedCompression.Annotations[estargz.TOCJSONDigestAnnotation]) + } + + tarLayerPrioritizedFiles, err := LayerFromFile("testdata/content.tar", + WithEstargz, + // We compare with default, so pass for apples-to-apples comparison. + WithCompressionLevel(gzip.DefaultCompression), + // By passing a list of priority files, we expect the layer to be different. + WithEstargzOptions(estargz.WithPrioritizedFiles([]string{ + "./bat", + }))) + if err != nil { + t.Fatalf("Unable to create layer with prioritized files from tar file: %v", err) + } + descriptorPrioritizedFiles, err := tarLayerPrioritizedFiles.(*layer).Descriptor() + if err != nil { + t.Fatalf("Descriptor() = %v", err) + } else if len(descriptorPrioritizedFiles.Annotations) != 1 { + t.Errorf("Annotations = %#v, wanted 1 annotation", descriptorPrioritizedFiles.Annotations) + } + + prioritizedDigest, err := tarLayerPrioritizedFiles.Digest() + if err != nil { + t.Fatal("Unable to generate digest with prioritized files", err) + } + + if defaultDigest.String() == prioritizedDigest.String() { + t.Errorf("expected digests to differ: %s", defaultDigest.String()) + } + + if descriptorDefaultCompression.Annotations[estargz.TOCJSONDigestAnnotation] == descriptorPrioritizedFiles.Annotations[estargz.TOCJSONDigestAnnotation] { + t.Errorf("wanted different toc digests got default: %s, prioritized: %s", + descriptorDefaultCompression.Annotations[estargz.TOCJSONDigestAnnotation], + descriptorPrioritizedFiles.Annotations[estargz.TOCJSONDigestAnnotation]) + } +} + +func TestLayerFromOpenerReader(t *testing.T) { + setupFixtures(t) + defer teardownFixtures(t) + + ucBytes, err := os.ReadFile("testdata/content.tar") + if err != nil { + t.Fatalf("Unable to read tar file: %v", err) + } + count := 0 + ucOpener := func() (io.ReadCloser, error) { + count++ + return io.NopCloser(bytes.NewReader(ucBytes)), nil + } + tarLayer, err := LayerFromOpener(ucOpener, WithCompressedCaching) + if err != nil { + t.Fatal("Unable to create layer from tar file:", err) + } + for i := 0; i < 10; i++ { + tarLayer.Compressed() + } + + // Store the count and reset the counter. + cachedCount := count + count = 0 + + tarLayer, err = LayerFromOpener(ucOpener) + if err != nil { + t.Fatal("Unable to create layer from tar file:", err) + } + for i := 0; i < 10; i++ { + tarLayer.Compressed() + } + + // We expect three calls: gzip sniff, diffid computation, cached compression + if cachedCount != 3 { + t.Errorf("cached count = %d, wanted %d", cachedCount, 3) + } + if cachedCount+10 != count { + t.Errorf("count = %d, wanted %d", count, cachedCount+10) + } + + gzBytes, err := os.ReadFile("gzip_content.tgz") + if err != nil { + t.Fatalf("Unable to read tar file: %v", err) + } + gzOpener := func() (io.ReadCloser, error) { + return io.NopCloser(bytes.NewReader(gzBytes)), nil + } + tarGzLayer, err := LayerFromOpener(gzOpener) + if err != nil { + t.Fatalf("Unable to create layer from tar file: %v", err) + } + + if err := compare.Layers(tarLayer, tarGzLayer); err != nil { + t.Errorf("compare.Layers: %v", err) + } + + zstdBytes, err := os.ReadFile("zstd_content.tar.zst") + if err != nil { + t.Fatalf("Unable to read tar file: %v", err) + } + zstdOpener := func() (io.ReadCloser, error) { + return io.NopCloser(bytes.NewReader(zstdBytes)), nil + } + tarZstdLayer, err := LayerFromOpener(zstdOpener) + if err != nil { + t.Fatalf("Unable to create layer from tar file: %v", err) + } + + if err := compare.Layers(tarLayer, tarZstdLayer); err != nil { + t.Errorf("compare.Layers: %v", err) + } +} + +func TestWithMediaType(t *testing.T) { + setupFixtures(t) + defer teardownFixtures(t) + + l, err := LayerFromFile("testdata/content.tar") + if err != nil { + t.Fatalf("Unable to create layer from tar file: %v", err) + } + got, err := l.MediaType() + if err != nil { + t.Fatalf("MediaType: %v", err) + } + if want := types.DockerLayer; got != want { + t.Errorf("got %v, want %v", got, want) + } + + l, err = LayerFromFile("testdata/content.tar", WithMediaType(types.OCILayer)) + if err != nil { + t.Fatalf("Unable to create layer from tar file: %v", err) + } + got, err = l.MediaType() + if err != nil { + t.Fatalf("MediaType: %v", err) + } + if want := types.OCILayer; got != want { + t.Errorf("got %v, want %v", got, want) + } +} + +func TestLayerFromReader(t *testing.T) { + setupFixtures(t) + defer teardownFixtures(t) + + ucBytes, err := os.ReadFile("testdata/content.tar") + if err != nil { + t.Fatalf("Unable to read tar file: %v", err) + } + tarLayer, err := LayerFromReader(bytes.NewReader(ucBytes)) + if err != nil { + t.Fatalf("Unable to create layer from tar file: %v", err) + } + + gzBytes, err := os.ReadFile("gzip_content.tgz") + if err != nil { + t.Fatalf("Unable to read tar file: %v", err) + } + tarGzLayer, err := LayerFromReader(bytes.NewReader(gzBytes)) + if err != nil { + t.Fatalf("Unable to create layer from tar file: %v", err) + } + + if err := compare.Layers(tarLayer, tarGzLayer); err != nil { + t.Errorf("compare.Layers: %v", err) + } + + zstdBytes, err := os.ReadFile("zstd_content.tar.zst") + if err != nil { + t.Fatalf("Unable to read tar file: %v", err) + } + tarZstdLayer, err := LayerFromReader(bytes.NewReader(zstdBytes)) + if err != nil { + t.Fatalf("Unable to create layer from tar file: %v", err) + } + + if err := compare.Layers(tarLayer, tarZstdLayer); err != nil { + t.Errorf("compare.Layers: %v", err) + } +} + +// Compression settings matter in order for the digest, size, +// compressed assertions to pass +// +// Since our gzip.GzipReadCloser uses gzip.BestSpeed +// we need our fixture to use the same - bazel's pkg_tar doesn't +// seem to let you control compression settings +func setupFixtures(t *testing.T) { + t.Helper() + + setupCompressedTar(t, "gzip_content.tgz") + setupCompressedTar(t, "zstd_content.tar.zst") +} + +func setupCompressedTar(t *testing.T, fileName string) { + t.Helper() + in, err := os.Open("testdata/content.tar") + if err != nil { + t.Errorf("Error setting up fixtures: %v", err) + } + + defer in.Close() + + out, err := os.Create(fileName) + if err != nil { + t.Errorf("Error setting up fixtures: %v", err) + } + + defer out.Close() + + gw, _ := gzip.NewWriterLevel(out, gzip.BestSpeed) + defer gw.Close() + + _, err = io.Copy(gw, in) + if err != nil { + t.Errorf("Error setting up fixtures: %v", err) + } +} + +func teardownFixtures(t *testing.T) { + t.Helper() + if err := os.Remove("gzip_content.tgz"); err != nil { + t.Errorf("Error tearing down fixtures: %v", err) + } + if err := os.Remove("zstd_content.tar.zst"); err != nil { + t.Errorf("Error tearing down fixtures: %v", err) + } +} |