use std::collections::{BTreeMap, BTreeSet};
use isoloom_manifest::{Manifest, OutputSource};
use crate::events::{Emitter, EventKind};
use crate::status::OutputValue;
use crate::vars::Vars;
pub(crate) fn read(manifest: &Manifest, vars: &Vars, engine: &BTreeMap<String, String>, engine_sensitive: &BTreeSet<String>) -> Vec<OutputValue> {
let sensitive =
|o: &isoloom_manifest::Output| o.sensitive || matches!(&o.from, OutputSource::TerraformOutput { name, .. } if engine_sensitive.contains(name.as_str()));
let raw: Vec<Option<String>> = manifest
.outputs
.iter()
.map(|o| match &o.from {
OutputSource::PublishedPort { machine, port } => vars.published().get(&(machine.to_string(), port.to_string())).map(u16::to_string),
OutputSource::Static { value } => Some(value.clone()),
OutputSource::TerraformOutput { name, default } => engine.get(name.as_str()).cloned().or_else(|| default.clone()),
})
.collect();
let mut known = vars.clone();
for (o, v) in manifest.outputs.iter().zip(&raw) {
if let (None, false, Some(v)) = (&o.format, sensitive(o), v) {
known.set_output(o.name.as_str(), v.clone());
}
}
manifest
.outputs
.iter()
.zip(raw)
.map(|(o, raw)| {
let at = format!("outputs.{}.format", o.name);
let value = raw.and_then(|v| match &o.format {
Some(format) => known.render_value(format, &v, &at).ok(),
None => Some(v),
});
OutputValue {
name: o.name.to_string(),
role: o.role,
sensitive: sensitive(o),
value,
}
})
.collect()
}
pub(crate) fn bind(vars: &mut Vars, outputs: &[OutputValue]) {
for o in outputs.iter().filter(|o| !o.sensitive) {
if let Some(v) = &o.value {
vars.set_output(&o.name, v.clone());
}
}
}
pub(crate) fn message(manifest: &Manifest, vars: &Vars, outputs: &[OutputValue]) -> Option<String> {
let marked = outputs.iter().filter(|o| o.sensitive).map(|o| o.name.clone());
let hidden: BTreeSet<String> = manifest
.outputs
.iter()
.filter(|o| o.sensitive)
.map(|o| o.name.to_string())
.chain(marked)
.collect();
manifest.environment.message.as_ref().map(|m| vars.render_lenient(m, &hidden))
}
pub(crate) fn emit(events: &Emitter<'_>, outputs: &[OutputValue]) {
for o in outputs {
let value = if o.sensitive {
o.value.as_ref().map(|_| "***".to_owned())
} else {
o.value.clone()
};
events.emit(EventKind::Output {
name: o.name.clone(),
role: o.role,
value,
sensitive: o.sensitive,
});
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use super::*;
use crate::testing::docker_manifest;
#[test]
fn the_entry_comes_from_the_published_port_and_fills_the_message() {
let m = docker_manifest();
let mut vars = Vars::default();
let none = BTreeMap::new();
let marked = BTreeSet::new();
assert_eq!(read(&m, &vars, &none, &marked)[0].value, None, "not published yet");
vars.set_published(BTreeMap::from([(("web".to_owned(), "portal".to_owned()), 41873)]));
let outputs = read(&m, &vars, &none, &marked);
assert_eq!(
(outputs[0].name.as_str(), outputs[0].value.as_deref()),
("web_portal", Some("http://127.0.0.1:41873/"))
);
bind(&mut vars, &outputs);
assert_eq!(message(&m, &vars, &outputs).unwrap(), "The portal answers at http://localhost:41873/.\n");
}
}