Development 开发

  1. Development notes 开发说明
    1. Code structure 代码结构
    2. Adding functionality 添加功能
    3. Modifying existing functionality
      修改现有功能
    4. Validation and failures 验证和失败
    5. Platforms 平台
    6. Network access 网络访问
    7. Execution stages 执行阶段
    8. Security 安全性
  2. Modifying the config spec
    修改配置规范
  3. Vendor 供应商
  4. Testing 测试
    1. Running blackbox tests 运行黑盒测试
    2. Blackbox test host system dependencies
      黑盒测试主机系统依赖
    3. Writing blackbox tests 编写黑盒测试
  5. Releasing Ignition 发布 Ignition
  6. The build process 构建过程
  7. Marking an experimental spec as stable
    将实验性规范标记为稳定

A Go 1.20+ environment and the blkid.h headers are required.
需要 Go 1.20+ 环境和 blkid.h 头。

# Debian/Ubuntu
sudo apt-get install libblkid-dev

# RPM-based
sudo dnf install libblkid-devel

Development notes 开发说明

See also the Ignition rationale.
另请参阅点火原理。

Code structure 代码结构

The frontend handles config parsing and validation which need not run on the target system. The backend performs the configuration of the target system. The frontend is a stable library API that is used by other programs, so existing frontend API cannot be changed without bumping the Ignition major version.
前端处理配置解析和验证,这些不需要在目标系统上运行。后端执行目标系统的配置。前端是一个稳定的库 API,被其他程序使用,因此不能更改现有的前端 API 而不升级 Ignition 主版本。

Adding functionality 添加功能

New config directives should only be added if the desired behavior cannot reasonably be achieved with existing directives. User-friendly wrappers for existing syntax (“sugar”) should be handled by Butane.
只有在现有指令无法合理实现所需行为时,才应添加新的配置指令。对于现有语法的用户友好包装(“糖”)应由 Butane 处理。

New behavior should only be added in the current experimental spec. If new functionality is backported to older specs, and a config using an older spec comes to depend on that functionality, then it won’t be obvious that the config will not work on all Ignition versions supporting that spec. It’s not always possible to follow this restriction, since the backend doesn’t know what config version the user specified. Where possible, use config validation to prevent the backend from seeing config directives that a spec version doesn’t support (for example, values of the filesystem format field).
新行为只能在当前实验性规范中添加。如果新功能被回溯到旧规范,并且使用旧规范的配置依赖于该功能,那么配置将无法在支持该规范的所有 Ignition 版本上运行,这将不明显。并不总是可能遵循此限制,因为后端不知道用户指定的配置版本。在可能的情况下,使用配置验证来防止后端看到特定版本不支持的配置指令(例如,文件系统 format 字段的值)。

New functionality added to a config spec must be declarative: it must describe what should exist, not what Ignition should do. In particular, field names should not include verbs. When adding functionality, carefully think through the interactions between features, which can be non-trivial. Features should be orthogonal, minimal, and low-level; making them user-friendly is the responsibility of Butane sugar.
添加到配置规范的新功能必须是声明性的:它必须描述应该存在什么,而不是 Ignition 应该做什么。特别是,字段名称不应包含动词。在添加功能时,仔细考虑功能之间的交互,这可能并不简单。功能应该是正交的、最小的和低级的;使它们用户友好是 Butane sugar 的责任。

When reprovisioning an existing node, the config may want to reuse existing disks and filesystems without reformatting them. Config directives should support detecting and reusing an existing object (RAID volume, filesystem, etc.) if its properties match those specified in the config.
在重新配置现有节点时,配置可能希望重用现有的磁盘和文件系统,而无需重新格式化它们。如果其属性与配置中指定的属性匹配,配置指令应支持检测和重用现有对象(RAID 卷、文件系统等)。

Ignition specs should not include distro-specific functionality such as package management. Features may require support from the distro (for example, setting kernel arguments), but such features should be broadly applicable. Distro-specific options such as support for SELinux, or paths to external binaries, can be configured at build time in the distro package. Distro-specific glue (e.g. support for reformatting the root filesystem) should be implemented outside the Ignition codebase, in Dracut modules that run between Ignition stages (see below).
点火规格不应包含特定于发行版的功能,如软件包管理。功能可能需要发行版的支持(例如,设置内核参数),但这些功能应具有广泛适用性。在 distro 软件包中可以在构建时配置特定于发行版的选项,例如支持 SELinux 或外部二进制文件的路径。特定于发行版的粘合剂(例如,支持重新格式化根文件系统)应在 Ignition 代码库之外实现,在运行在 Ignition 阶段之间的 Dracut 模块中实现(见下文)。

Ideally, functionality should not be added to an experimental spec in the same Ignition release that the spec is stabilized. Doing so prevents users from trying out the functionality before we commit to maintaining it.
理想情况下,在稳定规格的 Ignition 发布中不应向实验性规格添加功能。这样做会阻止用户在我们承诺维护之前尝试该功能。

Modifying existing functionality
修改现有功能

Bugfixes can be backported to older specs if working configs will not be affected and the current behavior is unintended. New config validations can (and should) be backported if the prohibited behavior always would have failed anyway.
如果工作配置不受影响且当前行为是意外的,则错误修复可以回溯到旧规范。如果被禁止的行为总是会失败,那么新的配置验证可以(也应该)回溯。

Existing config semantics can only be changed (without adding a flag field) if the spec major version is bumped. This might be appropriate e.g. to clean up some awkward syntax or change some default behavior. However, altogether removing functionality is very costly. The spec 2.x to 3.x transition was difficult because some 2.x configs cannot be represented in spec 3.x, so users were required to manually update their configs. If a major version bump only rearranges functionality but doesn’t remove any, Ignition can automatically translate the previous major version to the current one, and no flag day will be required.
只有在规范主版本被提升的情况下才能更改现有配置语义(而不添加标志字段)。这可能是合适的,例如清理一些尴尬的语法或更改一些默认行为。然而,完全删除功能是非常昂贵的。规范从 2.x 到 3.x 的转换很困难,因为一些 2.x 配置无法在规范 3.x 中表示,因此用户需要手动更新他们的配置。如果主版本升级只是重新排列功能而不删除任何功能,Ignition 可以自动将先前的主版本转换为当前版本,而不需要标志日。

Validation and failures 验证和失败

Ignition is a low-level tool and does not attempt to prevent configs from doing unreasonable things.
点火是一个低级工具,不会试图阻止配置执行不合理的操作。

  • Config validation should fail when it’s clear from inspection that Ignition will be unable to perform the requested provisioning. This must occur when the config is contradictory (e.g. specifies the same filesystem object as both a file and a directory) and thus would break the declarativeness of the spec, and may occur where the problem will produce a routine error at runtime (e.g. invalid arguments to mkfs).
    当检查明确表明 Ignition 无法执行请求的配置时,配置验证应该失败。当配置存在矛盾时(例如,将相同的文件系统对象同时指定为文件和目录),这种情况必须发生,从而会破坏规范的声明性,并且可能会在运行时产生常规错误(例如, mkfs 的无效参数)。
  • Ignition must fail at runtime when it is dynamically unable to perform the requested provisioning.
    当 Ignition 在运行时动态无法执行请求的配置时,必须失败。
  • If a config is implementable but will render the system non-functional, Ignition should execute the config anyway. Any user-friendly detection of unreasonable configs should happen in Butane.
    如果配置是可实现的,但会使系统无法正常运行,Ignition 应该仍然执行配置。任何用户友好的检测不合理配置的操作应该在 Butane 中进行。

Platforms 平台

Platform providers should continue retrying config fetch until it’s clear whether the user has provided an Ignition config. If Ignition eventually timed out when fetching a config, then a slow network device or block device (on a very large machine or a heavily-loaded VM host) could cause Ignition to prematurely fail, or to continue booting without applying the specified config.
平台提供者应该继续重试配置获取,直到清楚用户是否提供了 Ignition 配置。如果在获取配置时 Ignition 最终超时,那么慢速网络设备或块设备(在非常大的机器或负载很重的 VM 主机上)可能会导致 Ignition 过早失败,或者在不应用指定配置的情况下继续引导。

Platform providers must allow the user not to provide a config, e.g. to boot an exploratory OS instance and use Afterburn to inject SSH keys.
平台提供者必须允许用户不提供配置,例如启动一个探索性操作系统实例并使用 Afterburn 注入 SSH 密钥。

Ignition must never read from config providers that aren’t under the control of the platform, since this could allow config injection from unintended sources. For example, 169.254.169.254 is a link-local address and could easily be spoofed on platforms that don’t specially handle that address. As a corollary, the platform ID must always be explicitly set by the OS image, never guessed.
Ignition 绝不能从不受平台控制的配置提供者中读取,因为这可能允许来自意外来源的配置注入。例如, 169.254.169.254 是一个链路本地地址,在不专门处理该地址的平台上很容易被欺骗。作为推论,平台 ID 必须始终由操作系统镜像明确设置,而不是猜测。

Network access 网络访问

All config fields that cause network accesses, directly or indirectly, should be added to the fetch-offline needs-net detector.
所有导致网络访问的配置字段,直接或间接地,应添加到 fetch-offline 需要网络检测器中。

Any network accesses performed by subprocesses should be mimicked by Ignition before starting the subprocess. This ensures that Ignition’s retry logic is used, so Ignition doesn’t improperly fail if the network is still coming up.
子进程执行的任何网络访问都应在启动子进程之前由 Ignition 模拟。这确保了使用 Ignition 的重试逻辑,因此如果网络仍在启动中,Ignition 不会因此而错误地失败。

Execution stages 执行阶段

Ignition execution is divided into stages to allow other OS functionality to run in the middle of provisioning:
点火执行被分成多个阶段,以允许其他操作系统功能在配置过程中运行:

  1. fetch-offline stage  fetch-offline 阶段
  2. OS enables networking if required
    如果需要,操作系统会启用网络功能
  3. fetch stage  fetch 阶段
  4. OS examines fetched config and e.g. copies root filesystem contents to RAM
    操作系统检查获取的配置,例如将根文件系统内容复制到 RAM
  5. disks stage  disks 阶段
  6. OS mounts root filesystem
    操作系统挂载根文件系统
  7. mount stage  mount 阶段
  8. OS does any preprocessing of configured filesystems, including copying root filesystem contents back
    操作系统对配置的文件系统进行任何预处理,包括将根文件系统内容复制回去
  9. files stage  files 阶段
  10. OS does any postprocessing of provisioned system
    操作系统对已配置系统进行任何后处理
  11. umount stage  umount 阶段

New stages should only be created when OS hooks would otherwise need to run in the middle of a stage. Note that various external projects hardcode the list of Ignition stages.
只有在操作系统钩子必须在阶段中间运行时,才应创建新阶段。请注意,各种外部项目都将 Ignition 阶段列表硬编码。

Security 安全性

Ignition must always provide secure defaults, and does not provide config directives that support or encourage unsafe behavior. For example, Ignition does not support disabling HTTPS certificate checks, nor seeding the system entropy pool from potentially deterministic sources. Similarly, the LUKS discard option exists because users may legitimately want to trade off some security for hardware longevity, but Ignition defaults to the secure option.
Ignition 必须始终提供安全默认值,并且不提供支持或鼓励不安全行为的配置指令。例如,Ignition 不支持禁用 HTTPS 证书检查,也不支持从潜在确定性来源种子化系统熵池。类似地,LUKS discard 选项存在是因为用户可能合理地希望在硬件寿命和安全性之间进行权衡,但 Ignition 默认选择安全选项。

Users might put secrets in Ignition configs. On many platforms, userdata is accessible to unprivileged programs at runtime, potentially leaking those secrets. When the userdata is accessible via a network service, users can configure firewall rules to prevent such access, but this may not be possible for hypervisors that expose userdata through a kernel interface. Where possible, platform providers should provide a DelConfig method allowing Ignition to delete the userdata from the platform after provisioning is complete.
用户可能会将秘密放在 Ignition 配置中。在许多平台上,用户数据在运行时对非特权程序可访问,可能会泄露这些秘密。当用户数据通过网络服务访问时,用户可以配置防火墙规则以防止此类访问,但对于通过内核接口公开用户数据的虚拟化程序可能无法实现。在可能的情况下,平台提供商应提供一种 DelConfig 方法,允许 Ignition 在配置完成后从平台中删除用户数据。

Modifying the config spec
修改配置规范

Install schematyper to generate Go structs from JSON schema definitions.
安装 schematyper 以从 JSON 模式定义生成 Go 结构。

go get -u github.com/idubinskiy/schematyper

Modify config/v${LATEST_EXPERIMENTAL}/schema/ignition.json as necessary. This file adheres to the json schema spec.
根据需要修改 config/v${LATEST_EXPERIMENTAL}/schema/ignition.json 。该文件符合 JSON 模式规范。

Run the generate script to create config/vX_Y/types/schema.go. Once a configuration is stabilized (i.e. it is no longer -experimental), it is considered frozen. The json schemas used to create stable specs are kept for reference only and should not be changed.
运行 generate 脚本以创建 config/vX_Y/types/schema.go 。一旦配置稳定(即不再 -experimental ),则被视为冻结。用于创建稳定规范的 JSON 模式仅用于参考,不得更改。

./generate

Add whatever validation logic is necessary to config/v${LATEST_EXPERIMENTAL}/types, modify the translator at config/v${LATEST_EXPERIMENTAL}/translate/translate.go to handle the changes if necessary, and update config/v${LATEST_EXPERIMENTAL/translate/translate_test.go to properly test the changes.
添加必要的验证逻辑到 config/v${LATEST_EXPERIMENTAL}/types ,根据需要修改位于 config/v${LATEST_EXPERIMENTAL}/translate/translate.go 的翻译器以处理更改,并更新 config/v${LATEST_EXPERIMENTAL/translate/translate_test.go 以正确测试更改。

Finally, make whatever changes are necessary to internal to handle the new spec.
最后,对 internal 进行必要的更改以处理新的规范。

Vendor 供应商

Ignition uses go modules. Additionally, we keep all of the dependencies vendored in the repo. This has a few benefits:
Ignition 使用 go 模块。此外,我们将所有依赖项都存储在仓库中。这有一些好处:

  • Ensures modification to go.mod is intentional, since go build can update it without -mod=vendor
    确保对 go.mod 的修改是有意的,因为 go build 可以在没有 -mod=vendor 的情况下更新它
  • Ensures all builds occur with the same set of sources, since go build will only pull in sources for the targeted GOOS and GOARCH
    确保所有构建都使用相同的源代码集,因为 go build 仅会拉取针对 GOOSGOARCH 的源代码
  • Simplifies packaging in some cases since some package managers restrict network access during compilation.
    在某些情况下简化打包,因为一些软件包管理器在编译期间限制网络访问。

After modifying go.mod run make vendor to update the vendor directory.
修改 go.mod 后运行 make vendor 以更新供应商目录。

Group changes to go.mod, go.sum and vendor/ in their own commit; do not make code changes and vendoring changes in the same commit.
go.modgo.sumvendor/ 的组更改放在自己的提交中;不要在同一次提交中进行代码更改和供应商更改。

Testing 测试

Ignition uses three different test frameworks:
点火使用三种不同的测试框架:

  • Unit tests (./test) validate functionality that only affects internal program state.
    单元测试( ./test )验证仅影响内部程序状态的功能。
  • Blackbox tests validate config directives that affect the target disk.
    黑盒测试验证影响目标磁盘的配置指令。
  • Fedora CoreOS kola tests validate functionality that interacts with platforms (e.g. config fetching) or the rest of the OS. kola tests may be internal or external.
    Fedora CoreOS kola 测试验证与平台(例如配置获取)或操作系统的其余部分交互的功能。kola 测试可以是内部的也可以是外部的。

Running blackbox tests 运行黑盒测试

./build_blackbox_tests
sudo sh -c 'PATH=$PWD/bin/amd64:$PATH ./tests.test'

To run a subset of the blackbox tests, pass a regular expression into -test.run. As an example:
要运行黑盒测试的子集,请将正则表达式传递给 -test.run 。例如:

sudo sh -c 'PATH=$PWD/bin/amd64:$PATH ./tests.test -test.run TestIgnitionBlackBox/Preemption.*'

You can get a list of available tests to run by passing the -list option, like so:
通过传递 -list 选项,您可以获取要运行的可用测试列表,就像这样:

sudo sh -c 'PATH=$PWD/bin/amd64:$PATH ./tests.test -list'

Blackbox test host system dependencies
黑盒测试主机系统依赖项

The following packages are required by the blackbox tests:
黑盒测试需要以下软件包:

  • util-linux
  • dosfstools
  • e2fsprogs
  • btrfs-progs
  • xfsprogs
  • gdisk
  • coreutils
  • mdadm
  • libblkid-devel

Writing blackbox tests 编写黑盒测试

To add a blackbox test create a function which yields a Test object. A Test object consists of the following fields:
要添加一个黑盒测试,创建一个生成 Test 对象的函数。 Test 对象包括以下字段:

  • Name: string
  • In: []Disk object, which describes the Disks that should be created before Ignition is run.
    In[]Disk 对象,描述应在运行 Ignition 之前创建的磁盘。
  • Out: []Disk object, which describes the Disks that should be present after Ignition is run.
    Out[]Disk 对象,描述了在运行 Ignition 后应该存在的磁盘。
  • MntDevices: MntDevice object, which describes any disk related variable replacements that need to be done to the Ignition config before Ignition is run. This is done so that disks which are created during the test run can be referenced inside of an Ignition config.
    MntDevicesMntDevice 对象,描述了在运行 Ignition 前需要对 Ignition 配置进行的任何与磁盘相关的变量替换。这样做是为了在测试运行期间创建的磁盘可以在 Ignition 配置中引用。
  • SystemDirFiles: []File object which describes the Files that should be written into Ignition’s system config directory before Ignition is run.
    SystemDirFiles[]File 对象,描述了在运行 Ignition 前应该写入 Ignition 系统配置目录的文件。
  • Config: string type where the specific config version should be replaced by $version and will be updated before Ignition is run.
    Configstring 类型,应在运行 Ignition 之前用 $version 替换特定的配置版本,并在更新之前更新。
  • ConfigMinVersion: string type which describes the minimum config version the test should be run with. Copies of the test will be generated for every version, inside the same major version, that is equal to or greater than the specified ConfigMinVersion. If the test should run only once with a specfic config version, leave this field empty and replace $version in the Config field with the desired version.
    ConfigMinVersionstring 类型,描述测试应该运行的最低配置版本。测试的副本将为每个大版本内等于或大于指定的 ConfigMinVersion 的版本生成,如果测试应该仅使用特定的配置版本运行一次,请将此字段留空,并将 Config 字段中的$version 替换为所需的版本。

The test should be added to the init function inside of the test file. If the test module is being created then an init function should be created which registers the tests and the package must be imported inside of tests/registry/registry.go to allow for discovery.
测试应添加到测试文件的 init 函数中。如果正在创建测试模块,则应创建一个 init 函数,该函数注册测试,并且必须在 tests/registry/registry.go 中导入包以允许发现。

UUIDs may be required in the following fields of a Test object: In, Out, and Config. Replace all GUIDs with GUID varaibles which take on the format $uuid<num> (e.g. $uuid123). Where <num> must be a positive integer. GUID variables with identical <num> fields will be replaced with identical GUIDs. For example, look at tests/positive/partitions/zeros.go.
UUIDs 可能需要在 Test 对象的以下字段中: InOutConfig 。用格式 $uuid<num> (例如$uuid123)替换所有 GUID。其中 <num> 必须是正整数。具有相同 <num> 字段的 GUID 变量将被替换为相同的 GUID。例如,请查看 tests/positive/partitions/zeros.go。

Releasing Ignition 释放 Ignition

Create a new release checklist and follow the steps there.
创建一个新的发布检查表,并按照那里的步骤进行。

The build process 构建过程

Note that the build script included in this repository is a convenience script only and not used for the actual release binaries. Those are built using an ignition.spec maintained in Fedora rpms/ignition. (The ignition-validate container is built by the build_for_container script, which is not further described here.) This build process uses the go-rpm-macros to set up the Go build environment and is subject to the Golang Packaging Guidelines.
请注意,此存储库中包含的 build 脚本仅用作便利脚本,不用于实际发布的二进制文件。这些二进制文件是使用在 Fedora rpms/ignition 中维护的 ignition.spec 构建的。( ignition-validate 容器是由 build_for_container 脚本构建的,这里不再详细描述。)此构建过程使用 go-rpm-macros 来设置 Go 构建环境,并遵守 Golang 打包指南。

Consult the Package Maintenance Guide and the Pull Requests Guide if you want to contribute to the build process.
如果您想为构建过程做出贡献,请参考软件包维护指南和拉取请求指南。

In case you have trouble with the aforementioned standard Pull Request Guide, consult the Pagure documentation on the Remote Git to Pagure pull request workflow.
如果您在上述标准拉取请求指南中遇到问题,请查阅 Pagure 文档,了解远程 Git 到 Pagure 拉取请求工作流程。

Marking an experimental spec as stable
将实验性规范标记为稳定

Create a new stabilization checklist and follow the steps there.
创建一个新的稳定性检查表,并按照其中的步骤进行操作。