Skip to content

Commit 46fb815

Browse files
committedAug 1, 2024·
Add a simple status command
1 parent 38833da commit 46fb815

File tree

8 files changed

+189
-5
lines changed

8 files changed

+189
-5
lines changed
 

‎src/cmd/mod.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod execute;
88
mod list;
99
mod run;
1010
mod start;
11+
mod status;
1112
mod stop;
1213

1314
use crate::cleanup::CleanupManager;
@@ -17,6 +18,7 @@ pub use execute::Execute;
1718
use list::ListCommand;
1819
use run::RunCommand;
1920
use start::StartCommand;
21+
use status::StatusCommand;
2022
use stop::StopCommand;
2123

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

5557
/// List available targets
5658
List(ListCommand),
57-
// TODO: status, logs
59+
60+
/// Get the status of a daemon
61+
Status(StatusCommand),
62+
// TODO: logs
5863
}
5964

6065
impl Execute for Commands {
@@ -65,6 +70,7 @@ impl Execute for Commands {
6570
Commands::Stop(cmd) => cmd.execute(context, cleanup_manager),
6671
Commands::Build(cmd) => cmd.execute(context, cleanup_manager),
6772
Commands::List(cmd) => cmd.execute(context, cleanup_manager),
73+
Commands::Status(cmd) => cmd.execute(context, cleanup_manager),
6874
}
6975
}
7076
}

‎src/cmd/status.rs

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use std::sync::{Arc, Mutex};
2+
3+
use anyhow::{anyhow, Result};
4+
use clap::Parser;
5+
6+
use crate::cleanup::CleanupManager;
7+
use crate::cmd::execute::Execute;
8+
use crate::context::{CommandLookupResult, Context};
9+
use crate::outputs::OutputsManager;
10+
use crate::target::{StatusResult, Targetable};
11+
12+
#[derive(Parser, Debug)]
13+
pub struct StatusCommand {
14+
/// The name of the target to get status for
15+
pub name: String,
16+
}
17+
18+
impl Execute for StatusCommand {
19+
fn execute(
20+
&self,
21+
context: Context,
22+
_cleanup_manager: Arc<Mutex<CleanupManager>>,
23+
) -> Result<()> {
24+
let mut outputs = OutputsManager::default();
25+
match context.get_target(self.name.as_str()) {
26+
CommandLookupResult::Found(target) => {
27+
let builder = target.as_startable();
28+
if let Some(builder) = builder {
29+
match builder.status(&context, &mut outputs) {
30+
Ok(StatusResult::Running(msg)) => Ok(println!("[{}] {}", target.target_info().name, msg.as_str())),
31+
Ok(StatusResult::NotRunning()) => Ok(println!("[{}] Not running", target.target_info().name)),
32+
Err(e) => Err(e),
33+
}
34+
} else {
35+
Err(anyhow!(
36+
"Target <{}> is not startable",
37+
self.name
38+
))
39+
}
40+
},
41+
CommandLookupResult::NotFound => {
42+
Err(anyhow!(
43+
"Target <{}> not found in config file <{}>",
44+
self.name,
45+
context.config_path
46+
))
47+
},
48+
CommandLookupResult::Duplicates(duplicates) => {
49+
Err(anyhow!(
50+
"Target <{}> is ambiguous, possible values are <{}>, please specify the command to run using one of those names",
51+
self.name, duplicates.join(", ")
52+
))
53+
},
54+
}
55+
}
56+
}

‎src/commands.rs

+29
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,35 @@ pub fn stop_using_pidfile(pid_path: &std::path::PathBuf, on_stop: impl Fn()) ->
200200
Ok(())
201201
}
202202

203+
pub fn status_using_pidfile(pid_path: &std::path::PathBuf) -> Result<Option<String>> {
204+
let mut pid_str = std::fs::read_to_string(pid_path);
205+
match pid_str {
206+
Err(e) => match e.kind() {
207+
std::io::ErrorKind::NotFound => Ok(None),
208+
_ => Err(anyhow!(
209+
"Error reading pid file for target at <{}>: {}",
210+
pid_path.display(),
211+
e
212+
)),
213+
},
214+
Ok(ref mut pid_str) => {
215+
let pid_str = pid_str.trim().to_string();
216+
debug!(
217+
"Found pid <{}> for target at <{}>",
218+
pid_str,
219+
pid_path.display()
220+
);
221+
222+
let pid = nix::unistd::Pid::from_raw(pid_str.parse::<i32>()?);
223+
if is_process_alive(pid) {
224+
Ok(Some(format!("Process running with pid <{}>", pid)))
225+
} else {
226+
Ok(None)
227+
}
228+
}
229+
}
230+
}
231+
203232
/*
204233
use daemonize::{Daemonize, Outcome};
205234
let daemonize = Daemonize::new()

‎src/target.rs

+20
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,10 @@ impl Startable for Command {
532532
// TODO: last run file?
533533
Ok(())
534534
}
535+
536+
fn status(&self, context: &Context, outputs: &mut OutputsManager) -> Result<StatusResult> {
537+
self.inner_as_startable().status(context, outputs)
538+
}
535539
}
536540

537541
impl Command {
@@ -567,6 +571,20 @@ pub trait Runnable {
567571
) -> Result<()>;
568572
}
569573

574+
pub enum StatusResult {
575+
Running(String),
576+
NotRunning(),
577+
}
578+
579+
impl From<Option<String>> for StatusResult {
580+
fn from(val: Option<String>) -> Self {
581+
match val {
582+
None => StatusResult::NotRunning(),
583+
Some(s) => StatusResult::Running(s),
584+
}
585+
}
586+
}
587+
570588
pub trait Startable {
571589
fn start(
572590
&self,
@@ -582,6 +600,8 @@ pub trait Startable {
582600
outputs: &mut OutputsManager,
583601
cleanup_manager: Arc<Mutex<CleanupManager>>,
584602
) -> Result<()>;
603+
604+
fn status(&self, context: &Context, outputs: &mut OutputsManager) -> Result<StatusResult>;
585605
}
586606

587607
pub trait Buildable {

‎src/targets/command/container.rs

+18-2
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,18 @@ use log::{debug, info};
66
use validator::Validate;
77

88
use crate::cleanup::CleanupManager;
9-
use crate::commands::{run_command, spawn_command_with_pidfile, stop_using_pidfile};
9+
use crate::commands::{
10+
run_command, spawn_command_with_pidfile, status_using_pidfile, stop_using_pidfile,
11+
};
1012
use crate::config::ContainerCommand as ConfigContainerCommand;
1113
use crate::context::Context;
1214
use crate::default::{default_optional, default_to};
1315
use crate::outputs::OutputsManager;
1416
use crate::rand::rand_string;
1517
use crate::shell::{escape_and_prepend, escape_and_prepend_vec, escape_string};
16-
use crate::target::{create_metadata_dir, CommandInfo, Runnable, Startable, TargetInfo};
18+
use crate::target::{
19+
create_metadata_dir, CommandInfo, Runnable, Startable, StatusResult, TargetInfo,
20+
};
1721

1822
#[derive(Debug, Clone, Validate)]
1923
pub struct ContainerCommand {
@@ -189,6 +193,18 @@ impl Startable for ContainerCommand {
189193
};
190194
stop_using_pidfile(&pid_path, log_stop)
191195
}
196+
197+
fn status(&self, _context: &Context, _outputs: &mut OutputsManager) -> Result<StatusResult> {
198+
let config_dir = create_metadata_dir(self.target_info.name.to_string().as_str())?;
199+
200+
let pid_path = config_dir.join("pid");
201+
debug!(
202+
"Searching for pid file for target <{}> at <{}>",
203+
self.target_info.name,
204+
pid_path.display()
205+
);
206+
status_using_pidfile(&pid_path).map(|s| s.into())
207+
}
192208
}
193209

194210
pub struct ContainerRunInfo {

‎src/targets/command/exec.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ use log::{debug, info};
55
use validator::Validate;
66

77
use crate::cleanup::CleanupManager;
8-
use crate::commands::{run_command_with_env, spawn_command_with_pidfile, stop_using_pidfile};
8+
use crate::commands::{
9+
run_command_with_env, spawn_command_with_pidfile, status_using_pidfile, stop_using_pidfile,
10+
};
911
use crate::config::ExecCommand as ConfigExecCommand;
1012
use crate::context::Context;
1113
use crate::default::{default_optional, default_to};
1214
use crate::outputs::OutputsManager;
1315
use crate::target::create_metadata_dir;
14-
use crate::target::{CommandInfo, Runnable, Startable, TargetInfo};
16+
use crate::target::{CommandInfo, Runnable, Startable, StatusResult, TargetInfo};
1517

1618
#[derive(Debug, Clone, Validate)]
1719
pub struct ExecCommand {
@@ -147,4 +149,11 @@ impl Startable for ExecCommand {
147149
};
148150
stop_using_pidfile(&pid_path, log_stop)
149151
}
152+
153+
fn status(&self, _context: &Context, _outputs: &mut OutputsManager) -> Result<StatusResult> {
154+
let config_dir = create_metadata_dir(self.target_info.name.to_string().as_str())?;
155+
156+
let pid_path = config_dir.join("pid");
157+
status_using_pidfile(&pid_path).map(|s| s.into())
158+
}
150159
}

‎tests/test_start.rs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use assert_cmd::prelude::*;
2+
3+
mod common;
4+
5+
#[test]
6+
fn test_start() {
7+
let config_src = r#"
8+
[command.exec.do_stuff]
9+
command = "bash -c '$i=0; while $i<10; do i+=1; date; sleep 1; done'"
10+
daemon = true
11+
"#;
12+
13+
let test_context = common::TestContext::new();
14+
test_context.write_config(config_src);
15+
16+
let mut cmd = test_context.get_command();
17+
cmd.arg("start").arg("do_stuff");
18+
19+
cmd.assert().success();
20+
21+
let mut cmd = test_context.get_command();
22+
cmd.arg("stop").arg("do_stuff");
23+
24+
cmd.assert().success();
25+
}

‎tests/test_status.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use assert_cmd::prelude::*;
2+
use predicates::prelude::*;
3+
4+
mod common;
5+
6+
#[test]
7+
fn test_status_not_started() {
8+
let config_src = r#"
9+
[command.exec.do_stuff]
10+
command = "bash -c '$i=0; while $i<10; do i+=1; date; sleep 1; done'"
11+
daemon = true
12+
"#;
13+
14+
let test_context = common::TestContext::new();
15+
test_context.write_config(config_src);
16+
17+
let mut cmd = test_context.get_command();
18+
cmd.arg("status").arg("do_stuff");
19+
20+
cmd.assert().success().stdout(predicate::str::contains(
21+
"[command.exec.do_stuff] Not running",
22+
));
23+
}

0 commit comments

Comments
 (0)
Please sign in to comment.