change connector error management

main
Hannaeko 2022-04-22 20:43:24 +02:00
parent 06caf83e05
commit e44e6eea63
5 changed files with 96 additions and 48 deletions

View File

@ -50,7 +50,7 @@ impl<'r> FromRequest<'r> for DnsClient {
let config = try_outcome!(request.guard::<&State<Config>>().await); let config = try_outcome!(request.guard::<&State<Config>>().await);
match DnsClient::new(config.dns.server).await { match DnsClient::new(config.dns.server).await {
Err(e) => { Err(e) => {
println!("Failed to connect to DNS server {:#?}", e); println!("Failed to connect to DNS server: {}", e);
Outcome::Failure((Status::InternalServerError, ())) Outcome::Failure((Status::InternalServerError, ()))
}, },
Ok(c) => Outcome::Success(c) Ok(c) => Outcome::Success(c)

View File

@ -6,12 +6,10 @@ use crate::dns;
// E.g.: DNS update + axfr, zone file read + write // E.g.: DNS update + axfr, zone file read + write
#[async_trait] #[async_trait]
pub trait RecordConnector { pub trait RecordConnector {
type Error; async fn get_records(&mut self, zone: dns::Name, class: dns::DNSClass) -> Result<Vec<dns::Record>, Box<dyn ConnectorError>>;
async fn add_records(&mut self, zone: dns::Name, class: dns::DNSClass, new_records: Vec<dns::Record>) -> Result<(), Box<dyn ConnectorError>>;
async fn get_records(&mut self, zone: dns::Name, class: dns::DNSClass) -> Result<Vec<dns::Record>, Self::Error>; async fn update_records(&mut self, zone: dns::Name, class: dns::DNSClass, old_records: Vec<dns::Record>, new_records: Vec<dns::Record>) -> Result<(), Box<dyn ConnectorError>>;
async fn add_records(&mut self, zone: dns::Name, class: dns::DNSClass, new_records: Vec<dns::Record>) -> Result<(), Self::Error>; async fn delete_records(&mut self, zone: dns::Name, class: dns::DNSClass, records: Vec<dns::Record>) -> Result<(), Box<dyn ConnectorError>>;
async fn update_records(&mut self, zone: dns::Name, class: dns::DNSClass, old_records: Vec<dns::Record>, new_records: Vec<dns::Record>) -> Result<(), Self::Error>;
async fn delete_records(&mut self, zone: dns::Name, class: dns::DNSClass, records: Vec<dns::Record>) -> Result<(), Self::Error>;
// delete_records // delete_records
} }
@ -19,9 +17,13 @@ pub trait RecordConnector {
// E.g.: Manage catalog zone, dynamically generate knot / bind / nsd config... // E.g.: Manage catalog zone, dynamically generate knot / bind / nsd config...
#[async_trait] #[async_trait]
pub trait ZoneConnector { pub trait ZoneConnector {
type Error;
// get_zones // get_zones
// add_zone // add_zone
// delete_zone // delete_zone
async fn zone_exists(&mut self, zone: dns::Name, class: dns::DNSClass) -> Result<(), Self::Error>; async fn zone_exists(&mut self, zone: dns::Name, class: dns::DNSClass) -> Result<(), Box<dyn ConnectorError>>;
}
pub trait ConnectorError: std::fmt::Debug + std::fmt::Display {
fn is_proto_error(&self) -> bool;
fn zone_name(&self) -> Option<dns::Name>;
} }

View File

@ -6,7 +6,7 @@ use trust_dns_client::error::ClientError;
use super::{Name, Record, RData}; use super::{Name, Record, RData};
use super::client::{ClientResponse, DnsClient}; use super::client::{ClientResponse, DnsClient};
use super::connector::{RecordConnector, ZoneConnector}; use super::connector::{RecordConnector, ZoneConnector, ConnectorError};
const MAX_PAYLOAD_LEN: u16 = 1232; const MAX_PAYLOAD_LEN: u16 = 1232;
@ -33,23 +33,55 @@ impl DnsConnectorClient {
} }
} }
impl ConnectorError for DnsConnectorError {
fn zone_name(&self) -> Option<Name> {
if let DnsConnectorError::ResponceNotOk { code: _code, zone } = self {
Some(zone.clone())
} else {
None
}
}
fn is_proto_error(&self) -> bool {
return matches!(self, DnsConnectorError::ClientError(_));
}
}
impl std::fmt::Display for DnsConnectorError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DnsConnectorError::ClientError(e) => {
write!(f, "DNS client error: {}", e)
},
DnsConnectorError::ResponceNotOk { code, zone } => {
write!(f, "Query for zone {} failed with code {}", zone, code)
}
}
}
}
#[async_trait] #[async_trait]
impl RecordConnector for DnsConnectorClient { impl RecordConnector for DnsConnectorClient {
type Error = DnsConnectorError; //type Error = DnsConnectorError;
async fn get_records(&mut self, zone: Name, class: DNSClass) -> Result<Vec<Record>, Self::Error> async fn get_records(&mut self, zone: Name, class: DNSClass) -> Result<Vec<Record>, Box<dyn ConnectorError>>
{ {
let response = { let response = {
let query = self.client.query(zone.clone(), class, RecordType::AXFR); let query = self.client.query(zone.clone(), class, RecordType::AXFR);
query.await.map_err(|e| DnsConnectorError::ClientError(e))? match query.await.map_err(|e| Box::new(DnsConnectorError::ClientError(e))) {
Err(e) => return Err(e),
Ok(v) => v,
}
}; };
if response.response_code() != ResponseCode::NoError { if response.response_code() != ResponseCode::NoError {
return Err(DnsConnectorError::ResponceNotOk { return Err(Box::new(DnsConnectorError::ResponceNotOk {
code: response.response_code(), code: response.response_code(),
zone: zone, zone: zone,
}); }));
} }
let answers = response.answers(); let answers = response.answers();
@ -62,7 +94,7 @@ impl RecordConnector for DnsConnectorClient {
Ok(records) Ok(records)
} }
async fn add_records(&mut self, zone: Name, class: DNSClass, new_records: Vec<Record>) -> Result<(), Self::Error> async fn add_records(&mut self, zone: Name, class: DNSClass, new_records: Vec<Record>) -> Result<(), Box<dyn ConnectorError>>
{ {
// Taken from trust_dns_client::op::update_message::append // Taken from trust_dns_client::op::update_message::append
// The original function can not be used as is because it takes a RecordSet and not a Record list // The original function can not be used as is because it takes a RecordSet and not a Record list
@ -89,19 +121,22 @@ impl RecordConnector for DnsConnectorClient {
edns.set_version(0); edns.set_version(0);
} }
let response = ClientResponse(self.client.send(message)).await.map_err(|e| DnsConnectorError::ClientError(e))?; let response = match ClientResponse(self.client.send(message)).await.map_err(|e| Box::new(DnsConnectorError::ClientError(e))) {
Err(e) => return Err(e),
Ok(v) => v,
};
if response.response_code() != ResponseCode::NoError { if response.response_code() != ResponseCode::NoError {
return Err(DnsConnectorError::ResponceNotOk { return Err(Box::new(DnsConnectorError::ResponceNotOk {
code: response.response_code(), code: response.response_code(),
zone: zone, zone: zone,
}); }));
} }
Ok(()) Ok(())
} }
async fn update_records(&mut self, zone: Name, class: DNSClass, old_records: Vec<Record>, new_records: Vec<Record>) -> Result<(), Self::Error> async fn update_records(&mut self, zone: Name, class: DNSClass, old_records: Vec<Record>, new_records: Vec<Record>) -> Result<(), Box<dyn ConnectorError>>
{ {
// Taken from trust_dns_client::op::update_message::compare_and_swap // Taken from trust_dns_client::op::update_message::compare_and_swap
// The original function can not be used as is because it takes a RecordSet and not a Record list // The original function can not be used as is because it takes a RecordSet and not a Record list
@ -150,19 +185,22 @@ impl RecordConnector for DnsConnectorClient {
edns.set_version(0); edns.set_version(0);
} }
let response = ClientResponse(self.client.send(message)).await.map_err(|e| DnsConnectorError::ClientError(e))?; let response = match ClientResponse(self.client.send(message)).await.map_err(|e| Box::new(DnsConnectorError::ClientError(e))) {
Err(e) => return Err(e),
Ok(v) => v,
};
if response.response_code() != ResponseCode::NoError { if response.response_code() != ResponseCode::NoError {
return Err(DnsConnectorError::ResponceNotOk { return Err(Box::new(DnsConnectorError::ResponceNotOk {
code: response.response_code(), code: response.response_code(),
zone: zone, zone: zone,
}); }));
} }
Ok(()) Ok(())
} }
async fn delete_records(&mut self, zone: Name, class: DNSClass, records: Vec<Record>) -> Result<(), Self::Error> async fn delete_records(&mut self, zone: Name, class: DNSClass, records: Vec<Record>) -> Result<(), Box<dyn ConnectorError>>
{ {
// for updates, the query section is used for the zone // for updates, the query section is used for the zone
let mut zone_query = Query::new(); let mut zone_query = Query::new();
@ -197,13 +235,16 @@ impl RecordConnector for DnsConnectorClient {
edns.set_version(0); edns.set_version(0);
} }
let response = ClientResponse(self.client.send(message)).await.map_err(|e| DnsConnectorError::ClientError(e))?; let response = match ClientResponse(self.client.send(message)).await.map_err(|e| Box::new(DnsConnectorError::ClientError(e))) {
Err(e) => return Err(e),
Ok(v) => v,
};
if response.response_code() != ResponseCode::NoError { if response.response_code() != ResponseCode::NoError {
return Err(DnsConnectorError::ResponceNotOk { return Err(Box::new(DnsConnectorError::ResponceNotOk {
code: response.response_code(), code: response.response_code(),
zone: zone, zone: zone,
}); }));
} }
Ok(()) Ok(())
@ -214,20 +255,21 @@ impl RecordConnector for DnsConnectorClient {
#[async_trait] #[async_trait]
impl ZoneConnector for DnsConnectorClient { impl ZoneConnector for DnsConnectorClient {
type Error = DnsConnectorError; async fn zone_exists(&mut self, zone: Name, class: DNSClass) -> Result<(), Box<dyn ConnectorError>>
async fn zone_exists(&mut self, zone: Name, class: DNSClass) -> Result<(), Self::Error>
{ {
let response = { let response = {
let query = self.client.query(zone.clone(), class, RecordType::SOA); let query = self.client.query(zone.clone(), class, RecordType::SOA);
query.await.map_err(|e| DnsConnectorError::ClientError(e))? match query.await.map_err(|e| Box::new(DnsConnectorError::ClientError(e))) {
Err(e) => return Err(e),
Ok(v) => v,
}
}; };
if response.response_code() != ResponseCode::NoError { if response.response_code() != ResponseCode::NoError {
return Err(DnsConnectorError::ResponceNotOk { return Err(Box::new(DnsConnectorError::ResponceNotOk {
code: response.response_code(), code: response.response_code(),
zone: zone, zone: zone,
}); }));
} }
Ok(()) Ok(())

View File

@ -12,6 +12,6 @@ pub use trust_dns_client::rr::{
pub use trust_dns_proto::rr::Name; pub use trust_dns_proto::rr::Name;
// Reexport module types // Reexport module types
pub use connector::{RecordConnector, ZoneConnector}; pub use connector::{RecordConnector, ZoneConnector, ConnectorError};
pub use dns_connector::{DnsConnectorClient, DnsConnectorError}; pub use dns_connector::{DnsConnectorClient, DnsConnectorError};
pub use client::DnsClient; pub use client::DnsClient;

View File

@ -7,7 +7,7 @@ use rocket::serde::json::Json;
use serde_json::Value; use serde_json::Value;
use djangohashers::{HasherError}; use djangohashers::{HasherError};
use diesel::result::Error as DieselError; use diesel::result::Error as DieselError;
use crate::dns::DnsConnectorError; use crate::dns::ConnectorError;
use crate::models; use crate::models;
#[derive(Debug)] #[derive(Debug)]
@ -100,20 +100,23 @@ impl From<UserError> for ErrorResponse {
} }
} }
impl From<DnsConnectorError> for ErrorResponse { impl From<Box<dyn ConnectorError>> for ErrorResponse {
fn from(e: DnsConnectorError) -> Self { fn from(e: Box<dyn ConnectorError>) -> Self {
match e { println!("{}", e);
DnsConnectorError::ResponceNotOk { code, zone } => { if e.is_proto_error() {
println!("Query for zone {} failed with code {}", zone, code); return make_500(e);
} else {
ErrorResponse::new( let error = ErrorResponse::new(
Status::NotFound, Status::NotFound,
"Zone could not be found".into() "Zone could not be found".into()
).with_details(json!({ );
if let Some(zone) = e.zone_name() {
return error.with_details(json!({
"zone_name": zone.to_utf8() "zone_name": zone.to_utf8()
})) }));
}, } else {
DnsConnectorError::ClientError(e) => make_500(e) return error;
}
} }
} }
} }
@ -163,6 +166,7 @@ impl From<ErrorResponse> for (Status, ErrorResponse) {
} }
} }
// TODO: change for Display trait
pub fn make_500<E: std::fmt::Debug>(e: E) -> ErrorResponse { pub fn make_500<E: std::fmt::Debug>(e: E) -> ErrorResponse {
println!("Making 500 for Error: {:?}", e); println!("Making 500 for Error: {:?}", e);
ErrorResponse::new(Status::InternalServerError, "An unexpected error occured.".into()) ErrorResponse::new(Status::InternalServerError, "An unexpected error occured.".into())