Skip to content

Commit

Permalink
Add a new client / server command to rename CIDR.
Browse files Browse the repository at this point in the history
  • Loading branch information
skywhale committed Apr 22, 2024
1 parent 8ab0989 commit c1ea1bc
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 5 deletions.
50 changes: 49 additions & 1 deletion client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use shared::{
AddCidrOpts, AddDeleteAssociationOpts, AddPeerOpts, Association, AssociationContents, Cidr,
CidrTree, DeleteCidrOpts, EnableDisablePeerOpts, Endpoint, EndpointContents, InstallOpts,
Interface, IoErrorContext, ListenPortOpts, NatOpts, NetworkOpts, OverrideEndpointOpts, Peer,
RedeemContents, RenamePeerOpts, State, WrappedIoError, REDEEM_TRANSITION_WAIT,
RedeemContents, RenameCidrOpts, RenamePeerOpts, State, WrappedIoError, REDEEM_TRANSITION_WAIT,
};
use std::{
fmt, io,
Expand Down Expand Up @@ -193,6 +193,19 @@ enum Command {
sub_opts: AddCidrOpts,
},

/// Rename a CIDR
///
/// By default, you'll be prompted interactively to select a CIDR, but you can
/// also specify all the options in the command, eg:
///
/// --name 'group' --new-name 'family'
RenameCidr {
interface: Interface,

#[clap(flatten)]
sub_opts: RenameCidrOpts,
},

/// Delete a CIDR
DeleteCidr {
interface: Interface,
Expand Down Expand Up @@ -724,6 +737,37 @@ fn add_cidr(interface: &InterfaceName, opts: &Opts, sub_opts: AddCidrOpts) -> Re
Ok(())
}

fn rename_cidr(
interface: &InterfaceName,
opts: &Opts,
sub_opts: RenameCidrOpts,
) -> Result<(), Error> {
let InterfaceConfig { server, .. } =
InterfaceConfig::from_interface(&opts.config_dir, interface)?;
let api = Api::new(&server);

log::info!("Fetching CIDRs");
let cidrs: Vec<Cidr> = api.http("GET", "/admin/cidrs")?;

if let Some((cidr_request, old_name)) = prompts::rename_cidr(&cidrs, &sub_opts)? {
log::info!("Renaming CIDR...");

let id = cidrs
.iter()
.filter(|c| c.name == old_name)
.map(|c| c.id)
.next()
.ok_or_else(|| anyhow!("CIDR not found."))?;

api.http_form("PUT", &format!("/admin/cidrs/{id}"), cidr_request)?;
log::info!("CIDR renamed.");
} else {
log::info!("Exited without renaming CIDR.");
}

Ok(())
}

fn delete_cidr(
interface: &InterfaceName,
opts: &Opts,
Expand Down Expand Up @@ -1251,6 +1295,10 @@ fn run(opts: &Opts) -> Result<(), Error> {
interface,
sub_opts,
} => add_cidr(&interface, opts, sub_opts)?,
Command::RenameCidr {
interface,
sub_opts,
} => rename_cidr(&interface, opts, sub_opts)?,
Command::DeleteCidr {
interface,
sub_opts,
Expand Down
17 changes: 17 additions & 0 deletions server/src/api/admin/cidr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ pub async fn routes(
let form = form_body(req).await?;
handlers::create(form, session).await
},
(&Method::PUT, Some(id)) => {
let id: i64 = id.parse().map_err(|_| ServerError::NotFound)?;
let form = form_body(req).await?;
handlers::update(id, form, session).await
},
(&Method::DELETE, Some(id)) => {
let id: i64 = id.parse().map_err(|_| ServerError::NotFound)?;
handlers::delete(id, session).await
Expand All @@ -43,6 +48,18 @@ mod handlers {
json_status_response(cidr, StatusCode::CREATED)
}

pub async fn update(
id: i64,
form: CidrContents,
session: Session,
) -> Result<Response<Body>, ServerError> {
let conn = session.context.db.lock();
let cidr = DatabaseCidr::get(&conn, id)?;
DatabaseCidr::from(cidr).update(&conn, form)?;

status_response(StatusCode::NO_CONTENT)
}

pub async fn list(session: Session) -> Result<Response<Body>, ServerError> {
let conn = session.context.db.lock();
let cidrs = DatabaseCidr::list(&conn)?;
Expand Down
24 changes: 23 additions & 1 deletion server/src/db/cidr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::ServerError;
use ipnet::IpNet;
use rusqlite::{params, Connection};
use shared::{Cidr, CidrContents};
use std::ops::Deref;
use std::ops::{Deref, DerefMut};

pub static CREATE_TABLE_SQL: &str = "CREATE TABLE cidrs (
id INTEGER PRIMARY KEY,
Expand Down Expand Up @@ -35,6 +35,12 @@ impl Deref for DatabaseCidr {
}
}

impl DerefMut for DatabaseCidr {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}

impl DatabaseCidr {
pub fn create(conn: &Connection, contents: CidrContents) -> Result<Cidr, ServerError> {
let CidrContents { name, cidr, parent } = &contents;
Expand Down Expand Up @@ -106,6 +112,22 @@ impl DatabaseCidr {
Ok(Cidr { id, contents })
}

/// Update self with new contents, validating them and updating the backend in the process.
pub fn update(&mut self, conn: &Connection, contents: CidrContents) -> Result<(), ServerError> {
let new_contents = CidrContents {
name: contents.name,
..self.contents.clone()
};

conn.execute(
"UPDATE cidrs SET name = ?2 WHERE id = ?1",
params![self.id, &*new_contents.name,],
)?;

self.contents = new_contents;
Ok(())
}

pub fn delete(conn: &Connection, id: i64) -> Result<(), ServerError> {
conn.execute("DELETE FROM cidrs WHERE id = ?1", params![id])?;
Ok(())
Expand Down
33 changes: 32 additions & 1 deletion server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use rusqlite::Connection;
use serde::{Deserialize, Serialize};
use shared::{
get_local_addrs, AddCidrOpts, AddPeerOpts, DeleteCidrOpts, EnableDisablePeerOpts, Endpoint,
IoErrorContext, NetworkOpts, PeerContents, RenamePeerOpts, INNERNET_PUBKEY_HEADER,
IoErrorContext, NetworkOpts, PeerContents, RenameCidrOpts, RenamePeerOpts,
INNERNET_PUBKEY_HEADER,
};
use std::{
collections::{HashMap, VecDeque},
Expand Down Expand Up @@ -126,6 +127,14 @@ enum Command {
args: AddCidrOpts,
},

/// Rename an existing CIDR.
RenameCidr {
interface: Interface,

#[clap(flatten)]
args: RenameCidrOpts,
},

/// Delete a CIDR.
DeleteCidr {
interface: Interface,
Expand Down Expand Up @@ -288,6 +297,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
enable_or_disable_peer(&interface, &conf, true, opts.network, args)?
},
Command::AddCidr { interface, args } => add_cidr(&interface, &conf, args)?,
Command::RenameCidr { interface, args } => rename_cidr(&interface, &conf, args)?,
Command::DeleteCidr { interface, args } => delete_cidr(&interface, &conf, args)?,
Command::Completions { shell } => {
use clap::CommandFactory;
Expand Down Expand Up @@ -460,6 +470,27 @@ fn add_cidr(
Ok(())
}

fn rename_cidr(
interface: &InterfaceName,
conf: &ServerConfig,
opts: RenameCidrOpts,
) -> Result<(), Error> {
let conn = open_database_connection(interface, conf)?;
let cidrs = DatabaseCidr::list(&conn)?;

if let Some((cidr_request, old_name)) = shared::prompts::rename_cidr(&cidrs, &opts)? {
let db_cidr = DatabaseCidr::list(&conn)?
.into_iter()
.find(|c| c.name == old_name)
.ok_or_else(|| anyhow!("CIDR not found."))?;
db::DatabaseCidr::from(db_cidr).update(&conn, cidr_request)?;
} else {
println!("exited without creating CIDR.");
}

Ok(())
}

fn delete_cidr(
interface: &InterfaceName,
conf: &ServerConfig,
Expand Down
48 changes: 46 additions & 2 deletions shared/src/prompts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use crate::{
interface_config::{InterfaceConfig, InterfaceInfo, ServerInfo},
AddCidrOpts, AddDeleteAssociationOpts, AddPeerOpts, Association, Cidr, CidrContents, CidrTree,
DeleteCidrOpts, EnableDisablePeerOpts, Endpoint, Error, Hostname, IpNetExt, ListenPortOpts,
OverrideEndpointOpts, Peer, PeerContents, RenamePeerOpts, PERSISTENT_KEEPALIVE_INTERVAL_SECS,
OverrideEndpointOpts, Peer, PeerContents, RenameCidrOpts, RenamePeerOpts,
PERSISTENT_KEEPALIVE_INTERVAL_SECS,
};
use anyhow::anyhow;
use colored::*;
Expand Down Expand Up @@ -111,6 +112,49 @@ pub fn add_cidr(cidrs: &[Cidr], request: &AddCidrOpts) -> Result<Option<CidrCont
)
}

/// Bring up a prompt to rename an existing CIDR. Returns the CIDR request.
pub fn rename_cidr(
cidrs: &[Cidr],
args: &RenameCidrOpts,
) -> Result<Option<(CidrContents, String)>, Error> {
let old_cidr = if let Some(ref name) = args.name {
cidrs
.iter()
.find(|c| &c.name == name)
.ok_or_else(|| anyhow!("CIDR '{}' does not exist", name))?
.clone()
} else {
let (cidr_index, _) = select(
"CIDR to rename",
&cidrs.iter().map(|ep| ep.name.clone()).collect::<Vec<_>>(),
)?;
cidrs[cidr_index].clone()
};
let old_name = old_cidr.name.clone();
let new_name = if let Some(ref name) = args.new_name {
name.clone()
} else {
input("New Name", Prefill::None)?
};

let mut new_cidr = old_cidr;
new_cidr.contents.name = new_name.clone();

Ok(
if args.yes
|| confirm(&format!(
"Rename CIDR {} to {}?",
old_name.yellow(),
new_name.yellow()
))?
{
Some((new_cidr.contents, old_name))
} else {
None
},
)
}

/// 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
Expand Down Expand Up @@ -342,7 +386,7 @@ pub fn add_peer(
)
}

/// Bring up a prompt to create a new peer. Returns the peer request.
/// Bring up a prompt to rename an existing peer. Returns the peer request.
pub fn rename_peer(
peers: &[Peer],
args: &RenamePeerOpts,
Expand Down
15 changes: 15 additions & 0 deletions shared/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,21 @@ pub struct AddCidrOpts {
pub yes: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Args)]
pub struct RenameCidrOpts {
/// Name of CIDR to rename
#[clap(long)]
pub name: Option<String>,

/// The new name of the CIDR
#[clap(long)]
pub new_name: Option<String>,

/// Bypass confirmation
#[clap(long)]
pub yes: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Args)]
pub struct DeleteCidrOpts {
/// The CIDR name (eg. 'engineers')
Expand Down

0 comments on commit c1ea1bc

Please sign in to comment.