RFC-0242:配置功能

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

向组件框架添加配置功能

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

摘要

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

请注意,本文档取代了前面的结构化配置 RFC 中在前技术部分中提到的部分。

设计初衷

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 语法。配置 capability 的类型和值都在 CML 中定义。配置 capability 被路由到想要使用它的组件。当使用现有的结构化配置库启动时,使用配置功能的组件将能够看到该值。

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

// 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 capability 上的 typeelementmax_countmax_size 字段与其在 config 节中使用的字段含义相同。config 节支持的所有值都在 capabilities 代码块中受支持。

type 字段支持以下各项:

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

use 块支持与 capability 块相同的 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 节中声明。此信息现在将替换为 use 节中的信息。这意味着,为组件生成的结构化配置架构将是 use 代码块和 config 节中的字段声明的并集。

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

弃用了“可变性:父级”功能

在配置 capability 之前,可将配置值声明为 mutability: parent,以便让该组件的父项更改该值。

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

实现

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

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

性能

由于组件框架需要对组件使用的配置功能执行路由,因此启动组件的速度可能会稍慢一些。如果功能来自未解析且需要下载的组件,这可能很重要。对于已解析的组件,这应该可以忽略不计。

工效学设计

这项新 CML 功能的工效学设计与现有功能相同。添加和路由配置 capability 比较冗长,但系统会尽可能匹配现有功能语法。在可能的情况下,系统还会尽可能选择该语法以匹配现有的 config 块语法。如果这两种语法发生冲突,系统会优先使用现有的 capability 语法。匹配现有语法可以减少熟悉组件框架的用户的认知工作。

CF 团队可能会决定将 config 代码块保留为语法糖,以声明配置功能,或者,如果需要更好的人体工学设计,还可以添加其他语法糖。

向后兼容性

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

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

安全注意事项

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

有人认为,与在组件软件包内修改值的现有策略相比,以这种方式设置 capability 更加显眼。

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

隐私注意事项

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

测试

此功能需要单元测试和集成测试。常规测试领域如下:

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

此功能需要将以下测试添加到审查中:

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

这些测试都不需要新的测试基础架构。

文档

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

缺点、替代方案和问题

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

原始的结构化配置 RFC 建议,结构化配置直接从组件的软件包中获取。RFC 明确指出,它不会尝试处理“由其他组件(父组件和管理组件除外)设置的配置数据”。一种替代方案是继续不支持基于功能的配置方案。

配置功能(而不是替换服务)的缺点之一是配置功能更详细、更复杂。最初,在全局范围内实现内容总是比实现简单。但是,Fuchsia 发现,从长远来看,基于功能的系统最终更易于组合和理解。如果能够说特定配置仅由组件拓扑的子集使用,或者能够在两个不同组件中使用两个不同的值,将很有帮助。配置功能使这种方法更易于表达,并体现了 Fuchsia 其他位置使用的基于功能的系统。使用现有的 CML 语法进行配置可让 Fuchsia 开发者更轻松地理解该功能,并且与组件框架的现有工具完美配合。

缺点:有很多其他用途字段

将类型信息保留在 use 块中会增加 6 个仅对配置功能有效的字段。

可以将这 6 个字段合并到一个包含子字段的字段中,但最好添加这些字段,因为这样可以使语法与现有的 config 块语法保持接近。

缺点:混淆类型信息和路由

将配置架构放入 use 声明的一个缺点是,use 块会定义类型信息并定义路由。某些组件可能希望定义其配置而不指定路由,然后在不同场景中以不同的方式路由其配置。如果类型信息的位置与“使用”代码块的位置不同,则会更简单。

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

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

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

一种替代方案是,在 use 声明中不具有“type”字段,而是依赖现有“config”块中的类型信息。此 RFC 的早期版本就是如此。这种替代方案解决了目前混淆类型信息和路由的缺点。它使客户端可以更轻松地在一个位置定义配置并在另一个位置路由配置(可能包括包括不同的 CML 分片,以根据不同的配置以不同的方式路由)。

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

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

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

将结构化配置字段名称和类型信息封装在 config 块中后,as 可用于指定为其提供功能的字段。这与大多数非 path“重命名”在 CML 中完成的方式一致。

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

未知:通过功能平衡现有的结构化配置不一致问题

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

一个不一致的情况是,程序在很大程度上依赖于组件清单中定义的结构化配置的格式。通常,它是依赖于程序的清单,结构化配置会使这种关系循环。结构化配置 build 规则要求在组件、组件清单和程序之间添加一个额外的层,这一点很明显。这种循环不一致问题不会通过配置功能来解决,但有望在后续 RFC 中得到解决。

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

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

现有艺术和参考资料

以前的结构化配置 rfc: