1use std::collections::HashMap;
2use std::fmt::{self, Display};
3use std::fmt::Debug;
4use std::marker::PhantomData;
5
6use serde::de::{DeserializeOwned, Deserializer};
7use serde::Deserialize;
8use serde::Serialize;
9
10use crate::Spec;
11
12pub const DEFAULT_NS: &str = "default";
13pub const TYPE_OPAQUE: &str = "Opaque";
14
15pub trait K8Meta {
16 fn name(&self) -> &str;
18
19 fn namespace(&self) -> &str;
21}
22
23pub trait LabelProvider: Sized {
24 fn set_label_map(self, labels: HashMap<String, String>) -> Self;
25
26 fn set_labels<T: ToString>(self, labels: Vec<(T, T)>) -> Self {
28 let mut label_map = HashMap::new();
29 for (key, value) in labels {
30 label_map.insert(key.to_string(), value.to_string());
31 }
32 self.set_label_map(label_map)
33 }
34}
35
36#[derive(Deserialize, Serialize, Eq, PartialEq, Debug, Default, Clone)]
39#[serde(rename_all = "camelCase", default)]
40pub struct ObjectMeta {
41 pub name: String,
43 #[serde(skip_serializing_if = "String::is_empty")]
44 pub namespace: String,
45 #[serde(skip_serializing_if = "String::is_empty")]
46 pub uid: String,
47 #[serde(skip_serializing_if = "String::is_empty")]
48 pub creation_timestamp: String,
49 #[serde(skip_serializing_if = "Option::is_none")]
50 pub generation: Option<i32>,
51 #[serde(skip_serializing_if = "String::is_empty")]
52 #[serde(default)]
53 pub resource_version: String,
54 #[serde(skip_serializing_if = "Option::is_none")]
56 pub cluster_name: Option<String>,
57 #[serde(skip_serializing_if = "Option::is_none")]
58 pub deletion_timestamp: Option<String>,
59 #[serde(skip_serializing_if = "Option::is_none")]
60 pub deletion_grace_period_seconds: Option<u32>,
61 #[serde(skip_serializing_if = "HashMap::is_empty")]
62 pub labels: HashMap<String, String>,
63 #[serde(skip_serializing_if = "Vec::is_empty")]
64 pub owner_references: Vec<OwnerReferences>,
65 #[serde(skip_serializing_if = "HashMap::is_empty")]
66 pub annotations: HashMap<String, String>,
67 #[serde(skip_serializing_if = "Vec::is_empty")]
68 pub finalizers: Vec<String>,
69}
70
71impl LabelProvider for ObjectMeta {
72 fn set_label_map(mut self, labels: HashMap<String, String>) -> Self {
73 self.labels = labels;
74 self
75 }
76}
77
78impl K8Meta for ObjectMeta {
79 fn name(&self) -> &str {
80 &self.name
81 }
82
83 fn namespace(&self) -> &str {
84 &self.namespace
85 }
86}
87
88impl ObjectMeta {
89 pub fn new<S>(name: S, name_space: S) -> Self
90 where
91 S: Into<String>,
92 {
93 Self {
94 name: name.into(),
95 namespace: name_space.into(),
96 ..Default::default()
97 }
98 }
99
100 pub fn set_labels<T: Into<String>>(mut self, labels: Vec<(T, T)>) -> Self {
102 let mut label_map = HashMap::new();
103 for (key, value) in labels {
104 label_map.insert(key.into(), value.into());
105 }
106 self.labels = label_map;
107 self
108 }
109
110 pub fn named<S>(name: S) -> Self
112 where
113 S: Into<String>,
114 {
115 Self {
116 name: name.into(),
117 ..Default::default()
118 }
119 }
120
121 pub fn make_owner_reference<S: Spec>(&self) -> OwnerReferences {
124 OwnerReferences {
125 api_version: S::api_version(),
126 kind: S::kind(),
127 name: self.name.clone(),
128 uid: self.uid.clone(),
129 ..Default::default()
131 }
132 }
133
134 pub fn namespace(&self) -> &str {
135 &self.namespace
136 }
137
138 pub fn make_child_input_metadata<S: Spec>(&self, childname: String) -> InputObjectMeta {
140 let owner_references: Vec<OwnerReferences> = vec![self.make_owner_reference::<S>()];
141
142 InputObjectMeta {
143 name: childname,
144 namespace: self.namespace().to_owned(),
145 owner_references,
146 ..Default::default()
147 }
148 }
149
150 pub fn as_input(&self) -> InputObjectMeta {
151 InputObjectMeta {
152 name: self.name.clone(),
153 namespace: self.namespace.clone(),
154 owner_references: self.owner_references.clone(),
155 ..Default::default()
156 }
157 }
158
159 pub fn as_item(&self) -> ItemMeta {
160 ItemMeta {
161 name: self.name.clone(),
162 namespace: self.namespace.clone(),
163 }
164 }
165
166 pub fn as_update(&self) -> UpdateItemMeta {
167 UpdateItemMeta {
168 name: self.name.clone(),
169 namespace: self.namespace.clone(),
170 resource_version: self.resource_version.clone(),
171 annotations: self.annotations.clone(),
172 owner_references: self.owner_references.clone(),
173 finalizers: self.finalizers.clone(),
174 labels: self.labels.clone(),
175 }
176 }
177}
178
179#[derive(Deserialize, Serialize, Debug, Default, Clone)]
180#[serde(rename_all = "camelCase")]
181pub struct InputObjectMeta {
182 pub name: String,
183 pub labels: HashMap<String, String>,
184 pub namespace: String,
185 pub owner_references: Vec<OwnerReferences>,
186 pub finalizers: Vec<String>,
187 pub annotations: HashMap<String, String>,
188}
189
190impl LabelProvider for InputObjectMeta {
191 fn set_label_map(mut self, labels: HashMap<String, String>) -> Self {
192 self.labels = labels;
193 self
194 }
195}
196
197impl fmt::Display for InputObjectMeta {
198 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199 write!(f, "{}:{}", self.name, self.namespace)
200 }
201}
202
203impl K8Meta for InputObjectMeta {
204 fn name(&self) -> &str {
205 &self.name
206 }
207
208 fn namespace(&self) -> &str {
209 &self.namespace
210 }
211}
212
213impl InputObjectMeta {
214 pub fn named<S: Into<String>>(name: S, namespace: S) -> Self {
216 InputObjectMeta {
217 name: name.into(),
218 namespace: namespace.into(),
219 ..Default::default()
220 }
221 }
222}
223
224impl From<ObjectMeta> for InputObjectMeta {
225 fn from(meta: ObjectMeta) -> Self {
226 Self {
227 name: meta.name,
228 namespace: meta.namespace,
229 ..Default::default()
230 }
231 }
232}
233
234#[derive(Deserialize, Serialize, Debug, Default, Clone)]
236#[serde(rename_all = "camelCase")]
237pub struct ItemMeta {
238 pub name: String,
239 pub namespace: String,
240}
241
242impl From<ObjectMeta> for ItemMeta {
243 fn from(meta: ObjectMeta) -> Self {
244 Self {
245 name: meta.name,
246 namespace: meta.namespace,
247 }
248 }
249}
250
251#[derive(Deserialize, Serialize, Debug, Default, Clone)]
253#[serde(rename_all = "camelCase")]
254pub struct UpdateItemMeta {
255 pub name: String,
256 pub namespace: String,
257 pub labels: HashMap<String, String>,
258 pub resource_version: String,
259 pub annotations: HashMap<String, String>,
260 pub owner_references: Vec<OwnerReferences>,
261 pub finalizers: Vec<String>,
262}
263
264impl From<ObjectMeta> for UpdateItemMeta {
265 fn from(meta: ObjectMeta) -> Self {
266 Self {
267 name: meta.name,
268 labels: meta.labels,
269 namespace: meta.namespace,
270 resource_version: meta.resource_version,
271 annotations: meta.annotations,
272 owner_references: meta.owner_references,
273 finalizers: meta.finalizers,
274 }
275 }
276}
277
278impl K8Meta for UpdateItemMeta {
279 fn name(&self) -> &str {
280 &self.name
281 }
282
283 fn namespace(&self) -> &str {
284 &self.namespace
285 }
286}
287
288#[derive(Deserialize, Serialize, Debug, Clone, Eq, PartialEq)]
289#[serde(rename_all = "camelCase")]
290pub struct OwnerReferences {
291 pub api_version: String,
292 #[serde(default)]
293 pub block_owner_deletion: bool,
294 pub controller: Option<bool>,
295 pub kind: String,
296 pub name: String,
297 pub uid: String,
298}
299
300impl Default for OwnerReferences {
301 fn default() -> Self {
302 Self {
303 api_version: "v1".to_owned(),
304 block_owner_deletion: false,
305 controller: None,
306 kind: "".to_owned(),
307 uid: "".to_owned(),
308 name: "".to_owned(),
309 }
310 }
311}
312
313#[derive(Debug, Clone)]
314pub enum DeleteStatus<S>
315where
316 S: Spec,
317{
318 Deleted(MetaStatus),
319 ForegroundDelete(K8Obj<S>),
320}
321
322#[derive(Deserialize, Debug, Clone)]
323#[serde(rename_all = "camelCase")]
324pub struct MetaStatus {
325 pub api_version: String,
326 pub code: Option<u16>,
327 pub details: Option<StatusDetails>,
328 pub kind: String,
329 pub message: Option<String>,
330 pub reason: Option<String>,
331 pub status: StatusEnum,
332}
333
334impl Display for MetaStatus {
335 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
336 write!(f, "{}", self.status)?;
337
338 if let Some(ref code) = self.code {
339 write!(f, " ({code})")?;
340 }
341 if let Some(ref message) = self.message {
342 write!(f, ":{message}.")?;
343 }
344
345 Ok(())
346 }
347}
348
349impl std::error::Error for MetaStatus {}
350
351#[allow(clippy::upper_case_acronyms)]
353#[derive(Deserialize, Debug, Eq, PartialEq, Clone)]
354pub enum StatusEnum {
355 #[serde(rename = "Success")]
356 SUCCESS,
357 #[serde(rename = "Failure")]
358 FAILURE,
359}
360
361impl Display for StatusEnum {
362 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
363 match self {
364 Self::SUCCESS => write!(f, "Success"),
365 Self::FAILURE => write!(f, "Failure"),
366 }
367 }
368}
369
370#[derive(Deserialize, Serialize, Debug, Clone)]
376pub struct StatusDetails {
377 pub name: String,
378 pub group: Option<String>,
379 pub kind: String,
380 pub uid: Option<String>,
381}
382
383#[derive(Deserialize, Serialize, Debug, Default, Clone)]
384#[serde(rename_all = "camelCase")]
385#[serde(bound(serialize = "S: Serialize"))]
386#[serde(bound(deserialize = "S: DeserializeOwned"))]
387pub struct K8Obj<S>
388where
389 S: Spec,
390{
391 #[serde(default = "S::api_version")]
392 pub api_version: String,
393 #[serde(default = "S::kind")]
394 pub kind: String,
395 #[serde(default)]
396 pub metadata: ObjectMeta,
397 #[serde(default)]
398 pub spec: S,
399 #[serde(flatten)]
400 pub header: S::Header,
401 #[serde(default)]
402 pub status: S::Status,
403}
404
405impl<S> K8Obj<S>
406where
407 S: Spec,
408{
409 #[allow(dead_code)]
410 pub fn new<N>(name: N, spec: S) -> Self
411 where
412 N: Into<String>,
413 {
414 Self {
415 api_version: S::api_version(),
416 kind: S::kind(),
417 metadata: ObjectMeta::named(name),
418 spec,
419 ..Default::default()
420 }
421 }
422
423 #[allow(dead_code)]
424 pub fn set_status(mut self, status: S::Status) -> Self {
425 self.status = status;
426 self
427 }
428
429 pub fn as_status_update(&self, status: S::Status) -> UpdateK8ObjStatus<S> {
430 UpdateK8ObjStatus {
431 api_version: S::api_version(),
432 kind: S::kind(),
433 metadata: self.metadata.as_update(),
434 status,
435 ..Default::default()
436 }
437 }
438}
439
440impl<S> K8Obj<S>
441where
442 S: Spec,
443{
444 pub fn as_input(&self) -> InputK8Obj<S> {
445 K8SpecObj {
446 api_version: self.api_version.clone(),
447 kind: self.kind.clone(),
448 metadata: self.metadata.as_input(),
449 spec: self.spec.clone(),
450 ..Default::default()
451 }
452 }
453
454 pub fn as_update(&self) -> K8SpecObj<S, UpdateItemMeta> {
455 K8SpecObj {
456 api_version: self.api_version.clone(),
457 kind: self.kind.clone(),
458 metadata: self.metadata.as_update(),
459 spec: self.spec.clone(),
460 ..Default::default()
461 } as K8SpecObj<S, UpdateItemMeta>
462 }
463}
464
465#[derive(Deserialize, Serialize, Debug, Default, Clone)]
467#[serde(rename_all = "camelCase")]
468#[serde(bound(serialize = "S: Serialize, M: Serialize"))]
469#[serde(bound(deserialize = "S: DeserializeOwned, M: DeserializeOwned"))]
470pub struct K8SpecObj<S, M>
471where
472 S: Spec,
473{
474 pub api_version: String,
475 pub kind: String,
476 pub metadata: M,
477 pub spec: S,
478 #[serde(flatten)]
479 pub header: S::Header,
480}
481
482impl<S, M> K8SpecObj<S, M>
483where
484 S: Spec,
485{
486 pub fn new(spec: S, metadata: M) -> Self
487 where
488 M: Default,
489 {
490 Self {
491 api_version: S::api_version(),
492 kind: S::kind(),
493 metadata,
494 spec,
495 ..Default::default()
496 }
497 }
498}
499
500pub type InputK8Obj<S> = K8SpecObj<S, InputObjectMeta>;
501#[deprecated(note = "use UpdatedK8Obj instead")]
502pub type UpdateK8Obj<S> = K8SpecObj<S, ItemMeta>;
503pub type UpdatedK8Obj<S> = K8SpecObj<S, UpdateItemMeta>;
504
505#[derive(Deserialize, Serialize, Debug, Default, Clone)]
507#[serde(rename_all = "camelCase")]
508pub struct UpdateK8ObjStatus<S>
509where
510 S: Spec,
511{
512 pub api_version: String,
513 pub kind: String,
514 pub metadata: UpdateItemMeta,
515 pub status: S::Status,
516 pub data: PhantomData<S>,
517}
518
519impl<S> UpdateK8ObjStatus<S>
520where
521 S: Spec,
522{
523 pub fn new(status: S::Status, metadata: UpdateItemMeta) -> Self {
524 Self {
525 api_version: S::api_version(),
526 kind: S::kind(),
527 metadata,
528 status,
529 ..Default::default()
530 }
531 }
532}
533
534#[allow(deprecated)]
535impl<S> From<UpdateK8Obj<S>> for InputK8Obj<S>
536where
537 S: Spec,
538{
539 fn from(update: UpdateK8Obj<S>) -> Self {
540 Self {
541 api_version: update.api_version,
542 kind: update.kind,
543 metadata: update.metadata.into(),
544 spec: update.spec,
545 ..Default::default()
546 }
547 }
548}
549
550impl From<ItemMeta> for InputObjectMeta {
551 fn from(update: ItemMeta) -> Self {
552 Self {
553 name: update.name,
554 namespace: update.namespace,
555 ..Default::default()
556 }
557 }
558}
559
560#[derive(Deserialize, Serialize, Debug, Default, Clone, Eq, PartialEq)]
562#[serde(rename_all = "camelCase", default)]
563pub struct TemplateMeta {
564 pub name: Option<String>,
565 pub creation_timestamp: Option<String>,
566 pub labels: HashMap<String, String>,
567 pub annotations: HashMap<String, String>,
568}
569
570impl LabelProvider for TemplateMeta {
571 fn set_label_map(mut self, labels: HashMap<String, String>) -> Self {
572 self.labels = labels;
573 self
574 }
575}
576
577impl TemplateMeta {
578 pub fn named<S>(name: S) -> Self
580 where
581 S: Into<String>,
582 {
583 Self {
584 name: Some(name.into()),
585 ..Default::default()
586 }
587 }
588}
589
590#[derive(Deserialize, Serialize, Debug, Default, Clone, Eq, PartialEq)]
591#[serde(rename_all = "camelCase")]
592pub struct TemplateSpec<S> {
593 pub metadata: Option<TemplateMeta>,
594 pub spec: S,
595}
596
597impl<S> TemplateSpec<S> {
598 pub fn new(spec: S) -> Self {
599 TemplateSpec {
600 metadata: None,
601 spec,
602 }
603 }
604}
605
606#[derive(Deserialize, Serialize, Debug, Clone)]
607#[serde(rename_all = "camelCase")]
608#[serde(bound(serialize = "K8Obj<S>: Serialize"))]
609#[serde(bound(deserialize = "K8Obj<S>: DeserializeOwned"))]
610pub struct K8List<S>
611where
612 S: Spec,
613{
614 pub api_version: String,
615 pub kind: String,
616 pub metadata: ListMetadata,
617 pub items: Vec<K8Obj<S>>,
618}
619
620impl<S> K8List<S>
621where
622 S: Spec,
623{
624 #[allow(dead_code)]
625 pub fn new() -> Self {
626 K8List {
627 api_version: S::api_version(),
628 items: vec![],
629 kind: S::kind(),
630 metadata: ListMetadata {
631 _continue: None,
632 resource_version: S::api_version(),
633 },
634 }
635 }
636}
637
638impl<S> Default for K8List<S>
639where
640 S: Spec,
641{
642 fn default() -> Self {
643 Self::new()
644 }
645}
646
647pub trait DeserializeWith: Sized {
648 fn deserialize_with<'de, D>(de: D) -> Result<Self, D::Error>
649 where
650 D: Deserializer<'de>;
651}
652
653#[allow(clippy::upper_case_acronyms)]
654#[derive(Serialize, Deserialize, Debug, Clone)]
655#[serde(tag = "type", content = "object")]
656#[serde(bound(serialize = "K8Obj<S>: Serialize"))]
657#[serde(bound(deserialize = "K8Obj<S>: DeserializeOwned"))]
658pub enum K8Watch<S>
659where
660 S: Spec,
661{
662 ADDED(K8Obj<S>),
663 MODIFIED(K8Obj<S>),
664 DELETED(K8Obj<S>),
665}
666
667#[derive(Deserialize, Serialize, Debug, Clone)]
668#[serde(rename_all = "camelCase")]
669pub struct ListMetadata {
670 pub _continue: Option<String>,
671 #[serde(default)]
672 pub resource_version: String,
673}
674
675#[derive(Deserialize, Serialize, Default, Debug, Eq, PartialEq, Clone)]
676#[serde(rename_all = "camelCase")]
677pub struct LabelSelector {
678 pub match_labels: HashMap<String, String>,
679}
680
681impl LabelSelector {
682 pub fn new_labels<T: Into<String>>(labels: Vec<(T, T)>) -> Self {
683 let mut match_labels = HashMap::new();
684 for (key, value) in labels {
685 match_labels.insert(key.into(), value.into());
686 }
687 LabelSelector { match_labels }
688 }
689}
690
691#[derive(Deserialize, Serialize, Default, Debug, Clone, Eq, PartialEq)]
692#[serde(rename_all = "camelCase")]
693pub struct Env {
694 pub name: String,
695 pub value: Option<String>,
696 pub value_from: Option<EnvVarSource>,
697}
698
699impl Env {
700 pub fn key_value<T: Into<String>>(name: T, value: T) -> Self {
701 Self {
702 name: name.into(),
703 value: Some(value.into()),
704 value_from: None,
705 }
706 }
707
708 pub fn key_field_ref<T: Into<String>>(name: T, field_path: T) -> Self {
709 Self {
710 name: name.into(),
711 value: None,
712 value_from: Some(EnvVarSource::FieldRef(ObjectFieldSelector {
713 field_path: field_path.into(),
714 })),
715 }
716 }
717}
718
719#[derive(Deserialize, Serialize, Debug, Clone, Eq, PartialEq)]
720#[serde(rename_all = "camelCase")]
721pub enum EnvVarSource {
722 ConfigMapKeyRef(KeySelector),
723 FieldRef(ObjectFieldSelector),
724 ResourceFieldRef(ResourceFieldSelector),
725 SecretKeyRef(KeySelector),
726}
727
728#[derive(Deserialize, Serialize, Default, Debug, Clone, Eq, PartialEq)]
729#[serde(rename_all = "camelCase")]
730pub struct KeySelector {
731 pub key: String,
732 pub name: String,
733 #[serde(default)]
734 pub optional: bool,
735}
736
737#[derive(Deserialize, Serialize, Default, Debug, Clone, Eq, PartialEq)]
738#[serde(rename_all = "camelCase")]
739pub struct ObjectFieldSelector {
740 pub field_path: String,
741}
742
743#[derive(Deserialize, Serialize, Default, Debug, Clone, Eq, PartialEq)]
744#[serde(rename_all = "camelCase")]
745pub struct ResourceFieldSelector {
746 pub container_name: Option<String>,
747 pub divisor: Option<String>,
748 pub resource: String,
749}
750
751#[cfg(test)]
752mod test {
753
754 use super::Env;
755 use super::ObjectMeta;
756
757 #[test]
758 fn test_metadata_label() {
759 let metadata =
760 ObjectMeta::default().set_labels(vec![("app".to_owned(), "test".to_owned())]);
761
762 let maps = metadata.labels;
763 assert_eq!(maps.len(), 1);
764 assert_eq!(maps.get("app").unwrap(), "test");
765 }
766
767 #[test]
768 fn test_env() {
769 let env = Env::key_value("lang", "english");
770 assert_eq!(env.name, "lang");
771 assert_eq!(env.value, Some("english".to_owned()));
772 }
773}
774
775