11use std:: collections:: BTreeMap ;
2- use std:: error:: Error ;
2+ use std:: error:: Error as _ ;
33use std:: sync:: { Arc , Mutex } ;
44use std:: time:: Duration ;
55
@@ -14,6 +14,14 @@ use kube_runtime::watcher;
1414use rand:: { Rng , rng} ;
1515use 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.
1927pub struct Controller < Ctx : Context >
@@ -36,7 +44,6 @@ impl<Ctx: Context> Controller<Ctx>
3644where
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
213220pub 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 ,
0 commit comments