register and list workers

This commit is contained in:
2026-05-19 23:02:14 +02:00
parent beb4797119
commit 226dfa0e36
6 changed files with 119 additions and 9 deletions

View File

@@ -75,8 +75,12 @@ pub enum Command {
name: Option<String>,
},
/// List registered workers and their reachability status
Workers,
/// List registered workers
Workers {
/// Check reachability of each worker over SSH
#[arg(short, long)]
check: bool,
},
/// Set the default worker
Default {

View File

@@ -1,6 +1,43 @@
use anyhow::Result;
use crate::{config, ssh};
pub fn execute(connection: &str, name: Option<&str>) -> Result<()> {
let _ = (connection, name);
anyhow::bail!("not yet implemented")
let mut cfg = config::load()?;
let name = match name {
Some(n) => n.to_string(),
None => ssh::hostname_from_connection(connection),
};
// Reject duplicates (same name or same connection string).
if cfg.get_worker(&name).is_some() {
anyhow::bail!(
"a worker named '{}' is already registered\n\
Use 'p default {}' to make it the default, or choose a different name with -n",
name, name
);
}
let first = cfg.workers.is_empty();
cfg.workers.push(config::WorkerConfig {
name: name.clone(),
connection: connection.to_string(),
});
// First registered worker becomes the default automatically.
if first {
cfg.default_worker = Some(name.clone());
}
config::save(&cfg)?;
if first {
println!("Registered '{}' and set as default worker.", name);
} else {
println!("Registered '{}'.", name);
println!("Run 'p default {}' to make it the default.", name);
}
Ok(())
}

View File

@@ -1,6 +1,25 @@
use anyhow::Result;
use crate::config;
pub fn execute(worker: &str) -> Result<()> {
let _ = worker;
anyhow::bail!("not yet implemented")
let mut cfg = config::load()?;
if cfg.get_worker(worker).is_none() {
let known: Vec<&str> = cfg.workers.iter().map(|w| w.name.as_str()).collect();
if known.is_empty() {
anyhow::bail!("no workers registered yet — run 'p register <connection>' first");
}
anyhow::bail!(
"unknown worker '{}'\n\nKnown workers: {}",
worker,
known.join(", ")
);
}
cfg.default_worker = Some(worker.to_string());
config::save(&cfg)?;
println!("Default worker set to '{}'.", worker);
Ok(())
}

View File

@@ -1,5 +1,37 @@
use anyhow::Result;
pub fn execute() -> Result<()> {
anyhow::bail!("not yet implemented")
use crate::{config, ssh};
pub fn execute(check: bool) -> Result<()> {
let cfg = config::load()?;
if cfg.workers.is_empty() {
println!("No workers registered. Run 'p register <connection>' to add one.");
return Ok(());
}
let name_w = cfg.workers.iter().map(|w| w.name.len()).max().unwrap_or(0).max(4);
let conn_w = cfg.workers.iter().map(|w| w.connection.len()).max().unwrap_or(0).max(10);
if check {
println!(" {:<name_w$} {:<conn_w$} STATUS", "NAME", "CONNECTION");
println!(" {:<name_w$} {:<conn_w$} ------", "-".repeat(name_w), "-".repeat(conn_w));
} else {
println!(" {:<name_w$} CONNECTION", "NAME");
println!(" {:<name_w$} ----------", "-".repeat(name_w));
}
for worker in &cfg.workers {
let is_default = cfg.default_worker.as_deref() == Some(&worker.name);
let prefix = if is_default { "* " } else { " " };
if check {
let status = if ssh::is_reachable(worker) { "reachable" } else { "unreachable" };
println!("{}{:<name_w$} {:<conn_w$} {}", prefix, worker.name, worker.connection, status);
} else {
println!("{}{:<name_w$} {}", prefix, worker.name, worker.connection);
}
}
Ok(())
}

View File

@@ -61,7 +61,7 @@ fn main() -> Result<()> {
cli::Command::Register { connection, name } => {
commands::register::execute(&connection, name.as_deref())
}
cli::Command::Workers => commands::workers::execute(),
cli::Command::Workers { check } => commands::workers::execute(check),
cli::Command::Default { worker } => commands::set_default::execute(&worker),
}
}

View File

@@ -32,6 +32,24 @@ pub fn hostname_from_connection(conn: &str) -> String {
}
}
/// Check whether a worker is reachable over SSH (5 s timeout, no auth prompts).
pub fn is_reachable(worker: &WorkerConfig) -> bool {
let mut args = vec![
"-o".to_string(), "ConnectTimeout=5".to_string(),
"-o".to_string(), "BatchMode=yes".to_string(),
];
args.extend(ssh_args(worker));
args.push("true".to_string());
std::process::Command::new("ssh")
.args(&args)
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false)
}
/// Build the base ssh argument list for a worker (handles custom port).
pub fn ssh_args(worker: &WorkerConfig) -> Vec<String> {
let (user_host, port) = parse_connection(&worker.connection);