Skip to content

docker registry: add support for mounts#3053

Closed
simonferquel wants to merge 7 commits intocontainerd:masterfrom
simonferquel:push-can-mount
Closed

docker registry: add support for mounts#3053
simonferquel wants to merge 7 commits intocontainerd:masterfrom
simonferquel:push-can-mount

Conversation

@simonferquel
Copy link
Copy Markdown

@simonferquel simonferquel commented Feb 28, 2019

Based on #3052

Following @dmcgowan advice (#3045 (comment)), I wrote support for mounting blobs in a way that does not require a new interface, by just adding an OriginProvider field in the Resolver Options.

On the PRO side, it requires less changes to calling code to add mount capabilities.
On the CON side, Pusher.Push can now return a nil writer (in cases where the Push has been replaced by a mount) with no error. <-mitigated that by returning a ErrAlreadyExist error. Would love to have feedback on this.

I slightly prefer my previous design as the new one can provoke nil-dereferences panics in existing callers code.

@simonferquel simonferquel force-pushed the push-can-mount branch 3 times, most recently from 9e097b6 to 90dba5c Compare February 28, 2019 12:43
@codecov-io
Copy link
Copy Markdown

codecov-io commented Feb 28, 2019

Codecov Report

Merging #3053 into master will increase coverage by 0.07%.
The diff coverage is 56.89%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3053      +/-   ##
==========================================
+ Coverage   43.87%   43.94%   +0.07%     
==========================================
  Files         102      102              
  Lines       10903    10990      +87     
==========================================
+ Hits         4784     4830      +46     
- Misses       5384     5421      +37     
- Partials      735      739       +4
Flag Coverage Δ
#linux 47.53% <58.41%> (+0.04%) ⬆️
#windows 41.19% <56.89%> (+0.1%) ⬆️
Impacted Files Coverage Δ
remotes/docker/fetcher.go 54.34% <100%> (ø) ⬆️
remotes/docker/pusher.go 2.81% <17.5%> (+2.81%) ⬆️
remotes/docker/authorizer.go 67.5% <57.14%> (-0.71%) ⬇️
remotes/docker/resolver.go 60% <64.28%> (-0.16%) ⬇️
remotes/docker/scope.go 78.08% <83.33%> (+7.11%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c24a743...a892346. Read the comment docs.

@simonferquel
Copy link
Copy Markdown
Author

@dmcgowan @estesp PTAL (alongside #3052)
I think the design is really better (mount-optimization when calling Push now raise an ErrAlreadyExist error)

@ijc
Copy link
Copy Markdown
Contributor

ijc commented Mar 11, 2019

I slightly prefer my previous design as the new one can provoke nil-dereferences panics in existing callers code.

Is this still true? Where is the potential nil deref?

Copy link
Copy Markdown
Contributor

@ijc ijc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a few comments, but overall this is looking good to me (but I'm not a maintainer).

}
return context.WithValue(ctx, tokenScopesKey{}, []string{s}), nil
scopes := []string{s}
if combineWithExistingScopes {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would there be a downside to always doing this? It would mean simpler code here and in callers (no need for the new arg) and is what I think I would always expect of a function which takes a context and produces another one -- i.e. that they are cumulative.

ctx, err := contextWithRepositoryScope(ctx, mountCandidate, false, true)
if err != nil {
// try next candidate
continue
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would imply that findMatchingOrigins returned something malformatted, I think? It could be argued that this should be fatal and the error returned, but I think at least it should log an error.

}
i := strings.Index(mountCandidate.Locator, "/")
originRepoPath := mountCandidate.Locator[i+1:]
req, err = http.NewRequest(http.MethodPost, fmt.Sprintf("%s/?mount=%s&from=%s", p.url("blobs", "uploads"), desc.Digest, originRepoPath), nil)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should mount and from not be added with somethign like:

                q := req.URL.Query()
                q.Add("digest", desc.Digest.String())
                req.URL.RawQuery = q.Encode()

(which I stole from further down in this function). It's still more roundabout than I would like but is better than opencoding the query construction I think.

},
})
// mount succeeded, returning a nil writer, without error
return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "content %v mounted from %s", desc.Digest, mountCandidate)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know what you mean, but technically this is returning with an error. Maybe say "with canary error value"?

I also think this sort of subtlety is worthy of a godoc comment on the function itself.

// OriginProvider returns a slice of refspecs where the Pusher may find candidates for mounting
// instead of pushing from local store.
// When the Pusher succeeds by mounting, it returns an AlreadyExists error
OriginProvider func(ocispec.Descriptor) []reference.Spec
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an ErrAlreadyExists (Err prefix).

Like I commented above, I think this is worth repeating on the godoc for Push too.

@ijc
Copy link
Copy Markdown
Contributor

ijc commented Mar 11, 2019

Is this still true? Where is the potential nil deref?

I guess from the struck out stuff in the top comment that at some point Push returned nil, nil instead of nil, ErrAlreadyExists and that this change removed the danger? Old code calling this new function won't ever see an unexpected ErrAlreadyExists because they won't pass in an OriginProvider so there is no way a mount can happen. Right?

@dmcgowan dmcgowan added this to the 1.3 milestone Mar 13, 2019
Change the token scope merging to be grammar-aware.
This features:
 - smarter deduplication (repository:foo/bar:pull,push+repository:foo/bar:push,pull -> repository:foo/bar:pull,push)
 - sort-based normalization (actions on a resources are always sorted
 alphabethically, returned string slice is always sorted
 alphabethically)
 - merging of scopes concerning the same resource (repository:foo/bar:pull+repository:foo/bar:push => repository:foo/bar:pull,push)

Signed-off-by: Simon Ferquel <simon.ferquel@docker.com>
Signed-off-by: Simon Ferquel <simon.ferquel@docker.com>
Also, extracted token caching logic for better testability

Signed-off-by: Simon Ferquel <simon.ferquel@docker.com>
This add a notion of OriginResolver, to the docker resolver, so that we
can give the Pusher hints that it can actually mount a blob instead of
having to push it from a local resource.

If a Push operation succesfuly mount a blob, it returns a nil writer.

Signed-off-by: Simon Ferquel <simon.ferquel@docker.com>
This makes existing caller act as if the blob already existed, instead
of having to deal with a nil writer and no error

Signed-off-by: Simon Ferquel <simon.ferquel@docker.com>
Signed-off-by: Simon Ferquel <simon.ferquel@docker.com>
Signed-off-by: Simon Ferquel <simon.ferquel@docker.com>
@estesp
Copy link
Copy Markdown
Member

estesp commented Jun 13, 2019

Support for mount-on-push was merged in #3218. Thanks for your contribution, and feel free to let us know if any of the functionality and/or tests from your PR are still applicable.

@estesp estesp closed this Jun 13, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants