From 4aabb45138f87b8ab27018af30e2f55825e9230f Mon Sep 17 00:00:00 2001 From: 4t145 Date: Mon, 6 May 2024 16:14:28 +0800 Subject: [PATCH 1/3] install script for linux --- Cargo.toml | 2 +- binary/spacegate/Cargo.toml | 2 +- binary/spacegate/src/args.rs | 3 +++ crates/config/src/lib.rs | 4 ++++ crates/config/src/service.rs | 6 ++++- crates/extension/axum/src/lib.rs | 18 ++++++++++++++- crates/extension/redis/src/lib.rs | 20 ++++++++--------- crates/extension/tracing/Cargo.toml | 19 ---------------- crates/extension/tracing/src/lib.rs | 1 - crates/shell/src/constants.rs | 16 -------------- crates/shell/src/lib.rs | 10 +++++++-- resource/install/install.sh | 34 +++++++++++++++++++++++++++++ resource/install/spacegate.service | 13 +++++++++++ 13 files changed, 96 insertions(+), 52 deletions(-) delete mode 100644 crates/extension/tracing/Cargo.toml delete mode 100644 crates/extension/tracing/src/lib.rs delete mode 100644 crates/shell/src/constants.rs create mode 100644 resource/install/install.sh create mode 100644 resource/install/spacegate.service diff --git a/Cargo.toml b/Cargo.toml index 2f04bdcf..6d20ae8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ documentation = "https://docs.rs/spacegate/" repository = "https://github.com/ideal-world/spacegate" edition = "2021" license = "MIT/Apache-2.0" -rust-version = "1.64" +rust-version = "1.76" [workspace.dependencies] spacegate-kernel = { path = "./crates/kernel" } diff --git a/binary/spacegate/Cargo.toml b/binary/spacegate/Cargo.toml index 21fdeb84..6b70602a 100644 --- a/binary/spacegate/Cargo.toml +++ b/binary/spacegate/Cargo.toml @@ -14,7 +14,7 @@ readme = "../../README.md" [features] -default = ["fs", "dylib"] +default = ["fs"] full = ["k8s", "fs", "redis", "axum"] build-k8s = ["k8s", "redis", "axum"] build-local = ["fs", "redis", "axum"] diff --git a/binary/spacegate/src/args.rs b/binary/spacegate/src/args.rs index 7e81be0c..cc601fdf 100644 --- a/binary/spacegate/src/args.rs +++ b/binary/spacegate/src/args.rs @@ -101,6 +101,8 @@ pub struct Args { /// ## Redis /// `-c redis:redis://some-redis-url` #[arg(short, long, env)] + #[cfg_attr(feature = "build-k8s", arg(default_value_t=Config::K8s(String::from("default"))))] + #[cfg_attr(all(not(feature = "build-k8s"), target_family = "unix", feature="fs"), arg(default_value_t=Config::File(PathBuf::from("/etc/spacegate"))))] pub config: Config, /// The dynamic lib plugins dir /// @@ -108,5 +110,6 @@ pub struct Args { /// If you are using linux, you may put the plugins dll in `/lib/spacegate/plugins`. /// `-p /lib/spacegate/plugins` #[arg(short, long, env)] + #[cfg_attr(target_family = "unix", arg(default_value = "/lib/spacegate/plugins"))] pub plugins: Option, } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 0abc33fd..479ac686 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1,5 +1,9 @@ #![warn(clippy::indexing_slicing, clippy::unwrap_used, clippy::dbg_macro, clippy::undocumented_unsafe_blocks)] +//! This crate is aim to manage the configuration of spacegate application with various backends. + +/// re-export spacegate_model pub mod model; +/// Configuration backends and services traits pub mod service; pub use model::*; diff --git a/crates/config/src/service.rs b/crates/config/src/service.rs index 1f902d44..4dfc6831 100644 --- a/crates/config/src/service.rs +++ b/crates/config/src/service.rs @@ -1,10 +1,14 @@ -// pub mod backend; +/// Config file format pub mod config_format; +/// File system backend #[cfg(feature = "fs")] pub mod fs; +/// Kubernetes backend #[cfg(feature = "k8s")] pub mod k8s; +/// In-memory backend pub mod memory; +/// Redis backend #[cfg(feature = "redis")] pub mod redis; use std::{collections::BTreeMap, error::Error, str::FromStr}; diff --git a/crates/extension/axum/src/lib.rs b/crates/extension/axum/src/lib.rs index bea68c09..eedc91ee 100644 --- a/crates/extension/axum/src/lib.rs +++ b/crates/extension/axum/src/lib.rs @@ -9,9 +9,11 @@ use axum::{BoxError, Router}; use tokio::sync::RwLock; use tokio::task::JoinHandle; use tokio_util::sync::CancellationToken; - +/// Default port for the global server const GLOBAL_SERVER_PORT: u16 = 9876; +/// Default host for the global server const GLOBAL_SERVER_HOST: IpAddr = IpAddr::V6(Ipv6Addr::UNSPECIFIED); +/// Default bind to [::]:9876 const GLOBAL_SERVER_BIND: SocketAddr = SocketAddr::new(GLOBAL_SERVER_HOST, GLOBAL_SERVER_PORT); #[derive(Debug)] struct AxumServerInner { @@ -32,6 +34,13 @@ impl Default for AxumServerInner { } } +/// Global axum http server for spacegate and its plugins. +/// +/// # Usage +/// ``` +/// # use spacegate_ext_axum::GlobalAxumServer; +/// let server = GlobalAxumServer::default(); +/// ``` #[derive(Debug, Clone)] pub struct GlobalAxumServer(Arc>); @@ -42,6 +51,7 @@ impl Default for GlobalAxumServer { } impl GlobalAxumServer { + /// Set the bind address for the server. If the server is already running, new bind address will take effect after restart. pub async fn set_bind(&self, socket_addr: A) where A: Into, @@ -50,10 +60,14 @@ impl GlobalAxumServer { let mut wg = self.0.write().await; wg.bind = socket_addr; } + + /// Set the cancellation token for the server. pub async fn set_cancellation(&self, token: CancellationToken) { let mut wg = self.0.write().await; wg.cancel_token = token; } + + /// Modify the router with the given closure. pub async fn modify_router(&self, modify: M) where M: FnOnce(Router) -> Router, @@ -64,11 +78,13 @@ impl GlobalAxumServer { wg.router = (modify)(swap_out) } + /// Start the server, if the server is already running, it will be restarted. pub async fn start(&self) -> Result<(), std::io::Error> { let mut wg = self.0.write().await; wg.start().await } + /// Shutdown the server. pub async fn shutdown(&self) -> Result<(), std::io::Error> { let mut wg = self.0.write().await; wg.shutdown().await diff --git a/crates/extension/redis/src/lib.rs b/crates/extension/redis/src/lib.rs index 32d6668b..f0f6d9db 100644 --- a/crates/extension/redis/src/lib.rs +++ b/crates/extension/redis/src/lib.rs @@ -3,18 +3,11 @@ use std::{collections::HashMap, sync::RwLock}; pub use deadpool_redis::{Connection, Manager, Pool}; pub use redis; use redis::RedisResult; -/// hash: {gateway name} -> {gateway config} -pub const CONF_GATEWAY_KEY: &str = "sg:conf:gateway"; -/// hash: {gateway name} -> { -> } -pub const CONF_HTTP_ROUTE_KEY: &str = "sg:conf:route:http:"; -/// string: {timestamp}##{changed obj}##{method}##{changed gateway name}##{changed route name} -> None -/// changed obj: gateway/httproute -/// method: create/update/delete -/// changed route name: None or -pub const CONF_CHANGE_TRIGGER: &str = "sg:conf:change:trigger:"; +/// Wrapper for pooled Redis client. #[derive(Clone)] pub struct RedisClient { + /// Pooled Redis client. pub redis_conn_pool: Pool, } @@ -25,12 +18,13 @@ impl std::fmt::Debug for RedisClient { } impl RedisClient { + /// Create a new Redis client from connect url. pub fn new(url: impl AsRef) -> RedisResult { let url = url.as_ref(); let redis_conn_pool = Pool::builder(Manager::new(url)?).build().expect("Failed to create Redis pool"); Ok(Self { redis_conn_pool }) } - + /// Get a connection from the pool. pub async fn get_conn(&self) -> Connection { self.redis_conn_pool.get().await.unwrap() } @@ -42,29 +36,35 @@ impl From<&str> for RedisClient { } } +/// Redis Client Repository. #[derive(Debug, Default)] pub struct RedisClientRepo { repos: RwLock>, } impl RedisClientRepo { + /// Get the global Redis client repository instance. pub fn global() -> &'static Self { static INIT: std::sync::OnceLock = std::sync::OnceLock::new(); INIT.get_or_init(Self::new) } + /// Create a new Redis client repository. pub fn new() -> Self { Self { repos: RwLock::default() } } + /// Add a Redis client to the repository. pub fn add(&self, name: impl Into, client: impl Into) { self.repos.write().expect("poisoned global redis client repo").insert(name.into(), client.into()); } + /// Get a Redis client from the repository by its name. pub fn get(&self, name: &str) -> Option { self.repos.read().expect("poisoned global redis client repo").get(name).cloned() } + /// Remove a Redis client from the repository by its name. pub fn remove(&self, name: &str) -> Option { self.repos.write().expect("poisoned global redis client repo").remove(name) } diff --git a/crates/extension/tracing/Cargo.toml b/crates/extension/tracing/Cargo.toml deleted file mode 100644 index a3d4145c..00000000 --- a/crates/extension/tracing/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "spacegate-ext-tracing" -version.workspace = true -authors.workspace = true -description.workspace = true -keywords.workspace = true -categories.workspace = true -homepage.workspace = true -documentation.workspace = true -repository.workspace = true -edition.workspace = true -license.workspace = true -rust-version.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -tracing-subscriber = { workspace = true, features = ["env-filter"] } - diff --git a/crates/extension/tracing/src/lib.rs b/crates/extension/tracing/src/lib.rs deleted file mode 100644 index 8b137891..00000000 --- a/crates/extension/tracing/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/shell/src/constants.rs b/crates/shell/src/constants.rs deleted file mode 100644 index 42c87d9d..00000000 --- a/crates/shell/src/constants.rs +++ /dev/null @@ -1,16 +0,0 @@ -pub const DOMAIN_CODE: &str = "spacegate_shell"; - -pub const ANNOTATION_RESOURCE_PRIORITY: &str = "priority"; - -pub const GATEWAY_ANNOTATION_REDIS_URL: &str = "redis_url"; -pub const GATEWAY_ANNOTATION_LOG_LEVEL: &str = "log_level"; -pub const GATEWAY_ANNOTATION_LANGUAGE: &str = "lang"; -pub const GATEWAY_ANNOTATION_IGNORE_TLS_VERIFICATION: &str = "ignore_tls_verification"; - -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/crates/shell/src/lib.rs b/crates/shell/src/lib.rs index 8dd6f941..bc17c624 100644 --- a/crates/shell/src/lib.rs +++ b/crates/shell/src/lib.rs @@ -38,18 +38,24 @@ pub use spacegate_config::model; pub use spacegate_config::model::{BoxError, BoxResult}; use spacegate_config::service::{CreateListener, Retrieve}; use spacegate_config::Config; +/// re-export spacegate_kernel pub use spacegate_kernel as kernel; +/// re-export spacegate_plugin pub use spacegate_plugin as plugin; use tokio::signal; use tracing::{info, instrument}; +/// Configuration retrieval and event listener pub mod config; -pub mod constants; +/// http extensions pub mod extension; +/// Spacegate service creation pub mod server; +#[cfg(feature = "ext-axum")] +pub use spacegate_ext_axum as ext_axum; #[cfg(feature = "ext-redis")] -pub use spacegate_ext_redis; +pub use spacegate_ext_redis as ext_redis; #[cfg(feature = "fs")] /// # Startup the gateway by config file diff --git a/resource/install/install.sh b/resource/install/install.sh new file mode 100644 index 00000000..9ed61f6f --- /dev/null +++ b/resource/install/install.sh @@ -0,0 +1,34 @@ +CONFIG_PATH=/etc/spacegate/config.json +PLUGIN_PATH=/lib/spacegate/plugins +BIN_PATH=/usr/local/bin +cargo build --release --features dylib +sudo cp target/release/spacegate $BIN_PATH + +# Create config file +sudo mkdir /etc/spacegate + +if [ -f $CONFIG_PATH ]; then + echo "Config file already exists" +else + sudo echo "{}" > /etc/spacegate/config.json + sudo chmod 666 /etc/spacegate/config.json +fi + +if [ -f $PLUGIN_PATH ]; then + echo "Plugin dir already exists" +else + # Create plugin directory + sudo mkdir $PLUGIN_PATH +fi + + +if [ -f /etc/systemd/system/spacegate.service ]; then + echo "Systemd unit file already exists" +else + # Create systemd service + sudo cp resource/install/spacegate.service /etc/systemd/system/spacegate.service +fi + +# Enable and start service +sudo systemctl enable spacegate +sudo systemctl start spacegate \ No newline at end of file diff --git a/resource/install/spacegate.service b/resource/install/spacegate.service new file mode 100644 index 00000000..566bc48c --- /dev/null +++ b/resource/install/spacegate.service @@ -0,0 +1,13 @@ +[Unit] +Description=Spacegate + +[Service] +PIDFile=/run/spacegate.pid +ExecStart=spacegate +Restart=always +ExecStop=/bin/kill -INT $MAINPID +KillSignal=SIGINT +TimeoutStopSec=5 + +[Install] +WantedBy=multi-user.target \ No newline at end of file From 22cd6eb099630da29dd99d6a6f531075a054c95d Mon Sep 17 00:00:00 2001 From: 4t145 Date: Mon, 6 May 2024 16:14:33 +0800 Subject: [PATCH 2/3] fmt --- crates/extension/axum/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/extension/axum/src/lib.rs b/crates/extension/axum/src/lib.rs index eedc91ee..6d9ac68a 100644 --- a/crates/extension/axum/src/lib.rs +++ b/crates/extension/axum/src/lib.rs @@ -35,7 +35,7 @@ impl Default for AxumServerInner { } /// Global axum http server for spacegate and its plugins. -/// +/// /// # Usage /// ``` /// # use spacegate_ext_axum::GlobalAxumServer; From 8637b4ba75667266e58f9fac90417f839586d1c5 Mon Sep 17 00:00:00 2001 From: 4t145 Date: Mon, 6 May 2024 17:17:08 +0800 Subject: [PATCH 3/3] fix build error --- crates/shell/src/server.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/shell/src/server.rs b/crates/shell/src/server.rs index 1233d825..9588a2cd 100644 --- a/crates/shell/src/server.rs +++ b/crates/shell/src/server.rs @@ -69,13 +69,12 @@ fn collect_http_route( #[cfg(feature = "k8s")] { use crate::extension::k8s_service::K8sService; - use spacegate_config::model::BackendHost; use spacegate_kernel::helper_layers::map_request::{add_extension::add_extension, MapRequestLayer}; use spacegate_kernel::BoxLayer; - if let BackendHost::K8sService(data) = backend.host { - let namespace_ext = K8sService(data.into()); + if let BackendHost::K8sService(ref data) = backend.host { + let namespace_ext = K8sService(data.clone().into()); // need to add to front - builder = builder.plugin(SgBoxLayer::new(MapRequestLayer::new(add_extension(namespace_ext, true)))) + builder = builder.plugin(BoxLayer::new(MapRequestLayer::new(add_extension(namespace_ext, true)))) } } builder = builder.host(host).port(backend.port);