Signed-off-by: Doug Davis <dug@us.ibm.com>
| ... | ... |
@@ -18,6 +18,7 @@ import ( |
| 18 | 18 |
"github.com/docker/docker/trust" |
| 19 | 19 |
"github.com/docker/docker/utils" |
| 20 | 20 |
"github.com/docker/libtrust" |
| 21 |
+ "golang.org/x/net/context" |
|
| 21 | 22 |
) |
| 22 | 23 |
|
| 23 | 24 |
type v2Puller struct {
|
| ... | ... |
@@ -58,7 +59,13 @@ func (p *v2Puller) pullV2Repository(tag string) (err error) {
|
| 58 | 58 |
taggedName = utils.ImageReference(p.repoInfo.LocalName, tag) |
| 59 | 59 |
} else {
|
| 60 | 60 |
var err error |
| 61 |
- tags, err = p.repo.Manifests().Tags() |
|
| 61 |
+ |
|
| 62 |
+ manSvc, err := p.repo.Manifests(context.Background()) |
|
| 63 |
+ if err != nil {
|
|
| 64 |
+ return err |
|
| 65 |
+ } |
|
| 66 |
+ |
|
| 67 |
+ tags, err = manSvc.Tags() |
|
| 62 | 68 |
if err != nil {
|
| 63 | 69 |
return err |
| 64 | 70 |
} |
| ... | ... |
@@ -140,7 +147,7 @@ func (p *v2Puller) download(di *downloadInfo) {
|
| 140 | 140 |
di.err <- err |
| 141 | 141 |
return |
| 142 | 142 |
} |
| 143 |
- di.size = desc.Length |
|
| 143 |
+ di.size = desc.Size |
|
| 144 | 144 |
|
| 145 | 145 |
layerDownload, err := blobs.Open(nil, di.digest) |
| 146 | 146 |
if err != nil {
|
| ... | ... |
@@ -187,7 +194,12 @@ func (p *v2Puller) pullV2Tag(tag, taggedName string) (bool, error) {
|
| 187 | 187 |
logrus.Debugf("Pulling tag from V2 registry: %q", tag)
|
| 188 | 188 |
out := p.config.OutStream |
| 189 | 189 |
|
| 190 |
- manifest, err := p.repo.Manifests().GetByTag(tag) |
|
| 190 |
+ manSvc, err := p.repo.Manifests(context.Background()) |
|
| 191 |
+ if err != nil {
|
|
| 192 |
+ return false, err |
|
| 193 |
+ } |
|
| 194 |
+ |
|
| 195 |
+ manifest, err := manSvc.GetByTag(tag) |
|
| 191 | 196 |
if err != nil {
|
| 192 | 197 |
return false, err |
| 193 | 198 |
} |
| ... | ... |
@@ -16,6 +16,7 @@ import ( |
| 16 | 16 |
"github.com/docker/docker/registry" |
| 17 | 17 |
"github.com/docker/docker/runconfig" |
| 18 | 18 |
"github.com/docker/docker/utils" |
| 19 |
+ "golang.org/x/net/context" |
|
| 19 | 20 |
) |
| 20 | 21 |
|
| 21 | 22 |
type v2Pusher struct {
|
| ... | ... |
@@ -191,7 +192,11 @@ func (p *v2Pusher) pushV2Tag(tag string) error {
|
| 191 | 191 |
out.Write(p.sf.FormatStatus("", "Digest: %s", manifestDigest))
|
| 192 | 192 |
} |
| 193 | 193 |
|
| 194 |
- return p.repo.Manifests().Put(signed) |
|
| 194 |
+ manSvc, err := p.repo.Manifests(context.Background()) |
|
| 195 |
+ if err != nil {
|
|
| 196 |
+ return err |
|
| 197 |
+ } |
|
| 198 |
+ return manSvc.Put(signed) |
|
| 195 | 199 |
} |
| 196 | 200 |
|
| 197 | 201 |
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 |
| 35 | 35 |
clone git github.com/hashicorp/consul v0.5.2 |
| 36 | 36 |
|
| 37 | 37 |
# get graph and distribution packages |
| 38 |
-clone git github.com/docker/distribution 856638e299eddf01964fa918ac1552d8aa2e22b3 |
|
| 38 |
+clone git github.com/docker/distribution cd8ff553b6b1911be23dfeabb73e33108bcbf147 |
|
| 39 | 39 |
clone git github.com/vbatts/tar-split v0.9.4 |
| 40 | 40 |
|
| 41 | 41 |
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 |
| 97 | 97 |
community. While some of these standards may seem arbitrary, they somehow seem |
| 98 | 98 |
to result in a solid, consistent codebase. |
| 99 | 99 |
|
| 100 |
+It is possible that the code base does not currently comply with these |
|
| 101 |
+guidelines. We are not looking for a massive PR that fixes this, since that |
|
| 102 |
+goes against the spirit of the guidelines. All new contributions should make a |
|
| 103 |
+best effort to clean up and make the code base better than they left it. |
|
| 104 |
+Obviously, apply your best judgement. Remember, the goal here is to make the |
|
| 105 |
+code base easier for humans to navigate and understand. Always keep that in |
|
| 106 |
+mind when nudging others to comply. |
|
| 107 |
+ |
|
| 100 | 108 |
The rules: |
| 101 | 109 |
|
| 102 | 110 |
1. All code should be formatted with `gofmt -s`. |
| 103 | 111 |
2. All code should pass the default levels of |
| 104 | 112 |
[`golint`](https://github.com/golang/lint). |
| 105 |
-3. All code should follow the guidelines covered at |
|
| 106 |
- https://github.com/golang/go/wiki/CodeReviewComments. |
|
| 113 |
+3. All code should follow the guidelines covered in [Effective |
|
| 114 |
+ Go](http://golang.org/doc/effective_go.html) and [Go Code Review |
|
| 115 |
+ Comments](https://github.com/golang/go/wiki/CodeReviewComments). |
|
| 107 | 116 |
4. Comment the code. Tell us the why, the history and the context. |
| 108 | 117 |
5. Document _all_ declarations and methods, even private ones. Declare |
| 109 | 118 |
expectations, caveats and anything else that may be important. If a type |
| 110 | 119 |
gets exported, having the comments already there will ensure it's ready. |
| 111 |
-6. Variable name length should be proportional to it's context and no longer. |
|
| 112 |
- noALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo. In |
|
| 113 |
- practice, short methods will have short variable names and globals will |
|
| 120 |
+6. Variable name length should be proportional to its context and no longer. |
|
| 121 |
+ `noCommaALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo`. |
|
| 122 |
+ In practice, short methods will have short variable names and globals will |
|
| 114 | 123 |
have longer names. |
| 115 | 124 |
7. No underscores in package names. If you need a compound name, step back, |
| 116 | 125 |
and re-examine why you need a compound name. If you still think you need a |
| 117 | 126 |
compound name, lose the underscore. |
| 118 | 127 |
8. No utils or helpers packages. If a function is not general enough to |
| 119 |
- warrant it's own package, it has not been written generally enough to be a |
|
| 128 |
+ warrant its own package, it has not been written generally enough to be a |
|
| 120 | 129 |
part of a util package. Just leave it unexported and well-documented. |
| 121 |
-9. No, we don't need another unit testing framework. |
|
| 130 |
+9. All tests should run with `go test` and outside tooling should not be |
|
| 131 |
+ required. No, we don't need another unit testing framework. Assertion |
|
| 132 |
+ packages are acceptable if they provide _real_ incremental value. |
|
| 122 | 133 |
10. Even though we call these "rules" above, they are actually just |
| 123 | 134 |
guidelines. Since you've read all the rules, you now know that. |
| 124 | 135 |
|
| 125 | 136 |
If you are having trouble getting into the mood of idiomatic Go, we recommend |
| 126 |
-reading through [`Effective Go`](http://golang.org/doc/effective_go.html). The |
|
| 137 |
+reading through [Effective Go](http://golang.org/doc/effective_go.html). The |
|
| 127 | 138 |
[Go Blog](http://blog.golang.org/) is also a great resource. Drinking the |
| 128 | 139 |
kool-aid is a lot easier than going thirsty. |
| ... | ... |
@@ -7,6 +7,8 @@ for storing and distributing Docker images. It supersedes the [docker/docker- |
| 7 | 7 |
registry](https://github.com/docker/docker-registry) project with a new API |
| 8 | 8 |
design, focused around security and performance. |
| 9 | 9 |
|
| 10 |
+<img src="https://www.docker.com/sites/default/files/oyster-registry-3.png" width=200px/> |
|
| 11 |
+ |
|
| 10 | 12 |
This repository contains the following components: |
| 11 | 13 |
|
| 12 | 14 |
|**Component** |Description | |
| ... | ... |
@@ -35,6 +35,12 @@ type Namespace interface {
|
| 35 | 35 |
// registry may or may not have the repository but should always return a |
| 36 | 36 |
// reference. |
| 37 | 37 |
Repository(ctx context.Context, name string) (Repository, error) |
| 38 |
+ |
|
| 39 |
+ // Repositories fills 'repos' with a lexigraphically sorted catalog of repositories |
|
| 40 |
+ // up to the size of 'repos' and returns the value 'n' for the number of entries |
|
| 41 |
+ // which were filled. 'last' contains an offset in the catalog, and 'err' will be |
|
| 42 |
+ // set to io.EOF if there are no more entries to obtain. |
|
| 43 |
+ Repositories(ctx context.Context, repos []string, last string) (n int, err error) |
|
| 38 | 44 |
} |
| 39 | 45 |
|
| 40 | 46 |
// ManifestServiceOption is a function argument for Manifest Service methods |
| ... | ... |
@@ -106,7 +106,7 @@ func (e Error) ErrorCode() ErrorCode {
|
| 106 | 106 |
func (e Error) Error() string {
|
| 107 | 107 |
return fmt.Sprintf("%s: %s",
|
| 108 | 108 |
strings.ToLower(strings.Replace(e.Code.String(), "_", " ", -1)), |
| 109 |
- e.Code.Message()) |
|
| 109 |
+ e.Message) |
|
| 110 | 110 |
} |
| 111 | 111 |
|
| 112 | 112 |
// WithDetail will return a new Error, based on the current one, but with |
| ... | ... |
@@ -87,6 +87,30 @@ var ( |
| 87 | 87 |
Format: "<digest>", |
| 88 | 88 |
} |
| 89 | 89 |
|
| 90 |
+ linkHeader = ParameterDescriptor{
|
|
| 91 |
+ Name: "Link", |
|
| 92 |
+ Type: "link", |
|
| 93 |
+ Description: "RFC5988 compliant rel='next' with URL to next result set, if available", |
|
| 94 |
+ Format: `<<url>?n=<last n value>&last=<last entry from response>>; rel="next"`, |
|
| 95 |
+ } |
|
| 96 |
+ |
|
| 97 |
+ paginationParameters = []ParameterDescriptor{
|
|
| 98 |
+ {
|
|
| 99 |
+ Name: "n", |
|
| 100 |
+ Type: "integer", |
|
| 101 |
+ Description: "Limit the number of entries in each response. It not present, all entries will be returned.", |
|
| 102 |
+ Format: "<integer>", |
|
| 103 |
+ Required: false, |
|
| 104 |
+ }, |
|
| 105 |
+ {
|
|
| 106 |
+ Name: "last", |
|
| 107 |
+ Type: "string", |
|
| 108 |
+ Description: "Result set will include values lexically after last.", |
|
| 109 |
+ Format: "<integer>", |
|
| 110 |
+ Required: false, |
|
| 111 |
+ }, |
|
| 112 |
+ } |
|
| 113 |
+ |
|
| 90 | 114 |
unauthorizedResponse = ResponseDescriptor{
|
| 91 | 115 |
Description: "The client does not have access to the repository.", |
| 92 | 116 |
StatusCode: http.StatusUnauthorized, |
| ... | ... |
@@ -269,6 +293,9 @@ type ResponseDescriptor struct {
|
| 269 | 269 |
// Headers covers any headers that may be returned from the response. |
| 270 | 270 |
Headers []ParameterDescriptor |
| 271 | 271 |
|
| 272 |
+ // Fields describes any fields that may be present in the response. |
|
| 273 |
+ Fields []ParameterDescriptor |
|
| 274 |
+ |
|
| 272 | 275 |
// ErrorCodes enumerates the error codes that may be returned along with |
| 273 | 276 |
// the response. |
| 274 | 277 |
ErrorCodes []errcode.ErrorCode |
| ... | ... |
@@ -427,6 +454,36 @@ var routeDescriptors = []RouteDescriptor{
|
| 427 | 427 |
}, |
| 428 | 428 |
}, |
| 429 | 429 |
}, |
| 430 |
+ {
|
|
| 431 |
+ Description: "Return a portion of the tags for the specified repository.", |
|
| 432 |
+ PathParameters: []ParameterDescriptor{nameParameterDescriptor},
|
|
| 433 |
+ QueryParameters: paginationParameters, |
|
| 434 |
+ Successes: []ResponseDescriptor{
|
|
| 435 |
+ {
|
|
| 436 |
+ StatusCode: http.StatusOK, |
|
| 437 |
+ Description: "A list of tags for the named repository.", |
|
| 438 |
+ Headers: []ParameterDescriptor{
|
|
| 439 |
+ {
|
|
| 440 |
+ Name: "Content-Length", |
|
| 441 |
+ Type: "integer", |
|
| 442 |
+ Description: "Length of the JSON response body.", |
|
| 443 |
+ Format: "<length>", |
|
| 444 |
+ }, |
|
| 445 |
+ linkHeader, |
|
| 446 |
+ }, |
|
| 447 |
+ Body: BodyDescriptor{
|
|
| 448 |
+ ContentType: "application/json; charset=utf-8", |
|
| 449 |
+ Format: `{
|
|
| 450 |
+ "name": <name>, |
|
| 451 |
+ "tags": [ |
|
| 452 |
+ <tag>, |
|
| 453 |
+ ... |
|
| 454 |
+ ], |
|
| 455 |
+}`, |
|
| 456 |
+ }, |
|
| 457 |
+ }, |
|
| 458 |
+ }, |
|
| 459 |
+ }, |
|
| 430 | 460 |
}, |
| 431 | 461 |
}, |
| 432 | 462 |
}, |
| ... | ... |
@@ -1320,6 +1377,76 @@ var routeDescriptors = []RouteDescriptor{
|
| 1320 | 1320 |
}, |
| 1321 | 1321 |
}, |
| 1322 | 1322 |
}, |
| 1323 |
+ {
|
|
| 1324 |
+ Name: RouteNameCatalog, |
|
| 1325 |
+ Path: "/v2/_catalog", |
|
| 1326 |
+ Entity: "Catalog", |
|
| 1327 |
+ 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.", |
|
| 1328 |
+ Methods: []MethodDescriptor{
|
|
| 1329 |
+ {
|
|
| 1330 |
+ Method: "GET", |
|
| 1331 |
+ Description: "Retrieve a sorted, json list of repositories available in the registry.", |
|
| 1332 |
+ Requests: []RequestDescriptor{
|
|
| 1333 |
+ {
|
|
| 1334 |
+ Name: "Catalog Fetch Complete", |
|
| 1335 |
+ Description: "Request an unabridged list of repositories available.", |
|
| 1336 |
+ Successes: []ResponseDescriptor{
|
|
| 1337 |
+ {
|
|
| 1338 |
+ Description: "Returns the unabridged list of repositories as a json response.", |
|
| 1339 |
+ StatusCode: http.StatusOK, |
|
| 1340 |
+ Headers: []ParameterDescriptor{
|
|
| 1341 |
+ {
|
|
| 1342 |
+ Name: "Content-Length", |
|
| 1343 |
+ Type: "integer", |
|
| 1344 |
+ Description: "Length of the JSON response body.", |
|
| 1345 |
+ Format: "<length>", |
|
| 1346 |
+ }, |
|
| 1347 |
+ }, |
|
| 1348 |
+ Body: BodyDescriptor{
|
|
| 1349 |
+ ContentType: "application/json; charset=utf-8", |
|
| 1350 |
+ Format: `{
|
|
| 1351 |
+ "repositories": [ |
|
| 1352 |
+ <name>, |
|
| 1353 |
+ ... |
|
| 1354 |
+ ] |
|
| 1355 |
+}`, |
|
| 1356 |
+ }, |
|
| 1357 |
+ }, |
|
| 1358 |
+ }, |
|
| 1359 |
+ }, |
|
| 1360 |
+ {
|
|
| 1361 |
+ Name: "Catalog Fetch Paginated", |
|
| 1362 |
+ Description: "Return the specified portion of repositories.", |
|
| 1363 |
+ QueryParameters: paginationParameters, |
|
| 1364 |
+ Successes: []ResponseDescriptor{
|
|
| 1365 |
+ {
|
|
| 1366 |
+ StatusCode: http.StatusOK, |
|
| 1367 |
+ Body: BodyDescriptor{
|
|
| 1368 |
+ ContentType: "application/json; charset=utf-8", |
|
| 1369 |
+ Format: `{
|
|
| 1370 |
+ "repositories": [ |
|
| 1371 |
+ <name>, |
|
| 1372 |
+ ... |
|
| 1373 |
+ ] |
|
| 1374 |
+ "next": "<url>?last=<name>&n=<last value of n>" |
|
| 1375 |
+}`, |
|
| 1376 |
+ }, |
|
| 1377 |
+ Headers: []ParameterDescriptor{
|
|
| 1378 |
+ {
|
|
| 1379 |
+ Name: "Content-Length", |
|
| 1380 |
+ Type: "integer", |
|
| 1381 |
+ Description: "Length of the JSON response body.", |
|
| 1382 |
+ Format: "<length>", |
|
| 1383 |
+ }, |
|
| 1384 |
+ linkHeader, |
|
| 1385 |
+ }, |
|
| 1386 |
+ }, |
|
| 1387 |
+ }, |
|
| 1388 |
+ }, |
|
| 1389 |
+ }, |
|
| 1390 |
+ }, |
|
| 1391 |
+ }, |
|
| 1392 |
+ }, |
|
| 1323 | 1393 |
} |
| 1324 | 1394 |
|
| 1325 | 1395 |
var routeDescriptorsMap map[string]RouteDescriptor |
| ... | ... |
@@ -11,10 +11,12 @@ const ( |
| 11 | 11 |
RouteNameBlob = "blob" |
| 12 | 12 |
RouteNameBlobUpload = "blob-upload" |
| 13 | 13 |
RouteNameBlobUploadChunk = "blob-upload-chunk" |
| 14 |
+ RouteNameCatalog = "catalog" |
|
| 14 | 15 |
) |
| 15 | 16 |
|
| 16 | 17 |
var allEndpoints = []string{
|
| 17 | 18 |
RouteNameManifest, |
| 19 |
+ RouteNameCatalog, |
|
| 18 | 20 |
RouteNameTags, |
| 19 | 21 |
RouteNameBlob, |
| 20 | 22 |
RouteNameBlobUpload, |
| ... | ... |
@@ -100,6 +100,18 @@ func (ub *URLBuilder) BuildBaseURL() (string, error) {
|
| 100 | 100 |
return baseURL.String(), nil |
| 101 | 101 |
} |
| 102 | 102 |
|
| 103 |
+// BuildCatalogURL constructs a url get a catalog of repositories |
|
| 104 |
+func (ub *URLBuilder) BuildCatalogURL(values ...url.Values) (string, error) {
|
|
| 105 |
+ route := ub.cloneRoute(RouteNameCatalog) |
|
| 106 |
+ |
|
| 107 |
+ catalogURL, err := route.URL() |
|
| 108 |
+ if err != nil {
|
|
| 109 |
+ return "", err |
|
| 110 |
+ } |
|
| 111 |
+ |
|
| 112 |
+ return appendValuesURL(catalogURL, values...).String(), nil |
|
| 113 |
+} |
|
| 114 |
+ |
|
| 103 | 115 |
// BuildTagsURL constructs a url to list the tags in the named repository. |
| 104 | 116 |
func (ub *URLBuilder) BuildTagsURL(name string) (string, error) {
|
| 105 | 117 |
route := ub.cloneRoute(RouteNameTags) |
| ... | ... |
@@ -53,13 +53,6 @@ func handleErrorResponse(resp *http.Response) error {
|
| 53 | 53 |
err := parseHTTPErrorResponse(resp.Body) |
| 54 | 54 |
if uErr, ok := err.(*UnexpectedHTTPResponseError); ok {
|
| 55 | 55 |
return v2.ErrorCodeUnauthorized.WithDetail(uErr.Response) |
| 56 |
- /* |
|
| 57 |
- return &errcode.Error{
|
|
| 58 |
- Code: v2.ErrorCodeUnauthorized, |
|
| 59 |
- Message: v2.ErrorCodeUnauthorized.Message(), |
|
| 60 |
- Detail: uErr.Response, |
|
| 61 |
- } |
|
| 62 |
- */ |
|
| 63 | 56 |
} |
| 64 | 57 |
return err |
| 65 | 58 |
} |
| ... | ... |
@@ -21,6 +21,83 @@ import ( |
| 21 | 21 |
"github.com/docker/distribution/registry/storage/cache/memory" |
| 22 | 22 |
) |
| 23 | 23 |
|
| 24 |
+// Registry provides an interface for calling Repositories, which returns a catalog of repositories. |
|
| 25 |
+type Registry interface {
|
|
| 26 |
+ Repositories(ctx context.Context, repos []string, last string) (n int, err error) |
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+// NewRegistry creates a registry namespace which can be used to get a listing of repositories |
|
| 30 |
+func NewRegistry(ctx context.Context, baseURL string, transport http.RoundTripper) (Registry, error) {
|
|
| 31 |
+ ub, err := v2.NewURLBuilderFromString(baseURL) |
|
| 32 |
+ if err != nil {
|
|
| 33 |
+ return nil, err |
|
| 34 |
+ } |
|
| 35 |
+ |
|
| 36 |
+ client := &http.Client{
|
|
| 37 |
+ Transport: transport, |
|
| 38 |
+ Timeout: 1 * time.Minute, |
|
| 39 |
+ } |
|
| 40 |
+ |
|
| 41 |
+ return ®istry{
|
|
| 42 |
+ client: client, |
|
| 43 |
+ ub: ub, |
|
| 44 |
+ context: ctx, |
|
| 45 |
+ }, nil |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 48 |
+type registry struct {
|
|
| 49 |
+ client *http.Client |
|
| 50 |
+ ub *v2.URLBuilder |
|
| 51 |
+ context context.Context |
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+// Repositories returns a lexigraphically sorted catalog given a base URL. The 'entries' slice will be filled up to the size |
|
| 55 |
+// of the slice, starting at the value provided in 'last'. The number of entries will be returned along with io.EOF if there |
|
| 56 |
+// are no more entries |
|
| 57 |
+func (r *registry) Repositories(ctx context.Context, entries []string, last string) (int, error) {
|
|
| 58 |
+ var numFilled int |
|
| 59 |
+ var returnErr error |
|
| 60 |
+ |
|
| 61 |
+ values := buildCatalogValues(len(entries), last) |
|
| 62 |
+ u, err := r.ub.BuildCatalogURL(values) |
|
| 63 |
+ if err != nil {
|
|
| 64 |
+ return 0, err |
|
| 65 |
+ } |
|
| 66 |
+ |
|
| 67 |
+ resp, err := r.client.Get(u) |
|
| 68 |
+ if err != nil {
|
|
| 69 |
+ return 0, err |
|
| 70 |
+ } |
|
| 71 |
+ defer resp.Body.Close() |
|
| 72 |
+ |
|
| 73 |
+ switch resp.StatusCode {
|
|
| 74 |
+ case http.StatusOK: |
|
| 75 |
+ var ctlg struct {
|
|
| 76 |
+ Repositories []string `json:"repositories"` |
|
| 77 |
+ } |
|
| 78 |
+ decoder := json.NewDecoder(resp.Body) |
|
| 79 |
+ |
|
| 80 |
+ if err := decoder.Decode(&ctlg); err != nil {
|
|
| 81 |
+ return 0, err |
|
| 82 |
+ } |
|
| 83 |
+ |
|
| 84 |
+ for cnt := range ctlg.Repositories {
|
|
| 85 |
+ entries[cnt] = ctlg.Repositories[cnt] |
|
| 86 |
+ } |
|
| 87 |
+ numFilled = len(ctlg.Repositories) |
|
| 88 |
+ |
|
| 89 |
+ link := resp.Header.Get("Link")
|
|
| 90 |
+ if link == "" {
|
|
| 91 |
+ returnErr = io.EOF |
|
| 92 |
+ } |
|
| 93 |
+ |
|
| 94 |
+ default: |
|
| 95 |
+ return 0, handleErrorResponse(resp) |
|
| 96 |
+ } |
|
| 97 |
+ |
|
| 98 |
+ return numFilled, returnErr |
|
| 99 |
+} |
|
| 100 |
+ |
|
| 24 | 101 |
// NewRepository creates a new Repository for the given repository name and base URL |
| 25 | 102 |
func NewRepository(ctx context.Context, name, baseURL string, transport http.RoundTripper) (distribution.Repository, error) {
|
| 26 | 103 |
if err := v2.ValidateRepositoryName(name); err != nil {
|
| ... | ... |
@@ -444,3 +521,17 @@ func (bs *blobStatter) Stat(ctx context.Context, dgst digest.Digest) (distributi |
| 444 | 444 |
return distribution.Descriptor{}, handleErrorResponse(resp)
|
| 445 | 445 |
} |
| 446 | 446 |
} |
| 447 |
+ |
|
| 448 |
+func buildCatalogValues(maxEntries int, last string) url.Values {
|
|
| 449 |
+ values := url.Values{}
|
|
| 450 |
+ |
|
| 451 |
+ if maxEntries > 0 {
|
|
| 452 |
+ values.Add("n", strconv.Itoa(maxEntries))
|
|
| 453 |
+ } |
|
| 454 |
+ |
|
| 455 |
+ if last != "" {
|
|
| 456 |
+ values.Add("last", last)
|
|
| 457 |
+ } |
|
| 458 |
+ |
|
| 459 |
+ return values |
|
| 460 |
+} |