Skip to content

Commit 0274e86

Browse files
authored
test(policy): Reduce duplication in outbound policy API tests (#13543)
This change refactors the outbound policy API integration tests to reduce duplication and to be more principled about what combinations of routes and parents are tested. The outbound policy API supports many route types (Linkerd HTTPRoute, gateway HTTPRoute, GRPCRoute, TCPRoute, and TLSRoute) and each of these routes may be attached to up to two parent types (Service and EgressNetwork). Many behaviors of the outbound policy API are shared between these route and parent types and in order to test these behaviors, many tests were duplicated for each combination of types. This resulted in a large amount of duplication and made it difficult to determine exactly which combinations were being tested and if any combinations were missed. It also required that the duplicated tests be kept in sync and allowed the possibility that the tests could accidentally drift apart over time resulting in inconsistent test coverage. We introduce the TestRoute and TestParent traits which capture the common properties of routes and parents respectively and refactor the outbound policy API tests to be generic over these types and then explicitly invoke the test with the combinations of types to which the tested behavior applies. In cases where the tested behavior is specific to a particular type rather than being common to all types, a type specific test remains. Signed-off-by: Alex Leong <[email protected]>
1 parent a59e02c commit 0274e86

14 files changed

+3684
-6252
lines changed

policy-test/src/grpc.rs

-16
Original file line numberDiff line numberDiff line change
@@ -291,22 +291,6 @@ impl OutboundPolicyClient {
291291
Ok(rsp.into_inner())
292292
}
293293

294-
pub async fn watch(
295-
&mut self,
296-
ns: &str,
297-
svc: &k8s::Service,
298-
port: u16,
299-
) -> Result<tonic::Streaming<outbound::OutboundPolicy>, tonic::Status> {
300-
let address = svc
301-
.spec
302-
.as_ref()
303-
.expect("Service must have a spec")
304-
.cluster_ip
305-
.as_ref()
306-
.expect("Service must have a cluster ip");
307-
self.watch_ip(ns, address, port).await
308-
}
309-
310294
pub async fn watch_ip(
311295
&mut self,
312296
ns: &str,

policy-test/src/lib.rs

+35-24
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub mod bb;
66
pub mod curl;
77
pub mod grpc;
88
pub mod outbound_api;
9+
pub mod test_route;
910
pub mod web;
1011

1112
use kube::runtime::wait::Condition;
@@ -15,6 +16,7 @@ use linkerd_policy_controller_k8s_api::{
1516
ResourceExt,
1617
};
1718
use maplit::{btreemap, convert_args};
19+
use test_route::TestRoute;
1820
use tokio::time;
1921
use tracing::Instrument;
2022

@@ -206,24 +208,25 @@ pub async fn await_pod_ip(client: &kube::Client, ns: &str, name: &str) -> std::n
206208
.expect("pod IP must be valid")
207209
}
208210

209-
// Waits until an HttpRoute with the given namespace and name has a status set
210-
// on it, then returns the generic route status representation.
211-
pub async fn await_route_status(
212-
client: &kube::Client,
213-
ns: &str,
214-
name: &str,
215-
) -> k8s::policy::httproute::RouteStatus {
216-
use k8s::policy::httproute as api;
217-
let route_status = await_condition(client, ns, name, |obj: Option<&api::HttpRoute>| -> bool {
218-
obj.and_then(|route| route.status.as_ref()).is_some()
219-
})
220-
.await
221-
.expect("must fetch route")
222-
.status
223-
.expect("route must contain a status representation")
224-
.inner;
225-
tracing::trace!(?route_status, name, ns, "got route status");
226-
route_status
211+
// Waits until an HttpRoute with the given namespace and name has been accepted.
212+
pub async fn await_route_accepted<R: TestRoute>(client: &kube::Client, route: &R) {
213+
await_condition(
214+
client,
215+
&route.namespace().unwrap(),
216+
&route.name_unchecked(),
217+
|obj: Option<&R>| -> bool {
218+
obj.map_or(false, |route| {
219+
let conditions = route
220+
.conditions()
221+
.unwrap_or_default()
222+
.into_iter()
223+
.cloned()
224+
.collect::<Vec<_>>();
225+
is_status_accepted(&conditions)
226+
})
227+
},
228+
)
229+
.await;
227230
}
228231

229232
// Waits until an HttpRoute with the given namespace and name has a status set
@@ -591,17 +594,25 @@ pub fn mk_egress_net(ns: &str, name: &str) -> k8s::policy::EgressNetwork {
591594
}
592595

593596
#[track_caller]
594-
pub fn assert_resource_meta(meta: &Option<grpc::meta::Metadata>, resource: &Resource, port: u16) {
597+
pub fn assert_resource_meta(
598+
meta: &Option<grpc::meta::Metadata>,
599+
parent_ref: ParentReference,
600+
port: u16,
601+
) {
595602
println!("meta: {:?}", meta);
596-
tracing::debug!(?meta, ?resource, port, "Asserting service metadata");
603+
tracing::debug!(?meta, ?parent_ref, port, "Asserting parent metadata");
604+
let mut group = parent_ref.group.unwrap();
605+
if group.is_empty() {
606+
group = "core".to_string();
607+
}
597608
assert_eq!(
598609
meta,
599610
&Some(grpc::meta::Metadata {
600611
kind: Some(grpc::meta::metadata::Kind::Resource(grpc::meta::Resource {
601-
group: resource.group(),
602-
kind: resource.kind(),
603-
name: resource.name(),
604-
namespace: resource.namespace(),
612+
group,
613+
kind: parent_ref.kind.unwrap(),
614+
name: parent_ref.name,
615+
namespace: parent_ref.namespace.unwrap(),
605616
section: "".to_string(),
606617
port: port.into()
607618
})),

0 commit comments

Comments
 (0)