Cinder API 微版本¶
我们需要一种方法来引入 REST API 的更改,以修复错误和添加新功能。其中一些更改与旧版本不兼容,而我们目前没有办法做到这一点。这对于引入 Nova - Cinder API 的向后不兼容更改尤其重要。感谢 Nova 规范的作者,特别是 Christopeher Yeoh 和 Sean Dague,以及 Manila 团队,特别是 Clinton Knight。
蓝图:https://blueprints.launchpad.net/cinder/+spec/cinder-api-microversions
问题描述¶
Cinder REST API 的许多更改需要 API 使用者进行更改。例如,如果我们需要向由 Nova 调用的一种方法添加必需的参数,那么 Nova 调用代码和 Nova 使用的 cinderclient 都需要更改。但是,具有更改的新版本 Cinder 必须与旧版本的 Nova 协同工作,目前没有机制来实现这一点。添加微版本将解决此问题。通过 HTTP 标头中发送到 Cinder API 的字段协商最高支持的版本。在未发送该字段“versions”(即,预先存在此更改的客户端和脚本)的情况下,将使用最低支持的版本。这意味着我们当前的 Cinder API v2 将是默认版本,并且 API 使用者如果希望使用较新版本,则可以这样做。
不在范围之内:实验性 API。这将单独完成。请注意,实验性 API 与我们选择使用 API 微版本的方式不同。对于 Nova,微版本不必与旧版本兼容,并且微版本可能会删除 API。在这种微版本定义中,Manila 使用的实验性 API 与 Nova 使用的微版本没有区别。此规范假定 Cinder 将采用 Manila 使用的路线,并使用 Experimental API HTTP 标头来指示可能导致向后不兼容更改和/或被删除的 API。这些策略现在不必确定,可以在合并具有新微版本的补丁时决定。
用例¶
允许开发人员以向后兼容的方式修改 Cinder API,并动态地向 API 用户发出更改可用信号,而无需创建新的 API 扩展。
允许开发人员以非向后兼容的方式修改 Cinder API,同时仍然支持旧行为。REST API 的使用者能够在每次请求的基础上决定是希望 Cinder API 以新方式还是旧方式运行。部署者能够在支持开发人员实现这一点的条件下,提供新的向后不兼容的功能,而不会删除对先前行为的支持。
REST API 的使用者能够在每次请求的基础上决定他们想要使用的 API 版本(假设部署者支持他们想要的版本)。这意味着不升级的部署者不会破坏兼容性,并且不升级的客户端也将保持兼容。
提议的变更¶
Cinder 将使用我们称之为“API 微版本”的框架来允许对 API 进行更改,同时保持向后兼容性。基本思想是用户必须显式要求他们的请求使用 API 的特定版本。因此,可以在不破坏不明确要求它的用户的 API 的情况下添加破坏性更改。这是通过 HTTP 标头 X-OpenStack-Cinder-API-Version 完成的,它是一个从 2.1 开始的单调递增的语义版本号。
如果用户在不指定版本的情况下发出请求,他们将获得在 cinder/api/openstack/wsgi.py 中定义的 DEFAULT_API_VERSION。此值当前为 2.0,预计在很长一段时间内仍将如此。
为了便于讨论,“API”是 Cinder 树中的所有核心和可选扩展。请注意,无法对扩展进行版本控制。已经讨论过 Cinder 团队应该因此将 API 扩展移动到核心。将扩展移动到核心需要一些时间,不应阻止对 API 进行更改。由于无法使用 API-microversions 装饰器对 API 扩展的更改进行版本控制,因此我们必须接受这一点,直到完成将扩展移动到核心的工作。
API 的版本控制应该是一个单一的单调计数器。它将采用 X.Y 的形式,遵循以下约定
仅当对整个 API 产生重大的向后不兼容的 API 更改时,才会更改 X。也就是说,很少很少增加。
Y 当您对 API 进行任何更改时。请注意,这包括语义更改,这些更改可能不会影响输入或输出格式,甚至不会源自 API 代码层。我们不会在版本系统中区分向后兼容和向后不兼容的更改。但是,将在文档中明确说明哪些是向后兼容的更改,哪些是不兼容的更改。
请注意,API 中类似的更改组不会在单个版本更新下进行。这将最大限度地减少对用户的影响,因为他们可以控制想要公开的更改。
向后兼容的更改定义为允许在 OpenStack API 更改指南 下进行的更改。
对于 GET http://<cinder_URL>:8776,版本响应如下所示
{
"versions": [
{
"id": "v2.0",
"links": [
{
"href": "https://docs.openstack.org/",
"rel": "describedby",
"type": "text/html"
},
{
"href": "http://10.10.10.77:8776/v2/",
"rel": "self"
}
],
"media-types": [
{
"base": "application/json",
"type": "application/vnd.openstack.volume+json;version=1"
},
{
"base": "application/xml",
"type": "application/vnd.openstack.volume+xml;version=1"
}
],
"min_version": "",
"status": "SUPPORTED",
"updated": "2014-06-28T12:20:21Z",
"version": ""
},
{
"id": "v2.1",
"links": [
{
"href": "https://docs.openstack.org/",
"rel": "describedby",
"type": "text/html"
},
{
"href": "http://10.10.10.77:8776/v2/",
"rel": "self"
}
],
"media-types": [
{
"base": "application/json",
"type": "application/vnd.openstack.volume+json;version=1"
},
{
"base": "application/xml",
"type": "application/vnd.openstack.volume+xml;version=1"
}
],
"min_version": "2.0",
"status": "CURRENT",
"updated": "2015-09-16T11:33:21Z",
"version": "2.1"
}
]
}
这指定了服务器可以理解的最小和最大版本。min_version 将从 2.0 开始,代表 v2.0 API。请注意,这假设我们将 Mitaka 中放弃对 v1.0 的支持。如果存在我们认为不足以支持的维护负担,将来可能会增加它。此响应指示当前版本为 2.1。此数字将随着 API 微版本的每次单调递增而变化。
客户端交互¶
客户端通过以下方法指定他们想要使用的 API 版本,即一个新的标头
X-OpenStack-Cinder-API-Version: 2.114
从概念上讲,这就像 accept 标头一样。这是一个全局 API 版本。
从语义上讲,这意味着
如果未提供 X-OpenStack-Cinder-API-Version,则表现得好像发送了 min_version。
如果发送了 X-OpenStack-Cinder-API-Version,则使用该版本响应 API。如果超出支持的版本范围,则返回 406 Not Acceptable。
如果 X-OpenStack-Cinder-API-Version: latest(特殊关键字)返回 API 的 max_version。
关于使用“latest”作为微版本的说明:客户端不应在调用 Cinder API 时使用“latest”,因为客户端可能不支持最新的服务器 API 微版本。使用“latest”仅用于测试。一个实验性的(非门控的)Tempeset 测试应该使用微版本“latest”来检测何时必须更新 Tempest 测试。当 Tempest 测试过时时,此实验性测试将使用“latest”失败,因此我们将拥有自动检测必须更新 Tempest 的方法。
这意味着开箱即用,使用旧客户端,OpenStack 安装将返回 v2 的香草 OpenStack 响应。用户或 SDK 必须要求其他内容才能获得新功能。
始终在响应中返回两个额外的标头
X-OpenStack-Cinder-API-Version: version_number
Vary: X-OpenStack-Cinder-API-Version
第一个标头指定执行的 API 的版本号。
第二个标头用作缓存代理的提示,表明响应也依赖于 X-OpenStack-Cinder-API-Version,而不仅仅是主体和查询参数。有关详细信息,请参阅 RFC 2616 第 14.44 节。
实现设计细节¶
在每次请求中,X-OpenStack-Cinder-API-Version 标头字符串将在 wsgi 代码中转换为 APIVersionRequest 对象。路由将以通常的方式进行,并将版本对象附加到请求对象(所有 API 方法都期望该对象)。API 方法然后可以使用它来确定其对传入请求的行为。
我们需要支持的更改类型
* Status code changes (success and error codes)
* Allowable body parameters (affects input validation schemas too)
* Allowable url parameters
* General semantic changes
* Data returned in response
* Removal of resources in the API
* Removal of fields in a response object or changing the layout of the response
注意:此列表并非详尽无遗
在控制器用例中,可以使用装饰器标记方法,以指示它们实现哪些 API 版本。例如
@api_version(min_version='2.0', max_version='2.9')
def show(self, req, id):
pass
@api_version(min_version='2.17')
def show(self, req, id):
pass
对 API 版本 2.2 的请求将最终执行第一种方法,而对 API 版本 2.17 的请求将导致执行第二种方法。
对于内部方法实现非常相似且只有细微差异的情况,可以通过对内部方法进行版本控制来避免大量重复代码。例如
@api_version(min_version='2.0')
def _version_specific_func(self, req, arg1):
pass
@api_version(min_version='2.5')
def _version_specific_func(self, req, arg1):
pass
def show(self, req, id):
.... common stuff ....
self._version_specific_func(req, "foo")
.... common stuff ....
减少重复代码可最大限度地减少维护开销。因此,我们使用的技术将取决于代码的共同/不同之处以及方法中的位置。
将版本对象传递到方法,附加到请求对象,因此也可以在方法中进行非常具体的检查。例如
def show(self, req, id):
.... stuff ....
if req.ver_obj.matches(start_version, end_version):
.... Do version specific stuff ....
.... stuff ....
请注意,end_version 是可选的,在这种情况下,它将匹配大于或等于 start_version 的任何版本。
可在此处找到解释其工作原理的一些原型代码:https://github.com/scottdangelo/TestCinderAPImicroversions
验证模式装饰器也需要扩展以支持版本控制
@validation.schema(schema_definition, min_version, max_version)
请注意,min_version 和 max_version 都是可选参数。
可以通过指定 max_version 从 API 中删除方法、扩展或请求或响应中的字段
@api_version(min_version='2.0', max_version='2.9')
def show(self, req, id):
.... stuff ....
如果客户端发出对版本 2.11 的请求,则客户端将收到 404,就好像该方法根本不存在一样。如果整个 API 的最小版本提高到 2.10,则可以删除扩展本身。
整个 API 的最小版本只会由 Cinder 开发人员达成共识来决定,他们有维护向后兼容性的开销,以及希望永远向后兼容的部署者和用户。
由于我们在整个 API 中都有单调递增的版本号,而不是对单个插件进行版本控制,因此我们可能会遇到当前 DB 迁移更改集中的潜在合并冲突。抱歉,我不相信有任何方法可以避免这种情况,但欢迎提出任何建议!
客户端期望¶
与支持版本协商的系统一样,消耗此 API 的健壮客户端也需要支持一定范围的版本,否则该客户端将无法在与多个云通信的软件中使用。
具体示例是 OpenStack Infra 中的 nodepool。假设有一个世界,它定期连接到 4 个公共云。它们处于以下状态
- Cloud A:
- min_ver: 2.100
- max_ver: 2.300
- Cloud B:
- min_ver: 2.200
- max_ver: 2.450
- Cloud C:
- min_ver: 2.300
- max_ver: 2.600
- Cloud D:
- min_ver: 2.400
- max_ver: 2.800
并非所有云都可用单个 API 版本,这取决于其中一些云的年龄。但是,在客户端 SDK 中,某些基本功能(如启动)将存在,尽管根据 API 的版本,可能会获得不同的附加数据。客户端应尽可能平滑这些差异。
从现实的角度来看,这存在于今天,但没有基础设施来支持创建解决方案来解决它。
备选方案¶
另一种选择是立即进行所有向后不兼容的更改并进行主要的 API 发布。例如,将 url 前缀更改为 /v3 而不是 /v2。然后支持这两种实现很长时间。这种方法过去很困难,并且导致各种用户采用时间长。
数据模型影响¶
无
REST API 影响¶
如上所述,将添加其他版本信息到 GET /。这些应该是向后兼容的更改。
否则,除非提供了请求中描述的客户端标头,否则不会进行任何更改。
安全影响¶
无
通知影响¶
无
其他最终用户影响¶
SDK 作者需要开始使用 X-OpenStack-Cinder-API-Version 标头来访问新功能。仅在新版本中添加新功能的事实将鼓励他们这样做。
python-cinderclient 处于相同的情况,需要更新以支持新的标头才能支持新的 API 功能。
性能影响¶
无
其他部署者影响¶
无
开发人员影响¶
这将影响 Cinder 开发人员修改 REST API 代码和添加新扩展的方式。这将影响 Volume Manager 以及能够删除锁,而是为处于各种 -ing 状态的卷返回 VolumeIsBusy 的能力。
实现¶
负责人¶
- 主要负责人
Scott DAngelo
工作项¶
将 Manila 代码移植到 api-microversions 状态:完成(参见 https://review.openstack.org/#/c/224910/)
添加递增和装饰器的示例 (scottda) 状态:完成(参见 https://github.com/scottdangelo/TestCinderAPImicroversions)
为 python-cinderclient 实现更改 (scottda)
使用 python-cinderclient 更改进行测试 (scottda)
Cinder 文档更改 (scottda)
依赖项¶
任何对 API 进行向后不兼容更改的 Cinder 规范都依赖于此规范
测试¶
对于 tempest 来说,测试微版本支持的 API 的所有可能组合是不可行的。我们将选择代表已实现内容的特定版本。现有的 Cinder tempest 测试将用作未来 API 版本测试的基线。
文档影响¶
有关 API 的文档需要反映这些更改。这些在 WIP cinder 代码更改中开始,并将位于 cinder/api/openstack/rest_api_version_history.rst