Skip to content

Commit 16ef17b

Browse files
Merge pull request #41 from MaterializeInc/push-poyulksuoyru
allow making controllers without finalizers
2 parents 82f96c6 + 113fee7 commit 16ef17b

File tree

3 files changed

+53
-19
lines changed

3 files changed

+53
-19
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "k8s-controller"
3-
version = "0.7.0"
3+
version = "0.8.0"
44
edition = "2024"
55
rust-version = "1.89.0"
66

@@ -17,6 +17,7 @@ kube = { version = "2.0.1", default-features = false, features = ["client"] }
1717
kube-runtime = "2.0.1"
1818
rand = "0.9.2"
1919
serde = "1"
20+
thiserror = "2.0.17"
2021
tracing = "0.1"
2122

2223
[dev-dependencies]

src/controller.rs

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::collections::BTreeMap;
2-
use std::error::Error;
2+
use std::error::Error as _;
33
use std::sync::{Arc, Mutex};
44
use std::time::Duration;
55

@@ -14,6 +14,14 @@ use kube_runtime::watcher;
1414
use rand::{Rng, rng};
1515
use tracing::{Level, event};
1616

17+
#[derive(Debug, thiserror::Error)]
18+
pub enum Error<E: std::error::Error + 'static> {
19+
#[error("{0}")]
20+
ControllerError(#[source] E),
21+
#[error("{0}")]
22+
FinalizerError(#[source] kube_runtime::finalizer::Error<E>),
23+
}
24+
1725
/// The [`Controller`] watches a set of resources, calling methods on the
1826
/// provided [`Context`] when events occur.
1927
pub struct Controller<Ctx: Context>
@@ -36,7 +44,6 @@ impl<Ctx: Context> Controller<Ctx>
3644
where
3745
Ctx: Send + Sync + 'static,
3846
Ctx::Error: Send + Sync + 'static,
39-
Ctx::Resource: Send + Sync + 'static,
4047
Ctx::Resource: Clone + std::fmt::Debug + serde::Serialize,
4148
for<'de> Ctx::Resource: serde::Deserialize<'de>,
4249
<Ctx::Resource as Resource>::DynamicType:
@@ -213,15 +220,18 @@ where
213220
pub trait Context {
214221
/// The type of Kubernetes [resource](Resource) that will be watched by
215222
/// the [`Controller`] this context is passed to
216-
type Resource: Resource;
223+
type Resource: Resource + Send + Sync + 'static;
217224
/// The error type which will be returned by the [`apply`](Self::apply)
218-
/// and [`cleanup`](Self::apply) methods
225+
/// and [`cleanup`](Self::cleanup) methods
219226
type Error: std::error::Error;
220227

221228
/// The name to use for the finalizer. This must be unique across
222229
/// controllers - if multiple controllers with the same finalizer name
223230
/// run against the same resource, unexpected behavior can occur.
224-
const FINALIZER_NAME: &'static str;
231+
///
232+
/// If this is None (the default), a finalizer will not be used, and
233+
/// cleanup events will not be reported.
234+
const FINALIZER_NAME: Option<&'static str> = None;
225235

226236
/// This method is called when a watched resource is created or updated.
227237
/// The [`Client`] used by the controller is passed in to allow making
@@ -243,11 +253,19 @@ pub trait Context {
243253
/// be performed, otherwise if `None` is returned,
244254
/// [`success_action`](Self::success_action) will be called to find the
245255
/// action to perform.
256+
///
257+
/// Note that this method will only be called if a finalizer is used.
246258
async fn cleanup(
247259
&self,
248260
client: Client,
249261
resource: &Self::Resource,
250-
) -> Result<Option<Action>, Self::Error>;
262+
) -> Result<Option<Action>, Self::Error> {
263+
// use a better name for the parameter name in the docs
264+
let _client = client;
265+
let _resource = resource;
266+
267+
Ok(Some(Action::await_change()))
268+
}
251269

252270
/// This method is called when a call to [`apply`](Self::apply) or
253271
/// [`cleanup`](Self::cleanup) returns `Ok(None)`. It should return the
@@ -271,7 +289,7 @@ pub trait Context {
271289
fn error_action(
272290
self: Arc<Self>,
273291
resource: Arc<Self::Resource>,
274-
err: &kube_runtime::finalizer::Error<Self::Error>,
292+
err: &Error<Self::Error>,
275293
consecutive_errors: u32,
276294
) -> Action {
277295
// use a better name for the parameter name in the docs
@@ -290,7 +308,7 @@ pub trait Context {
290308
client: Client,
291309
api: Api<Self::Resource>,
292310
resource: Arc<Self::Resource>,
293-
) -> Result<Action, kube_runtime::finalizer::Error<Self::Error>>
311+
) -> Result<Action, Error<Self::Error>>
294312
where
295313
Self: Send + Sync + 'static,
296314
Self::Error: Send + Sync + 'static,
@@ -307,11 +325,8 @@ pub trait Context {
307325
let dynamic_type = Default::default();
308326
let kind = Self::Resource::kind(&dynamic_type).into_owned();
309327
let mut ran = false;
310-
let res = finalizer(
311-
&api,
312-
Self::FINALIZER_NAME,
313-
Arc::clone(&resource),
314-
|event| async {
328+
let res = if let Some(finalizer_name) = Self::FINALIZER_NAME {
329+
finalizer(&api, finalizer_name, Arc::clone(&resource), |event| async {
315330
ran = true;
316331
event!(
317332
Level::INFO,
@@ -339,9 +354,27 @@ pub trait Context {
339354
.unwrap_or_else(Action::await_change),
340355
};
341356
Ok(action)
342-
},
343-
)
344-
.await;
357+
})
358+
.await
359+
.map_err(Error::FinalizerError)
360+
} else {
361+
ran = true;
362+
event!(
363+
Level::INFO,
364+
resource_name = %resource.name_unchecked().as_str(),
365+
"Reconciling {} (apply).",
366+
kind,
367+
);
368+
let action = self
369+
.apply(client, &resource)
370+
.await
371+
.map_err(Error::ControllerError)?;
372+
Ok(if let Some(action) = action {
373+
action
374+
} else {
375+
self.success_action(&resource)
376+
})
377+
};
345378
if !ran {
346379
event!(
347380
Level::INFO,

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
//! type Resource = Pod;
9090
//! type Error = kube::Error;
9191
//!
92-
//! const FINALIZER_NAME: &'static str = "example.com/pod-counter";
92+
//! const FINALIZER_NAME: Option<&'static str> = Some("example.com/pod-counter");
9393
//!
9494
//! async fn apply(
9595
//! &self,
@@ -137,7 +137,7 @@
137137
//! # impl k8s_controller::Context for PodCounter {
138138
//! # type Resource = Pod;
139139
//! # type Error = kube::Error;
140-
//! # const FINALIZER_NAME: &'static str = "example.com/pod-counter";
140+
//! # const FINALIZER_NAME: Option<&'static str> = Some("example.com/pod-counter");
141141
//! # async fn apply(
142142
//! # &self,
143143
//! # client: Client,

0 commit comments

Comments
 (0)