1 package storage
2
3 import (
4 "bytes"
5 "context"
6 "crypto/sha256"
7 "fmt"
8 "io"
9 "io/ioutil"
10 "path"
11 "reflect"
12 "testing"
13
14 "github.com/distribution/reference"
15 "github.com/docker/distribution"
16 "github.com/docker/distribution/registry/storage/cache/memory"
17 "github.com/docker/distribution/registry/storage/driver/testdriver"
18 "github.com/docker/distribution/testutil"
19 "github.com/opencontainers/go-digest"
20 )
21
22
23
24 func TestWriteSeek(t *testing.T) {
25 ctx := context.Background()
26 imageName, _ := reference.WithName("foo/bar")
27 driver := testdriver.New()
28 registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
29 if err != nil {
30 t.Fatalf("error creating registry: %v", err)
31 }
32 repository, err := registry.Repository(ctx, imageName)
33 if err != nil {
34 t.Fatalf("unexpected error getting repo: %v", err)
35 }
36 bs := repository.Blobs(ctx)
37
38 blobUpload, err := bs.Create(ctx)
39
40 if err != nil {
41 t.Fatalf("unexpected error starting layer upload: %s", err)
42 }
43 contents := []byte{1, 2, 3}
44 blobUpload.Write(contents)
45 blobUpload.Close()
46 offset := blobUpload.Size()
47 if offset != int64(len(contents)) {
48 t.Fatalf("unexpected value for blobUpload offset: %v != %v", offset, len(contents))
49 }
50
51 }
52
53
54
55 func TestSimpleBlobUpload(t *testing.T) {
56 randomDataReader, dgst, err := testutil.CreateRandomTarFile()
57 if err != nil {
58 t.Fatalf("error creating random reader: %v", err)
59 }
60
61 ctx := context.Background()
62 imageName, _ := reference.WithName("foo/bar")
63 driver := testdriver.New()
64 registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
65 if err != nil {
66 t.Fatalf("error creating registry: %v", err)
67 }
68 repository, err := registry.Repository(ctx, imageName)
69 if err != nil {
70 t.Fatalf("unexpected error getting repo: %v", err)
71 }
72 bs := repository.Blobs(ctx)
73
74 h := sha256.New()
75 rd := io.TeeReader(randomDataReader, h)
76
77 blobUpload, err := bs.Create(ctx)
78
79 if err != nil {
80 t.Fatalf("unexpected error starting layer upload: %s", err)
81 }
82
83
84 if err := blobUpload.Cancel(ctx); err != nil {
85 t.Fatalf("unexpected error during upload cancellation: %v", err)
86 }
87
88
89 uploadPath := path.Dir(blobUpload.(*blobWriter).path)
90
91
92 _, err = driver.List(ctx, uploadPath)
93 if err == nil {
94 t.Fatal("files in upload path after cleanup")
95 }
96
97
98 _, err = bs.Resume(ctx, blobUpload.ID())
99 if err != distribution.ErrBlobUploadUnknown {
100 t.Fatalf("unexpected error resuming upload, should be unknown: %v", err)
101 }
102
103
104 blobUpload, err = bs.Create(ctx)
105 if err != nil {
106 t.Fatalf("unexpected error starting layer upload: %s", err)
107 }
108
109
110 randomDataSize, err := seekerSize(randomDataReader)
111 if err != nil {
112 t.Fatalf("error getting seeker size of random data: %v", err)
113 }
114
115 nn, err := io.Copy(blobUpload, rd)
116 if err != nil {
117 t.Fatalf("unexpected error uploading layer data: %v", err)
118 }
119
120 if nn != randomDataSize {
121 t.Fatalf("layer data write incomplete")
122 }
123
124 blobUpload.Close()
125
126 offset := blobUpload.Size()
127 if offset != nn {
128 t.Fatalf("blobUpload not updated with correct offset: %v != %v", offset, nn)
129 }
130
131
132 blobUpload, err = bs.Resume(ctx, blobUpload.ID())
133 if err != nil {
134 t.Fatalf("unexpected error resuming upload: %v", err)
135 }
136
137 sha256Digest := digest.NewDigest("sha256", h)
138 desc, err := blobUpload.Commit(ctx, distribution.Descriptor{Digest: dgst})
139 if err != nil {
140 t.Fatalf("unexpected error finishing layer upload: %v", err)
141 }
142
143
144 uploadPath = path.Dir(blobUpload.(*blobWriter).path)
145 _, err = driver.List(ctx, uploadPath)
146 if err == nil {
147 t.Fatal("files in upload path after commit")
148 }
149
150
151 if _, err := bs.Resume(ctx, blobUpload.ID()); err != distribution.ErrBlobUploadUnknown {
152 t.Fatalf("expected layer upload to be unknown, got %v", err)
153 }
154
155
156 statDesc, err := bs.Stat(ctx, desc.Digest)
157 if err != nil {
158 t.Fatalf("unexpected error checking for existence: %v, %#v", err, bs)
159 }
160
161 if !reflect.DeepEqual(statDesc, desc) {
162 t.Fatalf("descriptors not equal: %v != %v", statDesc, desc)
163 }
164
165 rc, err := bs.Open(ctx, desc.Digest)
166 if err != nil {
167 t.Fatalf("unexpected error opening blob for read: %v", err)
168 }
169 defer rc.Close()
170
171 h.Reset()
172 nn, err = io.Copy(h, rc)
173 if err != nil {
174 t.Fatalf("error reading layer: %v", err)
175 }
176
177 if nn != randomDataSize {
178 t.Fatalf("incorrect read length")
179 }
180
181 if digest.NewDigest("sha256", h) != sha256Digest {
182 t.Fatalf("unexpected digest from uploaded layer: %q != %q", digest.NewDigest("sha256", h), sha256Digest)
183 }
184
185
186 err = bs.Delete(ctx, desc.Digest)
187 if err != nil {
188 t.Fatalf("Unexpected error deleting blob")
189 }
190
191 d, err := bs.Stat(ctx, desc.Digest)
192 if err == nil {
193 t.Fatalf("unexpected non-error stating deleted blob: %v", d)
194 }
195
196 switch err {
197 case distribution.ErrBlobUnknown:
198 break
199 default:
200 t.Errorf("Unexpected error type stat-ing deleted manifest: %#v", err)
201 }
202
203 _, err = bs.Open(ctx, desc.Digest)
204 if err == nil {
205 t.Fatalf("unexpected success opening deleted blob for read")
206 }
207
208 switch err {
209 case distribution.ErrBlobUnknown:
210 break
211 default:
212 t.Errorf("Unexpected error type getting deleted manifest: %#v", err)
213 }
214
215
216 randomBlob, err := ioutil.ReadAll(randomDataReader)
217 if err != nil {
218 t.Fatalf("Error reading all of blob %s", err.Error())
219 }
220 expectedDigest := digest.FromBytes(randomBlob)
221 simpleUpload(t, bs, randomBlob, expectedDigest)
222
223 d, err = bs.Stat(ctx, expectedDigest)
224 if err != nil {
225 t.Errorf("unexpected error stat-ing blob")
226 }
227 if d.Digest != expectedDigest {
228 t.Errorf("Mismatching digest with restored blob")
229 }
230
231 _, err = bs.Open(ctx, expectedDigest)
232 if err != nil {
233 t.Errorf("Unexpected error opening blob")
234 }
235
236
237 registry, err = NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableRedirect)
238 if err != nil {
239 t.Fatalf("error creating registry: %v", err)
240 }
241 repository, err = registry.Repository(ctx, imageName)
242 if err != nil {
243 t.Fatalf("unexpected error getting repo: %v", err)
244 }
245 bs = repository.Blobs(ctx)
246 err = bs.Delete(ctx, desc.Digest)
247 if err == nil {
248 t.Errorf("Unexpected success deleting while disabled")
249 }
250 }
251
252
253
254
255 func TestSimpleBlobRead(t *testing.T) {
256 ctx := context.Background()
257 imageName, _ := reference.WithName("foo/bar")
258 driver := testdriver.New()
259 registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
260 if err != nil {
261 t.Fatalf("error creating registry: %v", err)
262 }
263 repository, err := registry.Repository(ctx, imageName)
264 if err != nil {
265 t.Fatalf("unexpected error getting repo: %v", err)
266 }
267 bs := repository.Blobs(ctx)
268
269 randomLayerReader, dgst, err := testutil.CreateRandomTarFile()
270 if err != nil {
271 t.Fatalf("error creating random data: %v", err)
272 }
273
274
275 desc, err := bs.Stat(ctx, dgst)
276 if err != distribution.ErrBlobUnknown {
277 t.Fatalf("expected not found error when testing for existence: %v", err)
278 }
279
280 _, err = bs.Open(ctx, dgst)
281 if err != distribution.ErrBlobUnknown {
282 t.Fatalf("expected not found error when opening non-existent blob: %v", err)
283 }
284
285 randomLayerSize, err := seekerSize(randomLayerReader)
286 if err != nil {
287 t.Fatalf("error getting seeker size for random layer: %v", err)
288 }
289
290 descBefore := distribution.Descriptor{Digest: dgst, MediaType: "application/octet-stream", Size: randomLayerSize}
291 t.Logf("desc: %v", descBefore)
292
293 desc, err = addBlob(ctx, bs, descBefore, randomLayerReader)
294 if err != nil {
295 t.Fatalf("error adding blob to blobservice: %v", err)
296 }
297
298 if desc.Size != randomLayerSize {
299 t.Fatalf("committed blob has incorrect length: %v != %v", desc.Size, randomLayerSize)
300 }
301
302 rc, err := bs.Open(ctx, desc.Digest)
303 if err != nil {
304 t.Fatalf("error opening blob with %v: %v", dgst, err)
305 }
306 defer rc.Close()
307
308
309 h := sha256.New()
310 nn, err := io.Copy(h, rc)
311 if err != nil {
312 t.Fatalf("unexpected error copying to hash: %v", err)
313 }
314
315 if nn != randomLayerSize {
316 t.Fatalf("stored incorrect number of bytes in blob: %d != %d", nn, randomLayerSize)
317 }
318
319 sha256Digest := digest.NewDigest("sha256", h)
320 if sha256Digest != desc.Digest {
321 t.Fatalf("fetched digest does not match: %q != %q", sha256Digest, desc.Digest)
322 }
323
324
325 offset, err := rc.Seek(0, io.SeekStart)
326 if err != nil {
327 t.Fatalf("error seeking blob: %v", err)
328 }
329
330 if offset != 0 {
331 t.Fatalf("seek failed: expected 0 offset, got %d", offset)
332 }
333
334 p, err := ioutil.ReadAll(rc)
335 if err != nil {
336 t.Fatalf("error reading all of blob: %v", err)
337 }
338
339 if len(p) != int(randomLayerSize) {
340 t.Fatalf("blob data read has different length: %v != %v", len(p), randomLayerSize)
341 }
342
343
344 _, err = randomLayerReader.Seek(0, io.SeekStart)
345 if err != nil {
346 t.Fatalf("error resetting layer reader: %v", err)
347 }
348
349 randomLayerData, err := ioutil.ReadAll(randomLayerReader)
350 if err != nil {
351 t.Fatalf("random layer read failed: %v", err)
352 }
353
354 if !bytes.Equal(p, randomLayerData) {
355 t.Fatalf("layer data not equal")
356 }
357 }
358
359
360
361 func TestBlobMount(t *testing.T) {
362 randomDataReader, dgst, err := testutil.CreateRandomTarFile()
363 if err != nil {
364 t.Fatalf("error creating random reader: %v", err)
365 }
366
367 ctx := context.Background()
368 imageName, _ := reference.WithName("foo/bar")
369 sourceImageName, _ := reference.WithName("foo/source")
370 driver := testdriver.New()
371 registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
372 if err != nil {
373 t.Fatalf("error creating registry: %v", err)
374 }
375
376 repository, err := registry.Repository(ctx, imageName)
377 if err != nil {
378 t.Fatalf("unexpected error getting repo: %v", err)
379 }
380 sourceRepository, err := registry.Repository(ctx, sourceImageName)
381 if err != nil {
382 t.Fatalf("unexpected error getting repo: %v", err)
383 }
384
385 sbs := sourceRepository.Blobs(ctx)
386
387 blobUpload, err := sbs.Create(ctx)
388
389 if err != nil {
390 t.Fatalf("unexpected error starting layer upload: %s", err)
391 }
392
393
394 randomDataSize, err := seekerSize(randomDataReader)
395 if err != nil {
396 t.Fatalf("error getting seeker size of random data: %v", err)
397 }
398
399 _, err = io.Copy(blobUpload, randomDataReader)
400 if err != nil {
401 t.Fatalf("unexpected error uploading layer data: %v", err)
402 }
403
404 desc, err := blobUpload.Commit(ctx, distribution.Descriptor{Digest: dgst})
405 if err != nil {
406 t.Fatalf("unexpected error finishing layer upload: %v", err)
407 }
408
409
410 statDesc, err := sbs.Stat(ctx, desc.Digest)
411 if err != nil {
412 t.Fatalf("unexpected error checking for existence: %v, %#v", err, sbs)
413 }
414
415 if !reflect.DeepEqual(statDesc, desc) {
416 t.Fatalf("descriptors not equal: %v != %v", statDesc, desc)
417 }
418
419 bs := repository.Blobs(ctx)
420
421 _, err = bs.Stat(ctx, desc.Digest)
422 if err == nil {
423 t.Fatalf("unexpected non-error stating unmounted blob: %v", desc)
424 }
425
426 canonicalRef, err := reference.WithDigest(sourceRepository.Named(), desc.Digest)
427 if err != nil {
428 t.Fatal(err)
429 }
430
431 bw, err := bs.Create(ctx, WithMountFrom(canonicalRef))
432 if bw != nil {
433 t.Fatal("unexpected blobwriter returned from Create call, should mount instead")
434 }
435
436 ebm, ok := err.(distribution.ErrBlobMounted)
437 if !ok {
438 t.Fatalf("unexpected error mounting layer: %v", err)
439 }
440
441 if !reflect.DeepEqual(ebm.Descriptor, desc) {
442 t.Fatalf("descriptors not equal: %v != %v", ebm.Descriptor, desc)
443 }
444
445
446 statDesc, err = bs.Stat(ctx, desc.Digest)
447 if err != nil {
448 t.Fatalf("unexpected error checking for existence: %v, %#v", err, bs)
449 }
450
451 if !reflect.DeepEqual(statDesc, desc) {
452 t.Fatalf("descriptors not equal: %v != %v", statDesc, desc)
453 }
454
455 rc, err := bs.Open(ctx, desc.Digest)
456 if err != nil {
457 t.Fatalf("unexpected error opening blob for read: %v", err)
458 }
459 defer rc.Close()
460
461 h := sha256.New()
462 nn, err := io.Copy(h, rc)
463 if err != nil {
464 t.Fatalf("error reading layer: %v", err)
465 }
466
467 if nn != randomDataSize {
468 t.Fatalf("incorrect read length")
469 }
470
471 if digest.NewDigest("sha256", h) != dgst {
472 t.Fatalf("unexpected digest from uploaded layer: %q != %q", digest.NewDigest("sha256", h), dgst)
473 }
474
475
476 err = sbs.Delete(ctx, desc.Digest)
477 if err != nil {
478 t.Fatalf("Unexpected error deleting blob")
479 }
480
481 _, err = bs.Stat(ctx, desc.Digest)
482 if err != nil {
483 t.Fatalf("unexpected error stating blob deleted from source repository: %v", err)
484 }
485
486 d, err := sbs.Stat(ctx, desc.Digest)
487 if err == nil {
488 t.Fatalf("unexpected non-error stating deleted blob: %v", d)
489 }
490
491 switch err {
492 case distribution.ErrBlobUnknown:
493 break
494 default:
495 t.Errorf("Unexpected error type stat-ing deleted manifest: %#v", err)
496 }
497
498
499 err = bs.Delete(ctx, desc.Digest)
500 if err != nil {
501 t.Fatalf("Unexpected error deleting blob")
502 }
503
504 d, err = bs.Stat(ctx, desc.Digest)
505 if err == nil {
506 t.Fatalf("unexpected non-error stating deleted blob: %v", d)
507 }
508
509 switch err {
510 case distribution.ErrBlobUnknown:
511 break
512 default:
513 t.Errorf("Unexpected error type stat-ing deleted manifest: %#v", err)
514 }
515 }
516
517
518 func TestLayerUploadZeroLength(t *testing.T) {
519 ctx := context.Background()
520 imageName, _ := reference.WithName("foo/bar")
521 driver := testdriver.New()
522 registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
523 if err != nil {
524 t.Fatalf("error creating registry: %v", err)
525 }
526 repository, err := registry.Repository(ctx, imageName)
527 if err != nil {
528 t.Fatalf("unexpected error getting repo: %v", err)
529 }
530 bs := repository.Blobs(ctx)
531
532 simpleUpload(t, bs, []byte{}, digestSha256Empty)
533 }
534
535 func simpleUpload(t *testing.T, bs distribution.BlobIngester, blob []byte, expectedDigest digest.Digest) {
536 ctx := context.Background()
537 wr, err := bs.Create(ctx)
538 if err != nil {
539 t.Fatalf("unexpected error starting upload: %v", err)
540 }
541
542 nn, err := io.Copy(wr, bytes.NewReader(blob))
543 if err != nil {
544 t.Fatalf("error copying into blob writer: %v", err)
545 }
546
547 if nn != 0 {
548 t.Fatalf("unexpected number of bytes copied: %v > 0", nn)
549 }
550
551 dgst, err := digest.FromReader(bytes.NewReader(blob))
552 if err != nil {
553 t.Fatalf("error getting digest: %v", err)
554 }
555
556 if dgst != expectedDigest {
557
558 t.Fatalf("digest not as expected: %v != %v", dgst, expectedDigest)
559 }
560
561 desc, err := wr.Commit(ctx, distribution.Descriptor{Digest: dgst})
562 if err != nil {
563 t.Fatalf("unexpected error committing write: %v", err)
564 }
565
566 if desc.Digest != dgst {
567 t.Fatalf("unexpected digest: %v != %v", desc.Digest, dgst)
568 }
569 }
570
571
572
573
574 func seekerSize(seeker io.ReadSeeker) (int64, error) {
575 current, err := seeker.Seek(0, io.SeekCurrent)
576 if err != nil {
577 return 0, err
578 }
579
580 end, err := seeker.Seek(0, io.SeekEnd)
581 if err != nil {
582 return 0, err
583 }
584
585 resumed, err := seeker.Seek(current, io.SeekStart)
586 if err != nil {
587 return 0, err
588 }
589
590 if resumed != current {
591 return 0, fmt.Errorf("error returning seeker to original state, could not seek back to original location")
592 }
593
594 return end, nil
595 }
596
597
598
599 func addBlob(ctx context.Context, bs distribution.BlobIngester, desc distribution.Descriptor, rd io.Reader) (distribution.Descriptor, error) {
600 wr, err := bs.Create(ctx)
601 if err != nil {
602 return distribution.Descriptor{}, err
603 }
604 defer wr.Cancel(ctx)
605
606 if nn, err := io.Copy(wr, rd); err != nil {
607 return distribution.Descriptor{}, err
608 } else if nn != desc.Size {
609 return distribution.Descriptor{}, fmt.Errorf("incorrect number of bytes copied: %v != %v", nn, desc.Size)
610 }
611
612 return wr.Commit(ctx, desc)
613 }
614
View as plain text