RFC-0124 - 分散式产品集成:工件说明和传播

RFC-0124:分散式产品集成:工件说明和传播
状态已接受
区域
  • 常规
说明

一种用于描述和传播工件的机制,以实现分散式产品集成。

Gerrit 更改
作者
审核人
提交日期(年-月-日)2021-03-30
审核日期(年-月-日)2021-08-30

摘要

该 RFC 介绍了一种机制,可用于将 Fuchsia 源代码树或花瓣仓库中的源代码或预构建工件提供给 Fuchsia 的汇编。

该机制包含两个基本方面:

  1. 工件的说明,使用适合在要组装的产品中选择每个工件的兼容或其他理想版本和变体版本的元信息。
  2. 工件的传播,以便从其源头源代码库和其使用目的地集成代码库中识别、引用和访问工件。

设计初衷

分散式产品集成路线图文档中所述,最好在 Fuchsia 源代码树之外组装产品。为此,您需要在 Fuchsia 源代码树之外执行独立映像汇编工具,并为其提供必要的源代码或预构建工件。Fuchsia 源代码树提供 Zircon(Fuchsia 内核)和系统软件包等工件。各种 Petal 代码库为其他组件(例如产品专用组件和运行程序)提供软件包。

Fuchsia 源代码树工件会以定期的版本节奏生成。同时,系统还会生成 Fuchsia SDK 的版本,这些版本会定期由花瓣代码库导入,然后反过来用于在花瓣代码库的版本节奏中生成自己的 Fuchsia 工件。

通常,花瓣代码库的发布节奏与 Fuchsia 源代码树的发布节奏不一致,版本的标识符也不同。花瓣可以选择将其发布节奏与 Fuchsia 保持一致,例如,为 Fuchsia SDK 的每个版本生成一个版本。

每个此类工件版本也可能会以不同的变体形式生成,例如适用于各种处理器架构的可执行文件、调试变体与优化变体、通过“稳定”与“最新”标签指定的变体(用于区分主干版本与稳定分支版本),或启用或停用构建时功能的变体。

为了在与 Fuchsia 源代码树不同的集成代码库中通过此类工件组建产品,这些工件必须可供集成代码库使用。此外,对于产品的每个版本和变体,必须从工件的适当版本和变体中选择产品集成所需的工件。

产品集成的当前流程称为“全球集成”。来自 petal 仓库的预构建工件通过一个称为“滚动”的过程导入回 Fuchsia 源代码树中。然后,将 Fuchsia 源代码树中最新源代码修订版本的所有工件以及已达到集成代码库的固定版本的花瓣代码库中的预构建软件包,组装为最终 build 产品。此流程如图 1 所示。

图 1 - 全球集成流程(图例 当前系统中所有协作部分的关系

此过程有几个缺点:

  • 产品汇编配置存储在 Fuchsia 源代码树中,而不是由
  • Fuchsia 系统的版本始终先于用来构建任何花瓣工件的 Fuchsia SDK 版本,因为 Fuchsia 系统是基于 Fuchsia 源代码树的最新源代码修订版本构建的,而花瓣工件的 SDK 却是根据较早的修订版本构建的。
  • 如果不同花瓣中的工件之间存在版本兼容性要求(例如 Flutter 运行程序和 aot 编译的 Flutter 应用),则只能通过在同一滚动器中专门将它们一起滚动来进行维护。
  • 即使花瓣工件在产品所有者的控制之下,用于产品集成的版本也不会,因为使新版本工件可用于产品集成的滚动过程会与所有其他花瓣工件的所有其他 Roller 协调,并且可能会因任何其他产品的集成测试失败而被阻止。

所提议的分散式产品集成流程对这些缺点进行了改进,因为直接在 Fuchsia 仓库之外启用产品组装:

  • 产品装配配置保存在每个产品的单独集成代码库中,这可由产品所有者控制。
  • 您可以选择与花瓣工件构建时所用的 Fuchsia SDK 兼容的 Fuchsia 工件版本,以便产品的所有工件之间实现 ABI 兼容性。例如,Fuchsia 版本号的第一个组件表示的是与 RFC-0002 中所规定、与所需的 6 周 ABI 兼容性窗口对应的 ABI 修订版本,每次创建 Fuchsia 版本分支时该组件都会递增。因此,可以根据用于构建产品的 SDK 的 Fuchsia 版本版本号的第一个组件,使产品中的所有工件保持一致。
  • 您可以直接选择来自不同 Petal 代码库的工件版本,使其满足相互兼容性要求,并根据直接指定的约束条件升级到下一个版本,而无需为这对工件设置专用滚动器。例如,您可以直接根据相同的 Flutter 和 Dart 版本号选择 Flutter 应用和相应的 Flutter 运行程序。
  • 产品可以按照自己的发布节奏选择所有贡献工件的版本和变体,而不会妨碍其他产品选择该版本。

利益相关方

教员:abarth@google.com

审核者:aaronwood@google.com(汇编)、etryzelaar@google.com(SWD)、wittrock@google.com(SWD)、atyfto@google.com(Fuchsia Build Infrastructure)、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 产品都可以使用更新后的工件版本进行构建和测试,则系统会提交对 Fuchsia 树的更改,并且更新后的工件版本现在是固定的版本。不同的预构建工件可以有单独的 Roller。

在现状下,Fuchsia 源代码树同时充当 Fuchsia 产品中使用的许多工件的源代码库,以及每个 Fuchsia 产品都从其组件组装的集成仓库

工件是指在组装产品时使用的任何成分。此类工件是从 Source Repositories 获取的,可以作为其中一个源代码项直接获取,也可以作为通过在代码库中执行构建流程获得的构建产品获取。大多数此类工件是预构建的 Fuchsia 软件包,但它们还包含 Zircon(Fucsia 内核),并且可能很快还会包含子汇编规范文件。Zircon 内核和预构建软件包是预构建工件;子组件规范文件是源工件。

从一个源代码库获取的工件最终会发布工件存储区。此类版本可以手动发布,也可以按预定的节奏自动发布。无论采用哪种方式,生成的工件都会发布到源代码库的专用 Artifact Store。工件存储区可以是可从源代码库外部找到工件的任何位置,而无需引用源代码库,更不用说执行其构建流程。因此,工件存储区可以是 CIPD 目录、GCS 存储桶或 TUF 代码库。对于此处设计的过程,工件存储区的具体类型并不重要。

TUF 代码库用于向 Fuchsia 设备进行软件交付、向开发者 Fuchsia 设备或模拟器(处于开发阶段)的新重新构建软件包,以及在生产环境中对 Fuchsia 设备的 OTA 更新。在内容寻址存储服务之上,在 Fuchsia 中使用 TUF 的具体方式特别适合存储 Fuchsia 软件包。由于许多工件都是 Fuchsia 软件包,因此在此设计中,最好使用 TUF 仓库作为工件存储区。

属性是键值对属性,用于描述工件,以便区分其版本和变体。键描述可变性的一个维度,而值是工件沿着该维度的位置。属性用于统一描述工件的版本和变体。在大多数情况下,无需区分用于描述版本的属性与用于描述变体的属性。工件的发布商可以指定以下属性:

  • CPU 架构变体,x64arm64
  • debugoptimized 编译变体。
  • 构建工件时所用源代码库的提交版本。
  • 构建工件时使用的构建参数。
  • 工件所用的 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 所示),花瓣构建流程使用 CIPD 目录作为其工件存储区。工件存储区将工件保存在路径名称(在 CIPD 中称为“软件包”,与 Fuchsia 软件包没有直接关系)下。CIPD 支持在“软件包”的同一路径名称下存储多个“实例”,并将“标记”和“引用”附加到实例。变体元信息编码在存储的工件的路径组件中。版本元信息编码在 CIPD 软件包实例的标记和引用中。产品 build 流程会通过滚动获取工件的固定版本,并始终根据固定版本的花瓣工件构建产品。

图 2 - 当前系统结构 当前系统中所有协作部分的关系

通过工件存储空间传播扩展元信息

此处提出的设计更改了工件存储区的结构,以保留已发布工件的元信息属性。然后,产品可以根据以属性值为依据指定的条件选择合适的工件。

该设计涉及三个额外的配置文件,以及三个用于操作配置文件以与工件存储区交互的工具。文件包括 artifact_groups.json(存储在工件存储区中)、artifact_spec.jsonartifact_lock.json(存储在产品集成代码库中)。工具 uploadupdatefetch 会对文件和工件进行操作,如图 3 所示,具体如下:

  1. 对于花瓣或 Fuchsia 构建流程中的一组工件,每个新版本都会在与源代码库关联的工件存储区内创建一个新的工件组。已发布的工件的属性记录在 artifact_groups.json 文件中,该文件也保存在工件存储区中。
    • Petal build 流程使用 upload 工具将新发布的工件发布到工件存储区,并更新 artifact_groups.json 文件。
  2. 在产品集成代码库中,artifact_spec.json 文件会保留从哪些工件存储区获取此集成代码库中产品组件的工件以及使用哪些属性的列表。
    • update 工具用于使用集成代码库中的 artifact_spec.json 文件以及 artifact_spec.json 文件中列出的所有工件存储区中的 artifact_groups.json 文件,选择实际要用于组合的工件集。
  3. update 工具选择的一组工件存储在 artifact_lock.json 文件中。
    • fetch 工具会下载 artifact_lock.json 文件中提及的所有工件,以便将其提供给集成代码库中的商品构建流程。

这些工具与构建规则的执行方式相关,具体取决于产品集成代码库中使用的特定构建系统。如需了解详情,请参阅实现部分。

这些工具允许在集成代码库中存在多个 artifact_spec.jsonartifact_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 root。某些工件(尤其是 Fuchsia 软件包)由进一步的存储组件(“blob”)表示,这些组件以传递方式包含在工件的直接内容中。这些附加组件存储在其内容地址下,但未在 artifact_groups.json 中明确提及。

唯一性不变量

Artifact Store 中需要维护一些唯一性不变性。这些不变项可确保通过引用工件的名称和属性来明确选择工件:

  1. 组名称在工件存储区内必须是唯一的,并且在整个历史记录中都保持不变。
  2. 工件名称在每个组中都必须是唯一的。
  3. 一个组中每个工件的属性在工件存储区中当前所有群组的同名所有工件中必须是唯一的。

当向工件存储区添加新的工件组时,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 都会记录为一个新组。因此,在概念上,组名称、其中的一组工件及其大多数属性都是不可变的。例外情况是指反映工件发布后工件相关信息的属性。例如,在工件发布后对其进行手动测试,从而更改工件的测试结果状态。可以更新反映此类信息的属性。

不过,原则上,没有任何因素会阻止工件组内容以及名称或属性随时间的推移而发生变化。可以想象,这在产品组装流水线中会很有用,但在我们提出的应用中不会用到它(请参阅下文中的实现部分)。

产品构建流程

集成代码库中的商品构建流程会建立一个 artifact_spec.json 文件(下文中称为“规范文件”)。此文件列出了产品在集成代码库中组装时所需的工件、用于获取这些工件的工件存储库,以及工件属性的模式和约束条件。还描述了工件存储区的类型,并且一个规范文件中可以同时存在多种不同类型的工件存储区。

具体而言,预构建工件可以存储在集成代码库本身中,并且可以在规范文件中作为类型为“local”的工件存储区进行引用。

集成代码库中的产品构建流程采用 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 文件中选择的工件方面是密封且可重现的。

实现

此处提出的设计将首先实现将工作站产品集成移至单独的代码库

另一个 RFC 中介绍了 artifact_spec.json 的必要详细信息,以及如何对 artifact_groups.json 文件进行处理以生成 artifact_lock.json 文件。

迁移现有花瓣和产品

只要 Fuchsia 源代码树(通过 integration.git 设置)仍需要 petal 预构建文件,就需要将其上传到 CIPD,以供 Fuchsia 源代码树中的 gn build 规则使用,并上传到外部产品使用的工件存储区。最终,Fuchsia 源代码树中将不再需要某些花瓣预构建文件,并且可以停止上传到 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 的内容地址(表示为 Merkle 根)。meta.far 中引用的 blob 列表使用 Fuchsia SDK 提供的工具获取,因此工件存储工具(上传、更新、提取)不依赖于 meta.far 的内部结构。

工具与 build 规则的关系

uploadupdatefetch 工具与 build 规则的执行方式相关,具体取决于源代码库和产品集成代码库中的特定 build 系统,以及 build 的执行基础架构。

upload 工具应在构建系统完成构建后执行。

通常,update 工具应在构建规则之外使用,并且在构建执行之前使用。

fetch 工具可以在构建规则执行之前使用,也可以在构建系统执行过程中使用(如果构建系统支持)。对于工作站,bazel 用作构建系统,该系统支持在所谓的“工作区规则”中设置输入文件集。

工具可执行文件的名称

uploadupdatefetch 工具最终可能会使用此设计中使用的概念名称以外的其他名称实现。具体而言,它们可以作为 ffx 工具的插件实现,也可以作为 bazel build 规则生成的工具实现。初始原型将它们实现为名为 artifact_upload.pyartifact_update.pyartifact_fetch.py 的 Python 脚本。

文件语法

artifact_groups.json 文件的语法由以下 JSON 架构给出。artifact_spec.jsonartifact_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.jsonartifact_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 作为工件存储区的做法一样,产品构建流程需要信任工件存储区。这种信任目前基于工件存储区对 Petal 和 Fuchsia 构建流程(工件从中提供给存储区)应用的访问控制和身份验证,以及依赖于构建基础架构和环境来解析工件存储区的配置名称以访问正确的工件存储区。

建立更多工件存储库和产品集成代码库后,需要评估对它们的信任依据,并相应地评估组合产品的可信度。

未来,我们可能会在目前使用的机制之外建立其他机制,例如验证签名(例如 TUF 代码库中的签名)。此处设计的机制可以支持传播相关信息,以便据此评估可信度,但此类信息的创建是相互独立的。

具体而言,关于 TUF 签名,请务必注意,此类签名仅提供证据来证明托管工件的代码库的可信性。它本身并不能构成评估从中获取的工件的可信性的完整依据,因为这还取决于签名密钥的控制、生成工件的构建流程的可信性,以及构建流程用作输入的传递来源和工具的可信性。对于目前用于 Fuchsia 产品的 TUF 实现(用于对上传到维护代码库的任何工件进行签名),还取决于 ACL 的完整性以及允许客户端访问该服务的身份验证机制。

多个花瓣 build 进程发布到同一工件存储区

建议发布源代码库不要共享工件存储区,这样无论它们是如何建立的,都不必与服务器共享信任关系。例如,在实现 TUF 时,发布商无需共享签名密钥。

发布到同一工件存储区的多个花瓣构建流程也可能会导致潜在危害。例如,一个发布者可能会覆盖另一个发布者发布的工件。但这超出了本 RFC 的范围,因为在本 RFC 中,发布到工件存储区的所有花瓣构建流程都由同一组织运营,因此可以得到妥善协调。如果我们想将其扩展到多个组织,则需要设计不同的协调模型。

性能

随着 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 代码库。

可以在所有这些工件存储区内创建适当的结构来定位工件(位于其内容地址下),并且可以在 artifact_groups.json 文件中维护元数据,这与上述实现中使用的 TUF 仓库中的方式相同。

每个版本的 TUF 代码库

此设计的早期版本中采用的另一种替代方案是为每个工件版本创建一个 TUF 仓库。upload 工具不会为版本的所有工件创建一个组,而是会创建一个新的 TUF 代码库来托管工件。一个知名的查找代码库包含记录属性的 artifact_groups.json 文件。它将包含每个工件组的代码库名称,而不是组名称。

此方法的优势在于支持使用仅在首次使用时分发给设备的临时软件包的产品。我们可能会将仓库作为软件包来源添加到产品中,用于在运行时解析软件包网址,而不是或除了在产品组合中包含此类发布仓库中的软件包之外。

我们之所以拒绝这种设计用途,是因为目前还没有此类产品,并且设备端和 TUF 基础架构中当前的软件分发机制虽然是为此类用途而设计的,但从未被证明能够以这种方式在生产环境中使用,并且在这种方式可行之前,这些机制预计还需要进一步发展。因此,我们非常希望避免将自己锁定在当前的软件交付状态。同样,TUF 基础架构不相信能够很好地处理大量 TUF 代码库。

TUF 代码库的 targets.json 中列出的所有工件

最初的设想是,所有工件(尤其是所有 Fuchsia 软件包)都将列在 TUF 代码库的 targets.json 文件中,并且用于在一个版本中对工件进行分组的目录是包含 TUF 代码库目标的实际目录。该信息与 artifact_groups.json 中已记录的信息重复,并且没有目的,因此为清晰起见,我们删除了该信息,并规避对 targets.json 施加的大小限制,此限制并非仅应用于 targets.json 中列出的文件。

未知

目前,关于所提交设计的可能应用扩展的问题仍未得到解答。

  • 此机制是否也适用于 Fuchsia SDK?与预构建工件是创建产品的前提条件一样,Fuchsia SDK 也是创建花瓣组件和 out of tree 产品的前提工件,并且需要根据描述这两者的属性,根据变体和版本进行类似的选择。因此,问题在于花瓣构建流程是否应在 artifact_spec.json 文件中声明其对 SDK 的使用,以及产品构建流程是否应在其 artifact_spec.json 文件中包含 Fuchsia SDK。
  • 此机制是否也可应用于花瓣 SDK,即一个花瓣提供给其他花瓣的 SDK(例如 Flutter SDK)?这并非实现分散式产品集成的必要条件,但可以简化产生的分布式构建流程。例如,通过检查 petal 代码库的锁定文件中提取的 Flutter SDK 版本,上传工具可以确定上传工件的正确 Flutter SDK 版本属性值。
  • 此机制还可用于发布构建的产品吗?在工作站实现中,Fuchsia 工作站产品可以发布到工件存储区。此上传的商品稍后可用于差异化组装。
  • 描述机制能否用于递归地为创建产品所涉及的所有二进制工件建立二进制透明度,从产品中包含的预构建工件、用于组装产品的工具的预构建工件、用于创建预构建工件的工具等开始。

文档

本文档可用作初始文档。工具中会提供相应的帮助页面工作站代码库设置将作为蓝图和演示,以供日后使用。

在先技术和参考文档

其他操作系统的产品集成通常通过对预构建的应用较少的源代码树进行,因此在代码库之间并发传播此类预构建的多个变体和版本的要求较少。

构建系统只需在当前版本中根据当前应用的 build 参数确定的变体中重新构建依赖项,即可“选择”依赖项的兼容“版本”。这里介绍的设计可以理解为将构建系统的操作方式推广为异步和分布式。

附录

图 4 - 全球集成图例 “全球集成”图的图例