feat: with monitor

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
2023-08-26 22:32:38 +02:00
parent 10eae9b36c
commit 569f5272e6
14 changed files with 526 additions and 95 deletions

View File

@@ -0,0 +1,85 @@
use std::sync::Arc;
use axum::{async_trait};
use churn_domain::{ServerEnrollReq};
use tokio::sync::Mutex;
#[derive(Clone)]
pub struct AgentService(Arc<dyn AgentServiceTrait + Send + Sync + 'static>);
impl std::ops::Deref for AgentService {
type Target = Arc<dyn AgentServiceTrait + Send + Sync + 'static>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Default for AgentService {
fn default() -> Self {
Self(Arc::new(DefaultAgentService::default()))
}
}
#[derive(Default)]
struct DefaultAgentService {
server: Arc<Mutex<String>>,
leases: Arc<Mutex<Vec<String>>>,
}
#[async_trait]
pub trait AgentServiceTrait {
async fn enroll(&self, agent_name: &str, server: &str, lease: &str) -> anyhow::Result<()>;
}
#[async_trait]
impl AgentServiceTrait for DefaultAgentService {
async fn enroll(&self, agent_name: &str, server: &str, lease: &str) -> anyhow::Result<()> {
let mut cur_server = self.server.lock().await;
let mut leases = self.leases.lock().await;
let client = reqwest::Client::new();
let req = client
.post(format!("{server}/agent/enroll"))
.json(&ServerEnrollReq {
lease: lease.into(),
agent_name: agent_name.into(),
})
.build()?;
let resp = client.execute(req).await?;
if !resp.status().is_success() {
if let Ok(text) = resp.text().await {
anyhow::bail!(
"could not enroll agent: {} at server: {}, error: {}",
agent_name,
server,
text
)
}
anyhow::bail!(
"could not enroll agent: {} at server: {}",
agent_name,
server
)
}
*cur_server = server.to_string();
leases.push(lease.to_string());
Ok(())
}
}

View File

@@ -1,7 +1,19 @@
mod agent;
use std::net::SocketAddr;
use axum::{response::IntoResponse, routing::get, Router};
use agent::AgentService;
use anyhow::Error;
use axum::{
extract::State,
http::StatusCode,
response::{IntoResponse, Response},
routing::{get, post},
Json, Router,
};
use churn_domain::AgentEnrollReq;
use clap::{Parser, Subcommand};
use serde_json::json;
#[derive(Parser)]
#[command(author, version, about, long_about = None, subcommand_required = true)]
@@ -30,6 +42,14 @@ enum Commands {
},
}
#[derive(Clone)]
#[derive(Default)]
struct AppState {
agent: AgentService,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
dotenv::dotenv().ok();
@@ -47,7 +67,10 @@ async fn handle_command(cmd: Command) -> anyhow::Result<()> {
Some(Commands::Daemon { host }) => {
tracing::info!("Starting churn server");
let app = Router::new().route("/ping", get(ping));
let app = Router::new()
.route("/enroll", post(enroll))
.route("/ping", get(ping))
.with_state(AppState::default());
tracing::info!("churn server listening on {}", host);
axum::Server::bind(&host)
@@ -58,14 +81,52 @@ async fn handle_command(cmd: Command) -> anyhow::Result<()> {
Ok(())
}
Some(Commands::Connect {
host,
token,
agent_name,
host: _,
token: _,
agent_name: _,
}) => todo!(),
None => todo!(),
}
}
enum AppError {
Internal(Error),
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, error_message) = match self {
AppError::Internal(e) => {
tracing::error!("failed with error: {}", e);
(
StatusCode::INTERNAL_SERVER_ERROR,
"failed with internal error",
)
}
};
let body = Json(json!({
"error": error_message,
}));
(status, body).into_response()
}
}
async fn ping() -> impl IntoResponse {
"pong!"
}
async fn enroll(
State(state): State<AppState>,
Json(req): Json<AgentEnrollReq>,
) -> Result<(), AppError> {
state
.agent
.enroll(&req.agent_name, &req.server, &req.lease)
.await
.map_err(AppError::Internal)?;
Ok(())
}