use std::{path::PathBuf, str::FromStr};
use clap::Args;
use miette::Context as _;
use miette::{miette, IntoDiagnostic};
use opentelemetry::trace::TraceContextExt;
use opentelemetry::KeyValue;
use tracing::instrument;
use ockam_api::cli_state::random_name;
use ockam_core::{opentelemetry_context_parser, AsyncTryClone, OpenTelemetryContext};
use ockam_node::Context;
use crate::node::util::NodeManagerDefaults;
use crate::service::config::Config;
use crate::util::api::TrustOpts;
use crate::util::embedded_node_that_is_not_stopped;
use crate::util::{async_cmd, local_cmd};
use crate::{docs, CommandGlobalOpts, Result};
pub mod background;
pub mod foreground;
const LONG_ABOUT: &str = include_str!("./static/create/long_about.txt");
const AFTER_LONG_HELP: &str = include_str!("./static/create/after_long_help.txt");
#[derive(Clone, Debug, Args)]
#[command(
long_about = docs::about(LONG_ABOUT),
after_long_help = docs::after_help(AFTER_LONG_HELP)
)]
pub struct CreateCommand {
#[arg(hide_default_value = true, default_value_t = random_name())]
pub node_name: String,
#[arg(display_order = 900, long, short)]
pub foreground: bool,
#[arg(long, short, value_name = "BOOL", default_value_t = false)]
skip_is_running_check: bool,
#[arg(display_order = 900, long = "exit-on-eof", short)]
pub exit_on_eof: bool,
#[arg(
display_order = 900,
long,
short,
id = "SOCKET_ADDRESS",
default_value = "127.0.0.1:0"
)]
pub tcp_listener_address: String,
#[arg(long, hide = true)]
pub child_process: bool,
#[arg(long, hide = true, value_parser = parse_launch_config)]
pub launch_config: Option<Config>,
#[arg(long = "identity", value_name = "IDENTITY_NAME")]
identity: Option<String>,
#[command(flatten)]
pub trust_opts: TrustOpts,
#[arg(long, hide = true, value_parser = opentelemetry_context_parser)]
pub opentelemetry_context: Option<OpenTelemetryContext>,
}
impl Default for CreateCommand {
fn default() -> Self {
let node_manager_defaults = NodeManagerDefaults::default();
Self {
skip_is_running_check: false,
node_name: random_name(),
exit_on_eof: false,
tcp_listener_address: node_manager_defaults.tcp_listener_address,
foreground: false,
child_process: false,
launch_config: None,
identity: None,
trust_opts: node_manager_defaults.trust_opts,
opentelemetry_context: None,
}
}
}
impl CreateCommand {
#[instrument(skip_all)]
pub fn run(self, opts: CommandGlobalOpts) -> miette::Result<()> {
if self.foreground {
if self.child_process {
opentelemetry::Context::current()
.span()
.set_attribute(KeyValue::new("background", "true"));
}
local_cmd(embedded_node_that_is_not_stopped(
opts.rt.clone(),
|ctx| async move { self.foreground_mode(&ctx, opts).await },
))
} else {
async_cmd(&self.name(), opts.clone(), |ctx| async move {
self.background_mode(&ctx, opts).await
})
}
}
pub fn name(&self) -> String {
if self.child_process {
"create background node".into()
} else {
"create node".into()
}
}
pub async fn async_run(self, ctx: &Context, opts: CommandGlobalOpts) -> miette::Result<()> {
let ctx = ctx.async_try_clone().await.into_diagnostic()?;
if self.foreground {
self.foreground_mode(&ctx, opts).await
} else {
self.background_mode(&ctx, opts).await
}
}
pub async fn guard_node_is_not_already_running(
&self,
opts: &CommandGlobalOpts,
) -> miette::Result<()> {
if !self.child_process {
if let Ok(node) = opts.state.get_node(&self.node_name).await {
if node.is_running() {
return Err(miette!("Node {} is already running", &self.node_name));
}
}
}
Ok(())
}
}
pub fn parse_launch_config(config_or_path: &str) -> Result<Config> {
match serde_json::from_str::<Config>(config_or_path) {
Ok(c) => Ok(c),
Err(_) => {
let path = PathBuf::from_str(config_or_path)
.into_diagnostic()
.wrap_err(miette!("Not a valid path"))?;
Config::read(path)
}
}
}