use super::container;
use super::host;
use super::instance;
use super::transparent;
use crate::definition::TransparentWorkdir;
use crate::session::{self, Session};
use crate::{InstanceMap, InstanceName};
#[derive(Clone, Debug, clap::Parser)]
pub enum Command {
List,
Start {
#[arg(long = "instance", default_value_t)]
instance_name: InstanceName,
#[arg(long = "name")]
name: session::Name,
},
Stop {
#[arg(long = "name")]
name: session::Name,
},
Psql {
#[arg(long = "name")]
name: session::Name,
},
Shell {
#[arg(long = "name")]
name: session::Name,
},
#[command(name = "run-env")]
RunEnv {
#[arg(long = "name")]
name: session::Name,
command: String,
arguments: Vec<String>,
},
#[command(name = "schema-dump")]
SchemaDump {
#[arg(long = "name")]
name: session::Name,
},
Pgbench {
#[arg(long = "name")]
name: session::Name,
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
arguments: Vec<String>,
},
Host {
#[arg(long = "name")]
name: session::Name,
#[command(subcommand)]
command: instance::Command,
},
Container {
#[arg(long = "name")]
name: session::Name,
#[command(subcommand)]
command: instance::Command,
},
Status {
#[arg(long = "name")]
name: Option<session::Name>,
},
}
impl Command {
pub async fn run(
&self,
backend: &ociman::Backend,
instance_map: &InstanceMap,
) -> Result<(), super::Error> {
match self {
Self::List => {
let sessions = Session::list(backend)
.await
.map_err(super::Error::Session)?;
for session in sessions {
println!("{}", session.name());
}
}
Self::Start {
instance_name,
name,
} => {
let cwd = std::env::current_dir().map_err(super::Error::CurrentDir)?;
let workdir = TransparentWorkdir::try_from(cwd)?;
let definition = super::get_instance(instance_map, instance_name)?
.definition(backend.clone(), instance_name)
.session_name(name.clone())
.transparent_workdir(workdir)
.remove(false);
definition.start().await?;
println!("Started session: {name}");
}
Self::Stop { name } => {
let session = Session::find(backend, name)
.await
.map_err(super::Error::SessionFind)?
.ok_or_else(|| super::Error::UnknownSession { name: name.clone() })?;
session.stop().await.map_err(super::Error::SessionStop)?;
println!("Stopped session: {name}");
}
Self::Psql { name } => {
run_attached(backend, name, &instance::Command::Psql).await?;
}
Self::Shell { name } => {
run_attached(backend, name, &instance::Command::Shell).await?;
}
Self::RunEnv {
name,
command,
arguments,
} => {
run_attached(
backend,
name,
&instance::Command::RunEnv {
command: command.clone(),
arguments: arguments.clone(),
},
)
.await?;
}
Self::SchemaDump { name } => {
run_attached(backend, name, &instance::Command::SchemaDump).await?;
}
Self::Pgbench { name, arguments } => {
run_attached(
backend,
name,
&instance::Command::Pgbench {
arguments: arguments.clone(),
},
)
.await?;
}
Self::Host { name, command } => {
let container = attach(backend, name).await?;
host::run_on_container(command, &container).await?;
}
Self::Container { name, command } => {
let container = attach(backend, name).await?;
container::run_on_container(command, &container).await?;
}
Self::Status { name } => {
run_status(backend, instance_map, name.as_ref()).await?;
}
}
Ok(())
}
}
async fn run_status(
backend: &ociman::Backend,
instance_map: &InstanceMap,
name: Option<&session::Name>,
) -> Result<(), super::Error> {
let sessions = match name {
Some(name) => vec![
Session::find(backend, name)
.await
.map_err(super::Error::SessionFind)?
.ok_or_else(|| super::Error::UnknownSession { name: name.clone() })?,
],
None => Session::list(backend)
.await
.map_err(super::Error::Session)?,
};
let mut rows = Vec::with_capacity(sessions.len());
for session in &sessions {
let seed_status = seed_status_for(backend, instance_map, session).await?;
rows.push((session.name().to_string(), seed_status));
}
if name.is_some() {
let (session_name, seed_status) = &rows[0];
println!("{session_name}");
println!(" seed: {seed_status}");
} else {
let mut table = comfy_table::Table::new();
table
.load_preset(comfy_table::presets::NOTHING)
.set_header(["NAME", "SEED"]);
for (session_name, seed_status) in &rows {
table.add_row([session_name.as_str(), seed_status.as_str()]);
}
println!("{table}");
}
Ok(())
}
async fn seed_status_for(
backend: &ociman::Backend,
instance_map: &InstanceMap,
session: &Session,
) -> Result<String, super::Error> {
let metadata = session.metadata().await?;
let Some(instance) = instance_map.get(&metadata.instance) else {
return Ok("unknown-instance".to_string());
};
let definition = instance.definition(backend.clone(), &metadata.instance);
let current_seeds = definition
.load_seeds(&metadata.instance)
.await
.map_err(crate::container::Error::from)?;
Ok(session::compute_seed_status(
&metadata.image,
&metadata.seeds,
&definition.image,
¤t_seeds,
)
.to_string())
}
async fn attach(
backend: &ociman::Backend,
name: &session::Name,
) -> Result<crate::Container, super::Error> {
let session = Session::find(backend, name)
.await
.map_err(super::Error::SessionFind)?
.ok_or_else(|| super::Error::UnknownSession { name: name.clone() })?;
Ok(crate::Container::attach_session(session, backend.clone()).await?)
}
async fn run_attached(
backend: &ociman::Backend,
name: &session::Name,
command: &instance::Command,
) -> Result<(), super::Error> {
let container = attach(backend, name).await?;
let cwd = std::env::current_dir().map_err(super::Error::CurrentDir)?;
let workdir = TransparentWorkdir::try_from(cwd)?;
transparent::run_on_container(command, &container, &workdir).await
}