|
本文档采用自动化机器翻译技术翻译。 尽管我们力求提供准确的译文,但不对翻译内容的完整性、准确性或可靠性作出任何保证。 若出现任何内容不一致情况,请以原始 英文 版本为准,且原始英文版本为权威文本。 |
|
这是尚未发布的文档。 Admission Controller 1.34-dev. |
编写验证逻辑
是时候编写实际的验证代码了。
它定义在 src/lib.rs 文件中。
在这个文件中,您可以找到一个名为 validate 的函数。
这是脚手架提供的函数:
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)
}
}
}
逐步分析代码清单:
-
在标记为 ➀ 的行中。将传入的
payload解析为ValidationRequest<Setting>对象。 这会自动用用户提供的参数填充ValidationRequest中的Settings实例。 -
在标记为 ➁ 的行中。将嵌入请求中的 Kubernetes 原始 JSON 对象转换为 Pod 结构 的实例。
-
在标记为 ➂ 的行中。请求中有一个 Pod 对象,代码只批准其中
metadata.name不等于硬编码值invalid-pod-name的请求。 -
在标记为 ➃ 的行中。请求中不包含 Pod 对象,因此策略接受该请求。
如您所见,代码已经在执行类似于您想要实现的验证。 您只需去除硬编码值,并使用用户通过策略设置提供的值。
您可以通过在 src/lib.rs 中替换脚手架 validate 函数来实现这一点,使用这个函数:
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()
}
}
}
单元测试
最后,您可以创建单元测试来检查验证代码是否按预期工作。
lib.rs 文件底部已经定义了测试,正如您所看到的,Admission Controller 的 Rust SDK 也提供了测试助手。
此外,脚手架项目已经在 test_data 目录中提供了默认的 测试夹具。您将使用这些记录的准入请求来编写单元测试。
将 src/lib.rs 末尾的测试部分内容更改为如下所示:
#[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(())
}
}
您现在在 lib.rs 中定义了三个单元测试:
-
accept_pod_with_valid_name:接受具有有效名称的 Pod -
reject_pod_with_invalid_name:拒绝具有无效名称的 Pod -
accept_request_with_non_pod_resource:接受没有Pod作为对象的请求
您可以再次运行单元测试:
$ 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
如果您需要编写一个简单的验证策略,这就是所需的全部。