自动化脚本¶
用于在配置构建的各个阶段自动执行 Python 脚本,实现动态配置生成、修改和验证。支持全局脚本和服务级脚本,脚本可串行或并行执行
概览¶
Auto 功能通过三个执行阶段来管理配置:
- setup:初始化阶段,在配置解析前预执行,用于生成基础配置或新增服务
- modify:修改阶段,在配置解析后(
ref展开、内置变量替换后)执行,用于动态调整已解析的配置 - restrict(out-of-date):验证阶段,在配置完全解析后执行,用于检查配置有效性
- post: 在构建完后执行任意操作,可以执行清理工作,甚至对output_dir中的项目内容进行修改
配置结构¶
全局自动化脚本¶
在顶层配置中声明 auto 块:
name: demo
inet: 10.88.0.0/24
auto:
setup: |
# Python 代码,可修改或增加 config 的内容
if 'custom_key' not in config:
config['custom_key'] = 'custom_value'
modify: |
# Python 代码,在所有 ref 解析后执行
for svc_name, svc_config in config.get('builds', {}).items():
svc_config['custom_field'] = 'modified'
restrict: |
# Python 代码,验证配置有效性
result = "PASS" # 必须赋值给 result,作为验证结果
if not config.get('inet'):
result = "ERROR: Missing inet"
post: |
output_dir = workdir / "output" / "recursor" / "content"
Path(output_dir).chmod(0o755)
builds:
recursor:
image: "bind"
ref: "std:recursor"
服务级自动化脚本¶
在各个服务配置中声明 auto 块:
builds:
my_service:
image: "bind"
ref: "std:auth"
auto:
setup: |
# 初始化该服务的配置
config['behavior'] = '. master com NS tld'
modify: |
# 修改该服务的配置
config['cap_add'] = ['NET_ADMIN']
restrict: |
# 验证该服务的配置
if 'behavior' not in config:
result = "ERROR: Missing behavior"
else:
result = "PASS"
脚本格式¶
单个脚本¶
脚本内容直接写在字符串中:
auto:
setup: |
config['new_service'] = {'image': 'bind', 'ref': 'std:recursor'}
多个脚本¶
使用列表串行执行多个脚本:
auto:
setup:
- |
config['builds'] = {}
- |
config['builds']['s1'] = {'image': 'bind', 'ref': 'std:auth'}
- |
config['builds']['s2'] = {'image': 'bind', 'ref': 'std:recursor'}
支持显式格式(虽然目前所有脚本都是 Python):
auto:
modify:
- content: |
config['builds']['s1']['cap_add'] = ['NET_ADMIN']
type: python
- content: |
config['builds']['s2']['cap_add'] = ['NET_ADMIN']
type: python
执行环境¶
每个脚本都在隔离的环境中执行,可访问以下全局变量:
| 变量名 | 类型 | 说明 |
|---|---|---|
config |
dict |
当前配置字典(全局脚本传入完整配置,服务级脚本传入该服务配置) |
service_name |
str | None |
当前服务名(全局脚本为 None,服务级脚本为对应的服务名) |
result |
Any |
仅 restrict 脚本使用,存储验证结果 |
fs |
FileSystem |
文件系统对象,用于文件读写操作 |
workdir |
DNSBPath |
工作目录路径(chroot),可用于相对路径操作,兼容(pathlib.PurePosixPath,使用pathlib.Path可以直接传递workdir作为构造参数) |
变量使用示例¶
# 读取文件
content = fs.read_text('resource:/configs/example.conf')
# 写入文件
fs.write_text('temp://temp/config.txt', 'some content')
# 检查路径存在
if fs.exists('file:///path/to/file'):
data = fs.read_text('file:///path/to/file')
并行执行¶
- 服务级
setup脚本:多个服务的脚本 并行 执行(但单个服务内的多个脚本串行) - 服务级
modify脚本:多个服务的脚本 并行 执行(但单个服务内的多个脚本串行) restrict脚本:所有脚本 并行 执行
例子¶
1. 动态服务生成¶
auto:
setup: |
# 基于配置动态生成多个递归服务
base_name = "recursor"
for i in range(3):
name = f"{base_name}_{i}"
config.setdefault('builds', {})[name] = {
'image': 'bind',
'ref': 'std:recursor',
'behavior': '. hint 8.8.8.8'
}
2. 根据条件修改配置¶
auto:
modify: |
# 根据镜像类型为所有服务注入额外参数
for svc_name, svc_config in config.get('builds', {}).items():
image_name = svc_config.get('image', '')
if 'bind' in image_name:
svc_config.setdefault('cap_add', []).append('NET_ADMIN')
3. 验证配置完整性¶
auto:
restrict: |
# 验证所有服务都有行为定义
errors = []
for svc_name, svc_config in config.get('builds', {}).items():
if not svc_config.get('behavior'):
errors.append(f"Service '{svc_name}' missing 'behavior'")
if errors:
result = "ERROR: " + "; ".join(errors)
else:
result = "PASS"
4. 生成行为脚本(服务级)¶
builds:
auth_server:
image: "bind"
ref: "std:auth"
auto:
setup: |
# 根据服务级参数生成 behavior
zone_name = config.get('zone_name', 'example.com')
config['behavior'] = f"{zone_name} master www A 1.2.3.4"
约束与限制¶
setup 阶段约束¶
- 在
setup阶段执行后,系统会自动进行ref解析 - 新增的服务可以继承其
ref对应模板中的auto.setup - 新增服务的
ref的对象中包含auto.setup,可能还有问题(待修改,目前不支持)
modify 阶段约束¶
modify阶段 禁止 在全局配置中使用include字段modify阶段 禁止 在服务配置中使用ref字段- 这两个限制是为了避免在修改后重新进行解析,保证整个构建流程的确定性
脚本执行错误处理¶
- 脚本执行异常会导致整个构建失败
- 错误消息会在日志中输出,包含脚本位置和异常详情
- 使用
--debug标志可获取更详细的日志信息
推荐¶
- 使用 setup 进行初始化:在不需要 ref 解析的场景下,使用
setup生成初始配置 - 使用 modify 进行微调:在所有 ref 都解析完毕后,使用
modify进行最终调整 - 使用 restrict 进行验证:在构建前,使用
restrict检查配置的有效性和完整性 - 避免过度复杂的脚本:脚本应保持简洁,复杂逻辑建议分离为多个脚本
- 利用并行执行:服务级脚本会自动并行执行,无需手动优化