kube_core/
util.rs

1//! Utils and helpers
2
3use crate::{
4    Request,
5    params::{Patch, PatchParams},
6    request,
7};
8use jiff::Timestamp;
9use k8s_openapi::api::apps::v1::{DaemonSet, Deployment, ReplicaSet, StatefulSet};
10
11/// Restartable Resource marker trait
12pub trait Restart {}
13
14impl Restart for Deployment {}
15impl Restart for DaemonSet {}
16impl Restart for StatefulSet {}
17impl Restart for ReplicaSet {}
18
19impl Request {
20    /// Restart a resource
21    pub fn restart(&self, name: &str) -> Result<http::Request<Vec<u8>>, request::Error> {
22        // `jiff::Timestamp` provides RFC3339 via `Display`, docs: https://docs.rs/jiff/latest/jiff/struct.Timestamp.html#impl-Display-for-Timestamp
23        let patch = serde_json::json!({
24          "spec": {
25            "template": {
26              "metadata": {
27                "annotations": {
28                  "kube.kubernetes.io/restartedAt": Timestamp::now().to_string()
29                }
30              }
31            }
32          }
33        });
34
35        let pparams = PatchParams::default();
36        self.patch(name, &pparams, &Patch::Merge(patch))
37    }
38}
39
40impl Request {
41    /// Cordon a resource
42    pub fn cordon(&self, name: &str) -> Result<http::Request<Vec<u8>>, request::Error> {
43        self.set_unschedulable(name, true)
44    }
45
46    /// Uncordon a resource
47    pub fn uncordon(&self, name: &str) -> Result<http::Request<Vec<u8>>, request::Error> {
48        self.set_unschedulable(name, false)
49    }
50
51    fn set_unschedulable(
52        &self,
53        node_name: &str,
54        value: bool,
55    ) -> Result<http::Request<Vec<u8>>, request::Error> {
56        self.patch(
57            node_name,
58            &PatchParams::default(),
59            &Patch::Strategic(serde_json::json!({ "spec": { "unschedulable": value } })),
60        )
61    }
62}
63
64#[cfg(test)]
65mod test {
66    use crate::{params::Patch, request::Request, resource::Resource};
67
68    #[test]
69    fn restart_patch_is_correct() {
70        use k8s_openapi::api::apps::v1 as appsv1;
71
72        let url = appsv1::Deployment::url_path(&(), Some("ns"));
73        let req = Request::new(url).restart("mydeploy").unwrap();
74        assert_eq!(req.uri(), "/apis/apps/v1/namespaces/ns/deployments/mydeploy?");
75        assert_eq!(req.method(), "PATCH");
76        assert_eq!(
77            req.headers().get("Content-Type").unwrap().to_str().unwrap(),
78            Patch::Merge(()).content_type()
79        );
80    }
81
82    #[test]
83    fn cordon_patch_is_correct() {
84        use k8s_openapi::api::core::v1::Node;
85
86        let url = Node::url_path(&(), Some("ns"));
87        let req = Request::new(url).cordon("mynode").unwrap();
88        assert_eq!(req.uri(), "/api/v1/namespaces/ns/nodes/mynode?");
89        assert_eq!(req.method(), "PATCH");
90        assert_eq!(
91            req.headers().get("Content-Type").unwrap().to_str().unwrap(),
92            Patch::Strategic(()).content_type()
93        );
94    }
95}