traffic_core: продакшн-фиксы runtime и валидации
This commit is contained in:
+50
-19
@@ -6,17 +6,21 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use crate::{config::Config, docker, firewall, iface, netns::build_transport, reboot, traffic};
|
||||
use crate::{config::Config, docker, firewall, iface, netns::build_transport, traffic};
|
||||
use tracing::{error, info};
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
struct LastActionState {
|
||||
last_ok_at: Option<String>,
|
||||
last_ok: Option<String>,
|
||||
last_error_at: Option<String>,
|
||||
last_error: Option<String>,
|
||||
}
|
||||
|
||||
const LAST_STATE_FILE: &str = "/run/awg2-harness/last_action.state";
|
||||
const EVENTS_FILE: &str = "/run/awg2-harness/events.log";
|
||||
const EVENTS_MAX_LINES: usize = 1000;
|
||||
const EVENTS_MAX_BYTES: usize = 256 * 1024;
|
||||
|
||||
pub fn run(cfg: Config) -> anyhow::Result<()> {
|
||||
info!("starting daemon");
|
||||
@@ -36,22 +40,22 @@ pub fn run(cfg: Config) -> anyhow::Result<()> {
|
||||
set_ok(&state, "startup firewall reconcile ok");
|
||||
append_event("startup_firewall ok");
|
||||
}
|
||||
let _ = reboot::maybe_schedule_reboot(&cfg);
|
||||
let reboot_cfg = cfg.clone();
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = reboot::reboot_scheduler_loop(reboot_cfg) {
|
||||
error!(error = %e, "reboot scheduler loop error");
|
||||
}
|
||||
});
|
||||
|
||||
let events_cfg = cfg.clone();
|
||||
let events_state = state.clone();
|
||||
thread::spawn(move || {
|
||||
loop {
|
||||
if let Err(e) = docker_events_loop(&events_cfg, &events_state) {
|
||||
set_err(&events_state, format!("docker events loop crashed: {e}"));
|
||||
thread::sleep(Duration::from_secs(10));
|
||||
match docker_events_loop(&events_cfg, &events_state) {
|
||||
Ok(()) => {
|
||||
set_err(
|
||||
&events_state,
|
||||
"docker events stream ended, restarting".to_string(),
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
set_err(&events_state, format!("docker events loop crashed: {e}"));
|
||||
}
|
||||
}
|
||||
thread::sleep(Duration::from_secs(10));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -144,13 +148,16 @@ fn wait_for_interface(cfg: &Config, container_name: &str, timeout_secs: u64) ->
|
||||
|
||||
fn set_ok(state: &Arc<Mutex<LastActionState>>, msg: &str) {
|
||||
if let Ok(mut s) = state.lock() {
|
||||
s.last_ok_at = Some(now_rfc3339());
|
||||
s.last_ok = Some(msg.to_string());
|
||||
s.last_error = None;
|
||||
persist_state(&s);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_err(state: &Arc<Mutex<LastActionState>>, msg: String) {
|
||||
if let Ok(mut s) = state.lock() {
|
||||
s.last_error_at = Some(now_rfc3339());
|
||||
s.last_error = Some(msg);
|
||||
persist_state(&s);
|
||||
}
|
||||
@@ -159,8 +166,10 @@ fn set_err(state: &Arc<Mutex<LastActionState>>, msg: String) {
|
||||
fn persist_state(state: &LastActionState) {
|
||||
let _ = fs::create_dir_all("/run/awg2-harness");
|
||||
let content = format!(
|
||||
"last_ok={}\nlast_error={}\n",
|
||||
"last_ok_at={}\nlast_ok={}\nlast_error_at={}\nlast_error={}\n",
|
||||
state.last_ok_at.clone().unwrap_or_default(),
|
||||
state.last_ok.clone().unwrap_or_default(),
|
||||
state.last_error_at.clone().unwrap_or_default(),
|
||||
state.last_error.clone().unwrap_or_default()
|
||||
);
|
||||
let _ = fs::write(LAST_STATE_FILE, content);
|
||||
@@ -168,12 +177,34 @@ fn persist_state(state: &LastActionState) {
|
||||
|
||||
fn append_event(event: &str) {
|
||||
let _ = fs::create_dir_all("/run/awg2-harness");
|
||||
let ts = chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true);
|
||||
if let Ok(mut f) = fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(EVENTS_FILE)
|
||||
let ts = now_rfc3339();
|
||||
let mut lines: Vec<String> = fs::read_to_string(EVENTS_FILE)
|
||||
.ok()
|
||||
.map(|s| s.lines().map(|v| v.to_string()).collect())
|
||||
.unwrap_or_default();
|
||||
lines.push(format!("{ts} {event}"));
|
||||
while lines.len() > EVENTS_MAX_LINES {
|
||||
lines.remove(0);
|
||||
}
|
||||
|
||||
let mut content = lines.join("\n");
|
||||
if !content.is_empty() {
|
||||
content.push('\n');
|
||||
}
|
||||
while content.len() > EVENTS_MAX_BYTES {
|
||||
if let Some(pos) = content.find('\n') {
|
||||
content.drain(..=pos);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(mut f) = fs::OpenOptions::new().create(true).write(true).truncate(true).open(EVENTS_FILE)
|
||||
{
|
||||
let _ = writeln!(f, "{ts} {event}");
|
||||
let _ = f.write_all(content.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
fn now_rfc3339() -> String {
|
||||
chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user