use std::path::PathBuf;
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
pub server_name: String,
pub token: String,
#[serde(default = "default_endpoint")]
pub api_endpoint: String,
#[serde(default)]
pub daemon: DaemonConfig,
#[serde(default)]
pub docker: DockerProbeConfig,
#[serde(default)]
pub systemd: SystemdProbeConfig,
#[serde(default)]
pub resources: ResourceProbeConfig,
#[serde(default)]
pub ports: PortProbeConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DaemonConfig {
#[serde(default = "default_heartbeat_secs")]
pub heartbeat_interval_secs: u64,
}
impl Default for DaemonConfig {
fn default() -> Self {
Self { heartbeat_interval_secs: default_heartbeat_secs() }
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct DockerProbeConfig {
#[serde(default)]
pub projects: Vec<PathBuf>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SystemdProbeConfig {
#[serde(default)]
pub units: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceProbeConfig {
#[serde(default = "default_disk_high")]
pub disk_high_percent: u8,
#[serde(default = "default_memory_high")]
pub memory_high_percent: u8,
#[serde(default)]
pub load_1m_high: f32,
}
impl Default for ResourceProbeConfig {
fn default() -> Self {
Self {
disk_high_percent: default_disk_high(),
memory_high_percent: default_memory_high(),
load_1m_high: 0.0,
}
}
}
fn default_disk_high() -> u8 {
90
}
fn default_memory_high() -> u8 {
90
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PortProbeConfig {
#[serde(default)]
pub tcp: Vec<String>,
}
fn default_endpoint() -> String {
"https://api.stackpatrol.dev".into()
}
fn default_heartbeat_secs() -> u64 {
30
}
pub fn config_path() -> Result<PathBuf> {
let dir = dirs::config_dir().context("could not resolve config dir")?;
Ok(dir.join("stackpatrol").join("config.toml"))
}
impl Config {
pub fn load() -> Result<Self> {
let path = config_path()?;
let raw = std::fs::read_to_string(&path)
.with_context(|| format!("reading {}", path.display()))?;
let cfg: Config = toml::from_str(&raw).context("parsing config TOML")?;
Ok(cfg)
}
pub fn save(&self) -> Result<PathBuf> {
let path = config_path()?;
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)
.with_context(|| format!("creating {}", parent.display()))?;
}
let body = toml::to_string_pretty(self).context("serializing config")?;
std::fs::write(&path, body).with_context(|| format!("writing {}", path.display()))?;
Ok(path)
}
}