use crate::app::{app_resources, apps_that_use_volume};
use crate::application_types::ApplicationValues;
use crate::dsh_api_client::DshApiClient;
use crate::error::DshApiResult;
use crate::parse::parse_volume_string;
use crate::types::{AppCatalogApp, AppCatalogAppResourcesValue, Application, Volume, VolumeStatus};
use crate::{Dependant, DependantApp, DependantApplication};
use futures::try_join;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum VolumeInjection {
#[serde(rename = "env")]
EnvVar { env_var_name: String },
#[serde(rename = "path")]
Path { directory_path: String },
#[serde(rename = "variable")]
Variable { function_name: String, parameter_name: String },
#[serde(rename = "volume")]
Volume { mount_path: String },
}
impl VolumeInjection {
pub(crate) fn env_var<T>(env_var: T) -> Self
where
T: Into<String>,
{
Self::EnvVar { env_var_name: env_var.into() }
}
pub(crate) fn volume<T>(mount_path: T) -> Self
where
T: Into<String>,
{
Self::Volume { mount_path: mount_path.into() }
}
}
impl Display for VolumeInjection {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
VolumeInjection::EnvVar { env_var_name } => write!(f, "{}", env_var_name),
VolumeInjection::Path { directory_path } => write!(f, "path:{}", directory_path),
VolumeInjection::Variable { function_name, parameter_name } => write!(f, "{{ {}('{}') }}", function_name, parameter_name),
VolumeInjection::Volume { mount_path } => write!(f, "mount:{}", mount_path),
}
}
}
impl DshApiClient {
pub async fn volume_with_dependants(&self, volume_id: &str) -> DshApiResult<(VolumeStatus, Vec<Dependant<VolumeInjection>>)> {
let (volume_status, applications, apps) = try_join!(
self.get_volume(volume_id),
self.get_application_configuration_map(),
self.get_appcatalogapp_configuration_map()
)?;
let mut dependants: Vec<Dependant<VolumeInjection>> = vec![];
for ApplicationValues { id, application, values } in volume_paths_from_applications(volume_id, &applications) {
dependants.push(Dependant::service(
id,
application.instances,
values
.iter()
.map(|mount_path| VolumeInjection::Volume { mount_path: mount_path.to_string() })
.collect_vec(),
));
}
for (app_id, _, resource_ids) in apps_that_use_volume(volume_id, &apps) {
dependants.push(Dependant::app(
app_id.to_string(),
resource_ids.iter().map(|resource_id| resource_id.to_string()).collect_vec(),
));
}
Ok((volume_status, dependants))
}
pub async fn volumes_with_dependant_applications(&self) -> DshApiResult<Vec<(String, Vec<DependantApplication<VolumeInjection>>)>> {
let (volume_ids, applications) = try_join!(self.get_volume_ids(), self.get_application_configuration_map())?;
let mut volumes = Vec::<(String, Vec<DependantApplication<VolumeInjection>>)>::new();
for volume_id in volume_ids {
let mut dependant_applications: Vec<DependantApplication<VolumeInjection>> = vec![];
for application in volume_paths_from_applications(volume_id.as_str(), &applications) {
dependant_applications.push(DependantApplication::new(
application.id.to_string(),
application.application.instances,
application.values.iter().map(|env_var| VolumeInjection::env_var(*env_var)).collect_vec(),
));
}
volumes.push((volume_id, dependant_applications));
}
Ok(volumes)
}
pub async fn volumes_with_dependant_apps(&self) -> DshApiResult<Vec<(String, Vec<DependantApp>)>> {
let (volume_ids, apps) = try_join!(self.get_volume_ids(), self.get_appcatalogapp_configuration_map())?;
let mut volumes = Vec::<(String, Vec<DependantApp>)>::new();
for volume_id in volume_ids {
let mut dependant_apps: Vec<DependantApp> = vec![];
for (app_id, _, resource_ids) in apps_that_use_volume(volume_id.as_str(), &apps) {
dependant_apps.push(DependantApp::new(
app_id.to_string(),
resource_ids.iter().map(|resource_id| resource_id.to_string()).collect_vec(),
));
}
volumes.push((volume_id, dependant_apps));
}
Ok(volumes)
}
pub async fn volumes_with_dependants(&self) -> DshApiResult<Vec<(String, Vec<Dependant<VolumeInjection>>)>> {
let (volume_ids, applications, apps) = try_join!(
self.get_volume_ids(),
self.get_application_configuration_map(),
self.get_appcatalogapp_configuration_map()
)?;
let mut volumes = Vec::<(String, Vec<Dependant<VolumeInjection>>)>::new();
for volume_id in volume_ids {
let mut dependants: Vec<Dependant<VolumeInjection>> = vec![];
for application in volume_paths_from_applications(volume_id.as_str(), &applications) {
dependants.push(Dependant::service(
application.id,
application.application.instances,
application
.values
.iter()
.map(|env_var| VolumeInjection::EnvVar { env_var_name: env_var.to_string() })
.collect_vec(),
));
}
for (app_id, _, resource_ids) in apps_that_use_volume(volume_id.as_str(), &apps) {
dependants.push(Dependant::app(
app_id.to_string(),
resource_ids.iter().map(|resource_id| resource_id.to_string()).collect_vec(),
));
}
volumes.push((volume_id, dependants));
}
Ok(volumes)
}
}
pub fn volume_paths_from_applications<'a>(volume_id: &str, applications: &'a HashMap<String, Application>) -> Vec<ApplicationValues<'a, &'a str>> {
let mut application_ids = applications.keys().collect_vec();
application_ids.sort();
let mut tuples: Vec<ApplicationValues<&str>> = vec![];
for application_id in application_ids {
let application = applications.get(application_id).unwrap();
let mut injections = Vec::<&str>::new();
for (path, application_volume) in &application.volumes {
if application_volume.name.contains(volume_id) {
injections.push(path);
}
}
if !injections.is_empty() {
tuples.push(ApplicationValues::new(application_id, application, injections));
}
}
tuples
}
pub fn volumes_from_application(application: &Application) -> Vec<(&str, &str)> {
let mut volumes = application
.volumes
.iter()
.filter_map(|(path, application_volumes)| {
parse_volume_string(application_volumes.name.as_str())
.map(|volume_id| (volume_id, path.as_str()))
.ok()
})
.collect_vec();
volumes.sort_by(|(volume_a, _), (volume_b, _)| volume_a.cmp(volume_b));
volumes
}
pub fn volumes_from_applications(applications: &HashMap<String, Application>) -> Vec<ApplicationValues<(&str, &str)>> {
let mut application_tuples = applications
.iter()
.filter_map(|(application_id, application)| {
let volume_injections: Vec<(&str, &str)> = volumes_from_application(application);
if !volume_injections.is_empty() {
Some(ApplicationValues::new(application_id, application, volume_injections))
} else {
None
}
})
.collect_vec();
application_tuples.sort();
application_tuples
}
pub fn volume_resources_from_app(app: &AppCatalogApp) -> Vec<(&str, &Volume)> {
app_resources(app, &|resource_value| match resource_value {
AppCatalogAppResourcesValue::Volume(volume) => Some(volume),
_ => None,
})
}