From b92ad65b170b789a3b9809a47de312e21a328fb3 Mon Sep 17 00:00:00 2001 From: Jake McGinty Date: Sat, 17 Apr 2021 12:18:50 +0900 Subject: [PATCH] client: add opts for non-interactive network installs --- client/src/main.rs | 64 ++++++++++++++++++++++++++----------------- server/src/main.rs | 10 +++---- shared/src/prompts.rs | 8 +++--- shared/src/types.rs | 19 +++++++++++-- 4 files changed, 65 insertions(+), 36 deletions(-) diff --git a/client/src/main.rs b/client/src/main.rs index f90db2f..f539c12 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -3,9 +3,9 @@ use dialoguer::{Confirm, Input}; use hostsfile::HostsBuilder; use indoc::printdoc; use shared::{ - interface_config::InterfaceConfig, prompts, AddCidrContents, AddPeerContents, Association, - AssociationContents, Cidr, CidrTree, EndpointContents, Interface, IoErrorContext, Peer, - RedeemContents, State, CLIENT_CONFIG_PATH, REDEEM_TRANSITION_WAIT, + interface_config::InterfaceConfig, prompts, AddCidrOpts, AddPeerOpts, Association, + AssociationContents, Cidr, CidrTree, EndpointContents, InstallOpts, Interface, IoErrorContext, + Peer, RedeemContents, State, CLIENT_CONFIG_PATH, REDEEM_TRANSITION_WAIT, }; use std::{ fmt, @@ -56,6 +56,9 @@ enum Command { #[structopt(flatten)] hosts: HostsOpt, + + #[structopt(flatten)] + opts: InstallOpts, }, /// Enumerate all innernet connections. @@ -107,7 +110,7 @@ enum Command { interface: Interface, #[structopt(flatten)] - args: AddPeerContents, + opts: AddPeerOpts, }, /// Add a new CIDR. @@ -115,7 +118,7 @@ enum Command { interface: Interface, #[structopt(flatten)] - args: AddCidrContents, + opts: AddCidrOpts, }, /// Disable an enabled peer. @@ -191,14 +194,20 @@ fn update_hosts_file( Ok(()) } -fn install(invite: &Path, hosts_file: Option) -> Result<(), Error> { +fn install(invite: &Path, hosts_file: Option, opts: InstallOpts) -> Result<(), Error> { shared::ensure_dirs_exist(&[*CLIENT_CONFIG_PATH])?; let config = InterfaceConfig::from_file(invite)?; - let iface = Input::with_theme(&*prompts::THEME) - .with_prompt("Interface name") - .default(config.interface.network_name.clone()) - .interact()?; + let iface = if opts.default_name { + config.interface.network_name.clone() + } else if let Some(ref iface) = opts.name { + iface.clone() + } else { + Input::with_theme(&*prompts::THEME) + .with_prompt("Interface name") + .default(config.interface.network_name.clone()) + .interact()? + }; let target_conf = CLIENT_CONFIG_PATH.join(&iface).with_extension("conf"); if target_conf.exists() { @@ -217,13 +226,14 @@ fn install(invite: &Path, hosts_file: Option) -> Result<(), Error> { fetch(&iface, false, hosts_file)?; - if Confirm::with_theme(&*prompts::THEME) - .with_prompt(&format!( - "Delete invitation file \"{}\" now? (It's no longer needed)", - invite.to_string_lossy().yellow() - )) - .default(true) - .interact()? + if opts.delete_invite + || Confirm::with_theme(&*prompts::THEME) + .with_prompt(&format!( + "Delete invitation file \"{}\" now? (It's no longer needed)", + invite.to_string_lossy().yellow() + )) + .default(true) + .interact()? { std::fs::remove_file(invite).with_path(invite)?; } @@ -473,13 +483,13 @@ fn uninstall(interface: &InterfaceName) -> Result<(), Error> { Ok(()) } -fn add_cidr(interface: &InterfaceName, args: AddCidrContents) -> Result<(), Error> { +fn add_cidr(interface: &InterfaceName, opts: AddCidrOpts) -> Result<(), Error> { let InterfaceConfig { server, .. } = InterfaceConfig::from_interface(interface)?; println!("Fetching CIDRs"); let api = Api::new(&server); let cidrs: Vec = api.http("GET", "/admin/cidrs")?; - let cidr_request = prompts::add_cidr(&cidrs, &args)?; + let cidr_request = prompts::add_cidr(&cidrs, &opts)?; println!("Creating CIDR..."); let cidr: Cidr = api.http_form("POST", "/admin/cidrs", cidr_request)?; @@ -499,7 +509,7 @@ fn add_cidr(interface: &InterfaceName, args: AddCidrContents) -> Result<(), Erro Ok(()) } -fn add_peer(interface: &InterfaceName, args: AddPeerContents) -> Result<(), Error> { +fn add_peer(interface: &InterfaceName, opts: AddPeerOpts) -> Result<(), Error> { let InterfaceConfig { server, .. } = InterfaceConfig::from_interface(interface)?; let api = Api::new(&server); @@ -509,7 +519,7 @@ fn add_peer(interface: &InterfaceName, args: AddPeerContents) -> Result<(), Erro let peers: Vec = api.http("GET", "/admin/peers")?; let cidr_tree = CidrTree::new(&cidrs[..]); - if let Some((peer_request, keypair)) = prompts::add_peer(&peers, &cidr_tree, &args)? { + if let Some((peer_request, keypair)) = prompts::add_peer(&peers, &cidr_tree, &opts)? { println!("Creating peer..."); let peer: Peer = api.http_form("POST", "/admin/peers", peer_request)?; let server_peer = peers.iter().find(|p| p.id == 1).unwrap(); @@ -520,7 +530,7 @@ fn add_peer(interface: &InterfaceName, args: AddPeerContents) -> Result<(), Erro &cidr_tree, keypair, &server.internal_endpoint, - &args.save_config, + &opts.save_config, )?; } else { println!("exited without creating peer."); @@ -830,7 +840,11 @@ fn run(opt: Opt) -> Result<(), Error> { }); match command { - Command::Install { config, hosts } => install(&config, hosts.into())?, + Command::Install { + config, + hosts, + opts, + } => install(&config, hosts.into(), opts)?, Command::Show { short, tree, @@ -849,8 +863,8 @@ fn run(opt: Opt) -> Result<(), Error> { )?, Command::Down { interface } => wg::down(&interface)?, Command::Uninstall { interface } => uninstall(&interface)?, - Command::AddPeer { interface, args } => add_peer(&interface, args)?, - Command::AddCidr { interface, args } => add_cidr(&interface, args)?, + Command::AddPeer { interface, opts } => add_peer(&interface, opts)?, + Command::AddCidr { interface, opts } => add_cidr(&interface, opts)?, Command::DisablePeer { interface } => enable_or_disable_peer(&interface, false)?, Command::EnablePeer { interface } => enable_or_disable_peer(&interface, true)?, Command::AddAssociation { interface } => add_association(&interface)?, diff --git a/server/src/main.rs b/server/src/main.rs index 6435d39..5f5e7a2 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -7,7 +7,7 @@ use ipnetwork::IpNetwork; use parking_lot::Mutex; use rusqlite::Connection; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use shared::{AddCidrContents, AddPeerContents, IoErrorContext, INNERNET_PUBKEY_HEADER}; +use shared::{AddCidrOpts, AddPeerOpts, IoErrorContext, INNERNET_PUBKEY_HEADER}; use std::{ convert::Infallible, env, @@ -64,7 +64,7 @@ enum Command { interface: Interface, #[structopt(flatten)] - args: AddPeerContents, + args: AddPeerOpts, }, /// Add a new CIDR to an existing network. @@ -72,7 +72,7 @@ enum Command { interface: Interface, #[structopt(flatten)] - args: AddCidrContents, + args: AddCidrOpts, }, } @@ -237,7 +237,7 @@ fn open_database_connection( fn add_peer( interface: &InterfaceName, conf: &ServerConfig, - args: AddPeerContents, + args: AddPeerOpts, ) -> Result<(), Error> { let config = ConfigFile::from_file(conf.config_path(interface))?; let conn = open_database_connection(interface, conf)?; @@ -280,7 +280,7 @@ fn add_peer( fn add_cidr( interface: &InterfaceName, conf: &ServerConfig, - args: AddCidrContents, + args: AddCidrOpts, ) -> Result<(), Error> { let conn = open_database_connection(interface, conf)?; let cidrs = DatabaseCidr::list(&conn)?; diff --git a/shared/src/prompts.rs b/shared/src/prompts.rs index 0934a74..73d7a56 100644 --- a/shared/src/prompts.rs +++ b/shared/src/prompts.rs @@ -1,7 +1,7 @@ use crate::{ interface_config::{InterfaceConfig, InterfaceInfo, ServerInfo}, - AddCidrContents, AddPeerContents, Association, Cidr, CidrContents, CidrTree, Error, Peer, - PeerContents, PERSISTENT_KEEPALIVE_INTERVAL_SECS, + AddCidrOpts, AddPeerOpts, Association, Cidr, CidrContents, CidrTree, Error, Peer, PeerContents, + PERSISTENT_KEEPALIVE_INTERVAL_SECS, }; use colored::*; use dialoguer::{theme::ColorfulTheme, Confirm, Input, Select}; @@ -32,7 +32,7 @@ pub fn hostname_validator(name: &String) -> Result<(), &'static str> { } /// Bring up a prompt to create a new CIDR. Returns the peer request. -pub fn add_cidr(cidrs: &[Cidr], request: &AddCidrContents) -> Result, Error> { +pub fn add_cidr(cidrs: &[Cidr], request: &AddCidrOpts) -> Result, Error> { let parent_cidr = if let Some(ref parent_name) = request.parent { cidrs .iter() @@ -161,7 +161,7 @@ pub fn delete_association<'a>( pub fn add_peer( peers: &[Peer], cidr_tree: &CidrTree, - args: &AddPeerContents, + args: &AddPeerOpts, ) -> Result, Error> { let leaves = cidr_tree.leaves(); diff --git a/shared/src/types.rs b/shared/src/types.rs index b205ee4..5615b71 100644 --- a/shared/src/types.rs +++ b/shared/src/types.rs @@ -167,7 +167,22 @@ pub struct RedeemContents { } #[derive(Debug, Clone, PartialEq, StructOpt)] -pub struct AddPeerContents { +pub struct InstallOpts { + /// Set a specific interface name + #[structopt(long, conflicts_with = "default-name")] + pub name: Option, + + /// Use the network name inside the invitation as the interface name + #[structopt(long = "default-name")] + pub default_name: bool, + + /// Delete the invitation after a successful install + #[structopt(short, long)] + pub delete_invite: bool, +} + +#[derive(Debug, Clone, PartialEq, StructOpt)] +pub struct AddPeerOpts { /// Name of new peer #[structopt(long)] pub name: Option, @@ -197,7 +212,7 @@ pub struct AddPeerContents { } #[derive(Debug, Clone, PartialEq, StructOpt)] -pub struct AddCidrContents { +pub struct AddCidrOpts { #[structopt(long)] pub name: Option,