Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove V2 Prefix from endpoints #28

Closed
bsatlas opened this issue Nov 24, 2018 · 15 comments
Closed

Remove V2 Prefix from endpoints #28

bsatlas opened this issue Nov 24, 2018 · 15 comments

Comments

@bsatlas
Copy link
Contributor

bsatlas commented Nov 24, 2018

I understand that backwards compatibility is important but since the distribution spec's logic is based on the OCI's well defined and versioned media types, I think it should be removed.

Docker's current implementation even has a Docker-Distribution-Api-Version: registry/2.0 header which serves the same purpose and provides more meaningful information in a cleaner way.

Including the path prefix and the header into the spec is kind of redundant. I'd like to see us pick one or the other before the first RC.

Thoughts?

@vbatts
Copy link
Member

vbatts commented Nov 26, 2018

hmmm, that could be a rippling change. @josephschorr or @dmcgowan would just a header for docker be enough?

@josephschorr
Copy link

A header won't be sufficient, unfortunately. We need some form of path prefix (whatever it may be) to ensure grouping and namespacing of the API endpoints; otherwise, they could easily conflict with other endpoints, including our own internal ones.

Now, if we made the prefix configurable by responding with a header or a <meta> tag, we'd be fine with that.

@bsatlas
Copy link
Contributor Author

bsatlas commented Nov 26, 2018

I'm okay with using a non-versioned prefix path! How about img or i?

Examples:
/img/library/ubuntu/manifests{id}
/i/library/ubuntu/manifests/{id}

@josephschorr
Copy link

Speaking only for myself personally, I'd still put a version code in there, ala i1, just in case there is ever a scenario where it makes sense to move to a completely revamped version of the protocol.

For example, Quay has "extensions" to the registry protocol hosted at c1 and, while we don't anticipate having a c2, I still feel it was the correct decision in case we do need to add conflicting paths.

@josephschorr
Copy link

(Oh, and if we did so, I'd vote for i1 or oci1)

@jonjohnsonjr
Copy link
Contributor

This is a rather tough change to make without boiling at least two oceans. I think it's important that we define things such that existing registries are compliant by assuming /v2/.

@bsatlas
Copy link
Contributor Author

bsatlas commented Nov 26, 2018

How about we create our own prefix, keep the /v2/ prefix as an alias for the v0.1.0 release and deprecate it in v0.2.0?

@josephschorr the Quay registry only supports the application/json media type that can't be versioned. We have well-defined versioned media types defined in the image-spec that allows us to use cleaner interfaces. In regards to revamping the protocol one day, the most realistic scenario I can see playing out would be for us to move away from REST to something like GRPC, which would then make path versioning a non-issue. Would you say this is somewhat accurate?

@josephschorr
Copy link

@atlaskerr Quay only supports the application/json media type at this moment, but that's a (big, but unrelated) side issue IMO

My concern is as follows: Let's say a future change is to not change the data format, but to change the URLs themselves used (in a situation in which we do keep using REST for its, say, simplicity). In that scenario, being versioned provides a nice escape hatch without having to mix endpoints and worry about them conflicting. I do agree it is unlikely, but what is the major downside of requiring a version number as part of a prefix, when we have already agreed a prefix is useful? User confusion?

@jzelinskie
Copy link
Member

Why not have no say on whether a URL prefix should exist, and rather use a /.well-known endpoint to discover any potential prefix. This adds an extra round-trip, but improves flexibility and allows registries to even migrate the path without breaking clients in the future.

@bsatlas
Copy link
Contributor Author

bsatlas commented Nov 27, 2018

@josephschorr Ah I see where you're coming from. The main reason I'd like to see a path
implementation without version prefix is so that implementers can develop
extensions on top of the protocol at the same prefix without having to be in
lockstep with the distribution specification.

Scenario 1: Versioned Prefix

For example, an organization wants to add image scanning functionality to their
registry. Since scanning is not in the scope of the spec, the organization has
to roll their own. They decide to add an operation for scanning at the registry,
image, and tag level:

Paths at T + 1 (8 paths maintained | 3 added)

Endpoints Media Type
/v2/ application/json or application/vnd.oci.*
/v2/{namespace}/manifests/{reference} application/vnd.oci.*
/v2/{namespace}/blobs/{digest} application/vnd.oci.*
/v2/{namespace}/blobs/uploads application/vnd.oci.*
/v2/{namespace}/blobs/uploads/{id} application/vnd.oci.*
/c1/scan application/json or application/vnd.proprietary.*
/c1/{namespace}/scan application/json or application/vnd.proprietary.*
/c1/{namespace}/manifests/{reference}/scan application/json or application/vnd.proprietary.*

Now, lets say that V3 of the spec comes out and changes the terms manifests
and blobs to package and layer respectively. A major side effect to this
is that now, the organization is implementing scanning for V3-based image paths
using the V2 format. The organization decides to add new alias paths for user
friendliness:

Paths at T + 2 (16 paths maintained | 8 added)

Endpoints Media Type
/v2/ application/json or application/vnd.oci.*
/v2/{namespace}/manifests/{reference} application/vnd.oci.*
/v2/{namespace}/blobs/{digest} application/vnd.oci.*
/v2/{namespace}/blobs/uploads application/vnd.oci.*
/v2/{namespace}/blobs/uploads/{id} application/vnd.oci.*
/v3/ application/json or application/vnd.oci.*
/v3/{namespace}/packages/{reference} application/vnd.oci.*
/v3/{namespace}/layers/{digest} application/vnd.oci.*
/v3/{namespace}/layers/uploads application/vnd.oci.*
/v3/{namespace}/layers/uploads/{id} application/vnd.oci.*
/c1/scan application/json or application/vnd.proprietary.*
/c1/{namespace}/scan application/json or application/vnd.proprietary.*
/c1/{namespace}/manifests/{reference}/scan application/json or application/vnd.proprietary.*
/c2/scan application/json or application/vnd.proprietary.*
/c2/{namespace}/scan application/json or application/vnd.proprietary.*
/c2/{namespace}/packages/{reference}/scan application/json or application/vnd.proprietary.*

After some time, the spec releases V3.1 which features a scanning endpoint
that standardizes how clients can interact with a registry to scan images. The
organization adds the scanning endpoint to the /v3/ prefix and maintains the
legacy scanning paths to maintain backwards compatibility:

Paths at T + 3 (19 paths maintained | 3 added)

Endpoints Media Type
/v2/ application/json or application/vnd.oci.*
/v2/{namespace}/manifests/{reference} application/vnd.oci.*
/v2/{namespace}/blobs/{digest} application/vnd.oci.*
/v2/{namespace}/blobs/uploads application/vnd.oci.*
/v2/{namespace}/blobs/uploads/{id} application/vnd.oci.*
/v3/ application/json or application/vnd.oci.*
/v3/{namespace}/packages/{reference} application/vnd.oci.*
/v3/{namespace}/layers/{digest} application/vnd.oci.*
/v3/{namespace}/layers/uploads application/vnd.oci.*
/v3/{namespace}/layers/uploads/{id} application/vnd.oci.*
/v3/scan application/vnd.oci.*
/v3/{namespace}/scan application/vnd.oci.*
/v3/{namespace}/packages/{reference}/scan application/vnd.oci.*
/c1/scan application/json or application/vnd.proprietary.*
/c1/{namespace}/scan application/json or application/vnd.proprietary.*
/c1/{namespace}/manifests/{reference}/scan application/json or application/vnd.proprietary.*
/c2/scan application/json or application/vnd.proprietary.*
/c2/{namespace}/scan application/json or application/vnd.proprietary.*
/c2/{namespace}/packages/{reference}/scan application/json or application/vnd.proprietary.*

Finally, the organization feels like the spec for scanning isn't robust enough
so they decide to make another custom scanning operation:

Paths at T + 4 (22 paths maintained | 3 added)

Endpoints Media Type
/v2/ application/json or application/vnd.oci.*
/v2/{namespace}/manifests/{reference} application/vnd.oci.*
/v2/{namespace}/blobs/{digest} application/vnd.oci.*
/v2/{namespace}/blobs/uploads application/vnd.oci.*
/v2/{namespace}/blobs/uploads/{id} application/vnd.oci.*
/v3/ application/json or application/vnd.oci.*
/v3/{namespace}/packages/{reference} application/vnd.oci.*
/v3/{namespace}/layers/{digest} application/vnd.oci.*
/v3/{namespace}/layers/uploads application/vnd.oci.*
/v3/{namespace}/layers/uploads/{id} application/vnd.oci.*
/v3/scan application/vnd.oci.*
/v3/{namespace}/scan/ application/vnd.oci.*
/v3/{namespace}/packages/{reference}/scan/ application/vnd.oci.*
/c1/scan application/json or application/vnd.proprietary.*
/c1/{namespace}/scan application/json or application/vnd.proprietary.*
/c1/{namespace}/manifests/{reference}/scan application/json or application/vnd.proprietary.*
/c2/scan application/json or application/vnd.proprietary.*
/c2/{namespace}/scan application/json or application/vnd.proprietary.*
/c2/{namespace}/packages/{reference}/scan application/json or application/vnd.proprietary.*
/c3/scan application/json or application/vnd.proprietary.*
/c3/{namespace}/scan application/json or application/vnd.proprietary.*
/c3/{namespace}/packages/{reference}/scan application/json or application/vnd.proprietary.*

After just 1 spec upgrade and 3 proprietary feature additions/updates, the paths
that the organization has to maintain nearly tripled from 8 to 22. Over time,
the registry becomes less and less RESTful. Operations for the same resources
are spread out across five unique prefixes
.

Scenario 2: Non-Versioned Prefix

If instead, the distribution spec standardized on a generic non-versioned
prefix for registry operations driven by the media types, the same scenario would go as follows:

Paths at T + 1 (8 paths maintained | 3 added)

Add scanning:

Endpoints Media Type
/img/ application/json or application/vnd.oci.*
/img/scan application/json or application/vnd.proprietary.*
/img/{namespace}/scan application/json or application/vnd.proprietary.*
/img/{namespace}/manifests/{reference}/scan application/json or application/vnd.proprietary.*
/img/{namespace}/manifests/{reference} application/vnd.oci.*
/img/{namespace}/blobs/{digest} application/vnd.oci.*
/img/{namespace}/blobs/uploads application/vnd.oci.*
/img/{namespace}/blobs/uploads/{id} application/vnd.oci.*

Paths at T + 2 (13 paths maintained | 5 added)

Upgrade to spec V3:

Endpoints Media Type
/img/ application/json or application/vnd.oci.*
/img/scan application/json or application/vnd.proprietary.*
/img/{namespace}/scan application/json or application/vnd.proprietary.*
/img/{namespace}/manifests/{reference}/scan application/json or application/vnd.proprietary.*
/img/{namespace}/manifests/{reference} application/vnd.oci.*
/img/{namespace}/blobs/{digest} application/vnd.oci.*
/img/{namespace}/blobs/uploads application/vnd.oci.*
/img/{namespace}/blobs/uploads/{id} application/vnd.oci.*
/img/{namespace}/packages/{reference} application/vnd.oci...v1+json
/img/{namespace}/packages/{reference}/scan application/json
/img/{namespace}/layers/{digest} application/vnd.oci...v1+json
/img/{namespace}/layers/uploads application/vnd.oci...v1+json
/img/{namespace}/layers/uploads/{id} application/vnd.oci...v1+json

Paths at T + 3 (13 paths maintained | 0 added)

Upgrade to spec V3.1:

Endpoints Media Type
/img/ application/json or application/vnd.oci.*
/img/scan application/json or application/vnd.proprietary.* or application/vnd.oci.*
/img/{namespace}/scan application/json or application/vnd.proprietary.* or application/vnd.oci.*
/img/{namespace}/manifests/{reference}/scan application/json or application/vnd.proprietary.* or application/vnd.oci.*
/img/{namespace}/manifests/{reference} application/vnd.oci.*
/img/{namespace}/blobs/{digest} application/vnd.oci.*
/img/{namespace}/blobs/uploads application/vnd.oci.*
/img/{namespace}/blobs/uploads/{id} application/vnd.oci.*
/img/{namespace}/packages/{reference} application/vnd.oci...v1+json
/img/{namespace}/packages/{reference}/scan application/json
/img/{namespace}/layers/{digest} application/vnd.oci...v1+json
/img/{namespace}/layers/uploads application/vnd.oci...v1+json
/img/{namespace}/layers/uploads/{id} application/vnd.oci...v1+json

Paths at T + 4 (13 paths maintained | 0 added)

Finally, add proprietary scanning:

Endpoints Media Type
/img/ application/json or application/vnd.oci.*
/img/scan application/json or application/vnd.proprietary.* application/vnd.oci.*
/img/{namespace}/scan application/json or application/vnd.proprietary.* application/vnd.oci.*
/img/{namespace}/manifests/{reference}/scan application/json or application/vnd.proprietary.* application/vnd.oci.*
/img/{namespace}/manifests/{reference} application/vnd.oci.*
/img/{namespace}/blobs/{digest} application/vnd.oci.*
/img/{namespace}/blobs/uploads application/vnd.oci.*
/img/{namespace}/blobs/uploads/{id} application/vnd.oci.*
/img/{namespace}/packages/{reference} application/vnd.oci...v1+json
/img/{namespace}/packages/{reference}/scan application/json
/img/{namespace}/layers/{digest} application/vnd.oci...v1+json
/img/{namespace}/layers/uploads application/vnd.oci...v1+json
/img/{namespace}/layers/uploads/{id} application/vnd.oci...v1+json

After the same transformations, the registry at T + 4 has nearly half as many
endpoints as scenario 1 and no changes were made to endpoints after T + 2. All image operations can be accessed from the same prefix without collisions and breaking changes between multiple spec versions can live in harmony.

By keeping the versioning out of the prefix, registries can add additional
functionality not included in the spec without exploding the amount of paths
that need to be maintained.

I think it looks nicer too!

@josephschorr
Copy link

josephschorr commented Nov 27, 2018

@atlaskerr Actually, I'd argue that you made my point for me with your example! :D

Let's say someone did follow your example and add a custom endpoint for security scanning under /scan; while I wouldn't advise doing so, it certainly is possible that someone would do so, because that path is unregistered in the current specification.

Then, later, we decide to add our own security scanning endpoint to the spec... at /scan. Now anyone who has implemented that path (and we won't know the total sum of products that have done so) is left with a big problem, because it is almost certainly true that the parameters accepted and the data returned by their implementation is vastly different from the specification; it is also unlikely they will simply be able to switch the returned value(s) based off of an incoming header or version code, unless they had the foresight to anticipate this being an issue.

I also don't think the perceived number of version prefixes is as large as your example provides: as mentioned, we on Quay have a /c1 endpoint for our own internal additions to the Docker APIs (we use our own prefix to address the exact concern I just expanded upon), and we've had no need over the past 4 years to add another one. Further, Docker has only revised their prefix once (v1 -> v2), despite technically having three versions of images (v1, v2 schema 1, v2 schema 2), because only the data format changed, and they made sure that all v2 schema 2 implementations could fallback to v2 schema 1 at any time.

I would treat the version on the path prefix like a semantic version: only change it if there is a backwards incompatible change to the URL paths defined in the specification. This would result in minimal changes (if any) to the path prefix, while still giving us the option to do so in the future if absolutely necessary. I think Docker's example here really applies: support for schema version 2 was a forward-compatible change over the existing URL paths, so no new prefix was necessary. In your example, the change to support packages instead of manifests could actually remain under the i1 (or whatever we choose) prefix, so long as it was an addition, and implementors were still required to implement manifests as a fallback for existing clients.

TL;DR: Treat the version in the path prefix as a semantic version, only change when absolutely necessary. Also, to be absolutely careful, we should explicitly state to implementors to not add their custom methods under the same prefix :)

@bsatlas
Copy link
Contributor Author

bsatlas commented Nov 27, 2018

@josephschorr it looks like we are at an impasse. I am in the mindset that implementers should be adding custom operations under the /img prefix, that is a fundamental part of REST. I don't agree to the spec forcing implementers and clients into accessing logic for images at five different prefixes based solely on if the operation is a standard or not. In my example, the oci and third-party operations for /img/scan/ do not collide because the endpoint returns different media types.

@josephschorr
Copy link

@atlaskerr While I disagree on the versioning aspect, I'd personally be fine with us dropping it if others disagree with my assessment on why its likely we'll need breaking changes.

On the custom extensions under the same prefix, however, I'm highly concerned that we disagree: if we allow anyone to add any paths that they wish to the OCI-standard prefix, then we are in for a massive world of hurt if we ever intend to extension the API, as we have zero guarantees that semantics will be compatible at all.

I don't agree that its a "fundamental part of REST" for others to be able to extend the protocol in such a manner; they certainly can do so, but they should be made aware of the risk that this can (and almost certainly will) conflict with future additions. Further, such extension seems... pointless? There is no downside, in my opinion, to custom extensions being placed into a different URL prefix; its all mostly called in an automated fashion anyway.

@bsatlas
Copy link
Contributor Author

bsatlas commented Nov 27, 2018

@josephschorr What's your email?

@vbatts
Copy link
Member

vbatts commented Nov 27, 2018 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants