Skip to content

Commit

Permalink
refactor: adapt to new API of policy-evaluator
Browse files Browse the repository at this point in the history
Adapt the code to the new API exposed by latest version of
policy-evaluator.

On top of that, adding some extra unit tests.

Signed-off-by: Flavio Castelli <[email protected]>
  • Loading branch information
flavio committed Dec 13, 2023
1 parent 37c3de9 commit 09199c7
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 27 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ opentelemetry = { version = "0.21", default-features = false, features = [
opentelemetry_sdk = { version = "0.21", features = ["rt-tokio"] }
procfs = "0.16"
#policy-evaluator = { git = "https://github.com/kubewarden/policy-evaluator", tag = "v0.12.2" }
policy-evaluator = { git = "https://github.com/flavio/policy-evaluator", branch = "on-demand" }
policy-evaluator = { git = "https://github.com/kubewarden/policy-evaluator", branch = "main" }
rayon = "1.8"
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
Expand Down
95 changes: 71 additions & 24 deletions src/workers/evaluation_environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,34 +198,28 @@ impl EvaluationEnvironment {
.ok_or(EvaluationError::PolicyNotFound(policy_id.to_string()))
}

/// Perform a request validation
pub fn validate(&self, policy_id: &str, req: &EvalRequest) -> Result<AdmissionResponse> {
/// Given a policy ID, returns the settings provided by the user inside of `policies.yml`
fn get_policy_settings(&self, policy_id: &str) -> Result<PolicyEvaluationSettings> {
let settings = self
.policy_id_to_settings
.get(policy_id)
.ok_or(EvaluationError::PolicyNotFound(policy_id.to_string()))?;
.ok_or(EvaluationError::PolicyNotFound(policy_id.to_string()))?
.clone();

let mut evaluator = self.rehydrate(policy_id)?;
Ok(settings)
}

let eval_ctx =
self.policy_id_to_eval_ctx
.get(policy_id)
.ok_or(EvaluationError::InternalError(format!(
"cannot find evaluation context for policy with ID {policy_id}"
)))?;
/// Perform a request validation
pub fn validate(&self, policy_id: &str, req: &EvalRequest) -> Result<AdmissionResponse> {
let settings = self.get_policy_settings(policy_id)?;
let mut evaluator = self.rehydrate(policy_id)?;

Ok(evaluator.validate(req.req.clone(), &settings.settings, eval_ctx))
Ok(evaluator.validate(req.req.clone(), &settings.settings))
}

/// Validate the settings the user provided for the given policy
pub fn validate_settings(&self, policy_id: &str) -> Result<SettingsValidationResponse> {
let settings =
self.policy_id_to_settings
.get(policy_id)
.ok_or(EvaluationError::InternalError(format!(
"cannot find settings for policy with ID {policy_id}"
)))?;

let settings = self.get_policy_settings(policy_id)?;
let mut evaluator = self.rehydrate(policy_id)?;

Ok(evaluator.validate_settings(&settings.settings))
Expand Down Expand Up @@ -294,15 +288,17 @@ fn create_policy_evaluator_pre(

#[cfg(test)]
mod tests {
use policy_evaluator::{
admission_response::AdmissionResponse, policy_evaluator::ValidateRequest,
};
use rstest::*;
use std::collections::BTreeSet;

use super::*;
use crate::admission_review::tests::build_admission_review;
use crate::config::Policy;

/// Given to identical wasm modules, only one instance of PolicyEvaluator is going to be
/// created
#[test]
fn avoid_duplicated_instaces_of_policy_evaluator() {
fn build_evaluation_environment() -> Result<EvaluationEnvironment> {
let engine = wasmtime::Engine::default();
let policy_ids = vec!["policy_1", "policy_2"];
let module = wasmtime::Module::new(&engine, "(module (func))")
Expand Down Expand Up @@ -333,15 +329,66 @@ mod tests {
precompiled_policies.insert(policy_url, precompiled_policy.clone());
}

let evaluation_environment = EvaluationEnvironment::new(
EvaluationEnvironment::new(
&engine,
&policies,
&precompiled_policies,
None,
None,
callback_handler_tx,
)
.unwrap();
}

#[rstest]
#[case("policy_not_defined", true)]
#[case("policy_1", false)]
fn return_policy_not_found_error(#[case] policy_id: &str, #[case] expect_error: bool) {
let eval_env = build_evaluation_environment().unwrap();
let req = ValidateRequest::AdmissionRequest(
build_admission_review().request.expect("no request"),
);

let (tx, _) = tokio::sync::oneshot::channel::<Option<AdmissionResponse>>();
let eval_req = EvalRequest {
policy_id: policy_id.to_string(),
req,
resp_chan: tx,
parent_span: tracing::Span::none(),
request_origin: crate::communication::RequestOrigin::Validate,
};

if expect_error {
assert!(matches!(
eval_env.get_policy_mode(policy_id),
Err(EvaluationError::PolicyNotFound(_))
));
assert!(matches!(
eval_env.get_policy_allowed_to_mutate(policy_id),
Err(EvaluationError::PolicyNotFound(_))
));
assert!(matches!(
eval_env.get_policy_settings(policy_id),
Err(EvaluationError::PolicyNotFound(_))
));
assert!(matches!(
eval_env.validate(policy_id, &eval_req),
Err(EvaluationError::PolicyNotFound(_))
));
} else {
assert!(eval_env.get_policy_mode(policy_id).is_ok());
assert!(eval_env.get_policy_allowed_to_mutate(policy_id).is_ok());
assert!(eval_env.get_policy_settings(policy_id).is_ok());
// note: we do not test `validate` with a known policy because this would
// cause another error. The test policy we're using is just an empty Wasm
// module
}
}

/// Given to identical wasm modules, only one instance of PolicyEvaluator is going to be
/// created
#[test]
fn avoid_duplicated_instaces_of_policy_evaluator() {
let evaluation_environment = build_evaluation_environment().unwrap();

assert_eq!(
evaluation_environment
Expand Down
1 change: 1 addition & 0 deletions src/workers/policy_evaluation_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::config::PolicyMode;
/// Holds the evaluation settings of loaded Policy. These settings are taken straight from the
/// `policies.yml` file provided by the user
#[cfg_attr(test, allow(dead_code))]
#[derive(Clone)]
pub(crate) struct PolicyEvaluationSettings {
/// Whether the policy is operating in `protect` or `monitor` mode
pub(crate) policy_mode: PolicyMode,
Expand Down

0 comments on commit 09199c7

Please sign in to comment.