1 package proxy
2
3 import (
4 "context"
5 "io"
6 "sync"
7 "testing"
8
9 "github.com/distribution/reference"
10 "github.com/docker/distribution"
11 "github.com/docker/distribution/manifest"
12 "github.com/docker/distribution/manifest/schema1"
13 "github.com/docker/distribution/registry/client/auth"
14 "github.com/docker/distribution/registry/client/auth/challenge"
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 "github.com/opencontainers/go-digest"
22 )
23
24 type statsManifest struct {
25 manifests distribution.ManifestService
26 stats map[string]int
27 }
28
29 type manifestStoreTestEnv struct {
30 manifestDigest digest.Digest
31 manifests proxyManifestStore
32 }
33
34 func (te manifestStoreTestEnv) LocalStats() *map[string]int {
35 ls := te.manifests.localManifests.(statsManifest).stats
36 return &ls
37 }
38
39 func (te manifestStoreTestEnv) RemoteStats() *map[string]int {
40 rs := te.manifests.remoteManifests.(statsManifest).stats
41 return &rs
42 }
43
44 func (sm statsManifest) Delete(ctx context.Context, dgst digest.Digest) error {
45 sm.stats["delete"]++
46 return sm.manifests.Delete(ctx, dgst)
47 }
48
49 func (sm statsManifest) Exists(ctx context.Context, dgst digest.Digest) (bool, error) {
50 sm.stats["exists"]++
51 return sm.manifests.Exists(ctx, dgst)
52 }
53
54 func (sm statsManifest) Get(ctx context.Context, dgst digest.Digest, options ...distribution.ManifestServiceOption) (distribution.Manifest, error) {
55 sm.stats["get"]++
56 return sm.manifests.Get(ctx, dgst)
57 }
58
59 func (sm statsManifest) Put(ctx context.Context, manifest distribution.Manifest, options ...distribution.ManifestServiceOption) (digest.Digest, error) {
60 sm.stats["put"]++
61 return sm.manifests.Put(ctx, manifest)
62 }
63
64 type mockChallenger struct {
65 sync.Mutex
66 count int
67 }
68
69
70 func (m *mockChallenger) tryEstablishChallenges(context.Context) error {
71 m.Lock()
72 defer m.Unlock()
73 m.count++
74 return nil
75 }
76
77 func (m *mockChallenger) credentialStore() auth.CredentialStore {
78 return nil
79 }
80
81 func (m *mockChallenger) challengeManager() challenge.Manager {
82 return nil
83 }
84
85 func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestEnv {
86 nameRef, err := reference.WithName(name)
87 if err != nil {
88 t.Fatalf("unable to parse reference: %s", err)
89 }
90 k, err := libtrust.GenerateECP256PrivateKey()
91 if err != nil {
92 t.Fatal(err)
93 }
94
95 ctx := context.Background()
96 truthRegistry, err := storage.NewRegistry(ctx, inmemory.New(),
97 storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()),
98 storage.Schema1SigningKey(k),
99 storage.EnableSchema1)
100 if err != nil {
101 t.Fatalf("error creating registry: %v", err)
102 }
103 truthRepo, err := truthRegistry.Repository(ctx, nameRef)
104 if err != nil {
105 t.Fatalf("unexpected error getting repo: %v", err)
106 }
107 tr, err := truthRepo.Manifests(ctx)
108 if err != nil {
109 t.Fatal(err.Error())
110 }
111 truthManifests := statsManifest{
112 manifests: tr,
113 stats: make(map[string]int),
114 }
115
116 manifestDigest, err := populateRepo(ctx, t, truthRepo, name, tag)
117 if err != nil {
118 t.Fatalf(err.Error())
119 }
120
121 localRegistry, err := storage.NewRegistry(ctx, inmemory.New(), storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), storage.EnableRedirect, storage.DisableDigestResumption, storage.Schema1SigningKey(k), storage.EnableSchema1)
122 if err != nil {
123 t.Fatalf("error creating registry: %v", err)
124 }
125 localRepo, err := localRegistry.Repository(ctx, nameRef)
126 if err != nil {
127 t.Fatalf("unexpected error getting repo: %v", err)
128 }
129 lr, err := localRepo.Manifests(ctx)
130 if err != nil {
131 t.Fatal(err.Error())
132 }
133
134 localManifests := statsManifest{
135 manifests: lr,
136 stats: make(map[string]int),
137 }
138
139 s := scheduler.New(ctx, inmemory.New(), "/scheduler-state.json")
140 return &manifestStoreTestEnv{
141 manifestDigest: manifestDigest,
142 manifests: proxyManifestStore{
143 ctx: ctx,
144 localManifests: localManifests,
145 remoteManifests: truthManifests,
146 scheduler: s,
147 repositoryName: nameRef,
148 authChallenger: &mockChallenger{},
149 },
150 }
151 }
152
153 func populateRepo(ctx context.Context, t *testing.T, repository distribution.Repository, name, tag string) (digest.Digest, error) {
154 m := schema1.Manifest{
155 Versioned: manifest.Versioned{
156 SchemaVersion: 1,
157 },
158 Name: name,
159 Tag: tag,
160 }
161
162 for i := 0; i < 2; i++ {
163 wr, err := repository.Blobs(ctx).Create(ctx)
164 if err != nil {
165 t.Fatalf("unexpected error creating test upload: %v", err)
166 }
167
168 rs, dgst, err := testutil.CreateRandomTarFile()
169 if err != nil {
170 t.Fatalf("unexpected error generating test layer file")
171 }
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
204
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
214 exists, err := env.manifests.Exists(ctx, env.manifestDigest)
215 if err != nil {
216 t.Fatalf("Error checking existence")
217 }
218 if !exists {
219 t.Errorf("Unexpected non-existent 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
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
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-existent 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
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 }
276
View as plain text