| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- //! Per-command handlers extracted from main.rs (the 500-line cap).
- //! `main.rs` keeps the clap dispatch; each `handle_*` does the command work.
- use crate::cli::{OutputFormat, PowerAction};
- use crate::config::Config;
- use crate::config::load_recipe;
- use crate::config::resolve_cluster;
- use crate::config::{Cluster, select_cluster_by_name};
- use crate::docker;
- use crate::exec;
- use crate::power;
- use crate::ssh::fetch_host_key;
- use anyhow::Result;
- use std::collections::HashSet;
- use tracing::info;
- /// Handle `logs` command: show container logs.
- pub(crate) async fn handle_logs(
- config: &Config,
- recipe_name: &str,
- cluster: &str,
- follow: bool,
- node: Option<String>,
- dry_run: bool,
- ) -> Result<()> {
- let cluster = resolve_cluster_for_op(config, recipe_name, cluster).await?;
- docker::show_logs(
- config,
- &cluster,
- recipe_name,
- follow,
- node.as_deref(),
- dry_run,
- )
- .await?;
- Ok(())
- }
- /// Handle `health` command: check cluster health or show host key fingerprints.
- pub(crate) async fn handle_health(
- config: &Config,
- cluster_name: Option<String>,
- fingerprint: bool,
- format: OutputFormat,
- ) -> Result<()> {
- if fingerprint {
- let clusters_to_check: Vec<Cluster> = match &cluster_name {
- Some(name) => vec![select_cluster_by_name(config, name)?],
- None => config.clusters.clone(),
- };
- for cluster in &clusters_to_check {
- println!(
- "\n{}",
- fluent_i18n::t!("cluster-section", { "name" => &cluster.name })
- );
- for node in &cluster.nodes {
- match fetch_host_key(config, node).await {
- Ok(fp) => println!(
- "{}",
- fluent_i18n::t!("host-key-line", { "name" => &node.name, "ip" => &node.ssh_ip, "key" => &fp })
- ),
- Err(e) => println!(
- "{}",
- fluent_i18n::t!("host-key-error", { "name" => &node.name, "ip" => &node.ssh_ip, "error" => &e.to_string() })
- ),
- }
- }
- }
- } else {
- match cluster_name {
- Some(ref name) => {
- let cluster = select_cluster_by_name(config, name)?;
- docker::health::health_check(config, &cluster, false, format).await?;
- }
- None => {
- let fleet = docker::health::collect(config, None, false).await?;
- docker::health::print_fleet(&fleet, format);
- }
- }
- }
- Ok(())
- }
- /// Handle `exec` command: run a command on cluster nodes via SSH.
- pub(crate) async fn handle_exec(
- config: &Config,
- cluster: Option<String>,
- recipe: Option<String>,
- node: Option<String>,
- alias: Option<String>,
- command: Vec<String>,
- dry_run: bool,
- ) -> Result<()> {
- let (cluster, _recipe) =
- exec::resolve_target(config, cluster.as_deref(), recipe.as_deref()).await?;
- let cmd_str = exec::resolve_command(config, alias.as_deref(), &command)?;
- exec::run(config, &cluster, node.as_deref(), &cmd_str, dry_run).await?;
- Ok(())
- }
- /// Handle `power` command: power on/off/switch nodes.
- pub(crate) async fn handle_power(
- config: &Config,
- action: PowerAction,
- dry_run: bool,
- ) -> Result<()> {
- match action {
- PowerAction::Off { cluster, node } => {
- let cluster = select_cluster_by_name(config, &cluster)?;
- let nodes = node.into_iter().collect::<Vec<_>>();
- power::power_off(config, &cluster, &nodes, dry_run).await?;
- }
- PowerAction::On { cluster, node } => {
- let cluster = select_cluster_by_name(config, &cluster)?;
- let nodes = node.into_iter().collect::<Vec<_>>();
- let targets = power::select_targets(&cluster, &nodes, "power on")?;
- let stale = if dry_run {
- Vec::new()
- } else {
- power::stale_nodes(&targets).await
- };
- let report = power::power_on(config, &cluster, &nodes, dry_run, &stale).await?;
- for line in report {
- info!("{}", line);
- }
- }
- PowerAction::Switch { from, to } => {
- let from = select_cluster_by_name(config, &from)?;
- let to = select_cluster_by_name(config, &to)?;
- power::power_switch(config, &from, &to, dry_run).await?;
- }
- }
- Ok(())
- }
- /// Resolve a cluster for container operations (stop/logs).
- /// When `auto`, tries live container label lookup first, then falls back
- /// to config-based auto-selection (for dry-run / offline scenarios).
- pub(crate) async fn resolve_cluster_for_op(
- config: &Config,
- recipe_name: &str,
- cluster_name: &str,
- ) -> Result<Cluster> {
- if cluster_name != "auto" {
- let recipe = load_recipe(recipe_name, &config.recipe_sources).await?;
- let (cluster, _) = resolve_cluster(config, &recipe, cluster_name, &HashSet::new())?;
- return Ok(cluster);
- }
- // Try live container label lookup first
- if let Ok(c) = docker::resolve_cluster_by_container_label(config, recipe_name).await {
- return Ok(c.clone());
- }
- // Fall back to config-based auto (dry-run / containers not running)
- let recipe = load_recipe(recipe_name, &config.recipe_sources).await?;
- let (cluster, _) = resolve_cluster(config, &recipe, "auto", &HashSet::new())?;
- Ok(cluster)
- }
|