Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Predicate on our "well-known attributes" #113

Merged
merged 26 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
271b464
Refactored Attribute to data::AttributeValue
alexsnaps Oct 19, 2024
762091d
Refactored property stuff to data::property
alexsnaps Oct 19, 2024
78d5eb0
Refactored using PropertyPath everywhere
alexsnaps Oct 19, 2024
7624900
Refactored using AttributeValue everywhere
alexsnaps Oct 19, 2024
54ad632
ports are u64, not strings... this err'ed into Default
alexsnaps Oct 19, 2024
eede19a
Slowly getting there
alexsnaps Oct 20, 2024
222a7cf
Wired the base
alexsnaps Oct 20, 2024
f4fd18f
Clippy fixes
alexsnaps Oct 21, 2024
34c7050
Notes for auth/json integration
alexsnaps Oct 21, 2024
df48e11
Typos and test infra for hostcalls::get_property
alexsnaps Oct 21, 2024
8c001f3
Predicate test
alexsnaps Oct 21, 2024
ce49eb2
Transparently replace PatternExpression with CEL Predicate
alexsnaps Oct 21, 2024
60820e0
Moar clippy
alexsnaps Oct 21, 2024
067aaf2
Deal with unknown attributes as JSON literals
alexsnaps Oct 22, 2024
06000e0
Arguable: but lets fall back to string on bad json
alexsnaps Oct 22, 2024
7dc7ac9
wasm props are in the filter_state
alexsnaps Oct 23, 2024
611a900
Deal with more types & json encode values
alexsnaps Oct 23, 2024
f911798
Have auth in wasm prop and log on unexpected type
alexsnaps Oct 23, 2024
dbdf7e1
Deal with previously resolved values on path digging into these
alexsnaps Oct 23, 2024
5deae3b
Don't warn on structs when storing values as json
alexsnaps Oct 23, 2024
a786e94
Support headers in CEL
alexsnaps Oct 23, 2024
00d4863
Remove trailing comment
alexsnaps Oct 23, 2024
b63057f
No more dead code
alexsnaps Oct 23, 2024
b9af3dc
Support for CEL expressions in data exports
alexsnaps Oct 24, 2024
7528942
request.time is a Timestamp
alexsnaps Oct 24, 2024
06cd03d
README reflecting new config
alexsnaps Oct 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 37 additions & 16 deletions src/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ use std::fmt::{Debug, Formatter};
use std::rc::Rc;
use std::sync::Arc;

use crate::attribute::Attribute;
use crate::configuration::action_set::ActionSet;
use crate::configuration::action_set_index::ActionSetIndex;
use crate::property_path::Path;
use crate::data::PropertyPath;
use crate::data::{AttributeValue, Predicate};
use crate::service::GrpcService;
use cel_interpreter::functions::duration;
use cel_interpreter::objects::ValueType;
Expand Down Expand Up @@ -38,7 +38,7 @@ pub struct SelectorItem {
pub default: Option<String>,

#[serde(skip_deserializing)]
path: OnceCell<Path>,
path: OnceCell<PropertyPath>,
}

impl SelectorItem {
Expand All @@ -48,7 +48,7 @@ impl SelectorItem {
.map_err(|p| format!("Err on {p:?}"))
}

pub fn path(&self) -> &Path {
pub fn path(&self) -> &PropertyPath {
self.path
.get()
.expect("SelectorItem wasn't previously compiled!")
Expand Down Expand Up @@ -107,7 +107,7 @@ pub struct PatternExpression {
pub value: String,

#[serde(skip_deserializing)]
path: OnceCell<Path>,
path: OnceCell<PropertyPath>,
#[serde(skip_deserializing)]
compiled: OnceCell<CelExpression>,
}
Expand All @@ -121,23 +121,22 @@ impl PatternExpression {
.set(self.try_into()?)
.map_err(|_| "Ooops".to_string())
}
pub fn path(&self) -> Vec<&str> {
pub fn path(&self) -> &PropertyPath {
self.path
.get()
.expect("PatternExpression wasn't previously compiled!")
.tokens()
}

pub fn eval(&self, raw_attribute: Vec<u8>) -> Result<bool, String> {
let cel_type = &self.compiled.get().unwrap().cel_type;
let value = match cel_type {
ValueType::String => Value::String(Arc::new(Attribute::parse(raw_attribute)?)),
ValueType::Int => Value::Int(Attribute::parse(raw_attribute)?),
ValueType::UInt => Value::UInt(Attribute::parse(raw_attribute)?),
ValueType::Float => Value::Float(Attribute::parse(raw_attribute)?),
ValueType::Bytes => Value::Bytes(Arc::new(Attribute::parse(raw_attribute)?)),
ValueType::Bool => Value::Bool(Attribute::parse(raw_attribute)?),
ValueType::Timestamp => Value::Timestamp(Attribute::parse(raw_attribute)?),
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 => {}
Expand All @@ -157,8 +156,7 @@ impl PatternExpression {
}

fn applies(&self) -> bool {
let attribute_path = self.path();
let attribute_value = match crate::property::get_property(attribute_path).unwrap() {
let attribute_value = match crate::data::get_property(self.path()).unwrap() {
//TODO(didierofrivia): Replace hostcalls by DI
None => {
debug!(
Expand All @@ -169,6 +167,11 @@ impl PatternExpression {
}
Some(attribute_bytes) => attribute_bytes,
};

// if someone would have the P_E be:
eguzki marked this conversation as resolved.
Show resolved Hide resolved
// selector: auth.identity.anonymous
// operator: eq
// value: \""true"\"
self.eval(attribute_value).unwrap_or_else(|e| {
debug!("pattern_expression_applies failed: {}", e);
false
Expand Down Expand Up @@ -443,13 +446,31 @@ impl TryFrom<PluginConfiguration> for FilterConfig {
return Err(result.err().unwrap());
}
}
let mut predicates = Vec::default();
for predicate in &action_set.route_rule_conditions.predicates {
predicates.push(Predicate::new(predicate).map_err(|e| e.to_string())?);
}
action_set
.route_rule_conditions
.compiled_predicates
.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())?);
}
action
.compiled_predicates
.set(predicates)
.expect("Predicates must not be compiled yet!");

for datum in &action.data {
let result = datum.item.compile();
if result.is_err() {
Expand Down
33 changes: 19 additions & 14 deletions src/configuration/action.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::attribute::Attribute;
use crate::configuration::{DataItem, DataType, PatternExpression};
use crate::data::Predicate;
use crate::envoy::{RateLimitDescriptor, RateLimitDescriptor_Entry};
use log::debug;
use protobuf::RepeatedField;
use serde::Deserialize;
use std::cell::OnceCell;

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
Expand All @@ -13,12 +14,24 @@ pub struct Action {
#[serde(default)]
pub conditions: Vec<PatternExpression>,
#[serde(default)]
pub predicates: Vec<String>,
#[serde(skip_deserializing)]
pub compiled_predicates: OnceCell<Vec<Predicate>>,
#[serde(default)]
pub data: Vec<DataItem>,
}

impl Action {
pub fn conditions_apply(&self) -> bool {
self.conditions.is_empty() || self.conditions.iter().all(|m| m.applies())
let predicates = self
.compiled_predicates
.get()
.expect("predicates must be compiled by now");
if predicates.is_empty() {
self.conditions.is_empty() || self.conditions.iter().all(PatternExpression::applies)
eguzki marked this conversation as resolved.
Show resolved Hide resolved
} else {
predicates.iter().all(Predicate::test)
}
}

pub fn build_descriptors(&self) -> RepeatedField<RateLimitDescriptor> {
Expand Down Expand Up @@ -47,15 +60,14 @@ impl Action {
Some(key) => key.to_owned(),
};

let attribute_path = selector_item.path();
let value = match crate::property::get_property(attribute_path.tokens())
.unwrap()
let value = match crate::data::get_attribute::<String>(selector_item.path())
.expect("Error!")
{
//TODO(didierofrivia): Replace hostcalls by DI
None => {
debug!(
"build_single_descriptor: selector not found: {}",
attribute_path
selector_item.path()
);
match &selector_item.default {
None => return None, // skipping the entire descriptor
Expand All @@ -64,14 +76,7 @@ impl Action {
}
// TODO(eastizle): not all fields are strings
// https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/advanced/attributes
Some(attribute_bytes) => match Attribute::parse(attribute_bytes) {
Ok(attr_str) => attr_str,
Err(e) => {
debug!("build_single_descriptor: failed to parse selector value: {}, error: {}",
attribute_path, e);
return None;
}
},
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: {}",
Expand Down
27 changes: 21 additions & 6 deletions src/configuration/action_set.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
use crate::configuration::action::Action;
use crate::configuration::PatternExpression;
use crate::data::Predicate;
use serde::Deserialize;
use std::cell::OnceCell;

#[derive(Deserialize, Debug, Clone, Default)]
pub struct RouteRuleConditions {
pub hostnames: Vec<String>,
#[serde(default)]
pub matches: Vec<PatternExpression>,
#[serde(default)]
pub predicates: Vec<String>,
#[serde(skip_deserializing)]
pub compiled_predicates: OnceCell<Vec<Predicate>>,
}

#[derive(Default, Deserialize, Debug, Clone)]
Expand All @@ -32,11 +38,20 @@ impl ActionSet {
}

pub fn conditions_apply(&self) -> bool {
self.route_rule_conditions.matches.is_empty()
|| self
.route_rule_conditions
.matches
.iter()
.all(|m| m.applies())
let predicates = self
.route_rule_conditions
.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.iter().all(Predicate::test)
}
}
}
Loading
Loading