client: add opts for non-interactive network installs

pull/54/head
Jake McGinty 2021-04-17 12:18:50 +09:00
parent 6d28e7f4ab
commit b92ad65b17
4 changed files with 65 additions and 36 deletions

View File

@ -3,9 +3,9 @@ use dialoguer::{Confirm, Input};
use hostsfile::HostsBuilder; use hostsfile::HostsBuilder;
use indoc::printdoc; use indoc::printdoc;
use shared::{ use shared::{
interface_config::InterfaceConfig, prompts, AddCidrContents, AddPeerContents, Association, interface_config::InterfaceConfig, prompts, AddCidrOpts, AddPeerOpts, Association,
AssociationContents, Cidr, CidrTree, EndpointContents, Interface, IoErrorContext, Peer, AssociationContents, Cidr, CidrTree, EndpointContents, InstallOpts, Interface, IoErrorContext,
RedeemContents, State, CLIENT_CONFIG_PATH, REDEEM_TRANSITION_WAIT, Peer, RedeemContents, State, CLIENT_CONFIG_PATH, REDEEM_TRANSITION_WAIT,
}; };
use std::{ use std::{
fmt, fmt,
@ -56,6 +56,9 @@ enum Command {
#[structopt(flatten)] #[structopt(flatten)]
hosts: HostsOpt, hosts: HostsOpt,
#[structopt(flatten)]
opts: InstallOpts,
}, },
/// Enumerate all innernet connections. /// Enumerate all innernet connections.
@ -107,7 +110,7 @@ enum Command {
interface: Interface, interface: Interface,
#[structopt(flatten)] #[structopt(flatten)]
args: AddPeerContents, opts: AddPeerOpts,
}, },
/// Add a new CIDR. /// Add a new CIDR.
@ -115,7 +118,7 @@ enum Command {
interface: Interface, interface: Interface,
#[structopt(flatten)] #[structopt(flatten)]
args: AddCidrContents, opts: AddCidrOpts,
}, },
/// Disable an enabled peer. /// Disable an enabled peer.
@ -191,14 +194,20 @@ fn update_hosts_file(
Ok(()) Ok(())
} }
fn install(invite: &Path, hosts_file: Option<PathBuf>) -> Result<(), Error> { fn install(invite: &Path, hosts_file: Option<PathBuf>, opts: InstallOpts) -> Result<(), Error> {
shared::ensure_dirs_exist(&[*CLIENT_CONFIG_PATH])?; shared::ensure_dirs_exist(&[*CLIENT_CONFIG_PATH])?;
let config = InterfaceConfig::from_file(invite)?; let config = InterfaceConfig::from_file(invite)?;
let iface = Input::with_theme(&*prompts::THEME) let iface = if opts.default_name {
.with_prompt("Interface name") config.interface.network_name.clone()
.default(config.interface.network_name.clone()) } else if let Some(ref iface) = opts.name {
.interact()?; 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"); let target_conf = CLIENT_CONFIG_PATH.join(&iface).with_extension("conf");
if target_conf.exists() { if target_conf.exists() {
@ -217,13 +226,14 @@ fn install(invite: &Path, hosts_file: Option<PathBuf>) -> Result<(), Error> {
fetch(&iface, false, hosts_file)?; fetch(&iface, false, hosts_file)?;
if Confirm::with_theme(&*prompts::THEME) if opts.delete_invite
.with_prompt(&format!( || Confirm::with_theme(&*prompts::THEME)
"Delete invitation file \"{}\" now? (It's no longer needed)", .with_prompt(&format!(
invite.to_string_lossy().yellow() "Delete invitation file \"{}\" now? (It's no longer needed)",
)) invite.to_string_lossy().yellow()
.default(true) ))
.interact()? .default(true)
.interact()?
{ {
std::fs::remove_file(invite).with_path(invite)?; std::fs::remove_file(invite).with_path(invite)?;
} }
@ -473,13 +483,13 @@ fn uninstall(interface: &InterfaceName) -> Result<(), Error> {
Ok(()) 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)?; let InterfaceConfig { server, .. } = InterfaceConfig::from_interface(interface)?;
println!("Fetching CIDRs"); println!("Fetching CIDRs");
let api = Api::new(&server); let api = Api::new(&server);
let cidrs: Vec<Cidr> = api.http("GET", "/admin/cidrs")?; let cidrs: Vec<Cidr> = api.http("GET", "/admin/cidrs")?;
let cidr_request = prompts::add_cidr(&cidrs, &args)?; let cidr_request = prompts::add_cidr(&cidrs, &opts)?;
println!("Creating CIDR..."); println!("Creating CIDR...");
let cidr: Cidr = api.http_form("POST", "/admin/cidrs", cidr_request)?; 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(()) 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 InterfaceConfig { server, .. } = InterfaceConfig::from_interface(interface)?;
let api = Api::new(&server); let api = Api::new(&server);
@ -509,7 +519,7 @@ fn add_peer(interface: &InterfaceName, args: AddPeerContents) -> Result<(), Erro
let peers: Vec<Peer> = api.http("GET", "/admin/peers")?; let peers: Vec<Peer> = api.http("GET", "/admin/peers")?;
let cidr_tree = CidrTree::new(&cidrs[..]); 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..."); println!("Creating peer...");
let peer: Peer = api.http_form("POST", "/admin/peers", peer_request)?; let peer: Peer = api.http_form("POST", "/admin/peers", peer_request)?;
let server_peer = peers.iter().find(|p| p.id == 1).unwrap(); 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, &cidr_tree,
keypair, keypair,
&server.internal_endpoint, &server.internal_endpoint,
&args.save_config, &opts.save_config,
)?; )?;
} else { } else {
println!("exited without creating peer."); println!("exited without creating peer.");
@ -830,7 +840,11 @@ fn run(opt: Opt) -> Result<(), Error> {
}); });
match command { match command {
Command::Install { config, hosts } => install(&config, hosts.into())?, Command::Install {
config,
hosts,
opts,
} => install(&config, hosts.into(), opts)?,
Command::Show { Command::Show {
short, short,
tree, tree,
@ -849,8 +863,8 @@ fn run(opt: Opt) -> Result<(), Error> {
)?, )?,
Command::Down { interface } => wg::down(&interface)?, Command::Down { interface } => wg::down(&interface)?,
Command::Uninstall { interface } => uninstall(&interface)?, Command::Uninstall { interface } => uninstall(&interface)?,
Command::AddPeer { interface, args } => add_peer(&interface, args)?, Command::AddPeer { interface, opts } => add_peer(&interface, opts)?,
Command::AddCidr { interface, args } => add_cidr(&interface, args)?, Command::AddCidr { interface, opts } => add_cidr(&interface, opts)?,
Command::DisablePeer { interface } => enable_or_disable_peer(&interface, false)?, Command::DisablePeer { interface } => enable_or_disable_peer(&interface, false)?,
Command::EnablePeer { interface } => enable_or_disable_peer(&interface, true)?, Command::EnablePeer { interface } => enable_or_disable_peer(&interface, true)?,
Command::AddAssociation { interface } => add_association(&interface)?, Command::AddAssociation { interface } => add_association(&interface)?,

View File

@ -7,7 +7,7 @@ use ipnetwork::IpNetwork;
use parking_lot::Mutex; use parking_lot::Mutex;
use rusqlite::Connection; use rusqlite::Connection;
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
use shared::{AddCidrContents, AddPeerContents, IoErrorContext, INNERNET_PUBKEY_HEADER}; use shared::{AddCidrOpts, AddPeerOpts, IoErrorContext, INNERNET_PUBKEY_HEADER};
use std::{ use std::{
convert::Infallible, convert::Infallible,
env, env,
@ -64,7 +64,7 @@ enum Command {
interface: Interface, interface: Interface,
#[structopt(flatten)] #[structopt(flatten)]
args: AddPeerContents, args: AddPeerOpts,
}, },
/// Add a new CIDR to an existing network. /// Add a new CIDR to an existing network.
@ -72,7 +72,7 @@ enum Command {
interface: Interface, interface: Interface,
#[structopt(flatten)] #[structopt(flatten)]
args: AddCidrContents, args: AddCidrOpts,
}, },
} }
@ -237,7 +237,7 @@ fn open_database_connection(
fn add_peer( fn add_peer(
interface: &InterfaceName, interface: &InterfaceName,
conf: &ServerConfig, conf: &ServerConfig,
args: AddPeerContents, args: AddPeerOpts,
) -> Result<(), Error> { ) -> Result<(), Error> {
let config = ConfigFile::from_file(conf.config_path(interface))?; let config = ConfigFile::from_file(conf.config_path(interface))?;
let conn = open_database_connection(interface, conf)?; let conn = open_database_connection(interface, conf)?;
@ -280,7 +280,7 @@ fn add_peer(
fn add_cidr( fn add_cidr(
interface: &InterfaceName, interface: &InterfaceName,
conf: &ServerConfig, conf: &ServerConfig,
args: AddCidrContents, args: AddCidrOpts,
) -> Result<(), Error> { ) -> Result<(), Error> {
let conn = open_database_connection(interface, conf)?; let conn = open_database_connection(interface, conf)?;
let cidrs = DatabaseCidr::list(&conn)?; let cidrs = DatabaseCidr::list(&conn)?;

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
interface_config::{InterfaceConfig, InterfaceInfo, ServerInfo}, interface_config::{InterfaceConfig, InterfaceInfo, ServerInfo},
AddCidrContents, AddPeerContents, Association, Cidr, CidrContents, CidrTree, Error, Peer, AddCidrOpts, AddPeerOpts, Association, Cidr, CidrContents, CidrTree, Error, Peer, PeerContents,
PeerContents, PERSISTENT_KEEPALIVE_INTERVAL_SECS, PERSISTENT_KEEPALIVE_INTERVAL_SECS,
}; };
use colored::*; use colored::*;
use dialoguer::{theme::ColorfulTheme, Confirm, Input, Select}; 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. /// Bring up a prompt to create a new CIDR. Returns the peer request.
pub fn add_cidr(cidrs: &[Cidr], request: &AddCidrContents) -> Result<Option<CidrContents>, Error> { pub fn add_cidr(cidrs: &[Cidr], request: &AddCidrOpts) -> Result<Option<CidrContents>, Error> {
let parent_cidr = if let Some(ref parent_name) = request.parent { let parent_cidr = if let Some(ref parent_name) = request.parent {
cidrs cidrs
.iter() .iter()
@ -161,7 +161,7 @@ pub fn delete_association<'a>(
pub fn add_peer( pub fn add_peer(
peers: &[Peer], peers: &[Peer],
cidr_tree: &CidrTree, cidr_tree: &CidrTree,
args: &AddPeerContents, args: &AddPeerOpts,
) -> Result<Option<(PeerContents, KeyPair)>, Error> { ) -> Result<Option<(PeerContents, KeyPair)>, Error> {
let leaves = cidr_tree.leaves(); let leaves = cidr_tree.leaves();

View File

@ -167,7 +167,22 @@ pub struct RedeemContents {
} }
#[derive(Debug, Clone, PartialEq, StructOpt)] #[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<String>,
/// 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 /// Name of new peer
#[structopt(long)] #[structopt(long)]
pub name: Option<String>, pub name: Option<String>,
@ -197,7 +212,7 @@ pub struct AddPeerContents {
} }
#[derive(Debug, Clone, PartialEq, StructOpt)] #[derive(Debug, Clone, PartialEq, StructOpt)]
pub struct AddCidrContents { pub struct AddCidrOpts {
#[structopt(long)] #[structopt(long)]
pub name: Option<String>, pub name: Option<String>,