Merge "Add document describing consuming version discovery"
This commit is contained in:
commit
8795075d59
|
@ -22,9 +22,18 @@ from the Service Catalog.
|
||||||
.. note:: The use of the word "object" in this document refers to a JSON
|
.. note:: The use of the word "object" in this document refers to a JSON
|
||||||
object, not an Object from any particular programming language.
|
object, not an Object from any particular programming language.
|
||||||
|
|
||||||
|
.. _catalog-user-request:
|
||||||
|
|
||||||
User Request
|
User Request
|
||||||
============
|
============
|
||||||
|
|
||||||
|
.. note:: It is worth noting that 'user' is a maleable concept. For instance,
|
||||||
|
the shade library performs service discovery on behalf of its users
|
||||||
|
so does not expect its users to provide a 'service-type'. In that
|
||||||
|
case, shade is the 'user' of the keystoneauth library which is the
|
||||||
|
discovery implementation. It is definitely not required that all
|
||||||
|
consumers of OpenStack clouds know all of these things.
|
||||||
|
|
||||||
The ultimate goal of this process is for a user to find the information about
|
The ultimate goal of this process is for a user to find the information about
|
||||||
an endpoint for a service given some inputs. The user will start the process
|
an endpoint for a service given some inputs. The user will start the process
|
||||||
knowing some number of these parameters. Each additional input expected from
|
knowing some number of these parameters. Each additional input expected from
|
||||||
|
@ -35,69 +44,192 @@ in helping the user ask the right question.
|
||||||
|
|
||||||
.. note:: Be liberal with what you accept and strict with what you emit.
|
.. note:: Be liberal with what you accept and strict with what you emit.
|
||||||
|
|
||||||
There is one piece of information that is absolutely required that the
|
The following is a list of such pieces of information that can be provided
|
||||||
user know:
|
as user input. When an implementation exposes the ability for a user to
|
||||||
|
express these parameters it is **STRONGLY** recommended that these names
|
||||||
|
be used, as they show up across the OpenStack ecosystem and make discussion
|
||||||
|
easier.
|
||||||
|
|
||||||
service-type
|
It is assumed that the user has an ``{auth-url}`` and authentication
|
||||||
|
information. The authentication process itself is out of the scope of this
|
||||||
|
document.
|
||||||
|
|
||||||
|
Required Inputs
|
||||||
|
---------------
|
||||||
|
|
||||||
|
There is one piece of information that is absolutely required that the
|
||||||
|
user know.
|
||||||
|
|
||||||
|
``service-type``
|
||||||
The official name of the service, such as ``compute``, ``image`` or
|
The official name of the service, such as ``compute``, ``image`` or
|
||||||
``block-storage`` as listed in the `OpenStack Service Types Authority`_.
|
``block-storage`` as listed in the :doc:`OpenStack Service Types Authority
|
||||||
|
<consuming-catalog/authority>`.
|
||||||
Required. It is impossible for a user to consume service discovery without
|
Required. It is impossible for a user to consume service discovery without
|
||||||
knowing what service they want to discover.
|
knowing what service they want to discover.
|
||||||
|
|
||||||
The user may also wish to express an alteration to the general algorithm:
|
Optional Filters
|
||||||
|
----------------
|
||||||
be-strict
|
|
||||||
Forgo lenient backwards compatibility concessions and be more strict in
|
|
||||||
input and output validation.
|
|
||||||
|
|
||||||
There are several optional pieces of information that the user might know,
|
There are several optional pieces of information that the user might know,
|
||||||
or additional constraints the user might wish to express.
|
or additional constraints the user might wish to express to control how the
|
||||||
|
endpoints for a service are selected.
|
||||||
|
|
||||||
region-name
|
``region-name``
|
||||||
The region of the service the user desires to work with. May be optional,
|
The region of the service the user desires to work with. May be optional,
|
||||||
depending on whether the cloud has more than one region. Services
|
depending on whether the cloud has more than one region. Services
|
||||||
all exist within regions, but some clouds only have one region.
|
all exist within regions, but some clouds only have one region.
|
||||||
If ``{be-strict}`` has been given, ``{region-name}`` is required.
|
If ``{be-strict}`` (see below) has been given, ``{region-name}`` is required.
|
||||||
|
|
||||||
.. note:: It is highly recommended that ``{region-name}`` always be required
|
.. note:: It is highly recommended that ``{region-name}`` always be required
|
||||||
to protect against single-region clouds adding a region in the
|
to protect against single-region clouds adding a region in the
|
||||||
future. However, keystoneauth today allows region-name to be omitted
|
future. However, the canonical OpenStack implementation
|
||||||
and there are a large number of clouds in existence with a single
|
*keystoneauth* today allows region name to be omitted and there are
|
||||||
region named ``RegionOne``. For completely new libraries or major
|
a large number of clouds in existence with a single region named
|
||||||
versions where breaking behavior is acceptable, requiring region-name
|
``RegionOne``. For completely new libraries or major versions
|
||||||
by default would be preferred.
|
where breaking behavior is acceptable, requiring region name
|
||||||
|
by default would be preferred, but breaking users just to introduce
|
||||||
|
the restriction is discouraged.
|
||||||
|
|
||||||
interface
|
``interface``
|
||||||
Which API interface, such as ``public``, ``internal``, or ``admin``
|
Which API interface, such as ``public``, ``internal`` or ``admin``, that
|
||||||
the user wants to use. A user can also request a list of interfaces they find
|
the user wants to use. A user should be able to request a list of interfaces
|
||||||
acceptable in the order of their preference, such as
|
they find acceptable in the order of their preference, such as
|
||||||
``['internal', 'public']`` (Optional, defaults to ``public``.)
|
``['internal', 'public']`` (Optional, defaults to ``public``)
|
||||||
|
|
||||||
service-name
|
``endpoint-version`` OR ``min-endpoint-version``, ``max-endpoint-version``
|
||||||
|
The **major** version of the service the user desires to work with. Optional.
|
||||||
|
|
||||||
|
An endpoint version is inherently a range with a minimum and a maximum value.
|
||||||
|
Whether it is presented to the user as a single parameter or a pair of
|
||||||
|
parameters is an implementation detail.
|
||||||
|
|
||||||
|
Each endpoint version is a string with one (``3``) or two (``3.1``) numbers,
|
||||||
|
separated by a dot.
|
||||||
|
|
||||||
|
.. warning:: Care has to be taken to not confuse major versions consisting
|
||||||
|
of two numbers with microversions. Microversions usually exist
|
||||||
|
within a certain major version, and also have a form of ``X.Y``.
|
||||||
|
No services currently use both major versions and microversions
|
||||||
|
in the form of ``X.Y``.
|
||||||
|
|
||||||
|
.. TODO(dtantsur): so, what if a service has both major versions in the form
|
||||||
|
of ``X.Y`` and microversions?
|
||||||
|
|
||||||
|
Version strings are not decimals, the are a tuple of 2 numbers combined with
|
||||||
|
a dot. Therefore, ``3.10`` is higher than ``3.9``.
|
||||||
|
|
||||||
|
A user can omit the endpoint-version indicating that they want to use
|
||||||
|
whatever endpoint is in the ``{service-catalog}``.
|
||||||
|
|
||||||
|
A user can desire to work with the latest available version, in which
|
||||||
|
case the ``{endpoint-version}`` should be ``latest``. If s
|
||||||
|
``{min-endpoint-version}`` is ``latest``, ``{max-endpoint-version}`` must be
|
||||||
|
omitted or also ``latest``.
|
||||||
|
|
||||||
|
A version can be specified with a minor value of ``latest`` to indicate
|
||||||
|
the highest minor version of a given major version. For instance,
|
||||||
|
``3.latest`` would match the highest of ``3.3`` and ``3.4`` but not ``4.0``.
|
||||||
|
|
||||||
|
If the parameter is presented as a single string, a single value should be
|
||||||
|
interpreted as if ``{min-endpoint-version}`` is the value given and
|
||||||
|
``{max-endpoint-version}`` is ``MAJOR.latest``. For instance, if ``3.4`` is
|
||||||
|
given as a single value, ``{min-endpoint-version}`` is ``3.4`` and
|
||||||
|
``{max-endpoint-version}`` is ``3.latest``.
|
||||||
|
|
||||||
|
It may seem strange from an individual user perspective to want a range or
|
||||||
|
``latest`` - but from a library and framework perspective, things like shade
|
||||||
|
or terraform may have internal logic that can handle more than one version of
|
||||||
|
a service and want to use the best version available.
|
||||||
|
|
||||||
|
.. note:: Guidance around 'latest' is different from that found in
|
||||||
|
:ref:`the microversion specification
|
||||||
|
<microversion-client-interaction>`. It is acceptable for a client
|
||||||
|
library or framework to be interested in the latest version
|
||||||
|
available but such a specification is internal and not sent to
|
||||||
|
the server. In the client case with major versions, ``latest`` acts
|
||||||
|
as an input to the version discovery process.
|
||||||
|
|
||||||
|
``service-name``
|
||||||
Arbitrary name given to the service by the deployer. Optional.
|
Arbitrary name given to the service by the deployer. Optional.
|
||||||
|
|
||||||
.. note:: In all except the most extreme cases this should never be needed and
|
.. note:: In all except the most extreme cases this should never be needed
|
||||||
its use as a meaningful identifier by Deployers is strongly
|
and its use as a meaningful identifier by Deployers is strongly
|
||||||
discouraged. However, the Consumer has no way to otherwise mitigate
|
discouraged. However, the Consumer has no way to otherwise mitigate
|
||||||
the situation if their Deployer has provided them with a catalog
|
the situation if their Deployer has provided them with a catalog
|
||||||
where a ``service-name`` must be used, so ``service-name`` must be
|
where a ``service-name`` must be used, so ``service-name`` must be
|
||||||
accepted as input. If ``{be-strict}`` has been requested,
|
accepted as input.
|
||||||
supplying ``{service-name}`` should be an error.
|
If ``{be-strict}`` (see below) has been requested, a user supplying
|
||||||
|
``{service-name}`` should be an error.
|
||||||
|
|
||||||
service-id
|
``service-id``
|
||||||
Unique identifier for an endpoint in the catalog. Optional.
|
Unique identifier for an endpoint in the catalog. Optional.
|
||||||
|
|
||||||
.. note:: On clouds with well-formed catalogs ``service-id`` should never be
|
.. note:: On clouds with well-formed catalogs ``service-id`` should never be
|
||||||
needed. If ``{be-strict}`` has been requested, supplying
|
needed. If ``{be-strict}`` has been requested, supplying
|
||||||
``{service-id}`` should be an error.
|
``{service-id}`` should be an error.
|
||||||
|
|
||||||
endpoint-override
|
``endpoint-override``
|
||||||
An endpoint for the service that the user has procured from some other
|
An endpoint for the service that the user has procured from some other
|
||||||
source. (Optional, defaults to omitted.)
|
source. (Optional, defaults to omitted.)
|
||||||
|
|
||||||
At the end of the discovery process, the user should know the
|
Discovery Behavior Modifiers
|
||||||
``{service-endpoint}``, which is the endpoint to use as the root of the
|
----------------------------
|
||||||
service, and the ``{interface}`` of the endpoint that was found.
|
|
||||||
|
The user may also wish to express alterations to the general algorithm.
|
||||||
|
Implementations may present these flags under any name that makes sense,
|
||||||
|
or may choose to not present them as behavioral modification options at all.
|
||||||
|
|
||||||
|
``be-strict``
|
||||||
|
Forgo leniant backwards compatibility concessions and be more strict in
|
||||||
|
input and output validation. Defaults to False.
|
||||||
|
|
||||||
|
``skip-discovery``
|
||||||
|
If the user wants to completely skip the version discovery process even if
|
||||||
|
logic would otherwise do it. This is useful if the user has specified an
|
||||||
|
``{endpoint-override}`` or they know they just want to use whatever is in
|
||||||
|
the catalog and do not need additional metadata about the endpoint. Defaults
|
||||||
|
to False
|
||||||
|
|
||||||
|
``fetch-version-information``
|
||||||
|
If the user has specified an ``{endpoint-version}`` which can be known to
|
||||||
|
match just from looking at the URL, the version discovery process will not
|
||||||
|
fetch version information documents. However, the user may need the
|
||||||
|
information, such as microversion ranges. Using
|
||||||
|
``{fetch-version-information}`` allows them to request that the version
|
||||||
|
document be fetched even when an optimization in the process would otherwise
|
||||||
|
allow fetching the document to be skipped. Defaults to False.
|
||||||
|
|
||||||
|
|
||||||
|
Discovery Results
|
||||||
|
=================
|
||||||
|
|
||||||
|
At the end of the discovery process, the user should know the following:
|
||||||
|
|
||||||
|
If the process was successful:
|
||||||
|
|
||||||
|
* The actual values found for all of the input values above.
|
||||||
|
|
||||||
|
Found values will be referred to in these documents as ``found-{value}`` to
|
||||||
|
differentiate. So if a user requested an ``{endpoint-version}`` of
|
||||||
|
``latest``, ``{found-endpoint-version}`` might be ``3.5``.
|
||||||
|
|
||||||
|
* ``service-endpoint``
|
||||||
|
The endpoint to use as the root of the service.
|
||||||
|
|
||||||
|
* ``max-version``
|
||||||
|
If the service supports microversions, what is the maximum microversion the
|
||||||
|
service supports. Optional, defaults to omitted, which implies that
|
||||||
|
microversions are not supported.
|
||||||
|
|
||||||
|
* ``min-version``
|
||||||
|
If the service supports microversions, what is the minimum microversion the
|
||||||
|
service supports. Optional, defaults to omitted, which implies that
|
||||||
|
microversions are not supported.
|
||||||
|
|
||||||
|
If the process was unsuccessful, an error should be returned explaining which
|
||||||
|
part failed. For instance, was a matching service not found at all or was
|
||||||
|
a matching version not found. If a matching version was not found, the error
|
||||||
|
should contain a list of version that were found.
|
||||||
|
|
||||||
In the description that follows, each of the above inputs and outputs will
|
In the description that follows, each of the above inputs and outputs will
|
||||||
be referred to like ``{endpoint-override}`` so that it is clear whether a user
|
be referred to like ``{endpoint-override}`` so that it is clear whether a user
|
||||||
|
@ -107,459 +239,54 @@ referred to at a later point are similarly referred to like
|
||||||
``{service-catalog}``. Names will not be reused within the process to
|
``{service-catalog}``. Names will not be reused within the process to
|
||||||
hold different content at different times.
|
hold different content at different times.
|
||||||
|
|
||||||
It is also assumed that the user has an ``{auth-url}`` and authentication
|
|
||||||
information. The authentication process itself is out of the scope of this
|
|
||||||
document.
|
|
||||||
|
|
||||||
Discovery Algorithm
|
Discovery Algorithm
|
||||||
===================
|
===================
|
||||||
|
|
||||||
Services should be registered in the ``{service-catalog}`` using their
|
Services should be registered in the ``{service-catalog}`` using their
|
||||||
``{service-type}`` from the `OpenStack Service Types Authority`_. However,
|
``{service-type}`` from the :doc:`OpenStack Service Types Authority
|
||||||
for historical reasons there are some services that have old service types
|
<consuming-catalog/authority>`. However, for historical reasons there are some
|
||||||
found in the wild. To facilitate moving forward with the correct
|
services that have old service types found in the wild. To facilitate moving
|
||||||
``{service-type}`` names, but also support existing users and installations,
|
forward with the correct ``{service-type}`` names, but also support existing
|
||||||
the `OpenStack Service Types Authority`_ contains a list of historical
|
users and installations, the OpenStack Service Types Authority contains a list
|
||||||
aliases for such services. See `Consuming Service Types Authority`_ for
|
of historical aliases for such services.
|
||||||
information on the data itself.
|
|
||||||
|
|
||||||
Clients will need a copy of the data published in the
|
Clients will need a copy of the data published in the
|
||||||
`OpenStack Service Types Authority`_ to be able to complete the full Discovery
|
OpenStack Service Types Authority to be able to complete the full Discovery
|
||||||
Algorithm. A client library could either keep a local copy or fetch the data
|
Algorithm. A client library could either keep a local copy or fetch the data
|
||||||
from https://service-types.openstack.org/service-types.json and potentially
|
from https://service-types.openstack.org/service-types.json and potentially
|
||||||
cache it. It is recommended that client libraries handle consumption of the
|
cache it. It is recommended that client libraries handle consumption of the
|
||||||
historical data for their users but also allow some mechanism for the user to
|
historical data for their users but also allow some mechanism for the user to
|
||||||
provide a more up to date version of the data if necessary. See
|
provide a more up to date verison of the data if necessary.
|
||||||
`Consuming Service Types Authority`_ for information on how to fetch the data.
|
|
||||||
|
|
||||||
The basic process is:
|
The basic process is:
|
||||||
|
|
||||||
#. If the user has provided ``{endpoint-override}``, STOP. This is the
|
|
||||||
``{service-endpoint}``.
|
|
||||||
|
|
||||||
#. Authenticate to keystone at the ``{auth-url}``, retreiving a ``token``
|
#. Authenticate to keystone at the ``{auth-url}``, retreiving a ``token``
|
||||||
which contains the ``{service-catalog}``.
|
which contains the ``{service-catalog}``.
|
||||||
|
|
||||||
#. Retrieve ``{catalog-endpoint}`` from the ``{service-catalog}`` given
|
.. note:: This step is obviously skipped for clouds without authentication.
|
||||||
some combination of ``{service-type}``, ``{interface}``, ``{service-name}``,
|
|
||||||
``{region-name}`` and ``{service-id}``. (See :ref:`endpoint-from-catalog`.)
|
|
||||||
|
|
||||||
.. _endpoint-from-catalog:
|
#. If the user has provided ``{endpoint-override}``, it is used as
|
||||||
|
``{catalog-endpoint}``.
|
||||||
|
|
||||||
Endpoint from Catalog
|
#. If the user has not provided ``{endpoint-override}``, retrieve matching
|
||||||
=====================
|
``{catalog-endpoint}`` from the ``{service-catalog}`` using the procedure
|
||||||
|
explained in :doc:`consuming-catalog/endpoint`.
|
||||||
|
|
||||||
The ``{service-catalog}`` can be found in the ``token`` returned from
|
#. If ``{skip-discovery}`` is true, STOP and use ``{catalog-endpoint}`` as
|
||||||
keystone authentication.
|
``{service-endpoint}``. Otherwise, discover the available API versions
|
||||||
|
and find the suitable ``{service-endpoint}`` using the version discovery
|
||||||
If v3 auth is used, the catalog will be in the ``catalog`` property of the
|
procedure from :doc:`consuming-catalog/version-discovery`.
|
||||||
top-level ``token`` object. Such as:
|
|
||||||
|
|
||||||
.. code-block:: json
|
|
||||||
|
|
||||||
{
|
|
||||||
"token": {
|
|
||||||
"catalog": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
If v2 auth is used it will be in the ``serviceCatalog`` property of the
|
|
||||||
top-level ``access`` object. Such as:
|
|
||||||
|
|
||||||
.. code-block:: json
|
|
||||||
|
|
||||||
{
|
|
||||||
"access": {
|
|
||||||
"serviceCatalog": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
In both cases, the catalog content itself is a list of objects. Each object has
|
|
||||||
two main keys that concern discovery:
|
|
||||||
|
|
||||||
type
|
|
||||||
Matches ``{service-type}``
|
|
||||||
|
|
||||||
endpoints
|
|
||||||
List of endpoint objects for that service
|
|
||||||
|
|
||||||
Additionally, for backwards compatibility reasons, the following keys may
|
|
||||||
need to be checked.
|
|
||||||
|
|
||||||
name
|
|
||||||
Matches ``{service-name}``
|
|
||||||
|
|
||||||
id
|
|
||||||
Matches ``{service-id}``
|
|
||||||
|
|
||||||
The list of endpoints has a different format depending on whether v2 or v3 auth
|
|
||||||
was used. For both versions each endpoint object has a ``region`` key,
|
|
||||||
which should match ``{region-name}`` if one was given.
|
|
||||||
|
|
||||||
In v2 auth the endpoint object has three keys ``publicURL``,
|
|
||||||
``internalURL``, ``adminURL``. The endpoint for the ``{interface}`` requested
|
|
||||||
by the user is found in the key with the name matching ``{interface}`` plus
|
|
||||||
the string ``URL``.
|
|
||||||
|
|
||||||
In v3 auth the endpoint object has a ``url`` that is the endpoint that is
|
|
||||||
being requested if the value of ``interface`` matches ``{interface}``.
|
|
||||||
|
|
||||||
Concrete examples of tokens with catalogs:
|
|
||||||
|
|
||||||
V3 Catalog Objects:
|
|
||||||
|
|
||||||
.. code-block:: json
|
|
||||||
|
|
||||||
{
|
|
||||||
"token": {
|
|
||||||
"catalog": [
|
|
||||||
{
|
|
||||||
"endpoints": [
|
|
||||||
{
|
|
||||||
"id": "39dc322ce86c4111b4f06c2eeae0841b",
|
|
||||||
"interface": "public",
|
|
||||||
"region": "RegionOne",
|
|
||||||
"url": "https://identity.example.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "ec642f27474842e78bf059f6c48f4e99",
|
|
||||||
"interface": "internal",
|
|
||||||
"region": "RegionOne",
|
|
||||||
"url": "https://identity.example.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "c609fc430175452290b62a4242e8a7e8",
|
|
||||||
"interface": "admin",
|
|
||||||
"region": "RegionOne",
|
|
||||||
"url": "https://identity.example.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": "4363ae44bdf34a3981fde3b823cb9aa2",
|
|
||||||
"type": "identity",
|
|
||||||
"name": "keystone"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
V2 Catalog Objects:
|
|
||||||
|
|
||||||
.. code-block:: json
|
|
||||||
|
|
||||||
{
|
|
||||||
"access": {
|
|
||||||
"serviceCatalog": [
|
|
||||||
{
|
|
||||||
"endpoints_links": [],
|
|
||||||
"endpoints": [
|
|
||||||
{
|
|
||||||
"adminURL": "https://identity.example.com/v2.0",
|
|
||||||
"region": "RegionOne",
|
|
||||||
"publicURL": "https://identity.example.com/v2.0",
|
|
||||||
"internalURL": "https://identity.example.com/v2.0",
|
|
||||||
"id": "4deb4d0504a044a395d4480741ba628c"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"type": "identity",
|
|
||||||
"name": "keystone"
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
The algorithm is:
|
|
||||||
|
|
||||||
#. Find the objects in the ``{service-catalog}`` that match the requested
|
|
||||||
``{service-type}``. (See `Match Candidate Entries`_.)
|
|
||||||
|
|
||||||
#. If ``{service-name}`` was given and the objects remaining have a ``name``
|
|
||||||
field, keep only the ones where ``name`` matches ``{service-name}``.
|
|
||||||
|
|
||||||
.. note:: Catalogs from Keystone v3 before v3.3 do not have a name field. If
|
|
||||||
``{be-strict}`` was not requested and the catalog does not have a
|
|
||||||
``name`` field, ``{service-name}`` should be ignored.
|
|
||||||
|
|
||||||
#. If ``{service-id}`` was given and the objects remaining have a ``id``
|
|
||||||
field, keep only the ones where ``id`` matches ``{service-id}``.
|
|
||||||
|
|
||||||
.. note:: Catalogs from Keystone v2 do not have an id field. If
|
|
||||||
``{be-strict}`` was not requested and the catalog does not have a
|
|
||||||
``id`` field, ``{service-id}`` should be ignored.
|
|
||||||
|
|
||||||
The list of remaining objects are the ``{candidate-catalog-objects}``. If there
|
|
||||||
are no endpoints, return an error that there are no endpoints matching
|
|
||||||
``{service-type}`` and ``{service-name}``.
|
|
||||||
|
|
||||||
Use ``{candidate-catalog-objects}`` to produce the list of
|
|
||||||
``{candidate-endpoints}``.
|
|
||||||
|
|
||||||
For each endpoint object in each of the ``{candidate-catalog-objects}``:
|
|
||||||
|
|
||||||
#. If v2, if there is no key of the form ``{interface}URL`` for any of the
|
|
||||||
the ``{interface}`` values given, discard the endpoint.
|
|
||||||
|
|
||||||
#. If v3, if ``interface`` does not match any of the ``{interface}`` values
|
|
||||||
given, discard the endpoint.
|
|
||||||
|
|
||||||
If there are no endpoints left, return an error that there are no endpoints
|
|
||||||
matching any of the ``{interface}`` values, preferrably including the list of
|
|
||||||
interfaces that were found.
|
|
||||||
|
|
||||||
For each remaining endpoint in ``{candidate-endpoints}``:
|
|
||||||
|
|
||||||
#. If ``{region_name}`` was given and does not match either of ``region``
|
|
||||||
or ``region_id``, discard the endpoint.
|
|
||||||
|
|
||||||
If there are no remaining endpoints, return an error that there are no
|
|
||||||
endpoints matching ``{region_name}``, preferrably including the list of
|
|
||||||
regions that were found.
|
|
||||||
|
|
||||||
#. From the set of remaining candidate endpoints, find the ones that best
|
|
||||||
matches the requested ``{service-type}``.
|
|
||||||
(See `Find Endpoint Matching Best Service Type`_.)
|
|
||||||
|
|
||||||
The remaining ``{candidate-endpoints}`` match the request. If there is more
|
|
||||||
than one of them, use the first, but emit a warning to the user that more
|
|
||||||
than one endpoint was left. If ``{be-strict}`` has been requested, return an
|
|
||||||
error instead with information about each of the endpoints left in the list.
|
|
||||||
|
|
||||||
.. note:: It would be more correct to raise an error if there is more than one
|
|
||||||
endpoint left, but the keystoneauth library returns the first and
|
|
||||||
changing that would break a large number of existing users. If one
|
|
||||||
is writing a completely new library from scratch, or a new major
|
|
||||||
version where behavior change is acceptable, it is preferable to
|
|
||||||
raise an error here if there is more than one endpoint left.
|
|
||||||
|
|
||||||
#. If v2, the ``{catalog-endpoint}`` is the value of ``{interface}URL``.
|
|
||||||
|
|
||||||
#. If v3, the ``{catalog-endpoint}`` is the value of ``url``.
|
|
||||||
|
|
||||||
Match Candidate Entries
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
For every entry in the catalog:
|
|
||||||
|
|
||||||
#. If the entry's type matches the requested ``{service-type}``, it is a
|
|
||||||
candidate.
|
|
||||||
|
|
||||||
#. If the requested type is an official type from the
|
|
||||||
`OpenStack Service Types Authority`_ that has aliases and one of the aliases
|
|
||||||
matches the entry's type, it is a candidate.
|
|
||||||
|
|
||||||
#. If the requested type is an alias of an official type from the
|
|
||||||
`OpenStack Service Types Authority`_ and the entry's type matches the
|
|
||||||
official type, it is a candidate.
|
|
||||||
|
|
||||||
.. note:: Requesting one alias and finding a different alias is not supported
|
|
||||||
at this point because most aliases carry implied information about
|
|
||||||
major versions as well. A subsequent spec adds the process for
|
|
||||||
version discovery at which point it can be safe to attempt to return
|
|
||||||
an endpoint listed under an alias different than what was requested.
|
|
||||||
|
|
||||||
Find Endpoint Matching Best Service Type
|
|
||||||
----------------------------------------
|
|
||||||
|
|
||||||
Given a list of candidate endpoints that have matched the other criteria:
|
|
||||||
|
|
||||||
#. Check the list of candidate endpoints to see if one of them matches the
|
|
||||||
requested ``{service-type}``. If any are an exact match,
|
|
||||||
`Find Endpoint Matching Best Interface`_.
|
|
||||||
|
|
||||||
#. If the requested ``{service-type}`` is an official type in the
|
|
||||||
`OpenStack Service Types Authority`_ that has aliases, check each alias
|
|
||||||
in order of preference as listed in the Authority to see if it has a
|
|
||||||
matching endpoint from the candidate endpoints. For all endpoints that
|
|
||||||
match the first alias with matching endpoints,
|
|
||||||
`Find Endpoint Matching Best Interface`_.
|
|
||||||
|
|
||||||
#. If the requested ``{service-type}`` is an alias of an official type in the
|
#. If the requested ``{service-type}`` is an alias of an official type in the
|
||||||
`OpenStack Service Types Authority`_ and any endpoints match the official
|
OpenStack Service Types Authority and any endpoints match the official
|
||||||
type, `Find Endpoint Matching Best Interface`_.
|
type, :ref:`find-endpoint-matching-best-service-type`.
|
||||||
|
|
||||||
Find Endpoint Matching Best Interface
|
|
||||||
-------------------------------------
|
|
||||||
|
|
||||||
Given a list of candidate endpoints that have matched the other criteria:
|
Table of Contents
|
||||||
|
=================
|
||||||
|
|
||||||
#. In order of preference of ``{interface}`` list, return all endpoints that
|
.. toctree::
|
||||||
match the first ``{interface}`` with matching endpoints.
|
|
||||||
|
|
||||||
For example, given the following catalog:
|
consuming-catalog/endpoint
|
||||||
|
consuming-catalog/version-discovery
|
||||||
.. code-block:: json
|
consuming-catalog/authority
|
||||||
|
|
||||||
{
|
|
||||||
"token": {
|
|
||||||
"catalog": [
|
|
||||||
{
|
|
||||||
"endpoints": [
|
|
||||||
{
|
|
||||||
"interface": "public",
|
|
||||||
"region": "RegionOne",
|
|
||||||
"url": "https://block-storage.example.com/v3"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": "4363ae44bdf34a3981fde3b823cb9aa3",
|
|
||||||
"type": "volumev3",
|
|
||||||
"name": "cinder"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"endpoints": [
|
|
||||||
{
|
|
||||||
"interface": "public",
|
|
||||||
"region": "RegionOne",
|
|
||||||
"url": "https://block-storage.example.com/v2"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": "4363ae44bdf34a3981fde3b823cb9aa2",
|
|
||||||
"type": "volumev2",
|
|
||||||
"name": "cinder"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
Then the following:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
service_type = 'block-storage'
|
|
||||||
# block-storage is not found, get list of aliases
|
|
||||||
# volumev3 is found, return it
|
|
||||||
|
|
||||||
service_type = 'volumev2'
|
|
||||||
# volumev2 not an official type in authority, but is in catalog
|
|
||||||
# return volumev2 entry
|
|
||||||
|
|
||||||
service_type = 'volume'
|
|
||||||
# volume not in authority or catalog
|
|
||||||
# volume is an alias of block-storage
|
|
||||||
# block-storage is not found. Return error.
|
|
||||||
|
|
||||||
Given the following catalog:
|
|
||||||
|
|
||||||
.. code-block:: json
|
|
||||||
|
|
||||||
{
|
|
||||||
"token": {
|
|
||||||
"catalog": [
|
|
||||||
{
|
|
||||||
"endpoints": [
|
|
||||||
{
|
|
||||||
"interface": "public",
|
|
||||||
"region": "RegionOne",
|
|
||||||
"url": "https://block-storage.example.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": "4363ae44bdf34a3981fde3b823cb9aa3",
|
|
||||||
"type": "block-storage",
|
|
||||||
"name": "cinder"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
Then the following:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
service_type = 'block-storage'
|
|
||||||
# block-storage is found, return it
|
|
||||||
|
|
||||||
service_type = 'volumev2'
|
|
||||||
# volumev2 not in authority, is an alias for block-storage
|
|
||||||
# block-storage is in the catalog, return it
|
|
||||||
|
|
||||||
Given the following catalog:
|
|
||||||
|
|
||||||
.. code-block:: json
|
|
||||||
|
|
||||||
{
|
|
||||||
"token": {
|
|
||||||
"catalog": [
|
|
||||||
{
|
|
||||||
"endpoints": [
|
|
||||||
{
|
|
||||||
"interface": "public",
|
|
||||||
"region": "RegionOne",
|
|
||||||
"url": "https://block-storage.example.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": "4363ae44bdf34a3981fde3b823cb9aa3",
|
|
||||||
"type": "block-storage",
|
|
||||||
"name": "cinder"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"endpoints": [
|
|
||||||
{
|
|
||||||
"interface": "public",
|
|
||||||
"region": "RegionOne",
|
|
||||||
"url": "https://block-storage.example.com/v2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"interface": "internal",
|
|
||||||
"region": "RegionOne",
|
|
||||||
"url": "https://block-storage.example.int/v2"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": "4363ae44bdf34a3981fde3b823cb9aa2",
|
|
||||||
"type": "volumev2",
|
|
||||||
"name": "cinder"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
Then the following:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
service_type = 'block-storage'
|
|
||||||
interface = ['internal', 'public']
|
|
||||||
# block-storage is found
|
|
||||||
# block-storage does not have internal, but has public
|
|
||||||
# return block-storage public
|
|
||||||
|
|
||||||
service_type = 'volumev2'
|
|
||||||
interface = ['internal', 'public']
|
|
||||||
# volumev2 not an official type in authority, but is in catalog
|
|
||||||
# volumev2 has an internal interface
|
|
||||||
# return volumev2 internal entry
|
|
||||||
|
|
||||||
Consuming Service Types Authority
|
|
||||||
=================================
|
|
||||||
|
|
||||||
The `OpenStack Service Types Authority`_ is data about official service type
|
|
||||||
names and historical service type names commonly in use from before there was
|
|
||||||
an official list. It is made available to allow libraries and other client
|
|
||||||
API consumers to be able to provide a consistent interface based on the
|
|
||||||
official list but still support existing names. Providing this support is
|
|
||||||
highly recommended, but is ultimately optional. The first step in the matching
|
|
||||||
process is always to return direct matches between the catalog and the user
|
|
||||||
request, so the existing consumption models from before the existence of the
|
|
||||||
authority should always work.
|
|
||||||
|
|
||||||
In order to consume the information in the `OpenStack Service Types Authority`_
|
|
||||||
it is important to know a few things:
|
|
||||||
|
|
||||||
#. The data is maintained in YAML format in git. This is the ultimately
|
|
||||||
authoritative source code for the list.
|
|
||||||
|
|
||||||
#. The data is published in JSON format at
|
|
||||||
https://service-types.openstack.org/service-types.json and has a JSONSchema
|
|
||||||
at https://service-types.openstack.org/published-schema.json.
|
|
||||||
|
|
||||||
#. The published data contains a version which is date based in
|
|
||||||
`ISO Date Time Format`_, a sha which contains the git sha of the
|
|
||||||
commit the published data was built from, and pre-built forward and reverse
|
|
||||||
mappings between official types and aliases.
|
|
||||||
|
|
||||||
#. The JSON file is served with ETag support and should be considered highly
|
|
||||||
cacheable.
|
|
||||||
|
|
||||||
#. The current version of the JSON file should always be the preferred file to
|
|
||||||
use.
|
|
||||||
|
|
||||||
#. The JSON file is similar to timezone data. It should not be considered
|
|
||||||
versioned such that stable releases of distros should provide a
|
|
||||||
frozen version of it. Distro packages should instead update for all
|
|
||||||
active releases when a new version of the file is published.
|
|
||||||
|
|
||||||
.. _OpenStack Service Types Authority: https://opendev.org/openstack/service-types-authority/
|
|
||||||
.. _ISO Date Time Format: https://tools.ietf.org/html/rfc3339#section-5.6
|
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
Consuming Service Types Authority
|
||||||
|
=================================
|
||||||
|
|
||||||
|
The `OpenStack Service Types Authority`_ is data about official service type
|
||||||
|
names and historical service type names commonly in use from before there was
|
||||||
|
an official list. It is made available to allow libraries and other client
|
||||||
|
API consumers to be able to provide a consistent interface based on the
|
||||||
|
official list but still support existing names. Providing this support is
|
||||||
|
highly recommended, but is ultimately optional. The first step in the matching
|
||||||
|
process is always to return direct matches between the catalog and the user
|
||||||
|
request, so the existing consumption models from before the existence of the
|
||||||
|
authority should always work.
|
||||||
|
|
||||||
|
In order to consume the information in the `OpenStack Service Types Authority`_
|
||||||
|
it is important to know a few things:
|
||||||
|
|
||||||
|
#. The data is maintained in YAML format in git. This is the ultimately
|
||||||
|
authoritative source code for the list.
|
||||||
|
|
||||||
|
#. The data is published in JSON format at
|
||||||
|
https://service-types.openstack.org/service-types.json and has a JSONSchema
|
||||||
|
at https://service-types.openstack.org/published-schema.json.
|
||||||
|
|
||||||
|
#. The published data contains a version which is date based in
|
||||||
|
`ISO Date Time Format`_, a sha which contains the git sha of the
|
||||||
|
commit the published data was built from, and pre-built forward and reverse
|
||||||
|
mappings between official types and aliases.
|
||||||
|
|
||||||
|
#. The JSON file is served with ETag support and should be considered highly
|
||||||
|
cacheable.
|
||||||
|
|
||||||
|
#. The current version of the JSON file should always be the preferred file to
|
||||||
|
use.
|
||||||
|
|
||||||
|
#. The JSON file is similar to timezone data. It should not be considered
|
||||||
|
versioned such that stable releases of distros should provide a
|
||||||
|
frozen version of it. Distro packages should instead update for all
|
||||||
|
active releases when a new version of the file is published.
|
||||||
|
|
||||||
|
|
||||||
|
.. _OpenStack Service Types Authority: https://opendev.org/openstack/service-types-authority/
|
||||||
|
.. _ISO Date Time Format: https://tools.ietf.org/html/rfc3339#section-5.6
|
|
@ -0,0 +1,471 @@
|
||||||
|
Endpoint Discovery
|
||||||
|
==================
|
||||||
|
|
||||||
|
Endpoint from Catalog
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
The ``{service-catalog}`` can be found in the ``token`` returned from
|
||||||
|
keystone authentication.
|
||||||
|
|
||||||
|
If v3 auth is used, the catalog will be in the ``catalog`` property of the
|
||||||
|
top-level ``token`` object. Such as:
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"token": {
|
||||||
|
"catalog": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
If v2 auth is used it will be in the ``serviceCatalog`` property of the
|
||||||
|
top-level ``access`` object. Such as:
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"access": {
|
||||||
|
"serviceCatalog": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
In both cases, the catalog content itself is a list of objects. Each object has
|
||||||
|
two main keys that concern discovery:
|
||||||
|
|
||||||
|
``type``
|
||||||
|
Matches ``{service-type}``
|
||||||
|
|
||||||
|
``endpoints``
|
||||||
|
List of endpoint objects for that service
|
||||||
|
|
||||||
|
Additionally, for backwards compatibility reasons, the following keys may
|
||||||
|
need to be checked.
|
||||||
|
|
||||||
|
``name``
|
||||||
|
Matches ``{service-name}``
|
||||||
|
|
||||||
|
``id``
|
||||||
|
Matches ``{service-id}``
|
||||||
|
|
||||||
|
The list of endpoints has a different format depending on whether v2 or v3 auth
|
||||||
|
was used. For both versions each endpoint object has a ``region`` key,
|
||||||
|
which should match ``{region-name}`` if one was given.
|
||||||
|
|
||||||
|
In v2 auth the endpoint object has three keys ``publicURL``,
|
||||||
|
``internalURL``, ``adminURL``. The endpoint for the ``{interface}`` requested
|
||||||
|
by the user is found in the key with the name matching ``{interface}`` plus
|
||||||
|
the string ``URL``.
|
||||||
|
|
||||||
|
In v3 auth the endpoint object has a ``url`` that is the endpoint that is
|
||||||
|
being requested if the value of ``interface`` matches ``{interface}``.
|
||||||
|
|
||||||
|
Examples of Tokens with Catalogs
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
V3 Catalog Objects:
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"token": {
|
||||||
|
"catalog": [
|
||||||
|
{
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"id": "39dc322ce86c4111b4f06c2eeae0841b",
|
||||||
|
"interface": "public",
|
||||||
|
"region": "RegionOne",
|
||||||
|
"url": "https://identity.example.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ec642f27474842e78bf059f6c48f4e99",
|
||||||
|
"interface": "internal",
|
||||||
|
"region": "RegionOne",
|
||||||
|
"url": "https://identity.example.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c609fc430175452290b62a4242e8a7e8",
|
||||||
|
"interface": "admin",
|
||||||
|
"region": "RegionOne",
|
||||||
|
"url": "https://identity.example.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "4363ae44bdf34a3981fde3b823cb9aa2",
|
||||||
|
"type": "identity",
|
||||||
|
"name": "keystone"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
V2 Catalog Objects:
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"access": {
|
||||||
|
"serviceCatalog": [
|
||||||
|
{
|
||||||
|
"endpoints_links": [],
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"adminURL": "https://identity.example.com/v2.0",
|
||||||
|
"region": "RegionOne",
|
||||||
|
"publicURL": "https://identity.example.com/v2.0",
|
||||||
|
"internalURL": "https://identity.example.com/v2.0",
|
||||||
|
"id": "4deb4d0504a044a395d4480741ba628c"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "identity",
|
||||||
|
"name": "keystone"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Endpoint Discovery Algorithm
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
#. If ``{endpoint-version}`` was given and ``{service-type}`` ends with a
|
||||||
|
suffix of ``v[0-9]+$`` and ``{endpoint-version}`` does not match that suffix
|
||||||
|
(see `Comparing Major Versions`_), STOP. Return an error that the user
|
||||||
|
has requested a versioned ``{service-type}`` alias and an incompatible
|
||||||
|
``{endpoint-version}``.
|
||||||
|
|
||||||
|
#. Find the objects in the ``{service-catalog}`` that match the requested
|
||||||
|
``{service-type}`` (see `Match Candidate Entries`_).
|
||||||
|
|
||||||
|
#. If ``{service-name}`` was given and the objects remaining have a ``name``
|
||||||
|
field, keep only the ones where ``name`` matches ``{service-name}``.
|
||||||
|
|
||||||
|
.. note:: Catalogs from Keystone v3 before v3.3 do not have a name field. If
|
||||||
|
``{be-strict}`` was not requested and the catalog does not have a
|
||||||
|
``name`` field, ``{service-name}`` should be ignored.
|
||||||
|
|
||||||
|
#. If ``{service-id}`` was given and the objects remaining have a ``id``
|
||||||
|
field, keep only the ones where ``id`` matches ``{service-id}``.
|
||||||
|
|
||||||
|
.. note:: Catalogs from Keystone v2 do not have an id field. If
|
||||||
|
``{be-strict}`` was not requested and the catalog does not have a
|
||||||
|
``id`` field, ``{service-id}`` should be ignored.
|
||||||
|
|
||||||
|
The list of remaining objects are the ``{candidate-catalog-objects}``. If this
|
||||||
|
list is empty, return an error that there are no endpoints matching
|
||||||
|
``{service-type}`` and ``{service-name}``.
|
||||||
|
|
||||||
|
#. Use ``{candidate-catalog-objects}`` to produce the list of
|
||||||
|
``{candidate-endpoints}``. For each endpoint object in each of the
|
||||||
|
``{candidate-catalog-objects}``:
|
||||||
|
|
||||||
|
#. If v2, if there is no key of the form ``{interface}URL`` for any of the
|
||||||
|
the ``{interface}`` values given, discard the endpoint.
|
||||||
|
|
||||||
|
#. If v3, if ``interface`` does not match any of the ``{interface}`` values
|
||||||
|
given, discard the endpoint.
|
||||||
|
|
||||||
|
#. If there are no endpoints left, return an error that there are no endpoints
|
||||||
|
matching any of the ``{interface}`` values, preferrably including the list
|
||||||
|
of interfaces that were found.
|
||||||
|
|
||||||
|
#. For each remaining endpoint in ``{candidate-endpoints}``, if
|
||||||
|
``{region_name}`` was given and does not match either of ``region`` or
|
||||||
|
``region_id``, discard the endpoint.
|
||||||
|
|
||||||
|
If there are no remaining endpoints, return an error that there are no
|
||||||
|
endpoints matching ``{region_name}``, preferrably including the list of
|
||||||
|
regions that were found.
|
||||||
|
|
||||||
|
#. From the set of remaining candidate endpoints, find the ones that best
|
||||||
|
matches the requested ``{service-type}`` (see `Find Endpoint Matching Best
|
||||||
|
Service Type`_).
|
||||||
|
|
||||||
|
#. From the set of remaining candidate endpoints, find the ones that best
|
||||||
|
matches the best available requested ``{interface}``: in order of
|
||||||
|
preference of the ``{interface}`` list, return all endpoints that match
|
||||||
|
the first ``{interface}`` that has any matching endpoints.
|
||||||
|
|
||||||
|
The remaining ``{candidate-endpoints}`` match the request. If there is more
|
||||||
|
than one of them, use the first, but emit a warning to the user that more
|
||||||
|
than one endpoint was left. If ``{be-strict}`` has been requested, return an
|
||||||
|
error instead with information about each of the endpoints left in the list.
|
||||||
|
|
||||||
|
.. note:: It would be more correct to raise an error if there is more than one
|
||||||
|
endpoint left, but the keystoneauth library returns the first and
|
||||||
|
changing that would break a large number of existing users. If one
|
||||||
|
is writing a completely new library from scratch, or a new major
|
||||||
|
version where behavior change is acceptable, it is preferable to
|
||||||
|
raise an error here if there is more than one endpoint left.
|
||||||
|
|
||||||
|
#. If v2, the ``{catalog-endpoint}`` is the value of ``{interface}URL``.
|
||||||
|
|
||||||
|
#. If v3, the ``{catalog-endpoint}`` is the value of ``url``.
|
||||||
|
|
||||||
|
Match Candidate Entries
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
For every entry in the catalog:
|
||||||
|
|
||||||
|
#. If the entry's type matches the requested ``{service-type}``, it is a
|
||||||
|
candidate.
|
||||||
|
|
||||||
|
#. If the requested type is an official type from the
|
||||||
|
:doc:`OpenStack Service Types Authority <authority>` that has aliases and
|
||||||
|
one of the aliases matches the entry's type, it is a candidate.
|
||||||
|
|
||||||
|
#. If the requested type is an alias of an official type from the
|
||||||
|
:doc:`OpenStack Service Types Authority <authority>` and the entry's type
|
||||||
|
matches the official type, it is a candidate.
|
||||||
|
|
||||||
|
#. If the requested type is an alias of an official type from the
|
||||||
|
:doc:`OpenStack Service Types Authority <authority>` that has aliases and
|
||||||
|
the entry's type matches one of the aliases and ``{endpoint-version}`` was
|
||||||
|
given and the found alias ends with a suffix of ``v[0-9]+$`` and
|
||||||
|
``{endpoint-version}`` matches the version in the suffix (see `Comparing
|
||||||
|
Major Versions`_) it is a candidate.
|
||||||
|
|
||||||
|
.. _find-endpoint-matching-best-service-type:
|
||||||
|
|
||||||
|
Find Endpoint Matching Best Service Type
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Given a list of candidate endpoints that have matched the other criteria:
|
||||||
|
|
||||||
|
#. Check the list of candidate endpoints to see if one of them matches the
|
||||||
|
requested ``{service-type}``. If any are an exact match, return them.
|
||||||
|
|
||||||
|
#. If the requested ``{service-type}``
|
||||||
|
|
||||||
|
* is an official type from the :doc:`OpenStack Service Types Authority
|
||||||
|
<authority>` that has aliases
|
||||||
|
* ``{endpoint-version}`` was given
|
||||||
|
|
||||||
|
Look for aliases that end with a version suffix of the form ``v[0-9]+$``.
|
||||||
|
If there are any aliases with a version suffix that matches the
|
||||||
|
``{endpoint-version}`` (see `Comparing Major Versions`_), look for those
|
||||||
|
aliases in the list of candidate endpoints. If any are a match, return them.
|
||||||
|
|
||||||
|
#. If the requested ``{service-type}``
|
||||||
|
|
||||||
|
* is an official type in the :doc:`OpenStack Service Types Authority
|
||||||
|
<authority>` that has aliases
|
||||||
|
* ``{endpoint-version}`` was not given
|
||||||
|
|
||||||
|
check each alias in the order listed to see if it has a matching endpoint
|
||||||
|
from the candidate endpoints. Return the endpoints that match the first
|
||||||
|
alias that has matching endpoints.
|
||||||
|
|
||||||
|
#. If the requested ``{service-type}``
|
||||||
|
|
||||||
|
* is an alias of an official type in the
|
||||||
|
:doc:`OpenStack Service Types Authority <authority>`
|
||||||
|
* ``{endpoint-version}`` was given
|
||||||
|
|
||||||
|
look for aliases that end with a version suffix of the form ``v[0-9]+$``. If
|
||||||
|
there are any aliases with a version suffix that matches the
|
||||||
|
``{endpoint-version}`` (see `Comparing Major Versions`_), look for those
|
||||||
|
aliases in the list of candidate endpoints.
|
||||||
|
|
||||||
|
Return the endpoints that match the alias with the highest matching version.
|
||||||
|
|
||||||
|
#. If there are no matching endpoints, return an error.
|
||||||
|
|
||||||
|
.. note:: The case where
|
||||||
|
|
||||||
|
* an alias was requested
|
||||||
|
* no ``{endpoint-version}`` was given
|
||||||
|
* there is a different alias in the catalog
|
||||||
|
|
||||||
|
is not safe and so is treated as a lack of matching endpoint on
|
||||||
|
purpose. Many of the aliases carry an implied version, so absent
|
||||||
|
a requested ``{endpoint-version}`` from the user, returning
|
||||||
|
an endpoint different than the one explicitly requested has a high
|
||||||
|
chance of not being the endpoint the user expected.
|
||||||
|
|
||||||
|
.. _comparing-major-versions:
|
||||||
|
|
||||||
|
Comparing Major Versions
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
When comparing Major Versions, there is a ``required`` and a ``candidate``:
|
||||||
|
|
||||||
|
* The ``required`` is what the user has requested.
|
||||||
|
* The ``candidate`` is the possible version being tested.
|
||||||
|
|
||||||
|
To be suitable a ``candidate`` must be of the same major version as
|
||||||
|
``required`` and be at least a match in minor level: ``candidate`` ``3.3``
|
||||||
|
is a match for ``required`` ``3.1`` but ``4.1`` is not.
|
||||||
|
|
||||||
|
Leading 'v' strings should be discarded in all cases.
|
||||||
|
|
||||||
|
#. Versions with only a single number normalize to ``.0``. That is,
|
||||||
|
a version of ``2`` should be treated as if it was ``2.0``.
|
||||||
|
|
||||||
|
#. If ``required`` is the string ``latest`` or contains no value, ``candidate``
|
||||||
|
matches.
|
||||||
|
|
||||||
|
#. If ``required`` is a range, any ``candidate`` that is greater than or equal
|
||||||
|
to the first value and less than or equal to the second value is a match.
|
||||||
|
Equality is judged by the above rules. Greater than and less than are judged
|
||||||
|
as expected: first by comparing the first number, and if those match then by
|
||||||
|
comparing the second number. Thus, a ``{required}`` of ``2,4`` matches
|
||||||
|
``2``, ``2.3``, ``3``, ``4`` and ``4.7``. A ``{required}`` of ``2.1,4.0``
|
||||||
|
matches ``2.3``, ``3``, ``4`` and ``4.7`` but not ``2``.
|
||||||
|
|
||||||
|
#. If ``required`` is a range without a maximum value, maximum should be
|
||||||
|
treated as if it is ``latest``.
|
||||||
|
|
||||||
|
Examples of discovery
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
For example, given the following catalog:
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"token": {
|
||||||
|
"catalog": [
|
||||||
|
{
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"interface": "public",
|
||||||
|
"region": "RegionOne",
|
||||||
|
"url": "https://block-storage.example.com/v3"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "4363ae44bdf34a3981fde3b823cb9aa3",
|
||||||
|
"type": "volumev3",
|
||||||
|
"name": "cinder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"interface": "public",
|
||||||
|
"region": "RegionOne",
|
||||||
|
"url": "https://block-storage.example.com/v2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "4363ae44bdf34a3981fde3b823cb9aa2",
|
||||||
|
"type": "volumev2",
|
||||||
|
"name": "cinder"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
Then the following:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
service_type = 'block-storage'
|
||||||
|
# block-storage is not found, get list of aliases
|
||||||
|
# volumev3 is found, return it
|
||||||
|
|
||||||
|
service_type = 'volumev2'
|
||||||
|
# volumev2 not an official type in authority, but is in catalog
|
||||||
|
# return volumev2 entry
|
||||||
|
|
||||||
|
service_type = 'volume'
|
||||||
|
# volume not in authority or catalog
|
||||||
|
# volume is an alias of block-storage
|
||||||
|
# block-storage is not found. Return error.
|
||||||
|
|
||||||
|
service_type = 'volume'
|
||||||
|
api_version = 2
|
||||||
|
# volume not in authority or catalog
|
||||||
|
# volume is an alias of block-storage
|
||||||
|
# block-storage is not found.
|
||||||
|
# volumev2 is an alias of block-storage and ends with v2 which matches
|
||||||
|
# api_version of 2
|
||||||
|
# return volumev2
|
||||||
|
|
||||||
|
Given the following catalog:
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"token": {
|
||||||
|
"catalog": [
|
||||||
|
{
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"interface": "public",
|
||||||
|
"region": "RegionOne",
|
||||||
|
"url": "https://block-storage.example.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "4363ae44bdf34a3981fde3b823cb9aa3",
|
||||||
|
"type": "block-storage",
|
||||||
|
"name": "cinder"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
Then the following:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
service_type = 'block-storage'
|
||||||
|
# block-storage is found, return it
|
||||||
|
|
||||||
|
service_type = 'volumev2'
|
||||||
|
# volumev2 not in authority, is an alias for block-storage
|
||||||
|
# block-storage is in the catalog, return it
|
||||||
|
|
||||||
|
service_type = 'volumev2'
|
||||||
|
api_version = '3'
|
||||||
|
# volumev2 ends with a version suffix of v2 which does not match 3
|
||||||
|
# return an error before even fetching the catalog
|
||||||
|
|
||||||
|
Given the following catalog:
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"token": {
|
||||||
|
"catalog": [
|
||||||
|
{
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"interface": "public",
|
||||||
|
"region": "RegionOne",
|
||||||
|
"url": "https://block-storage.example.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "4363ae44bdf34a3981fde3b823cb9aa3",
|
||||||
|
"type": "block-storage",
|
||||||
|
"name": "cinder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"interface": "public",
|
||||||
|
"region": "RegionOne",
|
||||||
|
"url": "https://block-storage.example.com/v2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"interface": "internal",
|
||||||
|
"region": "RegionOne",
|
||||||
|
"url": "https://block-storage.example.int/v2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "4363ae44bdf34a3981fde3b823cb9aa2",
|
||||||
|
"type": "volumev2",
|
||||||
|
"name": "cinder"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
Then the following:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
service_type = 'block-storage'
|
||||||
|
interface = ['internal', 'public']
|
||||||
|
# block-storage is found
|
||||||
|
# block-storage does not have internal, but has public
|
||||||
|
# return block-storage public
|
||||||
|
|
||||||
|
service_type = 'volumev2'
|
||||||
|
interface = ['internal', 'public']
|
||||||
|
# volumev2 not an official type in authority, but is in catalog
|
||||||
|
# volumev2 has an internal interface
|
||||||
|
# return volumev2 internal entry
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -8,6 +8,8 @@ API expose the URIs and resources to end users in a machine-readable way.
|
||||||
|
|
||||||
See also the topic document on :ref:`consuming-catalog`.
|
See also the topic document on :ref:`consuming-catalog`.
|
||||||
|
|
||||||
|
See also the topic document on :doc:`consuming-catalog/version-discovery`.
|
||||||
|
|
||||||
Guidance
|
Guidance
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,8 @@ For example, you cannot request the feature which was introduced at
|
||||||
microversion 2.100 without backwards incompatible changes which were
|
microversion 2.100 without backwards incompatible changes which were
|
||||||
introduced in microversion 2.99 and earlier.
|
introduced in microversion 2.99 and earlier.
|
||||||
|
|
||||||
|
.. _microversion-client-interaction:
|
||||||
|
|
||||||
Client Interaction
|
Client Interaction
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue