RFC-0242:配置功能

RFC-0242:配置功能
状态已接受
区域
  • 组件框架
说明

向组件框架添加配置功能

Gerrit 更改
作者
审核人
提交日期(年-月-日)2024-02-05
审核日期(年-月-日)2024-04-11

摘要

概述组件框架中配置功能的使用情况,以及将添加到 CML 文件以支持这些功能的语法。

需要注意的是,本文档取代了现有技术部分中提及的早期结构化配置 RFC 的部分内容。

设计初衷

Fuchsia 中的组件使用结构化配置来获取配置值。如今,配置值大多是从组件的软件包中加载的。父组件可以在启动动态组件时强制设置配置值,但 Fuchsia 中的大多数组件都是静态的。

添加此功能有多种动机:

  • 允许静态父级配置子级的配置。
  • 将单个配置值路由到多个组件。
  • 从 CML 文件提供配置值。

配置功能的首个客户是系统组装,因为此功能将有助于消除技术债务。之所以存在技术债务,是因为目前除了修改静态组件的软件包之外,没有其他方法可以为静态组件提供配置。系统程序集目前会打开 Fuchsia 软件包,修改其配置文件值,然后重新打包。这是技术债务,因为它会更改软件包的哈希值,并且配置值文件在技术上是组件的“私有”API,不易于发展。

对于无法重建的树外软件包,配置功能提供了一种配置这些组件的方法。目前无法在静态拓扑中修改这些组件的配置。

此功能可能在未来的许多配置用例中发挥作用。

利益相关方

教员:neelsa@google.com

审核者

  • 系统组装:aaronwood@google.com
  • 组件框架:geb@google.com
  • 安全性:markdittmer@google.com
  • 驱动程序框架:surajmalhotra@google.com
  • 组件框架:wittrock@google.com

社会化:在撰写 RFC 之前,已与组件框架团队讨论过此问题。

要求

配置功能可让 CML 作者设置静态组件的配置值。它们允许通过拓扑路由配置值,以便多个组件可以使用相同的值。通过拓扑路由值可让间接祖先指定值,而现有功能仅允许直系父级指定值。

配置功能应尽可能遵循现有的组件框架功能,以便与系统的其余部分保持一致。

设计

设计的大部分内容是用于声明和路由配置功能的 CML 语法。配置功能在 CML 中同时定义了类型和值。配置功能会路由到希望使用它的组件。使用配置功能的组件在通过现有的结构化配置库启动时,将能够看到该值。

以下是定义配置功能的示例:

// Configuration capabilities can be defined in any component.
// When a configuration capability is defined, a value must be set for it.
capabilities: [
  {
    // Define the name of the capability.
    config: "fuchsia.netstack.UseNetstack3",
    // Define the type of the configuration.
    type: "bool",
    // When defining a configuration capability, there *must* be a value.
    // If the route for the configuration capability ends at this definition, the
    // value of the capability will always be this value.
    value: "true"
  },
  // The below shows a string config.
  {
      config: "fuchsia.config.MyString",
      type: "string",
      max_size: 100,
      value: "test",
  },
  // The below shows a vector config.
  {
      config: "fuchsia.config.MyUint8Vector",
      type: "vector",
      element: { type: "uint8" },
      max_count: 100,
      value: [1, 2, 3 ],
  },
]

config 功能中的 typeelementmax_countmax_size 字段与它们在 config 节中的用法具有相同的含义。config stanza 中支持的所有值在 capabilities 块中均受支持。

type 字段支持以下内容:

  • bool
  • uint8、uint16、uint32、uint64
  • int8、int16、int32、int64
  • string
  • 矢量

using 块支持与 capabilities 块相同的 typeelementmax_countmax_size 字段。

以下是使用配置功能的语法示例:

// Using a configuration capability means that this configuration will show
// up in the component's structured config at runtime with the specified `key`
// name.
// NOTE: Using a configuration capability will override the value in the Config
// Value File (CVF) and the value obtained from the "mutability" setting.
use: [
  {
    config: "fuchsia.netstack.LaunchProcess",
    // This is the name that the component will see in its Structured Config
    // struct. If the `use` is optional this must match a name in the "config"
    // block of the component's manifest.
    key: "can_launch_process",
    // The using field needs the type information so that the component's
    // Config Value File format can be created.
    type: "bool",
    // From is identical to other capabilities.
    from: "parent",
  },
  // The below shows a vector config.
  {
    config: "fuchsia.netstack.MyUint8Vector",
    key: "my_vector",
    type: "vector",
    element: { type: "uint8" },
    max_count: 100,
    from: "parent",
  },
  // The below shows a string config.
  {
    config: "fuchsia.netstack.MyString",
    key: "my_string",
    type: "string",
    max_size: 256,
    from: "parent",
  },
]

以下是用于路由配置功能的语法示例:

// Expose and Offer behave the same as any other capability.
expose: [
  {
    config: "fuchsia.netstack.UseNetstack3",
    as: "fuchsia.mycomponent.UseVersion3",
    from: "self",
  },
]
offer: [
  {
    config: "fuchsia.config.MyString",
    as: "fuchsia.mycomponent.ProcessName",
    from: "self",
    to: "#mychild",
   },
]

可选功能

如果某个组件使用的配置功能将可用性设置为 optional,并且该功能正在从 void 进行路由,则该组件将从配置值文件 (CVF) 接收值。此功能旨在支持更新配置的软迁移。

废弃“config”块

在配置功能之前,配置字段是在 CML 的 config stanza 中声明的。此信息现在已替换为 use stanza 中的信息。这意味着,为组件生成的结构化配置架构将是 use 块和 config stanza 中字段声明的并集。

所有结构化配置客户端都迁移到配置功能后,将不再支持 CML 中的配置块。

弃用“mutability: parent”功能

在配置功能推出之前,配置值可以声明为 mutability: parent,这样该组件的父级就可以更改该值。

此功能的用户将从其父级迁移到 use 配置功能。所有用户迁移完毕后,此功能将被移除。

实现

这些 CML 功能的实现相对简单,可以在树中通过几个 CL 完成。客户端可以随时迁移到使用配置功能,因为这不会影响现有的结构化配置功能。

我们需要确保 Scrutiny 了解配置功能,并且可以验证特定产品是否具有给定的配置。

性能

由于组件框架需要对组件使用的配置功能执行路由,因此启动组件可能会略微变慢。 如果功能来自未解析且需要下载的组件,则这可能会非常重要。对于已解析的组件,此值应可忽略不计。

工效学设计

这项新 CML 功能的人体工程学设计与现有功能完全相同。添加和路由配置功能有点冗长,但选择此语法是为了尽可能与现有的功能语法保持一致。在可能的情况下,我们还选择与现有的 config 块语法相匹配的语法。如果这两种语法发生冲突,则优先使用现有的功能语法。与现有语法保持一致可减少熟悉组件框架的用户在认知方面的负担。

CF 团队可能会决定保留 config 块作为声明配置功能的语法糖,或者在需要更好的人体工程学时添加额外的语法糖。

向后兼容性

这是一项新功能。它向后兼容现有的结构化配置功能。

值得注意的是,将配置功能路由到组件始终优先于 CVF 文件中的值。如果组件具有可选的配置功能路由,但该路由不存在,则将使用 CVF 文件中的值。

安全注意事项

此功能不应影响安全性。用于调查 CML 的审查和其他工具将适用于此新功能类型。

有人可能会认为,以这种方式设置功能比在组件的软件包中修改值的现有策略更显眼。

如果某个组件正在使用必需的配置功能,但没有配置功能路由到该组件,则该组件将无法启动。这不应是安全问题,因为组件的父级是可信的,并且这些路由可以静态验证为正确的。

隐私注意事项

此提案应该不会对隐私权产生任何影响。

测试

此功能需要进行单元测试和集成测试。一般测试领域包括:

  • 解析新的 CML 语法。
  • 通过拓扑路由新功能。
  • 尝试启动缺少配置功能的组件。
  • 尝试启动配置功能类型错误的组件。
  • 启动具有配置功能的动态组件。

该功能需要在 Scrutiny 中添加以下测试:

  • 测试 Scrutiny 是否可以解析配置功能的值。
  • 测试不同配置值来源的优先级/覆盖逻辑。
  • 测试配置功能、配置块和可变性之间的互动。

这些测试都不需要新的测试基础设施。

文档

需要更新 Fuchsia.dev,以提供有关配置功能的文档。现有功能文档将用作此新文档的模板。

缺点、替代方案和未知因素

替代方案:实现配置替换服务

最初的结构化配置 RFC 提议直接从组件的软件包中获取结构化配置。 该 RFC 明确指出,它不打算解决“由其他组件(父组件和管理组件除外)设置的配置数据”的问题。一种替代方案是继续不支持基于功能的配置方案。

与替换服务相比,配置功能的缺点之一是配置功能更加冗长和复杂。最初,实现全局功能总是更简单。 不过,Fuchsia 发现,从长远来看,基于功能的系统最终会更具可组合性和可理解性。能够指定特定配置仅供部分组件拓扑使用,或者在两个不同的组件中使用两个不同的值,这非常有用。配置功能使表达更加简单,并体现了 Fuchsia 中其他位置使用的基于功能的系统。使用现有的 CML 语法进行配置可让 Fuchsia 开发者更轻松地理解该功能,并且该功能可与组件框架的现有工具很好地配合使用。

缺点:许多额外的用途字段

use 块中保留类型信息会添加 6 个额外的字段,这些字段仅对配置功能有效。

虽然可以将这 6 个字段合并为一个包含子字段的字段,但添加字段是更好的选择,因为这样可以使语法与现有的 config 块语法保持一致。

缺点:将类型信息与路由混为一谈

将配置架构放在 use 声明中的一个缺点是,use 块定义了类型信息,还定义了路由。某些组件可能希望在不指定路由的情况下定义其配置,然后在不同场景中以不同方式路由其配置。如果类型信息不在 Use 块中,则此操作会更简单。

想要更改类型定义方式的组件作者必须修改其 use 块,而不能仅修改其 config 块。

如果配置定义和路由的组合成为开发者的持续痛点,组件框架团队可能会重新考虑此决定。

替代方案:将配置类型定义保留在 config 块中

一种替代方案是在 use 声明中不包含“type”字段,而是依赖于现有“config”块中的类型信息。此 RFC 的早期版本就是这种情况。此替代方案解决了当前将类型信息与路由混为一谈的缺点。这样一来,客户端可以更轻松地在一个位置定义配置,并在另一个位置路由配置(可能包括不同的 CML 分片,以便根据不同的配置进行不同的路由)。

在当前基于结构化配置的实现中,Component Manager 在启动组件时会向该组件“推送”配置,因此 Component Manager 需要确保路由的功能的类型与目标结构化配置字段的类型相匹配。我们决定将类型信息放在 use 声明中,因为这与功能声明是对称的。当组件管理器执行路由时,需要确保类型信息一致,这意味着从逻辑上讲,信息应包含在路由的两端(功能和使用)中。

use 声明中保留类型信息的另一个原因是,这样一来,就可以将所有信息都保留在 use 声明中,而无需匹配的“config”块条目。这样一来,信息就会保留在 CML 文件中的一个位置,从而更易于审核。

替代方案:use 声明中的 as 字段

通过将结构化配置字段名称和类型信息封装在 config 块中,可以使用 as 来指定提供相应功能的字段。这与 CML 中大多数非 path“重命名”的实现方式一致。

之所以使用 key 一词,是因为该字段的运作方式与其他 as 字段不同。as 通常为可选,但 key 为必需。如果功能是可选的,key 还必须与 config 块中的现有键匹配。

未知:平衡现有结构化配置与功能的不一致性

现有的结构化配置在很多方面与其他功能的工作方式不一致。

一个不一致之处在于,该程序严重依赖于组件清单中定义的结构化配置的格式。通常,是清单依赖于程序,而结构化配置使这种关系成为循环关系。结构化配置 build 规则需要在组件、组件清单和程序之间添加一个额外的层,这体现了结构化配置的这一特点。此循环不一致性不会通过配置功能来解决,但有望在后续 RFC 中得到解决。

另一个不一致之处在于,结构化配置是在组件启动时“推送”到组件的,而其他功能是在组件访问时“拉取”的。此 RFC 也未解决此不一致问题。此 RFC 的目的是在组件清单中添加路由和功能支持。此 RFC 不会更改程序与结构化配置之间的接口,因此所需的迁移次数有限。

在实现配置功能并了解其使用方式后,Fuchsia 将能够更好地解决这些不一致问题。更多客户数据将有助于我们解决痛点并实施后续更改。

在先技术和参考资料

之前的结构化配置 RFC: