ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Rancher Controller 集成测试指南:基于 envtest 的本地控制平面测试实战

Rancher Controller 集成测试指南:基于 envtest 的本地控制平面测试实战 Rancher Controller 集成测试指南基于 envtest 的本地控制平面测试实战【免费下载链接】rancherComplete container management platform项目地址: https://gitcode.com/GitHub_Trending/ra/rancher导读本文以 Rancher 仓库中 tests/controllers/README.md 为核心系统讲解如何利用 controller-runtime 的envtest为 Rancher 的各类 Controller 编写并运行集成测试。envtest 会为测试拉起一个仅包含 etcd 与 Kubernetes API server 的本地控制平面让真实 Controller 注册其上运行无需任何外部集群即可验证业务逻辑。读完本文你将掌握setup-envtest工具链的安装、通过 Makefile 或手动方式运行测试的方法以及为 Wrangler 控制器与 Norman 控制器编写完整集成测试套件的标准步骤。一、为什么需要 Controller 集成测试Kubernetes Controller 的单元测试往往只覆盖单一函数无法验证创建某资源后 Controller 是否如预期地创建/修改其他资源这类跨资源、事件驱动的核心行为。而完全依赖真实集群又过于笨重需要搭建集群、部署 Rancher、等待 RBAC 与 Admission 生效一次验证循环代价高昂。Rancher 的解决方案是引入 envtest它由 kubebuilder 社区维护会在测试进程内启动一个本地控制平面——一个 etcd 实例加一个 Kubernetes API server不包含 kubelet、scheduler 等其他组件因此轻量且启动迅速。Controller 可以像在真实集群中一样注册到这个控制平面上并接受测试数据的驱动见 tests/controllers/README.md。这套机制对应到仓库中的目录结构如下tests/controllers/ ├── Makefile # make controller-test 入口 ├── README.md # 本文所依据的官方指南 ├── run_controller_tests.sh # 测试运行脚本 ├── authconfig/ # Norman 控制器集成测试 ├── common/ # 测试公共辅助函数 ├── deferRegistration/ # 延迟注册机制测试 ├── feature/ # Wrangler 控制器集成测试官方示例 ├── globalroles/ # 全局角色 RBAC 集成测试 └── oidc/ # OIDC Provider 端到端流程测试二、依赖安装setup-envtest运行这些集成测试前必须安装 setup-envtest它是 envtest 的配套命令行工具负责下载并管理本地控制平面所需的kube-apiserver、etcd二进制文件。安装命令go install sigs.k8s.io/controller-runtime/tools/setup-envtestlatest安装完成后setup-envtest会提供use子命令用于解析并输出指定版本控制平面二进制文件的路径详见 tests/controllers/README.md。值得注意在tests/controllers/run_controller_tests.sh中如果环境中找不到setup-envtest脚本会自动执行go install sigs.k8s.io/controller-runtime/tools/setup-envtestlatest完成安装因此即使跳过手动安装步骤直接运行测试脚本也能自动补齐依赖。三、运行集成测试的两种方式3.1 通过 Makefile 一键运行在 tests/controllers/Makefile 中定义了controller-test目标controller-test: ./run_controller_tests.sh在tests/controllers目录下执行make controller-test该命令会运行 run_controller_tests.sh脚本的核心逻辑如下#!/bin/bash # 1. 若未安装 setup-envtest 则自动安装 if ! command -v setup-envtest /dev/null then go install sigs.k8s.io/controller-runtime/tools/setup-envtestlatest fi # 2. 若未设置 KUBEBUILDER_ASSETS 则自动推导 if [ -z $KUBEBUILDER_ASSETS ]; then KUBEBUILDER_ASSETS$(setup-envtest use --use-env -p path $ENVTEST_K8S_VERSION) export KUBEBUILDER_ASSETS fi # 3. 以 verbose 模式运行 tests/controllers 下全部测试 go test -v $(dirname $0)/...从中可以提取三个关键点ENVTEST_K8S_VERSION环境变量用于指定本地控制平面的 Kubernetes 版本例如1.30。脚本将其透传给setup-envtest use不设置时使用最新可用版本。这一变量同时决定了下载的 apiserver/etcd 版本注意与测试所依赖的 API 保持兼容。KUBEBUILDER_ASSETS环境变量指向控制平面二进制目录envtest 依赖它找到kube-apiserver与etcd。脚本只有在变量为空时才自动推导因此显式设置后脚本会直接复用。测试范围go test -v递归执行tests/controllers/下所有_test.go包即包含feature、authconfig、globalroles、oidc、deferRegistration等多个套件。3.2 手动运行安装好setup-envtest后手动运行的关键是先把KUBEBUILDER_ASSETS导出为控制平面二进制路径export KUBEBUILDER_ASSETS$(setup-envtest use -p path)如果需要指定版本在use后追加版本号即可例如export KUBEBUILDER_ASSETS$(setup-envtest use -p path 1.30)设置完环境变量后运行方式与普通 Go 测试完全一致go test ./tests/controllers/...或按包单独运行go test ./tests/controllers/feature/...四、开发集成测试统一的初始化两步曲无论测试哪种 Controller所有集成测试都以完全相同的两步作为开局见 tests/controllers/README.md。4.1 第一步启动本地 Kubernetes 服务器创建envtest.Environment并调用Start()返回一个可用于构造各种 client 的 REST 配置testEnv envtest.Environment{} restCfg, err : testEnv.Start()envtest.Environment是 controller-runtime 提供的核心结构Start()会在后台拉起 apiserver 与 etcd 进程测试结束时通过testEnv.Stop()优雅回收。4.2 第二步创建测试所需的 CRDRancher 大量依赖management.cattle.io/v3等自定义资源因此测试必须先把相关 CRD 注册到本地控制平面。仓库通过 wrangler 的 CRD 工厂完成import github.com/rancher/wrangler/v3/pkg/crd factory, err : crd.NewFactoryFromClient(restCfg) err factory.BatchCreateCRDs(ctx, crd.CRD{ SchemaObject: CRD struct, NonNamespace: true/false, })其中SchemaObject传入具体的 CRD Go 结构体如v3.Feature{}NonNamespace标记该资源是否为集群级资源。这一步实际上是把仓库中由代码生成器产出的 CRD 定义见 pkg/apis/management.cattle.io/v3动态安装到 envtest 控制平面上从而让对应的 informer/client 正常工作。由于这两步几乎每个套件都要用到仓库将其封装进了 tests/controllers/common/common.go 的RegisterCRDs辅助函数例如 oidc 测试中只需一行common.RegisterCRDs(s.ctx, s.T(), restCfg, crd.CRD{SchemaObject: apimgmtv3.Token{}, NonNamespace: true}, crd.CRD{SchemaObject: apimgmtv3.User{}, NonNamespace: true}, crd.CRD{SchemaObject: apimgmtv3.OIDCClient{}, NonNamespace: true, Status: true}, )完成这两步之后流程便按控制器类型分叉Wrangler 控制器与 Norman 控制器的装配方式不同。五、Wrangler 控制器集成测试Wrangler 是 Rancher 自研的通用 controller 框架仓库中绝大多数控制器都构建于其上。官方以 feature 控制器 为例给出完整的四步装配流程。5.1 第一步创建 Wrangler 上下文import github.com/rancher/rancher/pkg/wrangler wranglerContext, err : wrangler.NewContext(ctx, nil, restCfg)wrangler.NewContext会基于restCfg一次性构造出所有 Rancher 资源类型的 client、cache 与 controller factory是后续注册与查询的统一入口。5.2 第二步注册控制器import github.com/rancher/rancher/pkg/controllers/management/feature feature.Register(ctx, wranglerContext)以 feature 控制器为例其Register函数定义在 pkg/controllers/management/feature/feature_handler.go 中负责把该控制器的 handler 挂载到 wrangler 上下文中对应的资源上。测试中直接调用与生产代码完全相同的Register这正是集成测试的价值所在。5.3 第三步创建并启动控制器工厂import k8s.io/apimachinery/pkg/runtime/schema controllerFactory : wranglerContext.ControllerFactory.ForResourceKind(schema.GroupVersionResource{ Group: management.cattle.io, Version: v3, Resource: features, }, Feature, false) err : controllerFactory.Start(ctx, 1)ForResourceKind按GroupVersionResource定位目标资源并返回对应的共享控制器第二个参数是 Kind 名第三个参数表示是否按命名空间区分随后Start(ctx, 1)以 1 个 worker 启动该控制器的 informer 与处理循环。5.4 第四步可选启动所需的其它资源缓存如果被测控制器在其 handler 中还会读取其它资源那么这些资源的 cache 也必须创建并启动否则读取会直接返回空或报错。feature 控制器的 handler 会读取NodeDriver因此测试中补充如下_, err : wranglerContext.ControllerFactory.SharedCacheFactory().ForKind(schema.GroupVersionKind{ Group: management.cattle.io, Version: v3, Kind: NodeDriver, }) err wranglerContext.ControllerFactory.SharedCacheFactory().StartGVK(s.ctx, schema.GroupVersionKind{ Group: management.cattle.io, Version: v3, Kind: NodeDriver, })ForKind预创建缓存StartGVK真正启动针对该 GVK 的 informer。5.5 完整示例FeatureTestSuite完成四步后控制器已注册并运行测试即可通过 wrangler 上下文直接操作资源例如wranglerContext.Mgmt.Feature().Get(feature-name, metav1.GetOptions{})以 feature/feature_test.go 为例整个套件使用 testify 的suite模式组织SetupSuite依次完成启动 envtest →BatchCreateCRDs创建Feature与NodeDriver两个集群级 CRD → 创建 wrangler 上下文 → 通过mcm.BuildScaledContext与NewManagementContext构建管理上下文 →feature.Register注册控制器 →ForResourceKindStart启动工厂 → 启动NodeDriver缓存 → 预创建harvesterNodeDriver 并调用features.InitializeFeatures初始化 feature 资源。随后TestHarvesterFeature通过把harvesterNodeDriver 的Spec.Active置为false再触发一次 feature 同步然后断言 NodeDriver 被控制器重新置回Activetrue来验证控制器的调和逻辑TestHarvesterBaremetalFeature则验证启用 baremetal feature 后控制器会为 feature 添加feature.cattle.io/experimentaltrue注解并将harvesterfeature 的Spec.Value置为true。断言普遍使用assert.EventuallyWithT配合tick 1s、duration 10s轮询等待以容忍控制器异步调和的时序。5.6 关键细节触发同步的辅助手法测试中有时需要主动触发一次资源调和比如triggerFeatureSyncfunc (s *FeatureTestSuite) triggerFeatureSync(name string) { h, _ : s.wranglerContext.Mgmt.Feature().Get(name, metav1.GetOptions{}) hCopy : h.DeepCopy() if hCopy.Annotations nil { hCopy.Annotations map[string]string{test: test} } else { hCopy.Annotations[test] test } s.wranglerContext.Mgmt.Feature().Update(hCopy) }通过修改资源注解触发 Update 事件从而驱动控制器重新执行调和逻辑。这是集成测试中主动制造事件的常见技巧尤其适用于验证非 feature 资源的联动修改。六、Norman 控制器集成测试原版 README 中 Norman Controller 部分标注为TODO但仓库实际上已经沉淀了一套成熟的 Norman 控制器测试范式以 authconfig/authconfig_test.go 为代表可视为对该 TODO 的落地补充。Normanlasso controller与 Wrangler 的差异在于它需要构建ManagementContext并通过 Norman 的ControllerFactory按 Kind 启动控制器。核心装配流程如下// 1. 启动 envtest 并注册 CRDToken、AuthConfig、User s.testEnv envtest.Environment{} restCfg, err : s.testEnv.Start() factory, err : crd.NewFactoryFromClient(restCfg) err factory.BatchCreateCRDs(s.ctx, crd.CRD{SchemaObject: v3.Token{}, NonNamespace: true}, crd.CRD{SchemaObject: v3.AuthConfig{}, NonNamespace: true}, crd.CRD{SchemaObject: v3.User{}, NonNamespace: true}, ).BatchWait() // 2. 创建 wrangler 上下文与 management 上下文 wranglerContext, err : wrangler.NewContext(s.ctx, nil, restCfg) scaledContext, clusterManager, _, err : multiclustermanager.BuildScaledContext(s.ctx, wranglerContext, multiclustermanager.Options{}) s.managementContext, err scaledContext.NewManagementContext() // 3. 注册 Norman 控制器 auth.RegisterEarly(s.ctx, s.managementContext, clusterManager) // 4. 启动 Norman 控制器与 Wrangler 缓存 common.StartNormanControllers(s.ctx, t, s.managementContext, schema.GroupVersionKind{Group: management.cattle.io, Version: v3, Kind: AuthConfig}, schema.GroupVersionKind{Group: management.cattle.io, Version: v3, Kind: User}, ) common.StartWranglerCaches(s.ctx, t, s.managementContext.Wrangler, schema.GroupVersionKind{Group: management.cattle.io, Version: v3, Kind: Token}, )其中BuildScaledContext见 pkg/multiclustermanager是 Rancher 生产初始化路径中的关键一步测试直接复用了它因此 Norman 控制器的注册方式与生产环境保持一致。该测试的业务场景也很有代表性TestTokensCleanup验证当认证提供方被禁用并打上清理注解后控制器应删除该提供方名下的全部 Token。测试先创建openldap的AuthConfigEnabled: true并分别创建 2 个openldapToken 与 2 个localToken随后把 AuthConfig 置为Enabled: false并设置auth.CleanupAnnotation注解最后用assert.EventuallyWithT轮询断言openldapToken 被全部清理而localToken 保留——完整覆盖了控制器禁用即清理的核心逻辑。七、公共测试辅助函数为了让各套件避免重复样板代码tests/controllers/common/common.go 提供了四个高度复用的函数函数作用RegisterCRDs(ctx, t, restCfg, crds...)基于 REST 配置批量注册 CRD 并等待就绪等价于第二节的初始化两步StartNormanControllers(ctx, t, m, gvk...)按management.cattle.io等 GVK 从 Norman 的ControllerFactory.ForKind取出控制器并逐个Start(ctx, 1)启动StartWranglerControllers(ctx, t, w, gvk...)从 Wrangler 的ControllerFactory.ForKind取出控制器并启动用于 Wrangler 控制器StartWranglerCaches(ctx, t, w, gvk...)对指定 GVK 调用SharedCacheFactory().StartGVK启动缓存 informer供控制器 handler 读取其它资源例如 globalroles 测试同时使用了StartWranglerControllers启动 GlobalRole/GlobalRoleBinding 控制器与StartWranglerCaches预热 ClusterRole、RoleBinding、Namespace 等 7 类资源的缓存这正是跨资源联动型控制器的典型测试装配。八、注意事项与最佳实践结合 tests/controllers 下各套件的实际写法可以总结出以下可复用的经验统一使用 suite 组织测试所有套件均基于testify/suite把环境初始化放在SetupSuite、清理放在TearDownSuite调用s.cancel()取消 context 并s.testEnv.Stop()测试方法则各自独立逻辑清晰且可并行管理。对异步行为使用 Eventually 断言控制器调和是异步的assert.EventuallyWithT配合tick轮询间隔与duration超时上限是标配。feature 测试使用1s/10sglobalroles 因涉及跨 RBAC 资源传播使用1s/20s。注意 envtest 的能力边界envtest 不运行 kube-controller-manager、garbage collector 等组件因此测试中不能依赖自动 GC 验证删除逻辑——globalroles 测试的注释明确说明这一点改为断言所创建资源携带正确的OwnerReference与归属标签如authz.management.cattle.io/globalroletrue从而间接保证删除能够级联生效。为控制器依赖的资源预置数据如果控制器 handler 会读取某个资源务必像 feature 测试创建harvesterNodeDriver 那样先创建该资源否则控制器可能因找不到依赖而静默失败。版本选择策略ENVTEST_K8S_VERSION不设置时默认取最新可用版本建议在 CI 中固定具体版本如1.30避免 apiserver 版本漂移导致测试结果不稳定。九、总结Rancher 的 Controller 集成测试体系以 envtest 本地控制平面为底座通过setup-envtest管理 apiserver/etcd 二进制以tests/controllers/Makefile与run_controller_tests.sh提供一键运行入口。对开发者而言掌握启动 envtest → 注册 CRD → 创建 wrangler/management 上下文 → Register 控制器 → 启动工厂与缓存这一标准流水线再结合 common/common.go 的辅助函数与 feature/feature_test.go 的完整范例即可为任意 Rancher 控制器编写出贴近生产路径、可稳定复现的集成测试在无外部集群的前提下持续保障控制器调和逻辑的正确性。【免费下载链接】rancherComplete container management platform项目地址: https://gitcode.com/GitHub_Trending/ra/rancher创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进