use std::time::Duration;
use crate::config::{DEFAULT_RUNPOD_IMAGE, DEFAULT_RUNPOD_VENV, Target};
use crate::exec::JobRuntime;
use super::JOB_ENV;
pub const WORKDIR: &str = "/workspace/overbrainer";
pub const VOLUME_MOUNT: &str = "/workspace/data";
pub const VOLUME_WORKDIR: &str = "/workspace/data/overbrainer";
pub const MIN_CUDA_VERSION: &str = "13.0";
#[derive(Debug, Clone, PartialEq)]
pub struct RunpodTarget {
pub gpu_types: Vec<String>,
pub gpu_count: u32,
pub image: String,
pub venv: String,
pub container_disk_gb: u32,
pub max_hours: f64,
pub boot_grace: Duration,
pub retrieve_grace: Duration,
pub data_center_ids: Vec<String>,
pub network_volume_id: Option<String>,
}
impl RunpodTarget {
#[must_use]
pub fn from_target(target: &Target) -> Option<Self> {
let Target::Runpod {
gpu_types,
gpu_count,
image,
venv,
container_disk_gb,
max_hours,
boot_grace_minutes,
retrieve_grace_minutes,
data_center_ids,
network_volume_id,
} = target
else {
return None;
};
Some(Self {
gpu_types: gpu_types.clone(),
gpu_count: *gpu_count,
image: image
.clone()
.unwrap_or_else(|| DEFAULT_RUNPOD_IMAGE.to_string()),
venv: venv
.clone()
.unwrap_or_else(|| DEFAULT_RUNPOD_VENV.to_string()),
container_disk_gb: *container_disk_gb,
max_hours: *max_hours,
boot_grace: Duration::from_secs(u64::from(*boot_grace_minutes) * 60),
retrieve_grace: Duration::from_secs(u64::from(*retrieve_grace_minutes) * 60),
data_center_ids: data_center_ids.clone(),
network_volume_id: network_volume_id.clone(),
})
}
#[must_use]
pub fn workdir(&self) -> &'static str {
if self.network_volume_id.is_some() {
VOLUME_WORKDIR
} else {
WORKDIR
}
}
#[must_use]
pub fn runtime(&self) -> JobRuntime {
JobRuntime::Native {
venv: Some(self.venv.clone()),
env_file: Some(JOB_ENV.to_string()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn config_target(volume: Option<&str>) -> Target {
Target::Runpod {
gpu_types: vec!["NVIDIA A40".into()],
gpu_count: 1,
image: None,
venv: None,
container_disk_gb: 50,
max_hours: 6.0,
boot_grace_minutes: 30,
retrieve_grace_minutes: 60,
data_center_ids: vec!["EU-RO-1".into()],
network_volume_id: volume.map(str::to_string),
}
}
#[test]
fn defaults_are_applied() -> Result<(), &'static str> {
let target = RunpodTarget::from_target(&config_target(None)).ok_or("not runpod")?;
assert_eq!(target.image, DEFAULT_RUNPOD_IMAGE);
assert_eq!(target.venv, "/workspace/axolotl-venv");
assert_eq!(target.boot_grace, Duration::from_secs(1800));
assert_eq!(target.retrieve_grace, Duration::from_secs(3600));
assert_eq!(target.workdir(), "/workspace/overbrainer");
assert_eq!(
target.runtime(),
JobRuntime::Native {
venv: Some("/workspace/axolotl-venv".into()),
env_file: Some("/etc/overbrainer/job.env".into()),
}
);
let on_volume =
RunpodTarget::from_target(&config_target(Some("vol1"))).ok_or("not runpod")?;
assert_eq!(on_volume.workdir(), "/workspace/data/overbrainer");
Ok(())
}
}