mod carry;
mod forms;
mod machine;
mod overrides;
mod refusals;
mod service;
mod trail;
pub use trail::Trail;
use indexmap::IndexMap;
use isoloom_catalogue::Catalogue;
use isoloom_spec::model::{self, Controller, Group, SecretSource, Target, VmImage};
use isoloom_spec::syntax::{self, Problem};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Scope {
Lab,
Target(Target),
}
impl Scope {
#[must_use]
pub fn target(self) -> Option<Target> {
match self {
Scope::Lab => None,
Scope::Target(t) => Some(t),
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct Lowered {
pub spec: model::Spec,
pub refusals: Vec<Problem>,
pub trail: Trail,
pub problems: Vec<Problem>,
}
pub(super) struct Lowering<'a> {
source: &'a syntax::Spec,
scope: Scope,
services: &'a service::Services,
}
#[must_use]
pub fn lower(source: &syntax::Spec, catalogue: &Catalogue, scope: Scope) -> Lowered {
let mut trail = Trail::default();
let mut secrets = source.secrets.clone();
let mut machines = IndexMap::new();
let services = service::Services::of(catalogue, source);
let mut problems = services.problems();
let l = Lowering {
source,
scope,
services: &services,
};
for (name, m) in &source.machines {
let lowered = machine::lower(&l, name, m, &mut trail);
for (secret, input) in lowered.inputs {
secrets.insert(secret, SecretSource::Input { input });
}
problems.extend(lowered.problems);
machines.insert(name.clone(), lowered.machine);
}
let mut spec = model::Spec {
version: 1,
name: source.name.clone(),
networks: overrides::networks(source, scope),
reach: source.reach.clone(),
dns: source.dns.clone(),
inputs: source.inputs.0.keys().map(ToString::to_string).collect(),
plain_inputs: source.inputs.0.iter().filter(|(_, i)| !i.sensitive).map(|(name, _)| name.to_string()).collect(),
secrets,
machines,
cloud: source.cloud.clone(),
volumes: overrides::volumes(source, scope),
constraints: source.constraints.clone(),
common: None,
groups: groups(source),
provision: source.provision.clone(),
controller: controller(source),
checks: source.checks.clone(),
targets: source.targets.clone(),
message: source.message.clone(),
tools: source.tools.clone(),
clones: source.clones.clone(),
};
machine::resolve_dns(&mut spec, scope);
let refusals = scope.target().map(|t| refusals::of(source, &services, t)).unwrap_or_default();
trail.inputs(source);
let spec = match isoloom_spec::vlans::flatten(spec.clone()) {
Ok(flat) => flat,
Err(refused) => {
problems.push(refused);
spec
}
};
Lowered {
spec,
refusals,
trail,
problems,
}
}
#[must_use]
pub fn missing_form(source: &syntax::Spec, catalogue: &Catalogue, target: Target) -> Option<Problem> {
refusals::forms(source, &service::Services::of(catalogue, source), target).into_iter().next()
}
fn groups(source: &syntax::Spec) -> IndexMap<String, Group> {
source
.groups
.iter()
.map(|(n, g)| {
(
n.clone(),
Group {
members: g.members.clone(),
..Group::default()
},
)
})
.collect()
}
fn controller(source: &syntax::Spec) -> Option<Controller> {
let vagrant = source.overrides.vagrant.as_ref().and_then(|v| v.controller.as_ref());
let image = vagrant.filter(|b| b.box_name.is_some() || b.box_version.is_some()).map(|b| VmImage {
vagrant: b.box_name.clone(),
vagrant_version: b.box_version.clone(),
..VmImage::default()
});
match (&source.controller, image) {
(None, None) => None,
(c, image) => {
let mut c = c.clone().unwrap_or_default();
if image.is_some() {
c.image = image;
}
Some(c)
}
}
}
#[cfg(test)]
mod tests;