...

Source file src/github.com/google/go-containerregistry/pkg/crane/crane_test.go

Documentation: github.com/google/go-containerregistry/pkg/crane

     1  // Copyright 2019 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 crane_test
    16  
    17  import (
    18  	"archive/tar"
    19  	"bytes"
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"net/http"
    24  	"net/http/httptest"
    25  	"net/url"
    26  	"os"
    27  	"path"
    28  	"strings"
    29  	"testing"
    30  
    31  	"github.com/google/go-containerregistry/internal/compare"
    32  	"github.com/google/go-containerregistry/pkg/authn"
    33  	"github.com/google/go-containerregistry/pkg/crane"
    34  	"github.com/google/go-containerregistry/pkg/name"
    35  	"github.com/google/go-containerregistry/pkg/registry"
    36  	v1 "github.com/google/go-containerregistry/pkg/v1"
    37  	"github.com/google/go-containerregistry/pkg/v1/empty"
    38  	"github.com/google/go-containerregistry/pkg/v1/mutate"
    39  	"github.com/google/go-containerregistry/pkg/v1/random"
    40  	"github.com/google/go-containerregistry/pkg/v1/remote"
    41  )
    42  
    43  // TODO(jonjohnsonjr): Test crane.Copy failures.
    44  func TestCraneRegistry(t *testing.T) {
    45  	// Set up a fake registry.
    46  	s := httptest.NewServer(registry.New())
    47  	defer s.Close()
    48  	u, err := url.Parse(s.URL)
    49  	if err != nil {
    50  		t.Fatal(err)
    51  	}
    52  
    53  	src := fmt.Sprintf("%s/test/crane", u.Host)
    54  	dst := fmt.Sprintf("%s/test/crane/copy", u.Host)
    55  
    56  	// Expected values.
    57  	img, err := random.Image(1024, 5)
    58  	if err != nil {
    59  		t.Fatal(err)
    60  	}
    61  	digest, err := img.Digest()
    62  	if err != nil {
    63  		t.Fatal(err)
    64  	}
    65  	rawManifest, err := img.RawManifest()
    66  	if err != nil {
    67  		t.Fatal(err)
    68  	}
    69  	manifest, err := img.Manifest()
    70  	if err != nil {
    71  		t.Fatal(err)
    72  	}
    73  	config, err := img.RawConfigFile()
    74  	if err != nil {
    75  		t.Fatal(err)
    76  	}
    77  	layer, err := img.LayerByDigest(manifest.Layers[0].Digest)
    78  	if err != nil {
    79  		t.Fatal(err)
    80  	}
    81  
    82  	// Load up the registry.
    83  	if err := crane.Push(img, src); err != nil {
    84  		t.Fatal(err)
    85  	}
    86  
    87  	// Test that `crane.Foo` returns expected values.
    88  	d, err := crane.Digest(src)
    89  	if err != nil {
    90  		t.Error(err)
    91  	} else if d != digest.String() {
    92  		t.Errorf("Digest(): %v != %v", d, digest)
    93  	}
    94  
    95  	m, err := crane.Manifest(src)
    96  	if err != nil {
    97  		t.Error(err)
    98  	} else if string(m) != string(rawManifest) {
    99  		t.Errorf("Manifest(): %v != %v", m, rawManifest)
   100  	}
   101  
   102  	c, err := crane.Config(src)
   103  	if err != nil {
   104  		t.Error(err)
   105  	} else if string(c) != string(config) {
   106  		t.Errorf("Config(): %v != %v", c, config)
   107  	}
   108  
   109  	// Make sure we pull what we pushed.
   110  	pulled, err := crane.Pull(src)
   111  	if err != nil {
   112  		t.Error(err)
   113  	}
   114  	if err := compare.Images(img, pulled); err != nil {
   115  		t.Fatal(err)
   116  	}
   117  
   118  	// Test that the copied image is the same as the source.
   119  	if err := crane.Copy(src, dst); err != nil {
   120  		t.Fatal(err)
   121  	}
   122  
   123  	// Make sure what we copied is equivalent.
   124  	// Also, get options coverage in a dumb way.
   125  	copied, err := crane.Pull(dst, crane.Insecure, crane.WithTransport(http.DefaultTransport), crane.WithAuth(authn.Anonymous), crane.WithAuthFromKeychain(authn.DefaultKeychain), crane.WithUserAgent("crane/tests"))
   126  	if err != nil {
   127  		t.Fatal(err)
   128  	}
   129  	if err := compare.Images(pulled, copied); err != nil {
   130  		t.Fatal(err)
   131  	}
   132  
   133  	if err := crane.Tag(dst, "crane-tag"); err != nil {
   134  		t.Fatal(err)
   135  	}
   136  
   137  	// Make sure what we tagged is equivalent.
   138  	tagged, err := crane.Pull(fmt.Sprintf("%s:%s", dst, "crane-tag"))
   139  	if err != nil {
   140  		t.Fatal(err)
   141  	}
   142  	if err := compare.Images(pulled, tagged); err != nil {
   143  		t.Fatal(err)
   144  	}
   145  
   146  	layerRef := fmt.Sprintf("%s/test/crane@%s", u.Host, manifest.Layers[0].Digest)
   147  	pulledLayer, err := crane.PullLayer(layerRef)
   148  	if err != nil {
   149  		t.Fatal(err)
   150  	}
   151  
   152  	if err := compare.Layers(pulledLayer, layer); err != nil {
   153  		t.Fatal(err)
   154  	}
   155  
   156  	// List Tags
   157  	// dst variable have: latest and crane-tag
   158  	tags, err := crane.ListTags(dst)
   159  	if err != nil {
   160  		t.Fatal(err)
   161  	}
   162  	if len(tags) != 2 {
   163  		t.Fatalf("wanted 2 tags, got %d", len(tags))
   164  	}
   165  
   166  	// create 4 tags for dst
   167  	for i := 1; i < 5; i++ {
   168  		if err := crane.Tag(dst, fmt.Sprintf("honk-tag-%d", i)); err != nil {
   169  			t.Fatal(err)
   170  		}
   171  	}
   172  
   173  	tags, err = crane.ListTags(dst)
   174  	if err != nil {
   175  		t.Fatal(err)
   176  	}
   177  	if len(tags) != 6 {
   178  		t.Fatalf("wanted 6 tags, got %d", len(tags))
   179  	}
   180  
   181  	// Delete the non existing image
   182  	if err := crane.Delete(dst + ":honk-image"); err == nil {
   183  		t.Fatal("wanted err, got nil")
   184  	}
   185  
   186  	// Delete the image
   187  	if err := crane.Delete(src); err != nil {
   188  		t.Fatal(err)
   189  	}
   190  
   191  	// check if the image was really deleted
   192  	if _, err := crane.Pull(src); err == nil {
   193  		t.Fatal("wanted err, got nil")
   194  	}
   195  
   196  	// check if the copied image still exist
   197  	dstPulled, err := crane.Pull(dst)
   198  	if err != nil {
   199  		t.Fatal(err)
   200  	}
   201  	if err := compare.Images(dstPulled, copied); err != nil {
   202  		t.Fatal(err)
   203  	}
   204  
   205  	// List Catalog
   206  	repos, err := crane.Catalog(u.Host)
   207  	if err != nil {
   208  		t.Fatal(err)
   209  	}
   210  	if len(repos) != 2 {
   211  		t.Fatalf("wanted 2 repos, got %d", len(repos))
   212  	}
   213  
   214  	// Test pushing layer
   215  	layer, err = img.LayerByDigest(manifest.Layers[1].Digest)
   216  	if err != nil {
   217  		t.Fatal(err)
   218  	}
   219  	if err := crane.Upload(layer, dst); err != nil {
   220  		t.Fatal(err)
   221  	}
   222  }
   223  
   224  func TestCraneCopyIndex(t *testing.T) {
   225  	// Set up a fake registry.
   226  	s := httptest.NewServer(registry.New())
   227  	defer s.Close()
   228  	u, err := url.Parse(s.URL)
   229  	if err != nil {
   230  		t.Fatal(err)
   231  	}
   232  
   233  	src := fmt.Sprintf("%s/test/crane", u.Host)
   234  	dst := fmt.Sprintf("%s/test/crane/copy", u.Host)
   235  
   236  	// Load up the registry.
   237  	idx, err := random.Index(1024, 3, 3)
   238  	if err != nil {
   239  		t.Fatal(err)
   240  	}
   241  	ref, err := name.ParseReference(src)
   242  	if err != nil {
   243  		t.Fatal(err)
   244  	}
   245  	if err := remote.WriteIndex(ref, idx); err != nil {
   246  		t.Fatal(err)
   247  	}
   248  
   249  	// Test that the copied index is the same as the source.
   250  	if err := crane.Copy(src, dst); err != nil {
   251  		t.Fatal(err)
   252  	}
   253  
   254  	d, err := crane.Digest(src)
   255  	if err != nil {
   256  		t.Fatal(err)
   257  	}
   258  	cp, err := crane.Digest(dst)
   259  	if err != nil {
   260  		t.Fatal(err)
   261  	}
   262  	if d != cp {
   263  		t.Errorf("Copied Digest(): %v != %v", d, cp)
   264  	}
   265  }
   266  
   267  func TestWithPlatform(t *testing.T) {
   268  	// Set up a fake registry with a platform-specific image.
   269  	s := httptest.NewServer(registry.New())
   270  	defer s.Close()
   271  	u, err := url.Parse(s.URL)
   272  	if err != nil {
   273  		t.Fatal(err)
   274  	}
   275  
   276  	imgs := []mutate.IndexAddendum{}
   277  	for _, plat := range []string{
   278  		"linux/amd64",
   279  		"linux/arm",
   280  	} {
   281  		img, err := crane.Image(map[string][]byte{
   282  			"platform.txt": []byte(plat),
   283  		})
   284  		if err != nil {
   285  			t.Fatal(err)
   286  		}
   287  		parts := strings.Split(plat, "/")
   288  		imgs = append(imgs, mutate.IndexAddendum{
   289  			Add: img,
   290  			Descriptor: v1.Descriptor{
   291  				Platform: &v1.Platform{
   292  					OS:           parts[0],
   293  					Architecture: parts[1],
   294  				},
   295  			},
   296  		})
   297  	}
   298  
   299  	idx := mutate.AppendManifests(empty.Index, imgs...)
   300  
   301  	src := path.Join(u.Host, "src")
   302  	dst := path.Join(u.Host, "dst")
   303  
   304  	ref, err := name.ParseReference(src)
   305  	if err != nil {
   306  		t.Fatal(err)
   307  	}
   308  
   309  	// Populate registry so we can copy from it.
   310  	if err := remote.WriteIndex(ref, idx); err != nil {
   311  		t.Fatal(err)
   312  	}
   313  
   314  	if err := crane.Copy(src, dst, crane.WithPlatform(imgs[1].Platform)); err != nil {
   315  		t.Fatal(err)
   316  	}
   317  
   318  	want, err := crane.Manifest(src, crane.WithPlatform(imgs[1].Platform))
   319  	if err != nil {
   320  		t.Fatal(err)
   321  	}
   322  	got, err := crane.Manifest(dst)
   323  	if err != nil {
   324  		t.Fatal(err)
   325  	}
   326  
   327  	if string(got) != string(want) {
   328  		t.Errorf("Manifest(%q) != Manifest(%q): (\n\n%s\n\n!=\n\n%s\n\n)", dst, src, string(got), string(want))
   329  	}
   330  
   331  	arch := "real fake doors"
   332  
   333  	// Now do a fake platform, should fail
   334  	if _, err := crane.Manifest(src, crane.WithPlatform(&v1.Platform{
   335  		OS:           "does-not-exist",
   336  		Architecture: arch,
   337  	})); err == nil {
   338  		t.Error("crane.Manifest(fake platform): got nil want err")
   339  	} else if !strings.Contains(err.Error(), arch) {
   340  		t.Errorf("crane.Manifest(fake platform): expected %q in error, got: %v", arch, err)
   341  	}
   342  }
   343  
   344  func TestCraneTarball(t *testing.T) {
   345  	t.Parallel()
   346  	// Write an image as a tarball.
   347  	tmp, err := os.CreateTemp("", "")
   348  	if err != nil {
   349  		t.Fatal(err)
   350  	}
   351  	defer os.Remove(tmp.Name())
   352  
   353  	img, err := random.Image(1024, 5)
   354  	if err != nil {
   355  		t.Fatal(err)
   356  	}
   357  	digest, err := img.Digest()
   358  	if err != nil {
   359  		t.Fatal(err)
   360  	}
   361  	src := fmt.Sprintf("test/crane@%s", digest)
   362  
   363  	if err := crane.Save(img, src, tmp.Name()); err != nil {
   364  		t.Errorf("Save: %v", err)
   365  	}
   366  
   367  	// Make sure the image we load has a matching digest.
   368  	img, err = crane.Load(tmp.Name())
   369  	if err != nil {
   370  		t.Fatal(err)
   371  	}
   372  
   373  	d, err := img.Digest()
   374  	if err != nil {
   375  		t.Fatal(err)
   376  	}
   377  	if d != digest {
   378  		t.Errorf("digest mismatch: %v != %v", d, digest)
   379  	}
   380  }
   381  
   382  func TestCraneSaveLegacy(t *testing.T) {
   383  	t.Parallel()
   384  	// Write an image as a legacy tarball.
   385  	tmp, err := os.CreateTemp("", "")
   386  	if err != nil {
   387  		t.Fatal(err)
   388  	}
   389  	defer os.Remove(tmp.Name())
   390  
   391  	img, err := random.Image(1024, 5)
   392  	if err != nil {
   393  		t.Fatal(err)
   394  	}
   395  
   396  	if err := crane.SaveLegacy(img, "test/crane", tmp.Name()); err != nil {
   397  		t.Errorf("SaveOCI: %v", err)
   398  	}
   399  }
   400  
   401  func TestCraneSaveOCI(t *testing.T) {
   402  	t.Parallel()
   403  	// Write an image as an OCI image layout.
   404  	tmp := t.TempDir()
   405  
   406  	img, err := random.Image(1024, 5)
   407  	if err != nil {
   408  		t.Fatal(err)
   409  	}
   410  	if err := crane.SaveOCI(img, tmp); err != nil {
   411  		t.Errorf("SaveLegacy: %v", err)
   412  	}
   413  }
   414  
   415  func TestCraneFilesystem(t *testing.T) {
   416  	t.Parallel()
   417  	tmp, err := os.CreateTemp("", "")
   418  	if err != nil {
   419  		t.Fatal(err)
   420  	}
   421  	img, err := random.Image(1024, 5)
   422  	if err != nil {
   423  		t.Fatal(err)
   424  	}
   425  
   426  	name := "/some/file"
   427  	content := []byte("sentinel")
   428  
   429  	tw := tar.NewWriter(tmp)
   430  	if err := tw.WriteHeader(&tar.Header{
   431  		Size: int64(len(content)),
   432  		Name: name,
   433  	}); err != nil {
   434  		t.Fatal(err)
   435  	}
   436  	if _, err := tw.Write(content); err != nil {
   437  		t.Fatal(err)
   438  	}
   439  	tw.Flush()
   440  	tw.Close()
   441  
   442  	img, err = crane.Append(img, tmp.Name())
   443  	if err != nil {
   444  		t.Fatal(err)
   445  	}
   446  
   447  	var buf bytes.Buffer
   448  	if err := crane.Export(img, &buf); err != nil {
   449  		t.Fatal(err)
   450  	}
   451  
   452  	tr := tar.NewReader(&buf)
   453  	for {
   454  		header, err := tr.Next()
   455  		if errors.Is(err, io.EOF) {
   456  			t.Fatalf("didn't find find")
   457  		} else if err != nil {
   458  			t.Fatal(err)
   459  		}
   460  		if header.Name == name {
   461  			b, err := io.ReadAll(tr)
   462  			if err != nil {
   463  				t.Fatal(err)
   464  			}
   465  			if string(b) != string(content) {
   466  				t.Fatalf("got back wrong content: %v != %v", string(b), string(content))
   467  			}
   468  			break
   469  		}
   470  	}
   471  }
   472  
   473  func TestStreamingAppend(t *testing.T) {
   474  	// Stdin will be an uncompressed layer.
   475  	layer, err := crane.Layer(map[string][]byte{
   476  		"hello": []byte(`world`),
   477  	})
   478  	if err != nil {
   479  		t.Fatal(err)
   480  	}
   481  	rc, err := layer.Uncompressed()
   482  	if err != nil {
   483  		t.Fatal(err)
   484  	}
   485  
   486  	tmp, err := os.CreateTemp("", "crane-append")
   487  	if err != nil {
   488  		t.Fatal(err)
   489  	}
   490  	defer os.Remove(tmp.Name())
   491  
   492  	if _, err := io.Copy(tmp, rc); err != nil {
   493  		t.Fatal(err)
   494  	}
   495  
   496  	stdin := os.Stdin
   497  	defer func() {
   498  		os.Stdin = stdin
   499  	}()
   500  
   501  	os.Stdin = tmp
   502  
   503  	img, err := crane.Append(empty.Image, "-")
   504  	if err != nil {
   505  		t.Fatal(err)
   506  	}
   507  	ll, err := img.Layers()
   508  	if err != nil {
   509  		t.Fatal(err)
   510  	}
   511  	if want, got := 1, len(ll); want != got {
   512  		t.Errorf("crane.Append(stdin) - len(layers): want %d != got %d", want, got)
   513  	}
   514  }
   515  
   516  func TestBadInputs(t *testing.T) {
   517  	t.Parallel()
   518  	invalid := "/dev/null/@@@@@@"
   519  
   520  	// Create a valid image reference that will fail with not found.
   521  	s := httptest.NewServer(http.NotFoundHandler())
   522  	u, err := url.Parse(s.URL)
   523  	if err != nil {
   524  		t.Fatal(err)
   525  	}
   526  	valid404 := fmt.Sprintf("%s/some/image", u.Host)
   527  
   528  	// e drops the first parameter so we can use the result of a function
   529  	// that returns two values as an expression above. This is a bit of a go quirk.
   530  	e := func(_ any, err error) error {
   531  		return err
   532  	}
   533  
   534  	for _, tc := range []struct {
   535  		desc string
   536  		err  error
   537  	}{
   538  		{"Push(_, invalid)", crane.Push(nil, invalid)},
   539  		{"Upload(_, invalid)", crane.Upload(nil, invalid)},
   540  		{"Delete(invalid)", crane.Delete(invalid)},
   541  		{"Delete: 404", crane.Delete(valid404)},
   542  		{"Save(_, invalid)", crane.Save(nil, invalid, "")},
   543  		{"SaveLegacy(_, invalid)", crane.SaveLegacy(nil, invalid, "")},
   544  		{"SaveLegacy(_, invalid)", crane.SaveLegacy(nil, valid404, invalid)},
   545  		{"SaveOCI(_, invalid)", crane.SaveOCI(nil, "")},
   546  		{"Copy(invalid, invalid)", crane.Copy(invalid, invalid)},
   547  		{"Copy(404, invalid)", crane.Copy(valid404, invalid)},
   548  		{"Copy(404, 404)", crane.Copy(valid404, valid404)},
   549  		{"Tag(invalid, invalid)", crane.Tag(invalid, invalid)},
   550  		{"Tag(404, invalid)", crane.Tag(valid404, invalid)},
   551  		{"Tag(404, 404)", crane.Tag(valid404, valid404)},
   552  		// These return multiple values, which are hard to use as expressions.
   553  		{"Pull(invalid)", e(crane.Pull(invalid))},
   554  		{"Digest(invalid)", e(crane.Digest(invalid))},
   555  		{"Manifest(invalid)", e(crane.Manifest(invalid))},
   556  		{"Config(invalid)", e(crane.Config(invalid))},
   557  		{"Config(404)", e(crane.Config(valid404))},
   558  		{"ListTags(invalid)", e(crane.ListTags(invalid))},
   559  		{"ListTags(404)", e(crane.ListTags(valid404))},
   560  		{"Append(_, invalid)", e(crane.Append(nil, invalid))},
   561  		{"Catalog(invalid)", e(crane.Catalog(invalid))},
   562  		{"Catalog(404)", e(crane.Catalog(u.Host))},
   563  		{"PullLayer(invalid)", e(crane.PullLayer(invalid))},
   564  		{"LoadTag(_, invalid)", e(crane.LoadTag("", invalid))},
   565  		{"LoadTag(invalid, 404)", e(crane.LoadTag(invalid, valid404))},
   566  	} {
   567  		if tc.err == nil {
   568  			t.Errorf("%s: expected err, got nil", tc.desc)
   569  		}
   570  	}
   571  }
   572  

View as plain text