use std::collections::BTreeSet;
use isoloom_manifest::{Native, Protocol};
use super::config::{self, ConfigPort};
use crate::executor::Cx;
use crate::net::HeldPort;
const PUBLISH_FIXED: &str = "ISOLOOM_PUBLISH_FIXED";
pub(super) fn pick(cx: &Cx<'_>, config: &str) -> Option<String> {
let ports = config::ports(config)?;
let fixed = cx.vars.params().contains_key(PUBLISH_FIXED);
let mut held: Vec<HeldPort> = Vec::new();
let mut taken: BTreeSet<(u16, Protocol)> = BTreeSet::new();
let pins = config::pinned_ports(&ports, |p| {
let mine = |n: &u16| cx.run.runner.reserve(cx.run.name(), *n, p.protocol);
if fixed && let Some(preferred) = preferred(cx, p).filter(|n| !taken.contains(&(*n, p.protocol)) && mine(n)) {
taken.insert((preferred, p.protocol));
return Some(preferred);
}
let port = free_port(cx, p.protocol)?;
taken.insert((port.port, p.protocol));
let number = port.port;
held.push(port);
Some(number)
});
drop(held);
pins
}
fn free_port(cx: &Cx<'_>, protocol: Protocol) -> Option<HeldPort> {
let runner = cx.run.runner;
let mut refused = Vec::new();
for _ in 0..64 {
let port = runner.config.ports.pick_free(protocol)?;
if runner.reserve(cx.run.name(), port.port, protocol) {
return Some(port);
}
refused.push(port);
}
None
}
fn preferred(cx: &Cx<'_>, p: &ConfigPort) -> Option<u16> {
let machine = cx.run.manifest().machines.iter().find(|m| match &m.native {
Some(Native::Compose { service }) => service.as_str() == p.service,
_ => m.name.as_str() == p.service,
})?;
let port = machine.ports.iter().find(|q| q.port.get() == p.target && q.kind.transport() == p.protocol)?;
port.publish.as_ref()?.preferred.map(std::num::NonZeroU16::get)
}