- Add 4-step progress modal to config apply flow (validate, write, reload, services ready) - Poll SSE-updated data-share-health attributes to detect when services finish restarting - Fix stale health bug: recalculate health for affected shares based on actual mount success instead of preserving old health from before reload - Add modal overlay/card/step CSS matching the dark theme - Include connection refactor (multi-protocol support) and probe helpers from prior work Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
143 lines
3.9 KiB
Rust
143 lines
3.9 KiB
Rust
//! Generate systemd unit files for Warpgate services.
|
|
|
|
use std::fmt::Write as _;
|
|
use std::fs;
|
|
use std::path::Path;
|
|
use std::process::Command;
|
|
|
|
use anyhow::{Context, Result};
|
|
|
|
use crate::config::Config;
|
|
|
|
/// Target directory for systemd unit files.
|
|
pub const SYSTEMD_DIR: &str = "/etc/systemd/system";
|
|
|
|
/// Single unified service unit name.
|
|
pub const RUN_SERVICE: &str = "warpgate.service";
|
|
|
|
/// Generate the single `warpgate.service` unit that runs `warpgate run`.
|
|
///
|
|
/// This replaces the old multi-unit approach. The `warpgate run` supervisor
|
|
/// manages rclone mount, SMB, NFS, and WebDAV internally.
|
|
pub fn generate_run_unit(_config: &Config) -> Result<String> {
|
|
let mut unit = String::new();
|
|
writeln!(unit, "# Generated by Warpgate — do not edit manually.")?;
|
|
writeln!(unit, "[Unit]")?;
|
|
writeln!(unit, "Description=Warpgate NAS cache proxy")?;
|
|
writeln!(unit, "After=network-online.target")?;
|
|
writeln!(unit, "Wants=network-online.target")?;
|
|
writeln!(unit)?;
|
|
writeln!(unit, "[Service]")?;
|
|
writeln!(unit, "Type=simple")?;
|
|
writeln!(unit, "ExecStart=/usr/local/bin/warpgate run")?;
|
|
writeln!(unit, "Restart=on-failure")?;
|
|
writeln!(unit, "RestartSec=10")?;
|
|
writeln!(unit, "KillMode=mixed")?;
|
|
writeln!(unit, "TimeoutStopSec=30")?;
|
|
writeln!(unit)?;
|
|
writeln!(unit, "[Install]")?;
|
|
writeln!(unit, "WantedBy=multi-user.target")?;
|
|
|
|
Ok(unit)
|
|
}
|
|
|
|
/// Install the single `warpgate.service` unit and reload systemd.
|
|
pub fn install_run_unit(config: &Config) -> Result<()> {
|
|
let systemd_dir = Path::new(SYSTEMD_DIR);
|
|
|
|
let unit_content = generate_run_unit(config)?;
|
|
fs::write(systemd_dir.join(RUN_SERVICE), unit_content)
|
|
.with_context(|| format!("Failed to write {RUN_SERVICE}"))?;
|
|
|
|
// Reload systemd daemon
|
|
let status = Command::new("systemctl")
|
|
.arg("daemon-reload")
|
|
.status()
|
|
.context("Failed to run systemctl daemon-reload")?;
|
|
|
|
if !status.success() {
|
|
anyhow::bail!("systemctl daemon-reload failed with exit code: {}", status);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
fn test_config() -> Config {
|
|
toml::from_str(
|
|
r#"
|
|
[[connections]]
|
|
name = "nas"
|
|
host = "10.0.0.1"
|
|
protocol = "sftp"
|
|
user = "admin"
|
|
|
|
[cache]
|
|
dir = "/tmp/cache"
|
|
|
|
[read]
|
|
[bandwidth]
|
|
[writeback]
|
|
[directory_cache]
|
|
[protocols]
|
|
|
|
[[shares]]
|
|
name = "photos"
|
|
connection = "nas"
|
|
remote_path = "/photos"
|
|
mount_point = "/mnt/photos"
|
|
"#,
|
|
)
|
|
.unwrap()
|
|
}
|
|
|
|
#[test]
|
|
fn test_generate_run_unit_sections() {
|
|
let config = test_config();
|
|
let unit = generate_run_unit(&config).unwrap();
|
|
|
|
assert!(unit.contains("[Unit]"));
|
|
assert!(unit.contains("[Service]"));
|
|
assert!(unit.contains("[Install]"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_generate_run_unit_content() {
|
|
let config = test_config();
|
|
let unit = generate_run_unit(&config).unwrap();
|
|
|
|
assert!(unit.contains("Description=Warpgate NAS cache proxy"));
|
|
assert!(unit.contains("After=network-online.target"));
|
|
assert!(unit.contains("Type=simple"));
|
|
assert!(unit.contains("ExecStart=/usr/local/bin/warpgate run"));
|
|
assert!(unit.contains("Restart=on-failure"));
|
|
assert!(unit.contains("RestartSec=10"));
|
|
assert!(unit.contains("KillMode=mixed"));
|
|
assert!(unit.contains("WantedBy=multi-user.target"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_systemd_constants() {
|
|
assert_eq!(SYSTEMD_DIR, "/etc/systemd/system");
|
|
assert_eq!(RUN_SERVICE, "warpgate.service");
|
|
}
|
|
}
|
|
|
|
/// Enable and start the single `warpgate.service`.
|
|
pub fn enable_and_start_run() -> Result<()> {
|
|
let status = Command::new("systemctl")
|
|
.args(["enable", "--now", RUN_SERVICE])
|
|
.status()
|
|
.with_context(|| format!("Failed to run systemctl enable --now {RUN_SERVICE}"))?;
|
|
|
|
if !status.success() {
|
|
anyhow::bail!("systemctl enable --now {RUN_SERVICE} failed with exit code: {status}");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|