feat(adminrpc): new standalone binary for admin gRPC service
Separate binary for Kubernetes internal admin RPC communication (SessionAdmin service on port 9090). Includes: - Redis cluster pool via session_manager - OTLP tracing with env-driven configuration - Tracing subscriber init (JSON to stderr) - Graceful startup with connection verification
This commit is contained in:
parent
4aaee59fa4
commit
fbd228f17e
20
Cargo.lock
generated
20
Cargo.lock
generated
@ -335,6 +335,21 @@ version = "2.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adminrpc"
|
||||||
|
version = "0.2.9"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"clap",
|
||||||
|
"config",
|
||||||
|
"deadpool-redis",
|
||||||
|
"observability",
|
||||||
|
"rpc",
|
||||||
|
"session_manager",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aead"
|
name = "aead"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@ -6795,10 +6810,11 @@ dependencies = [
|
|||||||
"prost 0.14.3",
|
"prost 0.14.3",
|
||||||
"prost-types 0.14.3",
|
"prost-types 0.14.3",
|
||||||
"session_manager",
|
"session_manager",
|
||||||
"slog",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"tonic 0.14.5",
|
"tonic 0.14.5",
|
||||||
|
"tonic-prost",
|
||||||
"tonic-prost-build",
|
"tonic-prost-build",
|
||||||
|
"tracing",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -7589,9 +7605,9 @@ dependencies = [
|
|||||||
"redis",
|
"redis",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"slog",
|
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tracing",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,7 @@ members = [
|
|||||||
"libs/agent-tool-derive",
|
"libs/agent-tool-derive",
|
||||||
"apps/migrate",
|
"apps/migrate",
|
||||||
"apps/app",
|
"apps/app",
|
||||||
|
"apps/adminrpc",
|
||||||
"apps/git-hook",
|
"apps/git-hook",
|
||||||
"apps/gitserver",
|
"apps/gitserver",
|
||||||
"apps/email",
|
"apps/email",
|
||||||
|
|||||||
44
apps/adminrpc/Cargo.toml
Normal file
44
apps/adminrpc/Cargo.toml
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
[package]
|
||||||
|
name = "adminrpc"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
description.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
readme.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
keywords.workspace = true
|
||||||
|
categories.workspace = true
|
||||||
|
documentation.workspace = true
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "adminrpc"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# gRPC
|
||||||
|
rpc = { workspace = true }
|
||||||
|
|
||||||
|
# Session / Redis
|
||||||
|
session_manager = { workspace = true }
|
||||||
|
deadpool-redis = { workspace = true, features = ["cluster"] }
|
||||||
|
|
||||||
|
# Observability
|
||||||
|
observability = { workspace = true }
|
||||||
|
tracing = { workspace = true }
|
||||||
|
|
||||||
|
# Config
|
||||||
|
config = { workspace = true }
|
||||||
|
|
||||||
|
# Utilities
|
||||||
|
anyhow = { workspace = true }
|
||||||
|
|
||||||
|
# Async runtime
|
||||||
|
tokio = { workspace = true, features = ["rt-multi-thread", "signal"] }
|
||||||
|
|
||||||
|
# CLI
|
||||||
|
clap = { workspace = true, features = ["derive"] }
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
9
apps/adminrpc/src/args.rs
Normal file
9
apps/adminrpc/src/args.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(name = "adminrpc")]
|
||||||
|
pub struct Args {
|
||||||
|
/// Override the bind address (default: 0.0.0.0:9090)
|
||||||
|
#[arg(short, long)]
|
||||||
|
pub bind: Option<String>,
|
||||||
|
}
|
||||||
68
apps/adminrpc/src/main.rs
Normal file
68
apps/adminrpc/src/main.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
use std::net::SocketAddr;
|
||||||
|
use anyhow::Context as _;
|
||||||
|
use clap::Parser;
|
||||||
|
use config::AppConfig;
|
||||||
|
use deadpool_redis::{cluster, Runtime};
|
||||||
|
use session_manager::{SessionManager, SessionStorage};
|
||||||
|
use rpc::admin::server::{serve, DEFAULT_GRPC_PORT};
|
||||||
|
|
||||||
|
mod args;
|
||||||
|
use args::Args;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
let cfg = AppConfig::load();
|
||||||
|
let log_level = cfg.log_level().unwrap_or_else(|_| "info".to_string());
|
||||||
|
observability::init_tracing_subscriber(&log_level);
|
||||||
|
|
||||||
|
let args = Args::parse();
|
||||||
|
let bind_addr: SocketAddr = args
|
||||||
|
.bind
|
||||||
|
.map(|s| s.parse())
|
||||||
|
.unwrap_or_else(|| format!("0.0.0.0:{}", DEFAULT_GRPC_PORT).parse())
|
||||||
|
.context("invalid bind address")?;
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
app_name = %cfg.app_name().unwrap_or_default(),
|
||||||
|
bind_addr = %bind_addr,
|
||||||
|
"Starting admin RPC server"
|
||||||
|
);
|
||||||
|
|
||||||
|
// ── Phase 6: OTLP tracing ──────────────────────────────────────────────
|
||||||
|
let _otel_guard = if cfg.otel_enabled().unwrap_or(false) {
|
||||||
|
let endpoint = cfg.otel_endpoint().unwrap_or_else(|_| "http://localhost:4317".to_string());
|
||||||
|
let service_name = cfg.otel_service_name().unwrap_or_else(|_| "adminrpc".to_string());
|
||||||
|
let service_version = cfg.otel_service_version().unwrap_or_else(|_| "0.1.0".to_string());
|
||||||
|
tracing::info!(endpoint = %endpoint, service = %service_name, "OTLP tracing enabled");
|
||||||
|
let guard = observability::init_otlp(&endpoint, &service_name, &service_version, &log_level)
|
||||||
|
.map_err(|e| anyhow::anyhow!("OTLP init failed: {}", e))?;
|
||||||
|
guard
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// Redis connection pool
|
||||||
|
let redis_url = cfg.redis_url()?;
|
||||||
|
tracing::info!(redis_url = %redis_url, "Connecting to Redis");
|
||||||
|
let manager = cluster::Manager::new(vec![redis_url.clone()], false)
|
||||||
|
.map_err(|e| anyhow::anyhow!("failed to create redis cluster manager: {}", e))?;
|
||||||
|
let pool: cluster::Pool = cluster::Pool::builder(manager)
|
||||||
|
.max_size(16)
|
||||||
|
.runtime(Runtime::Tokio1)
|
||||||
|
.build()
|
||||||
|
.map_err(|e| anyhow::anyhow!("failed to build redis pool: {}", e))?;
|
||||||
|
|
||||||
|
// Test connection
|
||||||
|
let _conn = pool.get().await
|
||||||
|
.context("redis pool connection failed")?;
|
||||||
|
tracing::info!("Redis connected");
|
||||||
|
|
||||||
|
let storage = SessionStorage::new(pool);
|
||||||
|
let session_manager = SessionManager::new(storage);
|
||||||
|
|
||||||
|
tracing::info!(addr = %bind_addr, "Admin gRPC server listening");
|
||||||
|
serve(bind_addr, session_manager).await?;
|
||||||
|
|
||||||
|
tracing::info!("Admin RPC server stopped");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user