|
Este documento ha sido traducido utilizando tecnología de traducción automática. Si bien nos esforzamos por proporcionar traducciones precisas, no ofrecemos garantías sobre la integridad, precisión o confiabilidad del contenido traducido. En caso de discrepancia, la versión original en inglés prevalecerá y constituirá el texto autorizado. |
|
Esta es documentación inédita para Admission Controller 1.34-dev. |
Escribiendo lógica de validación
Es hora de escribir el código de validación real.
Está definido en el archivo src/lib.rs.
En este archivo puedes encontrar una función llamada validate.
Esta es la función proporcionada como andamiaje:
fn validate(payload: &[u8]) -> CallResult {
let validation_request: ValidationRequest<Settings> = ValidationRequest::new(payload)?; (1)
info!(LOG_DRAIN, "starting validation");
if validation_request.request.kind.kind != apicore::Pod::KIND {
warn!(LOG_DRAIN, "Policy validates Pods only. Accepting resource"; "kind" => &validation_request.request.kind.kind);
return kubewarden::accept_request();
}
// TODO: you can unmarshal any Kubernetes API type you are interested in (2)
match serde_json::from_value::<apicore::Pod>(validation_request.request.object) {
Ok(pod) => {
// TODO: your logic goes here (3)
if pod.metadata.name == Some("invalid-pod-name".to_string()) {
let pod_name = pod.metadata.name.unwrap();
info!(
LOG_DRAIN,
"rejecting pod";
"pod_name" => &pod_name
);
kubewarden::reject_request(
Some(format!("pod name {} is not accepted", &pod_name)),
None,
None,
None,
)
} else {
info!(LOG_DRAIN, "accepting resource");
kubewarden::accept_request()
}
}
Err(_) => {
// TODO: handle as you wish
// We were forwarded a request we cannot unmarshal or
// understand, just accept it
warn!(LOG_DRAIN, "cannot unmarshal resource: this policy does not know how to evaluate this resource; accept it");
kubewarden::accept_request() (4)
}
}
}
Revisando el listado de código:
-
En la línea marcada ➀. Analiza el
payloadentrante en un objetoValidationRequest<Setting>. Esto llena automáticamente la instanciaSettingsdentro delValidationRequestcon los parámetros proporcionados por el usuario. -
En la línea marcada ➁. Convierte el objeto JSON en bruto de Kubernetes incrustado en la solicitud en una instancia del struct Pod
-
En la línea marcada ➂. La solicitud tiene un objeto Pod, el código solo aprueba las solicitudes que no tienen
metadata.nameigual al valor codificadoinvalid-pod-name. -
En la línea marcada ➃. La solicitud no contiene un objeto Pod, por lo tanto, la directiva acepta la solicitud.
Como puedes ver, el código ya está realizando una validación que se asemeja a la que deseas implementar. Solo tienes que eliminar el valor codificado y usar los valores proporcionados por el usuario a través de la configuración de la directiva.
Puedes hacerlo reemplazando la función de andamiaje validate en src/lib.rs con esta:
fn validate(payload: &[u8]) -> CallResult {
let validation_request: ValidationRequest<Settings> = ValidationRequest::new(payload)?;
info!(LOG_DRAIN, "starting validation");
if validation_request.request.kind.kind != apicore::Pod::KIND {
warn!(LOG_DRAIN, "Policy validates Pods only. Accepting resource"; "kind" => &validation_request.request.kind.kind);
return kubewarden::accept_request();
}
match serde_json::from_value::<apicore::Pod>(validation_request.request.object) {
Ok(pod) => {
let pod_name = pod.metadata.name.unwrap_or_default();
if validation_request
.settings
.invalid_names
.contains(&pod_name)
{
kubewarden::reject_request(
Some(format!("pod name {:?} is not accepted", pod_name)),
None,
None,
None,
)
} else {
kubewarden::accept_request()
}
}
Err(_) => {
// We were forwarded a request we cannot unmarshal or
// understand, just accept it
kubewarden::accept_request()
}
}
}
Pruebas unitarias
Finalmente, puedes crear pruebas unitarias para comprobar que el código de validación funciona como se espera.
El archivo lib.rs ya tiene pruebas definidas al final del archivo, y como puedes ver, el SDK de Rust de Admission Controller también proporciona ayudantes para pruebas.
Además, el proyecto de andamiaje ya incluye fixtures de prueba por defecto en el directorio test_data. Vas a utilizar estas solicitudes de admisión grabadas para escribir tus pruebas unitarias.
Cambia el contenido de la sección de pruebas al final de src/lib.rs para que se vea así:
#[cfg(test)]
mod tests {
use super::*;
use kubewarden_policy_sdk::test::Testcase;
use std::collections::HashSet;
#[test]
fn accept_pod_with_valid_name() -> Result<(), ()> {
let mut invalid_names = HashSet::new();
invalid_names.insert(String::from("bad_name1"));
let settings = Settings { invalid_names };
let request_file = "test_data/pod_creation.json";
let tc = Testcase {
name: String::from("Pod creation with valid name"),
fixture_file: String::from(request_file),
expected_validation_result: true,
settings,
};
let res = tc.eval(validate).unwrap();
assert!(
res.mutated_object.is_none(),
"Something mutated with test case: {}",
tc.name,
);
Ok(())
}
#[test]
fn reject_pod_with_invalid_name() -> Result<(), ()> {
let mut invalid_names = HashSet::new();
invalid_names.insert(String::from("nginx"));
let settings = Settings { invalid_names };
let request_file = "test_data/pod_creation.json";
let tc = Testcase {
name: String::from("Pod creation with invalid name"),
fixture_file: String::from(request_file),
expected_validation_result: false,
settings,
};
let res = tc.eval(validate).unwrap();
assert!(
res.mutated_object.is_none(),
"Something mutated with test case: {}",
tc.name,
);
Ok(())
}
#[test]
fn accept_request_with_non_pod_resource() -> Result<(), ()> {
let mut invalid_names = HashSet::new();
invalid_names.insert(String::from("prod"));
let settings = Settings { invalid_names };
let request_file = "test_data/ingress_creation.json";
let tc = Testcase {
name: String::from("Ingress creation"),
fixture_file: String::from(request_file),
expected_validation_result: true,
settings,
};
let res = tc.eval(validate).unwrap();
assert!(
res.mutated_object.is_none(),
"Something mutated with test case: {}",
tc.name,
);
Ok(())
}
}
Ahora tienes tres pruebas unitarias definidas en lib.rs:
-
accept_pod_with_valid_name: acepta un Pod con un nombre válido -
reject_pod_with_invalid_name: rechaza un Pod con un nombre inválido -
accept_request_with_non_pod_resource: acepta solicitudes que no tienen unPodcomo objeto
Puedes ejecutar las pruebas unitarias de nuevo:
$ cargo test
Compiling demo v0.1.0 (/home/flavio/hacking/kubernetes/kubewarden/demo)
Finished test [unoptimized + debuginfo] target(s) in 3.45s
Running target/debug/deps/demo-24670dd6a538fd72
running 5 tests
test settings::tests::accept_settings_with_a_list_of_invalid_names ... ok
test settings::tests::reject_settings_without_a_list_of_invalid_names ... ok
test tests::accept_request_with_non_pod_resource ... ok
test tests::accept_pod_with_valid_name ... ok
test tests::reject_pod_with_invalid_name ... ok
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Eso es todo lo que se requiere si necesitas escribir una directiva de validación simple.