Skip to main content

devops_validate/schema/
bundled.rs

1//! Bundled fallback schemas for offline mode
2//!
3//! These minimal schemas are embedded in the binary and used when:
4//! - No network connectivity
5//! - Remote schema fetch fails
6//! - User prefers offline operation
7//!
8//! They provide basic structural validation but may not include all fields
9//! from the official schemas.
10
11use serde_json::Value;
12
13/// Minimal K8s Deployment schema
14const K8S_DEPLOYMENT: &str = r#"
15{
16  "$schema": "http://json-schema.org/draft-07/schema#",
17  "type": "object",
18  "required": ["apiVersion", "kind", "metadata", "spec"],
19  "properties": {
20    "apiVersion": { "type": "string", "const": "apps/v1" },
21    "kind": { "type": "string", "const": "Deployment" },
22    "metadata": {
23      "type": "object",
24      "required": ["name"],
25      "properties": {
26        "name": { "type": "string" },
27        "namespace": { "type": "string" },
28        "labels": { "type": "object", "additionalProperties": { "type": "string" } },
29        "annotations": { "type": "object", "additionalProperties": { "type": "string" } }
30      }
31    },
32    "spec": {
33      "type": "object",
34      "required": ["selector", "template"],
35      "properties": {
36        "replicas": { "type": "integer", "minimum": 0 },
37        "selector": {
38          "type": "object",
39          "required": ["matchLabels"],
40          "properties": {
41            "matchLabels": { "type": "object", "additionalProperties": { "type": "string" } }
42          }
43        },
44        "template": {
45          "type": "object",
46          "required": ["metadata", "spec"],
47          "properties": {
48            "metadata": {
49              "type": "object",
50              "properties": {
51                "labels": { "type": "object", "additionalProperties": { "type": "string" } }
52              }
53            },
54            "spec": {
55              "type": "object",
56              "required": ["containers"],
57              "properties": {
58                "containers": {
59                  "type": "array",
60                  "items": {
61                    "type": "object",
62                    "required": ["name", "image"],
63                    "properties": {
64                      "name": { "type": "string" },
65                      "image": { "type": "string" },
66                      "ports": {
67                        "type": "array",
68                        "items": {
69                          "type": "object",
70                          "properties": {
71                            "containerPort": { "type": "integer" },
72                            "protocol": { "type": "string", "enum": ["TCP", "UDP"] }
73                          }
74                        }
75                      },
76                      "resources": {
77                        "type": "object",
78                        "properties": {
79                          "limits": { "type": "object" },
80                          "requests": { "type": "object" }
81                        }
82                      },
83                      "env": {
84                        "type": "array",
85                        "items": {
86                          "type": "object",
87                          "required": ["name"],
88                          "properties": {
89                            "name": { "type": "string" },
90                            "value": { "type": "string" }
91                          }
92                        }
93                      },
94                      "livenessProbe": { "type": "object" },
95                      "readinessProbe": { "type": "object" }
96                    }
97                  }
98                },
99                "initContainers": { "type": "array" },
100                "volumes": { "type": "array" },
101                "restartPolicy": { "type": "string", "enum": ["Always", "OnFailure", "Never"] }
102              }
103            }
104          }
105        },
106        "strategy": {
107          "type": "object",
108          "properties": {
109            "type": { "type": "string", "enum": ["RollingUpdate", "Recreate"] }
110          }
111        }
112      }
113    }
114  }
115}
116"#;
117
118/// Minimal K8s Service schema
119const K8S_SERVICE: &str = r#"
120{
121  "$schema": "http://json-schema.org/draft-07/schema#",
122  "type": "object",
123  "required": ["apiVersion", "kind", "metadata", "spec"],
124  "properties": {
125    "apiVersion": { "type": "string", "const": "v1" },
126    "kind": { "type": "string", "const": "Service" },
127    "metadata": {
128      "type": "object",
129      "required": ["name"],
130      "properties": {
131        "name": { "type": "string" },
132        "namespace": { "type": "string" }
133      }
134    },
135    "spec": {
136      "type": "object",
137      "required": ["ports"],
138      "properties": {
139        "type": { "type": "string", "enum": ["ClusterIP", "NodePort", "LoadBalancer", "ExternalName"] },
140        "selector": { "type": "object", "additionalProperties": { "type": "string" } },
141        "ports": {
142          "type": "array",
143          "items": {
144            "type": "object",
145            "required": ["port"],
146            "properties": {
147              "name": { "type": "string" },
148              "port": { "type": "integer", "minimum": 1, "maximum": 65535 },
149              "targetPort": { "oneOf": [{ "type": "integer" }, { "type": "string" }] },
150              "protocol": { "type": "string", "enum": ["TCP", "UDP", "SCTP"] },
151              "nodePort": { "type": "integer", "minimum": 30000, "maximum": 32767 }
152            }
153          }
154        },
155        "clusterIP": { "type": "string" },
156        "externalName": { "type": "string" }
157      }
158    }
159  }
160}
161"#;
162
163/// Minimal K8s ConfigMap schema
164const K8S_CONFIGMAP: &str = r#"
165{
166  "$schema": "http://json-schema.org/draft-07/schema#",
167  "type": "object",
168  "required": ["apiVersion", "kind", "metadata"],
169  "properties": {
170    "apiVersion": { "type": "string", "const": "v1" },
171    "kind": { "type": "string", "const": "ConfigMap" },
172    "metadata": {
173      "type": "object",
174      "required": ["name"],
175      "properties": {
176        "name": { "type": "string" },
177        "namespace": { "type": "string" }
178      }
179    },
180    "data": { "type": "object", "additionalProperties": { "type": "string" } },
181    "binaryData": { "type": "object", "additionalProperties": { "type": "string" } },
182    "immutable": { "type": "boolean" }
183  }
184}
185"#;
186
187/// Minimal K8s Secret schema
188const K8S_SECRET: &str = r#"
189{
190  "$schema": "http://json-schema.org/draft-07/schema#",
191  "type": "object",
192  "required": ["apiVersion", "kind", "metadata"],
193  "properties": {
194    "apiVersion": { "type": "string", "const": "v1" },
195    "kind": { "type": "string", "const": "Secret" },
196    "metadata": {
197      "type": "object",
198      "required": ["name"],
199      "properties": {
200        "name": { "type": "string" },
201        "namespace": { "type": "string" }
202      }
203    },
204    "type": { "type": "string" },
205    "data": { "type": "object", "additionalProperties": { "type": "string" } },
206    "stringData": { "type": "object", "additionalProperties": { "type": "string" } },
207    "immutable": { "type": "boolean" }
208  }
209}
210"#;
211
212/// Minimal GitLab CI schema
213const GITLAB_CI: &str = r#"
214{
215  "$schema": "http://json-schema.org/draft-07/schema#",
216  "type": "object",
217  "properties": {
218    "stages": {
219      "type": "array",
220      "items": { "type": "string" }
221    },
222    "variables": { "type": "object" },
223    "default": { "type": "object" },
224    "workflow": { "type": "object" }
225  },
226  "additionalProperties": {
227    "oneOf": [
228      { "type": "object" },
229      { "type": "array" }
230    ]
231  },
232  "patternProperties": {
233    "^[a-zA-Z_][a-zA-Z0-9_-]*$": {
234      "type": "object",
235      "properties": {
236        "stage": { "type": "string" },
237        "script": {
238          "oneOf": [
239            { "type": "string" },
240            { "type": "array", "items": { "type": "string" } }
241          ]
242        },
243        "image": { "type": "string" },
244        "only": { "type": "array" },
245        "except": { "type": "array" },
246        "rules": { "type": "array" },
247        "extends": { "type": "string" }
248      }
249    }
250  }
251}
252"#;
253
254/// Minimal Docker Compose schema
255const DOCKER_COMPOSE: &str = r#"
256{
257  "$schema": "http://json-schema.org/draft-07/schema#",
258  "type": "object",
259  "required": ["services"],
260  "properties": {
261    "version": { "type": "string" },
262    "services": {
263      "type": "object",
264      "additionalProperties": {
265        "type": "object",
266        "properties": {
267          "image": { "type": "string" },
268          "build": {
269            "oneOf": [
270              { "type": "string" },
271              { "type": "object" }
272            ]
273          },
274          "ports": {
275            "type": "array",
276            "items": {
277              "oneOf": [
278                { "type": "integer" },
279                { "type": "string" }
280              ]
281            }
282          },
283          "environment": {
284            "oneOf": [
285              { "type": "array" },
286              { "type": "object" }
287            ]
288          },
289          "volumes": { "type": "array" },
290          "depends_on": {
291            "oneOf": [
292              { "type": "array" },
293              { "type": "object" }
294            ]
295          },
296          "networks": { "type": "array" },
297          "restart": { "type": "string" }
298        }
299      }
300    },
301    "volumes": { "type": "object" },
302    "networks": { "type": "object" }
303  }
304}
305"#;
306
307/// Minimal GitHub Actions schema
308const GITHUB_ACTIONS: &str = r#"
309{
310  "$schema": "http://json-schema.org/draft-07/schema#",
311  "type": "object",
312  "required": ["on", "jobs"],
313  "properties": {
314    "name": { "type": "string" },
315    "on": {
316      "oneOf": [
317        { "type": "string" },
318        { "type": "array" },
319        { "type": "object" }
320      ]
321    },
322    "env": { "type": "object" },
323    "defaults": { "type": "object" },
324    "concurrency": { "type": "object" },
325    "jobs": {
326      "type": "object",
327      "additionalProperties": {
328        "type": "object",
329        "required": ["runs-on"],
330        "properties": {
331          "runs-on": {
332            "oneOf": [
333              { "type": "string" },
334              { "type": "array" }
335            ]
336          },
337          "needs": {
338            "oneOf": [
339              { "type": "string" },
340              { "type": "array" }
341            ]
342          },
343          "if": { "type": "string" },
344          "steps": {
345            "type": "array",
346            "items": {
347              "type": "object",
348              "properties": {
349                "name": { "type": "string" },
350                "run": { "type": "string" },
351                "uses": { "type": "string" },
352                "with": { "type": "object" },
353                "env": { "type": "object" },
354                "if": { "type": "string" }
355              }
356            }
357          },
358          "env": { "type": "object" },
359          "outputs": { "type": "object" },
360          "timeout-minutes": { "type": "integer" }
361        }
362      }
363    }
364  }
365}
366"#;
367
368/// Schema lookup table
369const BUNDLED_SCHEMAS: &[(&str, &str)] = &[
370    ("k8s/deployment", K8S_DEPLOYMENT),
371    ("k8s/service", K8S_SERVICE),
372    ("k8s/configmap", K8S_CONFIGMAP),
373    ("k8s/secret", K8S_SECRET),
374    ("gitlab-ci", GITLAB_CI),
375    ("docker-compose", DOCKER_COMPOSE),
376    ("github-actions", GITHUB_ACTIONS),
377];
378
379/// Get a bundled schema by type
380pub fn get_bundled_schema(schema_type: &str) -> Option<Value> {
381    for (key, schema_str) in BUNDLED_SCHEMAS {
382        if *key == schema_type {
383            return serde_json::from_str(schema_str).ok();
384        }
385    }
386    None
387}
388
389/// List all bundled schema types
390pub fn list_bundled_schemas() -> Vec<&'static str> {
391    BUNDLED_SCHEMAS.iter().map(|(key, _)| *key).collect()
392}
393
394#[cfg(test)]
395mod tests {
396    use super::*;
397
398    #[test]
399    fn test_get_bundled_schema_k8s_deployment() {
400        let schema = get_bundled_schema("k8s/deployment");
401        assert!(schema.is_some());
402        let schema = schema.unwrap();
403        assert_eq!(schema["properties"]["kind"]["const"], "Deployment");
404    }
405
406    #[test]
407    fn test_get_bundled_schema_gitlab_ci() {
408        let schema = get_bundled_schema("gitlab-ci");
409        assert!(schema.is_some());
410        let schema = schema.unwrap();
411        assert!(schema["properties"]["stages"].is_object());
412    }
413
414    #[test]
415    fn test_get_bundled_schema_unknown() {
416        let schema = get_bundled_schema("unknown/type");
417        assert!(schema.is_none());
418    }
419
420    #[test]
421    fn test_list_bundled_schemas() {
422        let schemas = list_bundled_schemas();
423        assert!(schemas.contains(&"k8s/deployment"));
424        assert!(schemas.contains(&"gitlab-ci"));
425        assert_eq!(schemas.len(), 7);
426    }
427
428    #[test]
429    fn test_all_schemas_valid_json() {
430        for (key, schema_str) in BUNDLED_SCHEMAS {
431            let result: Result<Value, _> = serde_json::from_str(schema_str);
432            assert!(result.is_ok(), "Schema {} is not valid JSON", key);
433        }
434    }
435}