kube_client/api/util/
mod.rs

1use crate::{
2    api::{Api, Resource},
3    Error, Result,
4};
5use k8s_openapi::api::{
6    authentication::v1::TokenRequest,
7    core::v1::{Node, ServiceAccount},
8};
9use kube_core::{params::PostParams, util::Restart};
10use serde::de::DeserializeOwned;
11
12mod csr;
13
14impl<K> Api<K>
15where
16    K: Restart + Resource + DeserializeOwned,
17{
18    /// Trigger a restart of a Resource.
19    pub async fn restart(&self, name: &str) -> Result<K> {
20        let mut req = self.request.restart(name).map_err(Error::BuildRequest)?;
21        req.extensions_mut().insert("restart");
22        self.client.request::<K>(req).await
23    }
24}
25
26impl Api<Node> {
27    /// Cordon a Node.
28    pub async fn cordon(&self, name: &str) -> Result<Node> {
29        let mut req = self.request.cordon(name).map_err(Error::BuildRequest)?;
30        req.extensions_mut().insert("cordon");
31        self.client.request::<Node>(req).await
32    }
33
34    /// Uncordon a Node.
35    pub async fn uncordon(&self, name: &str) -> Result<Node> {
36        let mut req = self.request.uncordon(name).map_err(Error::BuildRequest)?;
37        req.extensions_mut().insert("cordon");
38        self.client.request::<Node>(req).await
39    }
40}
41
42impl Api<ServiceAccount> {
43    /// Create a TokenRequest of a ServiceAccount
44    pub async fn create_token_request(
45        &self,
46        name: &str,
47        pp: &PostParams,
48        token_request: &TokenRequest,
49    ) -> Result<TokenRequest> {
50        let bytes = serde_json::to_vec(token_request).map_err(Error::SerdeError)?;
51        let mut req = self
52            .request
53            .create_subresource("token", name, pp, bytes)
54            .map_err(Error::BuildRequest)?;
55        req.extensions_mut().insert("create_token_request");
56        self.client.request::<TokenRequest>(req).await
57    }
58}
59
60// Tests that require a cluster and the complete feature set
61// Can be run with `cargo test -p kube-client --lib -- --ignored`
62#[cfg(test)]
63#[cfg(feature = "client")]
64mod test {
65    use crate::{
66        api::{Api, DeleteParams, ListParams, PostParams},
67        Client,
68    };
69    use k8s_openapi::api::{
70        authentication::v1::{TokenRequest, TokenRequestSpec, TokenReview, TokenReviewSpec},
71        core::v1::{Node, ServiceAccount},
72    };
73    use serde_json::json;
74
75    #[tokio::test]
76    #[ignore = "needs kubeconfig"]
77    async fn node_cordon_and_uncordon_works() -> Result<(), Box<dyn std::error::Error>> {
78        let client = Client::try_default().await?;
79
80        let node_name = "fakenode";
81        let fake_node = serde_json::from_value(json!({
82        "apiVersion": "v1",
83        "kind": "Node",
84        "metadata": {
85            "name": node_name,
86            },
87        }))?;
88
89        let nodes: Api<Node> = Api::all(client.clone());
90        nodes.create(&PostParams::default(), &fake_node).await?;
91
92        let schedulables = ListParams::default().fields("spec.unschedulable==false");
93        let nodes_init = nodes.list(&schedulables).await?;
94        let num_nodes_before_cordon = nodes_init.items.len();
95
96        nodes.cordon(node_name).await?;
97        let nodes_after_cordon = nodes.list(&schedulables).await?;
98        assert_eq!(nodes_after_cordon.items.len(), num_nodes_before_cordon - 1);
99
100        nodes.uncordon(node_name).await?;
101        let nodes_after_uncordon = nodes.list(&schedulables).await?;
102        assert_eq!(nodes_after_uncordon.items.len(), num_nodes_before_cordon);
103        nodes.delete(node_name, &DeleteParams::default()).await?;
104        Ok(())
105    }
106
107    #[tokio::test]
108    #[ignore = "requires a cluster"]
109    async fn create_token_request() -> Result<(), Box<dyn std::error::Error>> {
110        let client = Client::try_default().await?;
111
112        let serviceaccount_name = "fakesa";
113        let serviceaccount_namespace = "default";
114        let audiences = vec!["api".to_string()];
115
116        let serviceaccounts: Api<ServiceAccount> = Api::namespaced(client.clone(), serviceaccount_namespace);
117        let tokenreviews: Api<TokenReview> = Api::all(client);
118
119        // Create ServiceAccount
120        let fake_sa = serde_json::from_value(json!({
121            "apiVersion": "v1",
122            "kind": "ServiceAccount",
123            "metadata": {
124                "name": serviceaccount_name,
125            },
126        }))?;
127        serviceaccounts.create(&PostParams::default(), &fake_sa).await?;
128
129        // Create TokenRequest
130        let tokenrequest = serviceaccounts
131            .create_token_request(serviceaccount_name, &PostParams::default(), &TokenRequest {
132                metadata: Default::default(),
133                spec: TokenRequestSpec {
134                    audiences: audiences.clone(),
135                    bound_object_ref: None,
136                    expiration_seconds: None,
137                },
138                status: None,
139            })
140            .await?;
141        let token = tokenrequest.status.unwrap().token;
142        assert!(!token.is_empty());
143
144        // Check created token is valid with TokenReview
145        let tokenreview = tokenreviews
146            .create(&PostParams::default(), &TokenReview {
147                metadata: Default::default(),
148                spec: TokenReviewSpec {
149                    audiences: Some(audiences.clone()),
150                    token: Some(token),
151                },
152                status: None,
153            })
154            .await?;
155        let tokenreviewstatus = tokenreview.status.unwrap();
156        assert_eq!(tokenreviewstatus.audiences, Some(audiences));
157        assert_eq!(tokenreviewstatus.authenticated, Some(true));
158        assert_eq!(tokenreviewstatus.error, None);
159        assert_eq!(
160            tokenreviewstatus.user.unwrap().username,
161            Some(format!(
162                "system:serviceaccount:{serviceaccount_namespace}:{serviceaccount_name}"
163            ))
164        );
165
166        // Cleanup ServiceAccount
167        serviceaccounts
168            .delete(serviceaccount_name, &DeleteParams::default())
169            .await?;
170
171        Ok(())
172    }
173}