github.com/npaton/distribution@v2.3.1-rc.0+incompatible/registry/proxy/proxymanifeststore_test.go (about) 1 package proxy 2 3 import ( 4 "io" 5 "sync" 6 "testing" 7 8 "github.com/docker/distribution" 9 "github.com/docker/distribution/context" 10 "github.com/docker/distribution/digest" 11 "github.com/docker/distribution/manifest" 12 "github.com/docker/distribution/manifest/schema1" 13 "github.com/docker/distribution/reference" 14 "github.com/docker/distribution/registry/client/auth" 15 "github.com/docker/distribution/registry/proxy/scheduler" 16 "github.com/docker/distribution/registry/storage" 17 "github.com/docker/distribution/registry/storage/cache/memory" 18 "github.com/docker/distribution/registry/storage/driver/inmemory" 19 "github.com/docker/distribution/testutil" 20 "github.com/docker/libtrust" 21 ) 22 23 type statsManifest struct { 24 manifests distribution.ManifestService 25 stats map[string]int 26 } 27 28 type manifestStoreTestEnv struct { 29 manifestDigest digest.Digest // digest of the signed manifest in the local storage 30 manifests proxyManifestStore 31 } 32 33 func (te manifestStoreTestEnv) LocalStats() *map[string]int { 34 ls := te.manifests.localManifests.(statsManifest).stats 35 return &ls 36 } 37 38 func (te manifestStoreTestEnv) RemoteStats() *map[string]int { 39 rs := te.manifests.remoteManifests.(statsManifest).stats 40 return &rs 41 } 42 43 func (sm statsManifest) Delete(ctx context.Context, dgst digest.Digest) error { 44 sm.stats["delete"]++ 45 return sm.manifests.Delete(ctx, dgst) 46 } 47 48 func (sm statsManifest) Exists(ctx context.Context, dgst digest.Digest) (bool, error) { 49 sm.stats["exists"]++ 50 return sm.manifests.Exists(ctx, dgst) 51 } 52 53 func (sm statsManifest) Get(ctx context.Context, dgst digest.Digest, options ...distribution.ManifestServiceOption) (distribution.Manifest, error) { 54 sm.stats["get"]++ 55 return sm.manifests.Get(ctx, dgst) 56 } 57 58 func (sm statsManifest) Put(ctx context.Context, manifest distribution.Manifest, options ...distribution.ManifestServiceOption) (digest.Digest, error) { 59 sm.stats["put"]++ 60 return sm.manifests.Put(ctx, manifest) 61 } 62 63 /*func (sm statsManifest) Enumerate(ctx context.Context, manifests []distribution.Manifest, last distribution.Manifest) (n int, err error) { 64 sm.stats["enumerate"]++ 65 return sm.manifests.Enumerate(ctx, manifests, last) 66 } 67 */ 68 69 type mockChallenger struct { 70 sync.Mutex 71 count int 72 } 73 74 // Called for remote operations only 75 func (m *mockChallenger) tryEstablishChallenges(context.Context) error { 76 m.Lock() 77 defer m.Unlock() 78 79 m.count++ 80 return nil 81 } 82 83 func (m *mockChallenger) credentialStore() auth.CredentialStore { 84 return nil 85 } 86 87 func (m *mockChallenger) challengeManager() auth.ChallengeManager { 88 return nil 89 } 90 91 func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestEnv { 92 nameRef, err := reference.ParseNamed(name) 93 if err != nil { 94 t.Fatalf("unable to parse reference: %s", err) 95 } 96 97 ctx := context.Background() 98 truthRegistry, err := storage.NewRegistry(ctx, inmemory.New(), storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider())) 99 if err != nil { 100 t.Fatalf("error creating registry: %v", err) 101 } 102 truthRepo, err := truthRegistry.Repository(ctx, nameRef) 103 if err != nil { 104 t.Fatalf("unexpected error getting repo: %v", err) 105 } 106 tr, err := truthRepo.Manifests(ctx) 107 if err != nil { 108 t.Fatal(err.Error()) 109 } 110 truthManifests := statsManifest{ 111 manifests: tr, 112 stats: make(map[string]int), 113 } 114 115 manifestDigest, err := populateRepo(t, ctx, truthRepo, name, tag) 116 if err != nil { 117 t.Fatalf(err.Error()) 118 } 119 120 localRegistry, err := storage.NewRegistry(ctx, inmemory.New(), storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), storage.EnableRedirect, storage.DisableDigestResumption) 121 if err != nil { 122 t.Fatalf("error creating registry: %v", err) 123 } 124 localRepo, err := localRegistry.Repository(ctx, nameRef) 125 if err != nil { 126 t.Fatalf("unexpected error getting repo: %v", err) 127 } 128 lr, err := localRepo.Manifests(ctx) 129 if err != nil { 130 t.Fatal(err.Error()) 131 } 132 133 localManifests := statsManifest{ 134 manifests: lr, 135 stats: make(map[string]int), 136 } 137 138 s := scheduler.New(ctx, inmemory.New(), "/scheduler-state.json") 139 return &manifestStoreTestEnv{ 140 manifestDigest: manifestDigest, 141 manifests: proxyManifestStore{ 142 ctx: ctx, 143 localManifests: localManifests, 144 remoteManifests: truthManifests, 145 scheduler: s, 146 repositoryName: nameRef, 147 authChallenger: &mockChallenger{}, 148 }, 149 } 150 } 151 152 func populateRepo(t *testing.T, ctx context.Context, repository distribution.Repository, name, tag string) (digest.Digest, error) { 153 m := schema1.Manifest{ 154 Versioned: manifest.Versioned{ 155 SchemaVersion: 1, 156 }, 157 Name: name, 158 Tag: tag, 159 } 160 161 for i := 0; i < 2; i++ { 162 wr, err := repository.Blobs(ctx).Create(ctx) 163 if err != nil { 164 t.Fatalf("unexpected error creating test upload: %v", err) 165 } 166 167 rs, ts, err := testutil.CreateRandomTarFile() 168 if err != nil { 169 t.Fatalf("unexpected error generating test layer file") 170 } 171 dgst := digest.Digest(ts) 172 if _, err := io.Copy(wr, rs); err != nil { 173 t.Fatalf("unexpected error copying to upload: %v", err) 174 } 175 176 if _, err := wr.Commit(ctx, distribution.Descriptor{Digest: dgst}); err != nil { 177 t.Fatalf("unexpected error finishing upload: %v", err) 178 } 179 } 180 181 pk, err := libtrust.GenerateECP256PrivateKey() 182 if err != nil { 183 t.Fatalf("unexpected error generating private key: %v", err) 184 } 185 186 sm, err := schema1.Sign(&m, pk) 187 if err != nil { 188 t.Fatalf("error signing manifest: %v", err) 189 } 190 191 ms, err := repository.Manifests(ctx) 192 if err != nil { 193 t.Fatalf(err.Error()) 194 } 195 dgst, err := ms.Put(ctx, sm) 196 if err != nil { 197 t.Fatalf("unexpected errors putting manifest: %v", err) 198 } 199 200 return dgst, nil 201 } 202 203 // TestProxyManifests contains basic acceptance tests 204 // for the pull-through behavior 205 func TestProxyManifests(t *testing.T) { 206 name := "foo/bar" 207 env := newManifestStoreTestEnv(t, name, "latest") 208 209 localStats := env.LocalStats() 210 remoteStats := env.RemoteStats() 211 212 ctx := context.Background() 213 // Stat - must check local and remote 214 exists, err := env.manifests.Exists(ctx, env.manifestDigest) 215 if err != nil { 216 t.Fatalf("Error checking existance") 217 } 218 if !exists { 219 t.Errorf("Unexpected non-existant manifest") 220 } 221 222 if (*localStats)["exists"] != 1 && (*remoteStats)["exists"] != 1 { 223 t.Errorf("Unexpected exists count : \n%v \n%v", localStats, remoteStats) 224 } 225 226 if env.manifests.authChallenger.(*mockChallenger).count != 1 { 227 t.Fatalf("Expected 1 auth challenge, got %#v", env.manifests.authChallenger) 228 } 229 230 // Get - should succeed and pull manifest into local 231 _, err = env.manifests.Get(ctx, env.manifestDigest) 232 if err != nil { 233 t.Fatal(err) 234 } 235 236 if (*localStats)["get"] != 1 && (*remoteStats)["get"] != 1 { 237 t.Errorf("Unexpected get count") 238 } 239 240 if (*localStats)["put"] != 1 { 241 t.Errorf("Expected local put") 242 } 243 244 if env.manifests.authChallenger.(*mockChallenger).count != 2 { 245 t.Fatalf("Expected 2 auth challenges, got %#v", env.manifests.authChallenger) 246 } 247 248 // Stat - should only go to local 249 exists, err = env.manifests.Exists(ctx, env.manifestDigest) 250 if err != nil { 251 t.Fatal(err) 252 } 253 if !exists { 254 t.Errorf("Unexpected non-existant manifest") 255 } 256 257 if (*localStats)["exists"] != 2 && (*remoteStats)["exists"] != 1 { 258 t.Errorf("Unexpected exists count") 259 } 260 261 if env.manifests.authChallenger.(*mockChallenger).count != 2 { 262 t.Fatalf("Expected 2 auth challenges, got %#v", env.manifests.authChallenger) 263 } 264 265 // Get proxied - won't require another authchallenge 266 _, err = env.manifests.Get(ctx, env.manifestDigest) 267 if err != nil { 268 t.Fatal(err) 269 } 270 271 if env.manifests.authChallenger.(*mockChallenger).count != 2 { 272 t.Fatalf("Expected 2 auth challenges, got %#v", env.manifests.authChallenger) 273 } 274 275 }