1use crate::error::{Error, Result};
5use k8s_openapi::api::core::v1::Namespace;
6use kube::api::{DeleteParams, Patch, PatchParams, PostParams};
7use kube::{Api, Client};
8use serde_json::json;
9use tokio::time::{sleep, Duration};
10
11pub struct NamespaceHandle {
12 namespace: String,
13 api: Api<Namespace>,
14}
15
16impl NamespaceHandle {
17 pub fn new(client: Client, namespace: &str) -> Self {
18 let api: Api<Namespace> = Api::all(client.clone());
19 NamespaceHandle {
20 namespace: namespace.to_string(),
21 api,
22 }
23 }
24
25 pub async fn create(&self) -> Result<()> {
26 let ns = Namespace {
27 metadata: kube::api::ObjectMeta {
28 name: Some(self.namespace.clone()),
29 ..Default::default()
30 },
31 ..Default::default()
32 };
33
34 match self.api.create(&PostParams::default(), &ns).await {
35 Ok(_) => Ok(()),
36 Err(kube::Error::Api(ae)) if ae.code == 409 => Err(Error::NamespaceExists),
37 Err(e) => Err(Error::from(e)),
38 }
39 }
40
41 pub async fn delete(&self) -> Result<()> {
42 log::debug!("Deleting namespace");
43 if self.try_delete().await? {
44 self.force_delete().await?;
45 } else {
46 log::debug!("Namespace '{}' deleted gracefully.", self.namespace);
47 }
48 Ok(())
49 }
50
51 async fn try_delete(&self) -> Result<bool> {
52 let delete_params = DeleteParams::default();
53
54 match self.api.delete(&self.namespace, &delete_params).await {
55 Ok(delete_response) => {
56 if delete_response.left().is_some() {
57 if self.wait_for_deletion(30).await? {
58 Ok(false)
59 } else {
60 Ok(true)
61 }
62 } else {
63 Ok(false)
64 }
65 }
66 Err(kube::Error::Api(ae)) if ae.code == 404 => Ok(false),
67 Err(e) => Err(Error::from(e)),
68 }
69 }
70
71 async fn wait_for_deletion(&self, timeout_seconds: u64) -> Result<bool> {
72 log::debug!("Waiting for namespace deletion");
73 for _ in 0..timeout_seconds {
74 match self.api.get(&self.namespace).await {
75 Ok(_) => sleep(Duration::from_secs(1)).await,
76 Err(kube::Error::Api(ae)) if ae.code == 404 => {
77 return Ok(true);
78 }
79 Err(e) => return Err(Error::from(e)),
80 }
81 }
82 Ok(false)
83 }
84
85 async fn force_delete(&self) -> Result<()> {
86 log::debug!("Force deleting namespace");
87 let patch = json!({
88 "metadata": {
89 "finalizers": null
90 }
91 });
92
93 self.api
94 .patch(
95 &self.namespace,
96 &PatchParams::default(),
97 &Patch::Merge(&patch),
98 )
99 .await
100 .map_err(Error::from)?;
101
102 let delete_params = DeleteParams {
103 grace_period_seconds: Some(0),
104 ..DeleteParams::default()
105 };
106
107 match self.api.delete(&self.namespace, &delete_params).await {
108 Ok(_) => {
109 self.wait_for_deletion(10).await?;
110 log::debug!("Namespace '{}' force deleted.", self.namespace);
111 Ok(())
112 }
113 Err(e) => Err(Error::from(e)),
114 }
115 }
116}