client, server: adds ability to delete cidrs (#88)
This commit adds a `delete-cidr` to both the client and server. It walks through the prompts just like adding a CIDR. Only eligible CIDRs are presented to the user. Eligibilty requires: - CIDR has no child CIDRs - CIDR has no assigned peers Closes #23pull/92/head
parent
15595d6f23
commit
ff0527d836
|
@ -4,9 +4,9 @@ use hostsfile::HostsBuilder;
|
|||
use indoc::eprintdoc;
|
||||
use shared::{
|
||||
interface_config::InterfaceConfig, prompts, AddAssociationOpts, AddCidrOpts, AddPeerOpts,
|
||||
Association, AssociationContents, Cidr, CidrTree, EndpointContents, InstallOpts, Interface,
|
||||
IoErrorContext, NetworkOpt, Peer, RedeemContents, State, CLIENT_CONFIG_DIR,
|
||||
REDEEM_TRANSITION_WAIT,
|
||||
Association, AssociationContents, Cidr, CidrTree, DeleteCidrOpts, EndpointContents,
|
||||
InstallOpts, Interface, IoErrorContext, NetworkOpt, Peer, RedeemContents, State,
|
||||
CLIENT_CONFIG_DIR, REDEEM_TRANSITION_WAIT,
|
||||
};
|
||||
use std::{
|
||||
fmt,
|
||||
|
@ -148,6 +148,14 @@ enum Command {
|
|||
opts: AddCidrOpts,
|
||||
},
|
||||
|
||||
/// Delete a CIDR.
|
||||
DeleteCidr {
|
||||
interface: Interface,
|
||||
|
||||
#[structopt(flatten)]
|
||||
opts: DeleteCidrOpts,
|
||||
},
|
||||
|
||||
/// Disable an enabled peer.
|
||||
DisablePeer { interface: Interface },
|
||||
|
||||
|
@ -556,6 +564,23 @@ fn add_cidr(interface: &InterfaceName, opts: AddCidrOpts) -> Result<(), Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn delete_cidr(interface: &InterfaceName, opts: DeleteCidrOpts) -> Result<(), Error> {
|
||||
let InterfaceConfig { server, .. } = InterfaceConfig::from_interface(interface)?;
|
||||
println!("Fetching eligible CIDRs");
|
||||
let api = Api::new(&server);
|
||||
let cidrs: Vec<Cidr> = api.http("GET", "/admin/cidrs")?;
|
||||
let peers: Vec<Peer> = api.http("GET", "/admin/peers")?;
|
||||
|
||||
let cidr_id = prompts::delete_cidr(&cidrs, &peers, &opts)?;
|
||||
|
||||
println!("Deleting CIDR...");
|
||||
let _ = api.http("DELETE", &*format!("/admin/cidrs/{}", cidr_id))?;
|
||||
|
||||
println!("CIDR deleted.");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_peer(interface: &InterfaceName, opts: AddPeerOpts) -> Result<(), Error> {
|
||||
let InterfaceConfig { server, .. } = InterfaceConfig::from_interface(interface)?;
|
||||
let api = Api::new(&server);
|
||||
|
@ -957,6 +982,7 @@ fn run(opt: Opts) -> Result<(), Error> {
|
|||
Command::Uninstall { interface } => uninstall(&interface, opt.network)?,
|
||||
Command::AddPeer { interface, opts } => add_peer(&interface, opts)?,
|
||||
Command::AddCidr { interface, opts } => add_cidr(&interface, opts)?,
|
||||
Command::DeleteCidr { interface, opts } => delete_cidr(&interface, opts)?,
|
||||
Command::DisablePeer { interface } => enable_or_disable_peer(&interface, false)?,
|
||||
Command::EnablePeer { interface } => enable_or_disable_peer(&interface, true)?,
|
||||
Command::AddAssociation { interface, opts } => add_association(&interface, opts)?,
|
||||
|
|
|
@ -6,7 +6,7 @@ use ipnetwork::IpNetwork;
|
|||
use parking_lot::{Mutex, RwLock};
|
||||
use rusqlite::Connection;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use shared::{AddCidrOpts, AddPeerOpts, IoErrorContext, NetworkOpt, INNERNET_PUBKEY_HEADER};
|
||||
use shared::{AddCidrOpts, DeleteCidrOpts, AddPeerOpts, IoErrorContext, NetworkOpt, INNERNET_PUBKEY_HEADER};
|
||||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
convert::TryInto,
|
||||
|
@ -85,6 +85,15 @@ enum Command {
|
|||
#[structopt(flatten)]
|
||||
args: AddCidrOpts,
|
||||
},
|
||||
|
||||
/// Delete a CIDR.
|
||||
DeleteCidr {
|
||||
interface: Interface,
|
||||
|
||||
#[structopt(flatten)]
|
||||
args: DeleteCidrOpts,
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
pub type Db = Arc<Mutex<Connection>>;
|
||||
|
@ -221,6 +230,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
} => serve(*interface, &conf, routing).await?,
|
||||
Command::AddPeer { interface, args } => add_peer(&interface, &conf, args, opt.network)?,
|
||||
Command::AddCidr { interface, args } => add_cidr(&interface, &conf, args)?,
|
||||
Command::DeleteCidr { interface, args } => delete_cidr(&interface, &conf, args)?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -317,6 +327,25 @@ fn add_cidr(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn delete_cidr(interface: &InterfaceName, conf: &ServerConfig, args: DeleteCidrOpts) -> Result<(), Error> {
|
||||
println!("Fetching eligible CIDRs");
|
||||
let conn = open_database_connection(interface, conf)?;
|
||||
let cidrs = DatabaseCidr::list(&conn)?;
|
||||
let peers = DatabasePeer::list(&conn)?
|
||||
.into_iter()
|
||||
.map(|dp| dp.inner)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let cidr_id = prompts::delete_cidr(&cidrs, &peers, &args)?;
|
||||
|
||||
println!("Deleting CIDR...");
|
||||
let _ = DatabaseCidr::delete(&conn, cidr_id)?;
|
||||
|
||||
println!("CIDR deleted.");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn uninstall(
|
||||
interface: &InterfaceName,
|
||||
conf: &ServerConfig,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
interface_config::{InterfaceConfig, InterfaceInfo, ServerInfo},
|
||||
AddCidrOpts, AddPeerOpts, Association, Cidr, CidrContents, CidrTree, Endpoint, Error, Peer,
|
||||
PeerContents, PERSISTENT_KEEPALIVE_INTERVAL_SECS,
|
||||
AddCidrOpts, AddPeerOpts, Association, Cidr, CidrContents, CidrTree, DeleteCidrOpts, Endpoint,
|
||||
Error, Peer, PeerContents, PERSISTENT_KEEPALIVE_INTERVAL_SECS,
|
||||
};
|
||||
use colored::*;
|
||||
use dialoguer::{theme::ColorfulTheme, Confirm, Input, Select};
|
||||
|
@ -58,6 +58,42 @@ pub fn add_cidr(cidrs: &[Cidr], request: &AddCidrOpts) -> Result<Option<CidrCont
|
|||
)
|
||||
}
|
||||
|
||||
/// Bring up a prompt to delete a CIDR. Returns the peer request.
|
||||
pub fn delete_cidr(cidrs: &[Cidr], peers: &[Peer], request: &DeleteCidrOpts) -> Result<i64, Error> {
|
||||
let eligible_cidrs: Vec<_> = cidrs
|
||||
.iter()
|
||||
.filter(|cidr| {
|
||||
!peers.iter().any(|peer| peer.contents.cidr_id == cidr.id) &&
|
||||
!cidrs.iter().any(
|
||||
|cidr2| matches!(cidr2.contents.parent, Some(parent_id) if parent_id == cidr.id)
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let cidr = if let Some(ref name) = request.name {
|
||||
cidrs
|
||||
.iter()
|
||||
.find(|cidr| &cidr.name == name)
|
||||
.ok_or_else(|| format!("CIDR {} doesn't exist or isn't eligible for deletion", name))?
|
||||
} else {
|
||||
let cidr_index = Select::with_theme(&*THEME)
|
||||
.with_prompt("Delete CIDR")
|
||||
.items(&eligible_cidrs)
|
||||
.interact()?;
|
||||
&eligible_cidrs[cidr_index]
|
||||
};
|
||||
|
||||
if request.yes
|
||||
|| Confirm::with_theme(&*THEME)
|
||||
.with_prompt(&format!("Delete CIDR \"{}\"?", cidr.name))
|
||||
.default(false)
|
||||
.interact()?
|
||||
{
|
||||
Ok(cidr.id)
|
||||
} else {
|
||||
Err("Canceled".into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn choose_cidr<'a>(cidrs: &'a [Cidr], text: &'static str) -> Result<&'a Cidr, Error> {
|
||||
let eligible_cidrs: Vec<_> = cidrs
|
||||
.iter()
|
||||
|
|
|
@ -337,6 +337,17 @@ pub struct AddCidrOpts {
|
|||
pub yes: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, StructOpt)]
|
||||
pub struct DeleteCidrOpts {
|
||||
/// The CIDR name (eg. "engineers")
|
||||
#[structopt(long)]
|
||||
pub name: Option<String>,
|
||||
|
||||
/// Bypass confirmation
|
||||
#[structopt(long)]
|
||||
pub yes: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, StructOpt)]
|
||||
pub struct AddAssociationOpts {
|
||||
/// The first cidr to associate
|
||||
|
|
Loading…
Reference in New Issue