| RFC-0124:分散式产品集成:制品说明和传播 | |
|---|---|
| 状态 | 已接受 |
| 区域 |
|
| 说明 | 一种用于描述和传播分布式产品集成工件的机制。 |
| Gerrit 更改 | |
| 作者 | |
| 审核人 | |
| 提交日期(年-月-日) | 2021-03-30 |
| 审核日期(年-月-日) | 2021-08-30 |
摘要
此 RFC 描述了一种机制,用于使来自 Fuchsia 源代码树或 petal 代码库的源代码或预构建制品可用于组装 Fuchsia 源代码树之外的 Fuchsia 产品。
该机制包含两个重要方面:
- 使用元信息对工件进行说明,以便选择每个工件的兼容版本或理想版本和变体,将其组装到产品中。
- 工件的传播,以便可以从其来源代码库和使用它们的集成代码库中识别、引用和访问这些工件。
设计初衷
如分散式产品集成路线图文档中所述,我们希望在 Fuchsia 源代码树之外组装产品。为此,必须在 Fuchsia 源代码树之外执行独立映像组装工具,并向其提供必要的源代码或预构建工件。Fuchsia 源代码树提供 Zircon(Fuchsia 内核)和系统软件包等制品。各种 petal 代码库为其他组件(例如特定于产品的组件和 runner)提供软件包。
Fuchsia 源代码树制品以常规版本节奏生成。与此同时,Fuchsia SDK 的版本也会生成,这些版本会定期由 petal 代码库导入,并用于按照 petal 代码库的版本节奏生成自己的 Fuchsia 制品。
一般来说,petal 代码库的发布节奏与 Fuchsia 源代码树的发布节奏并不一致,并且版本标识符也不同。花瓣可以选择使其发布节奏与 Fuchsia 保持一致,例如,针对 Fuchsia SDK 的每个版本都生成一个版本。
每个此类制品版本也可能会以不同的变体形式生成,例如适用于各种处理器架构的可执行文件、调试变体与优化变体、通过某些“稳定版”与“最新版”标签指定的变体(用于区分主干版本与稳定分支版本),或者启用或停用 build 时功能的变体。
为了在与 Fuchsia 源代码树分开的集成代码库中从这些制品组装产品,必须使制品可供集成代码库使用。此外,产品集成所需的制品必须针对产品的每个版本和变体,在制品的相应版本和变体中进行选择。
目前的产品集成流程称为“全球集成”。来自 petal 代码库的预构建制品会导入回 Fuchsia 源代码树中,这个过程称为滚动。然后,将 Fuchsia 产品组装为最终 build 产品,这些产品包含最新源代码修订版本中 Fuchsia 源代码树的所有制品,以及已到达集成代码库的固定版本中来自 petal 代码库的预构建软件包。此流程如图 1 所示。
图 1 - 全球集成流程(图例)

此流程存在以下几个缺点:
- 产品组装配置保留在 Fuchsia 源代码树中,而不是由 <0x
- Fuchsia 系统的版本始终高于任何 petal 制品所用的 Fuchsia SDK 的版本,因为 Fuchsia 系统是从 Fuchsia 源代码树的最新源代码修订版本构建的,但 petal 制品的 SDK 是从较早的修订版本构建的。
- 如果来自不同花瓣的制品彼此之间存在版本兼容性要求(例如 Flutter Runner 和 AOT 编译的 Flutter 应用),则只能通过在同一滚动器中专门一起滚动它们来维护这些要求。
- 即使花瓣制品受产品所有者的控制,但用于产品集成的版本不受控制,因为使制品的新版本可用于产品集成的滚动流程会与其他所有花瓣制品的所有其他滚动者协调,并且可能会因任何其他产品的集成测试失败而受阻。
提议的去中心化产品集成流程通过在 Fuchsia 代码库之外实现产品组装,直接克服了这些缺点:
- 产品组装配置会保存在每个产品的单独集成代码库中,并且可以由产品所有者控制。
- 可以选择与构建 petal 制品的 Fuchsia SDK 兼容的版本,以实现产品的所有制品之间的 ABI 兼容性。例如,RFC-0002 中规定的类似 ABI 修订版本的内容,以及与所需的 6 周 ABI 兼容性窗口相对应的版本,由 Fuchsia 发布版本号的第一个组成部分表示,该组成部分会在每次创建 Fuchsia 发布分支时递增。这样一来,就可以将产品中的所有制品与构建它们所用的 SDK 的 Fuchsia 发布版本号的第一个组成部分保持一致。
- 可以直接选择来自不同 petal 代码库的制品版本,使其满足相互兼容性要求,并根据直接指定的限制条件升级到下一个版本,而无需为这对制品设置专用滚动器。例如,可以直接选择具有相同 Flutter 和 Dart 版本号的 Flutter 应用和相应的 Flutter Runner。
- 产品可以按照自己的发布节奏选择所有贡献制品的版本和变体,而不会因其他产品也选择该版本而受到阻碍。
利益相关方
Facilitator: abarth@google.com
审核者:aaronwood@google.com(Assembly)、etryzelaar@google.com(SWD)、wittrock@google.com(SWD)、atyfto@google.com(Fuchsia build 基础架构)、marvinpaul@google.com(TUF 服务器基础架构)、jsankey@google.com(安全性)、enoharemaien@google.com(安全与隐私)、schilit@google.com(相关 RFC 作者)。
咨询对象:软件交付团队、受管理的操作系统团队和基础架构团队的成员。
社会化:除了与咨询团队讨论之外,此 RFC 的草稿还发送到了 FEC 讨论邮寄名单以征求意见。
设计
在介绍设计概览之前,先介绍或阐明一些术语和概念。
术语
在以下段落中,粗体术语是指其含义由周围的文字解释的术语。
Fuchsia 源代码树包含多个不同的 Git 代码库,这些代码库通过一个名为 jiri 的工具根据保存在名为 integration.git 的 Git 代码库中的配置一起配置到同一个源代码树中。在这些 Git 代码库中,最重要的一个是 fuchsia.git,但还有许多其他代码库,包括第三方代码库。Jiri 配置还可控制将预构建的制品纳入到源代码树的专用部分中。预建的制品本身存储在名为 CIPD 的存储服务中,并受版本控制;而用于描述 CIPD 中预建制品的位置和修订版本的 jiri 配置则在 integration.git 代码库中受 Git 修订版本控制。
一个定期运行的构建作业(称为 Roller)会尝试更新 Fuchsia 源代码树中包含的预构建制品的版本。如果 Fuchsia 树的所有所需 build 产品都可以使用更新后的制品版本进行 build 和测试,则会将对 Fuchsia 树的更改提交,并且更新后的制品版本现在是已固定的版本。不同的预构建制品可以有单独的滚动器。
在现有状态下,Fuchsia 源代码树同时充当 Fuchsia 产品中使用的许多制品(工件)的源代码库和集成库,每个 Fuchsia 产品都从其各个部分组装而成。
制品是指用于组装产品的任何成分。此类制品是从源代码库中获取的,可以直接作为源代码项之一获取,也可以作为在代码库中执行构建流程后获得的构建产品获取。大多数此类制品都是预构建的 Fuchsia 软件包,但它们也包含 Fuchsia 内核 Zircon,并且可能很快还会包含子组件规范文件。Zircon 内核和预构建软件包是预构建的制品;子组件规范文件是源制品。
从一个源代码库获得的制品最终会发布到制品存储区。此类发布可以手动进行,也可以按预定的频次自动进行。无论哪种方式,生成的工件都会发布到源代码库的专用制品商店。制品存储区可以是任何可以从其源代码库外部找到制品的位置,而无需引用源代码库,更不用说执行其构建流程。因此,制品存储区可以是 CIPD 目录、GCS 存储分区或 TUF 代码库等。对于此处设计的流程,特定类型的制品存储区并非必需。
TUF 代码库用于向 Fuchsia 设备交付软件,包括向开发中的开发者 Fuchsia 设备或模拟器交付新重建的软件包,以及向生产中的 Fuchsia 设备交付 OTA 更新。在 Fuchsia 中使用 TUF 的特定方式(基于内容寻址的存储服务)尤其适合存储 Fuchsia 软件包,并且由于许多制品都是 Fuchsia 软件包,因此此设计倾向于使用 TUF 代码库作为制品存储区。
属性是描述制品(以便区分其版本和变体)的键值对属性。键描述了变异性的维度,值是制品在该维度上的位置。属性统一描述了制品的所有版本和变体。在大多数情况下,无需区分描述版本的属性和描述变体的属性。制品发布者可以指定以下属性:
- CPU 架构变体,
x64与arm64。 debug与optimized编译变体。- 构建相应制品所用的源代码库的提交版本。
- 构建相应制品时所用的 build 参数。
- 相应制品所用的 Fuchsia SDK 版本。
- 已构建制品的语义版本。例如,Chromium 发布版本(如
M88.xxx)或 Cast 发布版本(如1.56.xxx)。 - Fuchsia 系统制品 RFC-0002 中规定的 API 级别和 ABI 修订版本(一旦实现)。
- 花瓣提供的 API 级别或 SDK 版本,虽然不在 RFC-0002 的范围内,但与系统 ABI 一样重要。例如,Flutter 应用所用的 Flutter SDK 的发布版本。
传递属性:某些属性描述了工件的传递属性,因为它们与构建工件所用的输入的属性有关。例如,Fuchsia SDK 版本或 Flutter 版本实际上是用于构建所述制品的前身制品的属性。预计此类传递性属性会进一步扩展。因此,属性值可以是包含键值对的对象,并且可以递归。
依赖项属性:工件的依赖项(包括其版本和变体)也可以表示为属性。不过,Fuchsia 软件包是密封的,因此已包含其依赖项。因此,依赖项通常不会经常出现在 Fuchsia 制品的属性中。
现状
在当前的全局集成流程中,如图 2 所示,花瓣 build 流程使用 CIPD 目录作为其 制品存储区。制品存储区会将制品保存在路径名称下(在 CIPD 中称为“软件包”,与 Fuchsia 软件包没有直接关系)。CIPD 支持在同一“软件包”路径名称下存储多个“实例”,并支持将“标记”和“引用”附加到实例。变体元信息编码在存储的制品的路径名组件中。版本元信息编码在 CIPD 软件包实例的标记和引用中。产品 build 流程通过滚动获取工件的固定版本,并始终从 petal 工件的固定版本构建产品。
图 2 - 当前系统结构

通过工件存储空间传播扩展的元信息
此处提出的设计会更改制品存储区的结构,以便保留已发布制品的元信息属性。然后,产品可以根据以属性值形式指定的条件选择合适的制品。
该设计涉及三个额外的配置文件,以及三个用于操作配置文件以与制品存储区交互的工具。这些文件包括:artifact_groups.json(保存在制品存储区中);artifact_spec.json 和 artifact_lock.json(保存在产品集成代码库中)。工具 upload、update 和 fetch 可对文件和制品进行操作,如图 3 所示,如下所示:
- 每次从 petal 或 Fuchsia Build Process 发布一组新的制品时,都会在与源代码库关联的制品存储区内创建一个新的制品组。已发布工件的属性记录在文件
artifact_groups.json中,该文件也保存在工件存储区中。- Petal build 流程使用
upload工具将新发布的制品发布到制品存储区,并更新artifact_groups.json文件。
- Petal build 流程使用
- 在产品集成代码库中,
artifact_spec.json文件会保存以下列表:要从中获取制品以在此集成代码库中组装产品的制品存储区,以及要使用的属性。update工具用于选择实际用于程序集的工件集,它会使用集成代码库中的artifact_spec.json文件以及artifact_spec.json文件中列出的所有制品商店中的artifact_groups.json文件。
update工具选择的一组制品存储在artifact_lock.json文件中。fetch工具会下载artifact_lock.json文件中提及的所有制品,以便在集成代码库中提供给产品 build 流程。
这些工具与 build 规则执行之间的关系取决于产品集成代码库中使用的特定 build 系统。如需了解详情,请参阅实现部分。
这些工具允许集成代码库中存在多个 artifact_spec.json 和 artifact_lock.json 文件。
图 3 - 所有协同工作部件的关系

制品存储区内的逻辑结构
工件商店中工件和工件组的最终逻辑结构如下例所示:
└── artifact_groups
|
├── 92d483e5-ac7d-4029-a7db-e2ee6a8365c7
| |
| ├── web_engine
| |
| └── cast_runner
|
└── c907ff3f-cb15-4a7f-bb79-8cc23c0ff445
|
├── web_engine
|
└── cast_runner
一组制品的每个版本都以不透明的名称显示,而每个制品都以可识别的名称显示为组中的条目。
此结构与发布版本和各个制品的相关属性一起记录在 artifact_groups.json 文件中,以 JSON 数据结构的形式表示,详见下文。
制品和群组名称的用途
JSON 数据使用组名称和制品名称将记录的属性与它们描述的制品相关联。这两个组和制品名称都用于在表达式中引用它们,这些表达式会将制品选择到产品组装工作区中(在文件 artifact_spec.json 中)。制品名称也用作 fetch 工具存储其本地下载副本的文件名。
不过,这两个名称均不用于指定制品存储区中制品的存储位置。相反,所有制品都存储在其内容地址下,并且每个制品的内容地址都记录在 artifact_groups.json 中,作为 merkle 属性。
所使用的内容地址是相应制品直接内容的 Fuchsia Merkle 根。某些制品(尤其是 Fuchsia 软件包)由进一步的存储组件(“blob”)表示,这些组件以传递方式包含在制品的直接内容中。这些额外的组件存储在其内容地址下,但未在 artifact_groups.json 中明确提及。
唯一性不变量
Artifact Store 中需要保持一些唯一性不变量。这些不变量可确保通过引用制品名称和属性来明确选择制品:
- 组名称在整个历史记录中必须在制品存储区内保持唯一。
- 制品名称在每个组中必须是唯一的。
- 一个组中每个制品的属性在制品商店中当前所有组的所有同名制品中必须是唯一的。
当向制品存储区添加新的制品组时,upload 工具会维护这些唯一性不变量。update 工具还会检查唯一性,如果其访问的所有 artifact_group.json 文件不满足不变量,则拒绝计算更新。工件存储本身无需在维护这些不变量方面发挥任何作用。
群组名称是不透明的
您可能会想根据属性值来命名制品组,以便让制品存储区更易于理解或浏览。不过,只有当属性较少且属性集固定时,此方法才能很好地发挥作用。一旦属性集扩展到许多属性,并且是开放式的,那么仅从属性值的一组排列组合(即,架构是第一个、最后一个还是中间一个)来看,可能出现的排列组合数量就会呈指数级增长,因此仅从这些属性构建名称就会变得非常繁琐。此外,阅读此类名称不再轻松,维护唯一性不变量(尤其是在整个历史记录中)也更加麻烦。
因此,由于此设计专门旨在支持大型开放式属性集,因此将选择不透明的组名称,并满足所需的唯一性不变量。在上面的示例中,组名称是随机 UUID。另一种方法是使用单调递增的填充整数。
artifact_groups.json 文件
artifact_groups.json 文件列出了相应制品商店中的所有群组、所含制品和关联属性。
以下示例展示了与上述制品存储区示例对应的 artifact_groups.json。属性与制品组相关联,在这种情况下,所有属性都适用于组中的所有制品。属性也可以仅应用于单个制品。
此设计本身并未规定这组特性。所有属性均由构建和发布流程选择并分配,该流程会在源代码库中生成制品。分配的属性仅需满足上述唯一性不变量。
{
"schema_version": "https://fuchsia.dev/schemas/artifact_groups_schema.json",
"version": 15,
"artifact_groups": [
{
"name": "92d483e5-ac7d-4029-a7db-e2ee6a8365c7",
"attributes": {
"petal": "chromium.org",
"version": "chrominum_release_20210304",
"architecture": "arm64",
"sdk_version": "2.20210303.3.1",
"creation_time": "1622696983",
"commit": "b26e44e9910608a2d0aec9b38e003a04a2da06df"
},
"artifacts": [
{
"name": "web_engine",
"merkle":"90f67b10ded655852acb78a852ac5451486fc1e7378ce53368386244ce8f6e66",
"type": "package",
"attributes": {
"runner_version": "2.20210301.1.3"
}
},
{
"name": "cast_runner",
"merkle": "3394db36d228f4c719d055c394938c5a881ca6eea7ad3af0ad342e764cadc8b3",
"type": "package"
}
]
},
{
"name": "c907ff3f-cb15-4a7f-bb79-8cc23c0ff445",
"attributes": {
"petal": "chromium.org",
"version": "chrominum_release_20210402",
"architecture": "arm64",
"sdk_version": "2.20210303.3.4",
"creation_time": "1622157425",
"commit": "2dd76ad2298dfb869ef83c10b84b62485dc8a573"
},
"artifacts": [
{
"name": "web_engine",
"merkle": "acb78a852ac5451486fc1e7378ce53368386244ce8f6e6690f67b10ded655852",
"type": "package",
"attributes": {
"runner_version": "2.20210225.1.4"
}
},
{
"name": "cast_runner",
"merkle": "19d055c394938c5a881ca6eea7ad3af0ad342e764cadc8b33394db36d228f4c7",
"type": "package"
}
]
}
]
}
artifact_groups 列表中的每个组对象都包含 3 个属性:name 是组的名称,artifacts 是组中制品的列表,attributes 是组中所有制品共有的属性。
仅适用于单个制品的相关属性会直接包含在表示该制品的相应对象中。此制品的相关属性会附加到相应组的属性中,以获得完整的制品属性集。
版本号是一个单调递增的数字。update 工具使用此数字来防止意外回滚到旧版 artifact_groups.json 文件。
制品组及其属性的可变性
在典型使用场景中,一组制品的每个已发布 build 都会记录为一个新组。因此,从概念上讲,群组名称、其中的制品集及其大多数属性都是不可变的。例外情况是反映制品发布后出现的制品相关信息的属性。例如,在制品发布后,手动测试制品时,制品测试结果状态会发生变化。反映此类信息的属性可能会更新。
不过,原则上,没有任何因素会阻止制品组的内容以及名称或属性随时间变化。可以想象,这在产品组装流水线中可能很有用,但在所提出的应用中不会使用它(请参阅下面的实现部分)。
产品构建流程
集成代码库中的产品 build 流程会建立一个 artifact_spec.json 文件(以下称为“规范文件”)。此文件列出了产品在集成代码库中组装所需的制品、获取这些制品的制品存储区,以及制品属性的模式和限制。此外,还描述了制品存储区的类型,并且一个规范文件中可以同时存在多种不同类型的制品存储区。
具体而言,预构建的制品可以存储在集成代码库本身中,并且可以在规范文件中引用,就像从类型为“local”的制品存储区中引用一样。
集成代码库中的产品 build 流程使用 update 工具来匹配规范文件中提及的所有制品商店中的 artifact_groups.json 文件,并计算要使用的一组特定制品变体和版本。此集合记录在 artifact_lock.json 文件中。此文件应作为源代码文件提交到代码库中。
fetch 工具会读取 artifact_lock.json 文件,并将所有制品下载到集成代码库。这些制品不应作为源文件提交到代码库。不过,工件在 artifact_lock.json 文件中是通过从 artifact_groups.json 文件中获取的内容地址来标识的。这样可确保 artifact_lock.json 的已提交内容完全决定了提供给 build 的工件的内容,从而确保产品组装 build 在 artifact_lock.json 文件中选择的工件方面是密封且可重现的。
实现
此处提出的设计将首先用于将工作站产品集成移至单独的代码库。
artifact_spec.json 的所需详细信息以及如何针对 artifact_groups.json 文件处理 artifact_spec.json 以生成 artifact_lock.json 文件,请参阅另一篇 RFC。
迁移现有功能块和产品
只要 Fuchsia 源代码树(从 integration.git 设置)中仍需要 petal 预构建文件,就必须将它们上传到 CIPD(供 Fuchsia 源代码树中的 GN build 规则使用)和树外产品使用的制品存储区。最终,Fuchsia 源代码树中将不再需要某些 petal 预构建版本,并且可以停止上传到 CIPD。
我们无意替换使用 jiri 设置 Fuchsia 源代码树本身的机制,因为所有 Fuchsia 平台制品以及 SDK 和某些产品都将继续从 Fuchsia 源代码树构建。
制品存储区
工作站及其花瓣的实现将使用 TUF 代码库作为制品存储区。TUF 代码库的 targets.json 文件中只有一个条目,即 artifact_groups.json 文件的条目。工件本身存储在用于 Fuchsia 的 TUF 服务器基础架构维护的按内容寻址的 blob 存储区中,其对应的 blob 地址记录在 artifact_groups.json 文件中。
此外,工件也可以列在 targets.json 中,位于目录和工件名称下,但此设计中没有任何内容要求这样做。(之所以提及这一点,只是为了保持历史完整性,因为过去确实会这样做,以防有人期望看到 targets.json 条目。请参阅下文中的替代方案部分。)
Fuchsia 软件包结构依赖项
此处提出的三个与制品存储区交互的工具(上传、更新、提取)均针对 Fuchsia 软件包运行,因此会假设 Fuchsia 软件包的结构。具体而言,假设 Fuchsia 软件包由一个 meta.far blob 组成,该 blob 包含也属于该软件包的其他 blob 的内容地址(以 Merkle 根的形式表示)。meta.far 中引用的 blob 列表是通过 Fuchsia SDK 提供的工具获取的,因此制品存储工具(上传、更新、提取)不依赖于 meta.far 的内部结构。
工具与 build 规则的关系
upload、update 和 fetch 工具与 build 规则执行之间的关系取决于源代码库和产品集成代码库中的特定 build 系统,以及 build 执行所依赖的基础设施。
upload 工具应在 build 系统完成 build 后执行。
通常,update 工具应在构建规则之外使用,在执行构建之前使用。
fetch 工具可以在执行 build 规则之前使用,也可以在 build 系统支持的情况下作为 build 系统执行的一部分使用。对于工作站,bazel 用作 build 系统,支持在所谓的“工作区规则”中设置输入文件集。
工具可执行文件的名称
工具 upload、update、fetch 最终可能会以不同于本设计中使用的概念名称的其他名称实现。具体来说,它们可以实现为 ffx 工具的插件,也可以实现为由 bazel 构建规则生成的工具。初始原型将它们实现为名为 artifact_upload.py、artifact_update.py 和 artifact_fetch.py 的 Python 脚本。
文件语法
artifact_groups.json 文件的语法由以下 JSON 架构给出。artifact_spec.json 和 artifact_lock.json 文件的 JSON 架构记录在单独的 RFC 中。
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "https://fuchsia.dev/schemas/artifact_groups_schema.json",
"type": "object",
"additionalProperties": false,
"required": [
"contents",
"schema_version",
"version"
],
"properties": {
"schema_version": {
"type": "string"
},
"version": {
"type": "integer"
},
"artifact_groups": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string"
},
"attributes": {
"type": "object"
},
"artifacts": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"merkle",
"type",
],
"properties": {
"name": {
"type": "string"
},
"merkle": {
"type": "string"
},
"attributes": {
"type": "object"
},
"type": {
"type": "string"
}
}
}
}
}
}
}
}
}
文件语义
有关 artifact_groups.json 的文件格式语义,请参阅此处。artifact_spec.json 和 artifact_lock.json 的详细语义再次记录在单独的 RFC 中。
{
"schema_version": SCHEMA_VERSION,
"version": VERSION,
"artifact_groups": [
ARTIFACT_GROUP,
...
]
}
SCHEMA_VERSION
一个字符串,用于指明
artifact_groups.json的 JSON 架构的网址。
版本
一个整数,用于指明
artifact_groups.json的版本号。 发布新版本时,此数字应增加。
ARTIFACT_GROUP
每个 ARTIFACT_GROUP 都是一个对象,其格式如下:
{
"name": NAME,
"attributes": ATTRIBUTES,
"artifacts": [
ARTIFACT,
...
]
}
姓名
用于提供引用相应制品组的方法的字符串。
属性。
由发布商定义的对象。
制品
每个 ARTIFACT 都是一个对象,其格式如下:
{
"name": NAME,
"merkle": MERKLE,
"type": TYPE,
"attributes": ATTRIBUTES
},
姓名
包含相应制品名称的字符串。
MERKLE
相应制品的 Merkle 根的字符串。
类型
一个字符串,包含相应制品类型。如果
TYPE为"package",则由其 Merkle 根引用的 blob 中的相应制品是 Fuchsia 软件包的meta.far,并且在meta.far中以传递方式引用的 blob 始终与 meta.far blob 一起处理。否则,制品仅包含列出 Merkle 的单个 blob。
属性
与上面定义的 ATTRIBUTES 类似的对象。用于定义制品特有的属性。
安全注意事项
此 RFC 中的设计不会改变现有参与者之间的信任模型。此机制仅更改存储结构,不会更改使用制品存储区的行为者之间或行为者与制品存储区之间的信任关系。
信任模型
与使用 CIPD 作为制品存储区的现状一样,产品 build 流程需要信任制品存储区。此信任目前基于以下因素:制品存储区对 petal 和 Fuchsia build 流程应用的访问权限控制和身份验证(制品从这些流程提供给存储区);依赖 build 基础架构和环境来解析制品存储区的配置名称,以访问正确的制品存储区。
一旦建立更多制品存储区和产品集成代码库,就需要评估对它们的信任基础,并相应地评估组装产品的可信度。
未来,我们可能会建立高于当前所用机制的其他机制,例如验证签名(如 TUF 代码库中的签名)。此处设计的机制可以支持相关信息的传播,以便根据这些信息评估可信度,但此类信息的创建是正交的。
关于 TUF 签名,需要特别注意的是,此类签名仅提供证据来支持托管工件的代码库的可信度。它本身并不构成评估从中获取的制品可信度的完整依据,因为这还取决于签名密钥的控制、生成制品构建流程的可信度,以及构建流程用作输入的传递性来源和工具的可信度。对于目前用于 Fuchsia 产品的 TUF 实现(该实现会对上传到维护相应代码库的服务的所有制品进行签名),它还取决于 ACL 的完整性以及允许客户端访问该服务的身份验证机制。
多个 petal build 进程发布到同一制品存储区
建议发布源代码库不共享制品存储区,这样它们就不必与服务器共享信任关系,无论该关系是如何建立的。例如,在 TUF 实现中,发布者不必共享签名密钥。
此外,多个 petal 构建流程发布到同一制品商店也会带来潜在的妥协。例如,一个发布者可能会覆盖另一个发布者发布的制品。不过,这超出了本 RFC 的范围,因为在本 RFC 中,所有发布到制品存储区的 petal build 流程都由同一组织运营,因此可以进行适当的协调。如果我们想将此模式扩展到多个组织,则需要设计不同的协调模式。
性能
随着 artifact_groups.json 文件中商品数量的增加,查找与所有必需属性匹配的组的时间复杂度至少呈线性增长。如果制品之间存在约束,则可能是超线性,因为更新工具需要联接多个 artifact_groups.json 文件才能进行选择。制品发布者可能需要逐出过旧的制品组,以帮助提高客户端更新工具的性能。
选择制品后,大型制品的下载时间可能会很长。这与现状并无不同。与全局集成相比,此处介绍的设计具有以下优势:fetch 工具只需将一个产品的预构建制品下载到任何一个集成代码库。在当前的全局集成中,需要在 jiri update 期间下载所有已知产品的预构建制品。
隐私注意事项
工件存储空间中存储的唯一新数据是 artifact_groups.json 文件。工件商店的发布者有责任不在 artifact_groups.json 文件中发布 PII。
测试
工具使用的库(例如用于将 artifact_spec.json 文件中的属性模式与 artifact_groups.json 文件进行匹配)将通过单元测试进行测试。
对于作为可执行文件的工具的运行,将有从本地保存的黄金文件运行的集成测试。
对于端到端测试,将有一个“Hello World”产品位于自己的代码库中,该产品会持续运行相关机制,并可监控故障。
缺点、替代方案和未知因素
使用其他类型的制品存储区
除了 TUF 之外,还有其他可用的制品存储区。例如:
- CIPD,
- 静态 HTTPS 服务器,
- GCS 存储分区,
- Git 代码库。
可以在所有这些制品存储区内创建适当的结构,以按制品的内容地址查找制品,并且可以像在上述提议的实现中使用的 TUF 代码库中一样,在 artifact_groups.json 文件中维护元数据。
每个版本的 TUF 代码库
此设计早期版本中采用的另一种替代方案是为每个制品版本提供一个 TUF 代码库。upload 工具不会为发布版本的所有制品创建一个组,而是创建一个新的 TUF 制品库来托管制品。一个众所周知的查找代码库包含记录属性的 artifact_groups.json 文件。它将包含每个制品组的制品库名称,而不是组名称。
此方法的优势在于支持使用临时软件包的产品,这些软件包仅在首次使用时才会被交付到设备。除了在产品 build 中包含此类发布版本库中的软件包之外,或者作为补充,该版本库还会作为软件包来源包含在产品中,用于在运行时解析软件包网址。
此设计用途遭拒,因为目前尚不存在此类产品,并且设备上和 TUF 基础架构中的当前软件交付机制虽然是为此类用途而设计的,但从未被证明可以以这种方式在生产环境中使用,预计在可行之前还会进一步发展。因此,我们非常希望避免锁定到当前的软件交付状态。同样,人们认为 TUF 基础架构无法很好地处理大量 TUF 代码库。
TUF 代码库的 targets.json 中列出的所有制品
最初,我们设想所有制品(尤其是所有 Fuchsia 软件包)都将列在 TUF 代码库的 targets.json 文件中,而将制品分组到单个版本中的目录是包含 TUF 代码库目标的实际目录。该信息与 artifact_groups.json 中已记录的信息重复,没有任何用途,因此为了清晰起见,我们将其舍弃,同时也是为了规避对 targets.json 施加的大小限制(该限制不会施加到仅在 targets.json 中列出的文件)。
未知
目前,对于所呈现设计的可能扩展应用,仍存在一些未回答的问题。
- 此机制是否也适用于 Fuchsia SDK?Fuchsia SDK 是创建 petal 组件以及树外产品的必备制品,就像预构建制品是创建产品的必备制品一样,并且需要根据描述变体和版本的属性,以非常相似的方式根据变体和版本进行选择。因此,问题在于,petal 构建流程是否应在
artifact_spec.json文件中声明其对 SDK 的使用,以及产品构建流程是否应在其artifact_spec.json文件中包含 Fuchsia SDK。 - 此机制是否也适用于花瓣 SDK,即一个花瓣提供给其他花瓣的 SDK,例如 Flutter SDK?这并非启用去中心化产品集成所必需,但可以简化最终的分布式 build 流程。例如,通过检查 petal 代码库锁定文件中的已提取 Flutter SDK 版本,上传工具可以确定已上传制品的正确 Flutter SDK 版本属性值。
- 此机制是否也可用于发布已构建的产品?在工作站实现中,Fuchsia 工作站产品可以发布到制品商店。上传的此商品稍后可用于差异组装。
- 描述机制是否可用于为产品创建过程中涉及的所有二进制制品(从产品中包含的预构建制品、用于组装产品的工具的预构建制品、用于创建预构建制品的工具等)以传递方式建立二进制透明度。
文档
本文档是初始文档。届时会有相关工具,并且会提供帮助页面。工作站代码库设置将作为后续使用的蓝图和演示。
在先技术和参考资料
其他操作系统的产品集成通常来自对预建模块使用较少的源代码树,因此在仓库之间并发传播此类预建模块的多个变体和版本的要求也较少。
构建系统通过在当前版本中重新构建依赖项,在由当前应用的 build 参数确定的变体中“选择”依赖项的兼容“版本”。此处介绍的设计可以理解为构建系统在异步和分布式操作方面的泛化。
相关文档
- 分散式产品集成 Fuchsia 路线图条目。
- 平台版本控制 RFC
- 独立映像组装工具 RFC
- 工作站树外 RFC
- 树外 Bazel SDK RFC 草稿
- 分散式产品集成,制品选择规范 RFC 草稿
- 通过子组件组装产品 RFC 草稿
附录
图 4 - 全球集成图例
