Skip to content

Commit 44c7fb9

Browse files
author
Richard Scothern
committed
Merge pull request distribution#864 from stevvooe/use-correct-manifest-link
registry/storage: use correct manifest link
2 parents fee9a9a + 06a098c commit 44c7fb9

File tree

5 files changed

+126
-42
lines changed

5 files changed

+126
-42
lines changed

registry/storage/linkedblobstore.go

Lines changed: 70 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import (
1111
"github.com/docker/distribution/uuid"
1212
)
1313

14+
// linkPathFunc describes a function that can resolve a link based on the
15+
// repository name and digest.
16+
type linkPathFunc func(pm *pathMapper, name string, dgst digest.Digest) (string, error)
17+
1418
// linkedBlobStore provides a full BlobService that namespaces the blobs to a
1519
// given repository. Effectively, it manages the links in a given repository
1620
// that grant access to the global blob store.
@@ -23,11 +27,13 @@ type linkedBlobStore struct {
2327
deleteEnabled bool
2428
resumableDigestEnabled bool
2529

26-
// linkPath allows one to control the repository blob link set to which
27-
// the blob store dispatches. This is required because manifest and layer
28-
// blobs have not yet been fully merged. At some point, this functionality
29-
// should be removed an the blob links folder should be merged.
30-
linkPath func(pm *pathMapper, name string, dgst digest.Digest) (string, error)
30+
// linkPathFns specifies one or more path functions allowing one to
31+
// control the repository blob link set to which the blob store
32+
// dispatches. This is required because manifest and layer blobs have not
33+
// yet been fully merged. At some point, this functionality should be
34+
// removed an the blob links folder should be merged. The first entry is
35+
// treated as the "canonical" link location and will be used for writes.
36+
linkPathFns []linkPathFunc
3137
}
3238

3339
var _ distribution.BlobStore = &linkedBlobStore{}
@@ -213,13 +219,16 @@ func (lbs *linkedBlobStore) linkBlob(ctx context.Context, canonical distribution
213219
// Don't make duplicate links.
214220
seenDigests := make(map[digest.Digest]struct{}, len(dgsts))
215221

222+
// only use the first link
223+
linkPathFn := lbs.linkPathFns[0]
224+
216225
for _, dgst := range dgsts {
217226
if _, seen := seenDigests[dgst]; seen {
218227
continue
219228
}
220229
seenDigests[dgst] = struct{}{}
221230

222-
blobLinkPath, err := lbs.linkPath(lbs.pm, lbs.repository.Name(), dgst)
231+
blobLinkPath, err := linkPathFn(lbs.pm, lbs.repository.Name(), dgst)
223232
if err != nil {
224233
return err
225234
}
@@ -236,33 +245,43 @@ type linkedBlobStatter struct {
236245
*blobStore
237246
repository distribution.Repository
238247

239-
// linkPath allows one to control the repository blob link set to which
240-
// the blob store dispatches. This is required because manifest and layer
241-
// blobs have not yet been fully merged. At some point, this functionality
242-
// should be removed an the blob links folder should be merged.
243-
linkPath func(pm *pathMapper, name string, dgst digest.Digest) (string, error)
248+
// linkPathFns specifies one or more path functions allowing one to
249+
// control the repository blob link set to which the blob store
250+
// dispatches. This is required because manifest and layer blobs have not
251+
// yet been fully merged. At some point, this functionality should be
252+
// removed an the blob links folder should be merged. The first entry is
253+
// treated as the "canonical" link location and will be used for writes.
254+
linkPathFns []linkPathFunc
244255
}
245256

246257
var _ distribution.BlobDescriptorService = &linkedBlobStatter{}
247258

248259
func (lbs *linkedBlobStatter) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
249-
blobLinkPath, err := lbs.linkPath(lbs.pm, lbs.repository.Name(), dgst)
250-
if err != nil {
251-
return distribution.Descriptor{}, err
252-
}
260+
var (
261+
resolveErr error
262+
target digest.Digest
263+
)
264+
265+
// try the many link path functions until we get success or an error that
266+
// is not PathNotFoundError.
267+
for _, linkPathFn := range lbs.linkPathFns {
268+
var err error
269+
target, err = lbs.resolveWithLinkFunc(ctx, dgst, linkPathFn)
270+
271+
if err == nil {
272+
break // success!
273+
}
253274

254-
target, err := lbs.blobStore.readlink(ctx, blobLinkPath)
255-
if err != nil {
256275
switch err := err.(type) {
257276
case driver.PathNotFoundError:
258-
return distribution.Descriptor{}, distribution.ErrBlobUnknown
277+
resolveErr = distribution.ErrBlobUnknown // move to the next linkPathFn, saving the error
259278
default:
260279
return distribution.Descriptor{}, err
261280
}
281+
}
262282

263-
// TODO(stevvooe): For backwards compatibility with data in "_layers", we
264-
// need to hit layerLinkPath, as well. Or, somehow migrate to the new path
265-
// layout.
283+
if resolveErr != nil {
284+
return distribution.Descriptor{}, resolveErr
266285
}
267286

268287
if target != dgst {
@@ -276,13 +295,38 @@ func (lbs *linkedBlobStatter) Stat(ctx context.Context, dgst digest.Digest) (dis
276295
return lbs.blobStore.statter.Stat(ctx, target)
277296
}
278297

279-
func (lbs *linkedBlobStatter) Clear(ctx context.Context, dgst digest.Digest) error {
280-
blobLinkPath, err := lbs.linkPath(lbs.pm, lbs.repository.Name(), dgst)
298+
func (lbs *linkedBlobStatter) Clear(ctx context.Context, dgst digest.Digest) (err error) {
299+
// clear any possible existence of a link described in linkPathFns
300+
for _, linkPathFn := range lbs.linkPathFns {
301+
blobLinkPath, err := linkPathFn(lbs.pm, lbs.repository.Name(), dgst)
302+
if err != nil {
303+
return err
304+
}
305+
306+
err = lbs.blobStore.driver.Delete(ctx, blobLinkPath)
307+
if err != nil {
308+
switch err := err.(type) {
309+
case driver.PathNotFoundError:
310+
continue // just ignore this error and continue
311+
default:
312+
return err
313+
}
314+
}
315+
}
316+
317+
return nil
318+
}
319+
320+
// resolveTargetWithFunc allows us to read a link to a resource with different
321+
// linkPathFuncs to let us try a few different paths before returning not
322+
// found.
323+
func (lbs *linkedBlobStatter) resolveWithLinkFunc(ctx context.Context, dgst digest.Digest, linkPathFn linkPathFunc) (digest.Digest, error) {
324+
blobLinkPath, err := linkPathFn(lbs.pm, lbs.repository.Name(), dgst)
281325
if err != nil {
282-
return err
326+
return "", err
283327
}
284328

285-
return lbs.blobStore.driver.Delete(ctx, blobLinkPath)
329+
return lbs.blobStore.readlink(ctx, blobLinkPath)
286330
}
287331

288332
func (lbs *linkedBlobStatter) SetDescriptor(ctx context.Context, dgst digest.Digest, desc distribution.Descriptor) error {
@@ -297,5 +341,5 @@ func blobLinkPath(pm *pathMapper, name string, dgst digest.Digest) (string, erro
297341

298342
// manifestRevisionLinkPath provides the path to the manifest revision link.
299343
func manifestRevisionLinkPath(pm *pathMapper, name string, dgst digest.Digest) (string, error) {
300-
return pm.path(layerLinkPathSpec{name: name, digest: dgst})
344+
return pm.path(manifestRevisionLinkPathSpec{name: name, revision: dgst})
301345
}

registry/storage/manifeststore_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,3 +362,37 @@ func TestManifestStorage(t *testing.T) {
362362
t.Errorf("Unexpected success deleting while disabled")
363363
}
364364
}
365+
366+
// TestLinkPathFuncs ensures that the link path functions behavior are locked
367+
// down and implemented as expected.
368+
func TestLinkPathFuncs(t *testing.T) {
369+
for _, testcase := range []struct {
370+
repo string
371+
digest digest.Digest
372+
linkPathFn linkPathFunc
373+
expected string
374+
}{
375+
{
376+
repo: "foo/bar",
377+
digest: "sha256:deadbeaf",
378+
linkPathFn: blobLinkPath,
379+
expected: "/docker/registry/v2/repositories/foo/bar/_layers/sha256/deadbeaf/link",
380+
},
381+
{
382+
repo: "foo/bar",
383+
digest: "sha256:deadbeaf",
384+
linkPathFn: manifestRevisionLinkPath,
385+
expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/deadbeaf/link",
386+
},
387+
} {
388+
p, err := testcase.linkPathFn(defaultPathMapper, testcase.repo, testcase.digest)
389+
if err != nil {
390+
t.Fatalf("unexpected error calling linkPathFn(pm, %q, %q): %v", testcase.repo, testcase.digest, err)
391+
}
392+
393+
if p != testcase.expected {
394+
t.Fatalf("incorrect path returned: %q != %q", p, testcase.expected)
395+
}
396+
}
397+
398+
}

registry/storage/registry.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,13 @@ func (repo *repository) Name() string {
108108
// may be context sensitive in the future. The instance should be used similar
109109
// to a request local.
110110
func (repo *repository) Manifests(ctx context.Context, options ...distribution.ManifestServiceOption) (distribution.ManifestService, error) {
111+
manifestLinkPathFns := []linkPathFunc{
112+
// NOTE(stevvooe): Need to search through multiple locations since
113+
// 2.1.0 unintentionally linked into _layers.
114+
manifestRevisionLinkPath,
115+
blobLinkPath,
116+
}
117+
111118
ms := &manifestStore{
112119
ctx: ctx,
113120
repository: repo,
@@ -120,14 +127,14 @@ func (repo *repository) Manifests(ctx context.Context, options ...distribution.M
120127
repository: repo,
121128
deleteEnabled: repo.registry.deleteEnabled,
122129
blobAccessController: &linkedBlobStatter{
123-
blobStore: repo.blobStore,
124-
repository: repo,
125-
linkPath: manifestRevisionLinkPath,
130+
blobStore: repo.blobStore,
131+
repository: repo,
132+
linkPathFns: manifestLinkPathFns,
126133
},
127134

128135
// TODO(stevvooe): linkPath limits this blob store to only
129136
// manifests. This instance cannot be used for blob checks.
130-
linkPath: manifestRevisionLinkPath,
137+
linkPathFns: manifestLinkPathFns,
131138
},
132139
},
133140
tagStore: &tagStore{
@@ -153,9 +160,9 @@ func (repo *repository) Manifests(ctx context.Context, options ...distribution.M
153160
// to a request local.
154161
func (repo *repository) Blobs(ctx context.Context) distribution.BlobStore {
155162
var statter distribution.BlobDescriptorService = &linkedBlobStatter{
156-
blobStore: repo.blobStore,
157-
repository: repo,
158-
linkPath: blobLinkPath,
163+
blobStore: repo.blobStore,
164+
repository: repo,
165+
linkPathFns: []linkPathFunc{blobLinkPath},
159166
}
160167

161168
if repo.descriptorCache != nil {
@@ -171,7 +178,7 @@ func (repo *repository) Blobs(ctx context.Context) distribution.BlobStore {
171178

172179
// TODO(stevvooe): linkPath limits this blob store to only layers.
173180
// This instance cannot be used for manifest checks.
174-
linkPath: blobLinkPath,
181+
linkPathFns: []linkPathFunc{blobLinkPath},
175182
deleteEnabled: repo.registry.deleteEnabled,
176183
}
177184
}

registry/storage/signaturestore.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,10 @@ func (s *signatureStore) linkedBlobStore(ctx context.Context, revision digest.Di
132132
repository: s.repository,
133133
blobStore: s.blobStore,
134134
blobAccessController: &linkedBlobStatter{
135-
blobStore: s.blobStore,
136-
repository: s.repository,
137-
linkPath: linkpath,
135+
blobStore: s.blobStore,
136+
repository: s.repository,
137+
linkPathFns: []linkPathFunc{linkpath},
138138
},
139-
linkPath: linkpath,
139+
linkPathFns: []linkPathFunc{linkpath},
140140
}
141141
}

registry/storage/tagstore.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ func (ts *tagStore) delete(tag string) error {
122122
return ts.blobStore.driver.Delete(ts.ctx, tagPath)
123123
}
124124

125-
// namedBlobStore returns the namedBlobStore for the named tag, allowing one
125+
// linkedBlobStore returns the linkedBlobStore for the named tag, allowing one
126126
// to index manifest blobs by tag name. While the tag store doesn't map
127127
// precisely to the linked blob store, using this ensures the links are
128128
// managed via the same code path.
@@ -131,13 +131,12 @@ func (ts *tagStore) linkedBlobStore(ctx context.Context, tag string) *linkedBlob
131131
blobStore: ts.blobStore,
132132
repository: ts.repository,
133133
ctx: ctx,
134-
linkPath: func(pm *pathMapper, name string, dgst digest.Digest) (string, error) {
134+
linkPathFns: []linkPathFunc{func(pm *pathMapper, name string, dgst digest.Digest) (string, error) {
135135
return pm.path(manifestTagIndexEntryLinkPathSpec{
136136
name: name,
137137
tag: tag,
138138
revision: dgst,
139139
})
140-
},
140+
}},
141141
}
142-
143142
}

0 commit comments

Comments
 (0)