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 }