diff --git a/admin/src/helper.rs b/admin/src/helper.rs index 3c2ce795..497081b0 100644 --- a/admin/src/helper.rs +++ b/admin/src/helper.rs @@ -5,6 +5,7 @@ use tardis::basic::result::TardisResult; use tardis::regex; use tardis::regex::Regex; +//todo 使用这个get_k8s_client还是common里的? #[cfg(feature = "k8s")] pub async fn get_k8s_client() -> TardisResult { Client::try_default().await.map_err(|error| TardisError::wrap(&format!("[SG.admin] Get kubernetes client error: {error:?}"), "")) diff --git a/admin/src/service/route_service.rs b/admin/src/service/route_service.rs index 9094a4c4..27bff20f 100644 --- a/admin/src/service/route_service.rs +++ b/admin/src/service/route_service.rs @@ -1,9 +1,17 @@ +use crate::helper::get_k8s_client; use crate::model::query_dto::HttpRouteQueryInst; use crate::model::vo::http_route_vo::SgHttpRouteVo; use crate::model::vo::Vo; use crate::model::vo_converter::VoConv; use crate::service::base_service::VoBaseService; -use kernel_common::helper::k8s_helper::{format_k8s_obj_unique, parse_k8s_unique_or_default}; +use crate::service::plugin_service::PluginK8sService; +use k8s_gateway_api::Gateway; +use kernel_common::converter::plugin_k8s_conv::SgSingeFilter; +use kernel_common::helper::k8s_helper::{format_k8s_obj_unique, parse_k8s_obj_unique, parse_k8s_unique_or_default, WarpKubeResult}; +use kernel_common::k8s_crd::http_spaceroute::HttpSpaceroute; +use kube::api::{DeleteParams, PostParams}; +use kube::Api; +use std::collections::HashSet; use tardis::basic::result::TardisResult; pub struct HttpRouteVoService; @@ -34,7 +42,7 @@ impl HttpRouteVoService { ) } - pub(crate) async fn add(mut add: SgHttpRouteVo) -> TardisResult<()> { + pub(crate) async fn add(mut add: SgHttpRouteVo) -> TardisResult { #[cfg(feature = "k8s")] { let (namespace, raw_nmae) = parse_k8s_unique_or_default(&add.get_unique_name()); @@ -43,18 +51,66 @@ impl HttpRouteVoService { let add_model = add.clone().to_model().await?; #[cfg(feature = "k8s")] { - add_model + let (namespace, _) = parse_k8s_unique_or_default(&add.get_unique_name()); + let (httproute, sgfilters) = add_model.to_kube_httproute(); + let http_route_api: Api = Api::namespaced(get_k8s_client().await?, &namespace); + + let _ = http_route_api.create(&PostParams::default(), &httproute).await.warp_result_by_method("Add HttpSpaceroute")?; + + PluginK8sService::add_sgfilter_vec(&sgfilters.iter().collect::>()).await? } - Self::add_vo(add).await?; - Ok(()) + Ok(Self::add_vo(add).await?) } - pub(crate) async fn update(update: SgHttpRouteVo) -> TardisResult<()> { - Self::update_vo(update).await?; - Ok(()) + pub(crate) async fn update(update: SgHttpRouteVo) -> TardisResult { + let update_un = &update.get_unique_name(); + + let update_sg_httproute = update.clone().to_model().await?; + let old_sg_httproute = Self::get_by_id(&update.name).await?.to_model().await?; + #[cfg(feature = "k8s")] + { + let (namespace, name) = parse_k8s_obj_unique(update_un); + let http_route_api: Api = Api::namespaced(get_k8s_client().await?, &namespace); + let (update_httproute, update_filter) = update_sg_httproute.to_kube_httproute(); + http_route_api.replace(&name, &PostParams::default(), &update_httproute).await.warp_result_by_method("Replace HttpSpaceroute")?; + + Self::update_httproute_filter(old_sg_httproute.to_kube_httproute().1, update_filter).await?; + } + Ok(Self::update_vo(update).await?) } pub(crate) async fn delete(id: &str) -> TardisResult<()> { - Self::delete_vo(&id).await?; + let (namespace, name) = parse_k8s_obj_unique(id); + #[cfg(feature = "k8s")] + { + let http_route_api: Api = Api::namespaced(get_k8s_client().await?, &namespace); + + http_route_api.delete(&name, &DeleteParams::default()).await.warp_result_by_method("Delete HttpSpaceroute")?; + + let old_sg_httproute = Self::get_by_id(&id).await?.to_model().await?; + let (_, f_v) = old_sg_httproute.to_kube_httproute(); + PluginK8sService::delete_sgfilter_vec(&f_v.iter().collect::>()).await?; + } + Self::delete_vo(id).await?; + Ok(()) + } + + //todo 和gateway_service 里的那个合并 + #[cfg(feature = "k8s")] + async fn update_httproute_filter(old: Vec, update: Vec) -> TardisResult<()> { + if old.is_empty() && update.is_empty() { + return Ok(()); + } + + let old_set: HashSet<_> = old.into_iter().collect(); + let update_set: HashSet<_> = update.into_iter().collect(); + + let update_vec: Vec<_> = old_set.intersection(&update_set).collect(); + PluginK8sService::update_sgfilter_vec(&update_vec).await?; + let add_vec: Vec<_> = update_set.difference(&old_set).collect(); + PluginK8sService::add_sgfilter_vec(&add_vec).await?; + let delete_vec: Vec<_> = old_set.difference(&update_set).collect(); + PluginK8sService::delete_sgfilter_vec(&delete_vec).await?; + Ok(()) } } diff --git a/kernel-common/src/constants.rs b/kernel-common/src/constants.rs index 9ddb5e8f..88a62449 100644 --- a/kernel-common/src/constants.rs +++ b/kernel-common/src/constants.rs @@ -4,3 +4,7 @@ pub mod k8s_constants; pub const RAW_HTTP_ROUTE_KIND: &str = "raw.http.route.kind"; pub const RAW_HTTP_ROUTE_KIND_DEFAULT: &str = "HTTPRoute"; pub const RAW_HTTP_ROUTE_KIND_SPACEROUTE: &str = "HTTPSpaceroute"; + +pub const BANCKEND_KIND_EXTERNAL: &str = "External"; +pub const BANCKEND_KIND_EXTERNAL_HTTP: &str = "ExternalHttp"; +pub const BANCKEND_KIND_EXTERNAL_HTTPS: &str = "ExternalHttps"; diff --git a/kernel-common/src/converter/http_route_k8s_conv.rs b/kernel-common/src/converter/http_route_k8s_conv.rs index e1bcbc17..7cdd86bc 100644 --- a/kernel-common/src/converter/http_route_k8s_conv.rs +++ b/kernel-common/src/converter/http_route_k8s_conv.rs @@ -1,5 +1,7 @@ +use crate::constants::{BANCKEND_KIND_EXTERNAL, BANCKEND_KIND_EXTERNAL_HTTP, BANCKEND_KIND_EXTERNAL_HTTPS}; use crate::converter::plugin_k8s_conv::SgSingeFilter; use crate::helper::k8s_helper::{get_k8s_obj_unique, parse_k8s_obj_unique}; +use crate::inner_model::gateway::SgProtocol; use crate::inner_model::http_route::{ SgBackendRef, SgHttpHeaderMatch, SgHttpHeaderMatchType, SgHttpPathMatch, SgHttpPathMatchType, SgHttpQueryMatch, SgHttpQueryMatchType, SgHttpRoute, SgHttpRouteMatch, SgHttpRouteRule, @@ -29,7 +31,7 @@ impl SgHttpRoute { .map(|filters| { filters .into_iter() - .map(|f| { + .filter_map(|f| { f.to_singe_filter(K8sSgFilterSpecTargetRef { kind: "HTTPSpaceroute".to_string(), name: self.name.clone(), @@ -52,7 +54,7 @@ impl SgHttpRoute { .map(|b_f_vec| { b_f_vec .iter() - .map(|b_f| { + .filter_map(|b_f| { b_f.clone().to_singe_filter(K8sSgFilterSpecTargetRef { kind: "HTTPSpaceroute".to_string(), name: self.name.clone(), @@ -83,7 +85,7 @@ impl SgHttpRoute { .map(|filters| { filters .into_iter() - .map(|f| { + .filter_map(|f| { f.to_singe_filter(K8sSgFilterSpecTargetRef { kind: "HTTPSpaceroute".to_string(), name: self.name.clone(), @@ -135,7 +137,8 @@ impl SgHttpRoute { } impl SgHttpRouteRule { - /// `SgHttpRouteRule` to `HttpRouteRule`, excluding SgFilter. + /// # to_kube_httproute + /// `SgHttpRouteRule` to `HttpRouteRule`, include `HttpRouteFilter` and excluding `SgFilter`. pub(crate) fn to_kube_httproute(self) -> HttpRouteRule { HttpRouteRule { matches: self.matches.map(|m_vec| m_vec.into_iter().map(|m| m.to_kube_httproute()).flatten().collect::>()), @@ -210,21 +213,32 @@ impl SgHttpQueryMatch { } impl SgBackendRef { - //todo excluding SgFilter. pub(crate) fn to_kube_httproute(self) -> HttpBackendRef { + let kind = if self.namespace.is_none() { + match self.protocol { + Some(SgProtocol::Http) => Some(BANCKEND_KIND_EXTERNAL_HTTP.to_string()), + Some(SgProtocol::Https) => Some(BANCKEND_KIND_EXTERNAL_HTTPS.to_string()), + Some(SgProtocol::Ws) => Some(BANCKEND_KIND_EXTERNAL.to_string()), + Some(SgProtocol::Wss) => Some(BANCKEND_KIND_EXTERNAL.to_string()), + _ => None, + } + } else { + None + }; + HttpBackendRef { backend_ref: Some(BackendRef { - weight: None, - timeout_ms: None, + weight: self.weight, + timeout_ms: self.timeout_ms, inner: BackendObjectReference { group: None, - kind: None, + kind, name: self.name_or_host, namespace: self.namespace, - port: None, + port: Some(self.port), }, }), - filters: None, + filters: self.filters.map(|f_vec| f_vec.into_iter().filter_map(|f| f.to_http_route_filter()).collect()), } } } diff --git a/kernel-common/src/converter/plugin_k8s_conv.rs b/kernel-common/src/converter/plugin_k8s_conv.rs index 57537026..c99bfb8c 100644 --- a/kernel-common/src/converter/plugin_k8s_conv.rs +++ b/kernel-common/src/converter/plugin_k8s_conv.rs @@ -9,20 +9,31 @@ use std::hash::{Hash, Hasher}; use tardis::TardisFuns; impl SgRouteFilter { - pub fn to_singe_filter(self, target: K8sSgFilterSpecTargetRef) -> SgSingeFilter { - SgSingeFilter { - name: self.name, - namespace: target.namespace.clone().unwrap_or(DEFAULT_NAMESPACE.to_string()), - filter: K8sSgFilterSpecFilter { - code: self.code, - name: None, - enable: true, - config: self.spec, - }, - target_ref: target, + /// # 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 + /// the k8s object of `SGFilter`, whereas `to_http_route_filter` is used to convert to `HttpRouteFilter`, + /// a filter defined in the Gateway API. + pub fn to_singe_filter(self, target: K8sSgFilterSpecTargetRef) -> Option { + if self.code == SG_FILTER_HEADER_MODIFIER_CODE || self.code == SG_FILTER_REDIRECT_CODE || self.code == SG_FILTER_REWRITE_CODE { + None + } else { + Some(SgSingeFilter { + name: self.name, + namespace: target.namespace.clone().unwrap_or(DEFAULT_NAMESPACE.to_string()), + filter: K8sSgFilterSpecFilter { + code: self.code, + name: None, + enable: true, + config: self.spec, + }, + target_ref: target, + }) } } + /// # to_http_route_filter + /// ref [SgRouteFilter::to_singe_filter] pub fn to_http_route_filter(self) -> Option { if &self.code == SG_FILTER_HEADER_MODIFIER_CODE { if let Ok(header) = TardisFuns::json.json_to_obj::(self.spec) { diff --git a/kernel/src/config/config_by_k8s.rs b/kernel/src/config/config_by_k8s.rs index c0b9298b..f74a9635 100644 --- a/kernel/src/config/config_by_k8s.rs +++ b/kernel/src/config/config_by_k8s.rs @@ -18,9 +18,9 @@ use tardis::{ use crate::{do_startup, functions::http_route, shutdown}; -use crate::constants::{BANCKEND_KIND_EXTERNAL, BANCKEND_KIND_EXTERNAL_HTTP, BANCKEND_KIND_EXTERNAL_HTTPS}; use crate::helpers::k8s_helper; use kernel_common::constants::k8s_constants::{DEFAULT_NAMESPACE, GATEWAY_CLASS_NAME}; +use kernel_common::constants::{BANCKEND_KIND_EXTERNAL, BANCKEND_KIND_EXTERNAL_HTTP, BANCKEND_KIND_EXTERNAL_HTTPS}; use kernel_common::gatewayapi_support_filter::SgFilterHeaderModifierKind; use kernel_common::helper; use kernel_common::helper::k8s_helper::{get_k8s_obj_unique, WarpKubeResult}; diff --git a/kernel/src/constants.rs b/kernel/src/constants.rs index 8de863ea..93116c9b 100644 --- a/kernel/src/constants.rs +++ b/kernel/src/constants.rs @@ -1,7 +1,3 @@ pub const DOMAIN_CODE: &str = "spacegate_kernel"; pub const ANNOTATION_RESOURCE_PRIORITY: &str = "priority"; - -pub const BANCKEND_KIND_EXTERNAL: &str = "External"; -pub const BANCKEND_KIND_EXTERNAL_HTTP: &str = "ExternalHttp"; -pub const BANCKEND_KIND_EXTERNAL_HTTPS: &str = "ExternalHttps";