github.com/rish1988/moby@v25.0.2+incompatible/reference/store_test.go (about)

     1  package reference // import "github.com/docker/docker/reference"
     2  
     3  import (
     4  	"bytes"
     5  	"os"
     6  	"path/filepath"
     7  	"testing"
     8  
     9  	"github.com/distribution/reference"
    10  	"github.com/docker/docker/errdefs"
    11  	"github.com/opencontainers/go-digest"
    12  	"gotest.tools/v3/assert"
    13  	is "gotest.tools/v3/assert/cmp"
    14  )
    15  
    16  var (
    17  	saveLoadTestCases = map[string]digest.Digest{
    18  		"registry:5000/foobar:HEAD":      "sha256:470022b8af682154f57a2163d030eb369549549cba00edc69e1b99b46bb924d6",
    19  		"registry:5000/foobar:alternate": "sha256:ae300ebc4a4f00693702cfb0a5e0b7bc527b353828dc86ad09fb95c8a681b793",
    20  		"registry:5000/foobar:latest":    "sha256:6153498b9ac00968d71b66cca4eac37e990b5f9eb50c26877eb8799c8847451b",
    21  		"registry:5000/foobar:master":    "sha256:6c9917af4c4e05001b346421959d7ea81b6dc9d25718466a37a6add865dfd7fc",
    22  		"jess/hollywood:latest":          "sha256:ae7a5519a0a55a2d4ef20ddcbd5d0ca0888a1f7ab806acc8e2a27baf46f529fe",
    23  		"registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6": "sha256:24126a56805beb9711be5f4590cc2eb55ab8d4a85ebd618eed72bb19fc50631c",
    24  		"busybox:latest": "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c",
    25  	}
    26  
    27  	marshalledSaveLoadTestCases = []byte(`{"Repositories":{"busybox":{"busybox:latest":"sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c"},"jess/hollywood":{"jess/hollywood:latest":"sha256:ae7a5519a0a55a2d4ef20ddcbd5d0ca0888a1f7ab806acc8e2a27baf46f529fe"},"registry":{"registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6":"sha256:24126a56805beb9711be5f4590cc2eb55ab8d4a85ebd618eed72bb19fc50631c"},"registry:5000/foobar":{"registry:5000/foobar:HEAD":"sha256:470022b8af682154f57a2163d030eb369549549cba00edc69e1b99b46bb924d6","registry:5000/foobar:alternate":"sha256:ae300ebc4a4f00693702cfb0a5e0b7bc527b353828dc86ad09fb95c8a681b793","registry:5000/foobar:latest":"sha256:6153498b9ac00968d71b66cca4eac37e990b5f9eb50c26877eb8799c8847451b","registry:5000/foobar:master":"sha256:6c9917af4c4e05001b346421959d7ea81b6dc9d25718466a37a6add865dfd7fc"}}}`)
    28  )
    29  
    30  func TestLoad(t *testing.T) {
    31  	jsonFile := filepath.Join(t.TempDir(), "repositories.json")
    32  	err := os.WriteFile(jsonFile, marshalledSaveLoadTestCases, 0o666)
    33  	assert.NilError(t, err)
    34  
    35  	store, err := NewReferenceStore(jsonFile)
    36  	if err != nil {
    37  		t.Fatalf("error creating tag store: %v", err)
    38  	}
    39  
    40  	for refStr, expectedID := range saveLoadTestCases {
    41  		ref, err := reference.ParseNormalizedNamed(refStr)
    42  		if err != nil {
    43  			t.Fatalf("failed to parse reference: %v", err)
    44  		}
    45  		id, err := store.Get(ref)
    46  		if err != nil {
    47  			t.Fatalf("could not find reference %s: %v", refStr, err)
    48  		}
    49  		if id != expectedID {
    50  			t.Fatalf("expected %s - got %s", expectedID, id)
    51  		}
    52  	}
    53  }
    54  
    55  func TestSave(t *testing.T) {
    56  	jsonFile := filepath.Join(t.TempDir(), "repositories.json")
    57  	err := os.WriteFile(jsonFile, []byte(`{}`), 0o666)
    58  	assert.NilError(t, err)
    59  
    60  	store, err := NewReferenceStore(jsonFile)
    61  	if err != nil {
    62  		t.Fatalf("error creating tag store: %v", err)
    63  	}
    64  
    65  	for refStr, id := range saveLoadTestCases {
    66  		ref, err := reference.ParseNormalizedNamed(refStr)
    67  		if err != nil {
    68  			t.Fatalf("failed to parse reference: %v", err)
    69  		}
    70  		if canonical, ok := ref.(reference.Canonical); ok {
    71  			err = store.AddDigest(canonical, id, false)
    72  			if err != nil {
    73  				t.Fatalf("could not add digest reference %s: %v", refStr, err)
    74  			}
    75  		} else {
    76  			err = store.AddTag(ref, id, false)
    77  			if err != nil {
    78  				t.Fatalf("could not add reference %s: %v", refStr, err)
    79  			}
    80  		}
    81  	}
    82  
    83  	jsonBytes, err := os.ReadFile(jsonFile)
    84  	if err != nil {
    85  		t.Fatalf("could not read json file: %v", err)
    86  	}
    87  
    88  	if !bytes.Equal(jsonBytes, marshalledSaveLoadTestCases) {
    89  		t.Fatalf("save output did not match expectations\nexpected:\n%s\ngot:\n%s", marshalledSaveLoadTestCases, jsonBytes)
    90  	}
    91  }
    92  
    93  func TestAddDeleteGet(t *testing.T) {
    94  	jsonFile := filepath.Join(t.TempDir(), "repositories.json")
    95  	err := os.WriteFile(jsonFile, []byte(`{}`), 0o666)
    96  	assert.NilError(t, err)
    97  
    98  	store, err := NewReferenceStore(jsonFile)
    99  	if err != nil {
   100  		t.Fatalf("error creating tag store: %v", err)
   101  	}
   102  
   103  	testImageID1 := digest.Digest("sha256:9655aef5fd742a1b4e1b7b163aa9f1c76c186304bf39102283d80927c916ca9c")
   104  	testImageID2 := digest.Digest("sha256:9655aef5fd742a1b4e1b7b163aa9f1c76c186304bf39102283d80927c916ca9d")
   105  	testImageID3 := digest.Digest("sha256:9655aef5fd742a1b4e1b7b163aa9f1c76c186304bf39102283d80927c916ca9e")
   106  
   107  	// Try adding a reference with no tag or digest
   108  	nameOnly, err := reference.ParseNormalizedNamed("username/repo")
   109  	if err != nil {
   110  		t.Fatalf("could not parse reference: %v", err)
   111  	}
   112  	if err = store.AddTag(nameOnly, testImageID1, false); err != nil {
   113  		t.Fatalf("error adding to store: %v", err)
   114  	}
   115  
   116  	// Add a few references
   117  	ref1, err := reference.ParseNormalizedNamed("username/repo1:latest")
   118  	if err != nil {
   119  		t.Fatalf("could not parse reference: %v", err)
   120  	}
   121  	if err = store.AddTag(ref1, testImageID1, false); err != nil {
   122  		t.Fatalf("error adding to store: %v", err)
   123  	}
   124  
   125  	ref2, err := reference.ParseNormalizedNamed("username/repo1:old")
   126  	if err != nil {
   127  		t.Fatalf("could not parse reference: %v", err)
   128  	}
   129  	if err = store.AddTag(ref2, testImageID2, false); err != nil {
   130  		t.Fatalf("error adding to store: %v", err)
   131  	}
   132  
   133  	ref3, err := reference.ParseNormalizedNamed("username/repo1:alias")
   134  	if err != nil {
   135  		t.Fatalf("could not parse reference: %v", err)
   136  	}
   137  	if err = store.AddTag(ref3, testImageID1, false); err != nil {
   138  		t.Fatalf("error adding to store: %v", err)
   139  	}
   140  
   141  	ref4, err := reference.ParseNormalizedNamed("username/repo2:latest")
   142  	if err != nil {
   143  		t.Fatalf("could not parse reference: %v", err)
   144  	}
   145  	if err = store.AddTag(ref4, testImageID2, false); err != nil {
   146  		t.Fatalf("error adding to store: %v", err)
   147  	}
   148  	// Write the same values again; should silently succeed
   149  	if err = store.AddTag(ref4, testImageID2, false); err != nil {
   150  		t.Fatalf("error redundantly adding to store: %v", err)
   151  	}
   152  
   153  	ref5, err := reference.ParseNormalizedNamed("username/repo3@sha256:58153dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c")
   154  	if err != nil {
   155  		t.Fatalf("could not parse reference: %v", err)
   156  	}
   157  	if err = store.AddDigest(ref5.(reference.Canonical), testImageID2, false); err != nil {
   158  		t.Fatalf("error adding to store: %v", err)
   159  	}
   160  	// Write the same values again; should silently succeed
   161  	if err = store.AddDigest(ref5.(reference.Canonical), testImageID2, false); err != nil {
   162  		t.Fatalf("error redundantly adding to store: %v", err)
   163  	}
   164  	err = store.AddDigest(ref5.(reference.Canonical), testImageID3, false)
   165  	assert.Check(t, is.ErrorType(err, errdefs.IsConflict), "overwriting a digest with a different digest should fail")
   166  	err = store.AddDigest(ref5.(reference.Canonical), testImageID3, true)
   167  	assert.Check(t, is.ErrorType(err, errdefs.IsConflict), "overwriting a digest cannot be forced")
   168  
   169  	// Attempt to overwrite with force == false
   170  	err = store.AddTag(ref4, testImageID3, false)
   171  	assert.Check(t, is.ErrorType(err, errdefs.IsConflict), "did not get expected error on overwrite attempt")
   172  	// Repeat to overwrite with force == true
   173  	if err = store.AddTag(ref4, testImageID3, true); err != nil {
   174  		t.Fatalf("failed to force tag overwrite: %v", err)
   175  	}
   176  
   177  	// Check references so far
   178  	id, err := store.Get(nameOnly)
   179  	if err != nil {
   180  		t.Fatalf("Get returned error: %v", err)
   181  	}
   182  	if id != testImageID1 {
   183  		t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID1.String())
   184  	}
   185  
   186  	id, err = store.Get(ref1)
   187  	if err != nil {
   188  		t.Fatalf("Get returned error: %v", err)
   189  	}
   190  	if id != testImageID1 {
   191  		t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID1.String())
   192  	}
   193  
   194  	id, err = store.Get(ref2)
   195  	if err != nil {
   196  		t.Fatalf("Get returned error: %v", err)
   197  	}
   198  	if id != testImageID2 {
   199  		t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID2.String())
   200  	}
   201  
   202  	id, err = store.Get(ref3)
   203  	if err != nil {
   204  		t.Fatalf("Get returned error: %v", err)
   205  	}
   206  	if id != testImageID1 {
   207  		t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID1.String())
   208  	}
   209  
   210  	id, err = store.Get(ref4)
   211  	if err != nil {
   212  		t.Fatalf("Get returned error: %v", err)
   213  	}
   214  	if id != testImageID3 {
   215  		t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID3.String())
   216  	}
   217  
   218  	id, err = store.Get(ref5)
   219  	if err != nil {
   220  		t.Fatalf("Get returned error: %v", err)
   221  	}
   222  	if id != testImageID2 {
   223  		t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID3.String())
   224  	}
   225  
   226  	// Get should return ErrDoesNotExist for a nonexistent repo
   227  	nonExistRepo, err := reference.ParseNormalizedNamed("username/nonexistrepo:latest")
   228  	if err != nil {
   229  		t.Fatalf("could not parse reference: %v", err)
   230  	}
   231  	if _, err = store.Get(nonExistRepo); err != ErrDoesNotExist {
   232  		t.Fatal("Expected ErrDoesNotExist from Get")
   233  	}
   234  
   235  	// Get should return ErrDoesNotExist for a nonexistent tag
   236  	nonExistTag, err := reference.ParseNormalizedNamed("username/repo1:nonexist")
   237  	if err != nil {
   238  		t.Fatalf("could not parse reference: %v", err)
   239  	}
   240  	if _, err = store.Get(nonExistTag); err != ErrDoesNotExist {
   241  		t.Fatal("Expected ErrDoesNotExist from Get")
   242  	}
   243  
   244  	// Check References
   245  	refs := store.References(testImageID1)
   246  	if len(refs) != 3 {
   247  		t.Fatal("unexpected number of references")
   248  	}
   249  	// Looking for the references in this order verifies that they are
   250  	// returned lexically sorted.
   251  	if refs[0].String() != ref3.String() {
   252  		t.Fatalf("unexpected reference: %v", refs[0].String())
   253  	}
   254  	if refs[1].String() != ref1.String() {
   255  		t.Fatalf("unexpected reference: %v", refs[1].String())
   256  	}
   257  	if refs[2].String() != nameOnly.String()+":latest" {
   258  		t.Fatalf("unexpected reference: %v", refs[2].String())
   259  	}
   260  
   261  	// Check ReferencesByName
   262  	repoName, err := reference.ParseNormalizedNamed("username/repo1")
   263  	if err != nil {
   264  		t.Fatalf("could not parse reference: %v", err)
   265  	}
   266  	associations := store.ReferencesByName(repoName)
   267  	if len(associations) != 3 {
   268  		t.Fatal("unexpected number of associations")
   269  	}
   270  	// Looking for the associations in this order verifies that they are
   271  	// returned lexically sorted.
   272  	if associations[0].Ref.String() != ref3.String() {
   273  		t.Fatalf("unexpected reference: %v", associations[0].Ref.String())
   274  	}
   275  	if associations[0].ID != testImageID1 {
   276  		t.Fatalf("unexpected reference: %v", associations[0].Ref.String())
   277  	}
   278  	if associations[1].Ref.String() != ref1.String() {
   279  		t.Fatalf("unexpected reference: %v", associations[1].Ref.String())
   280  	}
   281  	if associations[1].ID != testImageID1 {
   282  		t.Fatalf("unexpected reference: %v", associations[1].Ref.String())
   283  	}
   284  	if associations[2].Ref.String() != ref2.String() {
   285  		t.Fatalf("unexpected reference: %v", associations[2].Ref.String())
   286  	}
   287  	if associations[2].ID != testImageID2 {
   288  		t.Fatalf("unexpected reference: %v", associations[2].Ref.String())
   289  	}
   290  
   291  	// Delete should return ErrDoesNotExist for a nonexistent repo
   292  	if _, err = store.Delete(nonExistRepo); err != ErrDoesNotExist {
   293  		t.Fatal("Expected ErrDoesNotExist from Delete")
   294  	}
   295  
   296  	// Delete should return ErrDoesNotExist for a nonexistent tag
   297  	if _, err = store.Delete(nonExistTag); err != ErrDoesNotExist {
   298  		t.Fatal("Expected ErrDoesNotExist from Delete")
   299  	}
   300  
   301  	// Delete a few references
   302  	if deleted, err := store.Delete(ref1); err != nil || !deleted {
   303  		t.Fatal("Delete failed")
   304  	}
   305  	if _, err := store.Get(ref1); err != ErrDoesNotExist {
   306  		t.Fatal("Expected ErrDoesNotExist from Get")
   307  	}
   308  	if deleted, err := store.Delete(ref5); err != nil || !deleted {
   309  		t.Fatal("Delete failed")
   310  	}
   311  	if _, err := store.Get(ref5); err != ErrDoesNotExist {
   312  		t.Fatal("Expected ErrDoesNotExist from Get")
   313  	}
   314  	if deleted, err := store.Delete(nameOnly); err != nil || !deleted {
   315  		t.Fatal("Delete failed")
   316  	}
   317  	if _, err := store.Get(nameOnly); err != ErrDoesNotExist {
   318  		t.Fatal("Expected ErrDoesNotExist from Get")
   319  	}
   320  }
   321  
   322  func TestInvalidTags(t *testing.T) {
   323  	store, err := NewReferenceStore(filepath.Join(t.TempDir(), "repositories.json"))
   324  	assert.NilError(t, err)
   325  	id := digest.Digest("sha256:470022b8af682154f57a2163d030eb369549549cba00edc69e1b99b46bb924d6")
   326  
   327  	// sha256 as repo name
   328  	ref, err := reference.ParseNormalizedNamed("sha256:abc")
   329  	assert.NilError(t, err)
   330  	err = store.AddTag(ref, id, true)
   331  	assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
   332  
   333  	// setting digest as a tag
   334  	ref, err = reference.ParseNormalizedNamed("registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6")
   335  	assert.NilError(t, err)
   336  
   337  	err = store.AddTag(ref, id, true)
   338  	assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
   339  }