feat: use interactive_parse lib to query for secrets attributes values #219

Merged
stremblay merged 5 commits from feat/json-attributes-prompt into master 2026-01-26 15:54:05 +00:00
21 changed files with 135 additions and 57 deletions

111
Cargo.lock generated
View File

@@ -686,6 +686,7 @@ dependencies = [
"regex",
"russh",
"russh-keys",
"schemars 0.8.22",
"serde",
"tokio",
]
@@ -875,6 +876,22 @@ dependencies = [
"shlex",
]
[[package]]
name = "cert_manager"
version = "0.1.0"
dependencies = [
"assert_cmd",
"cidr",
"env_logger",
"harmony",
"harmony_cli",
"harmony_macros",
"harmony_types",
"log",
"tokio",
"url",
]
[[package]]
name = "cfg-if"
version = "1.0.3"
@@ -1205,6 +1222,22 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "crossterm"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13"
dependencies = [
"bitflags 1.3.2",
"crossterm_winapi",
"libc",
"mio 0.8.11",
"parking_lot",
"signal-hook",
"signal-hook-mio",
"winapi 0.3.9",
]
[[package]]
name = "crossterm"
version = "0.28.1"
@@ -1767,6 +1800,7 @@ dependencies = [
"harmony_tui",
"harmony_types",
"log",
"schemars 0.8.22",
"serde",
"tokio",
"url",
@@ -1781,7 +1815,7 @@ dependencies = [
"harmony",
"harmony_macros",
"http 1.3.1",
"inquire",
"inquire 0.7.5",
"k8s-openapi",
"kube",
"log",
@@ -1857,6 +1891,7 @@ dependencies = [
"harmony_tui",
"harmony_types",
"log",
"schemars 0.8.22",
"serde",
"tokio",
"url",
@@ -1920,6 +1955,7 @@ dependencies = [
"harmony_secret_derive",
"harmony_types",
"log",
"schemars 0.8.22",
"serde",
"tokio",
"url",
@@ -1965,6 +2001,7 @@ dependencies = [
"harmony_secret",
"harmony_types",
"log",
"schemars 0.8.22",
"serde",
"tokio",
"url",
@@ -2033,6 +2070,7 @@ dependencies = [
"harmony_secret_derive",
"harmony_types",
"log",
"schemars 0.8.22",
"serde",
"tokio",
"url",
@@ -2548,7 +2586,7 @@ dependencies = [
"helm-wrapper-rs",
"hex",
"http 1.3.1",
"inquire",
"inquire 0.7.5",
"k3d-rs",
"k8s-openapi",
"kube",
@@ -2599,7 +2637,7 @@ dependencies = [
"harmony_tui",
"indicatif",
"indicatif-log-bridge",
"inquire",
"inquire 0.7.5",
"lazy_static",
"log",
"tokio",
@@ -2680,10 +2718,12 @@ dependencies = [
"harmony_secret_derive",
"http 1.3.1",
"infisical",
"inquire",
"inquire 0.7.5",
"interactive-parse",
"lazy_static",
"log",
"pretty_assertions",
"schemars 0.8.22",
"serde",
"serde_json",
"tempfile",
@@ -3348,6 +3388,22 @@ dependencies = [
"generic-array",
]
[[package]]
name = "inquire"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33e7c1ddeb15c9abcbfef6029d8e29f69b52b6d6c891031b88ed91b5065803b"
dependencies = [
"bitflags 1.3.2",
"crossterm 0.25.0",
"dyn-clone",
"lazy_static",
"newline-converter 0.2.2",
"thiserror 1.0.69",
"unicode-segmentation",
"unicode-width 0.1.14",
]
[[package]]
name = "inquire"
version = "0.7.5"
@@ -3359,7 +3415,7 @@ dependencies = [
"dyn-clone",
"fuzzy-matcher",
"fxhash",
"newline-converter",
"newline-converter 0.3.0",
"once_cell",
"unicode-segmentation",
"unicode-width 0.1.14",
@@ -3378,6 +3434,22 @@ dependencies = [
"syn 2.0.106",
]
[[package]]
name = "interactive-parse"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82c6684d66c9fd6b51cafbf2a9105583d5046dd4c6363f31745686f503a285e8"
dependencies = [
"crossterm 0.26.1",
"inquire 0.6.2",
"lazy_static",
"log",
"schemars 0.8.22",
"serde",
"serde_json",
"thiserror 1.0.69",
]
[[package]]
name = "io-uring"
version = "0.7.10"
@@ -3482,25 +3554,6 @@ dependencies = [
"thiserror 1.0.69",
]
[[package]]
name = "json-prompt"
version = "0.1.0"
dependencies = [
"brocade",
"cidr",
"env_logger",
"harmony",
"harmony_cli",
"harmony_macros",
"harmony_secret",
"harmony_secret_derive",
"harmony_types",
"log",
"serde",
"tokio",
"url",
]
[[package]]
name = "jsonpath-rust"
version = "0.7.5"
@@ -3956,6 +4009,15 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "newline-converter"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f71d09d5c87634207f894c6b31b6a2b2c64ea3bdcf71bd5599fdbbe1600c00f"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "newline-converter"
version = "0.3.0"
@@ -6113,6 +6175,7 @@ dependencies = [
"harmony_secret_derive",
"harmony_types",
"log",
"schemars 0.8.22",
"serde",
"tokio",
"url",

View File

@@ -16,3 +16,4 @@ env_logger.workspace = true
regex = "1.11.3"
harmony_secret = { path = "../harmony_secret" }
serde.workspace = true
schemars = "0.8"

View File

@@ -3,9 +3,10 @@ use std::net::{IpAddr, Ipv4Addr};
use brocade::{BrocadeOptions, ssh};
use harmony_secret::{Secret, SecretManager};
use harmony_types::switch::PortLocation;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Secret, Clone, Debug, Serialize, Deserialize)]
#[derive(Secret, Clone, Debug, JsonSchema, Serialize, Deserialize)]
struct BrocadeSwitchAuth {
username: String,
password: String,

View File

@@ -19,3 +19,4 @@ url = { workspace = true }
harmony_secret = { path = "../../harmony_secret" }
brocade = { path = "../../brocade" }
serde = { workspace = true }
schemars = "0.8"

View File

@@ -21,6 +21,7 @@ use harmony::{
use harmony_macros::{ip, mac_address};
use harmony_secret::{Secret, SecretManager};
use harmony_types::net::Url;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[tokio::main]
@@ -136,7 +137,7 @@ async fn main() {
.unwrap();
}
#[derive(Secret, Serialize, Deserialize, Debug)]
#[derive(Secret, Serialize, JsonSchema, Deserialize, Debug)]
pub struct BrocadeSwitchAuth {
pub username: String,
pub password: String,

View File

@@ -19,3 +19,4 @@ env_logger = { workspace = true }
url = { workspace = true }
serde = { workspace = true }
brocade = { path = "../../brocade" }
schemars = "0.8"

View File

@@ -25,6 +25,7 @@ use harmony::{
use harmony_macros::{ip, mac_address};
use harmony_secret::{Secret, SecretManager};
use harmony_types::net::Url;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[tokio::main]
@@ -192,7 +193,7 @@ async fn main() {
.unwrap();
}
#[derive(Secret, Serialize, Deserialize, Debug)]
#[derive(Secret, Serialize, Deserialize, JsonSchema, Debug)]
pub struct BrocadeSwitchAuth {
pub username: String,
pub password: String,

View File

@@ -20,3 +20,4 @@ env_logger = { workspace = true }
url = { workspace = true }
serde.workspace = true
brocade = { path = "../../brocade" }
schemars = "0.8"

View File

@@ -8,13 +8,14 @@ use harmony::{
};
use harmony_macros::{ip, ipv4};
use harmony_secret::{Secret, SecretManager};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::{
net::IpAddr,
sync::{Arc, OnceLock},
};
#[derive(Secret, Serialize, Deserialize, Debug, PartialEq)]
#[derive(Secret, Serialize, Deserialize, JsonSchema, Debug, PartialEq)]
struct OPNSenseFirewallConfig {
username: String,
password: String,
@@ -103,7 +104,7 @@ pub fn get_inventory() -> Inventory {
}
}
#[derive(Secret, Serialize, Deserialize, Debug)]
#[derive(Secret, Serialize, Deserialize, JsonSchema, Debug)]
pub struct BrocadeSwitchAuth {
pub username: String,
pub password: String,

View File

@@ -20,3 +20,4 @@ env_logger = { workspace = true }
url = { workspace = true }
serde.workspace = true
brocade = { path = "../../brocade" }
schemars = "0.8"

View File

@@ -9,6 +9,7 @@ use harmony::{
};
use harmony_macros::{ip, ipv4};
use harmony_secret::{Secret, SecretManager};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::{
net::IpAddr,
@@ -98,7 +99,7 @@ pub fn get_inventory() -> Inventory {
}
}
#[derive(Secret, Serialize, Deserialize, Debug)]
#[derive(Secret, Serialize, Deserialize, JsonSchema, Debug)]
pub struct BrocadeSwitchAuth {
pub username: String,
pub password: String,

View File

@@ -19,3 +19,4 @@ url = { workspace = true }
harmony_secret = { path = "../../harmony_secret" }
brocade = { path = "../../brocade" }
serde = { workspace = true }
schemars = "0.8"

View File

@@ -7,6 +7,7 @@ use harmony::{
};
use harmony_macros::{ip, ipv4};
use harmony_secret::{Secret, SecretManager};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[tokio::main]
@@ -70,7 +71,7 @@ async fn main() {
.unwrap();
}
#[derive(Secret, Serialize, Deserialize, Debug)]
#[derive(Secret, Serialize, Deserialize, JsonSchema, Debug)]
pub struct BrocadeSwitchAuth {
pub username: String,
pub password: String,

View File

@@ -20,3 +20,4 @@ env_logger = { workspace = true }
url = { workspace = true }
serde = { workspace = true }
brocade = { path = "../../brocade" }
schemars = "0.8"

View File

@@ -7,13 +7,14 @@ use harmony::{
};
use harmony_macros::{ip, ipv4};
use harmony_secret::{Secret, SecretManager};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::{
net::IpAddr,
sync::{Arc, OnceLock},
};
#[derive(Secret, Serialize, Deserialize, Debug, PartialEq)]
#[derive(Secret, Serialize, Deserialize, JsonSchema, Debug, PartialEq)]
struct OPNSenseFirewallConfig {
username: String,
password: String,

View File

@@ -1,20 +1,21 @@
use harmony_secret_derive::Secret;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Secret, Serialize, Deserialize, Debug, PartialEq)]
#[derive(Secret, Serialize, Deserialize, JsonSchema, Debug, PartialEq)]
pub struct OPNSenseFirewallCredentials {
pub username: String,
pub password: String,
}
// TODO we need a better way to handle multiple "instances" of the same secret structure.
#[derive(Secret, Serialize, Deserialize, Debug, PartialEq)]
#[derive(Secret, Serialize, Deserialize, JsonSchema, Debug, PartialEq)]
pub struct SshKeyPair {
pub private: String,
pub public: String,
}
#[derive(Secret, Serialize, Deserialize, Debug, PartialEq)]
#[derive(Secret, Serialize, Deserialize, JsonSchema, Debug, PartialEq)]
pub struct RedhatSecret {
pub pull_secret: String,
}

View File

@@ -28,6 +28,7 @@ use harmony_secret_derive::Secret;
use harmony_types::net::Url;
use kube::api::ObjectMeta;
use log::{debug, info};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
@@ -132,7 +133,7 @@ impl<
}
}
#[derive(Secret, Serialize, Deserialize, Clone, Debug)]
#[derive(Secret, Serialize, Deserialize, JsonSchema, Clone, Debug)]
struct NtfyAuth {
username: String,
password: String,

View File

@@ -4,6 +4,7 @@ use async_trait::async_trait;
use brocade::BrocadeOptions;
use harmony_secret::{Secret, SecretManager};
use harmony_types::id::Id;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::{
@@ -37,13 +38,13 @@ pub struct BrocadeEnableSnmpInterpret {
score: BrocadeEnableSnmpScore,
}
#[derive(Secret, Clone, Debug, Serialize, Deserialize)]
#[derive(Secret, Clone, Debug, JsonSchema, Serialize, Deserialize)]
struct BrocadeSwitchAuth {
username: String,
password: String,
}
#[derive(Secret, Clone, Debug, Serialize, Deserialize)]
#[derive(Secret, Clone, Debug, JsonSchema, Serialize, Deserialize)]
struct BrocadeSnmpAuth {
username: String,
auth_password: String,

View File

@@ -45,7 +45,7 @@ impl OKDRoleProperties for WorkerRole {
}
fn required_hosts(&self) -> i16 {
2
1
}
fn logical_hosts<'a>(&self, t: &'a HAClusterTopology) -> &'a Vec<LogicalHost> {

View File

@@ -19,6 +19,8 @@ tokio.workspace = true
async-trait.workspace = true
http.workspace = true
inquire.workspace = true
interactive-parse = "0.1.5"
schemars = "0.8"
[dev-dependencies]
pretty_assertions.workspace = true

View File

@@ -9,7 +9,10 @@ use config::INFISICAL_ENVIRONMENT;
use config::INFISICAL_PROJECT_ID;
use config::INFISICAL_URL;
use config::SECRET_STORE;
use interactive_parse::InteractiveParseObj;
use log::debug;
use log::info;
use schemars::JsonSchema;
use serde::{Serialize, de::DeserializeOwned};
use std::fmt;
use store::InfisicalSecretStore;
@@ -20,7 +23,8 @@ use tokio::sync::OnceCell;
pub use harmony_secret_derive::Secret;
// The Secret trait remains the same.
pub trait Secret: Serialize + DeserializeOwned + Sized {
// pub trait Secret: Serialize + DeserializeOwned + Sized {
pub trait Secret: Serialize + DeserializeOwned + JsonSchema + InteractiveParseObj + Sized {
const KEY: &'static str;
}
@@ -120,23 +124,15 @@ impl SecretManager {
Review

cleanup

cleanup
let ns = &manager.namespace;
let key = T::KEY;
let secret_json = inquire::Text::new(&format!(
"Secret not found for {} {}, paste the JSON here :",
ns, key
))
.prompt()
.map_err(|e| {
SecretStoreError::Store(format!("Failed to prompt secret {ns} {key} : {e}").into())
})?;
let secret: T = serde_json::from_str(&secret_json).map_err(|e| {
SecretStoreError::Deserialization {
key: T::KEY.to_string(),
source: e,
}
})?;
debug!("Prompting interactively for secret {ns} {key}");
info!("Secret not found for {} {}, fill the fields :", ns, key);
Ok(secret)
T::parse_to_obj().map_err(|e| {
SecretStoreError::Store(
format!("Failed to interactively parse secret {ns} {key}: {e}").into(),
)
})
})?;
if prompted {
@@ -168,12 +164,12 @@ mod test {
use pretty_assertions::assert_eq;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[derive(Serialize, Deserialize, JsonSchema, Debug, PartialEq)]
struct TestUserMeta {
labels: Vec<String>,
}
#[derive(Secret, Serialize, Deserialize, Debug, PartialEq)]
#[derive(Secret, Serialize, Deserialize, JsonSchema, Debug, PartialEq)]
struct TestSecret {
user: String,
password: String,