use std::sync::Arc;
use std::time::Duration;
use rightsize::{Container, ContainerGuard, Network, Result, RightsizeError, Wait};
const REST_PORT: u16 = 8081;
const RPC_PORT: u16 = 6123;
const JOBMANAGER_ALIAS: &str = "jobmanager";
pub struct FlinkContainer {
container: Container,
image: String,
network: Option<Arc<Network>>,
task_manager: Option<Container>,
}
impl FlinkContainer {
pub fn new() -> Self {
Self::with_image("flink:1.20.5")
}
pub fn with_image(image: &str) -> Self {
let container = Container::new(image)
.with_exposed_ports(&[REST_PORT, RPC_PORT])
.with_command(&["jobmanager"])
.with_memory_limit(1024)
.waiting_for(
Wait::for_http("/overview")
.for_port(REST_PORT)
.with_startup_timeout(Duration::from_secs(120)),
);
Self {
container,
image: image.to_string(),
network: None,
task_manager: None,
}
}
pub fn with_task_manager(mut self) -> Result<Self> {
if rightsize::backends::active_name() == "microsandbox" {
return Err(RightsizeError::unsupported_with_remedy(
"Flink TaskManager topology (network links; no nc/busybox in the flink image)",
"microsandbox",
"run this test with RIGHTSIZE_BACKEND=docker instead; JobManager-only \
(no with_task_manager()) is still supported on microsandbox",
));
}
let net = self
.network
.get_or_insert_with(|| Arc::new(Network::new_network()))
.clone();
self.container = self
.container
.with_network(&net)
.with_network_aliases(&[JOBMANAGER_ALIAS])
.with_env(
"FLINK_PROPERTIES",
&format!("jobmanager.rpc.address: {JOBMANAGER_ALIAS}"),
);
self.task_manager = Some(
Container::new(&self.image)
.with_command(&["taskmanager"])
.with_network(&net)
.with_env(
"FLINK_PROPERTIES",
&format!("jobmanager.rpc.address: {JOBMANAGER_ALIAS}"),
)
.with_memory_limit(1024)
.waiting_for(Wait::for_log_message(".*", 0)),
);
Ok(self)
}
pub async fn start(self) -> Result<FlinkGuard> {
crate::register_default_backends();
let jobmanager = self.container.start().await?;
let task_manager = match self.task_manager {
Some(tm) => match tm.start().await {
Ok(guard) => Some(guard),
Err(e) => {
jobmanager.stop().await.ok();
return Err(e);
}
},
None => None,
};
Ok(FlinkGuard {
jobmanager,
task_manager,
network: self.network,
})
}
}
impl Default for FlinkContainer {
fn default() -> Self {
Self::new()
}
}
pub struct FlinkGuard {
jobmanager: ContainerGuard,
task_manager: Option<ContainerGuard>,
network: Option<Arc<Network>>,
}
impl FlinkGuard {
pub fn rest_url(&self) -> String {
format!(
"http://{}:{}",
self.jobmanager.host(),
self.jobmanager.get_mapped_port(REST_PORT).unwrap()
)
}
pub async fn stop(self) -> Result<()> {
if let Some(tm) = self.task_manager {
tm.stop().await?;
}
self.jobmanager.stop().await?;
if let Some(net) = self.network {
net.close().await?;
}
Ok(())
}
}
impl std::ops::Deref for FlinkGuard {
type Target = ContainerGuard;
fn deref(&self) -> &ContainerGuard {
&self.jobmanager
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn with_image_smoke() {
let _ = FlinkContainer::new();
let _ = FlinkContainer::with_image("flink:1.20.5");
}
}