diff --git a/admin/src/api/gateway_api.rs b/admin/src/api/gateway_api.rs index bcc80902..2b35c691 100644 --- a/admin/src/api/gateway_api.rs +++ b/admin/src/api/gateway_api.rs @@ -1,4 +1,4 @@ -use crate::dto::query_dto::GatewayQueryDto; +use crate::model::query_dto::GatewayQueryDto; use crate::service::gateway_service::GatewayService; use kernel_common::inner_model::gateway::SgGateway; use tardis::web::poem_openapi; @@ -13,32 +13,32 @@ pub struct GatewayApi; #[poem_openapi::OpenApi(prefix_path = "/gateway")] impl GatewayApi { /// Get Gateway List - #[oai(path = "/", method = "get")] - async fn list( - &self, - name: Query>, - namespace: Query>, - port: Query>, - hostname: Query>, - ) -> TardisApiResult> { - let result = GatewayService::list( - namespace.0.clone(), - GatewayQueryDto { - name: name.0, - namespace: namespace.0, - port: port.0, - hostname: hostname.0, - }, - ) - .await?; - TardisResp::ok(result) - } - - /// Add Gateway - #[oai(path = "/", method = "post")] - async fn add(&self, namespace: Query>, gateway: Json) -> TardisApiResult { - TardisResp::ok(GatewayService::add(namespace.0, gateway.0).await?) - } + // #[oai(path = "/", method = "get")] + // async fn list( + // &self, + // name: Query>, + // namespace: Query>, + // port: Query>, + // hostname: Query>, + // ) -> TardisApiResult> { + // let result = GatewayService::list( + // namespace.0.clone(), + // GatewayQueryDto { + // name: name.0, + // namespace: namespace.0, + // port: port.0, + // hostname: hostname.0, + // }, + // ) + // .await?; + // TardisResp::ok(result) + // } + // + // /// Add Gateway + // #[oai(path = "/", method = "post")] + // async fn add(&self, namespace: Query>, gateway: Json) -> TardisApiResult { + // TardisResp::ok(GatewayService::add(namespace.0, gateway.0).await?) + // } /// Delete Gateway #[oai(path = "/", method = "delete")] diff --git a/admin/src/api/plugin_api.rs b/admin/src/api/plugin_api.rs index 571ddf06..c059f9b4 100644 --- a/admin/src/api/plugin_api.rs +++ b/admin/src/api/plugin_api.rs @@ -1,4 +1,4 @@ -use crate::dto::query_dto::PluginQueryDto; +use crate::model::query_dto::PluginQueryDto; use crate::service::plugin_service::PluginService; use tardis::web::poem_openapi; use tardis::web::poem_openapi::param::Query; diff --git a/admin/src/helper.rs b/admin/src/helper.rs index 3019122f..5fac8033 100644 --- a/admin/src/helper.rs +++ b/admin/src/helper.rs @@ -1,3 +1,6 @@ +use k8s_gateway_api::Gateway; +use k8s_openapi::api::core::v1::ConfigMap; +use kube::Api; #[cfg(feature = "k8s")] use kube::Client; use tardis::basic::error::TardisError; diff --git a/admin/src/main.rs b/admin/src/main.rs index 5e821bd1..47b3c2ab 100644 --- a/admin/src/main.rs +++ b/admin/src/main.rs @@ -3,9 +3,9 @@ use tardis::{basic::result::TardisResult, tokio, TardisFuns}; mod api; mod config; mod constants; -mod dto; mod helper; mod initializer; +mod model; mod service; #[tokio::main] diff --git a/admin/src/dto.rs b/admin/src/model.rs similarity index 86% rename from admin/src/dto.rs rename to admin/src/model.rs index 8e4b7127..ed064e76 100644 --- a/admin/src/dto.rs +++ b/admin/src/model.rs @@ -1,7 +1,7 @@ pub mod base_dto; -pub mod filter_dto; pub mod query_dto; - +pub mod vo; +pub mod vo_converter; #[cfg(feature = "k8s")] pub trait ToFields { fn to_fields_vec(&self) -> Vec; diff --git a/admin/src/dto/base_dto.rs b/admin/src/model/base_dto.rs similarity index 88% rename from admin/src/dto/base_dto.rs rename to admin/src/model/base_dto.rs index b8ddf246..f999d1b3 100644 --- a/admin/src/dto/base_dto.rs +++ b/admin/src/model/base_dto.rs @@ -1,8 +1,3 @@ -pub struct CommonPageDto { - pub page_size: u32, - pub page_number: u32, -} - pub struct TargetRefDTO { pub name: String, pub kind: Option, diff --git a/admin/src/dto/filter_dto.rs b/admin/src/model/filter_dto.rs similarity index 100% rename from admin/src/dto/filter_dto.rs rename to admin/src/model/filter_dto.rs diff --git a/admin/src/dto/query_dto.rs b/admin/src/model/query_dto.rs similarity index 93% rename from admin/src/dto/query_dto.rs rename to admin/src/model/query_dto.rs index e98c926a..3820643e 100644 --- a/admin/src/dto/query_dto.rs +++ b/admin/src/model/query_dto.rs @@ -1,6 +1,6 @@ -use crate::dto::base_dto::TargetRefDTO; +use crate::model::base_dto::TargetRefDTO; #[cfg(feature = "k8s")] -use crate::dto::ToFields; +use crate::model::ToFields; #[derive(Default)] pub struct GatewayQueryDto { diff --git a/admin/src/model/vo.rs b/admin/src/model/vo.rs new file mode 100644 index 00000000..5a42e706 --- /dev/null +++ b/admin/src/model/vo.rs @@ -0,0 +1,4 @@ +pub mod backend_vo; +pub mod gateway_vo; +pub mod http_route_vo; +pub mod plugin_vo; diff --git a/admin/src/model/vo/backend_vo.rs b/admin/src/model/vo/backend_vo.rs new file mode 100644 index 00000000..bcf87a4f --- /dev/null +++ b/admin/src/model/vo/backend_vo.rs @@ -0,0 +1,25 @@ +use kernel_common::inner_model::gateway::SgProtocol; +use serde::{Deserialize, Serialize}; +use tardis::web::poem_openapi; + +/// BackendRef defines how a HTTPRoute should forward an HTTP request. +#[derive(Default, Debug, Serialize, Deserialize, Clone, poem_openapi::Object)] +pub struct BackendRefVO { + /// Name is the kubernetes service name OR url host. + pub name_or_host: String, + /// Namespace is the kubernetes namespace + pub namespace: Option, + /// Port specifies the destination port number to use for this resource. + pub port: u16, + /// Timeout specifies the timeout for requests forwarded to the referenced backend. + pub timeout_ms: Option, + // Protocol specifies the protocol used to talk to the referenced backend. + pub protocol: Option, + /// Weight specifies the proportion of requests forwarded to the referenced backend. + /// This is computed as weight/(sum of all weights in this BackendRefs list). + /// For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. + /// Weight is not a percentage and the sum of weights does not need to equal 100. + pub weight: Option, + /// [crate::model::vo::plugin_vo::SgFilterVO]'s id + pub filters: Option>, +} diff --git a/admin/src/model/vo/gateway_vo.rs b/admin/src/model/vo/gateway_vo.rs new file mode 100644 index 00000000..e128144d --- /dev/null +++ b/admin/src/model/vo/gateway_vo.rs @@ -0,0 +1,51 @@ +use kernel_common::inner_model::gateway::{SgParameters, SgProtocol, SgTlsMode}; +use serde::{Deserialize, Serialize}; +use tardis::web::poem_openapi; + +/// Gateway represents an instance of a service-traffic handling infrastructure +/// by binding Listeners to a set of IP addresses. +/// +/// Reference: [Kubernetes Gateway](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Gateway) +#[derive(Default, Debug, Serialize, Deserialize, Clone, poem_openapi::Object)] +pub struct SgGatewayVO { + /// Name of the Gateway. Global Unique. + /// + /// In k8s mode, this name MUST be unique within a namespace. + /// format see [k8s_helper::format_k8s_obj_unique] + pub name: String, + /// Some parameters necessary for the gateway. + pub parameters: SgParameters, + /// Listeners associated with this Gateway. Listeners define logical endpoints + /// that are bound on this Gateway’s addresses. + pub listeners: Vec, + /// [crate::model::vo::plugin_vo::SgFilterVO]'s id + pub filters: Option>, +} + +/// Listener embodies the concept of a logical endpoint where a Gateway accepts network connections. +#[derive(Default, Debug, Serialize, Deserialize, Clone, poem_openapi::Object)] +pub struct SgListenerVO { + /// Name is the name of the Listener. This name MUST be unique within a Gateway. + pub name: String, + /// Ip bound to the Listener. Default is 0.0.0.0 + pub ip: Option, + /// Port is the network port. Multiple listeners may use the same port, subject + /// to the Listener compatibility rules. + pub port: u16, + /// Protocol specifies the network protocol this listener expects to receive. + pub protocol: SgProtocol, + /// SgTlsConfig's id refers to the TLS configuration. + pub tls: Option, + /// `HostName` is used to define the host on which the listener accepts requests. + pub hostname: Option, +} + +/// GatewayTLSConfig describes a TLS configuration. +/// unique by id +#[derive(Debug, Serialize, Deserialize, Clone, poem_openapi::Object)] +pub struct SgTlsConfigVO { + pub id: String, + pub mode: SgTlsMode, + pub key: String, + pub cert: String, +} diff --git a/admin/src/model/vo/http_route_vo.rs b/admin/src/model/vo/http_route_vo.rs new file mode 100644 index 00000000..38914fa9 --- /dev/null +++ b/admin/src/model/vo/http_route_vo.rs @@ -0,0 +1,31 @@ +use kernel_common::inner_model::http_route::SgHttpRouteMatch; +use serde::{Deserialize, Serialize}; +use tardis::web::poem_openapi; + +/// HTTPRoute provides a way to route HTTP requests. +/// +/// Reference: [Kubernetes Gateway](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io%2fv1beta1.HTTPRoute) +#[derive(Default, Debug, Serialize, Deserialize, Clone, poem_openapi::Object)] +pub struct SgHttpRoute { + /// Associated gateway name. + pub gateway_name: String, + /// Hostnames defines a set of hostname that should match against the HTTP Host header to select a HTTPRoute to process the request. + pub hostnames: Option>, + /// [crate::model::vo::plugin_vo::SgFilterVO]'s id + pub filters: Option>, + /// Rules are a list of HTTP matchers, filters and actions. + pub rules: Option>, +} + +/// HTTPRouteRule defines semantics for matching an HTTP request based on conditions (matches), processing it (filters), and forwarding the request to an API object +#[derive(Default, Debug, Serialize, Deserialize, Clone, poem_openapi::Object)] +pub struct SgHttpRouteRuleVO { + /// Matches define conditions used for matching the rule against incoming HTTP requests. Each match is independent, i.e. this rule will be matched if any one of the matches is satisfied. + pub matches: Option>, + /// [crate::model::vo::plugin_vo::SgFilterVO]'s id + pub filters: Option>, + /// [crate::model::vo::backend_vo::BackendRefVO]'s id + pub backends: Option>, + /// Timeout define the timeout for requests that match this rule. + pub timeout_ms: Option, +} diff --git a/admin/src/model/vo/plugin_vo.rs b/admin/src/model/vo/plugin_vo.rs new file mode 100644 index 00000000..bd7b6edb --- /dev/null +++ b/admin/src/model/vo/plugin_vo.rs @@ -0,0 +1,22 @@ +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use tardis::web::poem_openapi; + +/// RouteFilter defines processing steps that must be completed during the request or response lifecycle. +/// +/// There are four levels of filters +/// 1. Global level, which works on all requests under the same gateway service +/// 2. Routing level, which works on all requests under the same gateway route +/// 3. Rule level, which works on all requests under the same gateway routing rule +/// 4. Backend level, which works on all requests under the same backend +#[derive(Default, Debug, Serialize, Deserialize, Clone, poem_openapi::Object)] +pub struct SgFilterVO { + pub id: String, + /// Filter code, Used to match the corresponding filter. + pub code: String, + /// Filter name. If the name of the same filter exists at different levels of configuration, + /// only the child nodes take effect(Backend Level > Rule Level > Routing Level > Global Level) + pub name: Option, + /// filter parameters. + pub spec: Value, +} diff --git a/admin/src/model/vo_converter.rs b/admin/src/model/vo_converter.rs new file mode 100644 index 00000000..e69de29b diff --git a/admin/src/service.rs b/admin/src/service.rs index 186701c0..138a1cc6 100644 --- a/admin/src/service.rs +++ b/admin/src/service.rs @@ -1,2 +1,5 @@ +pub(crate) mod backend_ref_service; +pub(crate) mod base_service; pub(crate) mod gateway_service; pub(crate) mod plugin_service; +pub(crate) mod route_service; diff --git a/admin/src/service/backend_ref_service.rs b/admin/src/service/backend_ref_service.rs new file mode 100644 index 00000000..e69de29b diff --git a/admin/src/service/base_service.rs b/admin/src/service/base_service.rs new file mode 100644 index 00000000..2ea0748e --- /dev/null +++ b/admin/src/service/base_service.rs @@ -0,0 +1,58 @@ +use crate::helper::get_k8s_client; +use k8s_openapi::api::core::v1::ConfigMap; +use k8s_openapi::merge_strategies::list; +use kube::api::ListParams; +use kube::Api; +use serde::Deserialize; +use serde_json::Value; +use std::collections::HashMap; +use tardis::basic::error::TardisError; +use tardis::basic::result::TardisResult; + +pub const GATEWAY_CONFIG_NAME: &str = "gateway_config"; +pub const PLUGIN_CONFIG_NAME: &str = "plugin_config"; +pub const ROUTE_CONFIG_NAME: &str = "route_config"; +pub const BACKEND_REF_CONFIG_NAME: &str = "backend_ref_config"; + +pub trait GetConfigMapName { + fn get_config_map_name() -> String; +} + +pub struct BaseService; + +impl BaseService { + #[cfg(feature = "k8s")] + pub async fn list<'a, T>() -> TardisResult> + where + T: GetConfigMapName + Deserialize<'a>, + { + let mut items = get_config_map_api() + .await? + .list(&ListParams::default().fields(&format!("metadata.name={}", T::get_config_map_name()))) + .await + .map_err(|e| TardisError::io_error(&format!("err:{e}"), ""))? + .items; + if items.is_empty() { + Ok(HashMap::new()) + } else { + if let Some(b_map) = items.remove(0).data { + Ok(b_map.into_iter().collect()) + } else { + Ok(HashMap::new()) + } + } + } + + #[cfg(feature = "k8s")] + pub async fn add<'a, T>(config: T) -> TardisResult<()> + where + T: GetConfigMapName + Deserialize<'a>, + { + config + } +} + +#[cfg(feature = "k8s")] +pub async fn get_config_map_api() -> TardisResult> { + Ok(Api::namespaced(get_k8s_client().await?, "spacegate")) +} diff --git a/admin/src/service/gateway_service.rs b/admin/src/service/gateway_service.rs index 0f44eddc..e97959d8 100644 --- a/admin/src/service/gateway_service.rs +++ b/admin/src/service/gateway_service.rs @@ -1,8 +1,8 @@ -use crate::dto::query_dto::GatewayQueryDto; -#[cfg(feature = "k8s")] -use crate::dto::ToFields; #[cfg(feature = "k8s")] use crate::helper::get_k8s_client; +use crate::model::query_dto::GatewayQueryDto; +#[cfg(feature = "k8s")] +use crate::model::ToFields; use crate::service::plugin_service::PluginService; #[cfg(feature = "k8s")] diff --git a/admin/src/service/plugin_service.rs b/admin/src/service/plugin_service.rs index 8b700609..0125ee5c 100644 --- a/admin/src/service/plugin_service.rs +++ b/admin/src/service/plugin_service.rs @@ -1,8 +1,9 @@ -use crate::dto::query_dto::PluginQueryDto; -#[cfg(feature = "k8s")] -use crate::dto::ToFields; #[cfg(feature = "k8s")] use crate::helper::{get_k8s_client, WarpKubeResult}; +use crate::model::query_dto::PluginQueryDto; +use crate::model::vo::plugin_vo::SgFilterVO; +#[cfg(feature = "k8s")] +use crate::model::ToFields; #[cfg(feature = "k8s")] use kernel_common::constants::DEFAULT_NAMESPACE; #[cfg(feature = "k8s")] @@ -13,19 +14,28 @@ use kernel_common::k8s_crd::sg_filter::SgFilter; #[cfg(feature = "k8s")] use kube::{api::ListParams, api::PostParams, Api, ResourceExt}; use std::collections::HashMap; +use k8s_gateway_api::Gateway; use tardis::basic::error::TardisError; use tardis::basic::result::TardisResult; pub struct PluginService; impl PluginService { + pub async fn add(add: SgFilterVO) -> TardisResult> { + let result = vec![]; + #[cfg(feature = "k8s")] + {} + #[cfg(not(feature = "k8s"))] + {} + + Ok(result) + } + pub async fn list(query: PluginQueryDto) -> TardisResult> { let result = vec![]; #[cfg(feature = "k8s")] { - let filter_api: Api = Self::get_filter_api(&query.namespace).await?; - - let _filter_list = filter_api.list(&ListParams::default().fields(&query.to_fields())).await.map_err(|e| TardisError::io_error(&format!("err:{e}"), ""))?; + } #[cfg(not(feature = "k8s"))] {} diff --git a/admin/src/service/route_service.rs b/admin/src/service/route_service.rs new file mode 100644 index 00000000..e69de29b diff --git a/kernel-common/src/converter/http_route_k8s_conv.rs b/kernel-common/src/converter/http_route_k8s_conv.rs index e69de29b..8b137891 100644 --- a/kernel-common/src/converter/http_route_k8s_conv.rs +++ b/kernel-common/src/converter/http_route_k8s_conv.rs @@ -0,0 +1 @@ + diff --git a/kernel-common/src/inner_model/gateway.rs b/kernel-common/src/inner_model/gateway.rs index 4b52d00b..3f6aaa72 100644 --- a/kernel-common/src/inner_model/gateway.rs +++ b/kernel-common/src/inner_model/gateway.rs @@ -11,7 +11,6 @@ use tardis::web::poem_openapi; /// /// Reference: [Kubernetes Gateway](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Gateway) #[derive(Default, Debug, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "admin-support", derive(poem_openapi::Object))] pub struct SgGateway { /// Name of the Gateway. Global Unique. /// @@ -43,7 +42,6 @@ pub struct SgParameters { /// Listener embodies the concept of a logical endpoint where a Gateway accepts network connections. #[derive(Default, Debug, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "admin-support", derive(poem_openapi::Object))] pub struct SgListener { /// Name is the name of the Listener. This name MUST be unique within a Gateway. pub name: String, diff --git a/kernel-common/src/inner_model/http_route.rs b/kernel-common/src/inner_model/http_route.rs index fd4957da..54fb1845 100644 --- a/kernel-common/src/inner_model/http_route.rs +++ b/kernel-common/src/inner_model/http_route.rs @@ -1,6 +1,8 @@ use std::fmt; use serde::{Deserialize, Serialize}; +#[cfg(feature = "admin-support")] +use tardis::web::poem_openapi; use super::{gateway::SgProtocol, plugin_filter::SgRouteFilter}; @@ -34,6 +36,7 @@ pub struct SgHttpRouteRule { /// HTTPRouteMatch defines the predicate used to match requests to a given action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. #[derive(Default, Debug, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "admin-support", derive(poem_openapi::Object))] pub struct SgHttpRouteMatch { /// Path specifies a HTTP request path matcher. If this field is not specified, a default prefix match on the “/” path is provided. pub path: Option, @@ -47,6 +50,7 @@ pub struct SgHttpRouteMatch { /// HTTPPathMatch describes how to select a HTTP route by matching the HTTP request path. #[derive(Default, Debug, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "admin-support", derive(poem_openapi::Object))] pub struct SgHttpPathMatch { /// Type specifies how to match against the path Value. pub kind: SgHttpPathMatchType, @@ -56,6 +60,7 @@ pub struct SgHttpPathMatch { /// PathMatchType specifies the semantics of how HTTP paths should be compared. #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Default)] +#[cfg_attr(feature = "admin-support", derive(poem_openapi::Enum))] #[serde(rename_all = "lowercase")] pub enum SgHttpPathMatchType { /// Matches the URL path exactly and with case sensitivity. @@ -80,6 +85,7 @@ impl fmt::Display for SgHttpPathMatchType { /// HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request headers. #[derive(Default, Debug, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "admin-support", derive(poem_openapi::Object))] pub struct SgHttpHeaderMatch { /// Type specifies how to match against the value of the header. pub kind: SgHttpHeaderMatchType, @@ -91,6 +97,7 @@ pub struct SgHttpHeaderMatch { /// HeaderMatchType specifies the semantics of how HTTP header values should be compared. #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Default)] +#[cfg_attr(feature = "admin-support", derive(poem_openapi::Enum))] #[serde(rename_all = "lowercase")] pub enum SgHttpHeaderMatchType { /// Matches the HTTP header exactly and with case sensitivity. @@ -111,6 +118,7 @@ impl fmt::Display for SgHttpHeaderMatchType { /// HTTPQueryMatch describes how to select a HTTP route by matching HTTP query parameters. #[derive(Default, Debug, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "admin-support", derive(poem_openapi::Object))] pub struct SgHttpQueryMatch { /// Type specifies how to match against the value of the query parameter. pub kind: SgHttpQueryMatchType, @@ -122,6 +130,7 @@ pub struct SgHttpQueryMatch { /// HTTPQueryMatchType specifies the semantics of how HTTP query parameter values should be compared. #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Default)] +#[cfg_attr(feature = "admin-support", derive(poem_openapi::Enum))] #[serde(rename_all = "lowercase")] pub enum SgHttpQueryMatchType { /// Matches the HTTP query parameter exactly and with case sensitivity.