use std::{ops::Deref, str::FromStr, time::Duration};
use backon::ConstantBuilder;
use malstrom::types::WorkerId;
use once_cell::sync::Lazy;
use thiserror::Error;
use envconfig::Envconfig;
#[derive(Envconfig)]
pub(crate) struct CommonConfig {
#[envconfig(from = "MALSTROM_K8S_IS_COORDINATOR", default = "false")]
pub is_coordinator: bool,
#[envconfig(from = "MALSTROM_K8S_WORKER_SVC_NAME")]
pub worker_svc_name: String,
#[envconfig(from = "MALSTROM_K8S_COORDINATOR_SVC_NAME")]
pub coordinator_svc_name: String,
#[envconfig(from = "MALSTROM_K8S_SCALE")]
pub initial_scale: u64,
#[envconfig(from = "MALSTROM_K8S_NS")]
pub namespace: String,
#[envconfig(from = "MALSTROM_K8S_WORKER_STS_NAME")]
pub worker_sts_name: String,
#[envconfig(from = "MALSTROM_K8S_WORKER_HOSTNAME")]
pub hostname: Option<String>,
#[envconfig(nested)]
pub network: NetworkConfig,
}
impl CommonConfig {
pub(crate) fn get_worker_id(&self) -> WorkerId {
if self.is_coordinator {
return WorkerId::MAX;
}
let (_, ordinal) = self
.hostname
.as_ref()
.expect("Expected MALSTROM_K8S_WORKER_HOSTNAME to be set")
.rsplit_once("-")
.expect("Hostname should follow scheme <sts-name>-<ordinal>");
WorkerId::from_str(ordinal).expect("Pod ordinal should be a number")
}
}
#[derive(Envconfig)]
pub(crate) struct NetworkConfig {
#[envconfig(from = "MALSTROM_K8S_BUF_CAP", default = "1024")]
pub buffer_capacity: usize,
#[envconfig(from = "MALSTROM_K8S_PORT", default = "29091")]
pub port: u16,
#[envconfig(from = "MALSTROM_K8S_INIT_CONN_TIMEOUT", default = "120")]
pub initial_conn_timeout_sec: u64,
#[envconfig(from = "MALSTROM_K8S_ENQUEUE_TIMEOUT", default = "30")]
pub enqeueue_timeout_sec: u64,
}
impl NetworkConfig {
#[inline]
pub fn enqeueue_timeout(&self) -> Duration {
Duration::from_secs(self.enqeueue_timeout_sec)
}
pub(crate) fn initial_conn_retry(&self) -> ConstantBuilder {
let pause = Duration::from_secs(5);
let max_times = self.initial_conn_timeout_sec / pause.as_secs();
ConstantBuilder::default()
.with_delay(pause)
.with_max_times(max_times.try_into().unwrap())
}
}
pub(crate) struct PodOrdinal(WorkerId);
impl FromStr for PodOrdinal {
type Err = ConfigError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (_, ordinal) = s
.rsplit_once("-")
.ok_or(ConfigError::GetWorkerId(s.to_string()))?;
let as_num =
WorkerId::from_str(ordinal).map_err(|_| ConfigError::GetWorkerId(s.to_string()))?;
Ok(Self(as_num))
}
}
impl Deref for PodOrdinal {
type Target = WorkerId;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Error)]
pub(crate) enum ConfigError {
#[error("Cannot find worker id in hostname `{0}`")]
GetWorkerId(String),
}
#[cfg(test)]
pub(crate) static CONFIG: Lazy<CommonConfig> = Lazy::new(load_test_config);
#[cfg(not(test))]
pub(crate) static CONFIG: Lazy<CommonConfig> = Lazy::new(load_config);
#[cfg(not(test))]
fn load_config() -> CommonConfig {
CommonConfig::init_from_env().expect("Necessary env vars should be set")
}
#[cfg(test)]
fn load_test_config() -> CommonConfig {
use std::collections::HashMap;
CommonConfig {
is_coordinator: false,
worker_svc_name: "worker-svc.default.svc.cluster.locar".into(),
coordinator_svc_name: "coordinator-svc.default.svc.cluster.locar".into(),
initial_scale: 1,
namespace: "default".into(),
worker_sts_name: "worker".into(),
hostname: Some("worker-0".into()),
network: NetworkConfig::init_from_hashmap(&HashMap::new()).unwrap(),
}
}