//! k8shost API Server //! //! This is the main Kubernetes API server for PlasmaCloud's k8shost component. //! It provides a subset of the Kubernetes API compatible with kubectl and other //! k8s tooling, while integrating with PlasmaCloud's infrastructure. //! //! Architecture: //! - gRPC API server implementing k8shost-proto services //! - RESTful HTTP/JSON API for kubectl compatibility (future) //! - FlareDB backend for state storage //! - Integration with IAM for multi-tenant authentication //! - Scheduler for pod placement on nodes (future) //! - Controller manager for built-in controllers (future) mod auth; mod cni; mod services; mod storage; use anyhow::Result; use auth::AuthService; use k8shost_proto::{ deployment_service_server::{DeploymentService, DeploymentServiceServer}, node_service_server::NodeServiceServer, pod_service_server::PodServiceServer, service_service_server::ServiceServiceServer, *, }; use services::{node::NodeServiceImpl, pod::PodServiceImpl, service::ServiceServiceImpl}; use std::sync::Arc; use storage::Storage; use tonic::{transport::Server, Request, Response, Status}; use tracing::{info, warn}; #[tokio::main] async fn main() -> Result<()> { tracing_subscriber::fmt::init(); let addr = "[::]:6443".parse()?; info!("k8shost API server starting on {}", addr); // Initialize FlareDB storage let pd_addr = std::env::var("FLAREDB_PD_ADDR").unwrap_or_else(|_| "127.0.0.1:2379".to_string()); info!("Connecting to FlareDB PD at {}", pd_addr); let storage = match Storage::new(pd_addr).await { Ok(s) => { info!("Successfully connected to FlareDB"); Arc::new(s) } Err(e) => { warn!("Failed to connect to FlareDB: {}. Server will start but may not function correctly.", e); return Err(anyhow::anyhow!("Failed to connect to FlareDB: {}", e)); } }; // Initialize IAM authentication service let iam_addr = std::env::var("IAM_SERVER_ADDR").unwrap_or_else(|_| "127.0.0.1:50051".to_string()); info!("Connecting to IAM server at {}", iam_addr); let auth_service = match AuthService::new(&iam_addr).await { Ok(s) => { info!("Successfully connected to IAM server"); Arc::new(s) } Err(e) => { warn!("Failed to connect to IAM server: {}. Authentication will be disabled.", e); // For now, we fail if IAM is unavailable // In a more flexible setup, we might allow operation without auth for development return Err(anyhow::anyhow!("Failed to connect to IAM server: {}", e)); } }; // Create service implementations with storage let pod_service = PodServiceImpl::new(storage.clone()); let service_service = ServiceServiceImpl::new(storage.clone()); let node_service = NodeServiceImpl::new(storage.clone()); let deployment_service = DeploymentServiceImpl::default(); // Still unimplemented info!("Starting gRPC server with authentication..."); // Build server with authentication layer // Note: We use separate interceptor closures for each service Server::builder() .add_service( tonic::codegen::InterceptedService::new( PodServiceServer::new(pod_service), { let auth = auth_service.clone(); move |req: Request<()>| -> Result, Status> { let auth = auth.clone(); let runtime_handle = tokio::runtime::Handle::current(); runtime_handle.block_on(async move { let tenant_context = auth.authenticate(&req).await?; let mut req = req; req.extensions_mut().insert(tenant_context); Ok::<_, Status>(req) }) } }, ), ) .add_service( tonic::codegen::InterceptedService::new( ServiceServiceServer::new(service_service), { let auth = auth_service.clone(); move |req: Request<()>| -> Result, Status> { let auth = auth.clone(); let runtime_handle = tokio::runtime::Handle::current(); runtime_handle.block_on(async move { let tenant_context = auth.authenticate(&req).await?; let mut req = req; req.extensions_mut().insert(tenant_context); Ok::<_, Status>(req) }) } }, ), ) .add_service( tonic::codegen::InterceptedService::new( NodeServiceServer::new(node_service), { let auth = auth_service.clone(); move |req: Request<()>| -> Result, Status> { let auth = auth.clone(); let runtime_handle = tokio::runtime::Handle::current(); runtime_handle.block_on(async move { let tenant_context = auth.authenticate(&req).await?; let mut req = req; req.extensions_mut().insert(tenant_context); Ok::<_, Status>(req) }) } }, ), ) .add_service(DeploymentServiceServer::new(deployment_service)) .serve(addr) .await?; Ok(()) } // Deployment Service Implementation (placeholder - not part of MVP) #[derive(Debug, Default)] struct DeploymentServiceImpl; #[tonic::async_trait] impl DeploymentService for DeploymentServiceImpl { async fn create_deployment( &self, _request: Request, ) -> Result, Status> { Err(Status::unimplemented("create_deployment not yet implemented")) } async fn get_deployment( &self, _request: Request, ) -> Result, Status> { Err(Status::unimplemented("get_deployment not yet implemented")) } async fn list_deployments( &self, _request: Request, ) -> Result, Status> { Err(Status::unimplemented("list_deployments not yet implemented")) } async fn update_deployment( &self, _request: Request, ) -> Result, Status> { Err(Status::unimplemented("update_deployment not yet implemented")) } async fn delete_deployment( &self, _request: Request, ) -> Result, Status> { Err(Status::unimplemented("delete_deployment not yet implemented")) } }