change connector error management
This commit is contained in:
parent
06caf83e05
commit
e44e6eea63
5 changed files with 96 additions and 48 deletions
|
@ -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)
|
||||||
|
|
|
@ -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>;
|
||||||
}
|
}
|
|
@ -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(())
|
||||||
|
|
|
@ -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;
|
|
@ -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())
|
||||||
|
|
Loading…
Reference in a new issue