demo_campom10/
lib.rs

1use lazy_static::lazy_static;
2
3use guest::prelude::*;
4use kubewarden_policy_sdk::wapc_guest as guest;
5
6use k8s_openapi::api::core::v1 as apicore;
7use k8s_openapi::Resource;
8
9extern crate kubewarden_policy_sdk as kubewarden;
10use kubewarden::{logging, protocol_version_guest, request::ValidationRequest, validate_settings};
11
12mod settings;
13use settings::Settings;
14
15use slog::{info, o, warn, Logger};
16
17lazy_static! {
18    static ref LOG_DRAIN: Logger = Logger::root(
19        logging::KubewardenDrain::new(),
20        o!("policy" => "sample-policy")
21    );
22}
23
24#[no_mangle]
25pub extern "C" fn wapc_init() {
26    register_function("validate", validate);
27    register_function("validate_settings", validate_settings::<Settings>);
28    register_function("protocol_version", protocol_version_guest);
29}
30
31fn validate(payload: &[u8]) -> CallResult {
32    let validation_request: ValidationRequest<Settings> = ValidationRequest::new(payload)?;
33
34    info!(LOG_DRAIN, "starting validation");
35    if validation_request.request.kind.kind != apicore::Pod::KIND {
36        warn!(LOG_DRAIN, "Policy validates Pods only. Accepting resource"; "kind" => &validation_request.request.kind.kind);
37        return kubewarden::accept_request();
38    }
39
40    match serde_json::from_value::<apicore::Pod>(validation_request.request.object) {
41        // NOTE 1
42        Ok(mut pod) => {
43            let pod_name = pod.metadata.name.clone().unwrap_or_default();
44            if validation_request
45                .settings
46                .invalid_names
47                .contains(&pod_name)
48            {
49                kubewarden::reject_request(
50                    Some(format!("pod name {:?} is not accepted", pod_name)),
51                    None,
52                    None,
53                    None,
54                )
55            } else {
56                // NOTE 2
57                let mut new_annotations = pod.metadata.annotations.clone().unwrap_or_default();
58                new_annotations.insert(
59                    String::from("kubewarden.policy.demo/inspected"),
60                    String::from("true"),
61                );
62                pod.metadata.annotations = Some(new_annotations);
63
64                // NOTE 3
65                let mutated_object = serde_json::to_value(pod)?;
66                kubewarden::mutate_request(mutated_object)
67            }
68        }
69        Err(_) => {
70            // We were forwarded a request we cannot unmarshal or
71            // understand, just accept it
72            kubewarden::accept_request()
73        }
74    }
75}
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    use kubewarden_policy_sdk::test::Testcase;
81    use std::collections::HashSet;
82
83    #[test]
84    fn accept_pod_with_valid_name() -> Result<(), ()> {
85        let mut invalid_names = HashSet::new();
86        invalid_names.insert(String::from("bad_name1"));
87        let settings = Settings { invalid_names };
88    
89        let request_file = "test_data/pod_creation.json";
90        let tc = Testcase {
91            name: String::from("Pod creation with valid name"),
92            fixture_file: String::from(request_file),
93            expected_validation_result: true,
94            settings,
95        };
96    
97        let res = tc.eval(validate).unwrap();
98        // NOTE 1
99        assert!(
100            res.mutated_object.is_some(),
101            "Expected accepted object to be mutated",
102        );
103    
104        // NOTE 2
105        let final_pod =
106            serde_json::from_value::<apicore::Pod>(res.mutated_object.unwrap()).unwrap();
107        let final_annotations = final_pod.metadata.annotations.unwrap();
108        assert_eq!(
109            final_annotations.get_key_value("kubewarden.policy.demo/inspected"),
110            Some((
111                &String::from("kubewarden.policy.demo/inspected"),
112                &String::from("true")
113            )),
114        );
115    
116        Ok(())
117    }
118
119    #[test]
120    fn reject_pod_with_invalid_name() -> Result<(), ()> {
121        let mut invalid_names = HashSet::new();
122        invalid_names.insert(String::from("nginx"));
123        let settings = Settings { invalid_names };
124
125        let request_file = "test_data/pod_creation.json";
126        let tc = Testcase {
127            name: String::from("Pod creation with invalid name"),
128            fixture_file: String::from(request_file),
129            expected_validation_result: false,
130            settings,
131        };
132
133        let res = tc.eval(validate).unwrap();
134        assert!(
135            res.mutated_object.is_none(),
136            "Something mutated with test case: {}",
137            tc.name,
138        );
139
140        Ok(())
141    }
142
143    #[test]
144    fn accept_request_with_non_pod_resource() -> Result<(), ()> {
145        let mut invalid_names = HashSet::new();
146        invalid_names.insert(String::from("prod"));
147        let settings = Settings { invalid_names };
148
149        let request_file = "test_data/ingress_creation.json";
150        let tc = Testcase {
151            name: String::from("Ingress creation"),
152            fixture_file: String::from(request_file),
153            expected_validation_result: true,
154            settings,
155        };
156
157        let res = tc.eval(validate).unwrap();
158        assert!(
159            res.mutated_object.is_none(),
160            "Something mutated with test case: {}",
161            tc.name,
162        );
163
164        Ok(())
165    }
166}