Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
RWDai committed Apr 25, 2024
1 parent e7e4786 commit 31304ef
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 139 deletions.
218 changes: 129 additions & 89 deletions crates/config/src/service/k8s/convert/filter_k8s_conv.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::collections::HashMap;

use k8s_gateway_api::{HttpHeader, HttpPathModifier, HttpRequestHeaderFilter, HttpRequestRedirectFilter, HttpRouteFilter, HttpUrlRewriteFilter};
use kube::Api;
use spacegate_model::ext::k8s::crd::sg_filter::SgFilter;
use k8s_gateway_api::{HttpHeader, HttpPathModifier, HttpRequestHeaderFilter, HttpRequestRedirectFilter, HttpRouteFilter, HttpUrlRewriteFilter, LocalObjectReference};
use kube::{Api, ResourceExt};
use spacegate_model::{constants::SG_FILTER_KIND, ext::k8s::crd::sg_filter::SgFilter};

use crate::{
ext::k8s::{
Expand All @@ -21,8 +21,6 @@ use crate::{
};

pub(crate) trait PluginIdConv {
fn from_http_route_filter(route_filter: HttpRouteFilter) -> BoxResult<PluginConfig>;

/// # to_singe_filter
/// `to_single_filter` method and [SgRouteFilter::to_http_route_filter] method both convert from
/// `SgRouteFilter`to the k8s model. The difference lies in that `to_single_filter` only includes
Expand All @@ -32,13 +30,17 @@ pub(crate) trait PluginIdConv {

/// # to_http_route_filter
/// ref [SgRouteFilter::to_singe_filter]
/// can be use in rule level and backend level
async fn to_http_route_filter(self, client: &K8s) -> Option<HttpRouteFilter>;

fn from_http_route_filter(route_filter: HttpRouteFilter) -> Option<PluginInstanceId>;

/// can be ues in gateway and route level
async fn add_filter_target(&self, target: K8sSgFilterSpecTargetRef, client: &K8s);

/// mix of [SgRouteFilter::to_singe_filter] and [SgRouteFilter::to_http_route_filter]
/// PluginInstanceId can be converted into `SgRouteFilter` or `HttpRouteFilter`
async fn to_route_filter_or_add_filter_target(&self, target: K8sSgFilterSpecTargetRef, client: &K8s) -> Option<HttpRouteFilter>;
// mix of [SgRouteFilter::to_singe_filter] and [SgRouteFilter::to_http_route_filter]
// PluginInstanceId can be converted into `SgRouteFilter` or `HttpRouteFilter`
// async fn to_route_filter_or_add_filter_target(&self, target: K8sSgFilterSpecTargetRef, client: &K8s) -> Option<HttpRouteFilter>;
}

impl PluginIdConv for PluginInstanceId {
Expand Down Expand Up @@ -109,7 +111,17 @@ impl PluginIdConv for PluginInstanceId {
None
}
} else {
None
match self.name {
PluginInstanceName::Anon { uid: _ } => None,
PluginInstanceName::Named { name } => Some(HttpRouteFilter::ExtensionRef {
extension_ref: LocalObjectReference {
group: "".to_string(),
kind: SG_FILTER_KIND.to_string(),
name,
},
}),
PluginInstanceName::Mono => None,
}
}
} else {
None
Expand All @@ -119,92 +131,120 @@ impl PluginIdConv for PluginInstanceId {
}
}

fn from_http_route_filter(route_filter: HttpRouteFilter) -> BoxResult<PluginConfig> {
let process_header_modifier = |header_modifier: HttpRequestHeaderFilter, modifier_kind: SgFilterHeaderModifierKind| -> BoxResult<PluginConfig> {
let mut sg_sets = HashMap::new();
if let Some(adds) = header_modifier.add {
for add in adds {
sg_sets.insert(add.name, add.value);
}
}
if let Some(sets) = header_modifier.set {
for set in sets {
sg_sets.insert(set.name, set.value);
}
}
// fn from_http_route_filter(route_filter: HttpRouteFilter) -> BoxResult<PluginConfig> {
// let process_header_modifier = |header_modifier: HttpRequestHeaderFilter, modifier_kind: SgFilterHeaderModifierKind| -> BoxResult<PluginConfig> {
// let mut sg_sets = HashMap::new();
// if let Some(adds) = header_modifier.add {
// for add in adds {
// sg_sets.insert(add.name, add.value);
// }
// }
// if let Some(sets) = header_modifier.set {
// for set in sets {
// sg_sets.insert(set.name, set.value);
// }
// }

Ok(PluginConfig {
id: PluginInstanceId {
code: SG_FILTER_HEADER_MODIFIER_CODE.into(),
name: PluginInstanceName::Mono {},
},
spec: serde_json::to_value(SgFilterHeaderModifier {
kind: modifier_kind,
sets: if sg_sets.is_empty() { None } else { Some(sg_sets) },
remove: header_modifier.remove,
})?,
})
};
let sg_filter = match route_filter {
k8s_gateway_api::HttpRouteFilter::RequestHeaderModifier { request_header_modifier } => {
process_header_modifier(request_header_modifier, SgFilterHeaderModifierKind::Request)?
}
k8s_gateway_api::HttpRouteFilter::ResponseHeaderModifier { response_header_modifier } => {
process_header_modifier(response_header_modifier, SgFilterHeaderModifierKind::Response)?
}
k8s_gateway_api::HttpRouteFilter::RequestRedirect { request_redirect } => PluginConfig {
id: PluginInstanceId {
code: SG_FILTER_REDIRECT_CODE.into(),
name: PluginInstanceName::Mono {},
},
spec: serde_json::to_value(SgFilterRedirect {
scheme: request_redirect.scheme,
hostname: request_redirect.hostname,
path: request_redirect.path.map(|path| match path {
k8s_gateway_api::HttpPathModifier::ReplaceFullPath { replace_full_path } => SgHttpPathModifier {
kind: SgHttpPathModifierType::ReplaceFullPath,
value: replace_full_path,
},
k8s_gateway_api::HttpPathModifier::ReplacePrefixMatch { replace_prefix_match } => SgHttpPathModifier {
kind: SgHttpPathModifierType::ReplacePrefixMatch,
value: replace_prefix_match,
},
}),
port: request_redirect.port,
status_code: request_redirect.status_code,
})?,
},
k8s_gateway_api::HttpRouteFilter::URLRewrite { url_rewrite } => PluginConfig {
id: PluginInstanceId {
code: SG_FILTER_REWRITE_CODE.into(),
name: PluginInstanceName::Mono {},
},
spec: serde_json::to_value(SgFilterRewrite {
hostname: url_rewrite.hostname,
path: url_rewrite.path.map(|path| match path {
k8s_gateway_api::HttpPathModifier::ReplaceFullPath { replace_full_path } => SgHttpPathModifier {
kind: SgHttpPathModifierType::ReplaceFullPath,
value: replace_full_path,
},
k8s_gateway_api::HttpPathModifier::ReplacePrefixMatch { replace_prefix_match } => SgHttpPathModifier {
kind: SgHttpPathModifierType::ReplacePrefixMatch,
value: replace_prefix_match,
},
}),
})?,
},
k8s_gateway_api::HttpRouteFilter::RequestMirror { .. } => return Err("[SG.Common] HttpRoute [spec.rules.filters.type=RequestMirror] not supported yet".into()),
k8s_gateway_api::HttpRouteFilter::ExtensionRef { .. } => return Err("[SG.Common] HttpRoute [spec.rules.filters.type=ExtensionRef] not supported yet".into()),
};
Ok(sg_filter)
}
// Ok(PluginConfig {
// id: PluginInstanceId {
// code: SG_FILTER_HEADER_MODIFIER_CODE.into(),
// name: PluginInstanceName::Mono {},
// },
// spec: serde_json::to_value(SgFilterHeaderModifier {
// kind: modifier_kind,
// sets: if sg_sets.is_empty() { None } else { Some(sg_sets) },
// remove: header_modifier.remove,
// })?,
// })
// };
// let sg_filter = match route_filter {
// k8s_gateway_api::HttpRouteFilter::RequestHeaderModifier { request_header_modifier } => {
// process_header_modifier(request_header_modifier, SgFilterHeaderModifierKind::Request)?
// }
// k8s_gateway_api::HttpRouteFilter::ResponseHeaderModifier { response_header_modifier } => {
// process_header_modifier(response_header_modifier, SgFilterHeaderModifierKind::Response)?
// }
// k8s_gateway_api::HttpRouteFilter::RequestRedirect { request_redirect } => PluginConfig {
// id: PluginInstanceId {
// code: SG_FILTER_REDIRECT_CODE.into(),
// name: PluginInstanceName::Mono {},
// },
// spec: serde_json::to_value(SgFilterRedirect {
// scheme: request_redirect.scheme,
// hostname: request_redirect.hostname,
// path: request_redirect.path.map(|path| match path {
// k8s_gateway_api::HttpPathModifier::ReplaceFullPath { replace_full_path } => SgHttpPathModifier {
// kind: SgHttpPathModifierType::ReplaceFullPath,
// value: replace_full_path,
// },
// k8s_gateway_api::HttpPathModifier::ReplacePrefixMatch { replace_prefix_match } => SgHttpPathModifier {
// kind: SgHttpPathModifierType::ReplacePrefixMatch,
// value: replace_prefix_match,
// },
// }),
// port: request_redirect.port,
// status_code: request_redirect.status_code,
// })?,
// },
// k8s_gateway_api::HttpRouteFilter::URLRewrite { url_rewrite } => PluginConfig {
// id: PluginInstanceId {
// code: SG_FILTER_REWRITE_CODE.into(),
// name: PluginInstanceName::Mono {},
// },
// spec: serde_json::to_value(SgFilterRewrite {
// hostname: url_rewrite.hostname,
// path: url_rewrite.path.map(|path| match path {
// k8s_gateway_api::HttpPathModifier::ReplaceFullPath { replace_full_path } => SgHttpPathModifier {
// kind: SgHttpPathModifierType::ReplaceFullPath,
// value: replace_full_path,
// },
// k8s_gateway_api::HttpPathModifier::ReplacePrefixMatch { replace_prefix_match } => SgHttpPathModifier {
// kind: SgHttpPathModifierType::ReplacePrefixMatch,
// value: replace_prefix_match,
// },
// }),
// })?,
// },
// k8s_gateway_api::HttpRouteFilter::RequestMirror { .. } => return Err("[SG.Common] HttpRoute [spec.rules.filters.type=RequestMirror] not supported yet".into()),
// k8s_gateway_api::HttpRouteFilter::ExtensionRef { .. } => return Err("[SG.Common] HttpRoute [spec.rules.filters.type=ExtensionRef] not supported yet".into()),
// };
// Ok(sg_filter)
// }

async fn add_filter_target(&self, target: K8sSgFilterSpecTargetRef, client: &K8s) {
todo!()
}

async fn to_route_filter_or_add_filter_target(&self, target: K8sSgFilterSpecTargetRef, client: &K8s) -> Option<HttpRouteFilter> {
todo!()
fn from_http_route_filter(route_filter: HttpRouteFilter) -> Option<PluginInstanceId> {
match route_filter {
HttpRouteFilter::RequestHeaderModifier { request_header_modifier } => None,
HttpRouteFilter::ResponseHeaderModifier { response_header_modifier } => None,
HttpRouteFilter::RequestMirror { request_mirror } => None,
HttpRouteFilter::RequestRedirect { request_redirect } => None,
HttpRouteFilter::URLRewrite { url_rewrite } => None,
HttpRouteFilter::ExtensionRef { extension_ref } => Some(PluginInstanceId {
code: extension_ref.kind.into(),
name: PluginInstanceName::Named { name: extension_ref.name },
}),
}
}
}

pub(crate) trait PluginConfigConv {
fn from_first_filter_obj(filter_obj: SgFilter) -> Option<PluginConfig>;
}

impl PluginConfigConv for PluginConfig {
fn from_first_filter_obj(filter_obj: SgFilter) -> Option<PluginConfig> {
filter_obj.spec.filters.into_iter().filter(|f| f.enable).next().map(|f| PluginConfig {
id: PluginInstanceId {
code: f.code.into(),
name: PluginInstanceName::Named {
name: f.name.unwrap_or(filter_obj.name_any()),
},
},
spec: f.config,
})
}
}

Expand Down
53 changes: 46 additions & 7 deletions crates/config/src/service/k8s/convert/route_k8s_conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,48 @@ impl SgHttpRouteRuleConv for SgHttpRouteRule {
}

fn from_kube_httproute(rule: http_spaceroute::HttpRouteRule) -> BoxResult<SgHttpRouteRule> {
let (ext_plugins, legacy_plugins): (Vec<_>, Vec<_>) =
rule.filters.map(|f_vec| f_vec.into_iter().partition(|f| matches!(f, HttpRouteFilter::ExtensionRef { extension_ref: _ }))).unwrap_or_default();
let matches = if let Some(mut matches) = rule.matches {
if matches.len() > 1 {
if legacy_plugins.iter().find(|p| matches!(p, HttpRouteFilter::URLRewrite { url_rewrite: _ })).is_some() {
return Err("url_rewrite is not supported with multiple matches".into());
}
if legacy_plugins.iter().find(|p| matches!(p, HttpRouteFilter::RequestHeaderModifier { request_header_modifier: _ })).is_some() {
return Err("request_header_modifier is not supported with multiple matches".into());
}
Some(matches.into_iter().map(|m| SgHttpRouteMatch::from_kube_httproute(m)).collect::<Vec<_>>())
} else if let Some(match_) = matches.pop() {
let mut m = SgHttpRouteMatch::from_kube_httproute(match_);
if legacy_plugins.iter().filter(|p| matches!(p, HttpRouteFilter::URLRewrite { url_rewrite: _ })).count() > 1 {
return Err("url_rewrite can only have one in each rule".into());
} else if let Some(url_rewrite) = legacy_plugins.iter().find(|p| matches!(p, HttpRouteFilter::URLRewrite { url_rewrite: _ })) {
m.path.map(|m_p| match url_rewrite {
HttpRouteFilter::URLRewrite { url_rewrite } => {
if let Some(rewrite_path) = url_rewrite.path {
match m_p {
SgHttpPathMatch::Exact { value, replace } => match rewrite_path {
HttpPathModifier::ReplaceFullPath { replace_full_path } => todo!(),
HttpPathModifier::ReplacePrefixMatch { replace_prefix_match } => todo!(),
},
SgHttpPathMatch::Prefix { value, replace } => todo!(),
SgHttpPathMatch::RegExp { value, replace } => todo!(),
}
}
}
_ => unreachable!(),
});
}
Some(vec![m])
} else {
Some(vec![])
}
} else {
None
};
Ok(SgHttpRouteRule {
matches: rule.matches.map(|m_vec| m_vec.into_iter().map(SgHttpRouteMatch::from_kube_httproute).collect::<Vec<_>>()),
plugins: rule.filters.map(|f_vec| f_vec.into_iter().map(PluginConfig::from_http_route_filter).collect::<BoxResult<Vec<_>>>()).transpose()?.unwrap_or_default(),
matches,
plugins: ext_plugins.into_iter().filter_map(|f| PluginInstanceId::from_http_route_filter(f)).collect(),
backends: rule
.backend_refs
.map(|b_vec| b_vec.into_iter().filter_map(|b| SgBackendRef::from_kube_httproute(b).transpose()).collect::<BoxResult<Vec<_>>>())
Expand Down Expand Up @@ -271,8 +310,8 @@ impl SgBackendRefConv for SgBackendRef {
let backend_inner_ref = match self.host {
BackendHost::Host { host } => {
let kind = match self.protocol {
Some(SgBackendProtocol::Https) => Some(constants::BANCKEND_KIND_EXTERNAL_HTTPS.to_string()),
_ => Some(constants::BANCKEND_KIND_EXTERNAL_HTTP.to_string()),
Some(SgBackendProtocol::Https) => Some(constants::BACKEND_KIND_EXTERNAL_HTTPS.to_string()),
_ => Some(constants::BACKEND_KIND_EXTERNAL_HTTP.to_string()),
};
BackendObjectReference {
group: None,
Expand Down Expand Up @@ -306,15 +345,15 @@ impl SgBackendRefConv for SgBackendRef {
.map(|backend| {
let (protocol, backend_host) = if let Some(kind) = backend.inner.kind.as_ref() {
match kind.as_str() {
constants::BANCKEND_KIND_SERVICE => (
constants::BACKEND_KIND_SERVICE => (
None,
BackendHost::K8sService(K8sServiceData {
name: backend.inner.name,
namespace: backend.inner.namespace,
}),
),
constants::BANCKEND_KIND_EXTERNAL_HTTP => (Some(gateway::SgBackendProtocol::Http), BackendHost::Host { host: backend.inner.name }),
constants::BANCKEND_KIND_EXTERNAL_HTTPS => (Some(gateway::SgBackendProtocol::Https), BackendHost::Host { host: backend.inner.name }),
constants::BACKEND_KIND_EXTERNAL_HTTP => (Some(gateway::SgBackendProtocol::Http), BackendHost::Host { host: backend.inner.name }),
constants::BACKEND_KIND_EXTERNAL_HTTPS => (Some(gateway::SgBackendProtocol::Https), BackendHost::Host { host: backend.inner.name }),
_ => (None, BackendHost::Host { host: backend.inner.name }),
}
} else {
Expand Down
Loading

0 comments on commit 31304ef

Please sign in to comment.