config file
This commit is contained in:
+9
-5
@@ -7,6 +7,8 @@ param(
|
||||
|
||||
$PASSWD = Get-DefaultPassword $PASSWD
|
||||
|
||||
Write-Output $PSScriptRoot
|
||||
|
||||
# idk what this does it works like that then thats how it is
|
||||
pscp.exe -l root -scp -pw $PASSWD -r 192.168.100.1:/etc/ldlda_help $PSScriptRoot
|
||||
pscp.exe -l root -scp -pw $PASSWD 192.168.100.1:/etc/rc.local $PSScriptRoot
|
||||
@@ -23,13 +25,15 @@ New-Item -ItemType Directory -Force -Path $initDir | Out-Null
|
||||
$files = "update_wakey" , "wakey" , "update_tailscale" , "wireguard_setup", "lda-override"
|
||||
$remote = $files.ForEach({ "/etc/init.d/$_" })
|
||||
$remote | ForEach-Object {
|
||||
pscp.exe -l root -scp -pw $PASSWD "192.168.100.1:$_" "$initDir\"
|
||||
pscp.exe -l root -scp -pw $PASSWD "192.168.100.1:$_" "$initDir/"
|
||||
}
|
||||
|
||||
$hotplugDir = Join-Path $PSScriptRoot 'hotplug.d'
|
||||
New-Item -ItemType Directory -Force -Path $hotplugDir | Out-Null
|
||||
|
||||
$files = "dhcp/95-wakey", "neigh/95-wakey"
|
||||
$remote = $files.ForEach({ "/etc/hotplug.d/$_" })
|
||||
$hotplugDir = Join-Path $PSScriptRoot 'hotplug.d'
|
||||
|
||||
$remote | ForEach-Object {
|
||||
pscp.exe -l root -scp -pw $PASSWD "192.168.100.1:$_" "$hotplugDir\"
|
||||
$files | ForEach-Object {
|
||||
New-Item -ItemType Directory -Force -Path (Split-Path "$hotplugDir/$_")
|
||||
pscp.exe -l root -scp -pw $PASSWD "192.168.100.1:/etc/hotplug.d/$_" "$hotplugDir/$_"
|
||||
}
|
||||
|
||||
+40
-11
@@ -4,8 +4,6 @@ use clap::{ArgAction, Args, Parser, Subcommand};
|
||||
|
||||
use crate::config;
|
||||
|
||||
pub const DEFAULT_PID_FILE: &str = "/var/run/wakey-agent.pid";
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "wakey-agent")]
|
||||
#[command(version, about = "Outbound control-plane agent for Wakey")]
|
||||
@@ -42,9 +40,9 @@ pub struct ServeArgs {
|
||||
#[arg(long, short = 'c', default_value = config::DEFAULT_CONFIG_PATH)]
|
||||
pub config: PathBuf,
|
||||
|
||||
/// Path to pid file for reload signaling.
|
||||
#[arg(long, default_value = DEFAULT_PID_FILE)]
|
||||
pub pid_file: PathBuf,
|
||||
/// Path to pid file. Overrides `pid_file` in config.
|
||||
#[arg(long)]
|
||||
pub pid_file: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
@@ -69,9 +67,9 @@ pub struct EnrollArgs {
|
||||
#[arg(long)]
|
||||
pub reload_running: bool,
|
||||
|
||||
/// Path to pid file used when `--reload-running` is enabled.
|
||||
#[arg(long, default_value = DEFAULT_PID_FILE)]
|
||||
pub pid_file: PathBuf,
|
||||
/// Path to pid file used when `--reload-running` is enabled. Overrides `pid_file` in config.
|
||||
#[arg(long)]
|
||||
pub pid_file: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
@@ -107,9 +105,9 @@ pub struct InitConfigArgs {
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct ReloadArgs {
|
||||
/// Path to pid file for reload signaling.
|
||||
#[arg(long, default_value = DEFAULT_PID_FILE)]
|
||||
pub pid_file: PathBuf,
|
||||
/// Path to pid file for reload signaling. Overrides `pid_file` in config.
|
||||
#[arg(long)]
|
||||
pub pid_file: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
@@ -151,3 +149,34 @@ pub struct ObserveNeighArgs {
|
||||
#[arg(long)]
|
||||
pub ip: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use clap::Parser;
|
||||
|
||||
#[test]
|
||||
fn global_config_is_accepted_before_enroll() {
|
||||
let cli = Cli::try_parse_from([
|
||||
"wakey-agent",
|
||||
"--config",
|
||||
"/tmp/wakey-agent.toml",
|
||||
"enroll",
|
||||
"--server-url",
|
||||
"https://wakey.example.com",
|
||||
"--enroll-token",
|
||||
"token-123",
|
||||
])
|
||||
.expect("global --config should parse before enroll");
|
||||
|
||||
assert_eq!(cli.config, Some(PathBuf::from("/tmp/wakey-agent.toml")));
|
||||
let Command::Enroll(args) = cli.command else {
|
||||
panic!("expected enroll command");
|
||||
};
|
||||
assert_eq!(
|
||||
args.server_url.as_deref(),
|
||||
Some("https://wakey.example.com")
|
||||
);
|
||||
assert_eq!(args.enroll_token, "token-123");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub const DEFAULT_CONFIG_PATH: &str = "/etc/wakey-agent/config.toml";
|
||||
pub const DEFAULT_PID_FILE: &str = "/var/run/wakey-agent.pid";
|
||||
const WAKEY_DHCP_LEASES_ENV: &str = "WAKEY_DHCP_LEASES";
|
||||
const WAKEY_MAC_NAME_CACHE_ENV: &str = "WAKEY_MAC_NAME_CACHE";
|
||||
const WAKEY_OBSERVATION_STORE_ENV: &str = "WAKEY_OBSERVATION_STORE";
|
||||
@@ -22,6 +23,8 @@ pub struct AgentConfig {
|
||||
pub reconnect_max_ms: u64,
|
||||
#[serde(default = "default_observation_sync_interval_seconds")]
|
||||
pub observation_sync_interval_seconds: u64,
|
||||
#[serde(default = "default_pid_file")]
|
||||
pub pid_file: PathBuf,
|
||||
#[serde(default = "default_dhcp_leases_path")]
|
||||
pub dhcp_leases_path: PathBuf,
|
||||
#[serde(default = "default_mac_name_cache_path")]
|
||||
@@ -42,6 +45,7 @@ impl fmt::Debug for AgentConfig {
|
||||
"observation_sync_interval_seconds",
|
||||
&self.observation_sync_interval_seconds,
|
||||
)
|
||||
.field("pid_file", &self.pid_file)
|
||||
.field("dhcp_leases_path", &self.dhcp_leases_path)
|
||||
.field("mac_name_cache_path", &self.mac_name_cache_path)
|
||||
.field("observation_store_path", &self.observation_store_path)
|
||||
@@ -61,6 +65,10 @@ const fn default_observation_sync_interval_seconds() -> u64 {
|
||||
60
|
||||
}
|
||||
|
||||
fn default_pid_file() -> PathBuf {
|
||||
DEFAULT_PID_FILE.into()
|
||||
}
|
||||
|
||||
fn default_dhcp_leases_path() -> PathBuf {
|
||||
DEFAULT_DHCP_LEASES_PATH.into()
|
||||
}
|
||||
@@ -175,6 +183,7 @@ mod tests {
|
||||
reconnect_base_ms: 123,
|
||||
reconnect_max_ms: 456,
|
||||
observation_sync_interval_seconds: 7,
|
||||
pid_file: "/tmp/test-wakey-agent.pid".into(),
|
||||
dhcp_leases_path: "/tmp/test-dhcp.leases".into(),
|
||||
mac_name_cache_path: "/tmp/test-names.json".into(),
|
||||
observation_store_path: "/tmp/test-observations.json".into(),
|
||||
|
||||
@@ -27,6 +27,7 @@ pub async fn enroll(
|
||||
server_url: &str,
|
||||
enroll_token: &str,
|
||||
config_path: &Path,
|
||||
base_config: Option<&AgentConfig>,
|
||||
) -> Result<EnrollOutcome> {
|
||||
let server_url = normalize_server_url(server_url);
|
||||
let endpoint = format!("{server_url}/api/v1/agents/enroll");
|
||||
@@ -58,12 +59,27 @@ pub async fn enroll(
|
||||
server_url: payload.server_url.unwrap_or(server_url),
|
||||
agent_id: payload.agent_id,
|
||||
agent_token: payload.agent_token,
|
||||
reconnect_base_ms: 1_000,
|
||||
reconnect_max_ms: 30_000,
|
||||
observation_sync_interval_seconds: 60,
|
||||
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(),
|
||||
reconnect_base_ms: base_config
|
||||
.map(|config| config.reconnect_base_ms)
|
||||
.unwrap_or(1_000),
|
||||
reconnect_max_ms: base_config
|
||||
.map(|config| config.reconnect_max_ms)
|
||||
.unwrap_or(30_000),
|
||||
observation_sync_interval_seconds: base_config
|
||||
.map(|config| config.observation_sync_interval_seconds)
|
||||
.unwrap_or(60),
|
||||
pid_file: base_config
|
||||
.map(|config| config.pid_file.clone())
|
||||
.unwrap_or_else(|| crate::config::DEFAULT_PID_FILE.into()),
|
||||
dhcp_leases_path: base_config
|
||||
.map(|config| config.dhcp_leases_path.clone())
|
||||
.unwrap_or_else(|| "/tmp/dhcp.leases".into()),
|
||||
mac_name_cache_path: base_config
|
||||
.map(|config| config.mac_name_cache_path.clone())
|
||||
.unwrap_or_else(|| "/tmp/wakey_mac_names.json".into()),
|
||||
observation_store_path: base_config
|
||||
.map(|config| config.observation_store_path.clone())
|
||||
.unwrap_or_else(|| "/tmp/wakey_observations.json".into()),
|
||||
};
|
||||
let backup_path = save_config_with_backup(config_path, &config)?;
|
||||
info!(agent_id = %config.agent_id, config_path = %config_path.display(), "agent enrollment succeeded and config was written");
|
||||
@@ -143,7 +159,20 @@ mod tests {
|
||||
));
|
||||
let path = dir.join("config.toml");
|
||||
|
||||
let outcome = enroll(&server_url, "enroll-abc", &path)
|
||||
let base_config = AgentConfig {
|
||||
server_url: "https://old.example.com".into(),
|
||||
agent_id: "old-agent".into(),
|
||||
agent_token: "old-token".into(),
|
||||
reconnect_base_ms: 2_000,
|
||||
reconnect_max_ms: 60_000,
|
||||
observation_sync_interval_seconds: 30,
|
||||
pid_file: "/tmp/custom-wakey-agent.pid".into(),
|
||||
dhcp_leases_path: "/tmp/custom-dhcp.leases".into(),
|
||||
mac_name_cache_path: "/tmp/custom-names.json".into(),
|
||||
observation_store_path: "/tmp/custom-observations.json".into(),
|
||||
};
|
||||
|
||||
let outcome = enroll(&server_url, "enroll-abc", &path, Some(&base_config))
|
||||
.await
|
||||
.expect("enroll should succeed");
|
||||
let config = outcome.config;
|
||||
@@ -151,6 +180,8 @@ mod tests {
|
||||
assert_eq!(config.agent_id, "agent-123");
|
||||
assert_eq!(config.agent_token, "token-xyz");
|
||||
assert_eq!(config.server_url, "https://control.example.com");
|
||||
assert_eq!(config.pid_file, base_config.pid_file);
|
||||
assert_eq!(config.dhcp_leases_path, base_config.dhcp_leases_path);
|
||||
assert!(outcome.backup_path.is_none());
|
||||
|
||||
let persisted = crate::config::load_config(&path).expect("load persisted config");
|
||||
|
||||
+89
-17
@@ -29,21 +29,30 @@ async fn main() -> Result<()> {
|
||||
if let Some(config) = global_config {
|
||||
args.config = config.to_path_buf();
|
||||
}
|
||||
let existing_config = config::load_config(&args.config).ok();
|
||||
let resolved_server_url = if let Some(server_url) = args.server_url.as_deref() {
|
||||
server_url.to_string()
|
||||
} else if let Some(cfg) = existing_config.as_ref() {
|
||||
cfg.server_url.clone()
|
||||
} else {
|
||||
match config::load_config(&args.config) {
|
||||
Ok(cfg) => cfg.server_url,
|
||||
Err(_) => anyhow::bail!(
|
||||
"missing control-plane URL: pass --server-url or provide server_url in {}",
|
||||
args.config.display()
|
||||
),
|
||||
}
|
||||
anyhow::bail!(
|
||||
"missing control-plane URL: pass --server-url or provide server_url in {}",
|
||||
args.config.display()
|
||||
);
|
||||
};
|
||||
|
||||
::tracing::info!(server_url = %resolved_server_url, config = %args.config.display(), "wakey-agent command: enroll");
|
||||
let outcome =
|
||||
enroll::enroll(&resolved_server_url, &args.enroll_token, &args.config).await?;
|
||||
let outcome = enroll::enroll(
|
||||
&resolved_server_url,
|
||||
&args.enroll_token,
|
||||
&args.config,
|
||||
existing_config.as_ref(),
|
||||
)
|
||||
.await?;
|
||||
let pid_file = args
|
||||
.pid_file
|
||||
.as_deref()
|
||||
.unwrap_or(outcome.config.pid_file.as_path());
|
||||
println!("agent_id={}", outcome.config.agent_id);
|
||||
println!("config={}", args.config.display());
|
||||
println!("config_write=updated");
|
||||
@@ -51,10 +60,10 @@ async fn main() -> Result<()> {
|
||||
println!("config_backup={}", backup_path.display());
|
||||
}
|
||||
if args.reload_running {
|
||||
match serve::reload_daemon(&args.pid_file) {
|
||||
Ok(()) => println!("reload=signaled pid_file={}", args.pid_file.display()),
|
||||
match serve::reload_daemon(pid_file) {
|
||||
Ok(()) => println!("reload=signaled pid_file={}", pid_file.display()),
|
||||
Err(err) => {
|
||||
::tracing::warn!(error = %err, pid_file = %args.pid_file.display(), "enroll completed but daemon reload failed");
|
||||
::tracing::warn!(error = %err, pid_file = %pid_file.display(), "enroll completed but daemon reload failed");
|
||||
if let Some(backup_path) = &outcome.backup_path {
|
||||
match config::restore_backup(&args.config, backup_path) {
|
||||
Ok(()) => {
|
||||
@@ -83,9 +92,10 @@ async fn main() -> Result<()> {
|
||||
println!("reload=not_requested");
|
||||
println!("runtime_config=unchanged_until_reload_or_restart");
|
||||
println!(
|
||||
"next=wakey-agent reload --pid-file {} # or restart wakey-agent",
|
||||
args.pid_file.display()
|
||||
"next=wakey-agent reload --pid-file {} # if daemon is already running",
|
||||
pid_file.display()
|
||||
);
|
||||
println!("run={}", serve_command_for_config(&args.config));
|
||||
}
|
||||
}
|
||||
Command::InitConfig(mut args) => {
|
||||
@@ -98,8 +108,9 @@ async fn main() -> Result<()> {
|
||||
init_config(args)?
|
||||
}
|
||||
Command::Reload(args) => {
|
||||
::tracing::info!(pid_file = %args.pid_file.display(), "wakey-agent command: reload");
|
||||
serve::reload_daemon(&args.pid_file)?
|
||||
let pid_file = resolve_pid_file(global_config, args.pid_file.as_deref())?;
|
||||
::tracing::info!(pid_file = %pid_file.display(), "wakey-agent command: reload");
|
||||
serve::reload_daemon(&pid_file)?
|
||||
}
|
||||
Command::Observe(mut args) => {
|
||||
if let Some(config) = global_config {
|
||||
@@ -112,6 +123,27 @@ async fn main() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serve_command_for_config(config: &std::path::Path) -> String {
|
||||
if config == std::path::Path::new(config::DEFAULT_CONFIG_PATH) {
|
||||
"wakey-agent serve".to_string()
|
||||
} else {
|
||||
format!("wakey-agent --config {} serve", config.display())
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_pid_file(
|
||||
config_path: Option<&std::path::Path>,
|
||||
explicit_pid_file: Option<&std::path::Path>,
|
||||
) -> Result<std::path::PathBuf> {
|
||||
if let Some(pid_file) = explicit_pid_file {
|
||||
return Ok(pid_file.to_path_buf());
|
||||
}
|
||||
if let Some(config_path) = config_path {
|
||||
return Ok(config::load_config(config_path)?.pid_file);
|
||||
}
|
||||
Ok(config::DEFAULT_PID_FILE.into())
|
||||
}
|
||||
|
||||
fn observe(args: cli::ObserveArgs) -> Result<()> {
|
||||
let mut cmd = std::process::Command::new(resolve_wakey_binary());
|
||||
cmd.arg("observe");
|
||||
@@ -211,6 +243,7 @@ fn init_config(args: InitConfigArgs) -> Result<()> {
|
||||
reconnect_base_ms: 1_000,
|
||||
reconnect_max_ms: 30_000,
|
||||
observation_sync_interval_seconds: 60,
|
||||
pid_file: config::DEFAULT_PID_FILE.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(),
|
||||
@@ -230,10 +263,49 @@ fn init_config(args: InitConfigArgs) -> Result<()> {
|
||||
if let Some(path) = &args.config {
|
||||
config::save_config(path, &cfg)?;
|
||||
println!("config={}", path.display());
|
||||
println!("next=wakey-agent serve --config {}", path.display());
|
||||
println!("run={}", serve_command_for_config(path));
|
||||
} else {
|
||||
let rendered = toml::to_string_pretty(&cfg)?;
|
||||
print!("{}", rendered);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serve_command_omits_default_config_path() {
|
||||
assert_eq!(
|
||||
serve_command_for_config(std::path::Path::new(config::DEFAULT_CONFIG_PATH)),
|
||||
"wakey-agent serve"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serve_command_includes_custom_config_path() {
|
||||
assert_eq!(
|
||||
serve_command_for_config(std::path::Path::new("/tmp/wakey-agent.toml")),
|
||||
"wakey-agent --config /tmp/wakey-agent.toml serve"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_pid_file_wins_without_loading_config() {
|
||||
let pid_file = resolve_pid_file(
|
||||
Some(std::path::Path::new("/tmp/missing-agent.toml")),
|
||||
Some(std::path::Path::new("/tmp/explicit.pid")),
|
||||
)
|
||||
.expect("explicit pid should resolve");
|
||||
|
||||
assert_eq!(pid_file, std::path::PathBuf::from("/tmp/explicit.pid"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pid_file_defaults_when_no_config_is_available() {
|
||||
let pid_file = resolve_pid_file(None, None).expect("default pid should resolve");
|
||||
|
||||
assert_eq!(pid_file, std::path::PathBuf::from(config::DEFAULT_PID_FILE));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,11 @@ pub async fn serve(args: ServeArgs) -> Result<()> {
|
||||
);
|
||||
}
|
||||
|
||||
write_pid_file(&args.pid_file)?;
|
||||
info!(pid_file = %args.pid_file.display(), "wrote wakey-agent pid file");
|
||||
|
||||
let mut cfg = config::load_config(&args.config)?;
|
||||
let pid_file = args.pid_file.unwrap_or_else(|| cfg.pid_file.clone());
|
||||
write_pid_file(&pid_file)?;
|
||||
info!(pid_file = %pid_file.display(), "wrote wakey-agent pid file");
|
||||
|
||||
info!(config_path = %args.config.display(), agent_id = %cfg.agent_id, "starting wakey-agent");
|
||||
|
||||
let mut worker = tokio::spawn(session::run(cfg.clone()));
|
||||
@@ -51,7 +52,7 @@ pub async fn serve(args: ServeArgs) -> Result<()> {
|
||||
}
|
||||
join = &mut worker => {
|
||||
warn!("agent worker task exited; shutting down daemon");
|
||||
let _ = remove_pid_file(&args.pid_file);
|
||||
let _ = remove_pid_file(&pid_file);
|
||||
return join.context("agent session join failed")?;
|
||||
}
|
||||
}
|
||||
@@ -66,7 +67,7 @@ pub async fn serve(args: ServeArgs) -> Result<()> {
|
||||
worker.abort();
|
||||
}
|
||||
|
||||
let _ = remove_pid_file(&args.pid_file);
|
||||
let _ = remove_pid_file(&pid_file);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user