Skip to content

Commit

Permalink
feat(rust): first argument of node create can contain an inline con…
Browse files Browse the repository at this point in the history
…figuration
  • Loading branch information
adrianbenavides committed Oct 29, 2024
1 parent 1da5333 commit d91208c
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 16 deletions.
13 changes: 11 additions & 2 deletions implementations/rust/ockam/ockam_command/src/node/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use ockam_api::{fmt_log, fmt_ok};
use ockam_core::{opentelemetry_context_parser, OpenTelemetryContext};
use ockam_node::Context;

use crate::node::config::NodeConfig;
use crate::node::create::config::ConfigArgs;
use crate::node::util::NodeManagerDefaults;
use crate::service::config::Config;
Expand Down Expand Up @@ -127,6 +128,7 @@ impl Default for CreateCommand {
configuration: None,
enrollment_ticket: None,
variables: vec![],
started_from_configuration: false,
},
tcp_listener_address: node_manager_defaults.tcp_listener_address,
http_server: false,
Expand Down Expand Up @@ -200,6 +202,11 @@ impl CreateCommand {
return false;
}

// Ignore the config args if the node was started from a configuration
if self.config_args.started_from_configuration {
return false;
}

let name_arg_is_a_config = !self.has_name_arg();

let no_config_args = !name_arg_is_a_config
Expand All @@ -216,12 +223,14 @@ impl CreateCommand {
|| self.config_args.enrollment_ticket.is_some()
}

/// Return true if the `name` argument is not a config file path or URL
/// Return true if the `name` argument is not a URL, a file path, or an inline config
fn has_name_arg(&self) -> bool {
let is_url = is_url(&self.name).is_some();
let is_file = std::fs::metadata(&self.name)
.map(|m| m.is_file())
.unwrap_or(false);
is_url(&self.name).is_none() && !is_file
let is_inline_config = serde_yaml::from_str::<NodeConfig>(&self.name).is_ok();
!is_url && !is_file && !is_inline_config
}

fn parse_args(&self) -> miette::Result<()> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::node::CreateCommand;
use crate::run::parser::config::ConfigParser;
use crate::run::parser::resource::*;
use crate::run::parser::Version;
use crate::value_parsers::{parse_key_val, parse_path_or_url};
use crate::value_parsers::{parse_config_or_path_or_url, parse_key_val};
use crate::CommandGlobalOpts;
use clap::Args;
use miette::{miette, IntoDiagnostic};
Expand Down Expand Up @@ -36,6 +36,10 @@ pub struct ConfigArgs {
/// Example: `--variable KEY1=VALUE1 --variable KEY2=VALUE2`
#[arg(long = "variable", value_name = "VARIABLE", value_parser = parse_key_val::<String, String>)]
pub variables: Vec<(String, String)>,

/// A flag used internally to indicate that the node was started from a configuration file.
#[arg(hide = true, long)]
pub started_from_configuration: bool,
}

impl CreateCommand {
Expand Down Expand Up @@ -74,7 +78,7 @@ impl CreateCommand {
pub async fn get_node_config(&self) -> miette::Result<NodeConfig> {
let contents = match self.config_args.configuration.clone() {
Some(contents) => contents,
None => match parse_path_or_url(&self.name).await {
None => match parse_config_or_path_or_url::<NodeConfig>(&self.name).await {
Ok(contents) => contents,
Err(err) => {
// If just the enrollment ticket is passed, create a minimal configuration
Expand Down
27 changes: 27 additions & 0 deletions implementations/rust/ockam/ockam_command/src/run/parser/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,31 @@ mod tests {
assert_eq!(nodes[0].name, "node1");
assert_eq!(nodes[1].name, "node2");
}

#[test]
fn parse_without_variables_section() {
let mut contents = r#"
{
"nodes": [
"node1",
"node2",
],
}
"#
.to_string();
let parsed = ConfigParser::parse::<TestConfig>(&mut contents).unwrap();
let nodes = parsed.nodes.into_parsed_commands().unwrap();
assert_eq!(nodes.len(), 2);
assert_eq!(nodes[0].name, "node1");
assert_eq!(nodes[1].name, "node2");
}

#[test]
fn parse_from_inline_yaml() {
let mut contents = "relay: r1".to_string();
let parsed = ConfigParser::parse::<TestConfig>(&mut contents).unwrap();
let nodes = parsed.relays.into_parsed_commands(None).unwrap();
assert_eq!(nodes.len(), 1);
assert_eq!(nodes[0].relay_name, "r1");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ impl Resource<CreateCommand> for Node {

fn args(self) -> Vec<String> {
let mut args: BTreeMap<ArgKey, ArgValue> = BTreeMap::new();
args.insert("started-from-configuration".into(), true.into());
if let Some(name) = self.name {
args.insert("name".into(), name);
}
Expand Down Expand Up @@ -126,6 +127,7 @@ impl Node {
c.config_args.configuration = None;
c.config_args.variables = vec![];
c.config_args.enrollment_ticket = None;
c.config_args.started_from_configuration = true;
return Ok(c);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ pub struct Nodes {
impl Nodes {
fn get_subcommand(args: &[String]) -> Result<CreateCommand> {
if let OckamSubcommand::Node(cmd) = parse_cmd_from_args(CreateCommand::NAME, args)? {
if let node::NodeSubcommand::Create(c) = cmd.subcommand {
if let node::NodeSubcommand::Create(mut c) = cmd.subcommand {
c.config_args.started_from_configuration = true;
return Ok(c);
}
}
Expand Down
25 changes: 22 additions & 3 deletions implementations/rust/ockam/ockam_command/src/value_parsers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::util::parsers::hostname_parser;
use crate::CommandGlobalOpts;
use miette::{miette, Context, IntoDiagnostic};
use ockam_api::cli_state::{EnrollmentTicket, ExportedEnrollmentTicket, LegacyEnrollmentTicket};
use serde::Deserialize;
use std::str::FromStr;
use tracing::trace;
use url::Url;
Expand Down Expand Up @@ -47,13 +48,31 @@ pub async fn parse_enrollment_ticket(
.await?)
}

async fn parse_string_or_path_or_url(value: &str) -> miette::Result<String> {
pub(crate) async fn parse_config_or_path_or_url<'de, T: Deserialize<'de>>(
value: &'de str,
) -> miette::Result<String> {
match parse_path_or_url(value).await {
Ok(contents) => Ok(contents),
Err(_) => {
if serde_yaml::from_str::<T>(value).is_ok() {
Ok(value.to_string())
} else {
Err(miette!(
"Failed to parse value {} as a path, URL or configuration",
value
))
}
}
}
}

pub(crate) async fn parse_string_or_path_or_url(value: &str) -> miette::Result<String> {
parse_path_or_url(value)
.await
.or_else(|_| Ok(value.to_string()))
}

pub async fn parse_path_or_url(value: &str) -> miette::Result<String> {
pub(crate) async fn parse_path_or_url(value: &str) -> miette::Result<String> {
// If the URL is valid, download the contents
if let Some(url) = is_url(value) {
reqwest::get(url)
Expand All @@ -65,7 +84,7 @@ pub async fn parse_path_or_url(value: &str) -> miette::Result<String> {
.into_diagnostic()
.context("Failed to read contents from downloaded file")
}
// If not, try to read the contents from a file
// Try to read the contents from a file
else if tokio::fs::metadata(value).await.is_ok() {
tokio::fs::read_to_string(value)
.await
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,20 @@ teardown() {
run_success $OCKAM node show n --output json
assert_output --partial "\"name\": \"n\""
assert_output --partial "127.0.0.1:5432"

run_success "$OCKAM" node create "{name: o, tcp-outlets: {db-outlet: {to: 5433, at: o}}}"
run_success $OCKAM node show o --output json
assert_output --partial "\"name\": \"o\""
assert_output --partial "127.0.0.1:5433"

run_success "$OCKAM" node create "name: p"
run_success $OCKAM node show p --output json
assert_output --partial "\"name\": \"p\""

run_success "$OCKAM" node create "name: q" --foreground &
sleep 3
run_success $OCKAM node show q --output json
assert_output --partial "\"name\": \"q\""
}

@test "node - node in foreground with configuration is deleted if something fails" {
Expand All @@ -127,6 +141,10 @@ teardown() {
run_failure "$OCKAM" node create --configuration "{name: n, tcp-outlets: {db-outlet: {to: \"localhost:65536\"}}}"
run_success $OCKAM node show n --output json
assert_output --partial "[]"

run_failure "$OCKAM" node create "{name: n, tcp-outlets: {db-outlet: {to: \"localhost:65536\"}}}"
run_success $OCKAM node show n --output json
assert_output --partial "[]"
}

@test "node - create two nodes with the same inline configuration" {
Expand Down
Loading

0 comments on commit d91208c

Please sign in to comment.