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 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<PathBuf>) -> Result<(), Error> {
fn install(invite: &Path, hosts_file: Option<PathBuf>, 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<PathBuf>) -> 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<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...");
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<Peer> = 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)?,

View File

@ -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)?;

View File

@ -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<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 {
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<Option<(PeerContents, KeyPair)>, Error> {
let leaves = cidr_tree.leaves();

View File

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