k8_obj_metadata/
metadata.rs

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