devops_models/models/
k8s_storage.rs1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3use crate::models::k8s::K8sMetadata;
4use crate::models::validation::{ConfigValidator, Diagnostic, Severity, YamlType};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
11#[serde(deny_unknown_fields)]
12pub struct PVCResources {
13 #[serde(default)]
14 pub requests: HashMap<String, String>,
15 #[serde(default)]
16 pub limits: HashMap<String, String>,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
20#[serde(deny_unknown_fields)]
21pub struct PVCSpec {
22 #[serde(rename = "accessModes")]
23 pub access_modes: Vec<String>,
24 #[serde(default)]
25 pub resources: Option<PVCResources>,
26 #[serde(default, rename = "storageClassName")]
27 pub storage_class_name: Option<String>,
28 #[serde(default, rename = "volumeMode")]
29 pub volume_mode: Option<String>,
30 #[serde(default, rename = "volumeName")]
31 pub volume_name: Option<String>,
32 #[serde(default)]
33 pub selector: Option<serde_json::Value>,
34 #[serde(default, rename = "dataSource")]
35 pub data_source: Option<serde_json::Value>,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39#[serde(deny_unknown_fields)]
40pub struct K8sPVC {
41 #[serde(rename = "apiVersion")]
42 pub api_version: String,
43 pub kind: String,
44 pub metadata: K8sMetadata,
45 pub spec: PVCSpec,
46}
47
48const VALID_ACCESS_MODES: &[&str] = &[
49 "ReadWriteOnce", "ReadOnlyMany", "ReadWriteMany", "ReadWriteOncePod",
50];
51
52impl ConfigValidator for K8sPVC {
53 fn yaml_type(&self) -> YamlType { YamlType::K8sPVC }
54
55 fn validate_structure(&self) -> Vec<Diagnostic> {
56 let mut diags = Vec::new();
57 if self.spec.access_modes.is_empty() {
58 diags.push(Diagnostic {
59 severity: Severity::Error,
60 message: "accessModes is required and cannot be empty".into(),
61 path: Some("spec > accessModes".into()),
62 });
63 }
64 for mode in &self.spec.access_modes {
65 if !VALID_ACCESS_MODES.contains(&mode.as_str()) {
66 diags.push(Diagnostic {
67 severity: Severity::Error,
68 message: format!("Invalid accessMode '{}' — expected one of: {}", mode, VALID_ACCESS_MODES.join(", ")),
69 path: Some("spec > accessModes".into()),
70 });
71 }
72 }
73 diags
74 }
75
76 fn validate_semantics(&self) -> Vec<Diagnostic> {
77 let mut diags = Vec::new();
78 if self.spec.resources.is_none() {
79 diags.push(Diagnostic {
80 severity: Severity::Warning,
81 message: "No resources.requests.storage — PVC size is undefined".into(),
82 path: Some("spec > resources".into()),
83 });
84 } else if let Some(res) = &self.spec.resources
85 && !res.requests.contains_key("storage") {
86 diags.push(Diagnostic {
87 severity: Severity::Warning,
88 message: "No resources.requests.storage — PVC size is undefined".into(),
89 path: Some("spec > resources > requests".into()),
90 });
91 }
92 if self.spec.storage_class_name.is_none() {
93 diags.push(Diagnostic {
94 severity: Severity::Info,
95 message: "No storageClassName — cluster default StorageClass will be used".into(),
96 path: Some("spec > storageClassName".into()),
97 });
98 }
99 diags
100 }
101}