This commit is contained in:
GroovieGermanikus 2024-06-07 09:24:22 +02:00
parent 329033c439
commit 92c47e8b14
No known key found for this signature in database
GPG Key ID: 5B6EB831A5CD2015
4 changed files with 89 additions and 17 deletions

13
Cargo.lock generated
View File

@ -1350,6 +1350,16 @@ dependencies = [
"winapi",
]
[[package]]
name = "gethostname"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
dependencies = [
"libc",
"windows-targets 0.48.5",
]
[[package]]
name = "getrandom"
version = "0.1.16"
@ -2026,6 +2036,7 @@ dependencies = [
"config",
"enum-iterator 2.1.0",
"futures-util",
"gethostname 0.4.3",
"geyser-grpc-connector",
"itertools 0.10.5",
"jsonrpsee-types",
@ -3496,7 +3507,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efb65329e6716dc0ce5b1d719d6e0052e7ff1179ab361916f256054d6b846ee1"
dependencies = [
"crossbeam-channel",
"gethostname",
"gethostname 0.2.3",
"lazy_static",
"log",
"reqwest 0.11.27",

View File

@ -35,6 +35,7 @@ anyhow = "1.0.86"
reqwest = { version = "0.12.4", features = ["json"] }
enum-iterator = "2.1.0"
gethostname = "0.4.3"
url = "2.5.0"
config = "0.14.0"
itertools = "0.10.5"

View File

@ -16,6 +16,8 @@ DISCORD_WEBHOOK=https://discord.com/api/webhooks/abcedfgh
cargo run --bin rpc-node-check-alive
```
* `DISCORD_WEBHOOK` is optional
## Example output
```
2024-06-05T16:43:44.680699Z INFO rpc_node_check_alive: all tasks started...

View File

@ -9,7 +9,7 @@ use std::time::Duration;
use futures_util::FutureExt;
use geyser_grpc_connector::{GrpcConnectionTimeouts, GrpcSourceConfig, Message};
use geyser_grpc_connector::grpc_subscription_autoreconnect_streams::create_geyser_reconnecting_stream;
use serde_json::json;
use serde_json::{json, Value};
use solana_account_decoder::parse_token::spl_token_ids;
use solana_rpc_client::nonblocking::rpc_client::RpcClient;
use solana_rpc_client::rpc_client::GetConfirmedSignaturesForAddress2Config;
@ -31,6 +31,7 @@ use yellowstone_grpc_proto::geyser::{SubscribeRequest, SubscribeRequestFilterAcc
use yellowstone_grpc_proto::geyser::subscribe_update::UpdateOneof;
use anyhow::Context;
use enum_iterator::Sequence;
use gethostname::gethostname;
use solana_account_decoder::UiAccountEncoding;
use solana_rpc_client_api::config::{RpcAccountInfoConfig, RpcProgramAccountsConfig};
use solana_rpc_client_api::filter::{Memcmp, RpcFilterType};
@ -57,13 +58,15 @@ enum Check {
}
async fn send_webook_discord() {
let url = std::env::var("DISCORD_WEBHOOK").unwrap();
async fn send_webook_discord(discord_body: Value) {
let Ok(url) = std::env::var("DISCORD_WEBHOOK") else {
info!("sending to discord is disabled");
return;
};
let client = reqwest::Client::new();
let res = client.post(url)
.json(&json!({
"content": "Hello, World!"
}))
.json(&discord_body)
.send().await;
match res {
Ok(_) => {
@ -79,9 +82,6 @@ async fn send_webook_discord() {
async fn main() -> ExitCode {
tracing_subscriber::fmt::init();
// send_webook_discord().await;
// name of rpc node for logging/discord (e.g. hostname)
let rpcnode_label = std::env::var("RPCNODE_LABEL").unwrap();
@ -108,10 +108,6 @@ async fn main() -> ExitCode {
info!("checks enabled for rpcnode <{}>: {:?}", rpcnode_label, checks_enabled);
let mut all_check_tasks: JoinSet<CheckResult> = JoinSet::new();
if checks_enabled.contains(&Check::Gpa) {
@ -150,6 +146,7 @@ async fn main() -> ExitCode {
let mut tasks_success = Vec::new();
let mut tasks_successful = 0;
let mut tasks_timeout = 0;
let mut tasks_timedout = Vec::new();
let mut tasks_failed = 0;
while let Some(res) = all_check_tasks.join_next().await {
match res {
@ -161,6 +158,7 @@ async fn main() -> ExitCode {
Ok(CheckResult::Timeout(check)) => {
tasks_timeout += 1;
warn!("timeout running task <{:?}>", check);
tasks_timedout.push(check);
}
Err(_) => {
tasks_failed += 1;
@ -169,11 +167,15 @@ async fn main() -> ExitCode {
}
}
let tasks_total = tasks_successful + tasks_failed + tasks_timeout;
let success = tasks_failed + tasks_timeout == 0;
assert!(tasks_total > 0, "no results");
if tasks_failed + tasks_timeout > 0 {
let discord_body = create_discord_message(&rpcnode_label, checks_enabled, &mut tasks_success, tasks_timedout, success);
send_webook_discord(discord_body).await;
if !success {
warn!("rpcnode <{}> - tasks failed ({}) or timed out ({}) of {} total",
rpcnode_label, tasks_failed, tasks_timeout, tasks_total);
for check in enum_iterator::all::<Check>() {
@ -181,13 +183,69 @@ async fn main() -> ExitCode {
warn!("!! did not complet task <{:?}>", check);
}
}
return ExitCode::SUCCESS;
return ExitCode::FAILURE;
} else {
info!("rpcnode <{}> - all {} tasks completed: {:?}", rpcnode_label, tasks_total, tasks_success);
return ExitCode::FAILURE;
return ExitCode::SUCCESS;
}
}
fn create_discord_message(
rpcnode_label: &str, checks_enabled: Vec<Check>, mut tasks_success: &mut Vec<Check>, mut tasks_timedout: Vec<Check>, success: bool) -> Value {
let result_per_check = enum_iterator::all::<Check>().map(|check| {
let name = format!("{:?}", check);
let disabled = !checks_enabled.contains(&check);
let timedout = tasks_timedout.contains(&check);
let success = tasks_success.contains(&check);
let value = if disabled {
"disabled"
} else if timedout {
"timed out"
} else if success {
"OK"
} else {
"failed"
};
json! {
{
"name": name,
"value": value
}
}
}).collect_vec();
let fields = result_per_check;
let status_color = if success {
0x00FF00
} else {
0xFC4100
};
let hostname_executed = gethostname();
let body = json! {
{
"content": "Automatic RPC Node check script notification",
"username": "RPC Node Check",
"embeds": [
{
"title": rpcnode_label,
"description": "",
"color": status_color,
"fields":
fields
,
"footer": {
"text": format!("by groovie on {}", hostname_executed.to_string_lossy())
}
}
]
}
};
body
}
fn read_rpc_config() -> Arc<RpcClient> {
// http://...