use std::collections::{HashMap, HashSet};
use brane_cfg::info::Info as _;
use brane_cfg::infra::{InfraFile, InfraLocation};
use brane_cfg::node::NodeConfig;
use brane_prx::spec::NewPathRequestTlsOptions;
use log::{debug, error};
use specifications::address::Address;
use specifications::package::Capability;
use warp::hyper::header::HeaderValue;
use warp::hyper::{Body, Response};
use warp::{Rejection, Reply};
pub use crate::errors::InfraError as Error;
use crate::spec::Context;
pub async fn registries(context: Context) -> Result<impl Reply, Rejection> {
debug!("Handling GET on `/infra/registries` (i.e., list all registry endpoints)...");
let node_config: NodeConfig = match NodeConfig::from_path(&context.node_config_path) {
Ok(config) => config,
Err(err) => {
error!("Failed to load NodeConfig file: {}", err);
return Err(warp::reject::custom(Error::SecretError));
},
};
if !node_config.node.is_central() {
error!("Provided node config file '{}' is not for a central node", context.node_config_path.display());
return Err(warp::reject::custom(Error::SecretError));
}
let infra: InfraFile = match InfraFile::from_path(&node_config.node.central().paths.infra) {
Ok(infra) => infra,
Err(err) => {
error!("{}", Error::InfrastructureOpenError { path: node_config.node.central().paths.infra.clone(), err });
return Err(warp::reject::custom(Error::SecretError));
},
};
let mut locations: HashMap<String, Address> = HashMap::new();
for (name, loc) in infra.into_iter() {
locations.insert(name, loc.registry);
}
let body: String = match serde_json::to_string(&locations) {
Ok(body) => body,
Err(err) => {
error!("{}", Error::SerializeError { what: "list of all registry endpoints", err });
return Err(warp::reject::custom(Error::SecretError));
},
};
let body_len: usize = body.len();
let mut response = Response::new(Body::from(body));
response.headers_mut().insert("Content-Length", HeaderValue::from(body_len));
Ok(response)
}
pub async fn get_registry(loc: String, context: Context) -> Result<impl Reply, Rejection> {
debug!("Handling GET on `/infra/registries/{}` (i.e., get location registry address)...", loc);
let node_config: NodeConfig = match NodeConfig::from_path(&context.node_config_path) {
Ok(config) => config,
Err(err) => {
error!("Failed to load NodeConfig file: {}", err);
return Err(warp::reject::custom(Error::SecretError));
},
};
if !node_config.node.is_central() {
error!("Provided node config file '{}' is not for a central node", context.node_config_path.display());
return Err(warp::reject::custom(Error::SecretError));
}
let infra: InfraFile = match InfraFile::from_path(&node_config.node.central().paths.infra) {
Ok(infra) => infra,
Err(err) => {
error!("{}", Error::InfrastructureOpenError { path: node_config.node.central().paths.infra.clone(), err });
return Err(warp::reject::custom(Error::SecretError));
},
};
let info: &InfraLocation = match infra.get(&loc) {
Some(info) => info,
None => {
return Err(warp::reject::not_found());
},
};
let body: String = info.registry.serialize().to_string();
let body_len: usize = body.len();
let mut response = Response::new(Body::from(body));
response.headers_mut().insert("Content-Length", HeaderValue::from(body_len));
Ok(response)
}
pub async fn get_capabilities(loc: String, context: Context) -> Result<impl Reply, Rejection> {
debug!("Handling GET on `/infra/capabilities/{}` (i.e., get location capabilities)...", loc);
let node_config: NodeConfig = match NodeConfig::from_path(&context.node_config_path) {
Ok(config) => config,
Err(err) => {
error!("Failed to load NodeConfig file: {}", err);
return Err(warp::reject::custom(Error::SecretError));
},
};
if !node_config.node.is_central() {
error!("Provided node config file '{}' is not for a central node", context.node_config_path.display());
return Err(warp::reject::custom(Error::SecretError));
}
let infra: InfraFile = match InfraFile::from_path(&node_config.node.central().paths.infra) {
Ok(infra) => infra,
Err(err) => {
error!("{}", Error::InfrastructureOpenError { path: node_config.node.central().paths.infra.clone(), err });
return Err(warp::reject::custom(Error::SecretError));
},
};
let info: &InfraLocation = match infra.get(&loc) {
Some(info) => info,
None => {
return Err(warp::reject::not_found());
},
};
let reg_addr: String = format!("{}/infra/capabilities", info.registry);
let res: reqwest::Response = match context.proxy.get(®_addr, Some(NewPathRequestTlsOptions { use_client_auth: false, location: loc })).await {
Ok(res) => match res {
Ok(res) => res,
Err(err) => {
error!("{}", Error::RequestError { address: reg_addr, err });
return Err(warp::reject::custom(Error::SecretError));
},
},
Err(err) => {
error!("{}", Error::ProxyError { err });
return Err(warp::reject::custom(Error::SecretError));
},
};
if !res.status().is_success() {
error!("{}", Error::RequestFailure { address: reg_addr, code: res.status(), message: res.text().await.ok() });
return Err(warp::reject::custom(Error::SecretError));
}
let capabilities: String = match res.text().await {
Ok(caps) => caps,
Err(err) => {
error!("{}", Error::ResponseBodyError { address: reg_addr, err });
return Err(warp::reject::custom(Error::SecretError));
},
};
let capabilities: HashSet<Capability> = match serde_json::from_str(&capabilities) {
Ok(caps) => caps,
Err(err) => {
error!("{}", Error::ResponseParseError { address: reg_addr, raw: capabilities, err });
return Err(warp::reject::custom(Error::SecretError));
},
};
let body: String = match serde_json::to_string(&capabilities) {
Ok(body) => body,
Err(err) => {
error!("{}", Error::CapabilitiesSerializeError { err });
return Err(warp::reject::custom(Error::SecretError));
},
};
let body_len: usize = body.len();
let mut response = Response::new(Body::from(body));
response.headers_mut().insert("Content-Length", HeaderValue::from(body_len));
Ok(response)
}