1use std::{path::PathBuf, sync::Arc, time::Duration};
3
4use anyhow::Result;
5use clap::Parser;
6use salix_config::get_config;
7use salix_proto::{
8 MessageRequest, RegistrationRequest,
9 controller_service_server::{ControllerService, ControllerServiceServer},
10};
11use tokio::{sync::Mutex, task::JoinSet, time::sleep};
12use tonic::{Request, Response, Status, transport::Server};
13
14pub const ALPN_QUIC_SALIX: &[&[u8]] = &[b"salix"];
16
17#[derive(Parser)]
19#[command(version, about, long_about = None)]
20pub struct Cli {
21 #[arg(short, long, value_name = "FILE")]
22 config: Option<PathBuf>,
23}
24
25#[derive(Debug)]
26struct Agent {
27 agent_id: String,
28 hostname: String,
29 version: String,
30}
31
32#[derive(Debug, Default)]
33struct ControllerState {
34 agents: Vec<Agent>,
35}
36
37#[derive(Debug, Default)]
38struct Controller {
39 state: Arc<Mutex<ControllerState>>,
40}
41
42impl Controller {
43 fn new(state: Arc<Mutex<ControllerState>>) -> Self {
44 Self { state }
45 }
46}
47
48#[tonic::async_trait]
49impl ControllerService for Controller {
50 async fn register(
51 &self,
52 request: Request<RegistrationRequest>,
53 ) -> Result<Response<()>, Status> {
54 let request = request.into_inner();
55 if request.agent_id.is_none() {
56 return Err(Status::invalid_argument("agent_id is required"));
57 }
58
59 let mut state = self.state.lock().await;
60 state.agents.push(Agent {
61 agent_id: request.agent_id().to_owned(),
62 hostname: request.hostname().to_owned(),
63 version: request.version().to_owned(),
64 });
65
66 Ok(Response::new(()))
67 }
68
69 async fn message(&self, _request: Request<MessageRequest>) -> Result<Response<()>, Status> {
70 Ok(Response::new(()))
71 }
72}
73
74async fn print_state(state_m: Arc<Mutex<ControllerState>>) -> Result<(), tonic::transport::Error> {
75 loop {
76 let state = state_m.lock().await;
77 println!("Currently registered agents:");
78 for agent in &state.agents {
79 println!(
80 "{agent_id} {hostname} {version}",
81 agent_id = agent.agent_id,
82 hostname = agent.hostname,
83 version = agent.version
84 );
85 }
86 drop(state);
87 sleep(Duration::from_secs(10)).await;
88 }
89}
90
91#[tokio::main]
93pub async fn run(cli: Cli) -> Result<()> {
94 let config = get_config(cli.config)?;
95
96 let state = Arc::new(Mutex::new(ControllerState::default()));
97
98 let controller = Controller::new(state.clone());
99
100 let server = Server::builder()
101 .add_service(ControllerServiceServer::new(controller))
102 .serve(config.controller.listen);
103 let state_printer = print_state(state.clone());
104
105 let mut set = JoinSet::new();
106 set.spawn(server);
107 set.spawn(state_printer);
108 set.join_all().await;
109
110 Ok(())
111}