1use std::collections::BTreeMap;
2use std::fmt;
3
4use client::ResourceExt;
5use k8s::OwnerReferenceExt;
6
7use super::*;
8
9#[async_trait::async_trait]
12pub trait KubeClientExt2: KubeClientExt {
13 async fn get_secret_opt(
17 &self,
18 name: &str,
19 namespace: impl Into<Option<&str>> + Send,
20 ) -> client::Result<Option<corev1::Secret>> {
21 self.secrets(namespace).get_opt(name).await
22 }
23
24 async fn get_secret(
27 &self,
28 name: &str,
29 namespace: impl Into<Option<&str>> + Send,
30 ) -> client::Result<corev1::Secret> {
31 self.secrets(namespace).get(name).await
32 }
33
34 async fn get_deployment_opt(
38 &self,
39 name: &str,
40 namespace: impl Into<Option<&str>> + Send,
41 ) -> client::Result<Option<appsv1::Deployment>> {
42 self.deployments(namespace).get_opt(name).await
43 }
44
45 async fn get_deployment(
48 &self,
49 name: &str,
50 namespace: impl Into<Option<&str>> + Send,
51 ) -> client::Result<appsv1::Deployment> {
52 self.deployments(namespace).get(name).await
53 }
54
55 async fn get_apiservice_opt(
59 &self,
60 name: &str,
61 ) -> client::Result<Option<apiregistrationv1::APIService>> {
62 self.apiservices().get_opt(name).await
63 }
64
65 async fn get_apiservice(&self, name: &str) -> client::Result<apiregistrationv1::APIService> {
68 self.apiservices().get(name).await
69 }
70
71 async fn get_crd_opt(
75 &self,
76 name: &str,
77 ) -> client::Result<Option<apiextensionsv1::CustomResourceDefinition>> {
78 self.crds().get_opt(name).await
79 }
80
81 async fn get_crd(
84 &self,
85 name: &str,
86 ) -> client::Result<apiextensionsv1::CustomResourceDefinition> {
87 self.crds().get(name).await
88 }
89
90 async fn get_owner_k<O, K>(&self, o: &O) -> client::Result<Option<K>>
93 where
94 O: client::ResourceExt + Sync,
95 K: Clone
96 + fmt::Debug
97 + k8s::openapi::serde::de::DeserializeOwned
98 + client::Resource<Scope = k8s::openapi::NamespaceResourceScope>,
99 <K as client::Resource>::DynamicType: Default,
100 {
101 let dynamic_default = K::DynamicType::default();
102 let kind = K::kind(&dynamic_default);
103 let namespace = o.namespace();
104 if let Some(name) = o
105 .owner_references()
106 .iter()
107 .find(|owner| owner.kind == kind)
108 .map(|owner| &owner.name)
109 {
110 self.namespaced_k(namespace.as_deref()).get_opt(name).await
111 } else {
112 Ok(None)
113 }
114 }
115
116 async fn list_pods(
119 &self,
120 namespace: impl Into<Option<&str>> + Send,
121 ) -> client::Result<Vec<corev1::Pod>> {
122 self.list_k(namespace).await
123 }
124
125 async fn list_deployments(
128 &self,
129 namespace: impl Into<Option<&str>> + Send,
130 ) -> client::Result<Vec<appsv1::Deployment>> {
131 self.list_k(namespace).await
132 }
133
134 async fn list_replicasets(
137 &self,
138 namespace: impl Into<Option<&str>> + Send,
139 ) -> client::Result<Vec<appsv1::ReplicaSet>> {
140 self.list_k(namespace).await
141 }
142
143 async fn list_jobs(
146 &self,
147 namespace: impl Into<Option<&str>> + Send,
148 ) -> client::Result<Vec<batchv1::Job>> {
149 self.list_k(namespace).await
150 }
151
152 async fn list_cronjobs(
155 &self,
156 namespace: impl Into<Option<&str>> + Send,
157 ) -> client::Result<Vec<batchv1::CronJob>> {
158 self.list_k(namespace).await
159 }
160
161 async fn list_k<K>(&self, namespace: impl Into<Option<&str>> + Send) -> client::Result<Vec<K>>
164 where
165 K: Clone
166 + fmt::Debug
167 + k8s::openapi::serde::de::DeserializeOwned
168 + client::Resource<Scope = k8s::openapi::NamespaceResourceScope>,
169 <K as client::Resource>::DynamicType: Default,
170 {
171 let lp = self.list_params();
172 self.namespaced_k(namespace)
173 .list(&lp)
174 .await
175 .map(|list| list.items)
176 }
177
178 async fn get_pods_by_deployment_name(
182 &self,
183 name: &str,
184 namespace: impl Into<Option<&str>> + Send,
185 ) -> client::Result<Option<Vec<corev1::Pod>>> {
186 let Some(deployment) = self.get_deployment_opt(name, namespace).await? else {
188 return Ok(None);
189 };
190
191 self.get_pods_by_deployment(&deployment).await
192 }
193
194 async fn get_pods_by_deployment(
198 &self,
199 deployment: &appsv1::Deployment,
200 ) -> client::Result<Option<Vec<corev1::Pod>>> {
201 let namespace = deployment.namespace();
202 let mut replicasets = self
204 .list_replicasets(namespace.as_deref())
205 .await?
206 .into_iter()
207 .filter(|rs| rs.is_controlled_by(deployment))
208 .collect::<Vec<_>>();
209
210 replicasets.sort_by_key(|rs| rs.creation_timestamp());
212 let Some(new) = replicasets
213 .iter()
214 .find(|rs| match_template_spec_no_hash(rs, deployment))
215 else {
216 return Ok(None);
217 };
218
219 let pods = self
221 .list_pods(namespace.as_deref())
222 .await?
223 .into_iter()
224 .filter(|pod| pod.is_controlled_by(new))
225 .collect();
226
227 Ok(Some(pods))
228 }
229}
230
231impl KubeClientExt2 for client::Client {}
232
233fn match_template_spec_no_hash(rs: &appsv1::ReplicaSet, deployment: &appsv1::Deployment) -> bool {
234 let rs_template = rs_pod_template(rs).map(remove_hash);
235 let deployment_template = deployment_pod_template(deployment).map(remove_hash);
236 rs_template == deployment_template
237}
238
239fn remove_hash(template: &corev1::PodTemplateSpec) -> corev1::PodTemplateSpec {
240 let mut template = template.clone();
241 if let Some(labels) = labels_mut(&mut template) {
242 labels.remove(k8s::label::DEFAULT_DEPLOYMENT_UNIQUE_LABEL_KEY);
243 }
244 template
245}
246
247fn labels_mut(template: &mut corev1::PodTemplateSpec) -> Option<&mut BTreeMap<String, String>> {
248 template.metadata.as_mut()?.labels.as_mut()
249}
250
251fn rs_pod_template(rs: &appsv1::ReplicaSet) -> Option<&corev1::PodTemplateSpec> {
252 rs.spec.as_ref()?.template.as_ref()
253}
254
255fn deployment_pod_template(deployment: &appsv1::Deployment) -> Option<&corev1::PodTemplateSpec> {
256 deployment.spec.as_ref().map(|spec| &spec.template)
257}