- 服务网格
- 云原生
- 可观测性
【免费下载链接】linkerd2
Ultralight, security-first service mesh for Kubernetes. Main repo for Linkerd 2.x.
policy-test(linkerd-policy-test)是 Linkerd 2.x 仓库中专门为**策略控制器(policy controller)**编写的 Rust 集成测试 crate,它通过真实 Kubernetes 集群(本地 k3d 或 CI 集群)验证策略控制器对各类策略资源的准入校验、路由状态上报与出入站 gRPC 策略下发行为。本文以 policy-test/README.md 为主线,结合仓库源码梳理该 crate 的定位、测试组织方式、运行入口(just policy-test)以及本地与 CI 两种执行路径,帮助读者理解并上手这套测试体系。
一、policy-test 是什么:定位与核心目标
policy-test/README.md 开宗明义地指出:"Thepolicy-testcrate includes integration tests for the policy controller"——即这个 crate 的全部职责就是为 Linkerd 的策略控制器提供集成测试。它并不测试代理(proxy)本身,也不测试 CLI 或仪表盘,而是聚焦于策略控制器这一核心组件。
从仓库结构看,policy-test 与策略控制器的实现代码(Rust 编写)相邻而居:
- 策略控制器实现:
policy-controller/(包含core/、grpc/、k8s/等子 crate); - 策略控制器集成测试:
policy-test/(本主题)。
两者的依赖关系在 policy-test/Cargo.toml 中一目了然:
linkerd-policy-controller-core = { path = "../policy-controller/core" } linkerd-policy-controller-k8s-api = { path = "../policy-controller/k8s/api" } linkerd-policy-controller-grpc = { path = "../policy-controller/grpc" }即测试 crate 直接依赖策略控制器的核心逻辑、Kubernetes API 类型封装和 gRPC 服务定义,这使得测试既可以驱动真实的 API Server 校验策略资源,也可以通过 gRPC 客户端断言控制器下发的策略。
一句话概括其价值:在真实集群环境中,端到端地确认"用户创建的 Server、AuthorizationPolicy、HttpRoute、EgressNetwork 等资源,能否被策略控制器正确接受/拒绝,并转化为代理可见的策略配置"。
二、crate 的组成:src 公共工具库与 tests 测试套件
policy-test 目录内部划分为两层,这种"公共测试工具 + 独立测试用例"的结构是 Rust 集成测试的标准做法:
policy-test/ ├── Cargo.toml # crate 元信息与 feature 开关 ├── README.md # 运行说明(本文主体) ├── src/ # 可复用的测试工具库(对外导出为 linkerd_policy_test) │ ├── lib.rs # 公共 API:命名空间管理、资源创建/删除/等待等 │ ├── admission.rs # 准入测试辅助:accepts / rejects │ ├── bb.rs # "blocks/breaks" 测试辅助 │ ├── curl.rs # 在集群内运行 curl 并发起 HTTP 请求 │ ├── grpc.rs # 策略 gRPC 客户端(inbound/outbound) │ ├── outbound_api.rs │ ├── test_route.rs # 路由状态测试抽象 │ └── web.rs # 构造 web 测试工作负载(Service/Pod) └── tests/ # 实际集成测试用例(每个文件一组主题) ├── admit_*.rs # 准入测试:Server、ServerAuthorization、各类 Route 等 ├── e2e_*.rs # 端到端测试:授权策略、HTTP 路由、限流等 ├── inbound_api*.rs / outbound_api*.rs # gRPC 策略 API 测试 └── ...2.1 src/lib.rs:一切测试的地基
policy-test/src/lib.rs 是测试公共库的入口,其模块声明为:
pub mod admission; pub mod bb; pub mod curl; pub mod grpc; pub mod outbound_api; pub mod test_route; pub mod web;其中几个关键基础设施值得展开:
1. 临时命名空间机制with_temp_ns(lib.rs)。几乎每个测试都以with_temp_ns(|client, ns| async move { ... })的形式运行:它初始化一个 Kubernetes 客户端,创建一个名为linkerd-policy-test-XXXXXX(6 位随机小写字母数字后缀)的命名空间,等待 default ServiceAccount 就绪后执行测试闭包,测试结束自动删除命名空间。这保证了测试之间互不干扰。两个环境变量控制其行为:
POLICY_TEST_CONTEXT:指定 kubeconfig 中的 context(缺省时用kube::Client::try_default());POLICY_TEST_NO_CLEANUP:设置后保留测试命名空间,便于失败后现场排查。
2. 资源的创建/删除/更新辅助(lib.rs)。提供create、delete、update等泛型函数,统一以linkerd-policy-test作为 field manager(PostParams { field_manager: Some("linkerd-policy-test") }),创建对象时用tracing::trace!(?obj, "Creating")记录日志。对集群级资源(如 Namespace)则使用create_cluster_scoped/delete_cluster_scoped。
3. 条件等待工具(lib.rs)。await_condition基于 kube 的kube::runtime::wait::await_condition封装,超时 60 秒:
time::timeout( time::Duration::from_secs(60), kube::runtime::wait::await_condition(api, name, cond), ) .await .expect("condition timed out") .expect("API call failed")在此基础上派生了大量语义化等待函数:create_ready_pod(创建 Pod 并等待所有容器 ready,见 lib.rs)、await_pod_ip、await_route_accepted(HttpRoute 被策略控制器接受)、await_gateway_route_status、await_grpc_route_status、await_tls_route_status、await_egress_net_status等,以及endpoints_ready判断 Endpoints 是否已填充地址(lib.rs)。这些函数大量使用is_status_accepted检查资源的Accepted=True状态条件(lib.rs)。
4. 服务与 EgressNetwork 构造器(lib.rs)。create_service、create_opaque_service(带config.linkerd.io/opaque-ports注解)、create_egress_network、create_annotated_service等,直接构造并提交测试资源。默认的 EgressNetwork 使用TrafficPolicy::Allow(lib.rs)。
2.2 admission.rs:准入测试的两板斧
policy-test/src/admission.rs 极其精简但非常关键,它把"资源能否通过准入校验"抽象成两个函数:
accepts(f):在临时命名空间内创建资源并断言api.create(...)成功;rejects(f):在临时命名空间内创建资源并断言api.create(...)失败(res.expect_err("resource must not apply"))。
所有admit_*测试文件都是在这两个函数之上编写的。例如测试一个合法的 Server 应被接受、一个引用了不存在 Service 的 Server 应被拒绝等。
2.3 grpc.rs:直连策略 gRPC API
policy-test/src/grpc.rs 实现了一个策略 gRPC 客户端:通过 Kubernetes API 发现 destination 控制器 Pod,再利用 port-forward 连接运行中的实例,进而调用:
InboundServerPoliciesClient(inbound 服务器策略);OutboundPoliciesClient(outbound 策略)。
该客户端还导出了两个断言宏,用于校验默认策略的形状(grpc.rs):
assert_is_default_all_unauthenticated!($config); // 断言 labels 正确且仅含 1 条 all-unauthenticated 授权 assert_default_all_unauthenticated_labels!($config); // 断言 labels 为 ("group","")、("kind","default")、("name","all-unauthenticated")这组工具支撑了inbound_api*.rs、outbound_api*.rs等文件中对策略控制器 gRPC 输出的直接断言。
2.4 curl.rs / web.rs / bb.rs:端到端流量测试的抓手
curl.rs:在集群内启动 curl Pod,向目标 URL 发起 HTTP 请求并收集状态码/响应,是验证"策略真正生效于数据面"的关键手段;web.rs:构造标准的 web 测试工作负载(Service + Pod),供 e2e 测试复用;bb.rs:提供"blocks/breaks"类辅助,用于构造破坏性/边界场景。
三、tests/ 目录:四类测试主题
policy-test/tests/下共有 30 个集成测试文件,按文件名前缀可清晰分为四类:
| 类别 | 文件(节选) | 验证目标 |
|---|---|---|
| 准入校验 | admit_authorization_policy.rs、admit_server.rs、admit_server_authorization.rs、admit_egress_networks.rs、admit_network_authentication.rs、admit_meshtls_authentication.rs、admit_http_route.rs、admit_http_route_gateway.rs、admit_grpc_route.rs、admit_tcp_route.rs、admit_tls_route.rs、admit_http_local_ratelimit_policy.rs | 各种策略/路由资源的 Webhook 准入:合法资源被接受、非法资源被拒绝 |
| 端到端流量 | e2e_authorization_policy.rs、e2e_http_routing.rs、e2e_server_authorization.rs、e2e_egress_network.rs、e2e_failure_accrual.rs、e2e_http_local_ratelimit_policy.rs、e2e_appprotocol.rs、e2e_audit.rs | 通过真实 Pod + curl 验证策略在数据面实际生效 |
| 入站策略 API | inbound_api.rs、inbound_api_external_workload.rs、inbound_http_route_status.rs | 通过 gRPC 断言入站服务器策略内容与 HttpRoute 状态 |
| 出站策略 API | outbound_api.rs、outbound_api_http.rs、outbound_api_grpc.rs、outbound_api_tcp.rs、outbound_api_app_protocol.rs、outbound_api_egress_network.rs、outbound_api_failure_accrual.rs、outbound_http_route_status.rs | 通过 gRPC 断言出站策略(HTTP/gRPC/TCP/Egress 等) |
以e2e_http_routing.rs为例,其测试流程完整呈现了"建资源 → 起工作负载 → 发请求 → 断言结果"的端到端模式(e2e_http_routing.rs):
#[tokio::test(flavor = "current_thread")] async fn path_based_routing() { with_temp_ns(|client, ns| async move { // 1. 创建 policy.linkerd.io 的 HttpRoute:/valid 路由到 web,/invalid 路由到不存在的 foobar create(&client, k8s::policy::HttpRoute { /* parent_refs 指向 core Service "web",端口 80 ... */ }).await; // 2. 创建 web Service 与 Pod,并等待 Endpoints 就绪 tokio::join!( create(&client, web::service(&ns)), create_ready_pod(&client, web::pod(&ns)) ); await_condition(&client, &ns, "web", endpoints_ready).await; // 3. 用集群内 curl 分别请求 /valid、/invalid、/notfound let curl = curl::Runner::init(&client, &ns).await; let (valid, invalid, notfound) = tokio::join!( curl.run("curl-valid", "http://web/valid", LinkerdInject::Enabled), curl.run("curl-invalid", "http://web/invalid", LinkerdInject::Enabled), curl.run("curl-notfound", "http://web/notfound", LinkerdInject::Enabled), ); // 4. 断言各请求的 HTTP 状态码 let (valid_status, invalid_status, notfound_status) = tokio::join!( valid.http_status_code(), invalid.http_status_code(), notfound.http_status_code() ); // ... 期望 /valid 成功、/invalid 与 /notfound 被路由规则拒绝 }).await; }可以看到测试会主动向策略控制器提交policy.linkerd.io/HttpRoute,并用LinkerdInject::Enabled控制请求方是否注入 sidecar,从而区分不同数据面行为。
四、如何运行:just policy-test一站式入口
README 给出了本地运行方式,一行命令即可:
:; just policy-test这行命令背后是 justfile 中的一个复合配方,它把整套流程串联了起来:
policy-test: linkerd-install policy-test-deps-load policy-test-run && policy-test-cleanup linkerd-uninstall即依次执行:安装 Linkerd → 加载测试依赖镜像 → 运行测试 → 清理测试命名空间 → 卸载 Linkerd。配方中用到POLICY_TEST_CONTEXT环境变量,默认指向k3d-<k3d-name>这个 k3d 集群 context(justfile)。
4.1 子配方拆解
justfile 中还有几个可以单独调用的子配方:
policy-test-run *flags:只跑测试,不装 Linkerd(justfile)。实际执行cd policy-test && cargo test <feature-flags> <flags>;policy-test-build:只编译测试(cargo test --no-run),用于提前暴露编译错误(justfile);policy-test-cleanup:删除所有带linkerd-policy-test标签的命名空间,并轮询等待删除完成(justfile):
{{ _kubectl }} delete ns --selector='linkerd-policy-test' @while [ $({{ _kubectl }} get ns --selector='linkerd-policy-test' -o json |jq '.items | length') != "0" ]; do sleep 1 ; donepolicy-test-deps-pull/policy-test-deps-load:拉取并导入测试所需的镜像(justfile),包括chainguard/kubectl:latest-dev、curlimages/curl:latest、fortio/fortio:latest、ghcr.io/olix0r/hokay:latest等,其中镜像加载带有重试逻辑(失败后 sleep 1 重试,最多 3 次)。
4.2 feature 开关:Gateway API 版本适配
由于不同版本的 Gateway API bundle 提供不同的 API 版本,policy-test 通过 Cargo feature 控制测试范围(policy-test/Cargo.toml):
[features] default = ["gateway-api-experimental", "gateway-api-tls-route-v1"] gateway-api-experimental = [] # 启用仅存在于 Gateway API experimental 通道的资源测试 gateway-api-tls-route-v1 = ["gateway-api-tls-route"] # 按 v1 版本测试 TLSRoute gateway-api-tls-route-v1alpha2 = ["gateway-api-tls-route"] # 按 v1alpha2 版本测试 TLSRoute gateway-api-tls-route = [] # 隐含标记:TLSRoute 测试开启(不应单独启用)为什么 TLSRoute 需要如此细致的开关?Cargo.toml 中的注释解释得很清楚:TLSRoute 没有任何一个版本能被所有受支持的 Gateway API bundle 提供——v1alpha2是 v1.2~v1.4 以及 linkerd-crds chart 内嵌 CRD 唯一提供的版本,而v1是 v1.5 标准通道唯一提供的版本。因此测试必须按实际部署的 Gateway API 版本,用gateway-api-tls-route-v1或gateway-api-tls-route-v1alpha2来对齐要寻址的版本。对应地,lib.rs 中用条件编译在两种 TLSRoute 类型之间做别名切换:
#[cfg(all(feature = "gateway-api-tls-route", not(feature = "gateway-api-tls-route-v1alpha2")))] pub use linkerd_policy_controller_k8s_api::gateway::TLSRoute; #[cfg(feature = "gateway-api-tls-route-v1alpha2")] pub use linkerd_policy_controller_k8s_api::gateway::TLSRouteV1Alpha2 as TLSRoute;justfile 会根据环境变量GATEWAY_API_TLS_ROUTE(取值v1/v1alpha2/none)和GATEWAY_API_CHANNEL(experimental或其他)动态拼出 feature 参数(justfile):
_policy-test-flags := "--no-default-features" + (if GATEWAY_API_CHANNEL == "experimental" { " --features=gateway-api-experimental" } else { "" }) + _policy-test-tls-route-flags可见默认启用--no-default-features,由外部显式指定测试目标版本,以保证测试与集群中实际部署的 Gateway API 版本严格一致。
五、在 CI 中如何运行
README 提到 CI 运行方式参见工作流 integration.yml。该工作流中的 policy-test job 展示了完整的 CI 执行链(integration.yml):
- 准备环境:安装指定版本的 Rust 工具链、k3d、yq、cargo-nextest;
- 预编译:
just policy-test-build(先编译测试,避免运行阶段暴露编译问题); - 创建测试集群:
just k3d-k8s='<matrix 版本>' k3d-create,并在不同 Kubernetes 版本矩阵上运行; - 安装 Linkerd:加载构建产物中的 controller/proxy 镜像后执行
just linkerd-install; - 加载依赖镜像:
policy-test-deps-load带有重试循环(失败后 sleep 10 秒重试,最多 6 次),并注释说明"镜像加载在 CI 中容易失败,所以要重试"; - 运行测试:
just policy-test-run --jobs=1,并设置NEXTEST_RETRIES: 3让不稳定用例自动重试 3 次(nexte.st 的 retries 机制)。
其中--jobs=1与 nextest 的重试设置,说明这套集成测试在真实集群环境下具有一定时序敏感性,通过串行执行与自动重试来提高结果可靠性。
六、自定义与调试实践
综合以上源码细节,可以总结出几条实用技巧:
- 指定集群 context:本地有多个集群时,用
POLICY_TEST_CONTEXT=<context>覆盖默认的k3d-<name>指向; - 保留现场:测试失败想排查时设置
POLICY_TEST_NO_CLEANUP=1,命名空间与其中资源会保留(with_temp_ns仅在未设置该变量时删除命名空间,见 lib.rs);同时with_temp_ns在测试失败后会自动打印命名空间内所有 Pod 的reason/message与各容器状态,便于定位; - 单独编译:仅想验证代码可编译时使用
just policy-test-build; - 只跑测试:集群已就绪、Linkerd 已安装时,用
just policy-test-run --jobs=1直接执行,配合NEXTEST_RETRIES处理偶发不稳定; - 调整 Gateway API 目标版本:根据集群部署的 Gateway API 版本,设置
GATEWAY_API_TLS_ROUTE=v1|v1alpha2|none与GATEWAY_API_CHANNEL=experimental|standard; - 查看详细日志:测试库默认的日志过滤为
trace,tower=info,hyper=info,kube=info,h2=info(lib.rs),可通过RUST_LOG覆盖,帮助跟踪资源创建、等待条件与 gRPC 断言过程。
七、总结
policy-test 是 Linkerd 策略控制器质量保障的核心支柱。它以"真实集群 + 真实流量"的方式,把策略控制器的准入校验、路由状态上报与 gRPC 策略下发三条链路全部纳入自动化测试;通过临时命名空间、条件等待、feature 化版本适配等设计,兼顾了测试的隔离性、稳定性和对不同 Gateway API 版本的兼容性。无论你是想为策略控制器贡献新测试,还是想理解 Linkerd 策略体系的行为,policy-test/README.md 与 policy-test/ 目录下的源码都是最佳起点。
- 服务网格
- 云原生
- 可观测性
【免费下载链接】linkerd2
Ultralight, security-first service mesh for Kubernetes. Main repo for Linkerd 2.x.
相关推荐
Spinnaker Clouddriver 的 Amazon ECS 集成测试:测试架构、运行方式与编写指南
Spinnaker Clouddriver 的 Amazon ECS 集成测试:测试架构、运行方式与编写指南 导读 Spinnaker 的 clouddrive
后端DevOps云原生微服务wasm-bindgen 测试框架的活教材:深入解析 sample 测试 crate 与 wasm-bindgen-test 运行机制
wasm bindgen 测试框架的活教材:深入解析 sample 测试 crate 与 wasm bindgen test 运行机制 导读 wasm bind
开发工具Pyrefly VSCode 扩展集成测试指南:测试架构、运行方式与源码剖析
Pyrefly VSCode 扩展集成测试指南:测试架构、运行方式与源码剖析 本文以 Pyrefly 仓库中 lsp/src/test/README.md ht
开发工具静态分析IDE代码质量
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考