From 27caa5edac8969207a88214234c1f9b4803f9d14 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Thu, 24 Mar 2022 23:35:11 +0100 Subject: [PATCH] Refactor haproxy rt api --- src/haproxy.rs | 112 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 76 insertions(+), 36 deletions(-) diff --git a/src/haproxy.rs b/src/haproxy.rs index 2a3413e..d3e54ce 100644 --- a/src/haproxy.rs +++ b/src/haproxy.rs @@ -1,6 +1,9 @@ -use std::{path::Path, time::Duration}; use log::debug; -use tokio::{io::{AsyncReadExt, AsyncWriteExt}, time::timeout}; +use std::{path::Path, time::Duration}; +use tokio::{ + io::{AsyncReadExt, AsyncWriteExt}, + time::timeout, +}; #[derive(thiserror::Error, Debug)] pub enum HaProxyErr { @@ -28,49 +31,86 @@ impl HaProxyApi { let cert_path = cert_path.as_ref(); let cert_name = cert_path.file_name().unwrap().to_string_lossy().to_string(); let cert_content = tokio::fs::read_to_string(cert_path).await.unwrap(); - let haproxy_ssl_dir = &self.cert_dir; - - debug!("Trying to update haproxy cert in memory: {haproxy_ssl_dir}/{cert_name} <- {}", cert_path.display()); + let haproxy_cert_path = format!("{}/{cert_name}", &self.cert_dir); + + debug!( + "Trying to update haproxy cert in memory: {}/{cert_name} <- {}", + &self.cert_dir, + cert_path.display() + ); // Check for active transactions and abort them let resp = self.send(&format!("show ssl cert")).await?; - let transactions = resp.lines().filter(|line| line.starts_with("*")).map(|line| &line[1..]).collect::>(); + let transactions = resp + .lines() + .filter(|line| line.starts_with("*")) + .map(|line| &line[1..]) + .collect::>(); for transaction in transactions { - self.send(&format!("abort ssl cert {transaction}")).await?; + self.abort_cert(transaction).await?; } - // Try to create a new certificate in memory. This will do nothing when existing - let resp = self.send(&format!("new ssl cert {haproxy_ssl_dir}/{cert_name}")).await?; - - if !resp.contains("already exists") && !resp.contains("New empty certificate store ") { - return Err(HaProxyErr::UpdateFailed); - } - - // Set the cert data in memory to the new cert. This beginns a transaction - let resp = self.send(&format!("set ssl cert {haproxy_ssl_dir}/{cert_name} <<\n{cert_content}")).await?; - - if !resp.contains("Transaction created") { - return Err(HaProxyErr::UpdateFailed); - } - - // Commit the cert data transaction - let resp = self.send(&format!("commit ssl cert {haproxy_ssl_dir}/{cert_name}")).await?; - - if !resp.contains("Success") { - self.send(&format!("abort ssl cert {haproxy_ssl_dir}/{cert_name}")).await?; - return Err(HaProxyErr::UpdateFailed); - } - - // Try to add the certificate to the active list. This will do nothing if it is contained already - let resp = self.send(&format!("add ssl crt-list {haproxy_ssl_dir} <<\n{haproxy_ssl_dir}/{cert_name}\n")).await?; - - if !resp.contains("already exists") && !resp.contains("Inserting certificate") { - return Err(HaProxyErr::UpdateFailed); - } + self.new_cert(&haproxy_cert_path).await?; + self.set_cert(&haproxy_cert_path, &cert_content).await?; + self.commit_ssl_cert(&haproxy_cert_path).await?; + self.add_ssl_crt_list(&self.cert_dir, &haproxy_cert_path) + .await?; Ok(()) } + /// See: https://www.haproxy.com/documentation/hapee/latest/api/runtime-api/abort-ssl-cert/ + pub async fn abort_cert(&self, cert_path: &str) -> Result<(), HaProxyErr> { + self.send(&format!("abort ssl cert {cert_path}")).await?; + Ok(()) + } + + /// See: https://www.haproxy.com/documentation/hapee/latest/api/runtime-api/new-ssl-cert/ + pub async fn new_cert(&self, cert_path: &str) -> Result<(), HaProxyErr> { + let resp = self.send(&format!("new ssl cert {cert_path}")).await?; + if !resp.contains("already exists") && !resp.contains("New empty certificate store ") { + return Err(HaProxyErr::UpdateFailed); + } + Ok(()) + } + + /// See: https://www.haproxy.com/documentation/hapee/latest/api/runtime-api/set-ssl-cert/ + pub async fn set_cert(&self, cert_path: &str, cert_content: &str) -> Result<(), HaProxyErr> { + let resp = self + .send(&format!("set ssl cert {cert_path} <<\n{cert_content}")) + .await?; + if !resp.contains("Transaction created") { + return Err(HaProxyErr::UpdateFailed); + } + Ok(()) + } + + /// See: https://www.haproxy.com/documentation/hapee/latest/api/runtime-api/commit-ssl-cert/ + pub async fn commit_ssl_cert(&self, cert_path: &str) -> Result<(), HaProxyErr> { + let resp = self.send(&format!("commit ssl cert {cert_path}")).await?; + if !resp.contains("Success") { + self.send(&format!("abort ssl cert {cert_path}")).await?; + return Err(HaProxyErr::UpdateFailed); + } + Ok(()) + } + + /// See: https://www.haproxy.com/documentation/hapee/latest/api/runtime-api/add-ssl-crt-list/ + pub async fn add_ssl_crt_list( + &self, + list_path: &str, + cert_path: &str, + ) -> Result<(), HaProxyErr> { + let resp = self + .send(&format!("add ssl crt-list {list_path} <<\n{cert_path}\n")) + .await?; + if !resp.contains("already exists") && !resp.contains("Inserting certificate") { + return Err(HaProxyErr::UpdateFailed); + } + Ok(()) + } + + /// Send a string command to the haproxy runtime api and return the response string pub async fn send(&self, cmd: &str) -> Result { debug!("[Haproxy] send> '{cmd}'"); @@ -81,7 +121,7 @@ impl HaProxyApi { con.write_all(cmd.as_bytes()) .await .map_err(|_| HaProxyErr::Connection)?; - + con.write_all(&[b'\n']) .await .map_err(|_| HaProxyErr::Connection)?;