diff --git a/apps/igloo-kit-cli/src/cli.rs b/apps/igloo-kit-cli/src/cli.rs index b3fddeea2..c6bdb8836 100644 --- a/apps/igloo-kit-cli/src/cli.rs +++ b/apps/igloo-kit-cli/src/cli.rs @@ -14,7 +14,7 @@ use log::{debug, info}; use self::{ display::{Message, MessageType}, routines::{ - clean::CleanProject, initialize::InitializeProject, start::RunLocalInfratructure, + clean::CleanProject, initialize::InitializeProject, start::RunLocalInfrastructure, stop::StopLocalInfrastructure, validate::ValidateRedPandaCluster, RoutineController, RunMode, }, @@ -83,7 +83,7 @@ async fn top_command_handler(settings: Settings, commands: &Option) { let mut controller = RoutineController::new(); let run_mode = RunMode::Explicit {}; - controller.add_routine(Box::new(RunLocalInfratructure::new(project.clone()))); + controller.add_routine(Box::new(RunLocalInfrastructure::new(project.clone()))); controller.add_routine(Box::new(ValidateRedPandaCluster::new())); @@ -98,7 +98,7 @@ async fn top_command_handler(settings: Settings, commands: &Option) { } Some(Commands::Update {}) => { // This command may not be needed if we have incredible automation - todo!("Will update the project's underlying infrascructure based on any added objects") + todo!("Will update the project's underlying infrastructure based on any added objects") } Some(Commands::Stop {}) => { let mut controller = RoutineController::new(); diff --git a/apps/igloo-kit-cli/src/cli/routines.rs b/apps/igloo-kit-cli/src/cli/routines.rs index 8b0bac461..1433f9152 100644 --- a/apps/igloo-kit-cli/src/cli/routines.rs +++ b/apps/igloo-kit-cli/src/cli/routines.rs @@ -104,6 +104,7 @@ pub mod clean; pub mod initialize; pub mod start; pub mod stop; +mod util; pub mod validate; #[derive(Clone)] @@ -133,14 +134,23 @@ impl RoutineSuccess { pub struct RoutineFailure { message: Message, message_type: MessageType, - error: Error, + error: Option, } impl RoutineFailure { pub fn new(message: Message, error: Error) -> Self { Self { message, message_type: MessageType::Error, - error, + error: Some(error), + } + } + + /// create a RoutineFailure error without an io error + pub fn error(message: Message) -> Self { + Self { + message, + message_type: MessageType::Error, + error: None, } } } @@ -173,7 +183,14 @@ pub trait Routine { failure.message_type, Message::new( failure.message.action.clone(), - format!("{}: {}", failure.message.details.clone(), failure.error), + match &failure.error { + None => { + failure.message.details.clone() + } + Some(error) => { + format!("{}: {}", failure.message.details.clone(), error) + } + }, ) ); Err(failure) diff --git a/apps/igloo-kit-cli/src/cli/routines/clean.rs b/apps/igloo-kit-cli/src/cli/routines/clean.rs index bc2b5f11f..89150b6b0 100644 --- a/apps/igloo-kit-cli/src/cli/routines/clean.rs +++ b/apps/igloo-kit-cli/src/cli/routines/clean.rs @@ -1,5 +1,6 @@ use std::{fs, path::PathBuf}; +use crate::cli::routines::util::ensure_docker_running; use crate::{ cli::display::Message, project::Project, utilities::constants::PANDA_NETWORK, utilities::docker, }; @@ -30,6 +31,7 @@ impl Routine for CleanProject { ) })?; + ensure_docker_running()?; StopLocalInfrastructure::new(run_mode).run(run_mode)?; RemoveDockerNetwork::new(PANDA_NETWORK).run(run_mode)?; DeleteRedpandaMountVolume::new(internal_dir.clone()).run(run_mode)?; diff --git a/apps/igloo-kit-cli/src/cli/routines/initialize.rs b/apps/igloo-kit-cli/src/cli/routines/initialize.rs index 1675dff94..fe2e9f96c 100644 --- a/apps/igloo-kit-cli/src/cli/routines/initialize.rs +++ b/apps/igloo-kit-cli/src/cli/routines/initialize.rs @@ -1,9 +1,7 @@ -use std::{ - fs, - io::{Error, ErrorKind}, - path::PathBuf, -}; +use std::io::ErrorKind; +use std::{fs, path::PathBuf}; +use crate::cli::routines::util::ensure_docker_running; use crate::{ cli::display::Message, framework::{languages::create_models_dir, typescript::create_typescript_models_dir}, @@ -39,6 +37,8 @@ impl Routine for InitializeProject { ) })?; + ensure_docker_running()?; + CreateModelsVolume::new(self.project.clone()).run(run_mode)?; CreateDockerNetwork::new(PANDA_NETWORK).run(run_mode)?; @@ -97,10 +97,10 @@ impl Routine for ValidateMountVolumes { ))) } else { let message = format!("redpanda: {panda_house}, clickhouse: {clickhouse}"); - Err(RoutineFailure::new( - Message::new("Mount volume status".to_string(), message.clone()), - Error::new(ErrorKind::NotFound, message), - )) + Err(RoutineFailure::error(Message::new( + "Mount volume status".to_string(), + message.clone(), + ))) } } } diff --git a/apps/igloo-kit-cli/src/cli/routines/start.rs b/apps/igloo-kit-cli/src/cli/routines/start.rs index ed033754d..7e07ac1f7 100644 --- a/apps/igloo-kit-cli/src/cli/routines/start.rs +++ b/apps/igloo-kit-cli/src/cli/routines/start.rs @@ -6,23 +6,25 @@ use super::{ Routine, RoutineFailure, RoutineSuccess, RunMode, }; use crate::cli::routines::initialize::CreateIglooTempDirectoryTree; +use crate::cli::routines::util::ensure_docker_running; use crate::{ cli::display::Message, project::Project, utilities::docker::{self}, }; -pub struct RunLocalInfratructure { +pub struct RunLocalInfrastructure { project: Project, } -impl RunLocalInfratructure { +impl RunLocalInfrastructure { pub fn new(project: Project) -> Self { Self { project } } } -impl Routine for RunLocalInfratructure { +impl Routine for RunLocalInfrastructure { fn run_silent(&self) -> Result { + ensure_docker_running()?; let igloo_dir = self.project.internal_dir().map_err(|err| { RoutineFailure::new( Message::new( diff --git a/apps/igloo-kit-cli/src/cli/routines/stop.rs b/apps/igloo-kit-cli/src/cli/routines/stop.rs index f53d75a58..d0cc02626 100644 --- a/apps/igloo-kit-cli/src/cli/routines/stop.rs +++ b/apps/igloo-kit-cli/src/cli/routines/stop.rs @@ -1,4 +1,5 @@ use super::{Routine, RoutineFailure, RoutineSuccess, RunMode}; +use crate::cli::routines::util::ensure_docker_running; use crate::{cli::display::Message, utilities::docker}; pub struct StopLocalInfrastructure { @@ -11,6 +12,7 @@ impl StopLocalInfrastructure { } impl Routine for StopLocalInfrastructure { fn run_silent(&self) -> Result { + ensure_docker_running()?; let run_mode = self.run_mode; StopRedPandaContainer::new().run(run_mode)?; StopClickhouseContainer::new().run(run_mode)?; diff --git a/apps/igloo-kit-cli/src/cli/routines/util.rs b/apps/igloo-kit-cli/src/cli/routines/util.rs new file mode 100644 index 000000000..e10660a9a --- /dev/null +++ b/apps/igloo-kit-cli/src/cli/routines/util.rs @@ -0,0 +1,29 @@ +use crate::cli::display::Message; +use crate::cli::routines::RoutineFailure; +use crate::utilities::docker; + +pub fn ensure_docker_running() -> Result<(), RoutineFailure> { + let errors = docker::check_status().map_err(|err| { + RoutineFailure::new( + Message::new("Failed".to_string(), "to run `docker info`".to_string()), + err, + ) + })?; + + if errors.is_empty() { + Ok(()) + } else if errors + .iter() + .any(|s| s.ends_with("Is the docker daemon running?")) + { + Err(RoutineFailure::error(Message::new( + "Failed".to_string(), + "to run docker commands. Is docker running?".to_string(), + ))) + } else { + Err(RoutineFailure::error(Message::new( + "Failed".to_string(), + errors.join("\n"), + ))) + } +} diff --git a/apps/igloo-kit-cli/src/cli/routines/validate.rs b/apps/igloo-kit-cli/src/cli/routines/validate.rs index 3d3b3e582..6b4e283ac 100644 --- a/apps/igloo-kit-cli/src/cli/routines/validate.rs +++ b/apps/igloo-kit-cli/src/cli/routines/validate.rs @@ -1,6 +1,5 @@ use super::{Routine, RoutineFailure, RoutineSuccess}; use crate::{cli::display::Message, utilities::constants::PANDA_NETWORK, utilities::docker}; -use std::io::{Error, ErrorKind}; pub struct ValidateClickhouseRun; impl ValidateClickhouseRun { @@ -22,16 +21,10 @@ impl Routine for ValidateClickhouseRun { .iter() .find(|container| container.names.contains("clickhousedb-1")) .ok_or_else(|| { - RoutineFailure::new( - Message::new( - "Failed".to_string(), - "to find clickhouse docker container".to_string(), - ), - Error::new( - ErrorKind::Other, - "Failed to validate clickhouse container exists", - ), - ) + RoutineFailure::error(Message::new( + "Failed".to_string(), + "to find clickhouse docker container".to_string(), + )) })?; Ok(RoutineSuccess::success(Message::new( "Successfully".to_string(), @@ -61,16 +54,10 @@ impl Routine for ValidateRedPandaRun { .iter() .find(|container| container.names.contains("redpanda-1")) .ok_or_else(|| { - RoutineFailure::new( - Message::new( - "Failed".to_string(), - "to find redpanda docker container".to_string(), - ), - Error::new( - ErrorKind::Other, - "Failed to validate redpanda container exists", - ), - ) + RoutineFailure::error(Message::new( + "Failed".to_string(), + "to find redpanda docker container".to_string(), + )) })?; Ok(RoutineSuccess::success(Message::new( "Successfully".to_string(), @@ -101,16 +88,10 @@ impl Routine for ValidatePandaHouseNetwork { .iter() .find(|network| network.name == PANDA_NETWORK) .ok_or_else(|| { - RoutineFailure::new( - Message::new( - "Failed".to_string(), - "to find panda house docker network".to_string(), - ), - Error::new( - ErrorKind::Other, - "Failed to validate panda house docker network", - ), - ) + RoutineFailure::error(Message::new( + "Failed".to_string(), + "to find panda house docker network".to_string(), + )) })?; Ok(RoutineSuccess::success(Message::new( @@ -144,13 +125,10 @@ impl Routine for ValidateRedPandaCluster { "validated red panda cluster".to_string(), ))) } else { - Err(RoutineFailure::new( - Message::new( - "Failed".to_string(), - "to validate red panda cluster".to_string(), - ), - Error::new(ErrorKind::Other, "Failed to validate red panda cluster"), - )) + Err(RoutineFailure::error(Message::new( + "Failed".to_string(), + "to validate red panda cluster".to_string(), + ))) } } } diff --git a/apps/igloo-kit-cli/src/utilities/docker.rs b/apps/igloo-kit-cli/src/utilities/docker.rs index 14ae9c7e5..3e8b8496e 100644 --- a/apps/igloo-kit-cli/src/utilities/docker.rs +++ b/apps/igloo-kit-cli/src/utilities/docker.rs @@ -6,7 +6,7 @@ use std::{ use crate::infrastructure::olap::clickhouse::config::ClickhouseConfig; use crate::utilities::constants::PANDA_NETWORK; use serde::{Deserialize, Serialize}; -use serde_json::from_str; +use serde_json::{from_slice, from_str}; #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] @@ -357,6 +357,28 @@ fn start_container(name: &str) -> std::io::Result { output_to_result(output) } +pub fn check_status() -> std::io::Result> { + let child = Command::new("docker") + .arg("info") + .arg("--format") + .arg("{{json .ServerErrors}}") + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .spawn()?; + + let output = child.wait_with_output()?; + + if !output.status.success() { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "Failed to get Docker info", + )); + } + + let errors: Option> = from_slice(&output.stdout)?; + Ok(errors.unwrap_or_default()) +} + fn output_to_result(output: std::process::Output) -> std::io::Result { if output.status.success() { Ok(String::from_utf8_lossy(&output.stdout).to_string())