Skip to content

Commit

Permalink
Add a simple status command
Browse files Browse the repository at this point in the history
  • Loading branch information
james-w committed Aug 1, 2024
1 parent 38833da commit 46fb815
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 5 deletions.
8 changes: 7 additions & 1 deletion src/cmd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod execute;
mod list;
mod run;
mod start;
mod status;
mod stop;

use crate::cleanup::CleanupManager;
Expand All @@ -17,6 +18,7 @@ pub use execute::Execute;
use list::ListCommand;
use run::RunCommand;
use start::StartCommand;
use status::StatusCommand;
use stop::StopCommand;

#[derive(Parser, Debug)]
Expand Down Expand Up @@ -54,7 +56,10 @@ pub enum Commands {

/// List available targets
List(ListCommand),
// TODO: status, logs

/// Get the status of a daemon
Status(StatusCommand),
// TODO: logs
}

impl Execute for Commands {
Expand All @@ -65,6 +70,7 @@ impl Execute for Commands {
Commands::Stop(cmd) => cmd.execute(context, cleanup_manager),
Commands::Build(cmd) => cmd.execute(context, cleanup_manager),
Commands::List(cmd) => cmd.execute(context, cleanup_manager),
Commands::Status(cmd) => cmd.execute(context, cleanup_manager),
}
}
}
56 changes: 56 additions & 0 deletions src/cmd/status.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use std::sync::{Arc, Mutex};

use anyhow::{anyhow, Result};
use clap::Parser;

use crate::cleanup::CleanupManager;
use crate::cmd::execute::Execute;
use crate::context::{CommandLookupResult, Context};
use crate::outputs::OutputsManager;
use crate::target::{StatusResult, Targetable};

#[derive(Parser, Debug)]
pub struct StatusCommand {
/// The name of the target to get status for
pub name: String,
}

impl Execute for StatusCommand {
fn execute(
&self,
context: Context,
_cleanup_manager: Arc<Mutex<CleanupManager>>,
) -> Result<()> {
let mut outputs = OutputsManager::default();
match context.get_target(self.name.as_str()) {
CommandLookupResult::Found(target) => {
let builder = target.as_startable();
if let Some(builder) = builder {
match builder.status(&context, &mut outputs) {
Ok(StatusResult::Running(msg)) => Ok(println!("[{}] {}", target.target_info().name, msg.as_str())),
Ok(StatusResult::NotRunning()) => Ok(println!("[{}] Not running", target.target_info().name)),
Err(e) => Err(e),
}
} else {
Err(anyhow!(
"Target <{}> is not startable",
self.name
))
}
},
CommandLookupResult::NotFound => {
Err(anyhow!(
"Target <{}> not found in config file <{}>",
self.name,
context.config_path
))
},
CommandLookupResult::Duplicates(duplicates) => {
Err(anyhow!(
"Target <{}> is ambiguous, possible values are <{}>, please specify the command to run using one of those names",
self.name, duplicates.join(", ")
))
},
}
}
}
29 changes: 29 additions & 0 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,35 @@ pub fn stop_using_pidfile(pid_path: &std::path::PathBuf, on_stop: impl Fn()) ->
Ok(())
}

pub fn status_using_pidfile(pid_path: &std::path::PathBuf) -> Result<Option<String>> {
let mut pid_str = std::fs::read_to_string(pid_path);
match pid_str {
Err(e) => match e.kind() {
std::io::ErrorKind::NotFound => Ok(None),
_ => Err(anyhow!(
"Error reading pid file for target at <{}>: {}",
pid_path.display(),
e
)),
},
Ok(ref mut pid_str) => {
let pid_str = pid_str.trim().to_string();
debug!(
"Found pid <{}> for target at <{}>",
pid_str,
pid_path.display()
);

let pid = nix::unistd::Pid::from_raw(pid_str.parse::<i32>()?);
if is_process_alive(pid) {
Ok(Some(format!("Process running with pid <{}>", pid)))
} else {
Ok(None)
}
}
}
}

/*
use daemonize::{Daemonize, Outcome};
let daemonize = Daemonize::new()
Expand Down
20 changes: 20 additions & 0 deletions src/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,10 @@ impl Startable for Command {
// TODO: last run file?
Ok(())
}

fn status(&self, context: &Context, outputs: &mut OutputsManager) -> Result<StatusResult> {
self.inner_as_startable().status(context, outputs)
}
}

impl Command {
Expand Down Expand Up @@ -567,6 +571,20 @@ pub trait Runnable {
) -> Result<()>;
}

pub enum StatusResult {
Running(String),
NotRunning(),
}

impl From<Option<String>> for StatusResult {
fn from(val: Option<String>) -> Self {
match val {
None => StatusResult::NotRunning(),
Some(s) => StatusResult::Running(s),
}
}
}

pub trait Startable {
fn start(
&self,
Expand All @@ -582,6 +600,8 @@ pub trait Startable {
outputs: &mut OutputsManager,
cleanup_manager: Arc<Mutex<CleanupManager>>,
) -> Result<()>;

fn status(&self, context: &Context, outputs: &mut OutputsManager) -> Result<StatusResult>;
}

pub trait Buildable {
Expand Down
20 changes: 18 additions & 2 deletions src/targets/command/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ use log::{debug, info};
use validator::Validate;

use crate::cleanup::CleanupManager;
use crate::commands::{run_command, spawn_command_with_pidfile, stop_using_pidfile};
use crate::commands::{
run_command, spawn_command_with_pidfile, status_using_pidfile, stop_using_pidfile,
};
use crate::config::ContainerCommand as ConfigContainerCommand;
use crate::context::Context;
use crate::default::{default_optional, default_to};
use crate::outputs::OutputsManager;
use crate::rand::rand_string;
use crate::shell::{escape_and_prepend, escape_and_prepend_vec, escape_string};
use crate::target::{create_metadata_dir, CommandInfo, Runnable, Startable, TargetInfo};
use crate::target::{
create_metadata_dir, CommandInfo, Runnable, Startable, StatusResult, TargetInfo,
};

#[derive(Debug, Clone, Validate)]
pub struct ContainerCommand {
Expand Down Expand Up @@ -189,6 +193,18 @@ impl Startable for ContainerCommand {
};
stop_using_pidfile(&pid_path, log_stop)
}

fn status(&self, _context: &Context, _outputs: &mut OutputsManager) -> Result<StatusResult> {
let config_dir = create_metadata_dir(self.target_info.name.to_string().as_str())?;

let pid_path = config_dir.join("pid");
debug!(
"Searching for pid file for target <{}> at <{}>",
self.target_info.name,
pid_path.display()
);
status_using_pidfile(&pid_path).map(|s| s.into())
}
}

pub struct ContainerRunInfo {
Expand Down
13 changes: 11 additions & 2 deletions src/targets/command/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ use log::{debug, info};
use validator::Validate;

use crate::cleanup::CleanupManager;
use crate::commands::{run_command_with_env, spawn_command_with_pidfile, stop_using_pidfile};
use crate::commands::{
run_command_with_env, spawn_command_with_pidfile, status_using_pidfile, stop_using_pidfile,
};
use crate::config::ExecCommand as ConfigExecCommand;
use crate::context::Context;
use crate::default::{default_optional, default_to};
use crate::outputs::OutputsManager;
use crate::target::create_metadata_dir;
use crate::target::{CommandInfo, Runnable, Startable, TargetInfo};
use crate::target::{CommandInfo, Runnable, Startable, StatusResult, TargetInfo};

#[derive(Debug, Clone, Validate)]
pub struct ExecCommand {
Expand Down Expand Up @@ -147,4 +149,11 @@ impl Startable for ExecCommand {
};
stop_using_pidfile(&pid_path, log_stop)
}

fn status(&self, _context: &Context, _outputs: &mut OutputsManager) -> Result<StatusResult> {
let config_dir = create_metadata_dir(self.target_info.name.to_string().as_str())?;

let pid_path = config_dir.join("pid");
status_using_pidfile(&pid_path).map(|s| s.into())
}
}
25 changes: 25 additions & 0 deletions tests/test_start.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use assert_cmd::prelude::*;

mod common;

#[test]
fn test_start() {
let config_src = r#"
[command.exec.do_stuff]
command = "bash -c '$i=0; while $i<10; do i+=1; date; sleep 1; done'"
daemon = true
"#;

let test_context = common::TestContext::new();
test_context.write_config(config_src);

let mut cmd = test_context.get_command();
cmd.arg("start").arg("do_stuff");

cmd.assert().success();

let mut cmd = test_context.get_command();
cmd.arg("stop").arg("do_stuff");

cmd.assert().success();
}
23 changes: 23 additions & 0 deletions tests/test_status.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use assert_cmd::prelude::*;
use predicates::prelude::*;

mod common;

#[test]
fn test_status_not_started() {
let config_src = r#"
[command.exec.do_stuff]
command = "bash -c '$i=0; while $i<10; do i+=1; date; sleep 1; done'"
daemon = true
"#;

let test_context = common::TestContext::new();
test_context.write_config(config_src);

let mut cmd = test_context.get_command();
cmd.arg("status").arg("do_stuff");

cmd.assert().success().stdout(predicate::str::contains(
"[command.exec.do_stuff] Not running",
));
}

0 comments on commit 46fb815

Please sign in to comment.