feat: add agent module with Nginx service commands and routes
- Introduced a new agent module with commands for managing Nginx configurations. - Implemented `NginxService` for handling reload, validation, and configuration writing. - Added routes for status, validation, and configuration writing using Axum. - Created necessary command files: `reload.rs`, `run.rs`, `validate.rs`, `write_config.rs`. - Updated `Cargo.toml` and `Cargo.lock` to include new dependencies. - Added `.gitignore` for the agent module. - Updated `justfile` to include OpenAPI generation for the agent.
This commit is contained in:
83
apps/agent/src/main.rs
Normal file
83
apps/agent/src/main.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
mod commands;
|
||||
mod routes;
|
||||
|
||||
use axum::routing::get;
|
||||
use axum::{Router, routing::post};
|
||||
use clap::Parser;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::net::UnixListener;
|
||||
use tracing::{error, info};
|
||||
|
||||
use crate::commands::NginxService;
|
||||
use crate::routes::{status, validate, validate_and_reload, write_config};
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Args {
|
||||
/// Unix socket path to bind the daemon to
|
||||
sock: String,
|
||||
|
||||
/// Directory where generated nginx config files will be written
|
||||
#[arg(long, default_value = "/etc/nginx/conf.d")]
|
||||
nginx_config_dir: PathBuf,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let args = Args::parse();
|
||||
let sock = args.sock;
|
||||
|
||||
let path = PathBuf::from(&sock);
|
||||
if let Some(dir) = path.parent() {
|
||||
tokio::fs::create_dir_all(dir).await?;
|
||||
// permissive; set tighter perms in production via image/build steps
|
||||
tokio::fs::set_permissions(dir, std::fs::Permissions::from_mode(0o770)).await?;
|
||||
}
|
||||
// If an existing path exists at the socket location, ensure it's a socket
|
||||
match tokio::fs::metadata(&path).await {
|
||||
Ok(md) => {
|
||||
use std::os::unix::fs::FileTypeExt;
|
||||
if md.file_type().is_socket() {
|
||||
tokio::fs::remove_file(&path).await?;
|
||||
} else {
|
||||
return Err(
|
||||
format!("Socket path {} exists and is not a socket", path.display()).into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
|
||||
// bind using tokio's UnixListener (avoids converting a blocking std listener)
|
||||
let listener = UnixListener::bind(&path)?;
|
||||
// set socket perms to 0660 (best-effort)
|
||||
if let Err(err) =
|
||||
tokio::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o660)).await
|
||||
{
|
||||
error!(
|
||||
"Warning: failed to set permissions on socket {}: {}",
|
||||
path.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
|
||||
let scheduler = Arc::new(tokio_cron_scheduler::JobScheduler::new().await?);
|
||||
|
||||
let app = Router::new()
|
||||
.route("/status", get(status))
|
||||
.route("/validate_and_reload", post(validate_and_reload))
|
||||
.route("/validate", post(validate))
|
||||
.route("/write_config", post(write_config))
|
||||
.with_state(NginxService::new(scheduler.clone(), args.nginx_config_dir).await?);
|
||||
|
||||
scheduler.start().await?;
|
||||
|
||||
info!("Starting yanpm-daemon on unix socket: {}", sock);
|
||||
axum::serve::serve(listener, app).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user