feat: refactor import_certs command into separate module for improved structure
This commit is contained in:
119
apps/nxmesh-agent/src/cli/import_certs.rs
Normal file
119
apps/nxmesh-agent/src/cli/import_certs.rs
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(about = "Import certificates for agent from zip file or separate cert and key files")]
|
||||||
|
pub struct ImportCertsCommand {
|
||||||
|
/// Zip file containing ca.pem cert.pem and key.pem
|
||||||
|
#[arg(value_name = "ZIP_FILE", group = "input_source")]
|
||||||
|
zip: Option<String>,
|
||||||
|
/// Certificate name in zip file, required if using zip input
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
group = "input_source",
|
||||||
|
requires = "zip",
|
||||||
|
default_value = "cert.pem",
|
||||||
|
value_name = "CERT_NAME"
|
||||||
|
)]
|
||||||
|
cert_name: Option<String>,
|
||||||
|
/// Key name in zip file, required if using zip input
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
group = "input_source",
|
||||||
|
requires = "zip",
|
||||||
|
default_value = "key.pem",
|
||||||
|
value_name = "KEY_NAME"
|
||||||
|
)]
|
||||||
|
key_name: Option<String>,
|
||||||
|
/// CA certificate name in zip file, required if using zip input
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
group = "input_source",
|
||||||
|
requires = "zip",
|
||||||
|
default_value = "ca.pem",
|
||||||
|
value_name = "CA_NAME"
|
||||||
|
)]
|
||||||
|
ca_name: Option<String>,
|
||||||
|
|
||||||
|
// Separate cert and key file inputs, required if not using zip input
|
||||||
|
/// Certificate file path
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
group = "input_source",
|
||||||
|
requires = "key",
|
||||||
|
conflicts_with = "zip",
|
||||||
|
value_name = "CERT_FILE"
|
||||||
|
)]
|
||||||
|
cert: Option<String>,
|
||||||
|
|
||||||
|
/// Key file path
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
group = "input_source",
|
||||||
|
requires = "cert",
|
||||||
|
conflicts_with = "zip",
|
||||||
|
value_name = "KEY_FILE"
|
||||||
|
)]
|
||||||
|
key: Option<String>,
|
||||||
|
|
||||||
|
/// Master CA certificate file path for verifying master identity, optional if the CA certificate is already trusted by the system
|
||||||
|
/// This is required if the master server uses a self-signed certificate that is not trusted by the system
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
group = "input_source",
|
||||||
|
conflicts_with = "zip",
|
||||||
|
value_name = "CA_CERT_FILE"
|
||||||
|
)]
|
||||||
|
ca_cert: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_import_certs_with_zip_defaults() {
|
||||||
|
let parsed = ImportCertsCommand::try_parse_from(["import-certs", "bundle.zip"]);
|
||||||
|
assert!(parsed.is_ok());
|
||||||
|
|
||||||
|
let parsed = parsed.ok();
|
||||||
|
assert!(parsed.is_some());
|
||||||
|
let parsed = parsed.unwrap_or_else(|| unreachable!());
|
||||||
|
|
||||||
|
assert_eq!(parsed.zip.as_deref(), Some("bundle.zip"));
|
||||||
|
assert_eq!(parsed.cert_name.as_deref(), Some("cert.pem"));
|
||||||
|
assert_eq!(parsed.key_name.as_deref(), Some("key.pem"));
|
||||||
|
assert_eq!(parsed.ca_name.as_deref(), Some("ca.pem"));
|
||||||
|
assert!(parsed.cert.is_none());
|
||||||
|
assert!(parsed.key.is_none());
|
||||||
|
assert!(parsed.ca_cert.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rejects_import_certs_with_separate_files() {
|
||||||
|
let parsed = ImportCertsCommand::try_parse_from([
|
||||||
|
"import-certs",
|
||||||
|
"--cert",
|
||||||
|
"agent.crt",
|
||||||
|
"--key",
|
||||||
|
"agent.key",
|
||||||
|
"--ca-cert",
|
||||||
|
"ca.crt",
|
||||||
|
]);
|
||||||
|
assert!(parsed.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rejects_conflicting_zip_and_separate_inputs() {
|
||||||
|
let parsed = ImportCertsCommand::try_parse_from([
|
||||||
|
"import-certs",
|
||||||
|
"bundle.zip",
|
||||||
|
"--cert",
|
||||||
|
"agent.crt",
|
||||||
|
"--key",
|
||||||
|
"agent.key",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(parsed.is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
|
pub mod import_certs;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
@@ -13,78 +15,14 @@ pub struct Cli {
|
|||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum Commands {
|
pub enum Commands {
|
||||||
#[command(about = "Import certificates for agent from zip file or separate cert and key files")]
|
ImportCerts(import_certs::ImportCertsCommand),
|
||||||
ImportCerts {
|
|
||||||
// Zip file input, mutually exclusive with separate cert and key file inputs
|
|
||||||
/// Zip file containing ca.pem cert.pem and key.pem
|
|
||||||
#[arg(value_name = "ZIP_FILE", group = "input_source")]
|
|
||||||
zip: Option<String>,
|
|
||||||
/// Certificate name in zip file, required if using zip input
|
|
||||||
#[arg(
|
|
||||||
long,
|
|
||||||
group = "input_source",
|
|
||||||
requires = "zip",
|
|
||||||
default_value = "cert.pem",
|
|
||||||
value_name = "CERT_NAME"
|
|
||||||
)]
|
|
||||||
cert_name: Option<String>,
|
|
||||||
/// Key name in zip file, required if using zip input
|
|
||||||
#[arg(
|
|
||||||
long,
|
|
||||||
group = "input_source",
|
|
||||||
requires = "zip",
|
|
||||||
default_value = "key.pem",
|
|
||||||
value_name = "KEY_NAME"
|
|
||||||
)]
|
|
||||||
key_name: Option<String>,
|
|
||||||
/// CA certificate name in zip file, required if using zip input
|
|
||||||
#[arg(
|
|
||||||
long,
|
|
||||||
group = "input_source",
|
|
||||||
requires = "zip",
|
|
||||||
default_value = "ca.pem",
|
|
||||||
value_name = "CA_NAME"
|
|
||||||
)]
|
|
||||||
ca_name: Option<String>,
|
|
||||||
|
|
||||||
// Separate cert and key file inputs, required if not using zip input
|
|
||||||
/// Certificate file path
|
|
||||||
#[arg(
|
|
||||||
long,
|
|
||||||
group = "input_source",
|
|
||||||
requires = "key",
|
|
||||||
conflicts_with = "zip",
|
|
||||||
value_name = "CERT_FILE"
|
|
||||||
)]
|
|
||||||
cert: Option<String>,
|
|
||||||
|
|
||||||
/// Key file path
|
|
||||||
#[arg(
|
|
||||||
long,
|
|
||||||
group = "input_source",
|
|
||||||
requires = "cert",
|
|
||||||
conflicts_with = "zip",
|
|
||||||
value_name = "KEY_FILE"
|
|
||||||
)]
|
|
||||||
key: Option<String>,
|
|
||||||
|
|
||||||
/// Master CA certificate file path for verifying master identity, optional if the CA certificate is already trusted by the system
|
|
||||||
/// This is required if the master server uses a self-signed certificate that is not trusted by the system
|
|
||||||
#[arg(
|
|
||||||
long,
|
|
||||||
group = "input_source",
|
|
||||||
conflicts_with = "zip",
|
|
||||||
value_name = "CA_CERT_FILE"
|
|
||||||
)]
|
|
||||||
ca_cert: Option<String>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
use super::{Cli, Commands};
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parses_serve_flag_without_subcommand() {
|
fn parses_serve_flag_without_subcommand() {
|
||||||
@@ -98,65 +36,4 @@ mod tests {
|
|||||||
assert!(parsed.serve);
|
assert!(parsed.serve);
|
||||||
assert!(parsed.command.is_none());
|
assert!(parsed.command.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parses_import_certs_with_zip_defaults() {
|
|
||||||
let parsed = Cli::try_parse_from(["nxmesh-agent", "import-certs", "bundle.zip"]);
|
|
||||||
assert!(parsed.is_ok());
|
|
||||||
|
|
||||||
let parsed = parsed.ok();
|
|
||||||
assert!(parsed.is_some());
|
|
||||||
let parsed = parsed.unwrap_or_else(|| unreachable!());
|
|
||||||
|
|
||||||
match parsed.command {
|
|
||||||
Some(Commands::ImportCerts {
|
|
||||||
zip,
|
|
||||||
cert_name,
|
|
||||||
key_name,
|
|
||||||
ca_name,
|
|
||||||
cert,
|
|
||||||
key,
|
|
||||||
ca_cert,
|
|
||||||
}) => {
|
|
||||||
assert_eq!(zip.as_deref(), Some("bundle.zip"));
|
|
||||||
assert_eq!(cert_name.as_deref(), Some("cert.pem"));
|
|
||||||
assert_eq!(key_name.as_deref(), Some("key.pem"));
|
|
||||||
assert_eq!(ca_name.as_deref(), Some("ca.pem"));
|
|
||||||
assert!(cert.is_none());
|
|
||||||
assert!(key.is_none());
|
|
||||||
assert!(ca_cert.is_none());
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn rejects_import_certs_with_separate_files() {
|
|
||||||
let parsed = Cli::try_parse_from([
|
|
||||||
"nxmesh-agent",
|
|
||||||
"import-certs",
|
|
||||||
"--cert",
|
|
||||||
"agent.crt",
|
|
||||||
"--key",
|
|
||||||
"agent.key",
|
|
||||||
"--ca-cert",
|
|
||||||
"ca.crt",
|
|
||||||
]);
|
|
||||||
assert!(parsed.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn rejects_conflicting_zip_and_separate_inputs() {
|
|
||||||
let parsed = Cli::try_parse_from([
|
|
||||||
"nxmesh-agent",
|
|
||||||
"import-certs",
|
|
||||||
"bundle.zip",
|
|
||||||
"--cert",
|
|
||||||
"agent.crt",
|
|
||||||
"--key",
|
|
||||||
"agent.key",
|
|
||||||
]);
|
|
||||||
|
|
||||||
assert!(parsed.is_err());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user