container_device_interface/internal/validation/k8s/
objectmeta.rs

1use anyhow::{Error, Result};
2use std::collections::BTreeMap;
3
4const TOTAL_ANNOTATION_SIZE_LIMIT: usize = 256 * 1024; // 256 kB
5
6use super::validation::is_qualified_name;
7
8pub fn validate_annotations(annotations: &BTreeMap<String, String>, path: &str) -> Result<()> {
9    let mut errs = Vec::new();
10
11    for k in annotations.keys() {
12        for msg in is_qualified_name(&k.to_lowercase()) {
13            errs.push(format!("{}.{} is invalid: {}", path, k, msg));
14        }
15    }
16
17    if let Err(err) = validate_annotations_size(annotations) {
18        errs.push(format!("{} is too long: {}", path, err));
19    }
20
21    if errs.is_empty() {
22        Ok(())
23    } else {
24        Err(Error::msg(errs.join(", ")))
25    }
26}
27
28fn validate_annotations_size(annotations: &BTreeMap<String, String>) -> Result<()> {
29    let total_size: usize = annotations.iter().map(|(k, v)| k.len() + v.len()).sum();
30
31    if total_size > TOTAL_ANNOTATION_SIZE_LIMIT {
32        Err(Error::msg(format!(
33            "annotations size {} is larger than limit {}",
34            total_size, TOTAL_ANNOTATION_SIZE_LIMIT
35        )))
36    } else {
37        Ok(())
38    }
39}
40
41#[cfg(test)]
42mod tests {
43    use super::super::super::validate::validate_spec_annotations;
44    use super::validate_annotations;
45
46    use std::collections::BTreeMap;
47
48    #[test]
49    fn test_validate_annotations() {
50        let mut annotations = BTreeMap::new();
51        annotations.insert(
52            "cdi.k8s.io/vfio17".to_string(),
53            "nvidia.com/gpu=0".to_string(),
54        );
55        annotations.insert(
56            "cdi.k8s.io/vfio18".to_string(),
57            "nvidia.com/gpu=1".to_string(),
58        );
59        annotations.insert(
60            "cdi.k8s.io/vfio19".to_string(),
61            "nvidia.com/gpu=all".to_string(),
62        );
63        let path = "test.annotations";
64
65        assert!(validate_annotations(&annotations, path).is_ok());
66
67        let mut large_annotations = BTreeMap::new();
68        let long_value = "CDI".repeat(super::TOTAL_ANNOTATION_SIZE_LIMIT + 1);
69        large_annotations.insert("CDIKEY".to_string(), long_value);
70        assert!(validate_annotations(&large_annotations, path).is_err());
71
72        let mut invalid_annotations = BTreeMap::new();
73        invalid_annotations.insert(
74            "inv$$alid_CDIKEY".to_string(),
75            "inv$$alid_CDIVAL".to_string(),
76        );
77        assert!(validate_annotations(&invalid_annotations, path).is_err());
78    }
79
80    #[test]
81    fn test_validate_spec_annotations() {
82        let mut annotations = BTreeMap::new();
83        annotations.insert(
84            "cdi.k8s.io/vfio17".to_string(),
85            "nvidia.com/gpu=0".to_string(),
86        );
87        annotations.insert(
88            "cdi.k8s.io/vfio18".to_string(),
89            "nvidia.com/gpu=1".to_string(),
90        );
91        annotations.insert(
92            "cdi.k8s.io/vfio19".to_string(),
93            "nvidia.com/gpu=all".to_string(),
94        );
95
96        assert!(validate_spec_annotations("", &annotations).is_ok());
97        assert!(validate_spec_annotations("CDITEST", &annotations).is_ok());
98
99        let mut large_annotations = BTreeMap::new();
100        let long_value = "CDI".repeat(super::TOTAL_ANNOTATION_SIZE_LIMIT + 1);
101        large_annotations.insert("CDIKEY".to_string(), long_value);
102        assert!(validate_spec_annotations("", &large_annotations).is_err());
103
104        let mut invalid_annotations = BTreeMap::new();
105        invalid_annotations.insert(
106            "inva$$lid_CDIKEY".to_string(),
107            "inval$$id_CDIVAL".to_string(),
108        );
109        assert!(validate_spec_annotations("CDITEST", &invalid_annotations).is_err());
110    }
111}