mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Do Docker edits so we can use the new distribution code
Signed-off-by: Doug Davis <dug@us.ibm.com>
This commit is contained in:
parent
8b69552b50
commit
b874ef8f43
12 changed files with 282 additions and 21 deletions
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/docker/docker/trust"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/docker/libtrust"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type v2Puller struct {
|
||||
|
@ -58,7 +59,13 @@ func (p *v2Puller) pullV2Repository(tag string) (err error) {
|
|||
taggedName = utils.ImageReference(p.repoInfo.LocalName, tag)
|
||||
} else {
|
||||
var err error
|
||||
tags, err = p.repo.Manifests().Tags()
|
||||
|
||||
manSvc, err := p.repo.Manifests(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tags, err = manSvc.Tags()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -140,7 +147,7 @@ func (p *v2Puller) download(di *downloadInfo) {
|
|||
di.err <- err
|
||||
return
|
||||
}
|
||||
di.size = desc.Length
|
||||
di.size = desc.Size
|
||||
|
||||
layerDownload, err := blobs.Open(nil, di.digest)
|
||||
if err != nil {
|
||||
|
@ -187,7 +194,12 @@ func (p *v2Puller) pullV2Tag(tag, taggedName string) (bool, error) {
|
|||
logrus.Debugf("Pulling tag from V2 registry: %q", tag)
|
||||
out := p.config.OutStream
|
||||
|
||||
manifest, err := p.repo.Manifests().GetByTag(tag)
|
||||
manSvc, err := p.repo.Manifests(context.Background())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
manifest, err := manSvc.GetByTag(tag)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/docker/docker/registry"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/utils"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type v2Pusher struct {
|
||||
|
@ -191,7 +192,11 @@ func (p *v2Pusher) pushV2Tag(tag string) error {
|
|||
out.Write(p.sf.FormatStatus("", "Digest: %s", manifestDigest))
|
||||
}
|
||||
|
||||
return p.repo.Manifests().Put(signed)
|
||||
manSvc, err := p.repo.Manifests(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return manSvc.Put(signed)
|
||||
}
|
||||
|
||||
func (p *v2Pusher) pushV2Image(bs distribution.BlobService, img *image.Image) (digest.Digest, error) {
|
||||
|
|
|
@ -35,7 +35,7 @@ clone git github.com/coreos/go-etcd v2.0.0
|
|||
clone git github.com/hashicorp/consul v0.5.2
|
||||
|
||||
# get graph and distribution packages
|
||||
clone git github.com/docker/distribution 856638e299eddf01964fa918ac1552d8aa2e22b3
|
||||
clone git github.com/docker/distribution cd8ff553b6b1911be23dfeabb73e33108bcbf147
|
||||
clone git github.com/vbatts/tar-split v0.9.4
|
||||
|
||||
clone git github.com/opencontainers/runc v0.0.2 # libcontainer
|
||||
|
|
|
@ -97,32 +97,43 @@ Unless explicitly stated, we follow all coding guidelines from the Go
|
|||
community. While some of these standards may seem arbitrary, they somehow seem
|
||||
to result in a solid, consistent codebase.
|
||||
|
||||
It is possible that the code base does not currently comply with these
|
||||
guidelines. We are not looking for a massive PR that fixes this, since that
|
||||
goes against the spirit of the guidelines. All new contributions should make a
|
||||
best effort to clean up and make the code base better than they left it.
|
||||
Obviously, apply your best judgement. Remember, the goal here is to make the
|
||||
code base easier for humans to navigate and understand. Always keep that in
|
||||
mind when nudging others to comply.
|
||||
|
||||
The rules:
|
||||
|
||||
1. All code should be formatted with `gofmt -s`.
|
||||
2. All code should pass the default levels of
|
||||
[`golint`](https://github.com/golang/lint).
|
||||
3. All code should follow the guidelines covered at
|
||||
https://github.com/golang/go/wiki/CodeReviewComments.
|
||||
3. All code should follow the guidelines covered in [Effective
|
||||
Go](http://golang.org/doc/effective_go.html) and [Go Code Review
|
||||
Comments](https://github.com/golang/go/wiki/CodeReviewComments).
|
||||
4. Comment the code. Tell us the why, the history and the context.
|
||||
5. Document _all_ declarations and methods, even private ones. Declare
|
||||
expectations, caveats and anything else that may be important. If a type
|
||||
gets exported, having the comments already there will ensure it's ready.
|
||||
6. Variable name length should be proportional to it's context and no longer.
|
||||
noALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo. In
|
||||
practice, short methods will have short variable names and globals will
|
||||
6. Variable name length should be proportional to its context and no longer.
|
||||
`noCommaALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo`.
|
||||
In practice, short methods will have short variable names and globals will
|
||||
have longer names.
|
||||
7. No underscores in package names. If you need a compound name, step back,
|
||||
and re-examine why you need a compound name. If you still think you need a
|
||||
compound name, lose the underscore.
|
||||
8. No utils or helpers packages. If a function is not general enough to
|
||||
warrant it's own package, it has not been written generally enough to be a
|
||||
warrant its own package, it has not been written generally enough to be a
|
||||
part of a util package. Just leave it unexported and well-documented.
|
||||
9. No, we don't need another unit testing framework.
|
||||
9. All tests should run with `go test` and outside tooling should not be
|
||||
required. No, we don't need another unit testing framework. Assertion
|
||||
packages are acceptable if they provide _real_ incremental value.
|
||||
10. Even though we call these "rules" above, they are actually just
|
||||
guidelines. Since you've read all the rules, you now know that.
|
||||
|
||||
If you are having trouble getting into the mood of idiomatic Go, we recommend
|
||||
reading through [`Effective Go`](http://golang.org/doc/effective_go.html). The
|
||||
reading through [Effective Go](http://golang.org/doc/effective_go.html). The
|
||||
[Go Blog](http://blog.golang.org/) is also a great resource. Drinking the
|
||||
kool-aid is a lot easier than going thirsty.
|
||||
|
|
|
@ -7,6 +7,8 @@ for storing and distributing Docker images. It supersedes the [docker/docker-
|
|||
registry](https://github.com/docker/docker-registry) project with a new API
|
||||
design, focused around security and performance.
|
||||
|
||||
<img src="https://www.docker.com/sites/default/files/oyster-registry-3.png" width=200px/>
|
||||
|
||||
This repository contains the following components:
|
||||
|
||||
|**Component** |Description |
|
||||
|
|
|
@ -35,6 +35,12 @@ type Namespace interface {
|
|||
// registry may or may not have the repository but should always return a
|
||||
// reference.
|
||||
Repository(ctx context.Context, name string) (Repository, error)
|
||||
|
||||
// Repositories fills 'repos' with a lexigraphically sorted catalog of repositories
|
||||
// up to the size of 'repos' and returns the value 'n' for the number of entries
|
||||
// which were filled. 'last' contains an offset in the catalog, and 'err' will be
|
||||
// set to io.EOF if there are no more entries to obtain.
|
||||
Repositories(ctx context.Context, repos []string, last string) (n int, err error)
|
||||
}
|
||||
|
||||
// ManifestServiceOption is a function argument for Manifest Service methods
|
||||
|
|
|
@ -106,7 +106,7 @@ func (e Error) ErrorCode() ErrorCode {
|
|||
func (e Error) Error() string {
|
||||
return fmt.Sprintf("%s: %s",
|
||||
strings.ToLower(strings.Replace(e.Code.String(), "_", " ", -1)),
|
||||
e.Code.Message())
|
||||
e.Message)
|
||||
}
|
||||
|
||||
// WithDetail will return a new Error, based on the current one, but with
|
||||
|
|
|
@ -87,6 +87,30 @@ var (
|
|||
Format: "<digest>",
|
||||
}
|
||||
|
||||
linkHeader = ParameterDescriptor{
|
||||
Name: "Link",
|
||||
Type: "link",
|
||||
Description: "RFC5988 compliant rel='next' with URL to next result set, if available",
|
||||
Format: `<<url>?n=<last n value>&last=<last entry from response>>; rel="next"`,
|
||||
}
|
||||
|
||||
paginationParameters = []ParameterDescriptor{
|
||||
{
|
||||
Name: "n",
|
||||
Type: "integer",
|
||||
Description: "Limit the number of entries in each response. It not present, all entries will be returned.",
|
||||
Format: "<integer>",
|
||||
Required: false,
|
||||
},
|
||||
{
|
||||
Name: "last",
|
||||
Type: "string",
|
||||
Description: "Result set will include values lexically after last.",
|
||||
Format: "<integer>",
|
||||
Required: false,
|
||||
},
|
||||
}
|
||||
|
||||
unauthorizedResponse = ResponseDescriptor{
|
||||
Description: "The client does not have access to the repository.",
|
||||
StatusCode: http.StatusUnauthorized,
|
||||
|
@ -269,6 +293,9 @@ type ResponseDescriptor struct {
|
|||
// Headers covers any headers that may be returned from the response.
|
||||
Headers []ParameterDescriptor
|
||||
|
||||
// Fields describes any fields that may be present in the response.
|
||||
Fields []ParameterDescriptor
|
||||
|
||||
// ErrorCodes enumerates the error codes that may be returned along with
|
||||
// the response.
|
||||
ErrorCodes []errcode.ErrorCode
|
||||
|
@ -427,6 +454,36 @@ var routeDescriptors = []RouteDescriptor{
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Return a portion of the tags for the specified repository.",
|
||||
PathParameters: []ParameterDescriptor{nameParameterDescriptor},
|
||||
QueryParameters: paginationParameters,
|
||||
Successes: []ResponseDescriptor{
|
||||
{
|
||||
StatusCode: http.StatusOK,
|
||||
Description: "A list of tags for the named repository.",
|
||||
Headers: []ParameterDescriptor{
|
||||
{
|
||||
Name: "Content-Length",
|
||||
Type: "integer",
|
||||
Description: "Length of the JSON response body.",
|
||||
Format: "<length>",
|
||||
},
|
||||
linkHeader,
|
||||
},
|
||||
Body: BodyDescriptor{
|
||||
ContentType: "application/json; charset=utf-8",
|
||||
Format: `{
|
||||
"name": <name>,
|
||||
"tags": [
|
||||
<tag>,
|
||||
...
|
||||
],
|
||||
}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1320,6 +1377,76 @@ var routeDescriptors = []RouteDescriptor{
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: RouteNameCatalog,
|
||||
Path: "/v2/_catalog",
|
||||
Entity: "Catalog",
|
||||
Description: "List a set of available repositories in the local registry cluster. Does not provide any indication of what may be available upstream. Applications can only determine if a repository is available but not if it is not available.",
|
||||
Methods: []MethodDescriptor{
|
||||
{
|
||||
Method: "GET",
|
||||
Description: "Retrieve a sorted, json list of repositories available in the registry.",
|
||||
Requests: []RequestDescriptor{
|
||||
{
|
||||
Name: "Catalog Fetch Complete",
|
||||
Description: "Request an unabridged list of repositories available.",
|
||||
Successes: []ResponseDescriptor{
|
||||
{
|
||||
Description: "Returns the unabridged list of repositories as a json response.",
|
||||
StatusCode: http.StatusOK,
|
||||
Headers: []ParameterDescriptor{
|
||||
{
|
||||
Name: "Content-Length",
|
||||
Type: "integer",
|
||||
Description: "Length of the JSON response body.",
|
||||
Format: "<length>",
|
||||
},
|
||||
},
|
||||
Body: BodyDescriptor{
|
||||
ContentType: "application/json; charset=utf-8",
|
||||
Format: `{
|
||||
"repositories": [
|
||||
<name>,
|
||||
...
|
||||
]
|
||||
}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Catalog Fetch Paginated",
|
||||
Description: "Return the specified portion of repositories.",
|
||||
QueryParameters: paginationParameters,
|
||||
Successes: []ResponseDescriptor{
|
||||
{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: BodyDescriptor{
|
||||
ContentType: "application/json; charset=utf-8",
|
||||
Format: `{
|
||||
"repositories": [
|
||||
<name>,
|
||||
...
|
||||
]
|
||||
"next": "<url>?last=<name>&n=<last value of n>"
|
||||
}`,
|
||||
},
|
||||
Headers: []ParameterDescriptor{
|
||||
{
|
||||
Name: "Content-Length",
|
||||
Type: "integer",
|
||||
Description: "Length of the JSON response body.",
|
||||
Format: "<length>",
|
||||
},
|
||||
linkHeader,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var routeDescriptorsMap map[string]RouteDescriptor
|
||||
|
|
|
@ -11,10 +11,12 @@ const (
|
|||
RouteNameBlob = "blob"
|
||||
RouteNameBlobUpload = "blob-upload"
|
||||
RouteNameBlobUploadChunk = "blob-upload-chunk"
|
||||
RouteNameCatalog = "catalog"
|
||||
)
|
||||
|
||||
var allEndpoints = []string{
|
||||
RouteNameManifest,
|
||||
RouteNameCatalog,
|
||||
RouteNameTags,
|
||||
RouteNameBlob,
|
||||
RouteNameBlobUpload,
|
||||
|
|
|
@ -100,6 +100,18 @@ func (ub *URLBuilder) BuildBaseURL() (string, error) {
|
|||
return baseURL.String(), nil
|
||||
}
|
||||
|
||||
// BuildCatalogURL constructs a url get a catalog of repositories
|
||||
func (ub *URLBuilder) BuildCatalogURL(values ...url.Values) (string, error) {
|
||||
route := ub.cloneRoute(RouteNameCatalog)
|
||||
|
||||
catalogURL, err := route.URL()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return appendValuesURL(catalogURL, values...).String(), nil
|
||||
}
|
||||
|
||||
// BuildTagsURL constructs a url to list the tags in the named repository.
|
||||
func (ub *URLBuilder) BuildTagsURL(name string) (string, error) {
|
||||
route := ub.cloneRoute(RouteNameTags)
|
||||
|
|
|
@ -53,13 +53,6 @@ func handleErrorResponse(resp *http.Response) error {
|
|||
err := parseHTTPErrorResponse(resp.Body)
|
||||
if uErr, ok := err.(*UnexpectedHTTPResponseError); ok {
|
||||
return v2.ErrorCodeUnauthorized.WithDetail(uErr.Response)
|
||||
/*
|
||||
return &errcode.Error{
|
||||
Code: v2.ErrorCodeUnauthorized,
|
||||
Message: v2.ErrorCodeUnauthorized.Message(),
|
||||
Detail: uErr.Response,
|
||||
}
|
||||
*/
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -21,6 +21,83 @@ import (
|
|||
"github.com/docker/distribution/registry/storage/cache/memory"
|
||||
)
|
||||
|
||||
// Registry provides an interface for calling Repositories, which returns a catalog of repositories.
|
||||
type Registry interface {
|
||||
Repositories(ctx context.Context, repos []string, last string) (n int, err error)
|
||||
}
|
||||
|
||||
// NewRegistry creates a registry namespace which can be used to get a listing of repositories
|
||||
func NewRegistry(ctx context.Context, baseURL string, transport http.RoundTripper) (Registry, error) {
|
||||
ub, err := v2.NewURLBuilderFromString(baseURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Transport: transport,
|
||||
Timeout: 1 * time.Minute,
|
||||
}
|
||||
|
||||
return ®istry{
|
||||
client: client,
|
||||
ub: ub,
|
||||
context: ctx,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type registry struct {
|
||||
client *http.Client
|
||||
ub *v2.URLBuilder
|
||||
context context.Context
|
||||
}
|
||||
|
||||
// Repositories returns a lexigraphically sorted catalog given a base URL. The 'entries' slice will be filled up to the size
|
||||
// of the slice, starting at the value provided in 'last'. The number of entries will be returned along with io.EOF if there
|
||||
// are no more entries
|
||||
func (r *registry) Repositories(ctx context.Context, entries []string, last string) (int, error) {
|
||||
var numFilled int
|
||||
var returnErr error
|
||||
|
||||
values := buildCatalogValues(len(entries), last)
|
||||
u, err := r.ub.BuildCatalogURL(values)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
resp, err := r.client.Get(u)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
var ctlg struct {
|
||||
Repositories []string `json:"repositories"`
|
||||
}
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
|
||||
if err := decoder.Decode(&ctlg); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
for cnt := range ctlg.Repositories {
|
||||
entries[cnt] = ctlg.Repositories[cnt]
|
||||
}
|
||||
numFilled = len(ctlg.Repositories)
|
||||
|
||||
link := resp.Header.Get("Link")
|
||||
if link == "" {
|
||||
returnErr = io.EOF
|
||||
}
|
||||
|
||||
default:
|
||||
return 0, handleErrorResponse(resp)
|
||||
}
|
||||
|
||||
return numFilled, returnErr
|
||||
}
|
||||
|
||||
// NewRepository creates a new Repository for the given repository name and base URL
|
||||
func NewRepository(ctx context.Context, name, baseURL string, transport http.RoundTripper) (distribution.Repository, error) {
|
||||
if err := v2.ValidateRepositoryName(name); err != nil {
|
||||
|
@ -444,3 +521,17 @@ func (bs *blobStatter) Stat(ctx context.Context, dgst digest.Digest) (distributi
|
|||
return distribution.Descriptor{}, handleErrorResponse(resp)
|
||||
}
|
||||
}
|
||||
|
||||
func buildCatalogValues(maxEntries int, last string) url.Values {
|
||||
values := url.Values{}
|
||||
|
||||
if maxEntries > 0 {
|
||||
values.Add("n", strconv.Itoa(maxEntries))
|
||||
}
|
||||
|
||||
if last != "" {
|
||||
values.Add("last", last)
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue