libcalico-go 测试环境初始化指南:CRD、Mock Node 与 Namespace 的预置与原理

原创2026-09-28 23:14:47205 阅读
文章标签:网络云原生网络安全

libcalico-go 测试环境初始化指南:CRD、Mock Node 与 Namespace 的预置与原理

libcalico-go 是 Calico 的 Go 语言核心库,其单元测试与功能测试(FV)大量依赖真实的 Kubernetes API Server 作为后端数据存储。为了让测试在启动前就具备完整的运行前提,libcalico-go/test 目录下的 crds.yaml、mock-node.yaml、namespaces.yaml 三个清单文件承担了"测试前置环境初始化"的职责。本文以 libcalico-go/test/README.md 为主线,结合仓库内的 Makefile 目标、k8s 后端实现与测试用例,完整讲解这三类资源为何必须预置、如何被应用、以及它们背后的实现原理。读完本文,你将掌握在本地或 CI 中复现 libcalico-go 测试环境的完整操作步骤,并理解 Calico 数据模型与 Kubernetes CRD 之间的映射关系。

一、为什么测试需要"预置环境":libcalico-go 的 Datastore 抽象

libcalico-go 的核心设计是让上层组件(如 Felix、Typha、kube-controllers)通过统一的数据模型访问 Calico 数据,而底层数据存储可以被替换——包括 etcd、Kubernetes API Server 等。从 libcalico-go/lib/backend/k8s 目录的代码结构可以看出,Kubernetes 后端是其中最重要、测试覆盖最充分的实现之一。

使用 Kubernetes 作为后端时,Calico 的各类资源(IPPool、FelixConfig、BGPPeer 等)都以 CRD 的形式保存在 API Server 中。因此,测试进程要能正常工作,必须先满足三个前提:

  1. 对应的 CRD 已经被注册进 API Server(否则资源无法创建);
  2. 某些测试依赖的 Node 对象已经存在(否则部分测试场景无从谈起);
  3. 名字空间级资源(如 NetworkPolicy)所需的 Namespace 已经创建。

test/ 目录下的三个 YAML 清单正是为满足这三个前提而准备的,它们统一由 Makefile 在合适的时机应用进集群。这也是 libcalico-go/test/README.md 的标题"Create Custom Resource Definitions / Create Mock Nodes / Create Namespaces"所对应的三件事。

二、预置 CRD:Kubernetes 后端的类型基础

2.1 README 列出的 CRD 清单

根据 libcalico-go/test/README.md,crds.yaml 会在测试运行前被应用,用于初始化 Kubernetes 后端的 CRD,它创建了以下 7 种 CRD:

  • FelixConfig
  • BGPPeer
  • BGPConfig
  • IPPool
  • GlobalNetworkPolicy
  • ClusterInfo
  • NetworkPolicy

这些 CRD 是任何使用 Kubernetes 后端的 Calico 部署都必须在启动前创建好的,通常与安装 Calico 的同一个 manifest 一起下发。该 manifest 在 Makefile 中于 Kubernetes API Server 启动后应用。

2.2 仓库中的 CRD 源文件与生成方式

需要说明的是,在当前的 libcalico-go 目录内,CRD 的权威源文件位于 libcalico-go/config/crd 下,例如:

这些 YAML 并不是手工维护的,而是由 make gen-crds 目标基于 lib/apis 中的 Go 类型定义自动生成。在 libcalico-go/Makefile 中可以看到其生成流程:

  • 删除 config/crd/*.yaml 旧文件;
  • 使用仓库内打过 Calico 补丁的 controller-gen(CALICO_CONTROLLER_GEN)执行:
$(CALICO_CONTROLLER_GEN) crd:allowDangerousTypes=true,crdVersions=v1 paths=./lib/apis/... output:crd:dir=config/crd/
  • 清理多余的 _.yaml 与 _nodes.yaml 中间文件,去除每个文件开头的 YAML 分隔行,再拉取 Kubernetes SIG 的 ClusterNetworkPolicy CRD,最后用 prettier 统一缩进。

也就是说,libcalico-go/Makefile 中的 gen-crds 目标保证了 CRD 文件与 Go 类型定义始终一致,而 libcalico-go/config/crds.go 与 libcalico-go/config/crds_test.go 则负责在 Go 代码侧把整套 CRD 清单嵌入并校验。

2.3 测试中的实际使用方式

在 FV 测试中,测试代码会通过 controller-runtime client 直接操作这些 CRD 资源。以 libcalico-go/lib/backend/k8s/client_test.go 为例:

  • 先通过 CreateKubernetesClientset 建立与 API Server 的连接;
  • 调用 newCRDClient(config) 创建基于 CRD 的 client;
  • 启动 syncer 后,代码注释明确写道:"Node object is created by applying the mock-node.yaml manifest in advance"(Node 对象由提前应用的 mock-node.yaml 清单创建)。

这印证了 README 中的描述:测试假设环境已就绪,测试代码本身只消费这些资源,而不负责创建它们。这也是为什么这些 manifest 必须在 make cluster-create / make fv-setup 阶段统一应用。

三、预置 Mock Node:为基于 Node 的测试提供"锚点"

3.1 manifest 内容详解

libcalico-go/test/mock-node.yaml 创建了一个名为 127.0.0.1 的 Kubernetes Node 对象:

# This is a Node used by the k8s FV tests.  A number of tests
# rely on this Node existing in the Kubernetes API.
kind: Node
apiVersion: v1
metadata:
  name: "127.0.0.1"
spec:
  podCIDR: "192.168.0.0/24"
status:
  addresses:
    - type: NodeInternalIP
      address: "127.0.0.1/32"
    - type: NodeExternalIP
      address: "5.6.7.8/32"

各字段作用如下:

  • metadata.name: "127.0.0.1":测试环境通常跑在本地(kind 集群),以本机回环地址作为节点名,便于与本地 API Server 的地址保持一致;
  • spec.podCIDR: "192.168.0.0/24":为节点分配 Pod 网段,Calico 的 IPAM 与路由计算会依赖节点的 podCIDR 信息;
  • status.addresses:分别声明 NodeInternalIP(127.0.0.1/32)与 NodeExternalIP(5.6.7.8/32),模拟真实节点上报的地址状态。

3.2 为什么需要这个"假 Node"

从源码看,k8s 后端对 Node 资源的读取逻辑位于 libcalico-go/lib/backend/k8s/resources/node.go。在 FV 测试中,127.0.0.1 这个节点被大量测试用作默认主机,例如在 libcalico-go/lib/backend/k8s/client_test.go 附近,测试直接以 "127.0.0.1" 作为节点名访问。没有这个预置节点,这些测试要么报"节点不存在",要么需要每个测试自己创建节点——README 中"creates mock node object for the tests"这句话的本质,就是用一个统一预置的 Node 对象,为整批 FV 测试提供稳定的、可复用的锚点。

此外,libcalico-go/lib/backend/k8s/resources/node_test.go 对节点资源的转换逻辑有专门测试,mock-node.yaml 的存在让这类测试在真实 API Server 上也能直接运行。

四、预置 Namespace:NetworkPolicy 的作用域前提

4.1 manifest 内容详解

libcalico-go/test/namespaces.yaml 一次创建两个 Namespace:

# Create namespaces required for namespaced NetworkPolicy and NetworkSet tests.
kind: List
metadata:
apiVersion: v1
items:
  - apiVersion: v1
    kind: Namespace
    metadata:
      name: namespace-1
  - apiVersion: v1
    kind: Namespace
    metadata:
      name: namespace-2

这里使用 Kubernetes 原生的 List 类型一次下发多个对象,创建了 namespace-1 与 namespace-2 两个名字空间。

4.2 为什么必须预先创建

README 中解释得很清楚:NetworkPolicy CRD 是 Namespace 作用域(scoped)的资源,只有对应的 Namespace 存在时,才能在其中创建和使用 NetworkPolicy。同样的道理也适用于 NetworkSet。因此测试代码在 namespace-1、namespace-2 中创建 NetworkPolicy/NetworkSet 之前,必须先保证这两个 Namespace 存在于 API Server 中。

这也是 Kubernetes 资源模型的一个通用约束:名字空间级(namespaced)资源不能脱离其所属 Namespace 而存在,而 Namespace 本身是集群级资源,需要单独创建。预置 Namespace 正是为了把这一约束提前满足,让测试专注于策略本身的验证。

五、从 Makefile 看完整的环境装配流程

libcalico-go/Makefile 中与这三个 manifest 相关的目标如下:

5.1 cluster-create:创建 kind 集群并应用清单

cluster-create: kind-cluster-create
	@KUBECONFIG=$(KIND_KUBECONFIG) $(KUBECTL) apply -f test/mock-node.yaml
	@KUBECONFIG=$(KIND_KUBECONFIG) $(KUBECTL) apply -f test/namespaces.yaml

可以看到,cluster-create 目标在创建 kind 集群后,依次应用 test/mock-node.yaml 与 test/namespaces.yaml。Makefile 中的注释也指出:这些资源本应改由需要的测试自行创建,目前为了简化才集中预置("TODO: Remove the need for this extra target")。

5.2 fv-setup / fv:功能测试的完整装配

fv-setup: run-etcd run-etcd-tls cluster-create run-coredns
fv: fv-setup
	$(MAKE) fv-fast
	$(MAKE) fv-teardown

功能测试(FV)的装配链条为:

  1. run-etcd / run-etcd-tls:启动普通与 TLS 加密的 etcd(TLS 版本使用 libcalico-go/test/etcd-ut-certs 下的证书);
  2. cluster-create:创建 kind 集群,并应用 mock-node.yaml 与 namespaces.yaml;
  3. run-coredns:以 libcalico-go/test/coredns 配置启动 CoreDNS,为容器内测试提供 DNS 解析。

fv-fast 则把 kind 的 kubeconfig 挂载进测试容器,并传入 KUBECONFIG=/kubeconfig.yaml 环境变量,随后用 ginkgo 运行带 [Datastore] 标签的测试套件。CRD 的初始化发生在 kind 集群启动与 API Server 就绪之后(README 第 5-6 行明确说明 "This manifest is applied in the Makefile once kubernetes API server is running")。

5.3 单测与本地执行

如果只想在本地跑单元测试,libcalico-go/run-uts 脚本使用 ginkgo 递归运行测试并汇总覆盖率;但 Makefile 中的 ut-cover 目标注明"requires a local etcd and local kubernetes master to be running"——即本地的 etcd 与 Kubernetes master 也需要先就绪,这与 README 强调的"测试前先准备好数据存储与资源"一脉相承。

六、手动复现测试环境:实操清单

结合以上内容,在任何以 Kubernetes 为后端的 libcalico-go 测试场景中,环境预置的完整操作序列如下:

  1. 启动数据存储:准备 etcd(普通或 TLS),或直接使用 kind 创建的 Kubernetes API Server;
  2. 创建集群:执行 kind 集群创建,等待 API Server 就绪;
  3. 应用 CRD:将生成的 CRD 清单(来自 libcalico-go/config/crd 的 7 类 CRD,即 README 中列出的 FelixConfig、BGPPeer、BGPConfig、IPPool、GlobalNetworkPolicy、ClusterInfo、NetworkPolicy)应用到集群;
  4. 应用 Mock Node:执行 kubectl apply -f test/mock-node.yaml;
  5. 应用 Namespace:执行 kubectl apply -f test/namespaces.yaml;
  6. 运行测试:使用 ginkgo 运行带 [Datastore] 标签的功能测试,或运行普通单元测试。

在实际仓库中,步骤 3 的 CRD 应用被 README 描述为"在 Makefile 中于 API Server 运行后应用",而步骤 4、5 被固化在 cluster-create 目标中,因此执行 make fv 即可完成 1-5 步的自动化装配。

七、小结

libcalico-go/test 目录下的三个清单文件构成了 libcalico-go 在 Kubernetes 后端上运行测试的"前置环境契约":

理解这套预置机制,不仅能帮你快速搭建 libcalico-go 的本地/CI 测试环境,也解释了任何 Kubernetes 后端的 Calico 部署在安装时为何必须一并下发 CRD——它们是数据模型在 API Server 中落地的基石。

登录后查看全文
calico