Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ conformance-test:

conformance-binary: $(OUTPUT_DIRNAME)/conformance.test

TEST_REGISTRY_CONTAINER ?= ghcr.io/project-zot/zot-minimal-linux-amd64:v2.0.0-rc6@sha256:bf95a94849cd9c6f596fb10e5a2d03b74267e7886d1ba0b3dab33337d9e46e5c
TEST_REGISTRY_CONTAINER ?= ghcr.io/project-zot/zot-minimal-linux-amd64:v2.0.4@sha256:0312c23d9658b912a0d4db5c6ecd6d4391c1211912f10de3e9a52685f000318a
registry-ci:
docker rm -f oci-conformance && \
mkdir -p $(OUTPUT_DIRNAME) && \
Expand Down
35 changes: 23 additions & 12 deletions conformance/03_discovery_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"os"
"sort"
"strconv"
"strings"

Expand Down Expand Up @@ -61,17 +62,18 @@ var test03ContentDiscovery = func() {
SkipIfDisabled(contentDiscovery)
RunOnlyIf(runContentDiscoverySetup)
for i := 0; i < numTags; i++ {
tag := fmt.Sprintf("test%d", i)
tagList = append(tagList, tag)
req := client.NewRequest(reggie.PUT, "/v2/<name>/manifests/<reference>",
reggie.WithReference(tag)).
SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json").
SetBody(manifests[2].Content)
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(SatisfyAll(
BeNumerically(">=", 200),
BeNumerically("<", 300)))
for _, tag := range []string{"test" + strconv.Itoa(i), "TEST" + strconv.Itoa(i)} {
tagList = append(tagList, tag)
req := client.NewRequest(reggie.PUT, "/v2/<name>/manifests/<reference>",
reggie.WithReference(tag)).
SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json").
SetBody(manifests[2].Content)
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(SatisfyAll(
BeNumerically(">=", 200),
BeNumerically("<", 300)))
}
}
req := client.NewRequest(reggie.GET, "/v2/<name>/tags/list")
resp, err := client.Do(req)
Expand Down Expand Up @@ -253,14 +255,23 @@ var test03ContentDiscovery = func() {
})

g.Context("Test content discovery endpoints (listing tags)", func() {
g.Specify("GET request to list tags should yield 200 response", func() {
g.Specify("GET request to list tags should yield 200 response and be in sorted order", func() {
SkipIfDisabled(contentDiscovery)
req := client.NewRequest(reggie.GET, "/v2/<name>/tags/list")
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
tagList = getTagList(resp)
numTags = len(tagList)
// If the list is not empty, the tags MUST be in lexical order (i.e. case-insensitive alphanumeric order).
sortedTagListLexical := append([]string{}, tagList...)
sort.SliceStable(sortedTagListLexical, func(i, j int) bool {
return strings.ToLower(sortedTagListLexical[i]) < strings.ToLower(sortedTagListLexical[j])
})
// Historically, registries have not been lexical, so allow `sort.Strings` to be valid too.
sortedTagListAsciibetical := append([]string{}, tagList...)
sort.Strings(sortedTagListAsciibetical)
Expect(tagList).To(Or(Equal(sortedTagListLexical), Equal(sortedTagListAsciibetical)))
})

g.Specify("GET number of tags should be limitable by `n` query parameter", func() {
Expand Down
6 changes: 3 additions & 3 deletions spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ To fetch the list of tags, perform a `GET` request to a path in the following fo
`<name>` is the namespace of the repository.
Assuming a repository is found, this request MUST return a `200 OK` response code.
The list of tags MAY be empty if there are no tags on the repository.
If the list is not empty, the tags MUST be in lexical order (i.e. case-insensitive alphanumeric order).
If the list is not empty, the tags MUST be in lexical (i.e. case-insensitive alphanumeric order) or "ASCIIbetical" ([Go's `sort.Strings`](https://pkg.go.dev/sort#Strings)) order.

Upon success, the response MUST be a json body in the following format:
```json
Expand All @@ -546,7 +546,7 @@ A `Link` header MAY be included in the response when additional tags are availab
If included, the `Link` header MUST be set according to [RFC5988](https://www.rfc-editor.org/rfc/rfc5988.html) with the Relation Type `rel="next"`.
When `n` is zero, this endpoint MUST return an empty list, and MUST NOT include a `Link` header.
Without the `last` query parameter (described next), the list returned will start at the beginning of the list and include `<int>` results.
As above, the tags MUST be in lexical order.
As above, the tags MUST be in lexical or "ASCIIbetical" order.

The `last` query parameter provides further means for limiting the number of tags.
It is usually used in combination with the `n` parameter: `/v2/<name>/tags/list?n=<int>&last=<tagname>` <sup>[end-8b](#endpoints)</sup>
Expand All @@ -555,7 +555,7 @@ It is usually used in combination with the `n` parameter: `/v2/<name>/tags/list?
`<tagname>` MUST NOT be a numerical index, but rather it MUST be a proper tag.
A request of this sort will return up to `<int>` tags, beginning non-inclusively with `<tagname>`.
That is to say, `<tagname>` will not be included in the results, but up to `<int>` tags *after* `<tagname>` will be returned.
The tags MUST be in lexical order.
The tags MUST be in lexical or "ASCIIbetical" order.

When using the `last` query parameter, the `n` parameter is OPTIONAL.

Expand Down
Loading