use clap::Parser;
use emerwen_protocol::{
emerwen_protocol_server::{EmerwenProtocol, EmerwenProtocolServer},
target::{MethodGet, MethodPing},
SetTargetStateRequest, Target, TargetsResponse,
};
use tonic::{transport::Server, Request, Response, Status};
use tracing::{debug, info, level_filters::LevelFilter};
use tracing_subscriber::{prelude::*, EnvFilter};
#[derive(Parser)]
#[command(version)]
struct Args {
/// Enable debug logging
#[arg(long)]
debug: bool,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = Args::parse();
tracing_subscriber::registry()
.with(
tracing_subscriber::fmt::layer()
.with_file(args.debug)
.with_line_number(args.debug)
.with_level(true),
)
.with(
EnvFilter::builder()
.with_default_directive(if args.debug {
LevelFilter::DEBUG.into()
} else {
LevelFilter::INFO.into()
})
.from_env_lossy(),
)
.init();
let addr = "127.0.0.1:8000";
info!(message = "Starting emerwen master...", %addr);
debug!("Hello, debug!");
let workers = vec![Worker {
id: 0,
auth_token: "avain_perkele".into(),
targets: vec![
Target {
id: 0,
addr: "127.0.0.1".into(),
interval: 2000,
method: Some(MethodPing {}.into()),
},
Target {
id: 1,
addr: "https://liljamo.com/".into(),
interval: 2000,
method: Some(
MethodGet {
ok_codes: vec![200],
}
.into(),
),
},
Target {
id: 2,
addr: "10.1.2.30".into(),
interval: 2000,
method: Some(MethodPing {}.into()),
},
],
}];
let server = MasterServer::new(workers);
Server::builder()
.add_service(EmerwenProtocolServer::with_interceptor(
server,
move |req: Request<()>| {
match req.metadata().get("authorization") {
Some(_token) => {
// TODO: check
Ok(req)
}
None => Err(Status::unauthenticated("invalid auth")),
}
},
))
.serve(addr.parse()?)
.await?;
Ok(())
}
struct MasterServer {
workers: Vec<Worker>,
}
impl MasterServer {
fn new(workers: Vec<Worker>) -> MasterServer {
MasterServer { workers }
}
}
#[tonic::async_trait]
impl EmerwenProtocol for MasterServer {
async fn get_targets(&self, request: Request<()>) -> Result<Response<TargetsResponse>, Status> {
let token = match request.metadata().get("authorization") {
Some(value) => match value.to_str() {
Ok(bearer_token) => bearer_token.trim_start_matches("Bearer "),
Err(_) => {
return Err(Status::invalid_argument(
"couldn't read bearer auth to string",
))
}
},
None => {
return Err(Status::unauthenticated(
"request had no authorization, when it should",
))
}
};
let targets = match self
.workers
.iter()
.find(|worker| *worker.auth_token == *token)
{
Some(worker) => worker.targets.clone(),
None => {
return Err(Status::invalid_argument(
"no worker exists with the auth token",
))
}
};
Ok(Response::new(TargetsResponse { targets }))
}
async fn set_target_state(
&self,
request: Request<SetTargetStateRequest>,
) -> Result<Response<()>, Status> {
info!("{:?}", request.into_inner());
Ok(Response::new(()))
}
}
struct Worker {
pub id: u32,
pub auth_token: String,
pub targets: Vec<Target>,
}