k8s_controller/lib.rs
1#![allow(clippy::style)]
2#![allow(clippy::complexity)]
3#![allow(clippy::large_enum_variant)]
4#![allow(clippy::mutable_key_type)]
5#![allow(clippy::stable_sort_primitive)]
6#![allow(clippy::map_entry)]
7#![allow(clippy::box_default)]
8#![warn(clippy::bool_comparison)]
9#![warn(clippy::clone_on_ref_ptr)]
10#![warn(clippy::no_effect)]
11#![warn(clippy::unnecessary_unwrap)]
12#![warn(clippy::dbg_macro)]
13#![warn(clippy::todo)]
14#![warn(clippy::wildcard_dependencies)]
15#![warn(clippy::zero_prefixed_literal)]
16#![warn(clippy::borrowed_box)]
17#![warn(clippy::deref_addrof)]
18#![warn(clippy::double_must_use)]
19#![warn(clippy::double_parens)]
20#![warn(clippy::extra_unused_lifetimes)]
21#![warn(clippy::needless_borrow)]
22#![warn(clippy::needless_question_mark)]
23#![warn(clippy::needless_return)]
24#![warn(clippy::redundant_pattern)]
25#![warn(clippy::redundant_slicing)]
26#![warn(clippy::redundant_static_lifetimes)]
27#![warn(clippy::single_component_path_imports)]
28#![warn(clippy::unnecessary_cast)]
29#![warn(clippy::useless_asref)]
30#![warn(clippy::useless_conversion)]
31#![warn(clippy::builtin_type_shadow)]
32#![warn(clippy::duplicate_underscore_argument)]
33#![warn(clippy::double_neg)]
34#![warn(clippy::unnecessary_mut_passed)]
35#![warn(clippy::wildcard_in_or_patterns)]
36#![warn(clippy::crosspointer_transmute)]
37#![warn(clippy::excessive_precision)]
38#![warn(clippy::panicking_overflow_checks)]
39#![warn(clippy::as_conversions)]
40#![warn(clippy::match_overlapping_arm)]
41#![warn(clippy::zero_divided_by_zero)]
42#![warn(clippy::must_use_unit)]
43#![warn(clippy::suspicious_assignment_formatting)]
44#![warn(clippy::suspicious_else_formatting)]
45#![warn(clippy::suspicious_unary_op_formatting)]
46#![warn(clippy::mut_mutex_lock)]
47#![warn(clippy::print_literal)]
48#![warn(clippy::same_item_push)]
49#![warn(clippy::useless_format)]
50#![warn(clippy::write_literal)]
51#![warn(clippy::redundant_closure)]
52#![warn(clippy::redundant_closure_call)]
53#![warn(clippy::unnecessary_lazy_evaluations)]
54#![warn(clippy::partialeq_ne_impl)]
55#![warn(clippy::redundant_field_names)]
56#![warn(clippy::transmutes_expressible_as_ptr_casts)]
57#![warn(clippy::unused_async)]
58#![warn(clippy::disallowed_methods)]
59#![warn(clippy::disallowed_macros)]
60#![warn(clippy::disallowed_types)]
61#![warn(clippy::from_over_into)]
62#![cfg_attr(docsrs, feature(async_fn_in_trait))]
63
64//! This crate implements a lightweight framework around
65//! [`kube_runtime::Controller`] which provides a simpler interface for common
66//! controller patterns. To use it, you define the data that your controller is
67//! going to operate over, and implement the [`Context`] trait on that struct:
68//!
69//! ```no_run
70//! # use std::collections::BTreeSet;
71//! # use std::sync::{Arc, Mutex};
72//! # use k8s_openapi::api::core::v1::Pod;
73//! # use kube::{Client, Resource};
74//! # use kube_runtime::controller::Action;
75//! #[derive(Default, Clone)]
76//! struct PodCounter {
77//! pods: Arc<Mutex<BTreeSet<String>>>,
78//! }
79//!
80//! impl PodCounter {
81//! fn pod_count(&self) -> usize {
82//! let mut pods = self.pods.lock().unwrap();
83//! pods.len()
84//! }
85//! }
86//!
87//! #[async_trait::async_trait]
88//! impl k8s_controller::Context for PodCounter {
89//! type Resource = Pod;
90//! type Error = kube::Error;
91//!
92//! const FINALIZER_NAME: &'static str = "example.com/pod-counter";
93//!
94//! async fn apply(
95//! &self,
96//! client: Client,
97//! pod: &Self::Resource,
98//! ) -> Result<Option<Action>, Self::Error> {
99//! let mut pods = self.pods.lock().unwrap();
100//! pods.insert(pod.meta().uid.as_ref().unwrap().clone());
101//! Ok(None)
102//! }
103//!
104//! async fn cleanup(
105//! &self,
106//! client: Client,
107//! pod: &Self::Resource,
108//! ) -> Result<Option<Action>, Self::Error> {
109//! let mut pods = self.pods.lock().unwrap();
110//! pods.remove(pod.meta().uid.as_ref().unwrap());
111//! Ok(None)
112//! }
113//! }
114//! ```
115//!
116//! Then you can run it against your Kubernetes cluster by creating a
117//! [`Controller`]:
118//!
119//! ```no_run
120//! # use std::collections::BTreeSet;
121//! # use std::sync::{Arc, Mutex};
122//! # use std::thread::sleep;
123//! # use std::time::Duration;
124//! # use k8s_openapi::api::core::v1::Pod;
125//! # use kube::{Config, Client};
126//! # use kube_runtime::controller::Action;
127//! # use kube_runtime::watcher;
128//! # use tokio::task;
129//! # #[derive(Default, Clone)]
130//! # struct PodCounter {
131//! # pods: Arc<Mutex<BTreeSet<String>>>,
132//! # }
133//! # impl PodCounter {
134//! # fn pod_count(&self) -> usize { todo!() }
135//! # }
136//! # #[async_trait::async_trait]
137//! # impl k8s_controller::Context for PodCounter {
138//! # type Resource = Pod;
139//! # type Error = kube::Error;
140//! # const FINALIZER_NAME: &'static str = "example.com/pod-counter";
141//! # async fn apply(
142//! # &self,
143//! # client: Client,
144//! # pod: &Self::Resource,
145//! # ) -> Result<Option<Action>, Self::Error> { todo!() }
146//! # async fn cleanup(
147//! # &self,
148//! # client: Client,
149//! # pod: &Self::Resource,
150//! # ) -> Result<Option<Action>, Self::Error> { todo!() }
151//! # }
152//! # async fn foo() {
153//! let kube_config = Config::infer().await.unwrap();
154//! let kube_client = Client::try_from(kube_config).unwrap();
155//! let context = PodCounter::default();
156//! let controller = k8s_controller::Controller::namespaced_all(
157//! kube_client,
158//! context.clone(),
159//! watcher::Config::default(),
160//! );
161//! task::spawn(controller.run());
162//!
163//! loop {
164//! println!("{} pods running", context.pod_count());
165//! sleep(Duration::from_secs(1));
166//! }
167//! # }
168//! ```
169
170mod controller;
171
172pub use controller::{Context, Controller};