github.com/mika/distribution@v2.2.2-0.20160108133430-a75790e3d8e0+incompatible/registry/storage/manifeststore_test.go (about)

     1  package storage
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"reflect"
     7  	"testing"
     8  
     9  	"github.com/docker/distribution"
    10  	"github.com/docker/distribution/context"
    11  	"github.com/docker/distribution/digest"
    12  	"github.com/docker/distribution/manifest"
    13  	"github.com/docker/distribution/manifest/schema1"
    14  	"github.com/docker/distribution/registry/storage/cache/memory"
    15  	"github.com/docker/distribution/registry/storage/driver"
    16  	"github.com/docker/distribution/registry/storage/driver/inmemory"
    17  	"github.com/docker/distribution/testutil"
    18  	"github.com/docker/libtrust"
    19  )
    20  
    21  type manifestStoreTestEnv struct {
    22  	ctx        context.Context
    23  	driver     driver.StorageDriver
    24  	registry   distribution.Namespace
    25  	repository distribution.Repository
    26  	name       string
    27  	tag        string
    28  }
    29  
    30  func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestEnv {
    31  	ctx := context.Background()
    32  	driver := inmemory.New()
    33  	registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(
    34  		memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
    35  	if err != nil {
    36  		t.Fatalf("error creating registry: %v", err)
    37  	}
    38  
    39  	repo, err := registry.Repository(ctx, name)
    40  	if err != nil {
    41  		t.Fatalf("unexpected error getting repo: %v", err)
    42  	}
    43  
    44  	return &manifestStoreTestEnv{
    45  		ctx:        ctx,
    46  		driver:     driver,
    47  		registry:   registry,
    48  		repository: repo,
    49  		name:       name,
    50  		tag:        tag,
    51  	}
    52  }
    53  
    54  func TestManifestStorage(t *testing.T) {
    55  	env := newManifestStoreTestEnv(t, "foo/bar", "thetag")
    56  	ctx := context.Background()
    57  	ms, err := env.repository.Manifests(ctx)
    58  	if err != nil {
    59  		t.Fatal(err)
    60  	}
    61  
    62  	m := schema1.Manifest{
    63  		Versioned: manifest.Versioned{
    64  			SchemaVersion: 1,
    65  		},
    66  		Name: env.name,
    67  		Tag:  env.tag,
    68  	}
    69  
    70  	// Build up some test layers and add them to the manifest, saving the
    71  	// readseekers for upload later.
    72  	testLayers := map[digest.Digest]io.ReadSeeker{}
    73  	for i := 0; i < 2; i++ {
    74  		rs, ds, err := testutil.CreateRandomTarFile()
    75  		if err != nil {
    76  			t.Fatalf("unexpected error generating test layer file")
    77  		}
    78  		dgst := digest.Digest(ds)
    79  
    80  		testLayers[digest.Digest(dgst)] = rs
    81  		m.FSLayers = append(m.FSLayers, schema1.FSLayer{
    82  			BlobSum: dgst,
    83  		})
    84  		m.History = append(m.History, schema1.History{
    85  			V1Compatibility: "",
    86  		})
    87  
    88  	}
    89  
    90  	pk, err := libtrust.GenerateECP256PrivateKey()
    91  	if err != nil {
    92  		t.Fatalf("unexpected error generating private key: %v", err)
    93  	}
    94  
    95  	sm, merr := schema1.Sign(&m, pk)
    96  	if merr != nil {
    97  		t.Fatalf("error signing manifest: %v", err)
    98  	}
    99  
   100  	_, err = ms.Put(ctx, sm)
   101  	if err == nil {
   102  		t.Fatalf("expected errors putting manifest with full verification")
   103  	}
   104  
   105  	switch err := err.(type) {
   106  	case distribution.ErrManifestVerification:
   107  		if len(err) != 2 {
   108  			t.Fatalf("expected 2 verification errors: %#v", err)
   109  		}
   110  
   111  		for _, err := range err {
   112  			if _, ok := err.(distribution.ErrManifestBlobUnknown); !ok {
   113  				t.Fatalf("unexpected error type: %v", err)
   114  			}
   115  		}
   116  	default:
   117  		t.Fatalf("unexpected error verifying manifest: %v", err)
   118  	}
   119  
   120  	// Now, upload the layers that were missing!
   121  	for dgst, rs := range testLayers {
   122  		wr, err := env.repository.Blobs(env.ctx).Create(env.ctx)
   123  		if err != nil {
   124  			t.Fatalf("unexpected error creating test upload: %v", err)
   125  		}
   126  
   127  		if _, err := io.Copy(wr, rs); err != nil {
   128  			t.Fatalf("unexpected error copying to upload: %v", err)
   129  		}
   130  
   131  		if _, err := wr.Commit(env.ctx, distribution.Descriptor{Digest: dgst}); err != nil {
   132  			t.Fatalf("unexpected error finishing upload: %v", err)
   133  		}
   134  	}
   135  
   136  	var manifestDigest digest.Digest
   137  	if manifestDigest, err = ms.Put(ctx, sm); err != nil {
   138  		t.Fatalf("unexpected error putting manifest: %v", err)
   139  	}
   140  
   141  	exists, err := ms.Exists(ctx, manifestDigest)
   142  	if err != nil {
   143  		t.Fatalf("unexpected error checking manifest existence: %#v", err)
   144  	}
   145  
   146  	if !exists {
   147  		t.Fatalf("manifest should exist")
   148  	}
   149  
   150  	fromStore, err := ms.Get(ctx, manifestDigest)
   151  	if err != nil {
   152  		t.Fatalf("unexpected error fetching manifest: %v", err)
   153  	}
   154  
   155  	fetchedManifest, ok := fromStore.(*schema1.SignedManifest)
   156  	if !ok {
   157  		t.Fatalf("unexpected manifest type from signedstore")
   158  	}
   159  
   160  	if !reflect.DeepEqual(fetchedManifest, sm) {
   161  		t.Fatalf("fetched manifest not equal: %#v != %#v", fetchedManifest, sm)
   162  	}
   163  
   164  	_, pl, err := fetchedManifest.Payload()
   165  	if err != nil {
   166  		t.Fatalf("error getting payload %#v", err)
   167  	}
   168  
   169  	fetchedJWS, err := libtrust.ParsePrettySignature(pl, "signatures")
   170  	if err != nil {
   171  		t.Fatalf("unexpected error parsing jws: %v", err)
   172  	}
   173  
   174  	payload, err := fetchedJWS.Payload()
   175  	if err != nil {
   176  		t.Fatalf("unexpected error extracting payload: %v", err)
   177  	}
   178  
   179  	// Now that we have a payload, take a moment to check that the manifest is
   180  	// return by the payload digest.
   181  
   182  	dgst := digest.FromBytes(payload)
   183  	exists, err = ms.Exists(ctx, dgst)
   184  	if err != nil {
   185  		t.Fatalf("error checking manifest existence by digest: %v", err)
   186  	}
   187  
   188  	if !exists {
   189  		t.Fatalf("manifest %s should exist", dgst)
   190  	}
   191  
   192  	fetchedByDigest, err := ms.Get(ctx, dgst)
   193  	if err != nil {
   194  		t.Fatalf("unexpected error fetching manifest by digest: %v", err)
   195  	}
   196  
   197  	if !reflect.DeepEqual(fetchedByDigest, fetchedManifest) {
   198  		t.Fatalf("fetched manifest not equal: %#v != %#v", fetchedByDigest, fetchedManifest)
   199  	}
   200  
   201  	sigs, err := fetchedJWS.Signatures()
   202  	if err != nil {
   203  		t.Fatalf("unable to extract signatures: %v", err)
   204  	}
   205  
   206  	if len(sigs) != 1 {
   207  		t.Fatalf("unexpected number of signatures: %d != %d", len(sigs), 1)
   208  	}
   209  
   210  	// Now, push the same manifest with a different key
   211  	pk2, err := libtrust.GenerateECP256PrivateKey()
   212  	if err != nil {
   213  		t.Fatalf("unexpected error generating private key: %v", err)
   214  	}
   215  
   216  	sm2, err := schema1.Sign(&m, pk2)
   217  	if err != nil {
   218  		t.Fatalf("unexpected error signing manifest: %v", err)
   219  	}
   220  	_, pl, err = sm2.Payload()
   221  	if err != nil {
   222  		t.Fatalf("error getting payload %#v", err)
   223  	}
   224  
   225  	jws2, err := libtrust.ParsePrettySignature(pl, "signatures")
   226  	if err != nil {
   227  		t.Fatalf("error parsing signature: %v", err)
   228  	}
   229  
   230  	sigs2, err := jws2.Signatures()
   231  	if err != nil {
   232  		t.Fatalf("unable to extract signatures: %v", err)
   233  	}
   234  
   235  	if len(sigs2) != 1 {
   236  		t.Fatalf("unexpected number of signatures: %d != %d", len(sigs2), 1)
   237  	}
   238  
   239  	if manifestDigest, err = ms.Put(ctx, sm2); err != nil {
   240  		t.Fatalf("unexpected error putting manifest: %v", err)
   241  	}
   242  
   243  	fromStore, err = ms.Get(ctx, manifestDigest)
   244  	if err != nil {
   245  		t.Fatalf("unexpected error fetching manifest: %v", err)
   246  	}
   247  
   248  	fetched, ok := fromStore.(*schema1.SignedManifest)
   249  	if !ok {
   250  		t.Fatalf("unexpected type from signed manifeststore : %T", fetched)
   251  	}
   252  
   253  	if _, err := schema1.Verify(fetched); err != nil {
   254  		t.Fatalf("unexpected error verifying manifest: %v", err)
   255  	}
   256  
   257  	// Assemble our payload and two signatures to get what we expect!
   258  	expectedJWS, err := libtrust.NewJSONSignature(payload, sigs[0], sigs2[0])
   259  	if err != nil {
   260  		t.Fatalf("unexpected error merging jws: %v", err)
   261  	}
   262  
   263  	expectedSigs, err := expectedJWS.Signatures()
   264  	if err != nil {
   265  		t.Fatalf("unexpected error getting expected signatures: %v", err)
   266  	}
   267  
   268  	_, pl, err = fetched.Payload()
   269  	if err != nil {
   270  		t.Fatalf("error getting payload %#v", err)
   271  	}
   272  
   273  	receivedJWS, err := libtrust.ParsePrettySignature(pl, "signatures")
   274  	if err != nil {
   275  		t.Fatalf("unexpected error parsing jws: %v", err)
   276  	}
   277  
   278  	receivedPayload, err := receivedJWS.Payload()
   279  	if err != nil {
   280  		t.Fatalf("unexpected error extracting received payload: %v", err)
   281  	}
   282  
   283  	if !bytes.Equal(receivedPayload, payload) {
   284  		t.Fatalf("payloads are not equal")
   285  	}
   286  
   287  	receivedSigs, err := receivedJWS.Signatures()
   288  	if err != nil {
   289  		t.Fatalf("error getting signatures: %v", err)
   290  	}
   291  
   292  	for i, sig := range receivedSigs {
   293  		if !bytes.Equal(sig, expectedSigs[i]) {
   294  			t.Fatalf("mismatched signatures from remote: %v != %v", string(sig), string(expectedSigs[i]))
   295  		}
   296  	}
   297  
   298  	// Test deleting manifests
   299  	err = ms.Delete(ctx, dgst)
   300  	if err != nil {
   301  		t.Fatalf("unexpected an error deleting manifest by digest: %v", err)
   302  	}
   303  
   304  	exists, err = ms.Exists(ctx, dgst)
   305  	if err != nil {
   306  		t.Fatalf("Error querying manifest existence")
   307  	}
   308  	if exists {
   309  		t.Errorf("Deleted manifest should not exist")
   310  	}
   311  
   312  	deletedManifest, err := ms.Get(ctx, dgst)
   313  	if err == nil {
   314  		t.Errorf("Unexpected success getting deleted manifest")
   315  	}
   316  	switch err.(type) {
   317  	case distribution.ErrManifestUnknownRevision:
   318  		break
   319  	default:
   320  		t.Errorf("Unexpected error getting deleted manifest: %s", reflect.ValueOf(err).Type())
   321  	}
   322  
   323  	if deletedManifest != nil {
   324  		t.Errorf("Deleted manifest get returned non-nil")
   325  	}
   326  
   327  	// Re-upload should restore manifest to a good state
   328  	_, err = ms.Put(ctx, sm)
   329  	if err != nil {
   330  		t.Errorf("Error re-uploading deleted manifest")
   331  	}
   332  
   333  	exists, err = ms.Exists(ctx, dgst)
   334  	if err != nil {
   335  		t.Fatalf("Error querying manifest existence")
   336  	}
   337  	if !exists {
   338  		t.Errorf("Restored manifest should exist")
   339  	}
   340  
   341  	deletedManifest, err = ms.Get(ctx, dgst)
   342  	if err != nil {
   343  		t.Errorf("Unexpected error getting manifest")
   344  	}
   345  	if deletedManifest == nil {
   346  		t.Errorf("Deleted manifest get returned non-nil")
   347  	}
   348  
   349  	r, err := NewRegistry(ctx, env.driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableRedirect)
   350  	if err != nil {
   351  		t.Fatalf("error creating registry: %v", err)
   352  	}
   353  	repo, err := r.Repository(ctx, env.name)
   354  	if err != nil {
   355  		t.Fatalf("unexpected error getting repo: %v", err)
   356  	}
   357  	ms, err = repo.Manifests(ctx)
   358  	if err != nil {
   359  		t.Fatal(err)
   360  	}
   361  	err = ms.Delete(ctx, dgst)
   362  	if err == nil {
   363  		t.Errorf("Unexpected success deleting while disabled")
   364  	}
   365  }
   366  
   367  // TestLinkPathFuncs ensures that the link path functions behavior are locked
   368  // down and implemented as expected.
   369  func TestLinkPathFuncs(t *testing.T) {
   370  	for _, testcase := range []struct {
   371  		repo       string
   372  		digest     digest.Digest
   373  		linkPathFn linkPathFunc
   374  		expected   string
   375  	}{
   376  		{
   377  			repo:       "foo/bar",
   378  			digest:     "sha256:deadbeaf98fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
   379  			linkPathFn: blobLinkPath,
   380  			expected:   "/docker/registry/v2/repositories/foo/bar/_layers/sha256/deadbeaf98fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/link",
   381  		},
   382  		{
   383  			repo:       "foo/bar",
   384  			digest:     "sha256:deadbeaf98fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
   385  			linkPathFn: manifestRevisionLinkPath,
   386  			expected:   "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/deadbeaf98fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/link",
   387  		},
   388  	} {
   389  		p, err := testcase.linkPathFn(testcase.repo, testcase.digest)
   390  		if err != nil {
   391  			t.Fatalf("unexpected error calling linkPathFn(pm, %q, %q): %v", testcase.repo, testcase.digest, err)
   392  		}
   393  
   394  		if p != testcase.expected {
   395  			t.Fatalf("incorrect path returned: %q != %q", p, testcase.expected)
   396  		}
   397  	}
   398  
   399  }