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

     1  package storage
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/sha256"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"os"
    10  	"testing"
    11  
    12  	"github.com/docker/distribution"
    13  	"github.com/docker/distribution/context"
    14  	"github.com/docker/distribution/digest"
    15  	"github.com/docker/distribution/registry/storage/cache/memory"
    16  	"github.com/docker/distribution/registry/storage/driver/inmemory"
    17  	"github.com/docker/distribution/testutil"
    18  )
    19  
    20  // TestSimpleBlobUpload covers the blob upload process, exercising common
    21  // error paths that might be seen during an upload.
    22  func TestSimpleBlobUpload(t *testing.T) {
    23  	randomDataReader, dgst, err := testutil.CreateRandomTarFile()
    24  	if err != nil {
    25  		t.Fatalf("error creating random reader: %v", err)
    26  	}
    27  
    28  	ctx := context.Background()
    29  	imageName := "foo/bar"
    30  	driver := inmemory.New()
    31  	registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
    32  	if err != nil {
    33  		t.Fatalf("error creating registry: %v", err)
    34  	}
    35  	repository, err := registry.Repository(ctx, imageName)
    36  	if err != nil {
    37  		t.Fatalf("unexpected error getting repo: %v", err)
    38  	}
    39  	bs := repository.Blobs(ctx)
    40  
    41  	h := sha256.New()
    42  	rd := io.TeeReader(randomDataReader, h)
    43  
    44  	blobUpload, err := bs.Create(ctx)
    45  
    46  	if err != nil {
    47  		t.Fatalf("unexpected error starting layer upload: %s", err)
    48  	}
    49  
    50  	// Cancel the upload then restart it
    51  	if err := blobUpload.Cancel(ctx); err != nil {
    52  		t.Fatalf("unexpected error during upload cancellation: %v", err)
    53  	}
    54  
    55  	// Do a resume, get unknown upload
    56  	blobUpload, err = bs.Resume(ctx, blobUpload.ID())
    57  	if err != distribution.ErrBlobUploadUnknown {
    58  		t.Fatalf("unexpected error resuming upload, should be unknown: %v", err)
    59  	}
    60  
    61  	// Restart!
    62  	blobUpload, err = bs.Create(ctx)
    63  	if err != nil {
    64  		t.Fatalf("unexpected error starting layer upload: %s", err)
    65  	}
    66  
    67  	// Get the size of our random tarfile
    68  	randomDataSize, err := seekerSize(randomDataReader)
    69  	if err != nil {
    70  		t.Fatalf("error getting seeker size of random data: %v", err)
    71  	}
    72  
    73  	nn, err := io.Copy(blobUpload, rd)
    74  	if err != nil {
    75  		t.Fatalf("unexpected error uploading layer data: %v", err)
    76  	}
    77  
    78  	if nn != randomDataSize {
    79  		t.Fatalf("layer data write incomplete")
    80  	}
    81  
    82  	offset, err := blobUpload.Seek(0, os.SEEK_CUR)
    83  	if err != nil {
    84  		t.Fatalf("unexpected error seeking layer upload: %v", err)
    85  	}
    86  
    87  	if offset != nn {
    88  		t.Fatalf("blobUpload not updated with correct offset: %v != %v", offset, nn)
    89  	}
    90  	blobUpload.Close()
    91  
    92  	// Do a resume, for good fun
    93  	blobUpload, err = bs.Resume(ctx, blobUpload.ID())
    94  	if err != nil {
    95  		t.Fatalf("unexpected error resuming upload: %v", err)
    96  	}
    97  
    98  	sha256Digest := digest.NewDigest("sha256", h)
    99  	desc, err := blobUpload.Commit(ctx, distribution.Descriptor{Digest: dgst})
   100  	if err != nil {
   101  		t.Fatalf("unexpected error finishing layer upload: %v", err)
   102  	}
   103  
   104  	// After finishing an upload, it should no longer exist.
   105  	if _, err := bs.Resume(ctx, blobUpload.ID()); err != distribution.ErrBlobUploadUnknown {
   106  		t.Fatalf("expected layer upload to be unknown, got %v", err)
   107  	}
   108  
   109  	// Test for existence.
   110  	statDesc, err := bs.Stat(ctx, desc.Digest)
   111  	if err != nil {
   112  		t.Fatalf("unexpected error checking for existence: %v, %#v", err, bs)
   113  	}
   114  
   115  	if statDesc != desc {
   116  		t.Fatalf("descriptors not equal: %v != %v", statDesc, desc)
   117  	}
   118  
   119  	rc, err := bs.Open(ctx, desc.Digest)
   120  	if err != nil {
   121  		t.Fatalf("unexpected error opening blob for read: %v", err)
   122  	}
   123  	defer rc.Close()
   124  
   125  	h.Reset()
   126  	nn, err = io.Copy(h, rc)
   127  	if err != nil {
   128  		t.Fatalf("error reading layer: %v", err)
   129  	}
   130  
   131  	if nn != randomDataSize {
   132  		t.Fatalf("incorrect read length")
   133  	}
   134  
   135  	if digest.NewDigest("sha256", h) != sha256Digest {
   136  		t.Fatalf("unexpected digest from uploaded layer: %q != %q", digest.NewDigest("sha256", h), sha256Digest)
   137  	}
   138  
   139  	// Delete a blob
   140  	err = bs.Delete(ctx, desc.Digest)
   141  	if err != nil {
   142  		t.Fatalf("Unexpected error deleting blob")
   143  	}
   144  
   145  	d, err := bs.Stat(ctx, desc.Digest)
   146  	if err == nil {
   147  		t.Fatalf("unexpected non-error stating deleted blob: %v", d)
   148  	}
   149  
   150  	switch err {
   151  	case distribution.ErrBlobUnknown:
   152  		break
   153  	default:
   154  		t.Errorf("Unexpected error type stat-ing deleted manifest: %#v", err)
   155  	}
   156  
   157  	_, err = bs.Open(ctx, desc.Digest)
   158  	if err == nil {
   159  		t.Fatalf("unexpected success opening deleted blob for read")
   160  	}
   161  
   162  	switch err {
   163  	case distribution.ErrBlobUnknown:
   164  		break
   165  	default:
   166  		t.Errorf("Unexpected error type getting deleted manifest: %#v", err)
   167  	}
   168  
   169  	// Re-upload the blob
   170  	randomBlob, err := ioutil.ReadAll(randomDataReader)
   171  	if err != nil {
   172  		t.Fatalf("Error reading all of blob %s", err.Error())
   173  	}
   174  	expectedDigest := digest.FromBytes(randomBlob)
   175  	simpleUpload(t, bs, randomBlob, expectedDigest)
   176  
   177  	d, err = bs.Stat(ctx, expectedDigest)
   178  	if err != nil {
   179  		t.Errorf("unexpected error stat-ing blob")
   180  	}
   181  	if d.Digest != expectedDigest {
   182  		t.Errorf("Mismatching digest with restored blob")
   183  	}
   184  
   185  	_, err = bs.Open(ctx, expectedDigest)
   186  	if err != nil {
   187  		t.Errorf("Unexpected error opening blob")
   188  	}
   189  
   190  	// Reuse state to test delete with a delete-disabled registry
   191  	registry, err = NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableRedirect)
   192  	if err != nil {
   193  		t.Fatalf("error creating registry: %v", err)
   194  	}
   195  	repository, err = registry.Repository(ctx, imageName)
   196  	if err != nil {
   197  		t.Fatalf("unexpected error getting repo: %v", err)
   198  	}
   199  	bs = repository.Blobs(ctx)
   200  	err = bs.Delete(ctx, desc.Digest)
   201  	if err == nil {
   202  		t.Errorf("Unexpected success deleting while disabled")
   203  	}
   204  }
   205  
   206  // TestSimpleBlobRead just creates a simple blob file and ensures that basic
   207  // open, read, seek, read works. More specific edge cases should be covered in
   208  // other tests.
   209  func TestSimpleBlobRead(t *testing.T) {
   210  	ctx := context.Background()
   211  	imageName := "foo/bar"
   212  	driver := inmemory.New()
   213  	registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
   214  	if err != nil {
   215  		t.Fatalf("error creating registry: %v", err)
   216  	}
   217  	repository, err := registry.Repository(ctx, imageName)
   218  	if err != nil {
   219  		t.Fatalf("unexpected error getting repo: %v", err)
   220  	}
   221  	bs := repository.Blobs(ctx)
   222  
   223  	randomLayerReader, dgst, err := testutil.CreateRandomTarFile() // TODO(stevvooe): Consider using just a random string.
   224  	if err != nil {
   225  		t.Fatalf("error creating random data: %v", err)
   226  	}
   227  
   228  	// Test for existence.
   229  	desc, err := bs.Stat(ctx, dgst)
   230  	if err != distribution.ErrBlobUnknown {
   231  		t.Fatalf("expected not found error when testing for existence: %v", err)
   232  	}
   233  
   234  	rc, err := bs.Open(ctx, dgst)
   235  	if err != distribution.ErrBlobUnknown {
   236  		t.Fatalf("expected not found error when opening non-existent blob: %v", err)
   237  	}
   238  
   239  	randomLayerSize, err := seekerSize(randomLayerReader)
   240  	if err != nil {
   241  		t.Fatalf("error getting seeker size for random layer: %v", err)
   242  	}
   243  
   244  	descBefore := distribution.Descriptor{Digest: dgst, MediaType: "application/octet-stream", Size: randomLayerSize}
   245  	t.Logf("desc: %v", descBefore)
   246  
   247  	desc, err = addBlob(ctx, bs, descBefore, randomLayerReader)
   248  	if err != nil {
   249  		t.Fatalf("error adding blob to blobservice: %v", err)
   250  	}
   251  
   252  	if desc.Size != randomLayerSize {
   253  		t.Fatalf("committed blob has incorrect length: %v != %v", desc.Size, randomLayerSize)
   254  	}
   255  
   256  	rc, err = bs.Open(ctx, desc.Digest) // note that we are opening with original digest.
   257  	if err != nil {
   258  		t.Fatalf("error opening blob with %v: %v", dgst, err)
   259  	}
   260  	defer rc.Close()
   261  
   262  	// Now check the sha digest and ensure its the same
   263  	h := sha256.New()
   264  	nn, err := io.Copy(h, rc)
   265  	if err != nil {
   266  		t.Fatalf("unexpected error copying to hash: %v", err)
   267  	}
   268  
   269  	if nn != randomLayerSize {
   270  		t.Fatalf("stored incorrect number of bytes in blob: %d != %d", nn, randomLayerSize)
   271  	}
   272  
   273  	sha256Digest := digest.NewDigest("sha256", h)
   274  	if sha256Digest != desc.Digest {
   275  		t.Fatalf("fetched digest does not match: %q != %q", sha256Digest, desc.Digest)
   276  	}
   277  
   278  	// Now seek back the blob, read the whole thing and check against randomLayerData
   279  	offset, err := rc.Seek(0, os.SEEK_SET)
   280  	if err != nil {
   281  		t.Fatalf("error seeking blob: %v", err)
   282  	}
   283  
   284  	if offset != 0 {
   285  		t.Fatalf("seek failed: expected 0 offset, got %d", offset)
   286  	}
   287  
   288  	p, err := ioutil.ReadAll(rc)
   289  	if err != nil {
   290  		t.Fatalf("error reading all of blob: %v", err)
   291  	}
   292  
   293  	if len(p) != int(randomLayerSize) {
   294  		t.Fatalf("blob data read has different length: %v != %v", len(p), randomLayerSize)
   295  	}
   296  
   297  	// Reset the randomLayerReader and read back the buffer
   298  	_, err = randomLayerReader.Seek(0, os.SEEK_SET)
   299  	if err != nil {
   300  		t.Fatalf("error resetting layer reader: %v", err)
   301  	}
   302  
   303  	randomLayerData, err := ioutil.ReadAll(randomLayerReader)
   304  	if err != nil {
   305  		t.Fatalf("random layer read failed: %v", err)
   306  	}
   307  
   308  	if !bytes.Equal(p, randomLayerData) {
   309  		t.Fatalf("layer data not equal")
   310  	}
   311  }
   312  
   313  // TestLayerUploadZeroLength uploads zero-length
   314  func TestLayerUploadZeroLength(t *testing.T) {
   315  	ctx := context.Background()
   316  	imageName := "foo/bar"
   317  	driver := inmemory.New()
   318  	registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
   319  	if err != nil {
   320  		t.Fatalf("error creating registry: %v", err)
   321  	}
   322  	repository, err := registry.Repository(ctx, imageName)
   323  	if err != nil {
   324  		t.Fatalf("unexpected error getting repo: %v", err)
   325  	}
   326  	bs := repository.Blobs(ctx)
   327  
   328  	simpleUpload(t, bs, []byte{}, digest.DigestSha256EmptyTar)
   329  }
   330  
   331  func simpleUpload(t *testing.T, bs distribution.BlobIngester, blob []byte, expectedDigest digest.Digest) {
   332  	ctx := context.Background()
   333  	wr, err := bs.Create(ctx)
   334  	if err != nil {
   335  		t.Fatalf("unexpected error starting upload: %v", err)
   336  	}
   337  
   338  	nn, err := io.Copy(wr, bytes.NewReader(blob))
   339  	if err != nil {
   340  		t.Fatalf("error copying into blob writer: %v", err)
   341  	}
   342  
   343  	if nn != 0 {
   344  		t.Fatalf("unexpected number of bytes copied: %v > 0", nn)
   345  	}
   346  
   347  	dgst, err := digest.FromReader(bytes.NewReader(blob))
   348  	if err != nil {
   349  		t.Fatalf("error getting digest: %v", err)
   350  	}
   351  
   352  	if dgst != expectedDigest {
   353  		// sanity check on zero digest
   354  		t.Fatalf("digest not as expected: %v != %v", dgst, expectedDigest)
   355  	}
   356  
   357  	desc, err := wr.Commit(ctx, distribution.Descriptor{Digest: dgst})
   358  	if err != nil {
   359  		t.Fatalf("unexpected error committing write: %v", err)
   360  	}
   361  
   362  	if desc.Digest != dgst {
   363  		t.Fatalf("unexpected digest: %v != %v", desc.Digest, dgst)
   364  	}
   365  }
   366  
   367  // seekerSize seeks to the end of seeker, checks the size and returns it to
   368  // the original state, returning the size. The state of the seeker should be
   369  // treated as unknown if an error is returned.
   370  func seekerSize(seeker io.ReadSeeker) (int64, error) {
   371  	current, err := seeker.Seek(0, os.SEEK_CUR)
   372  	if err != nil {
   373  		return 0, err
   374  	}
   375  
   376  	end, err := seeker.Seek(0, os.SEEK_END)
   377  	if err != nil {
   378  		return 0, err
   379  	}
   380  
   381  	resumed, err := seeker.Seek(current, os.SEEK_SET)
   382  	if err != nil {
   383  		return 0, err
   384  	}
   385  
   386  	if resumed != current {
   387  		return 0, fmt.Errorf("error returning seeker to original state, could not seek back to original location")
   388  	}
   389  
   390  	return end, nil
   391  }
   392  
   393  // addBlob simply consumes the reader and inserts into the blob service,
   394  // returning a descriptor on success.
   395  func addBlob(ctx context.Context, bs distribution.BlobIngester, desc distribution.Descriptor, rd io.Reader) (distribution.Descriptor, error) {
   396  	wr, err := bs.Create(ctx)
   397  	if err != nil {
   398  		return distribution.Descriptor{}, err
   399  	}
   400  	defer wr.Cancel(ctx)
   401  
   402  	if nn, err := io.Copy(wr, rd); err != nil {
   403  		return distribution.Descriptor{}, err
   404  	} else if nn != desc.Size {
   405  		return distribution.Descriptor{}, fmt.Errorf("incorrect number of bytes copied: %v != %v", nn, desc.Size)
   406  	}
   407  
   408  	return wr.Commit(ctx, desc)
   409  }