...

Source file src/github.com/google/go-containerregistry/pkg/v1/tarball/layer_test.go

Documentation: github.com/google/go-containerregistry/pkg/v1/tarball

     1  // Copyright 2018 Google LLC All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package tarball
    16  
    17  import (
    18  	"bytes"
    19  	"compress/gzip"
    20  	"io"
    21  	"os"
    22  	"testing"
    23  
    24  	"github.com/containerd/stargz-snapshotter/estargz"
    25  	"github.com/google/go-containerregistry/internal/compare"
    26  	"github.com/google/go-containerregistry/pkg/compression"
    27  	v1 "github.com/google/go-containerregistry/pkg/v1"
    28  	"github.com/google/go-containerregistry/pkg/v1/types"
    29  	"github.com/google/go-containerregistry/pkg/v1/validate"
    30  )
    31  
    32  func TestLayerFromFile(t *testing.T) {
    33  	setupFixtures(t)
    34  	defer teardownFixtures(t)
    35  
    36  	tarLayer, err := LayerFromFile("testdata/content.tar")
    37  	if err != nil {
    38  		t.Fatalf("Unable to create layer from tar file: %v", err)
    39  	}
    40  
    41  	tarGzLayer, err := LayerFromFile("gzip_content.tgz")
    42  	if err != nil {
    43  		t.Fatalf("Unable to create layer from compressed tar file: %v", err)
    44  	}
    45  
    46  	tarZstdLayer, err := LayerFromFile("zstd_content.tar.zst")
    47  	if err != nil {
    48  		t.Fatalf("Unable to create layer from compressed tar file: %v", err)
    49  	}
    50  
    51  	if err := compare.Layers(tarLayer, tarGzLayer); err != nil {
    52  		t.Errorf("compare.Layers: %v", err)
    53  	}
    54  
    55  	if err := compare.Layers(tarLayer, tarZstdLayer); err != nil {
    56  		t.Errorf("compare.Layers: %v", err)
    57  	}
    58  
    59  	if err := validate.Layer(tarLayer); err != nil {
    60  		t.Errorf("validate.Layer(tarLayer): %v", err)
    61  	}
    62  
    63  	if err := validate.Layer(tarGzLayer); err != nil {
    64  		t.Errorf("validate.Layer(tarGzLayer): %v", err)
    65  	}
    66  
    67  	if err := validate.Layer(tarZstdLayer); err != nil {
    68  		t.Errorf("validate.Layer(tarZstdLayer): %v", err)
    69  	}
    70  
    71  	getTestDigest := func(testName string, opts ...LayerOption) v1.Hash {
    72  		layer, err := LayerFromFile("testdata/content.tar", opts...)
    73  		if err != nil {
    74  			t.Fatalf("Unable to create layer with '%s' compression from tar file: %v", testName, err)
    75  		}
    76  
    77  		digest, err := layer.Digest()
    78  		if err != nil {
    79  			t.Fatalf("Unable to generate digest with '%s' compression: %v", testName, err)
    80  		}
    81  
    82  		return digest
    83  	}
    84  
    85  	defaultDigest := getTestDigest("Gzip Default", WithCompressionLevel(gzip.DefaultCompression))
    86  	speedDigest := getTestDigest("Gzip BestSpeed", WithCompressionLevel(gzip.BestSpeed))
    87  	zstdDigest := getTestDigest("Zstd Default", WithCompression(compression.ZStd))
    88  	zstdDigest1 := getTestDigest("Zstd BestSpeed", WithCompression(compression.ZStd), WithCompressionLevel(1))
    89  
    90  	if defaultDigest.String() == speedDigest.String() {
    91  		t.Errorf("expected digests to differ: %s", defaultDigest.String())
    92  	}
    93  
    94  	if defaultDigest.String() == zstdDigest.String() {
    95  		t.Errorf("expected digests to differ: %s", defaultDigest.String())
    96  	}
    97  
    98  	if defaultDigest.String() == zstdDigest1.String() {
    99  		t.Errorf("expected digests to differ: %s", defaultDigest.String())
   100  	}
   101  }
   102  
   103  func TestLayerFromFileEstargz(t *testing.T) {
   104  	setupFixtures(t)
   105  	defer teardownFixtures(t)
   106  
   107  	tarLayer, err := LayerFromFile("testdata/content.tar", WithEstargz)
   108  	if err != nil {
   109  		t.Fatalf("Unable to create layer from tar file: %v", err)
   110  	}
   111  
   112  	if err := validate.Layer(tarLayer); err != nil {
   113  		t.Errorf("validate.Layer(tarLayer): %v", err)
   114  	}
   115  
   116  	tarLayerDefaultCompression, err := LayerFromFile("testdata/content.tar", WithEstargz, WithCompressionLevel(gzip.DefaultCompression))
   117  	if err != nil {
   118  		t.Fatalf("Unable to create layer with 'Default' compression from tar file: %v", err)
   119  	}
   120  	descriptorDefaultCompression, err := tarLayerDefaultCompression.(*layer).Descriptor()
   121  	if err != nil {
   122  		t.Fatalf("Descriptor() = %v", err)
   123  	} else if len(descriptorDefaultCompression.Annotations) != 1 {
   124  		t.Errorf("Annotations = %#v, wanted 1 annotation", descriptorDefaultCompression.Annotations)
   125  	}
   126  
   127  	defaultDigest, err := tarLayerDefaultCompression.Digest()
   128  	if err != nil {
   129  		t.Fatal("Unable to generate digest with 'Default' compression", err)
   130  	}
   131  
   132  	tarLayerSpeedCompression, err := LayerFromFile("testdata/content.tar", WithEstargz, WithCompressionLevel(gzip.BestSpeed))
   133  	if err != nil {
   134  		t.Fatalf("Unable to create layer with 'BestSpeed' compression from tar file: %v", err)
   135  	}
   136  	descriptorSpeedCompression, err := tarLayerSpeedCompression.(*layer).Descriptor()
   137  	if err != nil {
   138  		t.Fatalf("Descriptor() = %v", err)
   139  	} else if len(descriptorSpeedCompression.Annotations) != 1 {
   140  		t.Errorf("Annotations = %#v, wanted 1 annotation", descriptorSpeedCompression.Annotations)
   141  	}
   142  
   143  	speedDigest, err := tarLayerSpeedCompression.Digest()
   144  	if err != nil {
   145  		t.Fatal("Unable to generate digest with 'BestSpeed' compression", err)
   146  	}
   147  
   148  	if defaultDigest.String() == speedDigest.String() {
   149  		t.Errorf("expected digests to differ: %s", defaultDigest.String())
   150  	}
   151  
   152  	if descriptorDefaultCompression.Annotations[estargz.TOCJSONDigestAnnotation] == descriptorSpeedCompression.Annotations[estargz.TOCJSONDigestAnnotation] {
   153  		t.Errorf("wanted different toc digests got default: %s, speed: %s",
   154  			descriptorDefaultCompression.Annotations[estargz.TOCJSONDigestAnnotation],
   155  			descriptorSpeedCompression.Annotations[estargz.TOCJSONDigestAnnotation])
   156  	}
   157  
   158  	tarLayerPrioritizedFiles, err := LayerFromFile("testdata/content.tar",
   159  		WithEstargz,
   160  		// We compare with default, so pass for apples-to-apples comparison.
   161  		WithCompressionLevel(gzip.DefaultCompression),
   162  		// By passing a list of priority files, we expect the layer to be different.
   163  		WithEstargzOptions(estargz.WithPrioritizedFiles([]string{
   164  			"./bat",
   165  		})))
   166  	if err != nil {
   167  		t.Fatalf("Unable to create layer with prioritized files from tar file: %v", err)
   168  	}
   169  	descriptorPrioritizedFiles, err := tarLayerPrioritizedFiles.(*layer).Descriptor()
   170  	if err != nil {
   171  		t.Fatalf("Descriptor() = %v", err)
   172  	} else if len(descriptorPrioritizedFiles.Annotations) != 1 {
   173  		t.Errorf("Annotations = %#v, wanted 1 annotation", descriptorPrioritizedFiles.Annotations)
   174  	}
   175  
   176  	prioritizedDigest, err := tarLayerPrioritizedFiles.Digest()
   177  	if err != nil {
   178  		t.Fatal("Unable to generate digest with prioritized files", err)
   179  	}
   180  
   181  	if defaultDigest.String() == prioritizedDigest.String() {
   182  		t.Errorf("expected digests to differ: %s", defaultDigest.String())
   183  	}
   184  
   185  	if descriptorDefaultCompression.Annotations[estargz.TOCJSONDigestAnnotation] == descriptorPrioritizedFiles.Annotations[estargz.TOCJSONDigestAnnotation] {
   186  		t.Errorf("wanted different toc digests got default: %s, prioritized: %s",
   187  			descriptorDefaultCompression.Annotations[estargz.TOCJSONDigestAnnotation],
   188  			descriptorPrioritizedFiles.Annotations[estargz.TOCJSONDigestAnnotation])
   189  	}
   190  }
   191  
   192  func TestLayerFromOpenerReader(t *testing.T) {
   193  	setupFixtures(t)
   194  	defer teardownFixtures(t)
   195  
   196  	ucBytes, err := os.ReadFile("testdata/content.tar")
   197  	if err != nil {
   198  		t.Fatalf("Unable to read tar file: %v", err)
   199  	}
   200  	count := 0
   201  	ucOpener := func() (io.ReadCloser, error) {
   202  		count++
   203  		return io.NopCloser(bytes.NewReader(ucBytes)), nil
   204  	}
   205  	tarLayer, err := LayerFromOpener(ucOpener, WithCompressedCaching)
   206  	if err != nil {
   207  		t.Fatal("Unable to create layer from tar file:", err)
   208  	}
   209  	for i := 0; i < 10; i++ {
   210  		tarLayer.Compressed()
   211  	}
   212  
   213  	// Store the count and reset the counter.
   214  	cachedCount := count
   215  	count = 0
   216  
   217  	tarLayer, err = LayerFromOpener(ucOpener)
   218  	if err != nil {
   219  		t.Fatal("Unable to create layer from tar file:", err)
   220  	}
   221  	for i := 0; i < 10; i++ {
   222  		tarLayer.Compressed()
   223  	}
   224  
   225  	// We expect three calls: gzip sniff, diffid computation, cached compression
   226  	if cachedCount != 3 {
   227  		t.Errorf("cached count = %d, wanted %d", cachedCount, 3)
   228  	}
   229  	if cachedCount+10 != count {
   230  		t.Errorf("count = %d, wanted %d", count, cachedCount+10)
   231  	}
   232  
   233  	gzBytes, err := os.ReadFile("gzip_content.tgz")
   234  	if err != nil {
   235  		t.Fatalf("Unable to read tar file: %v", err)
   236  	}
   237  	gzOpener := func() (io.ReadCloser, error) {
   238  		return io.NopCloser(bytes.NewReader(gzBytes)), nil
   239  	}
   240  	tarGzLayer, err := LayerFromOpener(gzOpener)
   241  	if err != nil {
   242  		t.Fatalf("Unable to create layer from tar file: %v", err)
   243  	}
   244  
   245  	if err := compare.Layers(tarLayer, tarGzLayer); err != nil {
   246  		t.Errorf("compare.Layers: %v", err)
   247  	}
   248  
   249  	zstdBytes, err := os.ReadFile("zstd_content.tar.zst")
   250  	if err != nil {
   251  		t.Fatalf("Unable to read tar file: %v", err)
   252  	}
   253  	zstdOpener := func() (io.ReadCloser, error) {
   254  		return io.NopCloser(bytes.NewReader(zstdBytes)), nil
   255  	}
   256  	tarZstdLayer, err := LayerFromOpener(zstdOpener)
   257  	if err != nil {
   258  		t.Fatalf("Unable to create layer from tar file: %v", err)
   259  	}
   260  
   261  	if err := compare.Layers(tarLayer, tarZstdLayer); err != nil {
   262  		t.Errorf("compare.Layers: %v", err)
   263  	}
   264  }
   265  
   266  func TestWithMediaType(t *testing.T) {
   267  	setupFixtures(t)
   268  	defer teardownFixtures(t)
   269  
   270  	l, err := LayerFromFile("testdata/content.tar")
   271  	if err != nil {
   272  		t.Fatalf("Unable to create layer from tar file: %v", err)
   273  	}
   274  	got, err := l.MediaType()
   275  	if err != nil {
   276  		t.Fatalf("MediaType: %v", err)
   277  	}
   278  	if want := types.DockerLayer; got != want {
   279  		t.Errorf("got %v, want %v", got, want)
   280  	}
   281  
   282  	l, err = LayerFromFile("testdata/content.tar", WithMediaType(types.OCILayer))
   283  	if err != nil {
   284  		t.Fatalf("Unable to create layer from tar file: %v", err)
   285  	}
   286  	got, err = l.MediaType()
   287  	if err != nil {
   288  		t.Fatalf("MediaType: %v", err)
   289  	}
   290  	if want := types.OCILayer; got != want {
   291  		t.Errorf("got %v, want %v", got, want)
   292  	}
   293  }
   294  
   295  func TestLayerFromReader(t *testing.T) {
   296  	setupFixtures(t)
   297  	defer teardownFixtures(t)
   298  
   299  	ucBytes, err := os.ReadFile("testdata/content.tar")
   300  	if err != nil {
   301  		t.Fatalf("Unable to read tar file: %v", err)
   302  	}
   303  	tarLayer, err := LayerFromReader(bytes.NewReader(ucBytes))
   304  	if err != nil {
   305  		t.Fatalf("Unable to create layer from tar file: %v", err)
   306  	}
   307  
   308  	gzBytes, err := os.ReadFile("gzip_content.tgz")
   309  	if err != nil {
   310  		t.Fatalf("Unable to read tar file: %v", err)
   311  	}
   312  	tarGzLayer, err := LayerFromReader(bytes.NewReader(gzBytes))
   313  	if err != nil {
   314  		t.Fatalf("Unable to create layer from tar file: %v", err)
   315  	}
   316  
   317  	if err := compare.Layers(tarLayer, tarGzLayer); err != nil {
   318  		t.Errorf("compare.Layers: %v", err)
   319  	}
   320  
   321  	zstdBytes, err := os.ReadFile("zstd_content.tar.zst")
   322  	if err != nil {
   323  		t.Fatalf("Unable to read tar file: %v", err)
   324  	}
   325  	tarZstdLayer, err := LayerFromReader(bytes.NewReader(zstdBytes))
   326  	if err != nil {
   327  		t.Fatalf("Unable to create layer from tar file: %v", err)
   328  	}
   329  
   330  	if err := compare.Layers(tarLayer, tarZstdLayer); err != nil {
   331  		t.Errorf("compare.Layers: %v", err)
   332  	}
   333  }
   334  
   335  // Compression settings matter in order for the digest, size,
   336  // compressed assertions to pass
   337  //
   338  // Since our gzip.GzipReadCloser uses gzip.BestSpeed
   339  // we need our fixture to use the same - bazel's pkg_tar doesn't
   340  // seem to let you control compression settings
   341  func setupFixtures(t *testing.T) {
   342  	t.Helper()
   343  
   344  	setupCompressedTar(t, "gzip_content.tgz")
   345  	setupCompressedTar(t, "zstd_content.tar.zst")
   346  }
   347  
   348  func setupCompressedTar(t *testing.T, fileName string) {
   349  	t.Helper()
   350  	in, err := os.Open("testdata/content.tar")
   351  	if err != nil {
   352  		t.Errorf("Error setting up fixtures: %v", err)
   353  	}
   354  
   355  	defer in.Close()
   356  
   357  	out, err := os.Create(fileName)
   358  	if err != nil {
   359  		t.Errorf("Error setting up fixtures: %v", err)
   360  	}
   361  
   362  	defer out.Close()
   363  
   364  	gw, _ := gzip.NewWriterLevel(out, gzip.BestSpeed)
   365  	defer gw.Close()
   366  
   367  	_, err = io.Copy(gw, in)
   368  	if err != nil {
   369  		t.Errorf("Error setting up fixtures: %v", err)
   370  	}
   371  }
   372  
   373  func teardownFixtures(t *testing.T) {
   374  	t.Helper()
   375  	if err := os.Remove("gzip_content.tgz"); err != nil {
   376  		t.Errorf("Error tearing down fixtures: %v", err)
   377  	}
   378  	if err := os.Remove("zstd_content.tar.zst"); err != nil {
   379  		t.Errorf("Error tearing down fixtures: %v", err)
   380  	}
   381  }
   382  

View as plain text