Glance-v2-community-image-sharing-use-cases

This page explores how Images API calls will look/act based on the proposal in the glance spec: https://review.openstack.org/#/c/124050 Note: that spec has been abandoned, this page is left here for historical purposes (and so we don't forget the problems with the proposal in that spec).

(1) Create a community image
what: An image producer wants to make an image available to anyone who wants to boot from it.

how: POST /v2/images/{IMAGE_UUID}/members with request body { "member": "community" }

discussion: this is just like the current sharing call, except it takes the string 'community' instead of a tenant_id.

todo: I believe the tenant_id is defined as a String, so 'community' is a legal tenant_id. How do we ensure that the string used to designate the "community" pseudo-tenant isn't being used as an actual tenant_id?

(2) Restrict access to a community image
what: An image producer wants to make an image available to everyone in the cloud except for a few specific tenants.

how: can't do it with community images

discussion: with current image sharing, the image contains a membership list of users allowed to use the image; there's no provision for a list of users *prohibited* from using the image. So you can't do this with current image sharing, either. The sharing model we've adopted is that you can share with (1) particular users or (2) all users. So not being able to satisfy this use case seems OK.

(3) Create a non-discoverable community image (The "public-image-downgrade" use case)
what: An image producer wants to make an image available to everyone in the cloud but doesn't want it to be discoverable (i.e., a consumer must know the UUID in order to see/use the image). (This is the public-image-downgrade case, that is, the cloud provider doesn't want to support the image any more and doesn't want new instances booted from it, but also doesn't want to delete the image as some customers may still require it.)

how: can't do it under this scheme

discussion: the whole point of community images is that they're discoverable by the community of users! To address this use case, glance currently has a "feature" whereby if the owner of an image is null, any user who independently knows the UUID can do an image-show operation on it (and can boot a server from it). Only a glance administrator can set an image owner to null; some providers have been using this as a workaround in the absence of community images. Its drawback is that (1) only an administrator can do it, and (2) such an image is not discoverable. But this meets the public-image-downgrade case perfectly! So maybe we should turn this "feature" into an actual feature?

(4) Withdraw a community image
what: An image producer wants to withdraw a community image.

how: DELETE /v2/images/{IMAGE_UUID}/members/community

todo: do we disallow DELETE /v2/images/{IMAGE_UUID}/members/{consumer_tenant_id} for a community image? (If we don't the producer would be able to un-bookmark images for particular tenants, which seems counterintuitive)

todo: what happens to the member-list? The spec says same behavior as previously defined, but for current image sharing, you have to remove tenants one by one until the member-list is empty, at that point the image is no longer shared.

(5) Community image discovery
what: An image consumer wants to discover what community images are available.

how: GET /v2/images?visibility=shared&is_community=true

discussion: (rosmaita) What will happen if a user makes this call: GET /v2/images?visibility=shared ? I guess they'll get all images shared with them 1-1 AND all images shared 1-all ? I find this a bit problematic because the 1-1 shared images are supposedly images where you've entered a relationship with the image producer (i.e., given the producer your tenant_id), so presumably you have some interest in seeing those, whereas with the 1-many (what we're calling "community") images, the relationship is asymmetrical--the producer just tosses the image out there. Thus, I think you'd want to also have a call GET /v2/images?visibility=shared&is_community=false. But since the 'visibility' query parameter doesn't actually refer to an image property with that value, I think we could go ahead and design the calls like this:
 * GET /v2/images?visibility=shared works the way it does now, it gives you the 1-1 shared images that you've accepted
 * GET /v2/images?visibility=community would give you the 1-all shared images. (I think this should be *all* community images.  With community images, the bookmarking is a convenience; with 1-1 shared images, the "acceptance" is a key feature.  But will the asymmetry be confusing?)

(6) Discovery of community images from a particular image producer
what: An image consumer wants to discover what community images are available from a particular image producer.

how: GET /v2/images?visibility=shared&is_community=true&owner={producer_tenant_id}

(7) Bookmarking a community image
what: "Bookmarking": An image consumer really likes a community image and wants it to appear in his image-list.

how: PUT /v2/images/{IMAGE_UUID}/members/{consumer_tenant_id} with request body: { "status": "accepted" }

(8) Un-bookmarking (forgetting) a community image
what: An image consumer has a community image appearing in her image list but no longer wants it to show up there.

how: PUT /v2/images/{IMAGE_UUID}/members/{consumer_tenant_id} with request body: { "status": "rejected" }

todo: what should this call *really* do? remove the consumer_tenant_id from the image's member-list?
 * pro: (1) if we actually update the status on the image, then we should also (for consistency) allow a consumer doing a PUT with { "status": "pending" }, which doesn't really make sense. (2) this method allows the consumer to remove all trace that she was ever interested in the image (as far as the image producer is concerned, that is; the evidence is still in the glance database)
 * con: if the member is removed, then you won't be able to filter the community image list with "rejected" or "pending". Why whould a consumer want to do that?  Well, if you did a   GET /v2/images?visibility=community&member_status=rejected, you'd get a list of community images that you had looked at but decided that you didn't want.  But that seems kind of dumb.

todo: what about PUT /v2/images/{IMAGE_UUID}/members/{consumer_tenant_id} with request body: { "status": "pending" } ? Should that be an error, or just delete the consumer_tenant_id from the member list (if that's the behavior adopted for the "rejected" call)?

Problematic API Calls
The above brings up some questions about what expected behavior should be for some API calls.

PUT rejected
call: PUT /v2/images/{IMAGE_UUID}/members/{consumer_tenant_id} with request body: { "status": "rejected" }

behavior: Need to decide whether this acts as a DELETE of the consumer_tenant_id, or whether we'll track this member_status.

PUT pending
call: PUT /v2/images/{IMAGE_UUID}/members/{consumer_tenant_id} with request body: { "status": "pending" }

behavior: Need to decide whether this acts as a DELETE of the consumer_tenant_id, or whether we'll track this member_status.

GET accepted
call: GET /v2/images?visibility=community&member_status=accepted behavior: Returns only those community images that the caller has accepted.

GET rejected
call: GET /v2/images?visibility=community&member_status=rejected

behavior: Returns all community images *except* community images that the caller has accepted? Or is the filter ignored and this is the same result as the GET /v2/images?visibility=community call?

GET pending
call: GET /v2/images?visibility=community&member_status=pending

behavior: Same questions as for GET "rejected", above.