diff --git a/docs/spec/api.md b/docs/spec/api.md
index 483f15f0..ba4bc1ca 100644
--- a/docs/spec/api.md
+++ b/docs/spec/api.md
@@ -120,6 +120,16 @@ indicating what is different. Optionally, we may start marking parts of the
specification to correspond with the versions enumerated here.
+
+ - 2.0.4
>
+ -
+
+ - Added support for listing registry contents.
+ - Added pagination to tags API.
+ - Added common approach to support pagination.
+
+
+
- 2.0.3
-
- Allow repository name components to be one character.
@@ -131,7 +141,6 @@ specification to correspond with the versions enumerated here.
- Added section covering digest format.
- Added more clarification that manifest cannot be deleted by tag.
-
- 2.0.1
-
@@ -745,7 +754,9 @@ each unknown blob. The response format is as follows:
]
}
-#### Listing Image Tags
+
+
+### Listing Image Tags
It may be necessary to list all of the tags under a given repository. The tags
for an image repository can be retrieved with the following request:
@@ -766,8 +777,166 @@ The response will be in the following format:
}
For repositories with a large number of tags, this response may be quite
-large, so care should be taken by the client when parsing the response to
-reduce copying.
+large. If such a response is expected, one should use the pagination.
+
+#### Pagination
+
+Paginated tag results can be retrieved by adding the appropriate pagination
+parameters. Starting a paginated flow may begin as follows:
+
+```
+GET /v2//tags/list?n=
+```
+
+The above specifies that a tags response should be returned, from the start of
+the result set, ordered lexically, limiting the number of results to `n`. The
+response to such a request would look as follows:
+
+```
+200 OK
+Content-Type: application/json
+
+{
+ "name": ,
+ "tags": [
+ ,
+ ...
+ ]
+ "next": ?n=&last=
+}
+```
+
+> __TODO(stevvooe):__ Consider using a Header here, rather than a body parameter. A
+header would allow one to issue the next request before parsing the response
+body.
+
+To get the next result set, a client would issue the request as follows, using
+the value of "next" from the response body:
+
+```
+GET /v2//tags/list?n=&last=
+```
+
+The above process should then be repeated until the `next` parameter is no
+longer set in the response.
+
+The behavior of `last` is quite simple and can be demonstrated with an
+example. Let's say the repository has the following tags:
+
+```
+a
+b
+c
+d
+```
+
+If the value of `n` is 2, _a_ and _b_ will be returned on the first response.
+The `next` url within the respone will have `n` set to 2 and last set to _b_:
+
+```
+"next": ?n=2&last=b
+```
+
+The client can then issue the response, receiving the values _c_ and _d_. Note
+that n may change on second to last response or be omitted fully, if the
+server may so choose.
+
+### Listing Repositories
+
+Images are stored in collections, known as a _repository_, which is keyed by a
+`name`, as seen throughout the API specification. A registry instance may
+contain several repositories. The list of available repositories, or image
+names, is made available through the _catalog_.
+
+The catalog for a given registry can be retrived with the following request:
+
+```
+GET /v2/_catalog
+```
+
+The response will be in the following format:
+
+```
+200 OK
+Content-Type: application/json
+
+{
+ "repositories": [
+ ,
+ ...
+ ]
+}
+```
+
+For registries with a large number of repositories, this response may be quite
+large. If such a response is expected, one should use the pagination.
+
+#### Pagination
+
+Paginated repository results can be retrieved by adding the appropriate
+pagination parameters, which are similar to those available in the tag API.
+Starting a paginated flow may begin as follows:
+
+```
+GET /v2/_catalog?n=
+```
+
+The above specifies that a catalog response should be returned, from the start of
+the result set, ordered lexically, limiting the number of results to `n`. The
+response to such a request would look as follows:
+
+```
+200 OK
+Content-Type: application/json
+
+{
+ "repositories": [
+ ,
+ ...
+ ]
+ "next": ?n=&last=
+}
+```
+
+> __TODO(stevvooe):__ Consider using a Header here, rather than a body parameter. A
+header would allow one to issue the next request before parsing the response
+body.
+
+To get the next result set, a client would issue the request as follows, using
+the value of "next" from the response body:
+
+```
+GET /v2/_catalog?n=&last=
+```
+
+The above process should then be repeated until the `next` parameter is no
+longer set in the response.
+
+The result set of repository names is represented abstractly as a lexically
+sorted list, where the position in that list can be specified by the query
+term `last`. The entries in the response start _after_ the term specified by
+`last`, up to `n` entries.
+
+The behavior of `last` is quite simple when demonstrated with an example.
+Let's say the registry has the following repositories:
+
+```
+a
+b
+c
+d
+```
+
+If the value of `n` is 2, _a_ and _b_ will be returned on the first response.
+The `next` url within the respone will have `n` set to 2 and last set to _b_:
+
+```
+"next": ?n=2&last=b
+```
+
+The client can then issue the request with above value of `next`, receiving
+the values _c_ and _d_. Note that n may change on second to last response or
+be omitted fully, if the server may so choose.
### Deleting an Image
@@ -817,6 +986,7 @@ A list of methods and URIs are covered in the table below:
| PATCH | `/v2//blobs/uploads/` | Blob Upload | Upload a chunk of data for the specified upload. |
| PUT | `/v2//blobs/uploads/` | Blob Upload | Complete the upload specified by `uuid`, optionally appending the body as the final chunk. |
| DELETE | `/v2//blobs/uploads/` | Blob Upload | Cancel outstanding upload processes, releasing associated resources. If this is not called, the unfinished uploads will eventually timeout. |
+| GET | `/v2/_catalog` | Catalog | Retrieve a sorted, json list of repositories available in the registry. |
The detail for each endpoint is covered in the following sections.
@@ -886,7 +1056,6 @@ The API implements V2 protocol and is accessible.
-
###### On Failure: Unauthorized
```
@@ -1056,6 +1225,57 @@ The error codes that may be included in the response body are enumerated below:
+```
+GET /v2//tags/list?n=last=
+```
+
+Return a portion of the tags for the specified repository.
+
+
+The following parameters should be specified on the request:
+
+|Name|Kind|Description|
+|----|----|-----------|
+|`name`|path|Name of the target repository.|
+|`n`|query|Limit the number of entries in each response. It not present, all entries will be returned.|
+|`last`|query|Result set will include values lexically after last.|
+
+
+
+
+###### On Success: OK
+
+```
+200 OK
+Content-Length:
+Content-Type: application/json; charset=utf-8
+
+{
+ "name": ,
+ "tags": [
+ ,
+ ...
+ ],
+ "next": "?last=&n="
+}
+```
+
+A list of tags for the named repository.
+The following fields may be returned in the response body:
+
+|Name|Description|
+|----|-----------|
+|`next`|Provides the URL to get the next set of results, if available.|
+
+The following headers will be returned with the response:
+
+|Name|Description|
+|----|-----------|
+|`Content-Length`|Length of the JSON response body.|
+
+
+
+
### Manifest
@@ -1453,7 +1673,6 @@ The following parameters should be specified on the request:
-
###### On Failure: Invalid Name or Reference
```
@@ -2907,3 +3126,103 @@ The error codes that may be included in the response body are enumerated below:
+### Catalog
+
+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.
+
+
+
+#### GET Catalog
+
+Retrieve a sorted, json list of repositories available in the registry.
+
+
+##### Catalog Fetch Complete
+
+```
+GET /v2/_catalog
+```
+
+Request an unabridged list of repositories available.
+
+
+
+
+
+###### On Success: OK
+
+```
+200 OK
+Content-Length:
+Content-Type: application/json; charset=utf-8
+
+{
+ "repositories": [
+ ,
+ ...
+ ]
+}
+```
+
+Returns the unabridged list of repositories as a json response.
+
+The following headers will be returned with the response:
+
+|Name|Description|
+|----|-----------|
+|`Content-Length`|Length of the JSON response body.|
+
+
+
+##### Catalog Fetch Paginated
+
+```
+GET /v2/_catalog?n=last=
+```
+
+Return the specified portion of repositories.
+
+
+The following parameters should be specified on the request:
+
+|Name|Kind|Description|
+|----|----|-----------|
+|`n`|query|Limit the number of entries in each response. It not present, all entries will be returned.|
+|`last`|query|Result set will include values lexically after last.|
+
+
+
+
+###### On Success: OK
+
+```
+200 OK
+Content-Length:
+Content-Type: application/json; charset=utf-8
+
+{
+ "repositories": [
+ ,
+ ...
+ ]
+ "next": "?last=&n="
+}
+```
+
+
+The following fields may be returned in the response body:
+
+|Name|Description|
+|----|-----------|
+|`next`|Provides the URL to get the next set of results, if available.|
+
+The following headers will be returned with the response:
+
+|Name|Description|
+|----|-----------|
+|`Content-Length`|Length of the JSON response body.|
+
+
+
+
+
diff --git a/docs/spec/api.md.tmpl b/docs/spec/api.md.tmpl
index bc6d6f92..218f30c9 100644
--- a/docs/spec/api.md.tmpl
+++ b/docs/spec/api.md.tmpl
@@ -120,6 +120,16 @@ indicating what is different. Optionally, we may start marking parts of the
specification to correspond with the versions enumerated here.
+
+ - 2.0.4
>
+ -
+
+ - Added support for listing registry contents.
+ - Added pagination to tags API.
+ - Added common approach to support pagination.
+
+
+
- 2.0.3
-
- Allow repository name components to be one character.
@@ -131,7 +141,6 @@ specification to correspond with the versions enumerated here.
- Added section covering digest format.
- Added more clarification that manifest cannot be deleted by tag.
-
- 2.0.1
-
@@ -745,7 +754,9 @@ each unknown blob. The response format is as follows:
]
}
-#### Listing Image Tags
+
+
+### Listing Image Tags
It may be necessary to list all of the tags under a given repository. The tags
for an image repository can be retrieved with the following request:
@@ -766,8 +777,166 @@ The response will be in the following format:
}
For repositories with a large number of tags, this response may be quite
-large, so care should be taken by the client when parsing the response to
-reduce copying.
+large. If such a response is expected, one should use the pagination.
+
+#### Pagination
+
+Paginated tag results can be retrieved by adding the appropriate pagination
+parameters. Starting a paginated flow may begin as follows:
+
+```
+GET /v2//tags/list?n=
+```
+
+The above specifies that a tags response should be returned, from the start of
+the result set, ordered lexically, limiting the number of results to `n`. The
+response to such a request would look as follows:
+
+```
+200 OK
+Content-Type: application/json
+
+{
+ "name": ,
+ "tags": [
+ ,
+ ...
+ ]
+ "next": ?n=&last=
+}
+```
+
+> __TODO(stevvooe):__ Consider using a Header here, rather than a body parameter. A
+header would allow one to issue the next request before parsing the response
+body.
+
+To get the next result set, a client would issue the request as follows, using
+the value of "next" from the response body:
+
+```
+GET /v2//tags/list?n=&last=
+```
+
+The above process should then be repeated until the `next` parameter is no
+longer set in the response.
+
+The behavior of `last` is quite simple and can be demonstrated with an
+example. Let's say the repository has the following tags:
+
+```
+a
+b
+c
+d
+```
+
+If the value of `n` is 2, _a_ and _b_ will be returned on the first response.
+The `next` url within the respone will have `n` set to 2 and last set to _b_:
+
+```
+"next": ?n=2&last=b
+```
+
+The client can then issue the response, receiving the values _c_ and _d_. Note
+that n may change on second to last response or be omitted fully, if the
+server may so choose.
+
+### Listing Repositories
+
+Images are stored in collections, known as a _repository_, which is keyed by a
+`name`, as seen throughout the API specification. A registry instance may
+contain several repositories. The list of available repositories, or image
+names, is made available through the _catalog_.
+
+The catalog for a given registry can be retrived with the following request:
+
+```
+GET /v2/_catalog
+```
+
+The response will be in the following format:
+
+```
+200 OK
+Content-Type: application/json
+
+{
+ "repositories": [
+ ,
+ ...
+ ]
+}
+```
+
+For registries with a large number of repositories, this response may be quite
+large. If such a response is expected, one should use the pagination.
+
+#### Pagination
+
+Paginated repository results can be retrieved by adding the appropriate
+pagination parameters, which are similar to those available in the tag API.
+Starting a paginated flow may begin as follows:
+
+```
+GET /v2/_catalog?n=
+```
+
+The above specifies that a catalog response should be returned, from the start of
+the result set, ordered lexically, limiting the number of results to `n`. The
+response to such a request would look as follows:
+
+```
+200 OK
+Content-Type: application/json
+
+{
+ "repositories": [
+ ,
+ ...
+ ]
+ "next": ?n=&last=
+}
+```
+
+> __TODO(stevvooe):__ Consider using a Header here, rather than a body parameter. A
+header would allow one to issue the next request before parsing the response
+body.
+
+To get the next result set, a client would issue the request as follows, using
+the value of "next" from the response body:
+
+```
+GET /v2/_catalog?n=&last=
+```
+
+The above process should then be repeated until the `next` parameter is no
+longer set in the response.
+
+The result set of repository names is represented abstractly as a lexically
+sorted list, where the position in that list can be specified by the query
+term `last`. The entries in the response start _after_ the term specified by
+`last`, up to `n` entries.
+
+The behavior of `last` is quite simple when demonstrated with an example.
+Let's say the registry has the following repositories:
+
+```
+a
+b
+c
+d
+```
+
+If the value of `n` is 2, _a_ and _b_ will be returned on the first response.
+The `next` url within the respone will have `n` set to 2 and last set to _b_:
+
+```
+"next": ?n=2&last=b
+```
+
+The client can then issue the request with above value of `next`, receiving
+the values _c_ and _d_. Note that n may change on second to last response or
+be omitted fully, if the server may so choose.
### Deleting an Image
@@ -867,8 +1036,13 @@ Content-Type: {{.Body.ContentType}}{{end}}{{if .Body.Format}}
```
{{.Description}}
+{{if .Fields}}The following fields may be returned in the response body:
-{{if .Headers}}The following headers will be returned with the response:
+|Name|Description|
+|----|-----------|
+{{range .Fields}}|`{{.Name}}`|{{.Description}}|
+{{end}}{{end}}{{if .Headers}}
+The following headers will be returned with the response:
|Name|Description|
|----|-----------|
diff --git a/registry/api/v2/descriptors.go b/registry/api/v2/descriptors.go
index f2551ffe..4eec6492 100644
--- a/registry/api/v2/descriptors.go
+++ b/registry/api/v2/descriptors.go
@@ -87,6 +87,23 @@ var (
Format: "",
}
+ paginationParameters = []ParameterDescriptor{
+ {
+ Name: "n",
+ Type: "integer",
+ Description: "Limit the number of entries in each response. It not present, all entries will be returned.",
+ Format: "",
+ Required: false,
+ },
+ {
+ Name: "last",
+ Type: "string",
+ Description: "Result set will include values lexically after last.",
+ Format: "",
+ Required: false,
+ },
+ }
+
unauthorizedResponse = ResponseDescriptor{
Description: "The client does not have access to the repository.",
StatusCode: http.StatusUnauthorized,
@@ -269,6 +286,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 +447,44 @@ 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: "",
+ },
+ },
+ Fields: []ParameterDescriptor{
+ {
+ Name: "next",
+ Type: "url",
+ Description: "Provides the URL to get the next set of results, if available.",
+ Format: "",
+ },
+ },
+ Body: BodyDescriptor{
+ ContentType: "application/json; charset=utf-8",
+ Format: `{
+ "name": ,
+ "tags": [
+ ,
+ ...
+ ],
+ "next": "?last=&n="
+}`,
+ },
+ },
+ },
+ },
},
},
},
@@ -1320,6 +1378,83 @@ 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: "",
+ },
+ },
+ Body: BodyDescriptor{
+ ContentType: "application/json; charset=utf-8",
+ Format: `{
+ "repositories": [
+ ,
+ ...
+ ]
+}`,
+ },
+ },
+ },
+ },
+ {
+ 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": [
+ ,
+ ...
+ ]
+ "next": "?last=&n="
+}`,
+ },
+ Headers: []ParameterDescriptor{
+ {
+ Name: "Content-Length",
+ Type: "integer",
+ Description: "Length of the JSON response body.",
+ Format: "",
+ },
+ },
+ Fields: []ParameterDescriptor{
+ {
+ Name: "next",
+ Type: "url",
+ Description: "Provides the URL to get the next set of results, if available.",
+ Format: "",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
}
var routeDescriptorsMap map[string]RouteDescriptor
diff --git a/registry/api/v2/routes.go b/registry/api/v2/routes.go
index 69f9d901..d18860f5 100644
--- a/registry/api/v2/routes.go
+++ b/registry/api/v2/routes.go
@@ -11,6 +11,7 @@ const (
RouteNameBlob = "blob"
RouteNameBlobUpload = "blob-upload"
RouteNameBlobUploadChunk = "blob-upload-chunk"
+ RouteNameCatalog = "catalog"
)
var allEndpoints = []string{