mirror of https://github.com/linkerd/linkerd2.git
Add status support for BackendReferences (#10601)
A route may have two conditions in a parent status: a condition that states whether it has been accepted by the parents, and a condition that states whether all backend references -- that traffic matched against route is sent to -- have resolved successfully. Currently, the policy controller does not support the latter. This change introduces support for checking and setting a backendRef specific condition. A successful condition (ResolvedRefs = True) is met when all backend references point to a supported type, and that type exists in the cluster. Currently, only Service objects are supported. A nonexistent object, or an unsupported kind will reject the entire condition; the particular reason will be reflected in the condition's message. Since statuses are set on a route's parents, the same condition will apply to _all_ parents in a route (since there is no way to elicit different backends for different parents). If a route does not have any backend references, then the parent reference type will be used. As such, any parents that are not Services will automatically get an invalid backend condition (exception to the rule in the third paragraph where a condition is shared by all parents). When the parent is supported (i.e a Service) we needn't check its existence since the parent condition will already reflect that. --- Signed-off-by: Matei David <matei@buoyant.io> Co-authored-by: Eliza Weisman <eliza@buoyant.io> Co-authored-by: Oliver Gould <ver@buoyant.io>
This commit is contained in:
parent
f098e6e87f
commit
2b6760c824
|
|
@ -30,6 +30,9 @@ where
|
|||
t_group = "core";
|
||||
}
|
||||
|
||||
group.unwrap_or("core").eq_ignore_ascii_case(t_group)
|
||||
group
|
||||
.filter(|s| !s.is_empty())
|
||||
.unwrap_or("core")
|
||||
.eq_ignore_ascii_case(t_group)
|
||||
&& kind.eq_ignore_ascii_case(&*T::kind(&dt))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
pub use k8s_gateway_api::{
|
||||
CommonRouteSpec, Hostname, HttpBackendRef, HttpHeader, HttpHeaderMatch, HttpHeaderName,
|
||||
HttpMethod, HttpPathMatch, HttpPathModifier, HttpQueryParamMatch, HttpRequestHeaderFilter,
|
||||
HttpRequestRedirectFilter, HttpRouteMatch, LocalObjectReference, ParentReference, RouteStatus,
|
||||
BackendObjectReference, CommonRouteSpec, Hostname, HttpBackendRef, HttpHeader, HttpHeaderMatch,
|
||||
HttpHeaderName, HttpMethod, HttpPathMatch, HttpPathModifier, HttpQueryParamMatch,
|
||||
HttpRequestHeaderFilter, HttpRequestRedirectFilter, HttpRouteMatch, LocalObjectReference,
|
||||
ParentReference, RouteStatus,
|
||||
};
|
||||
|
||||
/// HTTPRoute provides a way to route HTTP requests. This includes the
|
||||
|
|
@ -206,3 +207,15 @@ where
|
|||
|
||||
super::targets_kind::<T>(parent_ref.group.as_deref(), kind)
|
||||
}
|
||||
|
||||
pub fn backend_ref_targets_kind<T>(backend_ref: &BackendObjectReference) -> bool
|
||||
where
|
||||
T: kube::Resource,
|
||||
T::DynamicType: Default,
|
||||
{
|
||||
// Default kind is assumed to be service for backend ref objects
|
||||
super::targets_kind::<T>(
|
||||
backend_ref.group.as_deref(),
|
||||
backend_ref.kind.as_deref().unwrap_or("service"),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,39 +19,224 @@ pub enum ParentReference {
|
|||
UnknownKind,
|
||||
}
|
||||
|
||||
pub(crate) fn make_parents(http_route: policy::HttpRoute) -> Vec<ParentReference> {
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub enum BackendReference {
|
||||
Service(ResourceId),
|
||||
Unknown,
|
||||
}
|
||||
|
||||
pub(crate) fn make_parents(http_route: &policy::HttpRoute) -> Vec<ParentReference> {
|
||||
let namespace = http_route
|
||||
.metadata
|
||||
.namespace
|
||||
.as_deref()
|
||||
.expect("HTTPRoute must have a namespace");
|
||||
http_route
|
||||
.spec
|
||||
.inner
|
||||
.parent_refs
|
||||
.into_iter()
|
||||
.iter()
|
||||
.flatten()
|
||||
.map(|parent_ref| ParentReference::from_parent_ref(parent_ref, &namespace))
|
||||
.map(|pr| ParentReference::from_parent_ref(pr, namespace))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn make_backends(http_route: &policy::HttpRoute) -> Vec<BackendReference> {
|
||||
let namespace = http_route
|
||||
.metadata
|
||||
.namespace
|
||||
.as_deref()
|
||||
.expect("HTTPRoute must have a namespace");
|
||||
http_route
|
||||
.spec
|
||||
.rules
|
||||
.iter()
|
||||
.flatten()
|
||||
.flat_map(|rule| rule.backend_refs.iter().flatten())
|
||||
.filter_map(|http_backend_ref| http_backend_ref.backend_ref.as_ref())
|
||||
.map(|br| BackendReference::from_backend_ref(&br.inner, namespace))
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl ParentReference {
|
||||
fn from_parent_ref(parent_ref: gateway::ParentReference, default_namespace: &str) -> Self {
|
||||
if policy::httproute::parent_ref_targets_kind::<Server>(&parent_ref) {
|
||||
fn from_parent_ref(parent_ref: &gateway::ParentReference, default_namespace: &str) -> Self {
|
||||
if policy::httproute::parent_ref_targets_kind::<Server>(parent_ref) {
|
||||
// If the parent reference does not have a namespace, default to using
|
||||
// the HTTPRoute's namespace.
|
||||
let namespace = parent_ref
|
||||
.namespace
|
||||
.unwrap_or_else(|| default_namespace.to_string());
|
||||
ParentReference::Server(ResourceId::new(namespace, parent_ref.name))
|
||||
} else if policy::httproute::parent_ref_targets_kind::<Service>(&parent_ref) {
|
||||
let namespace = parent_ref.namespace.as_deref().unwrap_or(default_namespace);
|
||||
ParentReference::Server(ResourceId::new(
|
||||
namespace.to_string(),
|
||||
parent_ref.name.clone(),
|
||||
))
|
||||
} else if policy::httproute::parent_ref_targets_kind::<Service>(parent_ref) {
|
||||
// If the parent reference does not have a namespace, default to using
|
||||
// the HTTPRoute's namespace.
|
||||
let namespace = parent_ref
|
||||
.namespace
|
||||
.unwrap_or_else(|| default_namespace.to_string());
|
||||
ParentReference::Service(ResourceId::new(namespace, parent_ref.name), parent_ref.port)
|
||||
let namespace = parent_ref.namespace.as_deref().unwrap_or(default_namespace);
|
||||
ParentReference::Service(
|
||||
ResourceId::new(namespace.to_string(), parent_ref.name.clone()),
|
||||
parent_ref.port,
|
||||
)
|
||||
} else {
|
||||
ParentReference::UnknownKind
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BackendReference {
|
||||
fn from_backend_ref(
|
||||
backend_ref: &gateway::BackendObjectReference,
|
||||
default_namespace: &str,
|
||||
) -> Self {
|
||||
if policy::httproute::backend_ref_targets_kind::<linkerd_policy_controller_k8s_api::Service>(
|
||||
backend_ref,
|
||||
) {
|
||||
let namespace = backend_ref
|
||||
.namespace
|
||||
.as_deref()
|
||||
.unwrap_or(default_namespace);
|
||||
BackendReference::Service(ResourceId::new(
|
||||
namespace.to_string(),
|
||||
backend_ref.name.clone(),
|
||||
))
|
||||
} else {
|
||||
BackendReference::Unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use linkerd_policy_controller_k8s_api::{policy, ObjectMeta};
|
||||
|
||||
fn mk_default_http_backends(
|
||||
backend_refs: Vec<gateway::BackendObjectReference>,
|
||||
) -> Option<Vec<gateway::HttpBackendRef>> {
|
||||
Some(
|
||||
backend_refs
|
||||
.into_iter()
|
||||
.map(|backend_ref| gateway::HttpBackendRef {
|
||||
backend_ref: Some(gateway::BackendRef {
|
||||
inner: backend_ref,
|
||||
weight: None,
|
||||
}),
|
||||
filters: None,
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn backendrefs_from_route() {
|
||||
let http_route = policy::HttpRoute {
|
||||
metadata: ObjectMeta {
|
||||
namespace: Some("foo".to_string()),
|
||||
name: Some("foo".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: policy::HttpRouteSpec {
|
||||
inner: gateway::CommonRouteSpec { parent_refs: None },
|
||||
hostnames: None,
|
||||
rules: Some(vec![
|
||||
policy::httproute::HttpRouteRule {
|
||||
matches: None,
|
||||
filters: None,
|
||||
backend_refs: mk_default_http_backends(vec![
|
||||
gateway::BackendObjectReference {
|
||||
group: None,
|
||||
kind: None,
|
||||
name: "ref-1".to_string(),
|
||||
namespace: Some("default".to_string()),
|
||||
port: None,
|
||||
},
|
||||
gateway::BackendObjectReference {
|
||||
group: None,
|
||||
kind: None,
|
||||
name: "ref-2".to_string(),
|
||||
namespace: None,
|
||||
port: None,
|
||||
},
|
||||
]),
|
||||
},
|
||||
policy::httproute::HttpRouteRule {
|
||||
matches: None,
|
||||
filters: None,
|
||||
backend_refs: mk_default_http_backends(vec![
|
||||
gateway::BackendObjectReference {
|
||||
group: Some("Core".to_string()),
|
||||
kind: Some("Service".to_string()),
|
||||
name: "ref-3".to_string(),
|
||||
namespace: Some("default".to_string()),
|
||||
port: None,
|
||||
},
|
||||
]),
|
||||
},
|
||||
policy::httproute::HttpRouteRule {
|
||||
matches: None,
|
||||
filters: None,
|
||||
backend_refs: None,
|
||||
},
|
||||
]),
|
||||
},
|
||||
status: None,
|
||||
};
|
||||
|
||||
let result = make_backends(&http_route);
|
||||
assert_eq!(
|
||||
3,
|
||||
result.len(),
|
||||
"expected only three BackendReferences from route"
|
||||
);
|
||||
result.into_iter().for_each(|backend_ref| {
|
||||
assert!(matches!(backend_ref, BackendReference::Service(_)));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn backendrefs_from_multiple_types() {
|
||||
let http_route = policy::HttpRoute {
|
||||
metadata: ObjectMeta {
|
||||
namespace: Some("default".to_string()),
|
||||
name: Some("foo".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: policy::HttpRouteSpec {
|
||||
inner: gateway::CommonRouteSpec { parent_refs: None },
|
||||
hostnames: None,
|
||||
rules: Some(vec![policy::httproute::HttpRouteRule {
|
||||
matches: None,
|
||||
filters: None,
|
||||
backend_refs: mk_default_http_backends(vec![
|
||||
gateway::BackendObjectReference {
|
||||
group: None,
|
||||
kind: None,
|
||||
name: "ref-1".to_string(),
|
||||
namespace: None,
|
||||
port: None,
|
||||
},
|
||||
gateway::BackendObjectReference {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: Some("Server".to_string()),
|
||||
name: "ref-2".to_string(),
|
||||
namespace: None,
|
||||
port: None,
|
||||
},
|
||||
]),
|
||||
}]),
|
||||
},
|
||||
status: None,
|
||||
};
|
||||
|
||||
let result = make_backends(&http_route);
|
||||
assert_eq!(
|
||||
2,
|
||||
result.len(),
|
||||
"expected only two BackendReferences from route"
|
||||
);
|
||||
let mut iter = result.into_iter();
|
||||
let known = iter.next().unwrap();
|
||||
assert!(matches!(known, BackendReference::Service(_)));
|
||||
let unknown = iter.next().unwrap();
|
||||
assert!(matches!(unknown, BackendReference::Unknown))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
http_route::{self, ParentReference},
|
||||
http_route::{self, BackendReference, ParentReference},
|
||||
resource_id::ResourceId,
|
||||
};
|
||||
use ahash::{AHashMap as HashMap, AHashSet as HashSet};
|
||||
|
|
@ -21,6 +21,23 @@ use tokio::{
|
|||
pub(crate) const POLICY_API_GROUP: &str = "policy.linkerd.io";
|
||||
const POLICY_API_VERSION: &str = "policy.linkerd.io/v1alpha1";
|
||||
|
||||
mod conditions {
|
||||
pub const RESOLVED_REFS: &str = "ResolvedRefs";
|
||||
pub const ACCEPTED: &str = "Accepted";
|
||||
}
|
||||
|
||||
mod reasons {
|
||||
pub const RESOLVED_REFS: &str = "ResolvedRefs";
|
||||
pub const BACKEND_NOT_FOUND: &str = "BackendNotFound";
|
||||
pub const INVALID_KIND: &str = "InvalidKind";
|
||||
pub const NO_MATCHING_PARENT: &str = "NoMatchingParent";
|
||||
}
|
||||
|
||||
mod cond_statuses {
|
||||
pub const STATUS_TRUE: &str = "True";
|
||||
pub const STATUS_FALSE: &str = "False";
|
||||
}
|
||||
|
||||
pub type SharedIndex = Arc<RwLock<Index>>;
|
||||
|
||||
pub struct Controller {
|
||||
|
|
@ -43,13 +60,19 @@ pub struct Index {
|
|||
claims: Receiver<Arc<Claim>>,
|
||||
updates: UnboundedSender<Update>,
|
||||
|
||||
/// Maps HttpRoute ids to a list of their parent refs, regardless of if
|
||||
/// those parents have accepted the route.
|
||||
http_route_parent_refs: HashMap<ResourceId, Vec<ParentReference>>,
|
||||
/// Maps HttpRoute ids to a list of their parent and backend refs,
|
||||
/// regardless of if those parents have accepted the route.
|
||||
http_route_refs: HashMap<ResourceId, References>,
|
||||
servers: HashSet<ResourceId>,
|
||||
services: HashSet<ResourceId>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
struct References {
|
||||
parents: Vec<ParentReference>,
|
||||
backends: Vec<BackendReference>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Update {
|
||||
pub id: ResourceId,
|
||||
|
|
@ -116,7 +139,7 @@ impl Index {
|
|||
name: name.to_string(),
|
||||
claims,
|
||||
updates,
|
||||
http_route_parent_refs: HashMap::new(),
|
||||
http_route_refs: HashMap::new(),
|
||||
servers: HashSet::new(),
|
||||
services: HashSet::new(),
|
||||
}))
|
||||
|
|
@ -156,24 +179,28 @@ impl Index {
|
|||
}
|
||||
}
|
||||
|
||||
// If the route is new or its parents have changed, return true so that a
|
||||
// patch is generated; otherwise return false.
|
||||
fn update_http_route(&mut self, id: ResourceId, parents: Vec<ParentReference>) -> bool {
|
||||
match self.http_route_parent_refs.entry(id) {
|
||||
// If the route is new or its parentRefs and/or backendRefs have changed,
|
||||
// return true, so that a patch is generated; otherwise return false.
|
||||
fn update_http_route(&mut self, id: ResourceId, references: &References) -> bool {
|
||||
match self.http_route_refs.entry(id) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(parents);
|
||||
entry.insert(references.clone());
|
||||
}
|
||||
Entry::Occupied(mut entry) => {
|
||||
if *entry.get() == parents {
|
||||
if entry.get() == references {
|
||||
return false;
|
||||
}
|
||||
entry.insert(parents);
|
||||
entry.insert(references.clone());
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn parent_status(&self, parent_ref: &ParentReference) -> Option<gateway::RouteParentStatus> {
|
||||
fn parent_status(
|
||||
&self,
|
||||
parent_ref: &ParentReference,
|
||||
backend_condition: k8s::Condition,
|
||||
) -> Option<gateway::RouteParentStatus> {
|
||||
match parent_ref {
|
||||
ParentReference::Server(server) => {
|
||||
let condition = if self.servers.contains(server) {
|
||||
|
|
@ -181,6 +208,7 @@ impl Index {
|
|||
} else {
|
||||
no_matching_parent()
|
||||
};
|
||||
|
||||
Some(gateway::RouteParentStatus {
|
||||
parent_ref: gateway::ParentReference {
|
||||
group: Some(POLICY_API_GROUP.to_string()),
|
||||
|
|
@ -200,6 +228,7 @@ impl Index {
|
|||
} else {
|
||||
no_matching_parent()
|
||||
};
|
||||
|
||||
Some(gateway::RouteParentStatus {
|
||||
parent_ref: gateway::ParentReference {
|
||||
group: None,
|
||||
|
|
@ -210,21 +239,45 @@ impl Index {
|
|||
port: *port,
|
||||
},
|
||||
controller_name: POLICY_CONTROLLER_NAME.to_string(),
|
||||
conditions: vec![condition],
|
||||
conditions: vec![condition, backend_condition],
|
||||
})
|
||||
}
|
||||
ParentReference::UnknownKind => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn backend_condition(&self, backend_refs: &[BackendReference]) -> k8s::Condition {
|
||||
// If even one backend has a reference to an unknown / unsupported
|
||||
// reference, return invalid backend condition
|
||||
if backend_refs
|
||||
.iter()
|
||||
.any(|reference| matches!(reference, BackendReference::Unknown))
|
||||
{
|
||||
return invalid_backend_kind();
|
||||
}
|
||||
|
||||
// If all references have been resolved (i.e exist in our services cache),
|
||||
// return positive status, otherwise, one of them does not exist
|
||||
if backend_refs.iter().any(|backend_ref| match backend_ref {
|
||||
BackendReference::Service(service) => self.services.contains(service),
|
||||
_ => false,
|
||||
}) {
|
||||
resolved_refs()
|
||||
} else {
|
||||
backend_not_found()
|
||||
}
|
||||
}
|
||||
|
||||
fn make_http_route_patch(
|
||||
&self,
|
||||
id: &ResourceId,
|
||||
parents: &[ParentReference],
|
||||
backends: &[BackendReference],
|
||||
) -> k8s::Patch<serde_json::Value> {
|
||||
let backend_condition = self.backend_condition(backends);
|
||||
let parent_statuses = parents
|
||||
.iter()
|
||||
.filter_map(|parent_ref| self.parent_status(parent_ref))
|
||||
.filter_map(|parent_ref| self.parent_status(parent_ref, backend_condition.clone()))
|
||||
.collect();
|
||||
let status = gateway::HttpRouteStatus {
|
||||
inner: gateway::RouteStatus {
|
||||
|
|
@ -235,8 +288,8 @@ impl Index {
|
|||
}
|
||||
|
||||
fn reconcile(&self) {
|
||||
for (id, parents) in self.http_route_parent_refs.iter() {
|
||||
let patch = self.make_http_route_patch(id, parents);
|
||||
for (id, references) in self.http_route_refs.iter() {
|
||||
let patch = self.make_http_route_patch(id, &references.parents, &references.backends);
|
||||
if let Err(error) = self.updates.send(Update {
|
||||
id: id.clone(),
|
||||
patch,
|
||||
|
|
@ -255,11 +308,16 @@ impl kubert::index::IndexNamespacedResource<k8s::policy::HttpRoute> for Index {
|
|||
let name = resource.name_unchecked();
|
||||
let id = ResourceId::new(namespace, name);
|
||||
|
||||
// Create the route parents and insert it into the index. If the
|
||||
// HTTPRoute is already in the index and it hasn't changed, skip
|
||||
// creating a patch.
|
||||
let parents = http_route::make_parents(resource);
|
||||
if !self.update_http_route(id.clone(), parents.clone()) {
|
||||
// Create the route parents
|
||||
let parents = http_route::make_parents(&resource);
|
||||
|
||||
// Create the route backends
|
||||
let backends = http_route::make_backends(&resource);
|
||||
|
||||
// Construct references and insert into the index; if the HTTPRoute is
|
||||
// already in the index and it hasn't changed, skip creating a patch.
|
||||
let references = References { parents, backends };
|
||||
if !self.update_http_route(id.clone(), &references) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -272,7 +330,7 @@ impl kubert::index::IndexNamespacedResource<k8s::policy::HttpRoute> for Index {
|
|||
|
||||
// Create a patch for the HTTPRoute and send it to the Controller so
|
||||
// that it is applied.
|
||||
let patch = self.make_http_route_patch(&id, &parents);
|
||||
let patch = self.make_http_route_patch(&id, &references.parents, &references.backends);
|
||||
if let Err(error) = self.updates.send(Update {
|
||||
id: id.clone(),
|
||||
patch,
|
||||
|
|
@ -283,7 +341,7 @@ impl kubert::index::IndexNamespacedResource<k8s::policy::HttpRoute> for Index {
|
|||
|
||||
fn delete(&mut self, namespace: String, name: String) {
|
||||
let id = ResourceId::new(namespace, name);
|
||||
self.http_route_parent_refs.remove(&id);
|
||||
self.http_route_refs.remove(&id);
|
||||
}
|
||||
|
||||
// Since apply only reindexes a single HTTPRoute at a time, there's no need
|
||||
|
|
@ -382,9 +440,9 @@ fn no_matching_parent() -> k8s::Condition {
|
|||
last_transition_time: k8s::Time(now()),
|
||||
message: "".to_string(),
|
||||
observed_generation: None,
|
||||
reason: "NoMatchingParent".to_string(),
|
||||
status: "False".to_string(),
|
||||
type_: "Accepted".to_string(),
|
||||
reason: reasons::NO_MATCHING_PARENT.to_string(),
|
||||
status: cond_statuses::STATUS_FALSE.to_string(),
|
||||
type_: conditions::ACCEPTED.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -393,8 +451,41 @@ fn accepted() -> k8s::Condition {
|
|||
last_transition_time: k8s::Time(now()),
|
||||
message: "".to_string(),
|
||||
observed_generation: None,
|
||||
reason: "Accepted".to_string(),
|
||||
status: "True".to_string(),
|
||||
type_: "Accepted".to_string(),
|
||||
reason: conditions::ACCEPTED.to_string(),
|
||||
status: cond_statuses::STATUS_TRUE.to_string(),
|
||||
type_: conditions::ACCEPTED.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn resolved_refs() -> k8s::Condition {
|
||||
k8s::Condition {
|
||||
last_transition_time: k8s::Time(now()),
|
||||
message: "".to_string(),
|
||||
observed_generation: None,
|
||||
reason: reasons::RESOLVED_REFS.to_string(),
|
||||
status: cond_statuses::STATUS_TRUE.to_string(),
|
||||
type_: conditions::RESOLVED_REFS.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn backend_not_found() -> k8s::Condition {
|
||||
k8s::Condition {
|
||||
last_transition_time: k8s::Time(now()),
|
||||
message: "".to_string(),
|
||||
observed_generation: None,
|
||||
reason: reasons::BACKEND_NOT_FOUND.to_string(),
|
||||
status: cond_statuses::STATUS_FALSE.to_string(),
|
||||
type_: conditions::RESOLVED_REFS.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn invalid_backend_kind() -> k8s::Condition {
|
||||
k8s::Condition {
|
||||
last_transition_time: k8s::Time(now()),
|
||||
message: "".to_string(),
|
||||
observed_generation: None,
|
||||
reason: reasons::INVALID_KIND.to_string(),
|
||||
status: cond_statuses::STATUS_FALSE.to_string(),
|
||||
type_: conditions::RESOLVED_REFS.to_string(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ async fn inbound_multiple_parents() {
|
|||
assert_eq!(invalid_cond.reason, "NoMatchingParent");
|
||||
|
||||
// Find status for valid parent and extract the condition
|
||||
let valid_cond = find_condition(parent_status.clone(), "test-valid-server")
|
||||
let valid_cond = find_condition(parent_status, "test-valid-server")
|
||||
.expect("must have at least one 'Accepted' condition set for valid parent");
|
||||
assert_eq!(valid_cond.status, "True");
|
||||
assert_eq!(valid_cond.reason, "Accepted")
|
||||
|
|
|
|||
Loading…
Reference in New Issue