Add prompt on run if --yes not specified, add tests, separate out function for filtering, bug fixes, etc
This commit is contained in:
parent
ee9270d771
commit
b781a7ff4a
1021
Cargo.lock
generated
1021
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -16,7 +16,7 @@ async fn main() {
|
|||||||
Box::new(ErrorScore {}),
|
Box::new(ErrorScore {}),
|
||||||
Box::new(PanicScore {}),
|
Box::new(PanicScore {}),
|
||||||
]);
|
]);
|
||||||
harmony_cli::init(maestro).await.unwrap();
|
harmony_cli::init(maestro, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
use assert_cmd::Command;
|
use assert_cmd::Command;
|
||||||
@ -24,7 +24,7 @@ use assert_cmd::Command;
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_example_success() {
|
fn test_example_success() {
|
||||||
let mut cmd = Command::cargo_bin("example-cli").unwrap();
|
let mut cmd = Command::cargo_bin("example-cli").unwrap();
|
||||||
let assert = cmd.args(&["--run", "--filter", "SuccessScore"]).assert();
|
let assert = cmd.args(&["--yes", "--filter", "SuccessScore"]).assert();
|
||||||
|
|
||||||
assert.success();
|
assert.success();
|
||||||
}
|
}
|
||||||
@ -32,7 +32,7 @@ fn test_example_success() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_example_fail() {
|
fn test_example_fail() {
|
||||||
let mut cmd_fail = Command::cargo_bin("example-cli").unwrap();
|
let mut cmd_fail = Command::cargo_bin("example-cli").unwrap();
|
||||||
let assert_fail = cmd_fail.args(&["--run", "--filter", "ErrorScore"]).assert();
|
let assert_fail = cmd_fail.args(&["--yes", "--filter", "ErrorScore"]).assert();
|
||||||
|
|
||||||
assert_fail.failure();
|
assert_fail.failure();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,7 +47,7 @@ impl<T: Topology> Score<T> for SuccessScore {
|
|||||||
|
|
||||||
/// An interpret that only returns the result it is given when built. It does nothing else. Only
|
/// An interpret that only returns the result it is given when built. It does nothing else. Only
|
||||||
/// useful for development/testing purposes.
|
/// useful for development/testing purposes.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
struct DummyInterpret {
|
struct DummyInterpret {
|
||||||
status: InterpretStatus,
|
status: InterpretStatus,
|
||||||
result: Result<Outcome, InterpretError>,
|
result: Result<Outcome, InterpretError>,
|
||||||
|
|||||||
8
harmony/src/modules/helm/resource.rs
Normal file
8
harmony/src/modules/helm/resource.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
use serde_yaml;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct HelmResourceScore {
|
||||||
|
// pub values: serde_yaml,
|
||||||
|
pub include_CRDs: bool,
|
||||||
|
}
|
||||||
@ -6,9 +6,12 @@ readme.workspace = true
|
|||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
assert_cmd = "2.0.17"
|
||||||
clap = { version = "4.5.35", features = ["derive"] }
|
clap = { version = "4.5.35", features = ["derive"] }
|
||||||
harmony = { path = "../harmony" }
|
harmony = { path = "../harmony" }
|
||||||
harmony_tui = { path = "../harmony_tui", optional = true }
|
harmony_tui = { path = "../harmony_tui", optional = true }
|
||||||
|
inquire = "0.7.5"
|
||||||
|
tokio.workspace = true
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
@ -1,19 +1,18 @@
|
|||||||
use std::error::Error;
|
use clap::Parser;
|
||||||
|
|
||||||
use clap::builder::ArgPredicate;
|
use clap::builder::ArgPredicate;
|
||||||
use clap::{Arg, CommandFactory, Parser};
|
|
||||||
use harmony;
|
use harmony;
|
||||||
use harmony::{score::Score, topology::Topology};
|
use harmony::{score::Score, topology::Topology};
|
||||||
|
use inquire::Confirm;
|
||||||
|
|
||||||
#[cfg(feature = "tui")]
|
#[cfg(feature = "tui")]
|
||||||
use harmony_tui;
|
use harmony_tui;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
struct Args {
|
pub struct Args {
|
||||||
/// Run score(s) or not
|
/// Run score(s) or not
|
||||||
#[arg(short, long, default_value_t = false)]
|
#[arg(short, long, default_value_t = false)]
|
||||||
run: bool,
|
yes: bool,
|
||||||
|
|
||||||
/// Filter query
|
/// Filter query
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
@ -24,22 +23,59 @@ struct Args {
|
|||||||
interactive: bool,
|
interactive: bool,
|
||||||
|
|
||||||
/// Run all or nth, defaults to all
|
/// Run all or nth, defaults to all
|
||||||
#[arg(short, long, default_value_t = true)]
|
#[arg(
|
||||||
|
short,
|
||||||
|
long,
|
||||||
|
default_value_t = true,
|
||||||
|
default_value_if("number", ArgPredicate::IsPresent, "false")
|
||||||
|
)]
|
||||||
all: bool,
|
all: bool,
|
||||||
|
|
||||||
/// Run nth matching, zero indexed
|
/// Run nth matching, zero indexed
|
||||||
#[arg(short, long, default_value_t = 0)]
|
#[arg(short, long, default_value_t = 0)]
|
||||||
number: u8,
|
number: usize,
|
||||||
|
|
||||||
/// list scores, will also be affected by run filter
|
/// list scores, will also be affected by run filter
|
||||||
#[arg(short, long, default_value_t = false)]
|
#[arg(short, long, default_value_t = false)]
|
||||||
list: bool,
|
list: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn maestro_scores_filter<T: Topology + std::fmt::Debug + Send + Sync + 'static>(
|
||||||
|
maestro: &harmony::maestro::Maestro<T>,
|
||||||
|
all: bool,
|
||||||
|
filter: Option<String>,
|
||||||
|
number: usize,
|
||||||
|
) -> Vec<Box<dyn Score<T>>> {
|
||||||
|
let scores = maestro.scores();
|
||||||
|
let scores_read = scores.read().expect("Should be able to read scores");
|
||||||
|
let mut scores_vec: Vec<Box<dyn Score<T>>> = match filter {
|
||||||
|
Some(f) => scores_read
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.clone_box())
|
||||||
|
.filter(|s| s.name().contains(&f))
|
||||||
|
.collect(),
|
||||||
|
None => scores_read.iter().map(|s| s.clone_box()).collect(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if !all {
|
||||||
|
let score = scores_vec.get(number);
|
||||||
|
match score {
|
||||||
|
Some(s) => scores_vec = vec![s.clone_box()],
|
||||||
|
None => return vec![],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return scores_vec;
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn init<T: Topology + std::fmt::Debug + Send + Sync + 'static>(
|
pub async fn init<T: Topology + std::fmt::Debug + Send + Sync + 'static>(
|
||||||
maestro: harmony::maestro::Maestro<T>,
|
maestro: harmony::maestro::Maestro<T>,
|
||||||
|
args_struct: Option<Args>,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let args = Args::parse();
|
let args = match args_struct {
|
||||||
|
Some(args) => args,
|
||||||
|
None => Args::parse(),
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "tui")]
|
#[cfg(feature = "tui")]
|
||||||
if args.interactive {
|
if args.interactive {
|
||||||
@ -51,28 +87,25 @@ pub async fn init<T: Topology + std::fmt::Debug + Send + Sync + 'static>(
|
|||||||
return Err("Not compiled with interactive support".into());
|
return Err("Not compiled with interactive support".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// if no score to run provided, and list isn't specified, print help
|
let scores_vec = maestro_scores_filter(&maestro, args.all, args.filter, args.number);
|
||||||
if !args.run && !args.list {
|
|
||||||
println!("Please include at least --run or --list");
|
|
||||||
Args::command().print_help();
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let scores = maestro.scores();
|
|
||||||
let scores_read = scores.read().expect("Should be able to read scores");
|
|
||||||
let scores_vec: Vec<Box<dyn Score<T>>> = match args.filter.clone() {
|
|
||||||
Some(f) => scores_read
|
|
||||||
.iter()
|
|
||||||
.map(|s| s.clone_box())
|
|
||||||
.filter(|s| s.name().contains(&f))
|
|
||||||
.collect(),
|
|
||||||
None => scores_read.iter().map(|s| s.clone_box()).collect(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if scores_vec.len() == 0 {
|
if scores_vec.len() == 0 {
|
||||||
return Err("No score found".into());
|
return Err("No score found".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if no score to run provided, and list isn't specified, print help
|
||||||
|
if !args.yes {
|
||||||
|
let confirmation =
|
||||||
|
Confirm::new(format!("This will run the following scores: {:#?}", scores_vec).as_str())
|
||||||
|
.with_default(true)
|
||||||
|
.prompt()
|
||||||
|
.expect("Unexpected prompt error");
|
||||||
|
|
||||||
|
if !confirmation {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if list option is specified, print filtered list and exit
|
// if list option is specified, print filtered list and exit
|
||||||
if args.list {
|
if args.list {
|
||||||
println!("Available scores: {:#?}", scores_vec);
|
println!("Available scores: {:#?}", scores_vec);
|
||||||
@ -80,21 +113,244 @@ pub async fn init<T: Topology + std::fmt::Debug + Send + Sync + 'static>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if all is specified, run all. Otherwise, run the first match or specified index
|
// if all is specified, run all. Otherwise, run the first match or specified index
|
||||||
if args.all {
|
for s in scores_vec {
|
||||||
for s in scores_vec {
|
println!("Running: {}", s.name());
|
||||||
println!("Running: {}", s.name());
|
maestro.interpret(s).await?;
|
||||||
maestro.interpret(s).await?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let score = scores_vec.get(args.number as usize);
|
|
||||||
match score {
|
|
||||||
Some(s) => {
|
|
||||||
println!("Running: {}", s.name());
|
|
||||||
maestro.interpret(s.clone_box()).await?;
|
|
||||||
}
|
|
||||||
None => return Err("Invalid index given".into()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use harmony::{
|
||||||
|
inventory::Inventory,
|
||||||
|
maestro::Maestro,
|
||||||
|
modules::dummy::{ErrorScore, PanicScore, SuccessScore},
|
||||||
|
topology::HAClusterTopology,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_init_success_score() {
|
||||||
|
let inventory = Inventory::autoload();
|
||||||
|
let topology = HAClusterTopology::autoload();
|
||||||
|
let mut maestro = Maestro::new(inventory, topology);
|
||||||
|
|
||||||
|
maestro.register_all(vec![
|
||||||
|
Box::new(SuccessScore {}),
|
||||||
|
Box::new(ErrorScore {}),
|
||||||
|
Box::new(PanicScore {}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let res = crate::init(
|
||||||
|
maestro,
|
||||||
|
Some(crate::Args {
|
||||||
|
yes: true,
|
||||||
|
filter: Some("SuccessScore".to_owned()),
|
||||||
|
interactive: false,
|
||||||
|
all: true,
|
||||||
|
number: 0,
|
||||||
|
list: false,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(res.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_init_error_score() {
|
||||||
|
let inventory = Inventory::autoload();
|
||||||
|
let topology = HAClusterTopology::autoload();
|
||||||
|
let mut maestro = Maestro::new(inventory, topology);
|
||||||
|
|
||||||
|
maestro.register_all(vec![
|
||||||
|
Box::new(SuccessScore {}),
|
||||||
|
Box::new(ErrorScore {}),
|
||||||
|
Box::new(PanicScore {}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let res = crate::init(
|
||||||
|
maestro,
|
||||||
|
Some(crate::Args {
|
||||||
|
yes: true,
|
||||||
|
filter: Some("ErrorScore".to_owned()),
|
||||||
|
interactive: false,
|
||||||
|
all: true,
|
||||||
|
number: 0,
|
||||||
|
list: false,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(res.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_init_number_score() {
|
||||||
|
let inventory = Inventory::autoload();
|
||||||
|
let topology = HAClusterTopology::autoload();
|
||||||
|
let mut maestro = Maestro::new(inventory, topology);
|
||||||
|
|
||||||
|
maestro.register_all(vec![
|
||||||
|
Box::new(SuccessScore {}),
|
||||||
|
Box::new(ErrorScore {}),
|
||||||
|
Box::new(PanicScore {}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let res = crate::init(
|
||||||
|
maestro,
|
||||||
|
Some(crate::Args {
|
||||||
|
yes: true,
|
||||||
|
filter: None,
|
||||||
|
interactive: false,
|
||||||
|
all: false,
|
||||||
|
number: 0,
|
||||||
|
list: false,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(res.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_filter_fn_all() {
|
||||||
|
let inventory = Inventory::autoload();
|
||||||
|
let topology = HAClusterTopology::autoload();
|
||||||
|
let mut maestro = Maestro::new(inventory, topology);
|
||||||
|
|
||||||
|
maestro.register_all(vec![
|
||||||
|
Box::new(SuccessScore {}),
|
||||||
|
Box::new(ErrorScore {}),
|
||||||
|
Box::new(PanicScore {}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let res = crate::maestro_scores_filter(&maestro, true, None, 0);
|
||||||
|
|
||||||
|
assert!(res.len() == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_filter_fn_all_success() {
|
||||||
|
let inventory = Inventory::autoload();
|
||||||
|
let topology = HAClusterTopology::autoload();
|
||||||
|
let mut maestro = Maestro::new(inventory, topology);
|
||||||
|
|
||||||
|
maestro.register_all(vec![
|
||||||
|
Box::new(SuccessScore {}),
|
||||||
|
Box::new(ErrorScore {}),
|
||||||
|
Box::new(PanicScore {}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let res = crate::maestro_scores_filter(&maestro, true, Some("Success".to_owned()), 0);
|
||||||
|
|
||||||
|
assert!(res.len() == 1);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
maestro
|
||||||
|
.interpret(res.get(0).unwrap().clone_box())
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_filter_fn_all_error() {
|
||||||
|
let inventory = Inventory::autoload();
|
||||||
|
let topology = HAClusterTopology::autoload();
|
||||||
|
let mut maestro = Maestro::new(inventory, topology);
|
||||||
|
|
||||||
|
maestro.register_all(vec![
|
||||||
|
Box::new(SuccessScore {}),
|
||||||
|
Box::new(ErrorScore {}),
|
||||||
|
Box::new(PanicScore {}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let res = crate::maestro_scores_filter(&maestro, true, Some("Error".to_owned()), 0);
|
||||||
|
|
||||||
|
assert!(res.len() == 1);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
maestro
|
||||||
|
.interpret(res.get(0).unwrap().clone_box())
|
||||||
|
.await
|
||||||
|
.is_err()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_filter_fn_all_score() {
|
||||||
|
let inventory = Inventory::autoload();
|
||||||
|
let topology = HAClusterTopology::autoload();
|
||||||
|
let mut maestro = Maestro::new(inventory, topology);
|
||||||
|
|
||||||
|
maestro.register_all(vec![
|
||||||
|
Box::new(SuccessScore {}),
|
||||||
|
Box::new(ErrorScore {}),
|
||||||
|
Box::new(PanicScore {}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let res = crate::maestro_scores_filter(&maestro, true, Some("Score".to_owned()), 0);
|
||||||
|
|
||||||
|
assert!(res.len() == 3);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
maestro
|
||||||
|
.interpret(res.get(0).unwrap().clone_box())
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
maestro
|
||||||
|
.interpret(res.get(1).unwrap().clone_box())
|
||||||
|
.await
|
||||||
|
.is_err()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_filter_fn_number() {
|
||||||
|
let inventory = Inventory::autoload();
|
||||||
|
let topology = HAClusterTopology::autoload();
|
||||||
|
let mut maestro = Maestro::new(inventory, topology);
|
||||||
|
|
||||||
|
maestro.register_all(vec![
|
||||||
|
Box::new(SuccessScore {}),
|
||||||
|
Box::new(ErrorScore {}),
|
||||||
|
Box::new(PanicScore {}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let res = crate::maestro_scores_filter(&maestro, false, None, 0);
|
||||||
|
|
||||||
|
println!("{:#?}", res);
|
||||||
|
|
||||||
|
assert!(res.len() == 1);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
maestro
|
||||||
|
.interpret(res.get(0).unwrap().clone_box())
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_filter_fn_number_invalid() {
|
||||||
|
let inventory = Inventory::autoload();
|
||||||
|
let topology = HAClusterTopology::autoload();
|
||||||
|
let mut maestro = Maestro::new(inventory, topology);
|
||||||
|
|
||||||
|
maestro.register_all(vec![
|
||||||
|
Box::new(SuccessScore {}),
|
||||||
|
Box::new(ErrorScore {}),
|
||||||
|
Box::new(PanicScore {}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let res = crate::maestro_scores_filter(&maestro, false, None, 11);
|
||||||
|
|
||||||
|
println!("{:#?}", res);
|
||||||
|
|
||||||
|
assert!(res.len() == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user