RFC-0242:配置功能 | |
---|---|
状态 | 已接受 |
区域 |
|
说明 | 向组件框架添加配置功能 |
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2024-02-05 |
审核日期(年-月-日) | 2024-04-11 |
摘要
概述组件框架中配置capabilities的使用,以及将添加到 CML 文件中以支持这些功能的语法。
请注意,本文档取代了先前结构化配置 RFC 的部分内容(如“先前技术”部分所述)。
设计初衷
Fuchsia 中的组件使用结构化配置来获取配置值。目前,配置值主要从组件的软件包加载。父组件可以在启动动态组件时强制设置配置值,但 Fuchsia 中的大多数组件都是静态的。
我们之所以添加此功能,有以下多种原因:
- 允许静态父级配置子级的配置。
- 将单个配置值路由到多个组件。
- 从 CML 文件提供配置值。
配置功能的首个客户是系统组装,因为此功能可让您消除技术债务。存在技术债务,是因为目前除了修改相应组件的软件包之外,没有其他方法可以向静态组件提供配置。系统汇编目前会打开 Fuchsia 软件包,修改其配置值文件,然后重新打包。这是一种技术债务,因为它会更改软件包的哈希值,而配置值文件在技术上是组件的“私有”API,不易进行演变。
对于无法重新构建的非树软件包,配置功能提供了一种配置这些组件的方法。目前无法在静态拓扑中修改这些组件的配置。
这项功能在未来的许多配置用例中都可能会派上用场。
利益相关方
教练:neelsa@google.com
Reviewers:
- 系统组装:aaronwood@google.com
- 组件框架:geb@google.com
- 安全:markdittmer@google.com
- 驱动程序框架:surajmalhotra@google.com
- 组件框架:wittrock@google.com
共享:在撰写 RFC 之前,我们已与组件框架团队讨论过此问题。
要求
借助配置功能,CML 作者可以设置静态组件的配置值。它们允许将配置值通过拓扑进行路由,以便多个组件可以使用相同的值。通过拓扑路由值允许间接祖先指定值,而现有功能仅允许直接父级指定值。
配置功能应尽可能遵循现有的组件框架功能,以便与系统的其余部分保持一致。
设计
设计的大部分内容是用于声明和路由配置功能的 CML 语法。配置 capability 的类型和值均在 CML 中定义。配置 capability 会路由到希望使用它的组件。使用配置功能的组件将能够在使用现有的结构化配置库启动时看到该值。
以下是定义配置 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 上的 type
、element
、max_count
、max_size
字段与在 config
诗节中的用法具有相同的含义。capabilities
块支持 config
诗节中支持的所有值。
type
字段支持以下内容:
- bool
- uint8、uint16、uint32、uint64
- int8、int16、int32、int64
- string
- 矢量
using 块支持与 capabilities 块相同的 type
、element
、max_count
和 max_size
字段。
以下是使用配置 capability 的示例语法:
// 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",
},
]
以下是路由配置 capability 的示例语法:
// 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
的配置 capability,并且该 capability 是从 void
路由的,则该组件将从配置值文件 (CVF) 接收值。此功能旨在支持用于更新配置的软迁移。
废弃“config”块
在配置功能之前,配置字段是在 CML 的 config
诗节中声明的。此信息现在将被 use
诗节中的信息取代。这意味着,为组件生成的结构化配置架构将是 use
块和 config
诗节中的字段声明的联合。
所有结构化配置客户端都迁移到配置功能后,我们将移除对 CML 中配置块的支持。
“mutability: parent”功能已废弃
在配置功能之前,可以将配置值声明为 mutability: parent
,这样该组件的父级便可以更改该值。
此功能的用户将从其上级迁移到 use
配置 capability。所有用户都完成迁移后,此功能将被移除。
实现
这些 CML 功能的实现相对简单,可以在几次 CL 中完成。客户端可以随时迁移到使用配置功能,因为这不会影响现有的结构化配置功能。
我们需要确保 Scrutiny 了解配置功能,并且能够验证特定产品是否具有给定配置。
性能
由于组件框架需要对组件使用的配置功能执行路由,因此启动组件可能会变慢一些。如果功能来自未解析且需要下载的组件,这可能会很重要。对于已解析的组件,这应该可以忽略不计。
工效学设计
这项新的 CML 功能的人体工学设计与现有功能相同。添加和路由配置 capability 的语法有点冗长,但我们选择的语法尽可能与现有 capability 语法保持一致。在可能的情况下,我们还选择了与现有 config
代码块语法匹配的语法。如果这两种语法发生冲突,则优先使用现有 capability 语法。与现有语法匹配可减少熟悉组件框架的用户的认知工作量。
CF 团队可能会决定将 config
块保留为用于声明配置功能的语法糖,或者在需要更好的工效学体验时添加其他语法糖。
向后兼容性
这是一项新功能。它向后兼容现有的结构化配置功能。
值得注意的是,将配置 capability 路由到组件始终优先于 CVF 文件中的值。如果组件具有可选配置 capability 路由,但该路由不存在,则系统将使用 CVF 文件中的值。
安全注意事项
此功能不应影响安全性。用于调查 CML 的 Scrutiny 和其他工具也适用于这种新 capability 类型。
有人可能会认为,与在组件软件包中修改值的现有策略相比,以这种方式设置功能会更显眼。
如果某个组件正在使用必需的配置 capability,但没有任何 capability 路由到该组件,则该组件将无法启动。这不应是一个安全问题,因为组件的父级是可信的,并且这些路线可以通过静态验证来确保正确无误。
隐私注意事项
此提案应该不会对隐私造成影响。
测试
此功能需要单元测试和集成测试。常规测试领域包括:
- 解析新的 CML 语法。
- 通过拓扑路由新 capability。
- 尝试启动缺少配置功能的组件。
- 尝试启动配置功能类型错误的组件。
- 启动具有配置功能的动态组件。
该功能需要向 Scrutiny 添加以下测试:
- 测试 Scrutiny 是否可以解析配置功能的值。
- 测试不同配置值来源的优先级/替换逻辑。
- 测试配置功能、配置块和可变性之间的互动。
这些测试都不需要新的测试基础架构。
文档
需要更新 Fuchsia.dev,以便提供有关配置功能的文档。现有的 capability 文档将用作此新文档的模板。
缺点、替代方案和未知情况
替代方案:实现配置替换服务
最初的结构化配置 RFC 提议,结构化配置直接从组件的软件包中获取。RFC 明确指出,它不会尝试解决“由其他组件(父级组件和管理组件除外)设置的配置数据”问题。一种替代方案是继续不支持基于功能的配置方案。
使用配置功能(而非替换服务)的一个缺点是,配置功能更详细、更复杂。在开始时,全局实现某项功能总是更简单。不过,Fuchsia 发现,从长远来看,基于 capability 的系统最终会变得更加可组合和易于理解。能够说明特定配置仅由组件拓扑的一部分使用,或者在两个不同的组件中使用两个不同的值,这非常有用。配置功能可让您更轻松地表达这一点,并体现了 Fuchsia 中其他地方使用的基于功能的系统。使用现有的 CML 语法进行配置,可让 Fuchsia 开发者更轻松地理解该功能,并且该功能可与组件框架的现有工具很好地组合使用。
缺点:许多其他使用情形字段
在 use
块中保留类型信息会额外添加 6 个字段,这些字段仅适用于配置功能。
可以将这 6 个字段合并为一个包含子字段的字段,但最好还是添加这些字段,因为这样可以使语法接近现有的 config
代码块语法。
缺点:混淆类型信息和路由
将配置架构放入 use
声明中的缺点之一是,use
块会定义类型信息,同时还会定义路由。某些组件可能希望在不指定路由的情况下定义其配置,然后在不同场景中以不同的方式路由其配置。如果类型信息位于 Use 块之外的位置,则更简单。
如果组件作者想要更改其类型的定义方式,则必须修改其 use
块,而不能仅修改其 config
块。
如果配置定义和路由的组合成为开发者的长期痛点,则组件框架团队可能会重新考虑这一决定。
替代方案:在 config
块中保留配置类型定义
一种替代方案是在 use
声明中不使用“type”字段,而是依赖于现有“config”块中的类型信息。此 RFC 的早期版本就是如此。这种替代方案解决了目前混淆类型信息和路由的缺点。这样,客户端就可以更轻松地在一个位置定义配置,并在另一个位置进行路由(可能包括不同的 CML 分片,以便根据不同的配置进行不同的路由)。
在当前基于结构化配置的实现中,Component Manager 会在启动组件时将配置“推送”到组件,因此 Component Manager 需要确保路由的 capability 的类型与目标结构化配置字段的类型一致。我们决定将类型信息放在 use
声明中,因为这与 capability 声明是对称的。当组件管理器执行路由时,需要确保类型信息保持一致,这意味着从逻辑上讲,信息应包含在路由的两个端点(capability 和 use)中。
在 use
声明中保留类型信息的另一个原因是,这样可以将所有信息保留在 use
声明中,而无需匹配的“config”块条目。这样,这些信息就会保存在 CML 文件中的单个位置,从而更易于审核。
替代方案:use
声明中的 as
字段
将结构化配置字段名称和类型信息封装在 config
块中后,as
可用于指定要向其提供 capability 的字段。这与 CML 中执行大多数非 path
“重命名”的方式一致。
之所以改用 key
一词,是因为该字段的运作方式与其他 as
字段不同。as
通常为可选项,但 key
是必填项。如果功能是可选的,key
还必须与 config
块中的现有键匹配。
未知:平衡现有结构化配置不一致性与功能
现有的结构化配置在多方面与其他 capability 的运作方式不一致。
其中一个不一致之处是,该程序在很大程度上依赖于组件清单中定义的结构化配置的格式。通常,清单依赖于程序,而结构化配置会使这种关系形成循环。这从以下事实可见一斑:结构化配置 build 规则需要在组件、组件清单和程序之间添加额外的层。配置功能无法解决这种循环不一致性,但后续的 RFC 应该可以解决此问题。
另一个不一致之处在于,结构化配置会在组件启动时“推送”到组件,而其他功能会在组件访问它们时“拉取”。本 RFC 也不会解决此不一致性问题。此 RFC 旨在为组件清单添加路由和 capability 支持。此 RFC 不会更改程序与结构化配置之间的接口,因此所需的迁移次数有限。
在实现配置功能并了解其使用方式后,Fuchsia 将能够更好地解决这些不一致性问题。更多客户数据有助于我们解决痛点并实施后续更改。
在先技术和参考文档
之前的结构化配置 RFC: