Gophercloud does not support versioned discovery #3349
Description
Per the keystone docs:
The OpenStack keystone service catalog allows API clients to dynamically discover and navigate to cloud services.
The catalog describes a list of services along with endpoints for same. For example:
GET /v3/auth/catalog{
"catalog": [
{
"endpoints": [
{
"id": "390de2ac62c5410fb53a7bf5e6bec533",
"interface": "public",
"region_id": "RegionOne",
"url": "http://identity.example.com",
"region": "RegionOne"
}
],
"id": "b2651a44a0bb44d5b5733052a1fc66f7",
"type": "identity",
"name": "keystone"
},
],
"links": {
"self": "http://identity.example.com/v3/auth/catalog"
}
}Notably absent from the catalog though is any mention of version information. It is possible to have multiple endpoints for the same service type using different names. For example, Cinder previously had a v2 API. It is possible to expose both of these using the same service type:
{
"catalog": [
{
"endpoints": [
{
"id": "cdec1b8a3134480c9e326edd8c1d4b13",
"interface": "public",
"region_id": "RegionOne",
"url": "https://volume.example.com/v2",
"region": "RegionOne"
}
],
"id": "8e5833137bc04a65a3f5dd500fe6bcc8",
"type": "block-storage",
"name": "cinder_v2"
},
{
"endpoints": [
{
"id": "5e444bf893b247a69bf1d6fcf3334a23",
"interface": "public",
"region_id": "RegionOne",
"url": "https://volume.example.com/v3",
"region": "RegionOne"
}
],
"id": "8e5833137bc04a65a3f5dd500fe6bcc8",
"type": "block-storage",
"name": "cinder_v3"
},
{
"endpoints": [
{
"id": "390de2ac62c5410fb53a7bf5e6bec533",
"interface": "public",
"region_id": "RegionOne",
"url": "https://identity.example.com",
"region": "RegionOne"
}
],
"id": "b2651a44a0bb44d5b5733052a1fc66f7",
"type": "identity",
"name": "keystone"
},
],
"links": {
"self": "https://identity.example.com/v3/auth/catalog"
}
}The expected behavior here is to fetch the version discovery document from the URL indicated for each endpoint (the root API) (for all services except Swift, unfortunately). This will return a document like so:
GET /v3/{
"versions": [
{
"id": "v3.0",
"status": "CURRENT",
"version": "3.71",
"min_version": "3.0",
"updated": "2023-08-31T00:00:00Z",
"links": [
{
"rel": "describedby",
"type": "text/html",
"href": "https://docs.openstack.org/"
},
{
"rel": "self",
"href": "http://volume.example.com/v3/"
}
],
"media-types": [
{
"base": "application/json",
"type": "application/vnd.openstack.volume+json;version=3"
}
]
}
]
}Gophercloud is not currently doing this, forcing deployments to use versioned service type aliases (i.e. volumev2 or volumev3) rather than the official service type. We should correct this.e
Activity
gtema commentedon Apr 18, 2025
I am not 100% sure it is right. Historically there were always volumev2 and volumev3 as different service types but with different names. The service type volume (or also block-storage) was then pointing to the default (preferred) api. From the DB pov it is not possible to have multiple services with the same service type.
There are however many tricks in the version discovery that I was reverse-engineering from docs and reference impl recently for the Rust. I will summarize them here later today/tomorrow.
gtema commentedon Apr 18, 2025
@stephenfin
Feel free to deduct the process from https://github.com/gtema/openstack/blob/main/openstack_sdk/src/openstack_async.rs#L449 which is a slightly optimized version of the reference implementation (as well as everything is in the single place compared to multiple places between os-service-type, keystoneauth, openstacksdk, os-client-config). You will hopefully see enough comments and tests with explanations.
You can't have multiple occurrences of the service with the same type, but you can use versioned type aliases (volumev2, volumev3). The discoverability rule says that you can take any type and suffix it with the vX. Whenever this particular version is explicitly requested by the client it should be preferred. On every entry of the catalog you MUST still perform version discovery. What you get in the discovery document is than taken as a base and is being "fixed" with the url from catalog (i.e. version discovery may return localhost:1234/abc where the localhost:1234 must be replaced with the domain:port which was used to obtain this document. You must also verify whether version discovery doc contains at all a reasonable url. Further there are endpoint_overrides in the config, so the order is: discovered from catalog, catalog, override. And as mentioned above - depending on the needed client (version) you may have override or discovered version or not, so the process is not very straight forward.