1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#![allow(clippy::style)]
#![allow(clippy::complexity)]
#![allow(clippy::large_enum_variant)]
#![allow(clippy::mutable_key_type)]
#![allow(clippy::stable_sort_primitive)]
#![allow(clippy::map_entry)]
#![allow(clippy::box_default)]
#![warn(clippy::bool_comparison)]
#![warn(clippy::clone_on_ref_ptr)]
#![warn(clippy::no_effect)]
#![warn(clippy::unnecessary_unwrap)]
#![warn(clippy::dbg_macro)]
#![warn(clippy::todo)]
#![warn(clippy::wildcard_dependencies)]
#![warn(clippy::zero_prefixed_literal)]
#![warn(clippy::borrowed_box)]
#![warn(clippy::deref_addrof)]
#![warn(clippy::double_must_use)]
#![warn(clippy::double_parens)]
#![warn(clippy::extra_unused_lifetimes)]
#![warn(clippy::needless_borrow)]
#![warn(clippy::needless_question_mark)]
#![warn(clippy::needless_return)]
#![warn(clippy::redundant_pattern)]
#![warn(clippy::redundant_slicing)]
#![warn(clippy::redundant_static_lifetimes)]
#![warn(clippy::single_component_path_imports)]
#![warn(clippy::unnecessary_cast)]
#![warn(clippy::useless_asref)]
#![warn(clippy::useless_conversion)]
#![warn(clippy::builtin_type_shadow)]
#![warn(clippy::duplicate_underscore_argument)]
#![warn(clippy::double_neg)]
#![warn(clippy::unnecessary_mut_passed)]
#![warn(clippy::wildcard_in_or_patterns)]
#![warn(clippy::crosspointer_transmute)]
#![warn(clippy::excessive_precision)]
#![warn(clippy::overflow_check_conditional)]
#![warn(clippy::as_conversions)]
#![warn(clippy::match_overlapping_arm)]
#![warn(clippy::zero_divided_by_zero)]
#![warn(clippy::must_use_unit)]
#![warn(clippy::suspicious_assignment_formatting)]
#![warn(clippy::suspicious_else_formatting)]
#![warn(clippy::suspicious_unary_op_formatting)]
#![warn(clippy::mut_mutex_lock)]
#![warn(clippy::print_literal)]
#![warn(clippy::same_item_push)]
#![warn(clippy::useless_format)]
#![warn(clippy::write_literal)]
#![warn(clippy::redundant_closure)]
#![warn(clippy::redundant_closure_call)]
#![warn(clippy::unnecessary_lazy_evaluations)]
#![warn(clippy::partialeq_ne_impl)]
#![warn(clippy::redundant_field_names)]
#![warn(clippy::transmutes_expressible_as_ptr_casts)]
#![warn(clippy::unused_async)]
#![warn(clippy::disallowed_methods)]
#![warn(clippy::disallowed_macros)]
#![warn(clippy::disallowed_types)]
#![warn(clippy::from_over_into)]
#![cfg_attr(docsrs, feature(async_fn_in_trait))]

//! This crate implements a lightweight framework around
//! [`kube_runtime::Controller`] which provides a simpler interface for common
//! controller patterns. To use it, you define the data that your controller is
//! going to operate over, and implement the [`Context`] trait on that struct:
//!
//! ```no_run
//! # use std::collections::BTreeSet;
//! # use std::sync::{Arc, Mutex};
//! # use k8s_openapi::api::core::v1::Pod;
//! # use kube::{Client, Resource};
//! # use kube_runtime::controller::Action;
//! #[derive(Default, Clone)]
//! struct PodCounter {
//!     pods: Arc<Mutex<BTreeSet<String>>>,
//! }
//!
//! impl PodCounter {
//!     fn pod_count(&self) -> usize {
//!         let mut pods = self.pods.lock().unwrap();
//!         pods.len()
//!     }
//! }
//!
//! #[async_trait::async_trait]
//! impl k8s_controller::Context for PodCounter {
//!     type Resource = Pod;
//!     type Error = kube::Error;
//!
//!     const FINALIZER_NAME: &'static str = "example.com/pod-counter";
//!
//!     async fn apply(
//!         &self,
//!         client: Client,
//!         pod: &Self::Resource,
//!     ) -> Result<Option<Action>, Self::Error> {
//!         let mut pods = self.pods.lock().unwrap();
//!         pods.insert(pod.meta().uid.as_ref().unwrap().clone());
//!         Ok(None)
//!     }
//!
//!     async fn cleanup(
//!         &self,
//!         client: Client,
//!         pod: &Self::Resource,
//!     ) -> Result<Option<Action>, Self::Error> {
//!         let mut pods = self.pods.lock().unwrap();
//!         pods.remove(pod.meta().uid.as_ref().unwrap());
//!         Ok(None)
//!     }
//! }
//! ```
//!
//! Then you can run it against your Kubernetes cluster by creating a
//! [`Controller`]:
//!
//! ```no_run
//! # use std::collections::BTreeSet;
//! # use std::sync::{Arc, Mutex};
//! # use std::thread::sleep;
//! # use std::time::Duration;
//! # use k8s_openapi::api::core::v1::Pod;
//! # use kube::{Config, Client};
//! # use kube_runtime::controller::Action;
//! # use kube_runtime::watcher;
//! # use tokio::task;
//! # #[derive(Default, Clone)]
//! # struct PodCounter {
//! #     pods: Arc<Mutex<BTreeSet<String>>>,
//! # }
//! # impl PodCounter {
//! #     fn pod_count(&self) -> usize { todo!() }
//! # }
//! # #[async_trait::async_trait]
//! # impl k8s_controller::Context for PodCounter {
//! #     type Resource = Pod;
//! #     type Error = kube::Error;
//! #     const FINALIZER_NAME: &'static str = "example.com/pod-counter";
//! #     async fn apply(
//! #         &self,
//! #         client: Client,
//! #         pod: &Self::Resource,
//! #     ) -> Result<Option<Action>, Self::Error> { todo!() }
//! #     async fn cleanup(
//! #         &self,
//! #         client: Client,
//! #         pod: &Self::Resource,
//! #     ) -> Result<Option<Action>, Self::Error> { todo!() }
//! # }
//! # async fn foo() {
//! let kube_config = Config::infer().await.unwrap();
//! let kube_client = Client::try_from(kube_config).unwrap();
//! let context = PodCounter::default();
//! let controller = k8s_controller::Controller::namespaced_all(
//!     kube_client,
//!     context.clone(),
//!     watcher::Config::default(),
//! );
//! task::spawn(controller.run());
//!
//! loop {
//!     println!("{} pods running", context.pod_count());
//!     sleep(Duration::from_secs(1));
//! }
//! # }
//! ```

mod controller;

pub use controller::{Context, Controller};