use std::net::Ipv4Addr;
use indexmap::IndexMap;
use isoloom_plan::Environment;
use isoloom_render::container::UTILITY_IMAGE;
use isoloom_render::{router, trunks};
use isoloom_spec::vlans;
use crate::file::{Attachment, Healthcheck, Restart, Seconds, Service};
use crate::images::TC_IMAGE;
use crate::interpolation::literal;
use crate::ipv6;
pub(crate) fn services(env: &Environment, ts: &[trunks::Trunk], services: &mut IndexMap<String, Service>) {
if let Some(addresses) = &env.router {
services.insert(router::NAME.into(), router_service(env, addresses));
}
let mut lans: Vec<&str> = ts.iter().map(|t| t.lan.as_str()).collect();
lans.dedup();
lans.retain(|lan| vlans::switch_of(env.spec(), lan).is_none());
for lan in lans {
let of_lan: Vec<&trunks::Trunk> = ts.iter().filter(|t| t.lan == lan).collect();
services.insert(trunks::switch_name(lan), switch_service(env, &of_lan));
}
}
fn router_service(env: &Environment, addresses: &IndexMap<String, Ipv4Addr>) -> Service {
let spec = env.spec();
let mut environment = IndexMap::from([("RULES".to_owned(), router::nftables(spec))]);
let tc = router::tc_script(spec);
if let Some(t) = &tc {
environment.insert("TC".into(), literal(t));
}
let start = if tc.is_some() {
"apk add --no-cache nftables iproute2 >/dev/null && printf '%s' \"$$RULES\" | nft -f - && printf '%s' \"$$TC\" | sh && exec sleep infinity"
} else {
"apk add --no-cache nftables >/dev/null && printf '%s' \"$$RULES\" | nft -f - && exec sleep infinity"
};
Service {
image: Some(env.images.pull(UTILITY_IMAGE)),
hostname: Some(router::NAME.into()),
cap_add: vec!["NET_ADMIN".into()],
sysctls: ipv6::forwarding(env),
networks: addresses.iter().map(|(n, a)| (n.clone(), Some(ipv6::router_attachment(env, n, *a)))).collect(),
environment: Some(environment),
init: true,
entrypoint: vec!["/bin/sh".into(), "-c".into(), start.into()],
healthcheck: Some(Healthcheck::shell("nft list table inet isoloom >/dev/null 2>&1", Seconds(3), 60)),
restart: Some(Restart::UnlessStopped),
..Service::default()
}
}
fn switch_service(env: &Environment, ts: &[&trunks::Trunk]) -> Service {
let spec = env.spec();
let mut networks = IndexMap::new();
for t in ts {
for v in &t.vlans {
networks.insert(v.network.clone(), Some(Attachment::at(trunks::switch_address(spec, &v.network))));
}
networks.insert(trunks::link_network(t), Some(Attachment::with_mac(trunks::mac(t, trunks::End::Switch))));
}
Service {
image: Some(env.images.pull(TC_IMAGE)),
cap_add: vec!["NET_ADMIN".into()],
networks,
init: true,
entrypoint: vec!["/bin/sh".into(), "-c".into(), literal(&trunks::switch_script(spec, ts))],
healthcheck: Some(Healthcheck::shell(trunks::switch_ready(ts), Seconds(2), 30)),
restart: Some(Restart::UnlessStopped),
..Service::default()
}
}