kube_lease_manager/lib.rs
1#![deny(unsafe_code, warnings, missing_docs)]
2//! Ergonomic and reliable leader election using Kubernetes Lease API.
3//!
4//! `kube-lease-manager` is a high-level helper to facilitate leader election using
5//! [Lease Kubernetes resource](https://kubernetes.io/docs/reference/kubernetes-api/cluster-resources/lease-v1/).
6//! It ensures that only a single instance of the lease managers holds the lock at any moment of time.
7//!
8//! Some of the typical use cases:
9//! * automatic coordination of leader election between several instances (Pods) of Kubernetes controllers;
10//! * ensure only a single instance of concurrent jobs is running right now;
11//! * exclusive acquiring of shared resource.
12//!
13//! ## Features
14//!
15//! * [`LeaseManager`] is a central part of the crate.
16//! This is a convenient wrapper around a Kubernetes `Lease`
17//! resource to manage all aspects of a leader election process.
18//! * Provides two different high-level approaches to lock and release lease:
19//! fully automated or partially manual lock control.
20//! * Uses [Server-Side-Apply](https://kubernetes.io/docs/reference/using-api/server-side-apply/)
21//! approach to update lease state that facilitates conflict detection and resolution
22//! and makes impossible concurrent locking.
23//! * Tolerate configurable time skew between nodes of the Kubernetes cluster.
24//! * Behavioral parameters of the lease manager are easily and flexibly configurable.
25//! * Uses well-known and highly appreciated [kube](https://crates.io/crates/kube)
26//! and [Tokio](https://crates.io/crates/tokio)
27//! crates to access Kubernetes API and coordinate asynchronous tasks execution.
28//! * You don't need to use low-level Kubernetes API.
29//! * Uses Tokio [tracing](https://crates.io/crates/tracing) crate to provide event logs.
30//!
31//! ---
32//!
33//! As mentioned above, `kube-lease-manager` provides two possible ways to manage lease lock:
34//! 1. _Fully automated_: you create a [`LeaseManager`] instance and run its [`watch()`](LeaseManager::watch())
35//! method.
36//! It returns [Tokio watch channel](https://docs.rs/tokio/latest/tokio/sync/watch/index.html)
37//! to watch on state changes
38//! Besides that it runs an unattended background task
39//! which permanently tries to lock lease if it's free and publish changed state to the channel.
40//! The task finishes if the channel is closed.
41//! 2. _Partially manual_: you create [`LeaseManager`]
42//! instance and use its [`changed()`](LeaseManager::changed())
43//! and [`release()`](LeaseManager::release()) methods to control lock.
44//! `changed()` tries to lock a lease as soon as it becomes free and returns the actual
45//! lock state when it's changed.
46//! Your responsibilities are:
47//! - to keep `changed()` running (it's a `Future`) to ensure the lock is refreshing while it's in use;
48//! - to call `release()` when you don't need the lock and want to make it free for others.
49//!
50//! The first way ensures that the lease is locked (has a holder) at any moment of time.
51//! Second makes it possible to acquire and release a lock when you need it.
52//!
53//! ## LeaseManager config
54//!
55//! The main config of the [`LeaseManager`] is a [`LeaseParams`] structure, which
56//! describes several parameters that affect managers' behavior.
57//! Those parameters are:
58//! * _identity_: unique string identifier of the lease manager among all other instances.
59//! Usually this is some randomly generated string (UUID, for example).
60//! `LeaseParams` can provide a default value by generating a random 32-symbol alphanumeric string,
61//! or you can explicitly specify identity string while creating `LeaseParams`
62//! or directly via [`LeaseManagerBuilder`].
63//! * _duration_:
64//! this is a maximum duration (in seconds) of lock validity after the last renewal (confirmation) of the lock.
65//! In other words,
66//! the current lock holder is obliged to renew (re-confirm) its lock during this time after the last renewal.
67//! If the holder didn't re-confirm the lock, any other `LeaseManager` instance is permitted to grab the lock.
68//! The default value provided by `LeaseParams` is 30 seconds and may be configured.
69//! * _grace_: to avoid flapping losses of lock, the actual leaseholder tries to re-confirm (renew) lock earlier,
70//! before it expires (before the end of the `duration` interval).
71//! This parameter defines an interval (in seconds)
72//! before lock expiration when the lock holder has to renew its lock.
73//! As a side effect,
74//! this interval can be considered as a maximum allowed time synchronization skew between nodes in the Kubernetes cluster
75//! to avoid overlying locking.
76//! The default value provided by `LeaseParams` is 5 seconds and may be configured.
77//! * _field_manager_:
78//! identifier of the Kubernetes [field manager](https://kubernetes.io/docs/reference/using-api/server-side-apply/#managers)
79//! to authorize changes of the Lease resources.
80//! It should be unique among other managers.
81//! Usually you don't need to specify it explicitly because of LeaseParams generates it concatenating crates'
82//! name (kube-lease-manager) and `identity` string.
83//! But it's possible to specify field manger directly via the `LeaseParams`
84//! [with_field_manager()](LeaseParams::with_field_manager) method or using [`LeaseManagerBuilder`].
85//!
86//! The next config option is a [`LeaseCreateMode`]
87//! which defines the behavior how [`LeaseManager`] manages Lease Kubernetes resource during startup.
88//! The default behavior is [`AutoCreate`](LeaseCreateMode::AutoCreate):
89//! create a resource if it doesn't exist or use an existing one if it's already present.
90//!
91//! The last significant parameter of the [`LeaseManager`] is a Lease resource name.
92//! Obviously, it has no defaults and should be specified explicitly via LeaseManager [`new()`](LeaseManager::new)
93//! constructor of using [`LeaseManagerBuilder`].
94//!
95//! ## Examples
96//!
97//! Create [`LeaseManager`] with reasonable defaults using convenient ['LeaseManagerBuilder'] and use ["watch"](LeaseManager::watch)
98//! approach to get notified about lock holder changes.
99//!
100//! ```no_run
101//! use kube::Client;
102//! use kube_lease_manager::{LeaseManagerBuilder, Result};
103//! use std::time::Duration;
104//!
105//! #[tokio::main]
106//! async fn main() -> Result<()> {
107//! // Use the default Kube client
108//! let client = Client::try_default().await?;
109//!
110//! // Create the simplest LeaseManager with reasonable defaults using a convenient builder.
111//! // It uses a Lease resource called `test-lease-name`.
112//! // With the default auto-create mode, Lease will be created if it doesn't exist,
113//! // or the existing one will be used otherwise.
114//! // The default lease duration is 30 seconds with a grace period of 5 seconds.
115//! let manager = LeaseManagerBuilder::new(client, "test-lease-name")
116//! .build()
117//! .await?;
118//!
119//! // Start manager in watching mode and get back status channel and task handler.
120//! let (mut channel, task) = manager.watch().await;
121//!
122//! // Watch on the channel for lock state changes.
123//! tokio::select! {
124//! _ = channel.changed() => {
125//! let lock_state = *channel.borrow_and_update();
126//!
127//! if lock_state {
128//! // Do something useful as a leader
129//! println!("Got a luck!");
130//! }
131//! }
132//! _ = tokio::time::sleep(Duration::from_secs(10)) => {
133//! println!("Unable to get lock during 10s");
134//! }
135//! }
136//!
137//! // Explicitly close the control channel
138//! drop(channel);
139//!
140//! // Wait for the finish of the manager and get it back
141//! let _manager = tokio::join!(task).0.unwrap()?;
142//!
143//! Ok(())
144//! }
145//! ```
146//!
147//! More examples with detailed explanations can be found in the corresponding documentation chapters about [`LeaseManager`],
148//! [`LeaseManagerBuilder`], [`LeaseParams`] and [`LeaseCreateMode`],
149//! as well as an explanation of the specific errors provided by [`LeaseManagerError`].
150//!
151//! ## Notes about dependencies
152//!
153//! 1. We depend on [`kube`](https://crates.io/crates/kube) as on the Kubernetes API library.
154//! In the majority of possible use cases, interaction with Kubernetes API requires TLS,
155//! so one of the relevant features of the `kube`
156//! crate should be added to your dependencies: `rustls-tls` or `openssl-tls`.
157//! Please consult with [`kube` crate documentation](https://crates.io/crates/kube).
158//! We deliberately don't include such a dependency in `kube-lease-manager`
159//! to don't force you to use a particular dependency,
160//! it's up to you.
161//! 2. We use [`k8s-openapi`](https://crates.io/crates/k8s-openapi),
162//! it provides bindings for the Kubernetes client API.
163//! If you use `kube-lease-manager`
164//! in a binary crate, you have to add this dependency as well with one of the necessary features enabled
165//! to reflect a minimal version of Kubernetes API of your application.
166//! For example, `v1_26` or `latest`.
167//! Please consult with [`k8s-openapi` crate documentation](https://crates.io/crates/k8s-openapi).
168//!
169mod backoff;
170mod error;
171mod manager;
172mod state;
173
174pub use error::*;
175pub use manager::*;