use crate::app::{app_resources, apps_that_use_secret};
use crate::application_types::{ApplicationValues, EnvVarInjection};
use crate::certificate::certificates_that_use_secret;
use crate::dsh_api_client::DshApiClient;
use crate::error::DshApiResult;
use crate::proxy::proxies_that_use_secret;
#[allow(unused_imports)]
use crate::types::{AllocationStatus, Empty, Secret};
use crate::types::{AppCatalogApp, AppCatalogAppResourcesValue, Application};
#[allow(unused_imports)]
use crate::DshApiError;
use crate::{Dependant, DependantApp, DependantApplication, DependantCertificate, DependantProxy};
use futures::try_join;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum SecretInjection {
#[serde(rename = "env")]
EnvVar { env_var_name: String },
#[serde(rename = "cert-chain-secret")]
CertChainSecret,
#[serde(rename = "key-secret")]
KeySecret,
#[serde(rename = "passphrase-secret")]
PassphraseSecret,
}
impl SecretInjection {
pub(crate) fn env_var<T>(env_var_name: T) -> Self
where
T: Into<String>,
{
Self::EnvVar { env_var_name: env_var_name.into() }
}
}
impl Display for SecretInjection {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::EnvVar { env_var_name } => write!(f, "{}", env_var_name),
Self::CertChainSecret => write!(f, "cert-chain-secret"),
Self::KeySecret => write!(f, "key-secret"),
Self::PassphraseSecret => write!(f, "passphrase-secret"),
}
}
}
impl DshApiClient {
pub async fn secret_dependants(&self, secret_name: &str) -> DshApiResult<Vec<Dependant<SecretInjection>>> {
let (applications, apps, proxies) = try_join!(self.get_application_configuration_map(), self.get_appcatalogapp_configuration_map(), self.proxies())?;
let mut dependants: Vec<Dependant<SecretInjection>> = vec![];
for application in secret_env_vars_from_applications(secret_name, &applications) {
dependants.push(Dependant::service(
application.id,
application.application.instances,
application
.values
.iter()
.map(|env_var| SecretInjection::EnvVar { env_var_name: env_var.to_string() })
.collect_vec(),
));
}
for (app_id, _, resource_ids) in apps_that_use_secret(secret_name, &apps) {
dependants.push(Dependant::app(
app_id.to_string(),
resource_ids.iter().map(|resource_id| resource_id.to_string()).collect_vec(),
));
}
for (proxy_id, proxy) in proxies_that_use_secret(secret_name, &proxies) {
dependants.push(Dependant::proxy(proxy_id.to_string(), proxy.instances.get()));
}
Ok(dependants)
}
pub async fn secret_names(&self) -> DshApiResult<Vec<(String, Option<String>)>> {
let mut secret_names = self.get_secret_ids().await?.into_iter().map(normalize_secret_name).collect_vec();
secret_names.sort_by(|(name_a, _), (name_b, _)| name_a.cmp(name_b));
Ok(secret_names)
}
pub async fn secret_names_non_system(&self) -> DshApiResult<Vec<String>> {
Ok(self.get_secret_ids().await?.into_iter().filter(|secret_id| !is_system_id(secret_id)).collect_vec())
}
pub async fn secret_names_system(&self) -> DshApiResult<Vec<(String, String)>> {
let mut secret_names = self
.secret_names()
.await?
.into_iter()
.flat_map(|(secret_name, secret_id)| secret_id.map(|id| (secret_name, id)))
.collect_vec();
secret_names.sort_by(|(name_a, _), (name_b, _)| name_a.cmp(name_b));
Ok(secret_names)
}
pub async fn secrets_with_dependants(&self) -> DshApiResult<Vec<(String, Option<String>, Vec<Dependant<SecretInjection>>)>> {
let (secret_names, certificates, applications, apps, proxies) = try_join!(
self.secret_names(),
self.certificates(),
self.get_application_configuration_map(),
self.get_appcatalogapp_configuration_map(),
self.proxies()
)?;
let mut secrets = Vec::<(String, Option<String>, Vec<Dependant<SecretInjection>>)>::new();
for (secret_name, secret_id) in secret_names {
let mut dependants: Vec<Dependant<SecretInjection>> = vec![];
for application in secret_env_vars_from_applications(&secret_name, &applications) {
dependants.push(Dependant::service(
application.id,
application.application.instances,
application
.values
.iter()
.map(|env_var| SecretInjection::EnvVar { env_var_name: env_var.to_string() })
.collect_vec(),
));
}
for (app_id, _, resource_ids) in apps_that_use_secret(&secret_name, &apps) {
dependants.push(Dependant::app(
app_id.to_string(),
resource_ids.iter().map(|resource_id| resource_id.to_string()).collect_vec(),
));
}
for dependant_certificate in certificates_that_use_secret(&secret_name, &certificates) {
dependants.push(Dependant::Certificate { certificate: dependant_certificate })
}
for (proxy_id, proxy) in proxies_that_use_secret(&secret_name, &proxies) {
dependants.push(Dependant::proxy(proxy_id.to_string(), proxy.instances.get()));
}
secrets.push((secret_name, secret_id, dependants));
}
Ok(secrets)
}
pub async fn secrets_with_dependant_applications(&self) -> DshApiResult<Vec<(String, Option<String>, Vec<DependantApplication<SecretInjection>>)>> {
let (secret_names, applications) = try_join!(self.secret_names(), self.get_application_configuration_map())?;
let mut secrets = Vec::<(String, Option<String>, Vec<DependantApplication<SecretInjection>>)>::new();
for (secret_name, secret_id) in secret_names {
let mut dependant_applications: Vec<DependantApplication<SecretInjection>> = vec![];
for application in secret_env_vars_from_applications(secret_name.as_str(), &applications) {
dependant_applications.push(DependantApplication::new(
application.id.to_string(),
application.application.instances,
application
.values
.iter()
.map(|env_var| SecretInjection::EnvVar { env_var_name: env_var.to_string() })
.collect_vec(),
));
}
secrets.push((secret_name, secret_id, dependant_applications));
}
Ok(secrets)
}
pub async fn secrets_with_dependant_apps(&self) -> DshApiResult<Vec<(String, Option<String>, Vec<DependantApp>)>> {
let (secret_names, apps) = try_join!(self.secret_names(), self.get_appcatalogapp_configuration_map())?;
let mut secrets = Vec::<(String, Option<String>, Vec<DependantApp>)>::new();
for (secret_name, secret_id) in secret_names {
let mut dependant_apps: Vec<DependantApp> = vec![];
for (app_id, _, resource_ids) in apps_that_use_secret(secret_name.as_str(), &apps) {
dependant_apps.push(DependantApp::new(
app_id.to_string(),
resource_ids.iter().map(|resource_id| resource_id.to_string()).collect_vec(),
));
}
secrets.push((secret_name, secret_id, dependant_apps));
}
Ok(secrets)
}
pub async fn secrets_with_dependant_certificates(&self) -> DshApiResult<Vec<(String, Option<String>, Vec<DependantCertificate>)>> {
let (secret_names, certificates) = try_join!(self.secret_names(), self.certificates())?;
let mut secrets = Vec::<(String, Option<String>, Vec<DependantCertificate>)>::new();
for (secret_name, secret_id) in &secret_names {
let mut dependant_certificates: Vec<DependantCertificate> = vec![];
for dependant_certificate in certificates_that_use_secret(secret_name, &certificates) {
dependant_certificates.push(dependant_certificate);
}
secrets.push((secret_name.clone(), secret_id.clone(), dependant_certificates));
}
Ok(secrets)
}
pub async fn secrets_with_dependant_proxies(&self) -> DshApiResult<Vec<(String, Option<String>, Vec<DependantProxy>)>> {
let (secret_names, proxies) = try_join!(self.secret_names(), self.proxies())?;
let mut secrets = Vec::<(String, Option<String>, Vec<DependantProxy>)>::new();
for (secret_name, secret_id) in secret_names {
let mut dependant_proxies: Vec<DependantProxy> = vec![];
for (proxy_id, proxy) in proxies_that_use_secret(secret_name.as_str(), &proxies) {
dependant_proxies.push(DependantProxy::new(proxy_id.to_string(), proxy.instances.get()));
}
secrets.push((secret_name, secret_id, dependant_proxies));
}
Ok(secrets)
}
}
pub fn secret_env_vars_from_application<'a>(secret_name: &str, application: &'a Application) -> Vec<&'a str> {
let mut secret_environment_variables = application
.secrets
.iter()
.filter_map(|secret| {
if secret_name == secret.name {
let secret_injections = secret
.injections
.iter()
.filter_map(|injection| injection.get("env").map(|secret_injection| secret_injection.as_str()))
.collect_vec();
if secret_injections.is_empty() {
None
} else {
Some(secret_injections)
}
} else {
None
}
})
.flatten()
.collect_vec();
secret_environment_variables.sort();
secret_environment_variables
}
pub fn secret_env_vars_from_applications<'a>(secret_name: &str, applications: &'a HashMap<String, Application>) -> Vec<ApplicationValues<'a, &'a str>> {
let mut application_tuples = applications
.iter()
.filter_map(|(application_id, application)| {
let injections = secret_env_vars_from_application(secret_name, application);
if !injections.is_empty() {
Some(ApplicationValues::new(application_id, application, injections))
} else {
None
}
})
.collect_vec();
application_tuples.sort();
application_tuples
}
#[deprecated]
pub fn secret_is_system(secret_id: &str) -> bool {
is_system_id(secret_id)
}
pub fn is_system_id(secret_id_name: &str) -> bool {
secret_id_name.contains('!')
}
pub fn is_system_name(secret_id_name: &str) -> bool {
secret_id_name.starts_with("system/")
}
#[deprecated]
pub fn secret_id_to_secret_name(secret_id: &String) -> Cow<String> {
if is_system_id(secret_id) {
Cow::Owned(format!("system{}", secret_id.replace("!", "/")))
} else {
Cow::Borrowed(secret_id)
}
}
pub fn normalize_secret_name(secret_id_name: String) -> (String, Option<String>) {
if is_system_id(&secret_id_name) {
(format!("system{}", secret_id_name.replace("!", "/")), Some(secret_id_name))
} else if let Some(stripped_system_name) = &secret_id_name.strip_prefix("system/") {
(secret_id_name.clone(), Some(format!("!{}", stripped_system_name.replace("/", "!"))))
} else {
(secret_id_name, None)
}
}
pub fn secret_resources_from_app(app: &AppCatalogApp) -> Vec<(&str, &Secret)> {
app_resources(app, &|resource_value| match resource_value {
AppCatalogAppResourcesValue::Secret(secret) => Some(secret),
_ => None,
})
}
pub fn secrets_from_application(application: &Application) -> Vec<EnvVarInjection> {
let mut grouped_injections: Vec<(&String, Vec<&str>)> = application
.secrets
.iter()
.filter_map(|secret| {
secret
.injections
.iter()
.filter_map(|injection| injection.get("env").map(|key| key.as_str()))
.collect_vec()
.first()
.map(|env_injection| (&secret.name, *env_injection))
})
.into_group_map()
.into_iter()
.collect_vec();
grouped_injections.iter_mut().for_each(|(_, injections)| injections.sort());
grouped_injections.sort();
grouped_injections
.into_iter()
.map(|(secret_name, injections)| EnvVarInjection::new(secret_name, injections))
.collect_vec()
}
pub fn secrets_from_applications(applications: &HashMap<String, Application>) -> Vec<ApplicationValues<EnvVarInjection>> {
let mut application_tuples = applications
.iter()
.filter_map(|(application_id, application)| {
let secret_injections = secrets_from_application(application);
if !secret_injections.is_empty() {
Some(ApplicationValues::new(application_id, application, secret_injections))
} else {
None
}
})
.collect_vec();
application_tuples.sort();
application_tuples
}
pub fn secrets_resources_from_apps<'a>(secret_names: &[String], apps: &'a HashMap<String, AppCatalogApp>) -> Vec<(String, &'a AppCatalogApp, Vec<String>)> {
let mut app_ids: Vec<String> = apps.keys().map(|p| p.to_string()).collect();
app_ids.sort();
let mut tuples: Vec<(String, &AppCatalogApp, Vec<String>)> = vec![];
for app_id in app_ids {
let mut resource_ids = vec![];
let app = apps.get(&app_id).unwrap();
for (secret_resource_id, secret) in secret_resources_from_app(app) {
if secret_names.contains(&secret.name) {
resource_ids.push(secret_resource_id.to_string())
}
}
if !resource_ids.is_empty() {
tuples.push((app_id, app, resource_ids));
}
}
tuples
}