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