Teleport Helm Chart 单元测试指南:基于 helm-unittest 的快照测试与快照更新实践
【免费下载链接】teleportThe easiest, and most secure way to access and protect all of your infrastructure.项目地址: https://gitcode.com/gh_mirrors/tel/teleport
导读
Helm Chart 在持续迭代中极易出现模板渲染回归:一个看似无害的values.yaml结构调整,可能导致渲染出的 Kubernetes 清单悄然变化。Teleport 仓库在 examples/chart/teleport-kube-agent/tests 目录中维护了一套完整的 Helm Chart 单元测试体系,基于 helm-unittest 为骨架,结合仓库中的 Makefile 目标、测试用例与快照文件,完整讲解这套测试体系的运行方式、快照更新流程及其底层实现,帮助你掌握"改模板 → 跑测试 → 校验快照 → 提交"的标准开发闭环。
一、测试目录概览:tests 目录下发生了什么
teleport-kube-agentChart 的单元测试全部集中在其 tests 子目录中,结构如下:
- tests/README.md:测试说明文档,即本文的主题文档,说明了测试工具、快照更新命令与提交规范;
- 一系列
*_test.yaml文件:每个文件对应一个被测试的模板(Template),内部声明断言与快照点; __snapshot__/目录:存放每次测试运行后自动生成的快照文件(.snap后缀),与测试用例一一对应。
当前仓库中实际存在的测试文件覆盖了 Chart 的主要资源模板:
| 测试文件 | 被测试模板 | 主要验证内容 |
|---|---|---|
| statefulset_test.yaml | statefulset.yaml、config.yaml | 副本数、亲和性、拓扑分布约束、SecurityContext、存储、Sidecar/Init 容器、探针、DNS 等 |
| config_test.yaml | config.yaml | 渲染出的teleport.yamlConfigMap 内容及各类 values 组合下的快照 |
| secret_test.yaml | secret.yaml | Join Token Secret 的生成规则、自定义名称等 |
| clusterrole_test.yaml 等 | 对应的 RBAC 模板 | ClusterRole / Role / Binding 的权限清单 |
| pdb_test.yaml | pdb.yaml | PodDisruptionBudget 配置 |
| psp_test.yaml | psp.yaml | PodSecurityPolicy 配置 |
| job_test.yaml | delete_hook.yaml | 删除钩子 Job |
| podmonitor_test.yaml | podmonitor.yaml | PodMonitor 指标抓取配置 |
| updater_deployment_test.yaml 等 | charts/teleport-kube-updater/templates/* | 子 Chartteleport-kube-updater的 Deployment / Role / RoleBinding |
一个值得注意的细节:所有测试用例文件头部都包含
chart: version: "99.0.0"、appVersion: "99.0.0"的覆盖声明(如 statefulset_test.yaml),其注释写明"Override chart version so snapshots are stable across releases",即固定 Chart 版本号,确保快照不会因版本号变化而失效——这是快照测试保持稳定的关键实践。
二、测试工具与安装机制:helm-unittest 插件
按 tests/README.md 的说明,这套测试运行在helm-unittest(Helm 官方单元测试插件)之上。仓库为插件版本做了精确锁定,见 build.assets/helm-unittest.version,内容为:
v1.0.3同时,build.assets 目录中还维护了插件的校验文件 helm-unittest.sha256,用于在下载插件时做哈希校验,保证供应链安全。
插件本身无需手动安装。根 Makefile 中的helmunit/installed目标(位于 Makefile#L1020-L1025 附近)会自动完成以下工作:
- 创建 Helm 插件目录(
rm -rf后重新mkdir -p); - 从 helm-unittest 官方 Release 下载对应版本(
$(HELM_UNITTEST_VERSION)即v1.0.3)的压缩包; - 使用
grep+ 校验和命令(grep "…helm-unittest.sha256 | … -c -)核对下载文件的哈希; - 解压安装到插件目录。
也就是说,只要本地具备 Helm 与网络,执行测试目标时会自动完成插件引导,无需人工干预。
三、运行测试与更新快照:两份命令的完整链路
3.1 文档给出的两条核心命令
tests/README.md 明确了两条命令,均需在 Teleport 仓库根目录执行:
# 更新快照(当测试失败且确认模板变更是有意为之) make -C build.assets test-helm-update-snapshots # 更新快照后重新运行测试验证 make -C build.assets test-helm执行流程为:先更新快照,再重新运行全部测试,确认全部通过后,将更新过的快照文件与代码改动一起提交。
3.2 底层 Makefile 解析:命令实际做了什么
第一条命令make -C build.assets test-helm-update-snapshots会切换到build.assets目录执行其 Makefile 中定义的桥接目标:
.PHONY: test-helm test-helm: buildbox /bin/bash -c "make -C $(SRCDIR) test-helm" .PHONY:test-helm-update-snapshots test-helm-update-snapshots: buildbox /bin/bash -c "make -C $(SRCDIR) test-helm-update-snapshots"它依赖buildbox目标(构建构建容器),随后在容器内调用根目录 Makefile 中的同名目标。根 Makefile 中的真正实现为:
.PHONY: test-helm test-helm: helmunit/installed $(HELMJANITOR) test .PHONY: test-helm-update-snapshots test-helm-update-snapshots: helmunit/installed $(HELMJANITOR) test --update-snapshots这里有两个关键点:
- 两个目标都以
helmunit/installed为前置依赖,即先确保 helm-unittest 插件已安装; - 二者都通过
$(HELMJANITOR)(仓库维护的 Helm 测试封装脚本/命令)驱动,区别仅在于是否追加--update-snapshots标志——该标志正是 helm-unittest 插件"重写快照文件"的开关。
值得留意的是根 Makefile 中针对 CI 的注释(Makefile#L1027-L1032):CI 环境负责将HELM_PLUGINS指向 helm-unittest 实际安装目录,而 GitHub Actions 构建因 home 目录不同、Helm 默认无法发现插件,所以 CI 中通过CI=true环境变量配合HELM_PLUGINS覆盖插件位置。
四、快照测试的运行机制与错误处理
4.1 快照如何产生与比对
helm-unittest 的快照机制可概括为三步:
- 首次运行:按测试用例渲染模板,将渲染结果(YAML 清单)序列化写入
__snapshot__/目录下对应的.snap文件; - 后续运行:重新渲染并逐字节与
.snap文件比对; - 结果判定:内容一致则通过;不一致则失败并输出 diff,提示开发者判断该差异是否属于预期变更。
仓库中的快照文件示例:config_test.yaml.snap 保存了每个测试场景渲染出的完整teleport.yamlConfigMap 内容,例如:
does not generate a config for clusterrole.yaml: 1: | apiVersion: v1 data: teleport.yaml: |- app_service: enabled: false auth_service: enabled: false db_service: enabled: false discovery_service: enabled: false jamf_service: enabled: false kubernetes_service: enabled: true kube_cluster_name: test-kube-cluster proxy_service: enabled: false ...可以看到,快照把渲染结果"拍平"成带缩进的字符串,任何字段的增删改(哪怕是布尔值的翻转)都会触发快照失配。
4.2 快照错误的三步处理流程(文档核心动作)
当测试因快照失配而失败时,tests/README.md 给出的标准动作是:
- 人工确认变更意图:审阅 diff,确认本次改动确实会(也应当)改变渲染输出——例如新增 values 字段、调整默认值、修改模板结构等;
- 更新快照:执行
make -C build.assets test-helm-update-snapshots,让测试框架把最新的渲染结果写回.snap文件; - 复跑验证:执行
make -C build.assets test-helm确认全部测试通过; - 一并提交:将更新后的快照文件与产生变更的代码/模板改动一同提交(Commit the updated snapshots along with your changes)。
这条流程的本质是"以 diff 作为变更审查的第一道关卡"——快照文件本身即可作为 Code Review 的评审对象,防止模板行为在无人察觉的情况下漂移。
五、测试用例写法剖析:从断言到快照
虽然 README 未展开测试语法,但仓库中的测试用例本身就是最好的学习材料。以下面几个真实用例说明其结构。
5.1 文件级结构
每个*_test.yaml由三部分组成(以 statefulset_test.yaml 为例):
suite: StatefulSet # 测试套件名称,将出现在测试报告中 templates: # 本套件渲染的模板列表 - statefulset.yaml - config.yaml chart: # 固定版本,保证快照稳定 version: "99.0.0" appVersion: "99.0.0" tests: # 用例列表 - it: creates a StatefulSet # 用例描述 ...5.2 三类核心断言语法
- 渲染结果断言:用
equal/notEqual/isNull/isNotNull/contains/notContains等关键字直接校验渲染清单的某个字段。例如副本数校验:
- it: should have one replica when replicaCount is not set template: statefulset.yaml values: - ../.lint/stateful.yaml asserts: - equal: path: spec.replicas value: 1- 快照断言:用
matchSnapshot将(可指定 path 范围的)渲染结果与快照文件比对。例如 Pod 标签校验:
- it: sets Pod labels when specified ... asserts: - equal: path: spec.template.metadata.labels.resource value: pod - matchSnapshot: path: spec.template.spec- 输入注入:通过
values(引用测试 fixtures)、set(内联覆盖 values)和release(模拟 Helm Release 元数据)构造被测场景。例如用set覆盖高可用副本数:
- it: should have multiple replicas when replicaCount is set (using highAvailability.replicaCount) template: statefulset.yaml values: - ../.lint/stateful.yaml set: highAvailability: replicaCount: 3 asserts: - equal: path: spec.replicas value: 35.3 values 测试夹具(.lint 目录)
所有测试共用的输入 values 集中在 .lint 目录,每个文件对应一种测试场景,例如:
stateful.yaml:StatefulSet 基础场景;extra-labels.yaml/annotations.yaml:自定义标签与注解;resources.yaml:资源配额;join-params-token.yaml、join-params-iam.yaml:不同 Join 方法;aws-databases.yaml、azure-databases.yaml、db.yaml:数据库接入场景;updater.yaml:自动更新(teleport-kube-updater)场景;existing-tls-secret-with-ca.yaml:自定义 TLS CA 场景;security-context-empty.yaml:显式清空 SecurityContext 的场景。
测试文件通过values: - ../.lint/xxx.yaml引用这些夹具,与set配合可以覆盖 Chart 的绝大多数配置组合。
5.4 底层实现佐证:从测试反推模板行为
测试用例还精确记录了模板的实现细节,例如 statefulset_test.yaml 断言容器与 Init 容器的默认 SecurityContext 为:
allowPrivilegeEscalation: false capabilities: drop: - ALL readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 9807 seccompProfile: type: RuntimeDefault而 secret_test.yaml 则验证了 Join Token Secret 的生成规则:既未提供authToken也未提供joinParams.tokenName时仍会生成 Secret;提供了二者之一时生成名为teleport-kube-agent-join-token的 Secret;通过secretName或joinTokenSecret.name可自定义 Secret 名称。这些断言直接对应 secret.yaml 模板的渲染逻辑,构成"测试 ↔ 模板"的双向印证。
六、快照与断言的分工:为什么两者都需要
从仓库的测试实践可以看出,Teleport 对两类验证策略的使用是刻意搭配的:
- 断言(assert)负责"语义正确性":显式声明关键字段的期望值(如
spec.replicas、SecurityContext 的runAsNonRoot: true、镜像地址public.ecr.aws/gravitational/teleport-distroless:<version>等)。这类断言即使快照更新后依然生效,能防止有人"顺手"把错误值也写进新快照; - 快照(snapshot)负责"无死角覆盖":将整个
spec.template.spec或完整 ConfigMap 纳入比对,捕捉任何未被显式断言覆盖的渲染细节变化。
二者结合,既保证了关键契约不退化,又保证了整体输出可审计。
七、开发流程落地建议
基于 tests/README.md 与仓库现状,为使用此 Chart 的开发者整理出一份可操作的实践清单:
- 本地环境准备:确保 Helm 可用;插件由
make test-helm自动安装(版本锁定为v1.0.3,哈希由 helm-unittest.sha256 校验); - 修改模板或 values 默认值后:先运行
make -C build.assets test-helm观察失败情况,阅读 diff 判断变更是否符合预期; - 确认变更有意后:执行
make -C build.assets test-helm-update-snapshots更新快照; - 复跑验证:再次执行
make -C build.assets test-helm,确保所有 suite 通过; - 提交规范:将
.snap快照变更与源码改动一并提交,让 Code Review 能看到渲染输出的实际变化; - 新增测试:参照 statefulset_test.yaml 的写法,在
tests/下新增*_test.yaml,并在.lint/中补充对应 values 夹具;新增用例通常应包含一个matchSnapshot断言以建立基线。
结语
Teleport 仓库通过 tests/README.md 这条不足二十行的说明,串联起了一整套可自动安装插件、可一键更新快照、可与 CI 集成的 Helm Chart 单元测试闭环。理解其背后的 Makefile 链路(build.assets桥接目标 → 根Makefile的helmunit/installed+--update-snapshots开关)、快照的比对机制以及"断言 + 快照"的双层校验设计,不仅能让你顺畅地为本仓库的 Chart 改动贡献代码,也能把这套模式迁移到任何需要长期维护的 Helm Chart 项目中。
【免费下载链接】teleportThe easiest, and most secure way to access and protect all of your infrastructure.项目地址: https://gitcode.com/gh_mirrors/tel/teleport
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考