use clap::Args;
use colorful::Colorful;
use ockam_api::cli_state::{StateDirTrait, StateItemTrait};
use ockam_api::nodes::BackgroundNode;
use ockam_node::Context;
use crate::node::show::print_query_status;
use crate::node::util::spawn_node;
use crate::util::node_rpc;
use crate::{docs, fmt_err, fmt_info, fmt_log, fmt_ok, fmt_warn, CommandGlobalOpts, OckamColor};
use super::get_node_name;
const LONG_ABOUT: &str = include_str!("./static/start/long_about.txt");
const PREVIEW_TAG: &str = include_str!("../static/preview_tag.txt");
const AFTER_LONG_HELP: &str = include_str!("./static/start/after_long_help.txt");
#[derive(Clone, Debug, Args)]
#[command(
long_about = docs::about(LONG_ABOUT),
before_help = docs::before_help(PREVIEW_TAG),
after_long_help = docs::after_help(AFTER_LONG_HELP)
)]
pub struct StartCommand {
node_name: Option<String>,
#[arg(long, default_value = "false")]
aws_kms: bool,
}
impl StartCommand {
pub fn run(self, opts: CommandGlobalOpts) {
node_rpc(run_impl, (opts, self))
}
}
async fn run_impl(
ctx: Context,
(opts, cmd): (CommandGlobalOpts, StartCommand),
) -> miette::Result<()> {
if cmd.node_name.is_some() || !opts.terminal.can_ask_for_user_input() {
let node_name = &get_node_name(&opts.state, &cmd.node_name);
start_single_node(node_name, opts, &ctx).await?;
return Ok(());
}
let inactive_nodes = get_inactive_nodes(&opts)?;
match inactive_nodes.len() {
0 => {
opts.terminal
.stdout()
.plain(fmt_info!(
"All the nodes are already started, nothing to do. Exiting gratefully"
))
.write_line()?;
}
1 => {
start_single_node(&inactive_nodes[0], opts, &ctx).await?;
}
_ => {
let selected_nodes = opts
.terminal
.select_multiple("Select the nodes".to_string(), inactive_nodes);
match selected_nodes.len() {
0 => {
opts.terminal
.stdout()
.plain(fmt_info!("No node selected, exiting gratefully!"))
.write_line()?;
}
1 => start_single_node(&selected_nodes[0], opts, &ctx).await?,
_ => {
if !opts.terminal.confirm_interactively(format!(
"You are about to start the given nodes:[ {} ]. Confirm?",
&selected_nodes.join(", ")
)) {
opts.terminal
.stdout()
.plain(fmt_info!("No node selected, exiting gratefully!"))
.write_line()?;
return Ok(());
}
let formatted_starts_result =
start_multiple_nodes(&ctx, &opts, &selected_nodes).await?;
opts.terminal
.stdout()
.plain(formatted_starts_result.join("\n"))
.write_line()?;
}
}
}
}
Ok(())
}
async fn start_single_node(
node_name: &str,
mut opts: CommandGlobalOpts,
ctx: &Context,
) -> miette::Result<()> {
let node_state = opts.state.nodes.get(node_name)?;
opts.global_args.verbose = node_state.config().setup().verbose;
if node_state.is_running() {
opts.terminal
.stdout()
.plain(fmt_err!(
"The node '{node_name}' is already running. If you want to restart it you can \
call `ockam node stop {node_name}` and then `ockam node start {node_name}`"
))
.write_line()?;
return Ok(());
}
let mut node: BackgroundNode = run_node(node_name, ctx, &opts).await?;
print_query_status(&opts, ctx, node_name, &mut node, true).await?;
Ok(())
}
async fn start_multiple_nodes(
ctx: &Context,
opts: &CommandGlobalOpts,
node_selected: &[String],
) -> miette::Result<Vec<String>> {
let mut node_error_flag: bool = false;
let mut node_starts_output: Vec<String> = vec![];
for node_name in node_selected {
match run_node(node_name, ctx, opts).await {
Ok(_) => node_starts_output.push(fmt_ok!("{node_name}")),
Err(_) => {
node_error_flag = true;
node_starts_output.push(fmt_warn!("{node_name}"))
}
}
}
if node_error_flag {
append_info_if_errors(&mut node_starts_output);
};
Ok(node_starts_output)
}
async fn run_node(
node_name: &str,
ctx: &Context,
opts: &CommandGlobalOpts,
) -> miette::Result<BackgroundNode> {
let node_state = opts.state.nodes.get(node_name)?;
node_state.kill_process(false)?;
let node_setup = node_state.config().setup();
let node_name = node_state.name();
spawn_node(
opts,
node_name, &node_setup.api_transport()?.addr.to_string(), None, None, None, None, None, None, None, None, None, true, )?;
let node = BackgroundNode::create(ctx, &opts.state, node_name).await?;
Ok(node)
}
fn get_inactive_nodes(opts: &CommandGlobalOpts) -> miette::Result<Vec<String>> {
let node_list = opts.state.nodes.list()?;
Ok(node_list
.iter()
.filter(|node_state| !(node_state.is_running()))
.map(|node| node.name().to_owned())
.collect())
}
fn append_info_if_errors(node_starts_output: &mut Vec<String>) {
node_starts_output.push(
"\n\n".to_string()
+ &fmt_err!("You can check the status of failed nodes using the command\n")
+ &fmt_log!(
"{}",
"ockam node show\n".color(OckamColor::PrimaryResource.color())
)
+ &fmt_log!("or check the logs with the command\n")
+ &fmt_log!(
"{}",
"ockam node logs".color(OckamColor::PrimaryResource.color())
),
);
}