DEVELOPMENT ENVIRONMENT

~liljamo/emerwen

ref: abcdf0d3b170901445527942b0a06d74cf4e46fc emerwen/emerwen-worker/src/monitor.rs -rw-r--r-- 3.4 KiB
abcdf0d3Jonni Liljamo feat(master): hello i succeeded in async 12 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/*
 * 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_protocol::{
    emerwen_protocol_client::EmerwenProtocolClient, target::Method, SetTargetStateRequest, Target,
    TargetState,
};
use tonic::{service::interceptor::InterceptedService, transport::Channel};

use crate::AuthInterceptor;

pub struct TargetMonitor {
    target: Target,
    client: EmerwenProtocolClient<InterceptedService<Channel, AuthInterceptor>>,
    last_state: TargetState,
}

impl TargetMonitor {
    pub fn new(
        target: Target,
        client: EmerwenProtocolClient<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(&params.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()))
    }
}