mirror of https://github.com/linkerd/linkerd2.git
Add test for inbound policy index deletion (#12143)
PR https://github.com/linkerd/linkerd2/pull/12088 fixed an issue where removing and then re-adding certain policy resources could leave the policy index in an incorrect state. We add a test for the specific condition that triggered this behavior to prevent against future regressions. Verified that this test fails prior to https://github.com/linkerd/linkerd2/pull/12088 but passes on main. Signed-off-by: Alex Leong <alex@buoyant.io>
This commit is contained in:
parent
179edce222
commit
83be0dd086
|
@ -1256,6 +1256,7 @@ dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"http",
|
"http",
|
||||||
"k8s-gateway-api",
|
"k8s-gateway-api",
|
||||||
|
"k8s-openapi",
|
||||||
"kube",
|
"kube",
|
||||||
"kubert",
|
"kubert",
|
||||||
"linkerd-policy-controller-core",
|
"linkerd-policy-controller-core",
|
||||||
|
|
|
@ -26,6 +26,7 @@ tracing = "0.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
chrono = { version = "0.4", default-features = false }
|
chrono = { version = "0.4", default-features = false }
|
||||||
|
k8s-openapi = { version = "0.20", features = ["schemars"] }
|
||||||
maplit = "1"
|
maplit = "1"
|
||||||
tokio-stream = "0.1"
|
tokio-stream = "0.1"
|
||||||
tokio-test = "0.4"
|
tokio-test = "0.4"
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use k8s_openapi::apimachinery::pkg::apis::meta::v1 as metav1;
|
||||||
|
use linkerd_policy_controller_core::{http_route, inbound};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn links_authorization_policy_with_mtls_name() {
|
fn links_authorization_policy_with_mtls_name() {
|
||||||
|
@ -264,6 +266,129 @@ fn links_authorization_policy_with_service_account() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn authorization_policy_prevents_index_deletion() {
|
||||||
|
let test = TestConfig::default();
|
||||||
|
|
||||||
|
// Create policy resources: Server, HttpRoute, AuthorizationPolicy, and NetworkAuthentication.
|
||||||
|
let server = mk_server(
|
||||||
|
"ns-0",
|
||||||
|
"srv-8080",
|
||||||
|
Port::Number(8080.try_into().unwrap()),
|
||||||
|
None,
|
||||||
|
Some(("app", "app-0")),
|
||||||
|
Some(k8s::policy::server::ProxyProtocol::Http1),
|
||||||
|
);
|
||||||
|
test.index.write().apply(server.clone());
|
||||||
|
|
||||||
|
let authz = ClientAuthorization {
|
||||||
|
networks: vec!["10.0.0.0/8".parse::<IpNet>().unwrap().into()],
|
||||||
|
authentication: ClientAuthentication::TlsAuthenticated(vec![IdentityMatch::Exact(
|
||||||
|
"foo.ns-0.serviceaccount.identity.linkerd.cluster.example.com".to_string(),
|
||||||
|
)]),
|
||||||
|
};
|
||||||
|
let authz_policy = k8s::policy::AuthorizationPolicy {
|
||||||
|
metadata: k8s::ObjectMeta {
|
||||||
|
namespace: Some("ns-0".to_string()),
|
||||||
|
name: Some("authz-foo".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
spec: k8s::policy::AuthorizationPolicySpec {
|
||||||
|
target_ref: LocalTargetRef {
|
||||||
|
group: Some("policy.linkerd.io".to_string()),
|
||||||
|
kind: "HTTPRoute".to_string(),
|
||||||
|
name: "route-foo".to_string(),
|
||||||
|
},
|
||||||
|
required_authentication_refs: vec![
|
||||||
|
NamespacedTargetRef {
|
||||||
|
group: Some("policy.linkerd.io".to_string()),
|
||||||
|
kind: "NetworkAuthentication".to_string(),
|
||||||
|
name: "net-foo".to_string(),
|
||||||
|
namespace: None,
|
||||||
|
},
|
||||||
|
NamespacedTargetRef {
|
||||||
|
group: None,
|
||||||
|
kind: "ServiceAccount".to_string(),
|
||||||
|
namespace: Some("ns-0".to_string()),
|
||||||
|
name: "foo".to_string(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
test.index.write().apply(authz_policy.clone());
|
||||||
|
let route = mk_http_route("ns-0", "route-foo", "srv-8080");
|
||||||
|
test.index.write().apply(route.clone());
|
||||||
|
let net_authn = mk_network_authentication(
|
||||||
|
"ns-0".to_string(),
|
||||||
|
"net-foo".to_string(),
|
||||||
|
vec![k8s::policy::network_authentication::Network {
|
||||||
|
cidr: "10.0.0.0/8".parse().unwrap(),
|
||||||
|
except: None,
|
||||||
|
}],
|
||||||
|
);
|
||||||
|
test.index.write().apply(net_authn.clone());
|
||||||
|
|
||||||
|
// Now we delete the server, and HTTPRoute.
|
||||||
|
<Index as kubert::index::IndexNamespacedResource<k8s::policy::Server>>::delete(
|
||||||
|
&mut test.index.write(),
|
||||||
|
"ns-0".to_string(),
|
||||||
|
"srv-8080".to_string(),
|
||||||
|
);
|
||||||
|
<Index as kubert::index::IndexNamespacedResource<k8s::policy::HttpRoute>>::delete(
|
||||||
|
&mut test.index.write(),
|
||||||
|
"ns-0".to_string(),
|
||||||
|
"route-foo".to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Recreate the pod, server, and HTTPRoute.
|
||||||
|
test.index.write().apply(server);
|
||||||
|
test.index.write().apply(route);
|
||||||
|
|
||||||
|
let mut pod = mk_pod("ns-0", "pod-0", Some(("container-0", None)));
|
||||||
|
pod.labels_mut()
|
||||||
|
.insert("app".to_string(), "app-0".to_string());
|
||||||
|
test.index.write().apply(pod.clone());
|
||||||
|
|
||||||
|
let rx = test
|
||||||
|
.index
|
||||||
|
.write()
|
||||||
|
.pod_server_rx("ns-0", "pod-0", 8080.try_into().unwrap())
|
||||||
|
.expect("pod-0.ns-0 should exist");
|
||||||
|
|
||||||
|
// AuthorizationPolicy should apply.
|
||||||
|
assert_eq!(
|
||||||
|
*rx.borrow(),
|
||||||
|
InboundServer {
|
||||||
|
reference: ServerRef::Server("srv-8080".to_string()),
|
||||||
|
authorizations: Default::default(),
|
||||||
|
protocol: ProxyProtocol::Http1,
|
||||||
|
http_routes: hashmap!(HttpRouteRef::Linkerd(http_route::GroupKindName{
|
||||||
|
group: "policy.linkerd.io".into(),
|
||||||
|
kind: "HTTPRoute".into(),
|
||||||
|
name: "route-foo".into(),
|
||||||
|
}) => HttpRoute {
|
||||||
|
rules: vec![inbound::HttpRouteRule {
|
||||||
|
matches: vec![http_route::HttpRouteMatch {
|
||||||
|
path: Some(http_route::PathMatch::Prefix("/foo".to_string())),
|
||||||
|
headers: vec![],
|
||||||
|
query_params: vec![],
|
||||||
|
method: None,
|
||||||
|
}],
|
||||||
|
filters: vec![],
|
||||||
|
}],
|
||||||
|
authorizations: hashmap!(
|
||||||
|
AuthorizationRef::AuthorizationPolicy("authz-foo".to_string()) => authz.clone()
|
||||||
|
)
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn mk_authorization_policy(
|
fn mk_authorization_policy(
|
||||||
ns: impl ToString,
|
ns: impl ToString,
|
||||||
name: impl ToString,
|
name: impl ToString,
|
||||||
|
@ -339,3 +464,64 @@ fn mk_network_authentication(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mk_http_route(
|
||||||
|
ns: impl ToString,
|
||||||
|
name: impl ToString,
|
||||||
|
server: impl ToString,
|
||||||
|
) -> k8s::policy::HttpRoute {
|
||||||
|
k8s::policy::HttpRoute {
|
||||||
|
metadata: k8s::ObjectMeta {
|
||||||
|
namespace: Some(ns.to_string()),
|
||||||
|
name: Some(name.to_string()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
spec: k8s::policy::HttpRouteSpec {
|
||||||
|
inner: k8s_gateway_api::CommonRouteSpec {
|
||||||
|
parent_refs: Some(vec![k8s_gateway_api::ParentReference {
|
||||||
|
group: Some("policy.linkerd.io".to_string()),
|
||||||
|
kind: Some("Server".to_string()),
|
||||||
|
namespace: Some(ns.to_string()),
|
||||||
|
name: server.to_string(),
|
||||||
|
section_name: None,
|
||||||
|
port: None,
|
||||||
|
}]),
|
||||||
|
},
|
||||||
|
rules: Some(vec![k8s::policy::httproute::HttpRouteRule {
|
||||||
|
matches: Some(vec![k8s::policy::httproute::HttpRouteMatch {
|
||||||
|
path: Some(k8s_gateway_api::HttpPathMatch::PathPrefix {
|
||||||
|
value: "/foo".to_string(),
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
}]),
|
||||||
|
filters: None,
|
||||||
|
backend_refs: None,
|
||||||
|
timeouts: None,
|
||||||
|
}]),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
status: Some(k8s::policy::httproute::HttpRouteStatus {
|
||||||
|
inner: k8s_gateway_api::RouteStatus {
|
||||||
|
parents: vec![k8s_gateway_api::RouteParentStatus {
|
||||||
|
conditions: vec![metav1::Condition {
|
||||||
|
type_: "Accepted".to_string(),
|
||||||
|
status: "True".to_string(),
|
||||||
|
message: String::new(),
|
||||||
|
reason: String::new(),
|
||||||
|
last_transition_time: metav1::Time(chrono::Utc::now()),
|
||||||
|
observed_generation: None,
|
||||||
|
}],
|
||||||
|
parent_ref: k8s_gateway_api::ParentReference {
|
||||||
|
group: Some("policy.linkerd.io".to_string()),
|
||||||
|
kind: Some("Server".to_string()),
|
||||||
|
namespace: Some(ns.to_string()),
|
||||||
|
name: server.to_string(),
|
||||||
|
section_name: None,
|
||||||
|
port: None,
|
||||||
|
},
|
||||||
|
controller_name: "linkerd.io/policy-controller".to_string(),
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue