Skip to content

Commit

Permalink
Merge pull request #78 from Kuadrant/service-from-config
Browse files Browse the repository at this point in the history
Setup services from configuration
  • Loading branch information
adam-cattermole authored Aug 29, 2024
2 parents 870cafd + a78e962 commit ed8eec1
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 104 deletions.
119 changes: 91 additions & 28 deletions src/configuration.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::cell::OnceCell;
use std::collections::HashMap;
use std::fmt::{Debug, Display, Formatter};
use std::rc::Rc;
use std::sync::Arc;

use cel_interpreter::objects::ValueType;
Expand All @@ -10,6 +12,7 @@ use serde::Deserialize;
use crate::attribute::Attribute;
use crate::policy::Policy;
use crate::policy_index::PolicyIndex;
use crate::service::GrpcService;

#[derive(Deserialize, Debug, Clone)]
pub struct SelectorItem {
Expand Down Expand Up @@ -439,15 +442,14 @@ pub fn type_of(path: &str) -> Option<ValueType> {

pub struct FilterConfig {
pub index: PolicyIndex,
// Deny/Allow request when faced with an irrecoverable failure.
pub failure_mode: FailureMode,
pub services: Rc<HashMap<String, Rc<GrpcService>>>,
}

impl Default for FilterConfig {
fn default() -> Self {
Self {
index: PolicyIndex::new(),
failure_mode: FailureMode::Deny,
services: Rc::new(HashMap::new()),
}
}
}
Expand Down Expand Up @@ -480,16 +482,33 @@ impl TryFrom<PluginConfiguration> for FilterConfig {
}
}

// configure grpc services from the extensions in config
let services = config
.extensions
.into_iter()
.map(|(name, ext)| {
(
name,
Rc::new(GrpcService::new(
ext.extension_type,
ext.endpoint,
ext.failure_mode,
)),
)
})
.collect();

Ok(Self {
index,
failure_mode: config.failure_mode,
services: Rc::new(services),
})
}
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "lowercase")]
pub enum FailureMode {
#[default]
Deny,
Allow,
}
Expand All @@ -504,8 +523,16 @@ pub enum ExtensionType {
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct PluginConfiguration {
#[serde(rename = "rateLimitPolicies")]
pub extensions: HashMap<String, Extension>,
pub policies: Vec<Policy>,
}

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Extension {
#[serde(rename = "type")]
pub extension_type: ExtensionType,
pub endpoint: String,
// Deny/Allow request when faced with an irrecoverable failure.
pub failure_mode: FailureMode,
}
Expand All @@ -515,12 +542,17 @@ mod test {
use super::*;

const CONFIG: &str = r#"{
"failureMode": "deny",
"rateLimitPolicies": [
"extensions": {
"limitador": {
"type": "ratelimit",
"endpoint": "limitador-cluster",
"failureMode": "deny"
}
},
"policies": [
{
"name": "rlp-ns-A/rlp-name-A",
"domain": "rlp-ns-A/rlp-name-A",
"service": "limitador-cluster",
"hostnames": ["*.toystore.com", "example.com"],
"rules": [
{
Expand Down Expand Up @@ -612,8 +644,8 @@ mod test {
#[test]
fn parse_config_min() {
let config = r#"{
"failureMode": "deny",
"rateLimitPolicies": []
"extensions": {},
"policies": []
}"#;
let res = serde_json::from_str::<PluginConfiguration>(config);
if let Err(ref e) = res {
Expand All @@ -628,12 +660,17 @@ mod test {
#[test]
fn parse_config_data_selector() {
let config = r#"{
"failureMode": "deny",
"rateLimitPolicies": [
"extensions": {
"limitador": {
"type": "ratelimit",
"endpoint": "limitador-cluster",
"failureMode": "deny"
}
},
"policies": [
{
"name": "rlp-ns-A/rlp-name-A",
"domain": "rlp-ns-A/rlp-name-A",
"service": "limitador-cluster",
"hostnames": ["*.toystore.com", "example.com"],
"rules": [
{
Expand Down Expand Up @@ -678,12 +715,17 @@ mod test {
#[test]
fn parse_config_condition_selector_operators() {
let config = r#"{
"failureMode": "deny",
"rateLimitPolicies": [
"extensions": {
"limitador": {
"type": "ratelimit",
"endpoint": "limitador-cluster",
"failureMode": "deny"
}
},
"policies": [
{
"name": "rlp-ns-A/rlp-name-A",
"domain": "rlp-ns-A/rlp-name-A",
"service": "limitador-cluster",
"hostnames": ["*.toystore.com", "example.com"],
"rules": [
{
Expand Down Expand Up @@ -757,12 +799,17 @@ mod test {
#[test]
fn parse_config_conditions_optional() {
let config = r#"{
"failureMode": "deny",
"rateLimitPolicies": [
"extensions": {
"limitador": {
"type": "ratelimit",
"endpoint": "limitador-cluster",
"failureMode": "deny"
}
},
"policies": [
{
"name": "rlp-ns-A/rlp-name-A",
"domain": "rlp-ns-A/rlp-name-A",
"service": "limitador-cluster",
"hostnames": ["*.toystore.com", "example.com"],
"rules": [
{
Expand Down Expand Up @@ -801,12 +848,17 @@ mod test {
fn parse_config_invalid_data() {
// data item fields are mutually exclusive
let bad_config = r#"{
"failureMode": "deny",
"rateLimitPolicies": [
"extensions": {
"limitador": {
"type": "ratelimit",
"endpoint": "limitador-cluster",
"failureMode": "deny"
}
},
"policies": [
{
"name": "rlp-ns-A/rlp-name-A",
"domain": "rlp-ns-A/rlp-name-A",
"service": "limitador-cluster",
"hostnames": ["*.toystore.com", "example.com"],
"rules": [
{
Expand All @@ -828,8 +880,14 @@ mod test {

// data item unknown fields are forbidden
let bad_config = r#"{
"failureMode": "deny",
"rateLimitPolicies": [
"extensions": {
"limitador": {
"type": "ratelimit",
"endpoint": "limitador-cluster",
"failureMode": "deny"
}
},
"policies": [
{
"name": "rlp-ns-A/rlp-name-A",
"domain": "rlp-ns-A/rlp-name-A",
Expand All @@ -852,12 +910,17 @@ mod test {

// condition selector operator unknown
let bad_config = r#"{
"failureMode": "deny",
"rateLimitPolicies": [
"extensions": {
"limitador": {
"type": "ratelimit",
"endpoint": "limitador-cluster",
"failureMode": "deny"
}
},
"policies": [
{
"name": "rlp-ns-A/rlp-name-A",
"domain": "rlp-ns-A/rlp-name-A",
"service": "limitador-cluster",
"hostnames": ["*.toystore.com", "example.com"],
"rules": [
{
Expand Down
31 changes: 22 additions & 9 deletions src/filter/http_context.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::configuration::{ExtensionType, FailureMode, FilterConfig};
use crate::configuration::{FailureMode, FilterConfig};
use crate::envoy::{RateLimitResponse, RateLimitResponse_Code};
use crate::policy::Policy;
use crate::service::rate_limit::RateLimitService;
Expand Down Expand Up @@ -40,14 +40,19 @@ impl Filter {
return Action::Continue;
}

let rls = GrpcServiceHandler::new(
ExtensionType::RateLimit,
rlp.service.clone(),
Rc::clone(&self.header_resolver),
);
// todo(adam-cattermole): For now we just get the first GrpcService but we expect to have
// an action which links to the service that should be used
let rls = self
.config
.services
.values()
.next()
.expect("expect a value");

let handler = GrpcServiceHandler::new(Rc::clone(rls), Rc::clone(&self.header_resolver));
let message = RateLimitService::message(rlp.domain.clone(), descriptors);

match rls.send(message) {
match handler.send(message) {
Ok(call_id) => {
debug!(
"#{} initiated gRPC call (id# {}) to Limitador",
Expand All @@ -57,7 +62,7 @@ impl Filter {
}
Err(e) => {
warn!("gRPC call to Limitador failed! {e:?}");
if let FailureMode::Deny = self.config.failure_mode {
if let FailureMode::Deny = rls.failure_mode() {
self.send_http_response(500, vec![], Some(b"Internal Server Error.\n"))
}
Action::Continue
Expand All @@ -66,7 +71,15 @@ impl Filter {
}

fn handle_error_on_grpc_response(&self) {
match &self.config.failure_mode {
// todo(adam-cattermole): We need a method of knowing which service is the one currently
// being used (the current action) so that we can get the failure mode
let rls = self
.config
.services
.values()
.next()
.expect("expect a value");
match rls.failure_mode() {
FailureMode::Deny => {
self.send_http_response(500, vec![], Some(b"Internal Server Error.\n"))
}
Expand Down
10 changes: 1 addition & 9 deletions src/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,16 @@ pub struct Rule {
pub struct Policy {
pub name: String,
pub domain: String,
pub service: String,
pub hostnames: Vec<String>,
pub rules: Vec<Rule>,
}

impl Policy {
#[cfg(test)]
pub fn new(
name: String,
domain: String,
service: String,
hostnames: Vec<String>,
rules: Vec<Rule>,
) -> Self {
pub fn new(name: String, domain: String, hostnames: Vec<String>, rules: Vec<Rule>) -> Self {
Policy {
name,
domain,
service,
hostnames,
rules,
}
Expand Down
8 changes: 1 addition & 7 deletions src/policy_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,7 @@ mod tests {
use crate::policy_index::PolicyIndex;

fn build_ratelimit_policy(name: &str) -> Policy {
Policy::new(
name.to_owned(),
"".to_owned(),
"".to_owned(),
Vec::new(),
Vec::new(),
)
Policy::new(name.to_owned(), "".to_owned(), Vec::new(), Vec::new())
}

#[test]
Expand Down
Loading

0 comments on commit ed8eec1

Please sign in to comment.