From cf4d9440d993b1380adc86325e4ba85a74984d0f Mon Sep 17 00:00:00 2001 From: Eguzki Astiz Lezaun Date: Fri, 8 Nov 2024 10:52:07 +0100 Subject: [PATCH 1/6] remove unused conditions Signed-off-by: Eguzki Astiz Lezaun --- src/configuration.rs | 691 ++------------------------------ src/configuration/action.rs | 28 +- src/configuration/action_set.rs | 15 +- src/data/mod.rs | 2 - src/operation_dispatcher.rs | 1 - tests/multi.rs | 39 +- 6 files changed, 61 insertions(+), 715 deletions(-) diff --git a/src/configuration.rs b/src/configuration.rs index 6605f9a..cefee85 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -7,14 +7,11 @@ use std::sync::Arc; use crate::configuration::action_set::ActionSet; use crate::configuration::action_set_index::ActionSetIndex; use crate::data; +use crate::data::Predicate; use crate::data::PropertyPath; -use crate::data::{AttributeValue, Predicate}; use crate::service::GrpcService; use cel_interpreter::functions::duration; -use cel_interpreter::objects::ValueType; -use cel_interpreter::{Context, Expression, Value}; -use cel_parser::{Atom, RelationOp}; -use log::debug; +use cel_interpreter::Value; use serde::de::{Error, Visitor}; use serde::{Deserialize, Deserializer}; use std::time::Duration; @@ -105,341 +102,6 @@ pub struct DataItem { pub item: DataType, } -#[derive(Deserialize, PartialEq, Debug, Clone)] -pub enum WhenConditionOperator { - #[serde(rename = "eq")] - Equal, - #[serde(rename = "neq")] - NotEqual, - #[serde(rename = "startswith")] - StartsWith, - #[serde(rename = "endswith")] - EndsWith, - #[serde(rename = "matches")] - Matches, -} - -#[derive(Deserialize, Debug, Clone)] -#[serde(rename_all = "camelCase")] -pub struct PatternExpression { - pub selector: String, - pub operator: WhenConditionOperator, - pub value: String, - - #[serde(skip_deserializing)] - path: OnceCell, - #[serde(skip_deserializing)] - compiled: OnceCell, -} - -impl PatternExpression { - pub fn compile(&self) -> Result<(), String> { - self.path - .set(self.selector.as_str().into()) - .map_err(|_| "Duh!")?; - self.compiled - .set(self.try_into()?) - .map_err(|_| "Ooops".to_string()) - } - pub fn path(&self) -> &PropertyPath { - self.path - .get() - .expect("PatternExpression wasn't previously compiled!") - } - - pub fn eval(&self, raw_attribute: Vec) -> Result { - let cel_type = &self.compiled.get().unwrap().cel_type; - let value = match cel_type { - ValueType::String => Value::String(Arc::new(AttributeValue::parse(raw_attribute)?)), - ValueType::Int => Value::Int(AttributeValue::parse(raw_attribute)?), - ValueType::UInt => Value::UInt(AttributeValue::parse(raw_attribute)?), - ValueType::Float => Value::Float(AttributeValue::parse(raw_attribute)?), - ValueType::Bytes => Value::Bytes(Arc::new(AttributeValue::parse(raw_attribute)?)), - ValueType::Bool => Value::Bool(AttributeValue::parse(raw_attribute)?), - ValueType::Timestamp => Value::Timestamp(AttributeValue::parse(raw_attribute)?), - // todo: Impl support for parsing these two types… Tho List/Map of what? - // ValueType::List => {} - // ValueType::Map => {} - _ => unimplemented!("Need support for {}", cel_type), - }; - let mut ctx = Context::default(); - ctx.add_variable_from_value("attribute", value); - Value::resolve(&self.compiled.get().unwrap().expression, &ctx) - .map(|v| { - if let Value::Bool(result) = v { - result - } else { - false - } - }) - .map_err(|err| format!("Error evaluating {:?}: {}", self.compiled, err)) - } - - fn applies(&self) -> bool { - let attribute_value = match crate::data::get_property(self.path()).unwrap() { - //TODO(didierofrivia): Replace hostcalls by DI - None => { - debug!( - "pattern_expression_applies: selector not found: {}, defaulting to ``", - self.selector - ); - b"".to_vec() - } - Some(attribute_bytes) => attribute_bytes, - }; - - // if someone would have the P_E be: - // selector: auth.identity.anonymous - // operator: eq - // value: \""true"\" - self.eval(attribute_value).unwrap_or_else(|e| { - debug!("pattern_expression_applies failed: {}", e); - false - }) - } -} - -struct CelExpression { - expression: Expression, - cel_type: ValueType, -} - -impl Debug for CelExpression { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "CelExpression({}, {:?}", self.cel_type, self.expression) - } -} - -impl Clone for CelExpression { - fn clone(&self) -> Self { - Self { - expression: self.expression.clone(), - cel_type: match self.cel_type { - ValueType::List => ValueType::List, - ValueType::Map => ValueType::Map, - ValueType::Function => ValueType::Function, - ValueType::Int => ValueType::Int, - ValueType::UInt => ValueType::UInt, - ValueType::Float => ValueType::Float, - ValueType::String => ValueType::String, - ValueType::Bytes => ValueType::Bytes, - ValueType::Bool => ValueType::Bool, - ValueType::Duration => ValueType::Duration, - ValueType::Timestamp => ValueType::Timestamp, - ValueType::Null => ValueType::Null, - }, - } - } -} - -impl TryFrom<&PatternExpression> for CelExpression { - type Error = String; - - fn try_from(expression: &PatternExpression) -> Result { - let cel_value = match cel_parser::parse(&expression.value) { - Ok(exp) => match exp { - Expression::Ident(ident) => Expression::Atom(Atom::String(ident)), - Expression::Member(_, _) => { - Expression::Atom(Atom::String(expression.value.to_string().into())) - } - _ => exp, - }, - Err(_) => Expression::Atom(Atom::String(expression.value.clone().into())), - }; - let cel_type = type_of(&expression.selector).unwrap_or(match &cel_value { - Expression::List(_) => Ok(ValueType::List), - Expression::Map(_) => Ok(ValueType::Map), - Expression::Atom(atom) => Ok(match atom { - Atom::Int(_) => ValueType::Int, - Atom::UInt(_) => ValueType::UInt, - Atom::Float(_) => ValueType::Float, - Atom::String(_) => ValueType::String, - Atom::Bytes(_) => ValueType::Bytes, - Atom::Bool(_) => ValueType::Bool, - Atom::Null => ValueType::Null, - }), - _ => Err(format!("Unsupported CEL value: {cel_value:?}")), - }?); - - let value = match cel_type { - ValueType::Map => match expression.operator { - WhenConditionOperator::Equal | WhenConditionOperator::NotEqual => { - if let Expression::Map(data) = cel_value { - Ok(Expression::Map(data)) - } else { - Err(format!("Can't compare {cel_value:?} with a Map")) - } - } - _ => Err(format!( - "Unsupported operator {:?} on Map", - &expression.operator - )), - }, - ValueType::Int | ValueType::UInt | ValueType::Float => match expression.operator { - WhenConditionOperator::Equal | WhenConditionOperator::NotEqual => { - if let Expression::Atom(atom) = &cel_value { - match atom { - Atom::Int(_) | Atom::UInt(_) | Atom::Float(_) => Ok(cel_value), - _ => Err(format!("Can't compare {cel_value:?} with a Number")), - } - } else { - Err(format!("Can't compare {cel_value:?} with a Number")) - } - } - _ => Err(format!( - "Unsupported operator {:?} on Number", - &expression.operator - )), - }, - ValueType::String => match &cel_value { - Expression::Atom(Atom::String(_)) => Ok(cel_value), - _ => Ok(Expression::Atom(Atom::String(Arc::new( - expression.value.clone(), - )))), - }, - ValueType::Bytes => match expression.operator { - WhenConditionOperator::Equal | WhenConditionOperator::NotEqual => { - if let Expression::Atom(atom) = &cel_value { - match atom { - Atom::String(_str) => Ok(cel_value), - Atom::Bytes(_bytes) => Ok(cel_value), - _ => Err(format!("Can't compare {cel_value:?} with Bytes")), - } - } else { - Err(format!("Can't compare {cel_value:?} with Bytes")) - } - } - _ => Err(format!( - "Unsupported operator {:?} on Bytes", - &expression.operator - )), - }, - ValueType::Bool => match expression.operator { - WhenConditionOperator::Equal | WhenConditionOperator::NotEqual => { - if let Expression::Atom(atom) = &cel_value { - match atom { - Atom::Bool(_) => Ok(cel_value), - _ => Err(format!("Can't compare {cel_value:?} with Bool")), - } - } else { - Err(format!("Can't compare {cel_value:?} with Bool")) - } - } - _ => Err(format!( - "Unsupported operator {:?} on Bool", - &expression.operator - )), - }, - ValueType::Timestamp => match expression.operator { - WhenConditionOperator::Equal | WhenConditionOperator::NotEqual => { - if let Expression::Atom(atom) = &cel_value { - match atom { - Atom::String(_) => Ok(Expression::FunctionCall( - Expression::Ident("timestamp".to_string().into()).into(), - None, - [cel_value].to_vec(), - )), - _ => Err(format!("Can't compare {cel_value:?} with Timestamp")), - } - } else { - Err(format!("Can't compare {cel_value:?} with Bool")) - } - } - _ => Err(format!( - "Unsupported operator {:?} on Bytes", - &expression.operator - )), - }, - _ => Err(format!( - "Still needs support for values of type `{cel_type}`" - )), - }?; - - let expression = match expression.operator { - WhenConditionOperator::Equal => Expression::Relation( - Expression::Ident(Arc::new("attribute".to_string())).into(), - RelationOp::Equals, - value.into(), - ), - WhenConditionOperator::NotEqual => Expression::Relation( - Expression::Ident(Arc::new("attribute".to_string())).into(), - RelationOp::NotEquals, - value.into(), - ), - WhenConditionOperator::StartsWith => Expression::FunctionCall( - Expression::Ident(Arc::new("startsWith".to_string())).into(), - Some(Expression::Ident("attribute".to_string().into()).into()), - [value].to_vec(), - ), - WhenConditionOperator::EndsWith => Expression::FunctionCall( - Expression::Ident(Arc::new("endsWith".to_string())).into(), - Some(Expression::Ident("attribute".to_string().into()).into()), - [value].to_vec(), - ), - WhenConditionOperator::Matches => Expression::FunctionCall( - Expression::Ident(Arc::new("matches".to_string())).into(), - Some(Expression::Ident("attribute".to_string().into()).into()), - [value].to_vec(), - ), - }; - - Ok(Self { - expression, - cel_type, - }) - } -} - -pub fn type_of(path: &str) -> Option { - match path { - "request.time" => Some(ValueType::Timestamp), - "request.id" => Some(ValueType::String), - "request.protocol" => Some(ValueType::String), - "request.scheme" => Some(ValueType::String), - "request.host" => Some(ValueType::String), - "request.method" => Some(ValueType::String), - "request.path" => Some(ValueType::String), - "request.url_path" => Some(ValueType::String), - "request.query" => Some(ValueType::String), - "request.referer" => Some(ValueType::String), - "request.useragent" => Some(ValueType::String), - "request.body" => Some(ValueType::String), - "source.address" => Some(ValueType::String), - "source.service" => Some(ValueType::String), - "source.principal" => Some(ValueType::String), - "source.certificate" => Some(ValueType::String), - "destination.address" => Some(ValueType::String), - "destination.service" => Some(ValueType::String), - "destination.principal" => Some(ValueType::String), - "destination.certificate" => Some(ValueType::String), - "connection.requested_server_name" => Some(ValueType::String), - "connection.tls_session.sni" => Some(ValueType::String), - "connection.tls_version" => Some(ValueType::String), - "connection.subject_local_certificate" => Some(ValueType::String), - "connection.subject_peer_certificate" => Some(ValueType::String), - "connection.dns_san_local_certificate" => Some(ValueType::String), - "connection.dns_san_peer_certificate" => Some(ValueType::String), - "connection.uri_san_local_certificate" => Some(ValueType::String), - "connection.uri_san_peer_certificate" => Some(ValueType::String), - "connection.sha256_peer_certificate_digest" => Some(ValueType::String), - "ratelimit.domain" => Some(ValueType::String), - "request.size" => Some(ValueType::Int), - "source.port" => Some(ValueType::Int), - "destination.port" => Some(ValueType::Int), - "connection.id" => Some(ValueType::Int), - "ratelimit.hits_addend" => Some(ValueType::Int), - "request.headers" => Some(ValueType::Map), - "request.context_extensions" => Some(ValueType::Map), - "source.labels" => Some(ValueType::Map), - "destination.labels" => Some(ValueType::Map), - "filter_state" => Some(ValueType::Map), - "connection.mtls" => Some(ValueType::Bool), - "request.raw_body" => Some(ValueType::Bytes), - "auth.identity" => Some(ValueType::Bytes), - _ => None, - } -} - pub struct FilterConfig { pub index: ActionSetIndex, pub services: Rc>>, @@ -460,12 +122,6 @@ impl TryFrom for FilterConfig { fn try_from(config: PluginConfiguration) -> Result { let mut index = ActionSetIndex::new(); for action_set in config.action_sets.iter() { - for pe in &action_set.route_rule_conditions.matches { - let result = pe.compile(); - if result.is_err() { - return Err(result.err().unwrap()); - } - } let mut predicates = Vec::default(); for predicate in &action_set.route_rule_conditions.predicates { predicates.push(Predicate::route_rule(predicate).map_err(|e| e.to_string())?); @@ -476,12 +132,6 @@ impl TryFrom for FilterConfig { .set(predicates) .expect("Predicates must not be compiled yet!"); for action in &action_set.actions { - for condition in &action.conditions { - let result = condition.compile(); - if result.is_err() { - return Err(result.err().unwrap()); - } - } let mut predicates = Vec::default(); for predicate in &action.predicates { predicates.push(Predicate::new(predicate).map_err(|e| e.to_string())?); @@ -621,22 +271,11 @@ mod test { "name": "rlp-ns-A/rlp-name-A", "routeRuleConditions": { "hostnames": ["*.toystore.com", "example.com"], - "matches": [ - { - "selector": "request.path", - "operator": "eq", - "value": "/admin/toy" - }, - { - "selector": "request.method", - "operator": "eq", - "value": "POST" - }, - { - "selector": "request.host", - "operator": "eq", - "value": "cars.toystore.com" - }] + "predicates": [ + "request.path == '/admin/toy'", + "request.method == 'POST'", + "request.host == 'cars.toystore.com'" + ] }, "actions": [ { @@ -646,12 +285,9 @@ mod test { { "service": "limitador", "scope": "rlp-ns-A/rlp-name-A", - "conditions": [ - { - "selector": "auth.metadata.username", - "operator": "eq", - "value": "alice" - }], + "predicates": [ + "auth.metadata.username == 'alice'" + ], "data": [ { "static": { @@ -701,8 +337,10 @@ mod test { panic!() } - let matches = &filter_config.action_sets[0].route_rule_conditions.matches; - assert_eq!(matches.len(), 3); + let predicates = &filter_config.action_sets[0] + .route_rule_conditions + .predicates; + assert_eq!(predicates.len(), 3); let actions = &filter_config.action_sets[0].actions; assert_eq!(actions.len(), 2); @@ -721,8 +359,8 @@ mod test { let rl_data_items = &rl_action.data; assert_eq!(rl_data_items.len(), 2); - let rl_conditions = &rl_action.conditions; - assert_eq!(rl_conditions.len(), 1); + let rl_predicates = &rl_action.predicates; + assert_eq!(rl_predicates.len(), 1); // TODO(eastizle): DataItem does not implement PartialEq, add it only for testing? //assert_eq!( @@ -839,48 +477,22 @@ mod test { "name": "rlp-ns-A/rlp-name-A", "routeRuleConditions": { "hostnames": ["*.toystore.com", "example.com"], - "matches": [ - { - "selector": "request.path", - "operator": "eq", - "value": "/admin/toy" - }, - { - "selector": "request.method", - "operator": "neq", - "value": "POST" - }, - { - "selector": "request.host", - "operator": "startswith", - "value": "cars." - }, - { - "selector": "request.host", - "operator": "endswith", - "value": ".com" - }, - { - "selector": "request.host", - "operator": "matches", - "value": "*.com" - }] + "predicates": [ + "request.path == '/admin/toy'", + "request.method != 'POST'", + "request.host.startsWith('cars.')", + "request.host.endsWith('.com')", + "request.host.matches('*.com')" + ] }, "actions": [ { "service": "limitador", "scope": "rlp-ns-A/rlp-name-A", - "conditions": [ - { - "selector": "auth.metadata.username", - "operator": "eq", - "value": "alice" - }, - { - "selector": "request.path", - "operator": "endswith", - "value": "/car" - }], + "predicates": [ + "auth.metadata.username == 'alice'", + "request.path.endsWith('/car')" + ], "data": [ { "selector": { "selector": "my.selector.path" } }] }] }] @@ -894,46 +506,13 @@ mod test { let filter_config = res.unwrap(); assert_eq!(filter_config.action_sets.len(), 1); - let matches = &filter_config.action_sets[0].route_rule_conditions.matches; - assert_eq!(matches.len(), 5); - - let expected_matches = [ - // selector, value, operator - ("request.path", "/admin/toy", WhenConditionOperator::Equal), - ("request.method", "POST", WhenConditionOperator::NotEqual), - ("request.host", "cars.", WhenConditionOperator::StartsWith), - ("request.host", ".com", WhenConditionOperator::EndsWith), - ("request.host", "*.com", WhenConditionOperator::Matches), - ]; + let predicates = &filter_config.action_sets[0] + .route_rule_conditions + .predicates; + assert_eq!(predicates.len(), 5); - for (m, (expected_selector, expected_value, expected_operator)) in - matches.iter().zip(expected_matches.iter()) - { - assert_eq!(m.selector, *expected_selector); - assert_eq!(m.value, *expected_value); - assert_eq!(m.operator, *expected_operator); - } - - let conditions = &filter_config.action_sets[0].actions[0].conditions; - assert_eq!(conditions.len(), 2); - - let expected_conditions = [ - // selector, value, operator - ( - "auth.metadata.username", - "alice", - WhenConditionOperator::Equal, - ), - ("request.path", "/car", WhenConditionOperator::EndsWith), - ]; - - for (condition, (expected_selector, expected_value, expected_operator)) in - conditions.iter().zip(expected_conditions.iter()) - { - assert_eq!(condition.selector, *expected_selector); - assert_eq!(condition.value, *expected_value); - assert_eq!(condition.operator, *expected_operator); - } + let predicates = &filter_config.action_sets[0].actions[0].predicates; + assert_eq!(predicates.len(), 2); } #[test] @@ -986,8 +565,10 @@ mod test { Timeout(Duration::from_millis(20)) ); - let matches = &filter_config.action_sets[0].route_rule_conditions.matches; - assert_eq!(matches.len(), 0); + let predicates = &filter_config.action_sets[0] + .route_rule_conditions + .predicates; + assert_eq!(predicates.len(), 0); } #[test] @@ -1058,38 +639,6 @@ mod test { }"#; let res = serde_json::from_str::(bad_config); assert!(res.is_err()); - - // condition selector operator unknown - let bad_config = r#"{ - "services": { - "limitador": { - "type": "ratelimit", - "endpoint": "limitador-cluster", - "failureMode": "deny" - } - }, - "actionSets": [ - { - "name": "rlp-ns-A/rlp-name-A", - "routeRuleConditions": { - "hostnames": ["*.toystore.com", "example.com"], - "matches": [ - { - "selector": "request.path", - "operator": "unknown", - "value": "/admin/toy" - }] - }, - "actions": [ - { - "service": "limitador", - "scope": "rlp-ns-A/rlp-name-A", - "data": [ { "selector": { "selector": "my.selector.path" } }] - }] - }] - }"#; - let res = serde_json::from_str::(bad_config); - assert!(res.is_err()); } #[test] @@ -1115,170 +664,4 @@ mod test { let rlp_option = filter_config.index.get_longest_match_action_sets("unknown"); assert!(rlp_option.is_none()); } - - mod pattern_expressions { - use crate::configuration::{PatternExpression, WhenConditionOperator}; - - #[test] - fn test_legacy_string() { - let p = PatternExpression { - selector: "request.id".to_string(), - operator: WhenConditionOperator::Equal, - value: "request_id".to_string(), - path: Default::default(), - compiled: Default::default(), - }; - p.compile().expect("Should compile fine!"); - assert_eq!( - p.eval("request_id".as_bytes().to_vec()), - Ok(true), - "Expression: {:?}", - p.compiled.get() - ) - } - - #[test] - fn test_proper_string() { - let p = PatternExpression { - selector: "request.id".to_string(), - operator: WhenConditionOperator::Equal, - value: "\"request_id\"".to_string(), - path: Default::default(), - compiled: Default::default(), - }; - p.compile().expect("Should compile fine!"); - assert_eq!( - p.eval("request_id".as_bytes().to_vec()), - Ok(true), - "Expression: {:?}", - p.compiled.get() - ) - } - - #[test] - fn test_proper_int_as_string() { - let p = PatternExpression { - selector: "request.id".to_string(), - operator: WhenConditionOperator::Equal, - value: "123".to_string(), - path: Default::default(), - compiled: Default::default(), - }; - p.compile().expect("Should compile fine!"); - assert_eq!( - p.eval("123".as_bytes().to_vec()), - Ok(true), - "Expression: {:?}", - p.compiled.get() - ) - } - - #[test] - fn test_proper_string_inferred() { - let p = PatternExpression { - selector: "foobar".to_string(), - operator: WhenConditionOperator::Equal, - value: "\"123\"".to_string(), - path: Default::default(), - compiled: Default::default(), - }; - p.compile().expect("Should compile fine!"); - assert_eq!( - p.eval("123".as_bytes().to_vec()), - Ok(true), - "Expression: {:?}", - p.compiled.get() - ) - } - - #[test] - fn test_int() { - let p = PatternExpression { - selector: "destination.port".to_string(), - operator: WhenConditionOperator::Equal, - value: "8080".to_string(), - path: Default::default(), - compiled: Default::default(), - }; - p.compile().expect("Should compile fine!"); - assert_eq!( - p.eval(8080_i64.to_le_bytes().to_vec()), - Ok(true), - "Expression: {:?}", - p.compiled.get() - ) - } - - #[test] - fn test_int_inferred() { - let p = PatternExpression { - selector: "foobar".to_string(), - operator: WhenConditionOperator::Equal, - value: "8080".to_string(), - path: Default::default(), - compiled: Default::default(), - }; - p.compile().expect("Should compile fine!"); - assert_eq!( - p.eval(8080_i64.to_le_bytes().to_vec()), - Ok(true), - "Expression: {:?}", - p.compiled.get() - ) - } - - #[test] - fn test_float_inferred() { - let p = PatternExpression { - selector: "foobar".to_string(), - operator: WhenConditionOperator::Equal, - value: "1.0".to_string(), - path: Default::default(), - compiled: Default::default(), - }; - p.compile().expect("Should compile fine!"); - assert_eq!( - p.eval(1_f64.to_le_bytes().to_vec()), - Ok(true), - "Expression: {:?}", - p.compiled.get() - ) - } - - #[test] - fn test_bool() { - let p = PatternExpression { - selector: "connection.mtls".to_string(), - operator: WhenConditionOperator::Equal, - value: "true".to_string(), - path: Default::default(), - compiled: Default::default(), - }; - p.compile().expect("Should compile fine!"); - assert_eq!( - p.eval((true as u8).to_le_bytes().to_vec()), - Ok(true), - "Expression: {:?}", - p.compiled.get() - ) - } - - #[test] - fn test_timestamp() { - let p = PatternExpression { - selector: "request.time".to_string(), - operator: WhenConditionOperator::Equal, - value: "2023-05-28T00:00:00+00:00".to_string(), - path: Default::default(), - compiled: Default::default(), - }; - p.compile().expect("Should compile fine!"); - assert_eq!( - p.eval(1685232000000000000_i64.to_le_bytes().to_vec()), - Ok(true), - "Expression: {:?}", - p.compiled.get() - ) - } - } } diff --git a/src/configuration/action.rs b/src/configuration/action.rs index 3e634d2..a98790e 100644 --- a/src/configuration/action.rs +++ b/src/configuration/action.rs @@ -1,4 +1,4 @@ -use crate::configuration::{DataItem, DataType, PatternExpression}; +use crate::configuration::{DataItem, DataType}; use crate::data::Predicate; use crate::envoy::{RateLimitDescriptor, RateLimitDescriptor_Entry}; use cel_interpreter::Value; @@ -13,8 +13,6 @@ pub struct Action { pub service: String, pub scope: String, #[serde(default)] - pub conditions: Vec, - #[serde(default)] pub predicates: Vec, #[serde(skip_deserializing)] pub compiled_predicates: OnceCell>, @@ -28,20 +26,16 @@ impl Action { .compiled_predicates .get() .expect("predicates must be compiled by now"); - if predicates.is_empty() { - self.conditions.is_empty() || self.conditions.iter().all(PatternExpression::applies) - } else { - predicates - .iter() - .enumerate() - .all(|(pos, predicate)| match predicate.test() { - Ok(b) => b, - Err(err) => { - error!("Failed to evaluate {}: {}", self.predicates[pos], err); - panic!("Err out of this!") - } - }) - } + predicates + .iter() + .enumerate() + .all(|(pos, predicate)| match predicate.test() { + Ok(b) => b, + Err(err) => { + error!("Failed to evaluate {}: {}", self.predicates[pos], err); + panic!("Err out of this!") + } + }) } pub fn build_descriptors(&self) -> RepeatedField { diff --git a/src/configuration/action_set.rs b/src/configuration/action_set.rs index 04f34f7..a705987 100644 --- a/src/configuration/action_set.rs +++ b/src/configuration/action_set.rs @@ -1,5 +1,4 @@ use crate::configuration::action::Action; -use crate::configuration::PatternExpression; use crate::data::Predicate; use log::error; use serde::Deserialize; @@ -9,8 +8,6 @@ use std::cell::OnceCell; pub struct RouteRuleConditions { pub hostnames: Vec, #[serde(default)] - pub matches: Vec, - #[serde(default)] pub predicates: Vec, #[serde(skip_deserializing)] pub compiled_predicates: OnceCell>, @@ -44,15 +41,8 @@ impl ActionSet { .compiled_predicates .get() .expect("predicates must be compiled by now"); - if predicates.is_empty() { - self.route_rule_conditions.matches.is_empty() - || self - .route_rule_conditions - .matches - .iter() - .all(|m| m.applies()) - } else { - predicates + predicates.is_empty() + || predicates .iter() .enumerate() .all(|(pos, predicate)| match predicate.test() { @@ -65,6 +55,5 @@ impl ActionSet { panic!("Err out of this!") } }) - } } } diff --git a/src/data/mod.rs b/src/data/mod.rs index 0e1cc10..d61009f 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -4,10 +4,8 @@ mod property; pub use attribute::get_attribute; pub use attribute::store_metadata; -pub use attribute::AttributeValue; pub use cel::Expression; pub use cel::Predicate; -pub use property::get_property; pub use property::Path as PropertyPath; diff --git a/src/operation_dispatcher.rs b/src/operation_dispatcher.rs index 8525c94..cc17143 100644 --- a/src/operation_dispatcher.rs +++ b/src/operation_dispatcher.rs @@ -363,7 +363,6 @@ mod tests { action: Action { service: "local".to_string(), scope: "".to_string(), - conditions: vec![], predicates: vec![], compiled_predicates: OnceCell::default(), data: vec![], diff --git a/tests/multi.rs b/tests/multi.rs index 5d9bc12..6082bea 100644 --- a/tests/multi.rs +++ b/tests/multi.rs @@ -503,22 +503,11 @@ fn authenticated_one_ratelimit_action_matches() { "name": "some-name", "routeRuleConditions": { "hostnames": ["*.toystore.com", "example.com"], - "matches": [ - { - "selector": "request.url_path", - "operator": "startswith", - "value": "/admin/toy" - }, - { - "selector": "request.host", - "operator": "eq", - "value": "cars.toystore.com" - }, - { - "selector": "request.method", - "operator": "eq", - "value": "POST" - }] + "predicates" : [ + "request.url_path.startsWith('/admin/toy')", + "request.host == 'cars.toystore.com'", + "request.method == 'POST'" + ] }, "actions": [ { @@ -528,12 +517,9 @@ fn authenticated_one_ratelimit_action_matches() { { "service": "limitador", "scope": "RLS-domain", - "conditions": [ - { - "selector": "source.address", - "operator": "eq", - "value": "127.0.0.1:80" - }], + "predicates" : [ + "source.address == '127.0.0.1:80'" + ], "data": [ { "static": { @@ -545,12 +531,9 @@ fn authenticated_one_ratelimit_action_matches() { { "service": "limitador", "scope": "RLS-domain", - "conditions": [ - { - "selector": "source.address", - "operator": "neq", - "value": "127.0.0.1:80" - }], + "predicates" : [ + "source.address != '127.0.0.1:80'" + ], "data": [ { "static": { From c712ab6d8b10d12f3ab3a382d79a9489fffc5978 Mon Sep 17 00:00:00 2001 From: Eguzki Astiz Lezaun Date: Fri, 8 Nov 2024 10:59:47 +0100 Subject: [PATCH 2/6] update docs Signed-off-by: Eguzki Astiz Lezaun --- README.md | 96 ------------------------------------------------------- 1 file changed, 96 deletions(-) diff --git a/README.md b/README.md index a638a36..5068a97 100644 --- a/README.md +++ b/README.md @@ -50,102 +50,6 @@ evaluating to a `bool` value, while `Expression`, used for passing data to a ser These expression can operate on the data made available to them through the Well Known Attributes, see below -#### Conditions, Selectors and Operators (deprecated!) - -
- -While still supported, these will eventually disappear. For now though, you still can express them as such: - -```yaml -services: - auth-service: - type: auth - endpoint: auth-cluster - failureMode: deny - timeout: 10ms - ratelimit-service: - type: ratelimit - endpoint: ratelimit-cluster - failureMode: deny -actionSets: - - name: rlp-ns-A/rlp-name-A - routeRuleConditions: - hostnames: [ "*.toystore.com" ] - matches: - - selector: request.url_path - operator: startswith - value: /get - - selector: request.host - operator: eq - value: test.toystore.com - - selector: request.method - operator: eq - value: GET - actions: - - service: ratelimit-service - scope: rlp-ns-A/rlp-name-A - conditions: [] - data: - - selector: - selector: request.headers.My-Custom-Header - - static: - key: admin - value: "1" -``` - -```Rust -#[derive(Deserialize, PartialEq, Debug, Clone)] -pub enum WhenConditionOperator { - #[serde(rename = "eq")] - EqualOperator, - #[serde(rename = "neq")] - NotEqualOperator, - #[serde(rename = "startswith")] - StartsWithOperator, - #[serde(rename = "endswith")] - EndsWithOperator, - #[serde(rename = "matches")] - MatchesOperator, -} -``` - -Selector of an attribute from the contextual properties provided by kuadrant. -See [Well Known Attributes](#Well-Known-Attributes) for more info about available attributes. - -The struct is - -```Rust -#[derive(Deserialize, Debug, Clone)] -pub struct SelectorItem { - // Selector of an attribute from the contextual properties provided by kuadrant - // during request and connection processing - pub selector: String, - - // If not set it defaults to `selector` field value as the descriptor key. - #[serde(default)] - pub key: Option, - - // An optional value to use if the selector is not found in the context. - // If not set and the selector is not found in the context, then no data is generated. - #[serde(default)] - pub default: Option, -} -``` - -Selectors are tokenized at each non-escaped occurrence of a separator character `.`. -Example: - -``` -Input: this.is.a.exam\.ple -> Retuns: ["this", "is", "a", "exam.ple"]. -``` - -Some path segments include dot `.` char in them. For instance envoy filter -names: `envoy.filters.http.header_to_metadata`. -In that particular cases, the dot chat (separator), needs to be escaped. - -
- - ### Well Known Attributes | Attribute | Description | From 86199146525833d183461ad72b34b6a1a759154e Mon Sep 17 00:00:00 2001 From: Eguzki Astiz Lezaun Date: Fri, 8 Nov 2024 11:13:33 +0100 Subject: [PATCH 3/6] fix tests Signed-off-by: Eguzki Astiz Lezaun --- tests/multi.rs | 10 +++++----- tests/rate_limited.rs | 30 ++++++++---------------------- 2 files changed, 13 insertions(+), 27 deletions(-) diff --git a/tests/multi.rs b/tests/multi.rs index 6082bea..d2cb383 100644 --- a/tests/multi.rs +++ b/tests/multi.rs @@ -682,11 +682,11 @@ fn authenticated_one_ratelimit_action_matches() { 10, 215, 1, 10, 20, 10, 18, 10, 16, 18, 10, 49, 46, 50, 46, 51, 46, 52, 58, 56, 48, 24, 200, 223, 2, 18, 23, 10, 21, 10, 19, 18, 14, 49, 50, 55, 46, 48, 46, 48, 46, 49, 58, 56, 48, 48, 48, 24, 192, 62, 34, 141, 1, 10, 0, 18, 136, 1, 18, 3, 71, 69, - 84, 26, 30, 10, 10, 58, 97, 117, 116, 104, 111, 114, 105, 116, 121, 18, 16, 97, 98, - 105, 95, 116, 101, 115, 116, 95, 104, 97, 114, 110, 101, 115, 115, 26, 14, 10, 7, - 58, 109, 101, 116, 104, 111, 100, 18, 3, 71, 69, 84, 26, 38, 10, 5, 58, 112, 97, - 116, 104, 18, 29, 47, 100, 101, 102, 97, 117, 108, 116, 47, 114, 101, 113, 117, - 101, 115, 116, 47, 104, 101, 97, 100, 101, 114, 115, 47, 112, 97, 116, 104, 34, 10, + 84, 26, 14, 10, 7, 58, 109, 101, 116, 104, 111, 100, 18, 3, 71, 69, 84, 26, 38, 10, + 5, 58, 112, 97, 116, 104, 18, 29, 47, 100, 101, 102, 97, 117, 108, 116, 47, 114, + 101, 113, 117, 101, 115, 116, 47, 104, 101, 97, 100, 101, 114, 115, 47, 112, 97, + 116, 104, 26, 30, 10, 10, 58, 97, 117, 116, 104, 111, 114, 105, 116, 121, 18, 16, + 97, 98, 105, 95, 116, 101, 115, 116, 95, 104, 97, 114, 110, 101, 115, 115, 34, 10, 47, 97, 100, 109, 105, 110, 47, 116, 111, 121, 42, 17, 99, 97, 114, 115, 46, 116, 111, 121, 115, 116, 111, 114, 101, 46, 99, 111, 109, 50, 4, 104, 116, 116, 112, 82, 4, 72, 84, 84, 80, 82, 20, 10, 4, 104, 111, 115, 116, 18, 12, 97, 117, 116, 104, diff --git a/tests/rate_limited.rs b/tests/rate_limited.rs index bbc0732..5ae17e1 100644 --- a/tests/rate_limited.rs +++ b/tests/rate_limited.rs @@ -96,22 +96,11 @@ fn it_limits() { "name": "some-name", "routeRuleConditions": { "hostnames": ["*.toystore.com", "example.com"], - "matches": [ - { - "selector": "request.url_path", - "operator": "startswith", - "value": "/admin/toy" - }, - { - "selector": "request.host", - "operator": "eq", - "value": "cars.toystore.com" - }, - { - "selector": "request.method", - "operator": "eq", - "value": "POST" - }] + "predicates" : [ + "request.url_path.startsWith('/admin/toy')", + "request.host == 'cars.toystore.com'", + "request.method == 'POST'" + ] }, "actions": [ { @@ -641,12 +630,9 @@ fn it_does_not_rate_limits_when_condition_does_not_match() { { "service": "limitador", "scope": "RLS-domain", - "conditions": [ - { - "selector": "request.url_path", - "operator": "startswith", - "value": "/admin/toy" - }] + "predicates" : [ + "request.url_path.startsWith('/admin/toy')" + ] }] }] }"#; From a62ad354001aabb7bcc1826ad9f6b737d9727ef6 Mon Sep 17 00:00:00 2001 From: Eguzki Astiz Lezaun Date: Fri, 8 Nov 2024 11:48:14 +0100 Subject: [PATCH 4/6] empty predicates do apply Signed-off-by: Eguzki Astiz Lezaun --- src/configuration/action.rs | 45 +++++++++++++++++++++++++-------- src/configuration/action_set.rs | 21 +++++++++++++++ 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/configuration/action.rs b/src/configuration/action.rs index a98790e..b61bdda 100644 --- a/src/configuration/action.rs +++ b/src/configuration/action.rs @@ -26,16 +26,17 @@ impl Action { .compiled_predicates .get() .expect("predicates must be compiled by now"); - predicates - .iter() - .enumerate() - .all(|(pos, predicate)| match predicate.test() { - Ok(b) => b, - Err(err) => { - error!("Failed to evaluate {}: {}", self.predicates[pos], err); - panic!("Err out of this!") - } - }) + predicates.is_empty() + || predicates + .iter() + .enumerate() + .all(|(pos, predicate)| match predicate.test() { + Ok(b) => b, + Err(err) => { + error!("Failed to evaluate {}: {}", self.predicates[pos], err); + panic!("Err out of this!") + } + }) } pub fn build_descriptors(&self) -> RepeatedField { @@ -121,3 +122,27 @@ impl Action { Some(res) } } + +#[cfg(test)] +mod test { + use crate::configuration::action::Action; + use std::cell::OnceCell; + + #[test] + fn empty_predicates_do_apply() { + let compiled_predicates = OnceCell::new(); + compiled_predicates + .set(Vec::default()) + .expect("predicates must not be compiled yet!"); + + let action = Action { + service: String::from("svc1"), + scope: String::from("sc1"), + predicates: vec![], + compiled_predicates, + data: vec![], + }; + + assert!(action.conditions_apply()) + } +} diff --git a/src/configuration/action_set.rs b/src/configuration/action_set.rs index a705987..1e8a61a 100644 --- a/src/configuration/action_set.rs +++ b/src/configuration/action_set.rs @@ -57,3 +57,24 @@ impl ActionSet { }) } } + +#[cfg(test)] +mod test { + use crate::configuration::action_set::ActionSet; + + fn build_action_set(name: &str) -> ActionSet { + ActionSet::new(name.to_owned(), Default::default(), Vec::new()) + } + + #[test] + fn empty_route_rule_conditions_do_apply() { + let action_set_1 = build_action_set("as_1"); + action_set_1 + .route_rule_conditions + .compiled_predicates + .set(Vec::default()) + .expect("Predicates must not be compiled yet!"); + + assert!(action_set_1.conditions_apply()) + } +} From ce3a829c1746fb3d617d202593be9ea8ae6c23a5 Mon Sep 17 00:00:00 2001 From: Eguzki Astiz Lezaun Date: Fri, 8 Nov 2024 12:00:32 +0100 Subject: [PATCH 5/6] change visibility of property::get_property to the parent module Signed-off-by: Eguzki Astiz Lezaun --- src/data/property.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/property.rs b/src/data/property.rs index 812edf8..06ad798 100644 --- a/src/data/property.rs +++ b/src/data/property.rs @@ -77,7 +77,7 @@ pub(super) fn host_get_property(path: &Path) -> Result>, Status> proxy_wasm::hostcalls::get_property(path.tokens()) } -pub fn get_property(path: &Path) -> Result>, Status> { +pub(super) fn get_property(path: &Path) -> Result>, Status> { match *path.tokens() { ["source", "remote_address"] => remote_address(), ["auth", ..] => host_get_property(&wasm_prop(path.tokens().as_slice())), From 21b2fd7b9172c5c4bea0f4ea9024657b838ba196 Mon Sep 17 00:00:00 2001 From: Eguzki Astiz Lezaun Date: Fri, 8 Nov 2024 15:54:18 +0100 Subject: [PATCH 6/6] =?UTF-8?q?selector=20data=20type=20=F0=9F=94=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Eguzki Astiz Lezaun --- src/configuration.rs | 165 +++--------------------------------- src/configuration/action.rs | 33 +------- tests/rate_limited.rs | 105 +---------------------- 3 files changed, 16 insertions(+), 287 deletions(-) diff --git a/src/configuration.rs b/src/configuration.rs index cefee85..a9c5dda 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -8,7 +8,6 @@ use crate::configuration::action_set::ActionSet; use crate::configuration::action_set_index::ActionSetIndex; use crate::data; use crate::data::Predicate; -use crate::data::PropertyPath; use crate::service::GrpcService; use cel_interpreter::functions::duration; use cel_interpreter::Value; @@ -37,39 +36,6 @@ impl ExpressionItem { } } -#[derive(Deserialize, Debug, Clone)] -pub struct SelectorItem { - // Selector of an attribute from the contextual properties provided by kuadrant - // during request and connection processing - pub selector: String, - - // If not set it defaults to `selector` field value as the descriptor key. - #[serde(default)] - pub key: Option, - - // An optional value to use if the selector is not found in the context. - // If not set and the selector is not found in the context, then no data is generated. - #[serde(default)] - pub default: Option, - - #[serde(skip_deserializing)] - path: OnceCell, -} - -impl SelectorItem { - pub fn compile(&self) -> Result<(), String> { - self.path - .set(self.selector.as_str().into()) - .map_err(|p| format!("Err on {p:?}")) - } - - pub fn path(&self) -> &PropertyPath { - self.path - .get() - .expect("SelectorItem wasn't previously compiled!") - } -} - #[derive(Deserialize, Debug, Clone)] pub struct StaticItem { pub value: String, @@ -81,7 +47,6 @@ pub struct StaticItem { #[serde(rename_all = "lowercase")] pub enum DataType { Static(StaticItem), - Selector(SelectorItem), Expression(ExpressionItem), } @@ -89,7 +54,6 @@ impl DataType { pub fn compile(&self) -> Result<(), String> { match self { DataType::Static(_) => Ok(()), - DataType::Selector(selector) => selector.compile(), DataType::Expression(exp) => exp.compile(), } } @@ -405,118 +369,7 @@ mod test { } #[test] - fn parse_config_data_selector() { - let config = r#"{ - "services": { - "limitador": { - "type": "ratelimit", - "endpoint": "limitador-cluster", - "failureMode": "deny" - } - }, - "actionSets": [ - { - "name": "rlp-ns-A/rlp-name-A", - "routeRuleConditions": { - "hostnames": ["*.toystore.com", "example.com"] - }, - "actions": [ - { - "service": "limitador", - "scope": "rlp-ns-A/rlp-name-A", - "data": [ - { - "selector": { - "selector": "my.selector.path", - "key": "mykey", - "default": "my_selector_default_value" - } - }] - }] - }] - }"#; - let res = serde_json::from_str::(config); - if let Err(ref e) = res { - eprintln!("{e}"); - } - assert!(res.is_ok()); - - let filter_config = res.unwrap(); - assert_eq!(filter_config.action_sets.len(), 1); - - let actions = &filter_config.action_sets[0].actions; - assert_eq!(actions.len(), 1); - - let data_items = &actions[0].data; - assert_eq!(data_items.len(), 1); - - if let DataType::Selector(selector_item) = &data_items[0].item { - assert_eq!(selector_item.selector, "my.selector.path"); - assert_eq!(selector_item.key.as_ref().unwrap(), "mykey"); - assert_eq!( - selector_item.default.as_ref().unwrap(), - "my_selector_default_value" - ); - } else { - panic!(); - } - } - - #[test] - fn parse_config_condition_selector_operators() { - let config = r#"{ - "services": { - "limitador": { - "type": "ratelimit", - "endpoint": "limitador-cluster", - "failureMode": "deny" - } - }, - "actionSets": [ - { - "name": "rlp-ns-A/rlp-name-A", - "routeRuleConditions": { - "hostnames": ["*.toystore.com", "example.com"], - "predicates": [ - "request.path == '/admin/toy'", - "request.method != 'POST'", - "request.host.startsWith('cars.')", - "request.host.endsWith('.com')", - "request.host.matches('*.com')" - ] - }, - "actions": [ - { - "service": "limitador", - "scope": "rlp-ns-A/rlp-name-A", - "predicates": [ - "auth.metadata.username == 'alice'", - "request.path.endsWith('/car')" - ], - "data": [ { "selector": { "selector": "my.selector.path" } }] - }] - }] - }"#; - let res = serde_json::from_str::(config); - if let Err(ref e) = res { - eprintln!("{e}"); - } - assert!(res.is_ok()); - - let filter_config = res.unwrap(); - assert_eq!(filter_config.action_sets.len(), 1); - - let predicates = &filter_config.action_sets[0] - .route_rule_conditions - .predicates; - assert_eq!(predicates.len(), 5); - - let predicates = &filter_config.action_sets[0].actions[0].predicates; - assert_eq!(predicates.len(), 2); - } - - #[test] - fn parse_config_conditions_optional() { + fn parse_config_predicates_optional() { let config = r#"{ "services": { "limitador": { @@ -543,8 +396,9 @@ mod test { } }, { - "selector": { - "selector": "auth.metadata.username" + "expression": { + "key": "username", + "value": "auth.metadata.username" } }] }] @@ -569,6 +423,12 @@ mod test { .route_rule_conditions .predicates; assert_eq!(predicates.len(), 0); + + let actions = &filter_config.action_sets[0].actions; + assert_eq!(actions.len(), 1); + + let action_predicates = &actions[0].predicates; + assert_eq!(action_predicates.len(), 0); } #[test] @@ -598,8 +458,9 @@ mod test { "key": "rlp-ns-A/rlp-name-A", "value": "1" }, - "selector": { - "selector": "auth.metadata.username" + "expression": { + "key": "username", + "value": "auth.metadata.username" } }] }] diff --git a/src/configuration/action.rs b/src/configuration/action.rs index b61bdda..58e893f 100644 --- a/src/configuration/action.rs +++ b/src/configuration/action.rs @@ -2,7 +2,7 @@ use crate::configuration::{DataItem, DataType}; use crate::data::Predicate; use crate::envoy::{RateLimitDescriptor, RateLimitDescriptor_Entry}; use cel_interpreter::Value; -use log::{debug, error}; +use log::error; use protobuf::RepeatedField; use serde::Deserialize; use std::cell::OnceCell; @@ -80,37 +80,6 @@ impl Action { } }, ), - DataType::Selector(selector_item) => { - let descriptor_key = match &selector_item.key { - None => selector_item.path().to_string(), - Some(key) => key.to_owned(), - }; - - let value = match crate::data::get_attribute::(selector_item.path()) - .expect("Error!") - { - //TODO(didierofrivia): Replace hostcalls by DI - None => { - debug!( - "build_single_descriptor: selector not found: {}", - selector_item.path() - ); - match &selector_item.default { - None => return None, // skipping the entire descriptor - Some(default_value) => default_value.clone(), - } - } - // TODO(eastizle): not all fields are strings - // https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/advanced/attributes - Some(attr_str) => attr_str, - // Alternative implementation (for rust >= 1.76) - // Attribute::parse(attribute_bytes) - // .inspect_err(|e| debug!("#{} build_single_descriptor: failed to parse selector value: {}, error: {}", - // filter.context_id, attribute_path, e)) - // .ok()?, - }; - (descriptor_key, value) - } }; let mut descriptor_entry = RateLimitDescriptor_Entry::new(); descriptor_entry.set_key(key); diff --git a/tests/rate_limited.rs b/tests/rate_limited.rs index 5ae17e1..f0fd84f 100644 --- a/tests/rate_limited.rs +++ b/tests/rate_limited.rs @@ -374,7 +374,7 @@ fn it_passes_additional_headers() { #[test] #[serial] -fn it_rate_limits_with_empty_conditions() { +fn it_rate_limits_with_empty_predicates() { let args = tester::MockSettings { wasm_path: wasm_module(), quiet: false, @@ -496,108 +496,7 @@ fn it_rate_limits_with_empty_conditions() { #[test] #[serial] -fn it_does_not_rate_limits_when_selector_does_not_exist_and_misses_default_value() { - let args = tester::MockSettings { - wasm_path: wasm_module(), - quiet: false, - allow_unexpected: false, - }; - let mut module = tester::mock(args).unwrap(); - - module - .call_start() - .execute_and_expect(ReturnType::None) - .unwrap(); - - let root_context = 1; - let cfg = r#"{ - "services": { - "limitador": { - "type": "ratelimit", - "endpoint": "limitador-cluster", - "failureMode": "deny", - "timeout": "5s" - } - }, - "actionSets": [ - { - "name": "some-name", - "routeRuleConditions": { - "hostnames": ["*.com"] - }, - "actions": [ - { - "service": "limitador", - "scope": "RLS-domain", - "data": [ - { - "selector": { - "selector": "unknown.path" - } - } - ] - }] - }] - }"#; - - module - .call_proxy_on_context_create(root_context, 0) - .expect_log(Some(LogLevel::Info), Some("#1 set_root_context")) - .execute_and_expect(ReturnType::None) - .unwrap(); - module - .call_proxy_on_configure(root_context, 0) - .expect_log(Some(LogLevel::Info), Some("#1 on_configure")) - .expect_get_buffer_bytes(Some(BufferType::PluginConfiguration)) - .returning(Some(cfg.as_bytes())) - .expect_log(Some(LogLevel::Info), None) - .execute_and_expect(ReturnType::Bool(true)) - .unwrap(); - - let http_context = 2; - module - .call_proxy_on_context_create(http_context, root_context) - .expect_log(Some(LogLevel::Debug), Some("#2 create_http_context")) - .execute_and_expect(ReturnType::None) - .unwrap(); - - module - .call_proxy_on_request_headers(http_context, 0, false) - .expect_log(Some(LogLevel::Debug), Some("#2 on_http_request_headers")) - .expect_get_header_map_value(Some(MapType::HttpRequestHeaders), Some(":authority")) - .returning(Some("a.com")) - .expect_log( - Some(LogLevel::Debug), - Some("#2 action_set selected some-name"), - ) - // retrieving properties for RateLimitRequest - .expect_log( - Some(LogLevel::Debug), - Some("get_property: path: [\"unknown\", \"path\"]"), - ) - .expect_get_property(Some(vec!["unknown", "path"])) - .returning(None) - .expect_log( - Some(LogLevel::Debug), - Some("build_single_descriptor: selector not found: unknown.path"), - ) - .expect_log( - Some(LogLevel::Debug), - Some("grpc_message_request: empty descriptors"), - ) - .execute_and_expect(ReturnType::Action(Action::Continue)) - .unwrap(); - - module - .call_proxy_on_response_headers(http_context, 0, false) - .expect_log(Some(LogLevel::Debug), Some("#2 on_http_response_headers")) - .execute_and_expect(ReturnType::Action(Action::Continue)) - .unwrap(); -} - -#[test] -#[serial] -fn it_does_not_rate_limits_when_condition_does_not_match() { +fn it_does_not_rate_limits_when_predicates_does_not_match() { let args = tester::MockSettings { wasm_path: wasm_module(), quiet: false,