外观
Normalize 钩子、模板集与枚举
Normalize 钩子
validate_references 在所有跨字段校验通过后,按固定顺序执行四个归一化钩子,全部经 object.__setattr__ 回写到 frozen 对象。
每个钩子都是幂等的——序列化后的 DesiredState 回灌给 agent 再次校验不会翻倍注入。
_normalize_wireguard_port_publish_runtime
- 收集所有
kind=wireguard接口的listen_port,同节点内必须唯一(重复即报错)。 - 若设了
runtime.wireguard_port_range,所有listen_port必须落在该范围内(越界报错)。 - 据
wireguard_port_range合成一条托管PortPublishSpec(host_ip加effective_host_start–effective_host_end→start–end,协议udp),注入到router-netns角色服务的ports;已存在同 key 则不重复。
未设端口范围或没有 WG 接口时原样返回。
_normalize_bird_control_socket
无条件注入 bird-router 的 /run/bird 可写挂载(source=runtime/bird-run、target=/run/bird、readonly=False)。
这是 bird-router 的一等不变量:宿主上的 agent 经此直连 bird.ctl 采集路由。已有 /run/bird 挂载则只校验其可写——只读会报错。没有 bird-router 服务时原样返回。
_normalize_dns_runtime
「分配组即启用」的服务面。据 dns 决定框架托管的 CoreDNS 服务:
dns为None或enabled=False→ 剥掉所有dns角色服务。DesiredState 里不含即意味着 agent 拆除 CoreDNS。- 启用且尚无
dns角色服务 → 注入 CoreDNS:name=dn42-dns、image=coredns/coredns:1.12.1、command=["-conf", "/etc/coredns/Corefile"]、network_mode=service:<router-netns>、挂coredns → /etc/coredns、依赖 router-netns 与可选的 wg-gateway。没有 router-netns 服务则跳过注入。 - 已有
dns服务则保留(去重)。
_normalize_dns_anycast
「分配组即启用」的网络面,与上一钩子同构。dns.bind_addresses 是 DNS 服务地址的唯一真源。
- 先按保留名
dns-anycast剔除interfaces与bird.dummy_interfaces里的同名残留——单源识别即幂等。 - 启用(
dns非空、enabled、有bind_addresses)→ 派生重建:合成一条dns-anycastdummy 接口承载这些地址(裸 IP 补成 v4/32、v6/128),并登记为track_service=True的DummyInterfaceSpec,于是 BIRD 的 direct protocol 起源对应前缀、任播地址进 BGP。多节点订阅同一组即拿到相同地址,构成 anycast。 - 未启用或无 bind 地址 → 只剔除不重建:地址既不挂内核也不宣告,没提供 DNS 的节点不会黑洞任播流量。
TemplateSetSpec
源码 desired_state.py。决定模板层选用哪套模板目录。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
bird | str | 否(默认 "config-bird2/v1") | BIRD 模板集版本 |
wireguard | str | 否(默认 "config-wireguard/v1") | WireGuard 模板集版本 |
coredns | str | None | 否(默认 "config-coredns/v1") | CoreDNS 模板集版本;None 表示不使用 |
docker | str | 否(默认 "config-docker/v1") | Docker 构建产物模板集版本 |
scripts | str | 否(默认 "config-scripts/v1") | 启动与应用脚本模板集版本 |
枚举
源码 enums.py,全部继承 (str, Enum),可直接接受原始字符串输入并序列化为可读字符串。
| 枚举 | 取值 | 用途 |
|---|---|---|
InterfaceKind | dummy / wireguard / underlay | InterfaceSpec.kind |
ServiceRole | router-netns / wg-gateway / bird-router / rpki-cache / dns / debug-shell | RuntimeServiceSpec.role;决定必需挂载与默认 IP 推导 |
AddressFamily | ipv4 / ipv6 / mp-bgp | BgpSessionSpec.address_family |
ImportLimitAction | block / restart / disable / warn | 导入上限超限动作 |
enums.py 还含 ApplyStatus、BootstrapStatus、AgentCapability、RuntimeResourceStatus、DriftSeverity、NodeHealth、ObservationStatus 等,但那些属于 agent 上报与控制面观测协议,不出现在 DesiredState 里。
完整示例
摘自 packages/dn42_schemas/dn42_schemas/testing.py 的 build_hkg1_example_state()(golden 样本),为可读性按 JSON 缩略并加注释。
注意 DNS 任播地址只写在 dns.bind_addresses——dns-anycast 接口与 dummy 登记由 normalize 钩子派生,不手写在 interfaces 或 bird.dummy_interfaces 里。
jsonc
{
"schema_version": "v1",
"generation": 1,
"node": {
"node_id": "edge1",
"site": "hkg",
"region": "asia-east", // Dn42OriginRegionCommunity
"asn": 4242420000,
"router_id": "172.20.0.62",
"ipv4_prefixes": ["172.20.0.0/26"],
"ipv6_prefixes": ["fdce:1111:2222::/48"],
"loopback_ipv4": "172.20.0.62", // 必须 ∈ ipv4_prefixes
"loopback_ipv6": "fdce:1111:2222:9500::1"
},
"runtime": {
"underlay": { "subnet": "10.254.42.0/24", "gateway": "10.254.42.1" },
"router_dockerfile": { "base_image": "debian:13-slim", "debian_mirror": "deb.debian.org" },
"services": [
{ "name": "dn42-router-netns", "role": "router-netns",
"build": { "target": "netns" }, "command": ["sleep", "infinity"],
"cap_add": ["NET_ADMIN", "NET_RAW"], "devices": ["/dev/net/tun:/dev/net/tun"] },
{ "name": "dn42-wg-gateway", "role": "wg-gateway",
"build": { "target": "wg-gateway" },
"command": ["/opt/dn42/scripts/wg/start-wg-gateway.sh"],
"network_mode": "service:dn42-router-netns",
"volumes": [ { "source": "wireguard", "target": "/etc/wireguard" },
{ "source": "scripts", "target": "/opt/dn42/scripts" } ],
"depends_on": ["dn42-router-netns"] },
{ "name": "dn42-bird-router", "role": "bird-router",
"build": { "target": "bird-router" },
"command": ["/opt/dn42/scripts/bird/start-bird-router.sh"],
"network_mode": "service:dn42-router-netns",
"volumes": [ { "source": "bird", "target": "/etc/bird" },
{ "source": "scripts", "target": "/opt/dn42/scripts" },
// 可省略,_normalize_bird_control_socket 会补
{ "source": "runtime/bird-run", "target": "/run/bird", "readonly": false } ],
"depends_on": ["dn42-router-netns", "dn42-wg-gateway", "dn42-rpki-cache"] },
{ "name": "dn42-rpki-cache", "role": "rpki-cache", "image": "rpki/stayrtr:latest",
"command": ["-checktime=false", "-cache=https://dn42.burble.com/roa/dn42_roa_46.json"] }
]
},
"bird": {
"region": "asia-east",
"large_communities": { "origin_node_id": 62 },
"internal_topology": {
"routers": ["edge1", "edge2"], // 必须含本节点 edge1
"hosts": {
"edge1": { "ownip": "172.20.0.62", "ownip6": "fdce:1111:2222:9500::1" },
"edge2": { "ownip": "198.18.1.3", "ownip6": "fdce:1111:2222:ff01::3" }
},
"igp_adjacencies": [ { "node": "edge2", "cost": 10 } ]
}
},
"interfaces": [
{ "name": "dn42-lo", "kind": "dummy", "mtu": null,
"addresses": ["172.20.0.62/32", "fdce:1111:2222:9500::1/128"] },
{ "name": "as4242420001", "kind": "wireguard",
// 字面私钥(base64 32 字节);实际由 materializer 从节点密钥表注入
"private_key_ref": "s2ljcc2rBbcmSbpSlQO3xZK20RqSxPFOcYM39Ge678M=",
"addresses": ["172.20.0.62/32", "fdce:1111:2222:9500::1/128"],
"peer_routes": ["172.20.0.105/32", "fdce:1111:2222:dead::11/128"],
"wireguard_peer": { "public_key": "+aFW7xRRTwOZ6w0EmrvqN4ng2QcFA0/9Wdu9GkdwJgQ=",
"allowed_ips": ["0.0.0.0/0", "::/0"] } }
// …igp-edge2 内部 WG 接口略
],
"bgp_sessions": [
{ "name": "demopeer_4242420001_ex01_v4", "remote_asn": 4242420001,
"neighbor": "172.20.0.105", "source_address": "172.20.0.62",
"address_family": "ipv4", "interface": "as4242420001" }
// …_v6 会话略
],
"dns": {
"bind_addresses": ["172.20.0.20", "172.20.0.22",
"fdce:1111:2222::20", "fdce:1111:2222::22"],
"zones": [ { "zone": "example.dn42", "records_ref": "zone://example.dn42" },
{ "zone": "0.20.172.in-addr.arpa", "records_ref": "zone://0.20.172" } ],
"forwards": [ { "zone": "dn42", "upstreams": ["172.20.0.53"] } ]
},
"templates": { "bird": "config-bird2/v1", "wireguard": "config-wireguard/v1",
"coredns": "config-coredns/v1", "docker": "config-docker/v1",
"scripts": "config-scripts/v1" }
}经 validate_references 之后,因 dns.enabled 默认为真且有 bind_addresses:
interfaces多出一条dns-anycastdummy 接口(地址172.20.0.20/32、…::20/128等);bird.dummy_interfaces多出dns-anycast: { track_service: true };runtime.services多出dn42-dns(CoreDNS)服务;- bird-router 的
/run/bird可写挂载即使省略也会被补齐。