Add log-init-function and websocket retry
This commit is contained in:
parent
6536ff0792
commit
50bcac2a1b
12 changed files with 72 additions and 68 deletions
BIN
Cargo.lock
generated
BIN
Cargo.lock
generated
Binary file not shown.
|
@ -1,19 +1,20 @@
|
||||||
use std::str;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::relay_loop::run_relay_loop;
|
use crate::relay_loop::run_relay_loop;
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
use emgauwa_lib::db;
|
use emgauwa_lib::constants::WEBSOCKET_RETRY_TIMEOUT;
|
||||||
use emgauwa_lib::db::{DbController, DbRelay};
|
use emgauwa_lib::db::{DbController, DbRelay};
|
||||||
use emgauwa_lib::handlers::v1::ws::controllers::ControllerWsAction;
|
use emgauwa_lib::handlers::v1::ws::controllers::ControllerWsAction;
|
||||||
use emgauwa_lib::models::{Controller, FromDbModel};
|
use emgauwa_lib::models::{Controller, FromDbModel};
|
||||||
use emgauwa_lib::types::ControllerUid;
|
use emgauwa_lib::types::ControllerUid;
|
||||||
use futures::channel::mpsc;
|
use emgauwa_lib::{db, utils};
|
||||||
use futures::{future, pin_mut, SinkExt, StreamExt};
|
use futures::{SinkExt, StreamExt};
|
||||||
use sqlx::pool::PoolConnection;
|
use sqlx::pool::PoolConnection;
|
||||||
use sqlx::Sqlite;
|
use sqlx::Sqlite;
|
||||||
use tokio::io::AsyncReadExt;
|
use tokio::time;
|
||||||
use tokio_tungstenite::tungstenite::Error;
|
use tokio_tungstenite::tungstenite::Error;
|
||||||
use tokio_tungstenite::{connect_async, tungstenite::protocol::Message};
|
use tokio_tungstenite::{connect_async, tungstenite::protocol::Message};
|
||||||
|
use utils::init_logging;
|
||||||
|
|
||||||
mod driver;
|
mod driver;
|
||||||
mod relay_loop;
|
mod relay_loop;
|
||||||
|
@ -51,6 +52,7 @@ async fn create_this_relay(
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let settings = settings::init();
|
let settings = settings::init();
|
||||||
|
init_logging(&settings.logging.level);
|
||||||
|
|
||||||
let pool = db::init(&settings.database).await;
|
let pool = db::init(&settings.database).await;
|
||||||
|
|
||||||
|
@ -87,46 +89,47 @@ async fn main() {
|
||||||
settings.core.host, settings.core.port
|
settings.core.host, settings.core.port
|
||||||
);
|
);
|
||||||
|
|
||||||
let (stdin_tx, stdin_rx) = mpsc::unbounded();
|
tokio::spawn(run_relay_loop(settings));
|
||||||
tokio::spawn(read_stdin(stdin_tx));
|
|
||||||
|
|
||||||
let (ws_stream, _) = connect_async(url).await.expect("Failed to connect");
|
loop {
|
||||||
|
time::sleep(WEBSOCKET_RETRY_TIMEOUT).await;
|
||||||
|
|
||||||
|
let connect_result = connect_async(&url).await;
|
||||||
|
if let Err(err) = connect_result {
|
||||||
|
log::warn!(
|
||||||
|
"Failed to connect to websocket: {}. Retrying in {} seconds...",
|
||||||
|
err,
|
||||||
|
WEBSOCKET_RETRY_TIMEOUT.as_secs()
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let (ws_stream, _) = connect_result.unwrap();
|
||||||
|
|
||||||
let (mut write, read) = ws_stream.split();
|
let (mut write, read) = ws_stream.split();
|
||||||
|
|
||||||
let ws_action = ControllerWsAction::Register(this);
|
let ws_action = ControllerWsAction::Register(this.clone());
|
||||||
println!("Sending action: {:?}", ws_action);
|
|
||||||
let ws_action_json = serde_json::to_string(&ws_action).unwrap();
|
let ws_action_json = serde_json::to_string(&ws_action).unwrap();
|
||||||
println!("Sending json: {}", ws_action_json);
|
|
||||||
write.send(Message::text(ws_action_json)).await.unwrap();
|
write.send(Message::text(ws_action_json)).await.unwrap();
|
||||||
let ws_to_stdout = read.for_each(handle_message);
|
|
||||||
let stdin_to_ws = stdin_rx.map(Ok).forward(write);
|
|
||||||
|
|
||||||
tokio::spawn(run_relay_loop(settings));
|
let read_handler = read.for_each(handle_message);
|
||||||
|
|
||||||
pin_mut!(stdin_to_ws, ws_to_stdout);
|
read_handler.await;
|
||||||
future::select(stdin_to_ws, ws_to_stdout).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Our helper method which will read data from stdin and send it along the
|
log::warn!(
|
||||||
// sender provided.
|
"Lost connection to websocket. Retrying in {} seconds...",
|
||||||
async fn read_stdin(tx: mpsc::UnboundedSender<Message>) {
|
WEBSOCKET_RETRY_TIMEOUT.as_secs()
|
||||||
let mut stdin = tokio::io::stdin();
|
);
|
||||||
loop {
|
|
||||||
let mut buf = vec![0; 1024];
|
|
||||||
let n = match stdin.read(&mut buf).await {
|
|
||||||
Err(_) | Ok(0) => break,
|
|
||||||
Ok(n) => n,
|
|
||||||
};
|
|
||||||
buf.truncate(n);
|
|
||||||
tx.unbounded_send(Message::text(str::from_utf8(&buf).unwrap()))
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_message(message_result: Result<Message, Error>) {
|
pub async fn handle_message(message_result: Result<Message, Error>) {
|
||||||
match message_result {
|
match message_result {
|
||||||
Ok(message) => println!("{}", message.into_text().unwrap()),
|
Ok(message) => {
|
||||||
Err(err) => println!("Error: {}", err),
|
if let Message::Text(msg_text) = message {
|
||||||
|
log::debug!("{}", msg_text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => log::debug!("Error: {}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,6 @@ pub async fn run_relay_loop(settings: Settings) {
|
||||||
loop {
|
loop {
|
||||||
let next_timestamp = Local::now().naive_local().time() + default_duration;
|
let next_timestamp = Local::now().naive_local().time() + default_duration;
|
||||||
time::sleep(default_duration).await;
|
time::sleep(default_duration).await;
|
||||||
println!("Relay loop: {}", next_timestamp)
|
log::debug!("Relay loop: {}", next_timestamp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ actix-web = "4.4"
|
||||||
actix-web-actors = "4.2"
|
actix-web-actors = "4.2"
|
||||||
actix-cors = "0.6"
|
actix-cors = "0.6"
|
||||||
|
|
||||||
simple_logger = "4.2"
|
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
|
|
|
@ -10,8 +10,7 @@ use actix_web::{middleware, web, App, HttpServer};
|
||||||
use emgauwa_lib::db::DbController;
|
use emgauwa_lib::db::DbController;
|
||||||
use emgauwa_lib::handlers;
|
use emgauwa_lib::handlers;
|
||||||
use emgauwa_lib::types::ConnectedControllersType;
|
use emgauwa_lib::types::ConnectedControllersType;
|
||||||
use log::{trace, LevelFilter};
|
use emgauwa_lib::utils::init_logging;
|
||||||
use simple_logger::SimpleLogger;
|
|
||||||
|
|
||||||
mod settings;
|
mod settings;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
@ -19,15 +18,7 @@ mod utils;
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
let settings = settings::init();
|
let settings = settings::init();
|
||||||
|
init_logging(&settings.logging.level);
|
||||||
let log_level: LevelFilter =
|
|
||||||
LevelFilter::from_str(&settings.logging.level).expect("Error parsing log level.");
|
|
||||||
trace!("Log level set to {:?}", log_level);
|
|
||||||
|
|
||||||
SimpleLogger::new()
|
|
||||||
.with_level(log_level)
|
|
||||||
.init()
|
|
||||||
.expect("Error initializing logger.");
|
|
||||||
|
|
||||||
let listener = TcpListener::bind(format!("{}:{}", settings.host, settings.port))
|
let listener = TcpListener::bind(format!("{}:{}", settings.host, settings.port))
|
||||||
.expect("Error creating listener");
|
.expect("Error creating listener");
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
|
use log::log;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::io::{Error, ErrorKind};
|
use std::io::{Error, ErrorKind};
|
||||||
|
|
||||||
|
@ -26,11 +27,11 @@ fn drop_privileges_group(group: &str) -> Result<(), Error> {
|
||||||
if let Ok(cstr) = CString::new(group.as_bytes()) {
|
if let Ok(cstr) = CString::new(group.as_bytes()) {
|
||||||
let p = unsafe { libc::getgrnam(cstr.as_ptr()) };
|
let p = unsafe { libc::getgrnam(cstr.as_ptr()) };
|
||||||
if p.is_null() {
|
if p.is_null() {
|
||||||
eprintln!("Unable to getgrnam of group: {}", group);
|
log::error!("Unable to getgrnam of group: {}", group);
|
||||||
return Err(Error::last_os_error());
|
return Err(Error::last_os_error());
|
||||||
}
|
}
|
||||||
if unsafe { libc::setgid((*p).gr_gid) } != 0 {
|
if unsafe { libc::setgid((*p).gr_gid) } != 0 {
|
||||||
eprintln!("Unable to setgid of group: {}", group);
|
log::error!("Unable to setgid of group: {}", group);
|
||||||
return Err(Error::last_os_error());
|
return Err(Error::last_os_error());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -47,11 +48,11 @@ fn drop_privileges_user(user: &str) -> Result<(), Error> {
|
||||||
if let Ok(cstr) = CString::new(user.as_bytes()) {
|
if let Ok(cstr) = CString::new(user.as_bytes()) {
|
||||||
let p = unsafe { libc::getpwnam(cstr.as_ptr()) };
|
let p = unsafe { libc::getpwnam(cstr.as_ptr()) };
|
||||||
if p.is_null() {
|
if p.is_null() {
|
||||||
eprintln!("Unable to getpwnam of user: {}", user);
|
log::error!("Unable to getpwnam of user: {}", user);
|
||||||
return Err(Error::last_os_error());
|
return Err(Error::last_os_error());
|
||||||
}
|
}
|
||||||
if unsafe { libc::setuid((*p).pw_uid) } != 0 {
|
if unsafe { libc::setuid((*p).pw_uid) } != 0 {
|
||||||
eprintln!("Unable to setuid of user: {}", user);
|
log::error!("Unable to setuid of user: {}", user);
|
||||||
return Err(Error::last_os_error());
|
return Err(Error::last_os_error());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -14,13 +14,14 @@ serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
|
|
||||||
|
simple_logger = "4.2"
|
||||||
|
log = "0.4"
|
||||||
|
|
||||||
config = "0.13"
|
config = "0.13"
|
||||||
|
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
|
|
||||||
sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio", "macros", "chrono"] }
|
sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio", "macros", "chrono"] }
|
||||||
libsqlite3-sys = { version = "*", features = ["bundled"] }
|
libsqlite3-sys = { version = "*", features = ["bundled"] }
|
||||||
|
|
||||||
log = "0.4"
|
|
||||||
uuid = "1.6"
|
uuid = "1.6"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
|
|
|
@ -3,3 +3,5 @@ use std::time::Duration;
|
||||||
pub const DEFAULT_PORT: u16 = 4419;
|
pub const DEFAULT_PORT: u16 = 4419;
|
||||||
pub const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
|
pub const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
|
||||||
pub const HEARTBEAT_TIMEOUT: Duration = Duration::from_secs(15);
|
pub const HEARTBEAT_TIMEOUT: Duration = Duration::from_secs(15);
|
||||||
|
|
||||||
|
pub const WEBSOCKET_RETRY_TIMEOUT: Duration = Duration::from_secs(5);
|
||||||
|
|
|
@ -9,18 +9,12 @@ use crate::models::{convert_db_list, Controller};
|
||||||
use crate::types::ConnectedControllersType;
|
use crate::types::ConnectedControllersType;
|
||||||
|
|
||||||
#[get("/api/v1/controllers")]
|
#[get("/api/v1/controllers")]
|
||||||
pub async fn index(
|
pub async fn index(pool: web::Data<Pool<Sqlite>>) -> Result<HttpResponse, ApiError> {
|
||||||
pool: web::Data<Pool<Sqlite>>,
|
|
||||||
connected_controllers: web::Data<ConnectedControllersType>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
let mut pool_conn = pool.acquire().await?;
|
let mut pool_conn = pool.acquire().await?;
|
||||||
|
|
||||||
let db_controllers = DbController::get_all(&mut pool_conn).await?;
|
let db_controllers = DbController::get_all(&mut pool_conn).await?;
|
||||||
|
|
||||||
let controllers: Vec<Controller> = convert_db_list(&mut pool_conn, db_controllers)?;
|
let controllers: Vec<Controller> = convert_db_list(&mut pool_conn, db_controllers)?;
|
||||||
|
|
||||||
let data = connected_controllers.lock().unwrap();
|
|
||||||
println!("{:?}", *data);
|
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(controllers))
|
Ok(HttpResponse::Ok().json(controllers))
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,5 @@ pub async fn ws_controllers(
|
||||||
stream,
|
stream,
|
||||||
)
|
)
|
||||||
.map_err(|_| ApiError::InternalError(String::from("error starting websocket")));
|
.map_err(|_| ApiError::InternalError(String::from("error starting websocket")));
|
||||||
println!("{:?}", resp);
|
|
||||||
resp
|
resp
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,14 +17,14 @@ pub trait FromDbModel {
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct Schedule {
|
pub struct Schedule {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub s: DbSchedule,
|
pub s: DbSchedule,
|
||||||
pub tags: Vec<String>,
|
pub tags: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct Relay {
|
pub struct Relay {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub r: DbRelay,
|
pub r: DbRelay,
|
||||||
|
@ -33,7 +33,7 @@ pub struct Relay {
|
||||||
pub tags: Vec<String>,
|
pub tags: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct Controller {
|
pub struct Controller {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub c: DbController,
|
pub c: DbController,
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
use log::LevelFilter;
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
pub fn load_settings<T>(config_name: &str, env_prefix: &str) -> T
|
pub fn load_settings<T>(config_name: &str, env_prefix: &str) -> T
|
||||||
where
|
where
|
||||||
for<'de> T: serde::Deserialize<'de>,
|
for<'de> T: serde::Deserialize<'de>,
|
||||||
|
@ -16,3 +20,13 @@ where
|
||||||
.try_deserialize::<T>()
|
.try_deserialize::<T>()
|
||||||
.expect("Error reading settings")
|
.expect("Error reading settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn init_logging(level: &str) {
|
||||||
|
let log_level: LevelFilter = LevelFilter::from_str(level).expect("Error parsing log level.");
|
||||||
|
log::trace!("Log level set to {:?}", log_level);
|
||||||
|
|
||||||
|
SimpleLogger::new()
|
||||||
|
.with_level(log_level)
|
||||||
|
.init()
|
||||||
|
.expect("Error initializing logger.");
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue