mirror of https://github.com/docker/docs.git
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
|
@ -18,6 +18,7 @@ import (
|
||||||
"github.com/docker/docker/trust"
|
"github.com/docker/docker/trust"
|
||||||
"github.com/docker/docker/utils"
|
"github.com/docker/docker/utils"
|
||||||
"github.com/docker/libtrust"
|
"github.com/docker/libtrust"
|
||||||
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
type v2Puller struct {
|
type v2Puller struct {
|
||||||
|
@ -58,7 +59,13 @@ func (p *v2Puller) pullV2Repository(tag string) (err error) {
|
||||||
taggedName = utils.ImageReference(p.repoInfo.LocalName, tag)
|
taggedName = utils.ImageReference(p.repoInfo.LocalName, tag)
|
||||||
} else {
|
} else {
|
||||||
var err error
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -140,7 +147,7 @@ func (p *v2Puller) download(di *downloadInfo) {
|
||||||
di.err <- err
|
di.err <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
di.size = desc.Length
|
di.size = desc.Size
|
||||||
|
|
||||||
layerDownload, err := blobs.Open(nil, di.digest)
|
layerDownload, err := blobs.Open(nil, di.digest)
|
||||||
if err != nil {
|
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)
|
logrus.Debugf("Pulling tag from V2 registry: %q", tag)
|
||||||
out := p.config.OutStream
|
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 {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/docker/docker/registry"
|
"github.com/docker/docker/registry"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
"github.com/docker/docker/utils"
|
"github.com/docker/docker/utils"
|
||||||
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
type v2Pusher struct {
|
type v2Pusher struct {
|
||||||
|
@ -191,7 +192,11 @@ func (p *v2Pusher) pushV2Tag(tag string) error {
|
||||||
out.Write(p.sf.FormatStatus("", "Digest: %s", manifestDigest))
|
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) {
|
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
|
clone git github.com/hashicorp/consul v0.5.2
|
||||||
|
|
||||||
# get graph and distribution packages
|
# 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/vbatts/tar-split v0.9.4
|
||||||
|
|
||||||
clone git github.com/opencontainers/runc v0.0.2 # libcontainer
|
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
|
community. While some of these standards may seem arbitrary, they somehow seem
|
||||||
to result in a solid, consistent codebase.
|
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:
|
The rules:
|
||||||
|
|
||||||
1. All code should be formatted with `gofmt -s`.
|
1. All code should be formatted with `gofmt -s`.
|
||||||
2. All code should pass the default levels of
|
2. All code should pass the default levels of
|
||||||
[`golint`](https://github.com/golang/lint).
|
[`golint`](https://github.com/golang/lint).
|
||||||
3. All code should follow the guidelines covered at
|
3. All code should follow the guidelines covered in [Effective
|
||||||
https://github.com/golang/go/wiki/CodeReviewComments.
|
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.
|
4. Comment the code. Tell us the why, the history and the context.
|
||||||
5. Document _all_ declarations and methods, even private ones. Declare
|
5. Document _all_ declarations and methods, even private ones. Declare
|
||||||
expectations, caveats and anything else that may be important. If a type
|
expectations, caveats and anything else that may be important. If a type
|
||||||
gets exported, having the comments already there will ensure it's ready.
|
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.
|
6. Variable name length should be proportional to its context and no longer.
|
||||||
noALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo. In
|
`noCommaALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo`.
|
||||||
practice, short methods will have short variable names and globals will
|
In practice, short methods will have short variable names and globals will
|
||||||
have longer names.
|
have longer names.
|
||||||
7. No underscores in package names. If you need a compound name, step back,
|
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
|
and re-examine why you need a compound name. If you still think you need a
|
||||||
compound name, lose the underscore.
|
compound name, lose the underscore.
|
||||||
8. No utils or helpers packages. If a function is not general enough to
|
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.
|
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
|
10. Even though we call these "rules" above, they are actually just
|
||||||
guidelines. Since you've read all the rules, you now know that.
|
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
|
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
|
[Go Blog](http://blog.golang.org/) is also a great resource. Drinking the
|
||||||
kool-aid is a lot easier than going thirsty.
|
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
|
registry](https://github.com/docker/docker-registry) project with a new API
|
||||||
design, focused around security and performance.
|
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:
|
This repository contains the following components:
|
||||||
|
|
||||||
|**Component** |Description |
|
|**Component** |Description |
|
||||||
|
|
|
@ -35,6 +35,12 @@ type Namespace interface {
|
||||||
// registry may or may not have the repository but should always return a
|
// registry may or may not have the repository but should always return a
|
||||||
// reference.
|
// reference.
|
||||||
Repository(ctx context.Context, name string) (Repository, error)
|
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
|
// ManifestServiceOption is a function argument for Manifest Service methods
|
||||||
|
|
|
@ -106,7 +106,7 @@ func (e Error) ErrorCode() ErrorCode {
|
||||||
func (e Error) Error() string {
|
func (e Error) Error() string {
|
||||||
return fmt.Sprintf("%s: %s",
|
return fmt.Sprintf("%s: %s",
|
||||||
strings.ToLower(strings.Replace(e.Code.String(), "_", " ", -1)),
|
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
|
// WithDetail will return a new Error, based on the current one, but with
|
||||||
|
|
|
@ -87,6 +87,30 @@ var (
|
||||||
Format: "<digest>",
|
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{
|
unauthorizedResponse = ResponseDescriptor{
|
||||||
Description: "The client does not have access to the repository.",
|
Description: "The client does not have access to the repository.",
|
||||||
StatusCode: http.StatusUnauthorized,
|
StatusCode: http.StatusUnauthorized,
|
||||||
|
@ -269,6 +293,9 @@ type ResponseDescriptor struct {
|
||||||
// Headers covers any headers that may be returned from the response.
|
// Headers covers any headers that may be returned from the response.
|
||||||
Headers []ParameterDescriptor
|
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
|
// ErrorCodes enumerates the error codes that may be returned along with
|
||||||
// the response.
|
// the response.
|
||||||
ErrorCodes []errcode.ErrorCode
|
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
|
var routeDescriptorsMap map[string]RouteDescriptor
|
||||||
|
|
|
@ -11,10 +11,12 @@ const (
|
||||||
RouteNameBlob = "blob"
|
RouteNameBlob = "blob"
|
||||||
RouteNameBlobUpload = "blob-upload"
|
RouteNameBlobUpload = "blob-upload"
|
||||||
RouteNameBlobUploadChunk = "blob-upload-chunk"
|
RouteNameBlobUploadChunk = "blob-upload-chunk"
|
||||||
|
RouteNameCatalog = "catalog"
|
||||||
)
|
)
|
||||||
|
|
||||||
var allEndpoints = []string{
|
var allEndpoints = []string{
|
||||||
RouteNameManifest,
|
RouteNameManifest,
|
||||||
|
RouteNameCatalog,
|
||||||
RouteNameTags,
|
RouteNameTags,
|
||||||
RouteNameBlob,
|
RouteNameBlob,
|
||||||
RouteNameBlobUpload,
|
RouteNameBlobUpload,
|
||||||
|
|
|
@ -100,6 +100,18 @@ func (ub *URLBuilder) BuildBaseURL() (string, error) {
|
||||||
return baseURL.String(), nil
|
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.
|
// BuildTagsURL constructs a url to list the tags in the named repository.
|
||||||
func (ub *URLBuilder) BuildTagsURL(name string) (string, error) {
|
func (ub *URLBuilder) BuildTagsURL(name string) (string, error) {
|
||||||
route := ub.cloneRoute(RouteNameTags)
|
route := ub.cloneRoute(RouteNameTags)
|
||||||
|
|
|
@ -53,13 +53,6 @@ func handleErrorResponse(resp *http.Response) error {
|
||||||
err := parseHTTPErrorResponse(resp.Body)
|
err := parseHTTPErrorResponse(resp.Body)
|
||||||
if uErr, ok := err.(*UnexpectedHTTPResponseError); ok {
|
if uErr, ok := err.(*UnexpectedHTTPResponseError); ok {
|
||||||
return v2.ErrorCodeUnauthorized.WithDetail(uErr.Response)
|
return v2.ErrorCodeUnauthorized.WithDetail(uErr.Response)
|
||||||
/*
|
|
||||||
return &errcode.Error{
|
|
||||||
Code: v2.ErrorCodeUnauthorized,
|
|
||||||
Message: v2.ErrorCodeUnauthorized.Message(),
|
|
||||||
Detail: uErr.Response,
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,83 @@ import (
|
||||||
"github.com/docker/distribution/registry/storage/cache/memory"
|
"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
|
// 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) {
|
func NewRepository(ctx context.Context, name, baseURL string, transport http.RoundTripper) (distribution.Repository, error) {
|
||||||
if err := v2.ValidateRepositoryName(name); err != nil {
|
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)
|
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 New Issue