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 }