Skip to content

Commit cb97d2f

Browse files
committed
Generic wait-for-condition Condition
Super unhappy with the API, but it's a start... Fixes kube-rs#669 Signed-off-by: Teo Klestrup Röijezon <[email protected]>
1 parent 6a4f2b6 commit cb97d2f

File tree

1 file changed

+72
-2
lines changed

1 file changed

+72
-2
lines changed

kube-runtime/src/wait.rs

+72-2
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,11 @@ where
8585
/// }
8686
/// }
8787
/// ```
88-
pub trait Condition<K> {
88+
pub trait Condition<K: ?Sized> {
8989
fn matches_object(&self, obj: Option<&K>) -> bool;
9090
}
9191

92-
impl<K, F: Fn(Option<&K>) -> bool> Condition<K> for F {
92+
impl<K: ?Sized, F: Fn(Option<&K>) -> bool> Condition<K> for F {
9393
fn matches_object(&self, obj: Option<&K>) -> bool {
9494
(self)(obj)
9595
}
@@ -100,6 +100,7 @@ pub mod conditions {
100100
pub use super::Condition;
101101
use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition;
102102
use kube_client::Resource;
103+
use serde::Serialize;
103104

104105
/// An await condition that returns `true` once the object has been deleted.
105106
///
@@ -134,6 +135,75 @@ pub mod conditions {
134135
false
135136
}
136137
}
138+
139+
/// An await condition that returns `true` if the object exists.
140+
///
141+
/// NOTE: If waiting for an object to be deleted, do _not_ [invert](`Condition::not`) this [`Condition`].
142+
/// Instead, use [`is_deleted`], which considers a deleted-then-recreated object to have been deleted.
143+
#[must_use]
144+
pub fn exists<K>() -> impl Condition<K> {
145+
|obj: Option<&K>| obj.is_some()
146+
}
147+
148+
/// An await condition for [`Resource`] that returns `true` if the object's condition of the given type holds true
149+
/// for the given `value_cond`.
150+
///
151+
/// # Value condition
152+
///
153+
/// The value condition is passed `None` if the object does not exist or does not have the given condition (combine with
154+
/// [`exists`] if you need to validate whether the object exists). Otherwise, the value should be one of `"True"`, `"False"`, or `"Unknown"`
155+
/// (see <https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#condition-v1-meta> for more details).
156+
///
157+
/// # Stability
158+
///
159+
/// This is an experimental API that should be expected to change. It has a few particular problems:
160+
///
161+
/// 1. It is completely untyped
162+
/// 2. It makes fairly deep assumptions about the structure of the object and its status
163+
/// 3. It doesn't have any way to signal errors gracefully
164+
/// 4. It has some unfortunate lifetime problems that prevent bringing in a closure context
165+
///
166+
/// # Usage
167+
///
168+
/// ```rust
169+
/// # use k8s_openapi::api::core::v1::{Pod, PodCondition, PodStatus};
170+
/// # use kube_runtime::wait::{conditions::unstable_has_status_condition, Condition};
171+
/// let pod = |ready: String| Pod {
172+
/// status: Some(PodStatus {
173+
/// conditions: Some(vec![PodCondition {
174+
/// type_: "Ready".to_string(),
175+
/// status: ready,
176+
/// ..PodCondition::default()
177+
/// }]),
178+
/// ..PodStatus::default()
179+
/// }),
180+
/// ..Pod::default()
181+
/// };
182+
/// let cond_status_ready: fn(Option<&str>) -> bool = |status| status == Some("True");
183+
/// let cond_pod_ready = unstable_has_status_condition("Ready", cond_status_ready);
184+
/// assert!(!cond_pod_ready.matches_object(Some(&pod("False".to_string()))));
185+
/// assert!(cond_pod_ready.matches_object(Some(&pod("True".to_string()))));
186+
/// ```
187+
#[must_use]
188+
pub fn unstable_has_status_condition<'a, K: Serialize + Resource, StatusCond: Condition<str> + 'a>(
189+
condition_type: &'a str,
190+
status_cond: StatusCond,
191+
) -> impl Condition<K> + 'a {
192+
move |obj: Option<&K>| {
193+
let serialized_obj = serde_json::to_value(obj).ok();
194+
status_cond.matches_object(serialized_obj.as_ref().and_then(|obj| {
195+
obj.get("status")?
196+
.get("conditions")?
197+
.as_array()?
198+
.iter()
199+
.find(|cond| {
200+
cond.get("type").and_then(serde_json::Value::as_str) == Some(condition_type)
201+
})?
202+
.get("status")?
203+
.as_str()
204+
}))
205+
}
206+
}
137207
}
138208

139209
/// Utilities for deleting objects

0 commit comments

Comments
 (0)