client: don't leave interface behind on failed install

pull/48/head
Jake McGinty 2021-04-10 17:38:59 +09:00
parent dcf553c8fd
commit c15db6f833
2 changed files with 61 additions and 40 deletions

View File

@ -183,7 +183,7 @@ fn update_hosts_file(
fn install(invite: &Path, hosts_file: Option<PathBuf>) -> Result<(), Error> { fn install(invite: &Path, hosts_file: Option<PathBuf>) -> Result<(), Error> {
shared::ensure_dirs_exist(&[*CLIENT_CONFIG_PATH])?; shared::ensure_dirs_exist(&[*CLIENT_CONFIG_PATH])?;
let mut config = InterfaceConfig::from_file(invite)?; let config = InterfaceConfig::from_file(invite)?;
let iface = Input::with_theme(&*prompts::THEME) let iface = Input::with_theme(&*prompts::THEME)
.with_prompt("Interface name") .with_prompt("Interface name")
@ -196,7 +196,59 @@ fn install(invite: &Path, hosts_file: Option<PathBuf>) -> Result<(), Error> {
} }
let iface = iface.parse()?; let iface = iface.parse()?;
redeem_invite(&iface, config, target_conf).map_err(|e| {
println!("{} bringing down the interface.", "[*]".dimmed());
if let Err(e) = wg::down(&iface) {
println!("{} failed to bring down interface: {}.", "[*]".yellow(), e.to_string());
};
println!("{} Failed to redeem invite. Now's a good time to make sure the server is started and accessible!", "[!]".red());
e
})?;
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()?
{
std::fs::remove_file(invite).with_path(invite)?;
}
printdoc!(
"
{star} Done!
{interface} has been {installed}.
It's recommended to now keep the interface automatically refreshing via systemd:
{systemctl_enable}{interface}
By default, innernet will write to your /etc/hosts file for peer name
resolution. To disable this behavior, use the --no-write-hosts or --write-hosts [PATH]
options.
See the manpage or innernet GitHub repo for more detailed instruction on managing your
interface and network. Have fun!
",
star = "[*]".dimmed(),
interface = iface.to_string().yellow(),
installed = "installed".green(),
systemctl_enable = "systemctl enable --now innernet@".yellow(),
);
Ok(())
}
fn redeem_invite(
iface: &InterfaceName,
mut config: InterfaceConfig,
target_conf: PathBuf,
) -> Result<(), Error> {
println!("{} bringing up the interface.", "[*]".dimmed()); println!("{} bringing up the interface.", "[*]".dimmed());
wg::up( wg::up(
&iface, &iface,
@ -243,43 +295,6 @@ fn install(invite: &Path, hosts_file: Option<PathBuf>) -> Result<(), Error> {
.set_private_key(keypair.private) .set_private_key(keypair.private)
.apply(&iface)?; .apply(&iface)?;
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()?
{
std::fs::remove_file(invite).with_path(invite)?;
}
printdoc!(
"
{star} Done!
{interface} has been {installed}.
It's recommended to now keep the interface automatically refreshing via systemd:
{systemctl_enable}{interface}
By default, innernet will write to your /etc/hosts file for peer name
resolution. To disable this behavior, use the --no-write-hosts or --write-hosts [PATH]
options.
See the manpage or innernet GitHub repo for more detailed instruction on managing your
interface and network. Have fun!
",
star = "[*]".dimmed(),
interface = iface.to_string().yellow(),
installed = "installed".green(),
systemctl_enable = "systemctl enable --now innernet@".yellow(),
);
Ok(()) Ok(())
} }

View File

@ -2,6 +2,7 @@ use crate::{ClientError, Error};
use colored::*; use colored::*;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use shared::{interface_config::ServerInfo, INNERNET_PUBKEY_HEADER}; use shared::{interface_config::ServerInfo, INNERNET_PUBKEY_HEADER};
use ureq::{Agent, AgentBuilder};
use std::time::Duration; use std::time::Duration;
pub fn human_duration(duration: Duration) -> String { pub fn human_duration(duration: Duration) -> String {
@ -48,12 +49,17 @@ pub fn human_size(bytes: u64) -> String {
} }
pub struct Api<'a> { pub struct Api<'a> {
agent: Agent,
server: &'a ServerInfo, server: &'a ServerInfo,
} }
impl<'a> Api<'a> { impl<'a> Api<'a> {
pub fn new(server: &'a ServerInfo) -> Self { pub fn new(server: &'a ServerInfo) -> Self {
Self { server } let agent = AgentBuilder::new()
.timeout(Duration::from_secs(5))
.redirects(0)
.build();
Self { agent, server }
} }
pub fn http<T: DeserializeOwned>(&self, verb: &str, endpoint: &str) -> Result<T, Error> { pub fn http<T: DeserializeOwned>(&self, verb: &str, endpoint: &str) -> Result<T, Error> {
@ -75,7 +81,7 @@ impl<'a> Api<'a> {
endpoint: &str, endpoint: &str,
form: Option<S>, form: Option<S>,
) -> Result<T, Error> { ) -> Result<T, Error> {
let request = ureq::request( let request = self.agent.request(
verb, verb,
&format!("http://{}/v1{}", self.server.internal_endpoint, endpoint), &format!("http://{}/v1{}", self.server.internal_endpoint, endpoint),
) )