/*
* Copyright (C) 2024 Jonni Liljamo <jonni@liljamo.com>
*
* This file is licensed under GPL-3.0-or-later, see NOTICE and LICENSE for
* more information.
*/
use std::time::Duration;
use emerwen_proto::emerwen::{
shared::{target::Method, Target, TargetState},
workertomaster::{worker_to_master_client::WorkerToMasterClient, SetTargetStateRequest},
};
use tonic::{service::interceptor::InterceptedService, transport::Channel};
use crate::AuthInterceptor;
pub struct TargetMonitor {
target: Target,
client: WorkerToMasterClient<InterceptedService<Channel, AuthInterceptor>>,
last_state: TargetState,
}
impl TargetMonitor {
pub fn new(
target: Target,
client: WorkerToMasterClient<InterceptedService<Channel, AuthInterceptor>>,
) -> TargetMonitor {
TargetMonitor {
target,
client,
last_state: TargetState::Unknown,
}
}
pub async fn run(&mut self) -> Result<(), Box<dyn std::error::Error>> {
// TODO: do some testing and figure out good logic for when we should
// return "Unknown"... Prolly check for specific errors in the monitor_ functions
let sleep_duration = Duration::from_millis(self.target.interval.into());
if let Some(method) = &self.target.method {
loop {
let state;
match method {
Method::Ping(_) => match self.monitor_ping().await {
Ok(up) => {
if up {
state = TargetState::Up;
} else {
state = TargetState::Down;
}
}
Err(_) => {
state = TargetState::Unknown;
}
},
Method::Get(params) => match self.monitor_get(¶ms.ok_codes).await {
Ok(up) => {
if up {
state = TargetState::Up;
} else {
state = TargetState::Down;
}
}
Err(_) => {
state = TargetState::Unknown;
}
},
}
if state != self.last_state {
self.last_state = state;
let _ = self
.client
.set_target_state(SetTargetStateRequest {
id: self.target.id,
state: state.into(),
})
.await;
}
tokio::time::sleep(sleep_duration).await;
}
} else {
// FIXME: lmao didn't have a method?
}
Ok(())
}
async fn monitor_ping(&self) -> Result<bool, Box<dyn std::error::Error>> {
match surge_ping::ping(self.target.addr.parse()?, &[0u8; 8]).await {
Ok((_packet, _duration)) => Ok(true),
Err(_) => Ok(false),
}
}
async fn monitor_get(&self, ok_codes: &[u32]) -> Result<bool, Box<dyn std::error::Error>> {
let response = reqwest::get(self.target.addr.clone()).await?;
Ok(ok_codes.contains(&response.status().as_u16().into()))
}
}