Skip to content

Commit

Permalink
Add cross-repository mounting section to spec/test
Browse files Browse the repository at this point in the history
* New test in 02_push_test.go
* Section added to spec.md detailing cross-repository mounting
* Section added to conformance/README.md on new configuration options
for cross-repository mounting

Signed-off-by: Peter Engelbert <pmengelbert@gmail.com>
  • Loading branch information
pmengelbert committed Oct 27, 2020
1 parent db37dc2 commit b910554
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 20 deletions.
34 changes: 34 additions & 0 deletions conformance/02_push_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,40 @@ var test02Push = func() {
})
})

g.Context("Cross-Repository Blob Mount", func() {
g.Specify("POST request to mount another repository's blob should return 201", func() {
SkipIfDisabled(push)
var err error

req := client.NewRequest(reggie.POST, "/v2/<name>/blobs/uploads/",
reggie.WithName(crossmountNamespace)).
SetQueryParam("mount", testBlobADigest).
SetQueryParam("from", client.Config.DefaultName)
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(SatisfyAny(
Equal(http.StatusCreated),
Equal(http.StatusMethodNotAllowed),
))

lastResponse = nil
if resp.StatusCode() == http.StatusCreated {
lastResponse = resp
Expect(lastResponse.GetRelativeLocation()).ToNot(BeEmpty())
}
})

g.Specify("GET request to test digest within cross-mount namespace should return 200", func() {
SkipIfDisabled(push)
RunOnlyIfNot(lastResponse == nil)

req := client.NewRequest(reggie.GET, lastResponse.GetRelativeLocation())
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
})
})

g.Context("Manifest Upload", func() {
g.Specify("GET nonexistent manifest should return 404", func() {
SkipIfDisabled(push)
Expand Down
10 changes: 10 additions & 0 deletions conformance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Next, set environment variables with your registry details:
# Registry details
export OCI_ROOT_URL="https://r.myreg.io"
export OCI_NAMESPACE="myorg/myrepo"
export OCI_CROSSMOUNT_NAMESPACE="myorg/other"
export OCI_USERNAME="myuser"
export OCI_PASSWORD="mypass"
Expand Down Expand Up @@ -105,6 +106,15 @@ environment:
OCI_SKIP_EMPTY_LAYER_PUSH_TEST=1
```

Some registries allow cross-repository blob mounting. As such, the test suite will need access to a second namespace.
This namespace may need to be configured on the server-side in advance, and it is specified by setting the following in
the environment:

```
# The destination repository for cross-repository mounting:
OCI_CROSSMOUNT_NAMESPACE="myorg/other"
```

##### Content Discovery

The Content Discovery tests validate that the contents of a registry can be discovered.
Expand Down
15 changes: 9 additions & 6 deletions conformance/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ type (
)

const (
pull = 1 << iota
push
contentDiscovery
contentManagement

BLOB_UNKNOWN = iota
BLOB_UPLOAD_INVALID
BLOB_UPLOAD_UNKNOWN
Expand Down Expand Up @@ -57,6 +62,7 @@ const (
envVarHideSkippedWorkflows = "OCI_HIDE_SKIPPED_WORKFLOWS"
envVarAuthScope = "OCI_AUTH_SCOPE"
envVarDeleteManifestBeforeBlobs = "OCI_DELETE_MANIFEST_BEFORE_BLOBS"
envVarCrossmountNamespace = "OCI_CROSSMOUNT_NAMESPACE"

emptyLayerTestTag = "emptylayer"
testTagName = "tagtest0"
Expand All @@ -66,11 +72,6 @@ const (
titleContentDiscovery = "Content Discovery"
titleContentManagement = "Content Management"

pull = 1 << iota
push
contentDiscovery
contentManagement

// layerBase64String is a base64 encoding of a simple tarball, obtained like this:
// $ echo 'you bothered to find out what was in here. Congratulations!' > test.txt
// $ tar czvf test.tar.gz test.txt
Expand Down Expand Up @@ -103,6 +104,7 @@ var (
client *reggie.Client
configBlobContent []byte
configBlobContentLength string
crossmountNamespace string
dummyDigest string
errorCodes []string
invalidManifestContent []byte
Expand All @@ -111,8 +113,8 @@ var (
layerBlobContentLength string
manifestContent []byte
manifestDigest string
emptyLayerManifestContent []byte
emptyLayerManifestDigest string
emptyLayerManifestContent []byte
nonexistentManifest string
reportJUnitFilename string
reportHTMLFilename string
Expand All @@ -136,6 +138,7 @@ func init() {
username := os.Getenv(envVarUsername)
password := os.Getenv(envVarPassword)
authScope := os.Getenv(envVarAuthScope)
crossmountNamespace = os.Getenv(envVarCrossmountNamespace)

debug, _ := strconv.ParseBool(os.Getenv(envVarDebug))

Expand Down
46 changes: 32 additions & 14 deletions spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,23 @@ Location: <blob-location>
Here, `<blob-location>` is a pullable blob URL.


##### Mounting a blob from another repository
If a necessary blob exists already in another repository, it can be mounted into a different repository via a `POST`
request in the following format:

`/v2/<name>/blobs/uploads/?mount=<digest>&from=<other_namespace>` <sup>[end-11](#endpoints)</sup>.

In this case, `<name>` is the namespace to which the blob will be mounted. `<digest>` is the digest of the blob to mount,
and `<other_namespace>` is the namespace from which the blob should be mounted.

The response to a successful mount MUST be `201 Created`, and MUST contain the following header:
```
Location: <blob-location>
```

The digest contained in the `Location` header MAY be different from that of the blob that was mounted. As such, a client
SHOULD use the digest found in the path from this header and SHOULD NOT use the digest of the blob that was mounted.

##### Pushing Manifests

To push a manifest, perform a `PUT` request to a path in the following format, and with the following headers
Expand Down Expand Up @@ -414,20 +431,21 @@ of this specification.

#### Endpoints

| ID | Method | API endpoint | Accepted Successful Response Codes | Accepted Failure Response Codes |
| ------ | -------- | ------------------------------------------------------ | ---------------------------------- | ------------------------------- |
| end-1 | `GET` | `/v2/` | `200` | `404`/`401` |
| end-2 | `GET` | `/v2/<name>/blobs/<digest>` | `200` | `404` |
| end-3 | `GET` | `/v2/<name>/manifests/<reference>` | `200` | `404` |
| end-4a | `POST` | `/v2/<name>/blobs/uploads/` | `202` | `404` |
| end-4b | `POST` | `/v2/<name>/blobs/uploads/?digest=<digest>` | `201`/`202` | `404`/`400` |
| end-5 | `PATCH` | `/v2/<name>/blobs/uploads/<reference>` | `202` | `404`/`416` |
| end-6 | `PUT` | `/v2/<name>/blobs/uploads/<reference>?digest=<digest>` | `201` | `404`/`400` |
| end-7 | `PUT` | `/v2/<name>/manifests/<reference>` | `201` | `404` |
| end-8a | `GET` | `/v2/<name>/tags/list` | `200` | `404` |
| end-8b | `GET` | `/v2/<name>/tags/list?n=<integer>&last=<integer>` | `200` | `404` |
| end-9 | `DELETE` | `/v2/<name>/manifests/<reference>` | `202` | `404`/`400`/`405` |
| end-10 | `DELETE` | `/v2/<name>/blobs/<digest>` | `202` | `404`/`405` |
| ID | Method | API endpoint | Accepted Successful Response Codes | Accepted Failure Response Codes |
| ------ | -------- | ----------------------------------------------------------------- | ---------------------------------- | ------------------------------- |
| end-1 | `GET` | `/v2/` | `200` | `404`/`401` |
| end-2 | `GET` | `/v2/<name>/blobs/<digest>` | `200` | `404` |
| end-3 | `GET` | `/v2/<name>/manifests/<reference>` | `200` | `404` |
| end-4a | `POST` | `/v2/<name>/blobs/uploads/` | `202` | `404` |
| end-4b | `POST` | `/v2/<name>/blobs/uploads/?digest=<digest>` | `201`/`202` | `404`/`400` |
| end-5 | `PATCH` | `/v2/<name>/blobs/uploads/<reference>` | `202` | `404`/`416` |
| end-6 | `PUT` | `/v2/<name>/blobs/uploads/<reference>?digest=<digest>` | `201` | `404`/`400` |
| end-7 | `PUT` | `/v2/<name>/manifests/<reference>` | `201` | `404` |
| end-8a | `GET` | `/v2/<name>/tags/list` | `200` | `404` |
| end-8b | `GET` | `/v2/<name>/tags/list?n=<integer>&last=<integer>` | `200` | `404` |
| end-9 | `DELETE` | `/v2/<name>/manifests/<reference>` | `202` | `404`/`400`/`405` |
| end-10 | `DELETE` | `/v2/<name>/blobs/<digest>` | `202` | `404`/`405` |
| end-11 | `POST` | `/v2/<name>/blobs/uploads/?mount=<digest>&from=<other_namespace>` | `201` | `405` |

#### Error Codes

Expand Down

0 comments on commit b910554

Please sign in to comment.