Artifact Repository

https://blueprints.launchpad.net/glance/+spec/artifact-repository

扩展 Glance 的功能,不仅存储虚拟机镜像,还存储任何其他工件,即带有组合元数据的二进制对象。

Glance 应成为此类工件的目录,提供存储、搜索和检索其工件、其元数据和关联二进制对象的功能。

问题描述

各种 OpenStack 服务通常需要编目它们用来运行的不同对象。这些对象可能包含各种数据以及用于标识和描述的元数据。

Nova 用于运行虚拟机的镜像只是这些对象的最佳示例。其他示例包括 Heat 模板、Solum Plan 文件、Mistral Workbooks、Murano Application Packages 等。

“编目”意味着具有以下功能
  • 可靠地存储对象

  • 存储后保证其不可变性

  • 提供搜索功能以查找目录中的对象

  • 返回有关请求对象的详细信息

  • 允许服务获取对象以供使用

  • 控制对对象的访问:强制使用和修改策略、发布场景等

  • 管理对象的生命周期

显然,这组功能对于不同类型的对象是通用的,并且通常与使用这些对象的相关项目的核心使命无关。因此,建议提供一个专用服务,该服务将为其他 OpenStack 服务提供编目功能。由于 Glance 已经作为 Nova 的镜像目录,建议扩展其使命,以便它也可以作为其他服务的目录。

存储在目录中的对象称为Artifacts(工件),目录本身称为Artifact Repository(工件仓库)。

提议的变更

建议将 Artifact Repository 功能添加到 Glance,即创建一组 API、数据模型和服务,这些 API、数据模型和服务将允许在 Glance 中存储不同类型的工件。

需要引入以下概念和行为

Artifact

Artifact 是一个存储了描述的二进制数据块 – metadata(元数据)。

所有工件都应具有以下属性

  • 工件应可被 OpenStack 服务使用。就像 Nova 使用镜像来运行虚拟机一样,其他现有或即将推出的 OpenStack 服务将使用不同类型的工件。可被服务使用很重要:不能被其他服务使用(例如用户数据)的数据块不是工件。如果有人希望存储此类不相关的数据,那么 Swift(或任何其他通用对象存储)应该是一个合适的选择。

  • 工件的描述(元数据)应具有定义明确、可发现的结构。工件的元数据应仅包含固定的一组字段。不同类型的工件可能有不同的允许字段集,但应禁止附加意外值。

  • 工件是不可变的。也就是说,一旦工件可用,就不应有任何更改其二进制内容和至少其元数据有意义的部分的方法。因此,一旦消费者发现了一个工件,其内容在对象的整个生命周期内都保证保持不变,并且没有其他对象可以获得相同的 ID。工件可以被删除,但任何引入的修改都将导致创建一个具有新 ID 的新对象。

注意:为了正确处理修改场景,建议不要删除现有的工件,而是创建其新版本。请参阅下面的“Artifact Versions”(工件版本)部分

Artifact types

工件的类型定义了其用途和内容。它由应该使用该工件的 OpenStack 服务定义,但是工件类型与服务之间的映射不是一对一的:单个服务可以使用多个不同类型的工件,并且在某些情况下,相同类型的工件可以被不同的服务使用。

工件类型的示例包括 Images(由 Nova 使用)、Templates(由 Heat 和其他基于 Heat 的服务使用,例如 Solum 或 Murano)、Solum plan 文件(由 Solum 使用)、Application Packages(由 Murano Application Catalog 使用)等。工件的类型定义了其二进制数据和元数据的结构。某些仓库操作也可以按工件类型定义,并且可能对不同类型具有不同的行为。

可以添加新的工件类型。这将使用插件系统实现。每个插件定义一个或多个工件类型,并完全描述其数据结构、元数据模式和操作。

可以通过一组特殊的 API 调用发现特定 Glance 仓库支持的工件类型,以及特定类型的结构和可用操作。

Artifact plugin

插件是一个 Python 模块,定义一个或多个工件类型,以及它们的自定义元数据字段、BLOB 类型、自定义验证逻辑等(请参阅下面的相应部分)。

Glance 使用 stevedore 库来发现插件中定义的工件类型。插件中定义的每个端点标识导出的工件类型。每个端点的名称应等于工件类型的名称。所有端点都应属于 glance.artifacts.types 命名空间。

每个工件类型都分配了一个版本。从代码的角度来看,相同工件类型的每个版本都是一个具有不同版本标识属性的不同 Python 类。单个插件可以包含相同工件类型的多个版本。此外,相同工件类型的不同版本可以定义在不同的插件中:由插件开发人员决定哪种组合最适合他们的需求。

如果插件包含某个工件类型的单个版本,则相应的端点应导出该特定类型类定义,其类型名称与端点名称匹配。

如果插件包含某个工件类型的多个版本,则相应的端点应导出类定义列表,并且这些类中的每个类都应具有不同的类型版本值和相同的类型名称,所有名称均等于端点名称。

特定工件类型的给定版本只能在一个插件中定义,也就是说,如果可能,则不能有第二个插件定义具有相同版本和相同工件类型。

在启动时,Glance 将检查所有启用的插件,并确保没有工件类型冲突。如果多个活动插件定义了相同的工件类型和版本,则会发生关键异常,并且工件服务器将无法启动。解决冲突和卸载不必要的插件由操作员负责。

要启用插件,云操作员必须将 Python 模块安装到运行 Glance 的节点的 Python 环境中,并对配置文件进行适当的更改。

只有来自显式启用的插件的类型才可供使用。

Metadata Structure

描述工件的元数据必须具有预定义的、可发现的结构。其中一些元数据字段对于所有类型的工件都是通用的,而其他字段仅针对特定类型定义。

其中一些字段是不可变的:也就是说,它们的值只能在工件交互式创建时更改(见下文),并且一旦工件发布到仓库中就被固定。其他字段可以在任何时候修改。还有一些系统字段,这些字段不能在任何时候修改 - 它们的值由仓库自动设置。

Common metadata fields

名称

访问

必需

数据类型

描述

ID

System

N/A

UUID String

标识仓库中的工件。一旦工件草稿注册,就会自动分配。不应由用户显式设置,但是管理员可以根据需要覆盖此值。

类型

Immutable

字母数字字符串,有效的工件类型名称

标识工件的类型,因此其数据和元数据结构应该是仓库支持的类型之一(由活动插件处理)

TypeVersion

System

N/A

遵循 SemVer 表示法的字符串

一个系统字段,定义了工件类型的版本,即包含工件类型定义的插件的版本。在创建工件记录时自动设置该值。

将来,这可用于对工件数据和元数据进行自动或手动迁移。

名称

Immutable

字母数字字符串,最大长度为 255 个字符

定义了工件的名称。将来,(名称、版本) 的某种唯一性将得到保证,以便能够在目录中标识工件。

版本

Immutable

遵循 SemVer 表示法

定义了工件的版本。有关详细信息,请参阅下面的“Artifact versioning”(工件版本)部分。

描述

Mutable

字符串

描述工件的任意文本,最大长度为 255 个字符

标签

Mutable

字母数字字符串集合,每个字符串最大长度为 255 个字符

与工件关联的一组任意字符串标签,用于搜索和筛选功能

Visibility

Mutable

Enum,可以是 PRIVATE、PUBLIC

一个字段,指示工件是否可供云中的所有租户访问(PUBLIC),或者仅供其所有者访问(PRIVATE)。

State

System

N/A

Enum,可以是:CREATING、ACTIVE、DEACTIVATED、DELETED

一个由仓库设置的系统字段,指示工件生命周期的当前阶段。有关详细信息,请参阅下面的“Artifact Lifecyle”(工件生命周期)

负责人

System

N/A

String,OpenStack 租户(项目)标识符

标识将工件上传到的租户(项目)

CreatedAt

System

N/A

Timestamp

工件记录在仓库中创建的时间。一旦设置,就不会更改

UpdatedAt

System

N/A

Timestamp

工件上次修改的时间。仓库在每次更新工件记录或其相关项的操作中自动更新。

PublishedAt

System

N/A

Timestamp

工件发布的时间,即从 CREATING 状态移动到 ACTIVE 状态。对于处于 CREATING 状态的工件为空

DeletedAt

System

N/A

Timestamp

工件删除的时间,即从 CREATING、ACTIVE 或 DEACTIVATED 状态移动到 DELETED 状态。对于未处于 DELETED 状态的工件为空

Type-specific metadata fields

对于每种类型的工件,可以定义其他元数据字段。此字段集及其属性由工件类型指定。定义工件的插件定义了其所有特定于类型的字段。每个特定于类型的元数据字段由具有以下属性的数据结构表示

  • Field name - 标识该字段的字母数字值。应在工件类型中是唯一的

  • Field type - 存储在该字段中的数据的类型。标识可以对工件进行查询和筛选的类型。支持以下类型:

    • String - 简单的字符串值(最大长度为 255 个字符)。在列出查询中,可以使用此字段按值相等性筛选工件(例如,“筛选类型为‘Image’且字段‘OS_TYPE’等于‘Linux’的所有工件”)

    • Integer. - 整数字段。在列出查询中,可以使用此字段按值相等性和值范围筛选工件(例如,“筛选类型为‘Image’且字段‘REQUIRED_RAM’小于或等于 16 的所有工件”)

    • Numeric - 与 Integer 相同,但存储为 Numeric

    • Boolean* - 布尔值,值为‘True’或‘False’。在列出查询中,可以使用此字段按值相等性筛选工件。

    • Text - 长文本字段。不能用于筛选,只是存储任意大型文本描述、JSON 等。从数据库加载这些字段应该延迟到列出工件、进行修改等操作时 - 只有在客户端请求工件内容时才加载它们。这减轻了数据库的负载。此外,插件开发人员可以放置自定义约束来限制文本的长度。

    • Array - 一种特定类型的值列表(String、Text、Integer、Numeric、Boolean)或它们的组合。此类字段可用于类似标签的筛选语义(例如,工件的类型可以有一个名为“Category”的字段,每个此类工件可以分配多个类别,并且筛选查询可以用来仅列出具有特定类别的工件)

    • Dict - 一组键值对,其中键是字符串值(最大长度为 255 个字符),而值可以是 String、Text、Integer、Numeric 或 Boolean 中的任何一种。可用于通过具有特定键值或具有特定键的任何值来筛选工件。

  • Required - 布尔值,指示工件是否有效需要指定该字段值。

  • Mutable - 布尔值,指示在工件发布后是否可以更改该字段值。

  • Internal - 一个布尔值,指示该字段的值不向最终用户显示(不能使用 API 获取或用于查询),但可以由插件逻辑内部访问。

  • Default - 可以由工件类型指定该字段的默认值。

  • Allowed values - 该字段可以存储的可能值的列表

  • Constraints - 可以为每个元数据字段定义多个约束。可用的约束取决于字段类型

    • String - minLength、maxLength、pattern

    • IntegerNumeric - 最小值、最大值

    • Array - 元素的类型(要么是所有元素的单个类型,要么是每个元素的特定类型)、数组中元素的最小和最大数量、元素的类型特定约束。

    • Dict - 值的类型(要么是所有值的单个类型,要么是每个值的特定类型)、字典中值的最小和最大数量、值的类型特定约束。

    此外,插件开发人员可以为每个字段定义自定义的、基于代码的约束。

Artifact Versions

工件仓库能够存储相同工件的不同版本。从仓库的角度来看,这些是不同的对象(具有不同的 ID,独立存储并具有独立的生命周期),但具有相同的类型和名称,但版本字段不同。

版本字段是常见的不可变元数据字段之一(见上文)。它遵循 SemVer 表示法,即其值由 3 个数字部分(主版本、次版本和补丁,以及可选的字母数字预发布后缀)组成,例如 5.1.3 或 1.1.4-alpha。根据 SemVer 规范,版本值之间定义了顺序,例如 1.2.3 > 1.2.2、1.0.1 < 1.1.0、1.0.0 > 1.0.0-some-suffix 等。当指定版本时,可以省略其中一些部分:例如,10 将表示 10.0.0,5.1 将表示 5.1.0。仓库将接受此类输入并将其转换为完整的 semver 表示法。主版本部分是必需的。主版本或次版本部分应非零:例如,0.1 是可能的,而 0.0 则不是。

Binary Data

数据是工件的核心和灵魂,也是它们存在的主要原因。有时它只是简单的文本(或 XML、YAML 或 JSON 对象,表示为文本),可以存储在工件的附加元数据字段中,但在许多情况下,它是一个大型二进制对象,例如 VM 镜像或其他大型字节块。即使是文本数据通常也与工件的元数据完全无关——因此必须单独处理。

无论数据是二进制的还是文本的,都应该与工件记录独立存储在一些专用的存储系统中——就像当前版本的 Glance 中 VM 镜像独立于其定义一样。

当工件最初在仓库中创建时(记录插入数据库),它处于 CREATING 状态(参见下面的“工件生命周期”部分),并且没有关联的二进制数据。然后——在发布工件之前——其所有者可以使用适当的 API 调用上传数据,或者指定已预先上传到某些后端存储的现有二进制 blob 的位置/uri。如果上传了新的 blob,它们将被放置到存储系统中(正在使用的 Glance 实例的存储系统),并通过其 ID 与工件记录关联。如果传递了现有的位置/uri,则仅建立关联。

当浏览工件仓库时,仅返回工件元数据:每个特定工件的数据对象必须通过发送适当的 API 调用到 Glance 或直接到适当的基础存储系统来单独下载。

与当前的 Glance 镜像不同,单个工件可以关联多个二进制对象。

例如,作为工件存储的 Heat 模板可能需要 Heat 模板本身、多个提供者模板,以及可能需要在目录 UI 中显示的图标。所有这些都被认为是“工件的数据”(尽管模板实际上不是二进制的),并且没有意义将它们合并到一个数据块中:不同的模板可能在不同的时间点需要,而图标仅在 UI 中需要——因此它们作为不同的“二进制对象”存储,并在真正需要时逐一检索。

这意味着每个工件可以关联多个不同的 BLOB。

BLOB 的数量及其类型(例如,上面的“Heat 模板”、“提供者模板”和“图标”)是每个工件类型定义的。当工件处于 CREATING 状态时,其所有者必须逐一上传所有必需的部分,每次指定其类型。如果未上传某些必需的 blob,则“发布检查”将失败,并且工件将不会被接受到仓库中。因此,总的来说,这类似于指定其他特定类型元数据字段的值。

对于每个工件类型,其插件可以定义几个 BLOB 属性,这些属性对应于此特定类型支持的 BLOB。BLOB 属性可以以 2 种形式定义——要么是单个 BinaryObject,要么是 BinaryObjectList。后者可以用最大和最小长度约束。每个 BLOB 属性由具有以下属性的数据结构表示

  • 名称(字母数字字符串)- blob 属性的名称。应在每个工件类型中唯一。

  • 必需(布尔值)- 一个标志,指示工件是否需要此属性的 blob。

  • 最小长度(仅适用于 BinaryObjectList)(正整数值或 None)- 指定每个工件所需的最小 blob 数量。如果设置为 None,则每个工件中 BLOB 列表中的 blob 数量没有下限。

  • 最大长度(仅适用于 BinaryObjectList)(正整数值或 None)- 指定每个工件的最大 blob 数量。如果设置为 None,则每个工件中 BLOB 列表中的 blob 数量没有上限。

    例如,上述工件类型“Heat 模板”可以定义一个可选的 BinaryObjectList 属性,其名称设置为“ProviderTemplate”,以及一个必需的 BinaryObjectList 属性,其名称为“Icon”,并且最小和最大长度约束等于 1。这意味着这种类型的工件必须包含精确一个图标 blob 和零个或多个提供者模板 blob。

仓库提供了一个 API 调用来检查每个工件的 blob 内容。

在上面的示例中,API 返回一个包含两个键的字典:“ProviderTemplates”和“Icon”。第一个键定义了一个记录列表,每个记录对应于不同的提供者模板 blob,第二个键定义了一个与图标 blob 对应的单个记录。每个 blob 记录包含特定 blob 的 ID,可用于下载 blob。

二进制数据是不可变的,并且一旦工件移动到 ACTIVE 状态,就不能添加新的 blob,也不能删除现有的 blob。当工件被删除时,其所有 blob 也被删除。blob 数据只有在用户具有访问包含该 blob 的工件的权限时才能访问。

工件依赖关系

在 OpenStack 生态系统中,实体很少单独存在:通常它们以某种方式相互交互。工件也不例外:通常,一个工件可能需要以某种方式与其他工件一起工作。在这种情况下,引入工件依赖关系的概念是合理的:一个工件可以依赖于任何类型的任何数量的其他工件。

例如,可能有一个工件包含一个可重用的 Puppet 清单包,用于配置 VM 上的某些常见软件组件(工件 A)。包含一些 Heat 软件配置模板(工件 B)的另一个工件可以使用工件 A 中的 Puppet 清单进行在模板创建的 VM 上的某些部署后配置操作。在这种情况下,工件 B 可以将工件 A 指定为其依赖项,以便将要使用 B 的服务可以检测到它也可能需要 A,并提前下载它。

这最终创建了具有传递依赖关系的工件链。工件仓库提供的 API 方法应允许检索给定工件的所有依赖项。对相同 API 的后续调用将允许检索所有传递依赖项。

这些依赖关系实际上是一种特殊的工件元数据:它们存储在仓库数据库中,以及工件记录中。它们是不可变的,即只能在工件处于 CREATING 状态时定义,并且在发布后不能修改。

创建、查看或修改特定工件的依赖关系的操作应实现为类似于其他元数据编辑的 API 集合。

依赖关系不能是循环的(例如,如果 A 依赖于 B,B 依赖于 C,C 依赖于 D,则 D 不能依赖于 A),并且已发布的工件不能依赖于处于“CREATING”状态的工件。例如,如果 A 依赖于 B,并且两者都处于“CREATING”状态,则 B 应该在 A 之前发布,否则 A 的发布检查将失败。

依赖关系只能在属于与依赖工件相同的租户的工件上创建。一旦建立依赖关系,在删除依赖工件之前,目标工件就不能被删除。如果两者都被删除并且应该恢复,则恢复应该按照创建的顺序进行:首先恢复目标工件,然后恢复依赖工件。

工件生命周期

工件可以处于三种状态之一,指示其生命周期的不同阶段

  • CREATING 状态指示工件记录已在仓库中创建(已分配 ID),但工件仍在构建中(某些字段需要设置,数据上传等)。可变元数据字段可以在工件处于此状态时修改。可以上传二进制数据或指定预上传 blob 的位置 uri,可以设置或修改依赖关系。

    处于 CREATING 状态的工件对其他租户不可见,即使“Visibility”字段设置为“PUBLIC”。当工件处于 CREATING 状态时,其所有者可以调用“Publish”API 方法来发布工件。这将验证工件并将其状态更改为 ACTIVE。当工件处于 CREATING 状态时,其所有者可以启动其删除。这将更改工件的状态为“DELETED”。

  • ACTIVE 状态指示工件可供使用。当工件处于此状态时,不可变的元数据字段不能更新,不能上传新的二进制数据,也不能删除现有数据。

    如果“Visibility”字段设置为 PUBLIC,则处于 ACTIVE 状态的工件对其他租户可见。当工件处于 ACTIVE 状态时,假定它可以由第三方服务使用。当工件处于 ACTIVE 状态时,其所有者可以启动其删除。这将更改工件的状态为“DELETED”。

  • DEACTIVATED 状态指示先前发布的工件已被云管理员临时停用(即防止使用)。工件对可以正常访问它的所有用户可见(即对其所有者和其他租户(如果公开)),并且可以获取其元数据,但二进制数据不可访问,并且在停用被删除之前,第三方服务不应使用该工件。通常用于调查有关工件报告的问题。完成调查后,可以重新激活工件(通过将其状态更改回 ACTIVE)或删除它。

  • DELETED 状态指示工件已被删除,其数据和元数据对象不再可访问。

    只有在配置文件中启用了“delayed_delete”选项时,此状态才可用,这使得删除不是立即的:工件只是被标记为已删除,但是实际删除会延迟到可配置的时间段。

    如果禁用了 delayed_delete,则工件不会进入 DELETED 状态,其记录将在执行删除操作时永久从仓库中删除。

可以在 此处 找到状态转换图。

组合工件

工件可以使用迭代过程创建,该过程从在数据库中创建工件记录开始。然后可以调用多个 API 来设置各种元数据属性的值,上传二进制数据,指定依赖关系链接等。

在组合工件时,它保持在 CREATING 状态。

发布工件

发布是一个使工件可供使用的操作:当所有元数据字段都设置并且二进制数据部分已上传(这可能是一个漫长的多步骤过程)时,可以通过调用特殊的 API 方法将其状态更改为 ACTIVE 来使其可用。

在此调用执行期间,Glance 将验证所有依赖关系是否设置为现有且已发布的工件。如果至少有一个依赖关系不存在(即,在创建依赖关系之后工件已被删除)或未处于 ACTIVE 状态,则发布操作将失败。

删除工件

只有其所有者或云管理员才能删除工件。当调用删除操作时,将执行以下操作

  • 检查当前租户的所有工件。如果其中任何一个对要删除的工件具有依赖关系,则中止删除操作

  • 如果启用了延迟删除,则将工件的状态设置为 DELETED,并且删除操作计划在可配置的时间间隔内进行

  • 如果未启用软删除,则计划立即运行后台删除操作

删除操作(无论是延迟的还是立即的)执行以下操作

  • 从基础存储系统中删除工件的所有二进制数据对象。

  • 从数据库中删除工件的记录,包括所有自定义元数据属性值、关联的标签和传出依赖关系

备选方案

目录服务的需求似乎很明显,但有时会问 Glance 是否适合这个项目。有些人建议使用完全独立的服務,有些人建议使用 Swift 代替 Glance。

为什么不使用独立的服務?

因为 Glance 的镜像就是工件的完美例子:它们是存储了描述它们的任意元数据的二进制对象。Glance 已经提供了搜索和过滤功能,只需要扩展它们以支持除镜像以外的其他工件类型。

为什么不使用 Swift?

有几个原因。

首先,Swift 不是目录,它只是一个对象存储。没有搜索或过滤功能,不能保证对象不变性。

然后,Swift 具有不同的抽象级别:它不关心其中存储的数据,而 Glance 应该了解工件类型的具体信息。

最后但并非最不重要的一点是,许多生产级的 OpenStack 部署没有部署 Swift,因为它们不需要它。与此同时,Glance 存在于每个 OpenStack 云上。

与此同时,Swift 仍然可以用作工件的基础存储——就像 Glance 用它来存储镜像一样。

数据模型影响

这将使用关系数据库,并存在于 Glance 现有的关系数据所在的同一个数据库中,但预计不会对现有的 Glance 数据模型产生影响。所有这些都是新的功能。

将添加对以下的支持:* glance/db/sqlalchemy/api.py * registry/api.py * simple/api.py

将添加一个新的脚本 glance/db/sqlalchemy/artifacts.py

表类将在 glance/db/sqlalchemy/models_artifacts.py 中

以下 DB 模式是初始建议的模式。为了便于阅读,未显示约束。

工件表

字段

类型

是否为空

id

varchar(36)

NO

主键

name

varchar(255)

NO

MUL

type_name

varchar(255)

NO

MUL

type_version_prefix

bigint(20)

NO

type_version_suffix

varchar(255)

YES

type_version_meta

varchar(255)

YES

version_prefix

bigint(20)

NO

version_suffix

varchar(255)

YES

version_meta

varchar(255)

YES

description

text

YES

visibility

varchar(32)

NO

MUL

state

varchar(32)

NO

MUL

owner

varchar(255)

NO

MUL

created_at

datetime

NO

updated_at

datetime

NO

published_at

datetime

YES

deleted_at

datetime

YES


工件 Blob 表

字段

类型

是否为空

id

varchar(36)

NO

主键

name

varchar(36)

NO

MUL

artifact_id

varchar(36)

NO

MUL

size

bigint(20)

NO

position

int(11)

YES

item_key

varchar(329)

YES

MUL

checksum

varchar(329)

YES

MUL

created_at

datetime

NO

updated_at

datetime

NO


工件 Blob 位置表

字段

类型

是否为空

id

varchar(36)

NO

主键

value

text

NO

blob_id

varchar(36)

NO

MUL

status

varchar(36)

YES

position

int(11)

YES

created_at

datetime

NO

updated_at

datetime

NO


工件依赖关系表

字段

类型

是否为空

id

varchar(36)

NO

主键

artifact_source

varchar(36)

NO

MUL

artifact_dest

varchar(36)

NO

MUL

artifact_origin

varchar(36)

NO

MUL

is_direct

tinyint(1)

NO

position

int(11)

YES

name

varchar(36)

YES

created_at

datetime

NO

updated_at

datetime

NO


制品属性表

字段

类型

是否为空

id

varchar(36)

NO

主键

artifact_id

varchar(36)

NO

MUL

string_value

varchar(255)

YES

int_value

int(11)

YES

numeric_value

decimal(10,0)

YES

bool_value

tinyint(1)

YES

text_value

text

YES

position

int(11)

YES

name

varchar(36)

NO

MUL

created_at

datetime

NO

updated_at

datetime

NO


制品标签表

字段

类型

是否为空

id

varchar(36)

NO

主键

artifact_id

varchar(36)

NO

MUL

value

varchar(255)

YES

created_at

datetime

NO

updated_at

datetime

NO

REST API 影响

所有新的 API 都将放置在 /v2/artifacts API 分支下

特定制品类型的 API 应该放置在 /v2/artifacts/{artifact_type},其中 artifact_type 是由制品类型定义(即插件)定义的常量,通常应该是制品类型名称的复数形式。例如,对于类型为“template”的制品,该常量可能称为 ‘templates’,因此 API 端点将以 /v2/artifacts/templates 开头。

artifact_type 常量应该明确标识制品类型,因此这些常量的数值在活动插件定义的全部制品类型中应该是唯一的。

artifact_type 常量应该后跟 type_version 标识符,该标识符包含以 v 开头的符合 SemVer 规范的字符串,例如 v1.1.5。这将标识可用制品类型的特定版本。在“列出制品”和“获取制品”API 调用中,type_version 是可选的,可以省略。

  • 列出制品
    • GET /v2/artifacts/{artifact_type}/[{type_version}/]creating - 列出

      制品草稿

      返回指定类型且属于当前租户的处于 CREATING 状态的制品列表。如果用户是管理员,则返回属于所有租户的处于 CREATING 状态的制品。

    • GET /v2/artifacts/{artifact_type}/[{type_version}/] - 列出制品

      已准备好使用

      返回处于 ACTIVE 状态的制品列表,这些制品要么属于当前租户,要么通过将 Visibility 元数据字段设置为 PUBLIC 来向所有人开放。

    • GET /v2/artifacts/{artifact_type}/[{type_version}/]deactivated - 列出

      处于 DEACTIVATED 状态的制品。

      返回处于 DEACTIVATE 状态的制品列表,这些制品暂时暂停使用。

    • URL 参数
      • artifact_type 制品类型的标识符,应等于一个

        在活动制品插件之一中定义的有效常量。

      • type_version 可选的标识符,用于定义制品类型的版本。如果省略,则假定所有版本的制品类型,但排序和筛选功能仅限于通用属性。

    • 查询参数

      查询可以包含用于根据大多数通用和特定于类型的元数据字段进行筛选和排序的参数。只有在设置了 type_version 参数到制品类型的特定版本时,才能使用特定于类型的字段。参数集及其值应符合制品类型及其版本定义的模式。

      过滤:

      • 筛选键可以是任何通用和特定于类型的元数据字段的原始类型,如 ‘string’、‘numeric’、‘int’ 和 ‘bool’。但是,只有在提供制品版本时,才允许按特定于类型的属性进行筛选。

        直接比较需要将属性名称指定为查询参数,并将筛选值指定为其值,例如 ?name=some_name

        参数名称和值区分大小写。

      • 制品 API 支持以下格式的筛选操作:?name=<op>:some_name,其中 op 是以下之一

        1. eq:等于;

        2. ne:不等于;

        3. gt:大于;

        4. ge:大于或等于;

        5. lt:小于;

        6. le:小于或等于。

      • 集合比较筛选适用于所有具有集合值的特定于类型的元数据字段以及通用字段“tags”。

        集合比较需要将属性名称指定为查询参数。该属性可以重复多次,例如,查询 ?tags=abc&tags=cde&tags=qwerty 将筛选具有 ‘abc’、‘cde’ 或 ‘qwerty’ 标签的制品。

      • 通过 in 操作检查数组属性中的条目。它的执行方式与其他筛选器相同,例如 ?items_array=in:array_element

      排序

      为了以任何排序顺序和方向检索数据,制品 REST API 接受多个排序键和方向。

      制品 API 将与 API 工作组排序指南 对齐,并支持请求上的以下参数

      • sort:逗号分隔的排序键列表,每个键可选地附加 <:dir>,其中 ‘dir’ 是相应排序键的方向(支持的值为 ‘asc’ 表示升序和 ‘desc’ 表示降序)

      排序键可以是任何通用和特定于类型的元数据字段的原始类型,如 ‘string’、‘numeric’、‘int’ 和 ‘bool’。但是,只有在提供制品版本时,才允许按特定于类型的属性进行排序。

      排序方向的默认值为 ‘desc’,排序键的默认值为 ‘created_at’。

      分页

      limitmarker 查询参数可用于像当前版本的 Glance “List Images” API 一样对制品集合进行分页。

      最大 limit 数量为 1000。这是出于安全原因,为了保护系统免受入侵者攻击,防止他们发送可能一次性拉取整个数据库的请求。

    • HTTP 响应
      • 200 如果 artifact_type 有效

      • 404 如果未定义任何制品类型来处理指定的 artifact_type

    • 响应模式:[JSON 列表,包含制品的元数据]

  • 创建一个新的制品草稿
    • POST /v2/artifacts/{artifact_type}/{type_version}/creating

    • 在数据库中创建一个新的制品记录,制品状态设置为 CREATING。请求主体可以包含制品的初始元数据。

    • URL 参数
      • artifact_type 制品类型的标识符,应等于在活动制品插件之一中定义的有效常量。

      • type_version 标识符,用于定义制品类型的版本。

    • HTTP 响应
      • 201 如果一切顺利。Location 标头设置为制品位置

      • 404 如果未定义任何制品类型来处理指定的 artifact_type 和/或 type_version

      • 400 如果已存在具有相同名称和版本的该类型的制品。

    • 响应模式:[JSON,包含创建的制品]

  • 发布制品
    • POST /v2/artifacts/{artifact_type}/{type_version}/{id}/publish

    • 发布制品,即将其移动到 ACTIVE 状态。

    • URL 参数
      • artifact_type 制品类型的标识符,应等于在活动制品插件之一中定义的有效常量。

      • id 制品标识符

      • type_version 标识符,用于定义制品类型的版本。

    • HTTP 响应
      • 200 如果一切顺利

      • 404 如果未找到具有给定 ID 的制品,或者如果找到的制品的类型与 artifact_type 参数指定的类型不同(如果它不等于通用值 ‘artifacts’),或者如果找到的制品不属于当前租户。

      • 400 如果制品草稿依赖于缺失或未发布的制品。

      • 403 如果制品不在 CREATING 状态

    • 请求主体:无

    • 响应模式:[JSON,包含发布的制品]

  • 获取制品
    • GET /v2/artifacts/{artifact_type}/[{type_version}]/{id}

    • 返回包含所有通用和特定于类型的元数据的制品记录

    • URL 参数
      • artifact_type 制品类型的标识符,应等于在活动制品插件之一中定义的有效常量。

      • id 制品标识符

      • type_version 可选的标识符,用于定义制品类型的版本。与其他(修改)调用不同,在此调用中可以省略它,以便能够通过其类型和 ID 获取制品,而与类型版本无关。

    • HTTP 响应
      • 200 如果一切顺利

      • 404 如果未找到具有给定 ID 的制品,或者如果找到的制品的类型与 artifact_type 参数指定的类型不同(如果它不等于通用值 ‘artifacts’),或者如果当前租户无法访问找到的制品。

    • 响应模式:[JSON 制品定义,待定]

  • 更新制品
    • PATCH /v2/artifacts/{artifact_type}/{type_version}/{id}

    • 更新制品的元数据字段。如果制品的状态不是 CREATING,则只能更新可变字段。

    • URL 参数
      • artifact_type 制品类型的标识符,应等于在活动制品插件之一中定义的有效常量。

      • id 制品标识符

    • HTTP 响应
      • 200 如果一切顺利

      • 404 如果未找到具有给定 ID 的制品,或者如果找到的制品的类型与 artifact_type 参数指定的类型不同(如果它不等于通用值 ‘artifacts’),或者如果找到的制品不属于当前租户。

      • 403 如果 PATCH 尝试在制品状态不是 CREATING 时修改不可变属性

    • 请求模式:[JSON patch,待定]

    • 响应模式:[JSON 制品定义,待定]

  • 删除制品
    • DELETE /v2/artifacts/{artifact_type}/{type_version}/{id}

    • 删除制品。有关详细信息,请参阅删除制品

    • URL 参数
      • artifact_type 制品类型的标识符,应等于在活动制品插件之一中定义的有效常量。也可以包含等于 “artifacts” 的值

      • id 制品标识符

      • type_version 标识符,用于定义制品类型的版本。

    • HTTP 响应

      • 200 如果一切顺利

      • 404 如果未找到具有给定 ID 的制品,或者如果找到的

        制品的类型与 artifact_type 参数指定的类型不同(如果它不等于通用值 ‘artifacts’),或者如果找到的制品不属于当前租户。

      • 400 如果有其他制品依赖于该制品。

一个详细示例

假设制品类型具有以下定义的几个特定于类型的属性

tags = Array(item_type=String()) dependencies = ArtifactReferenceList() blob = BinaryObject()

对于给定的制品,以下是示例 HTTP 请求。

  • GET /v2/artifacts/{artifact_type}/{id}

检索类型为 artifact_type 且 ID 为 id 的制品。

  • PATCH /v2/artifacts/{artifact_type}/{type_version}/{id} body = [{‘op’: ‘add’, ‘path’: ‘/tags/-’, ‘value’: ‘new’}]

此请求将字符串 ‘new’ 附加到 tags 属性。这里 tags 不是像在 images 中那样的保留名称,它只是为表示字符串数组的制品属性选择的名称。由于这是一个数据修改请求,它应该包含 URI 的 type_version 部分,以确保调用者确切知道正在针对哪个版本的制品模式。

  • PATCH /v2/artifacts/{artifact_type}/{type_version}/{id} body = [{‘op’: ‘remove’, ‘path’: ‘/dependencies/0’}]

此请求从制品的 dependencies 属性中删除第一个元素。请注意,dependencies 不是保留字,而是 ArtifactReferences 列表的自定义属性名称。

  • PUT /v2/artifacts/{artifact_type}/{type_version}/{id}/blob 或 POST /v2/artifacts/{artifact_type}/{type_version}/{id}/blob

    content-type ‘application/octet-stream’ body = SOMEDATA

此请求将请求主体中指定的数据上传到 BinaryObject 属性 blob

  • GET /v2/artifacts/{artifact_type}/{id}/blob/download

此请求检索 BinaryObject 属性 blob 的值。

  • DELETE /v2/artifacts/{artifact_type}/{type_version}/{id}/files/{file_id}

安全影响

  • 制品类型(它们的特定于类型的元数据字段和 BLOB 类型)在独立的 python 模块(插件)中定义。此代码可能由第三方开发人员提供,因此必须检查是否存在潜在的安全问题。但是,需要明确的云操作员操作才能启用这些模块(即,它们不是用户提供的),因此,只要操作员仅启用受信任的插件,就不会存在安全威胁。

  • 制品类型可以定义自定义逻辑来验证其特定于类型的元数据字段的值。此自定义逻辑是插件的一部分(即,需要云操作员的操作才能启用它)。因此,除非操作员启用不受信任的插件,否则这里没有安全问题。

  • 由于此更改添加了一组新的 API,因此还应添加一组新的策略,以提供基于角色的访问控制。稍后可以添加特定于插件的策略。但是,此规范不包含这些策略的任何操作,因此必须由后续规范指定它们。

通知影响

稍后将添加不同的规范/蓝图

其他最终用户影响

随着添加新的 API,应修改 python-glanceclient 和 glance CLI 以支持这些交互。

性能影响

新的 API 和数据模型应遵循与现有 API 相同的架构。因此,调用 API 时不应影响性能。

长时间运行的任务(例如制品删除)应异步进行,并应由专用的后台工作程序执行,以免干扰 API 性能。异步任务的实现应重用现有的延迟删除功能。

其他部署者影响

数据库模式更改应通过迁移进行。必须在开始使用新功能之前执行这些迁移。

Glance 部署应包括添加和激活定义制品类型的插件。

开发人员影响

现有 API 未被修改。

所有与制品相关的操作都将作为新的 API 端点在 /v2/ 分支下提供

现有的 images API 将不会迁移到 v2 分支的 artifacts。在实现和稳定 artifacts 后,将存在一种替代的 Images 实现,它将使用 artifacts 的语义而不是当前的语义,但是此实现将不会取代现有的实现

当前 images API 的弃用超出了当前规范的范围,除非提交了单独的蓝图并宣布了适当的弃用周期,否则不应发生。

实现

负责人

主要负责人

ativelkov

其他贡献者

gokrokve mfedosin ivasilevskaya

评审人员

mfedosin jokke hemanth-makkapati nikhil-komawar

工作项

  1. 制品及其通用元数据属性的数据库层和数据迁移

  2. 用于存储和管理特定于类型的元数据字段和 blob 引用的数据库层和数据迁移

  3. 允许指定制品类型及其元数据结构的插件接口。

  4. 用于制品组合、发布、删除和搜索的 REST API,以及检索其数据和元数据

  5. 修改插件接口以允许自定义逻辑定义,用于将制品作为单个操作导入和导出

  6. 支持制品导入/导出操作的 REST API

  7. 用于添加制品依赖关系关系的数据库层和数据迁移

  8. 支持在制品组合期间定义依赖关系关系的 REST API

  9. delayed_delete 功能:修改当前的延迟删除以支持制品删除。

  10. 修改 python-glancecleint 以使用所有新的 API。

依赖项

不需要新的依赖项。

测试

所有新的 API 都应通过功能和集成测试与 Tempest 覆盖

数据迁移和数据库 API 应通过单元测试覆盖。

文档影响

所有新的 API、配置选项和策略都应记录在案。

必须添加一个新的文档 - “插件开发者指南”。

参考资料