im pretty sure every subcommands use the config file

This commit is contained in:
lda
2026-04-27 04:13:50 +07:00 Verified
parent 1a259ed1cf
commit 4efa70c228
4 changed files with 86 additions and 24 deletions
+19 -1
View File
@@ -14,6 +14,10 @@ pub struct Cli {
#[arg(short = 'v', long = "verbose", action = ArgAction::Count, global = true)] #[arg(short = 'v', long = "verbose", action = ArgAction::Count, global = true)]
pub verbose: u8, pub verbose: u8,
/// Path to the agent config file for commands that use agent config.
#[arg(long, global = true)]
pub config: Option<PathBuf>,
#[command(subcommand)] #[command(subcommand)]
pub command: Command, pub command: Command,
} }
@@ -28,6 +32,9 @@ pub enum Command {
InitConfig(InitConfigArgs), InitConfig(InitConfigArgs),
/// Reload a running agent daemon by sending SIGHUP. /// Reload a running agent daemon by sending SIGHUP.
Reload(ReloadArgs), Reload(ReloadArgs),
/// Upload local observations to the control plane once and exit.
#[command(visible_alias = "sync")]
SyncObservations(SyncObservationsArgs),
/// Pass local hotplug observations through to the wakey CLI. /// Pass local hotplug observations through to the wakey CLI.
Observe(ObserveArgs), Observe(ObserveArgs),
} }
@@ -76,6 +83,10 @@ pub struct InitConfigArgs {
#[arg(long)] #[arg(long)]
pub config: Option<PathBuf>, pub config: Option<PathBuf>,
/// Existing agent config to use as a base before applying explicit overrides.
#[arg(long)]
pub from_config: Option<PathBuf>,
/// Print config to stdout. /// Print config to stdout.
#[arg(long, conflicts_with = "config")] #[arg(long, conflicts_with = "config")]
pub stdout: bool, pub stdout: bool,
@@ -104,10 +115,17 @@ pub struct ReloadArgs {
pub pid_file: PathBuf, pub pid_file: PathBuf,
} }
#[derive(Args)]
pub struct SyncObservationsArgs {
/// Path to the agent config file.
#[arg(long, default_value = config::DEFAULT_CONFIG_PATH)]
pub config: PathBuf,
}
#[derive(Args)] #[derive(Args)]
pub struct ObserveArgs { pub struct ObserveArgs {
/// Path to the agent config file. If present, local path settings are passed through. /// Path to the agent config file. If present, local path settings are passed through.
#[arg(long, default_value = config::DEFAULT_CONFIG_PATH)] #[arg(long, global = true, default_value = config::DEFAULT_CONFIG_PATH)]
pub config: PathBuf, pub config: PathBuf,
#[command(subcommand)] #[command(subcommand)]
+57 -20
View File
@@ -15,13 +15,20 @@ use cli::{Cli, Command, InitConfigArgs, ObserveCommand};
async fn main() -> Result<()> { async fn main() -> Result<()> {
let cli = Cli::parse(); let cli = Cli::parse();
tracing::init(cli.verbose); tracing::init(cli.verbose);
let global_config = cli.config.as_deref();
match cli.command { match cli.command {
Command::Serve(args) => { Command::Serve(mut args) => {
if let Some(config) = global_config {
args.config = config.to_path_buf();
}
::tracing::info!("wakey-agent command: serve"); ::tracing::info!("wakey-agent command: serve");
serve::serve(args).await? serve::serve(args).await?
} }
Command::Enroll(args) => { Command::Enroll(mut args) => {
if let Some(config) = global_config {
args.config = config.to_path_buf();
}
let resolved_server_url = if let Some(server_url) = args.server_url.as_deref() { let resolved_server_url = if let Some(server_url) = args.server_url.as_deref() {
server_url.to_string() server_url.to_string()
} else { } else {
@@ -81,12 +88,34 @@ async fn main() -> Result<()> {
); );
} }
} }
Command::InitConfig(args) => init_config(args)?, Command::InitConfig(mut args) => {
if let Some(config) = global_config
&& args.config.is_none()
&& !args.stdout
{
args.config = Some(config.to_path_buf());
}
init_config(args)?
}
Command::Reload(args) => { Command::Reload(args) => {
::tracing::info!(pid_file = %args.pid_file.display(), "wakey-agent command: reload"); ::tracing::info!(pid_file = %args.pid_file.display(), "wakey-agent command: reload");
serve::reload_daemon(&args.pid_file)? serve::reload_daemon(&args.pid_file)?
} }
Command::Observe(args) => observe(args)?, Command::SyncObservations(mut args) => {
if let Some(config) = global_config {
args.config = config.to_path_buf();
}
::tracing::info!(config = %args.config.display(), "wakey-agent command: sync-observations");
let cfg = config::load_config(&args.config)?;
let accepted = session::sync_observations_once(&cfg).await?;
println!("observations_synced={accepted}");
}
Command::Observe(mut args) => {
if let Some(config) = global_config {
args.config = config.to_path_buf();
}
observe(args)?
}
} }
Ok(()) Ok(())
@@ -164,24 +193,32 @@ fn init_config(args: InitConfigArgs) -> Result<()> {
); );
} }
let cfg = config::AgentConfig { let mut cfg = if let Some(from_config) = &args.from_config {
server_url: args config::load_config(from_config)?
.server_url } else {
.unwrap_or_else(|| "https://wakey.ldlda.com".to_string()), config::AgentConfig {
agent_id: args server_url: "https://wakey.ldlda.com".to_string(),
.agent_id agent_id: "REPLACE_ME_AGENT_ID".to_string(),
.unwrap_or_else(|| "REPLACE_ME_AGENT_ID".to_string()), agent_token: "REPLACE_ME_AGENT_TOKEN".to_string(),
agent_token: args reconnect_base_ms: 1_000,
.agent_token reconnect_max_ms: 30_000,
.unwrap_or_else(|| "REPLACE_ME_AGENT_TOKEN".to_string()), observation_sync_interval_seconds: 60,
reconnect_base_ms: 1_000, dhcp_leases_path: "/tmp/dhcp.leases".into(),
reconnect_max_ms: 30_000, mac_name_cache_path: "/tmp/wakey_mac_names.json".into(),
observation_sync_interval_seconds: 60, observation_store_path: "/tmp/wakey_observations.json".into(),
dhcp_leases_path: "/tmp/dhcp.leases".into(), }
mac_name_cache_path: "/tmp/wakey_mac_names.json".into(),
observation_store_path: "/tmp/wakey_observations.json".into(),
}; };
if let Some(server_url) = args.server_url {
cfg.server_url = server_url;
}
if let Some(agent_id) = args.agent_id {
cfg.agent_id = agent_id;
}
if let Some(agent_token) = args.agent_token {
cfg.agent_token = agent_token;
}
if let Some(path) = &args.config { if let Some(path) = &args.config {
config::save_config(path, &cfg)?; config::save_config(path, &cfg)?;
println!("config={}", path.display()); println!("config={}", path.display());
+8 -3
View File
@@ -164,13 +164,18 @@ struct AgentObservationRequest {
last_seen_unix: u64, last_seen_unix: u64,
} }
async fn send_agent_observations(client: &reqwest::Client, config: &AgentConfig) -> Result<()> { pub async fn sync_observations_once(config: &AgentConfig) -> Result<usize> {
let client = reqwest::Client::new();
send_agent_observations(&client, config).await
}
async fn send_agent_observations(client: &reqwest::Client, config: &AgentConfig) -> Result<usize> {
let observations = let observations =
wakey::wakey_linux::dhcp::list_local_observations_from_path(&config.observation_store_path) wakey::wakey_linux::dhcp::list_local_observations_from_path(&config.observation_store_path)
.await .await
.context("failed to read local observations")?; .context("failed to read local observations")?;
if observations.is_empty() { if observations.is_empty() {
return Ok(()); return Ok(0);
} }
let url = observations_url(&config.server_url)?; let url = observations_url(&config.server_url)?;
@@ -210,7 +215,7 @@ async fn send_agent_observations(client: &reqwest::Client, config: &AgentConfig)
observations = payload.observations.len(), observations = payload.observations.len(),
"synced local observations" "synced local observations"
); );
Ok(()) Ok(payload.observations.len())
} }
pub fn next_backoff_ms(current_ms: u64, max_ms: u64) -> u64 { pub fn next_backoff_ms(current_ms: u64, max_ms: u64) -> u64 {
+2
View File
@@ -167,6 +167,7 @@ pub async fn observe_dhcp_event(
hostname: Option<&str>, hostname: Option<&str>,
) -> io::Result<bool> { ) -> io::Result<bool> {
if !matches!(action, "add" | "update" | "old" | "remove") { if !matches!(action, "add" | "update" | "old" | "remove") {
// old not emitted by hotplug
return Ok(false); return Ok(false);
} }
@@ -228,6 +229,7 @@ pub async fn observe_neighbor_event(
ip: Option<IpAddr>, ip: Option<IpAddr>,
) -> io::Result<bool> { ) -> io::Result<bool> {
if !matches!(action, "add" | "update" | "old" | "remove") { if !matches!(action, "add" | "update" | "old" | "remove") {
// update and remove not emitted by hotplug
return Ok(false); return Ok(false);
} }
let Some(key) = mac let Some(key) = mac