| 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 功能中的 type、element、max_count、max_size 字段与它们在 config 节中的用法具有相同的含义。config stanza 中支持的所有值在 capabilities 块中均受支持。
type 字段支持以下内容:
- bool
- uint8、uint16、uint32、uint64
- int8、int16、int32、int64
- string
- 矢量
using 块支持与 capabilities 块相同的 type、element、max_count 和 max_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: