From af02a83a506e11dc6b93cf001e6e8de9441c926c Mon Sep 17 00:00:00 2001 From: Ivan Shumkov Date: Wed, 16 Oct 2024 23:19:04 +0700 Subject: [PATCH 1/4] fix(dashmate): invalid drive status check --- .../src/config/generateEnvsFactory.js | 1 + .../dashmate/src/status/scopes/platform.js | 34 ++++---- packages/rs-drive-abci/src/main.rs | 84 ++++++++++--------- 3 files changed, 65 insertions(+), 54 deletions(-) diff --git a/packages/dashmate/src/config/generateEnvsFactory.js b/packages/dashmate/src/config/generateEnvsFactory.js index 781ef7bdaa..8d7823db07 100644 --- a/packages/dashmate/src/config/generateEnvsFactory.js +++ b/packages/dashmate/src/config/generateEnvsFactory.js @@ -69,6 +69,7 @@ export default function generateEnvsFactory(configFile, homeDir, getConfigProfil let driveAbciMetricsUrl = ''; if (config.get('platform.drive.abci.metrics.enabled')) { + // IP and port inside container driveAbciMetricsUrl = 'http://0.0.0.0:29090'; } diff --git a/packages/dashmate/src/status/scopes/platform.js b/packages/dashmate/src/status/scopes/platform.js index 7605104ecb..9c78f5fcec 100644 --- a/packages/dashmate/src/status/scopes/platform.js +++ b/packages/dashmate/src/status/scopes/platform.js @@ -1,4 +1,5 @@ import prettyMs from 'pretty-ms'; +import DockerComposeError from '../../docker/errors/DockerComposeError.js'; import providers from '../providers.js'; import { DockerStatusEnum } from '../enums/dockerStatus.js'; import { ServiceStatusEnum } from '../enums/serviceStatus.js'; @@ -162,30 +163,33 @@ export default function getPlatformScopeFactory( try { info.dockerStatus = await determineStatus.docker(dockerCompose, config, 'drive_abci'); - info.serviceStatus = determineStatus.platform(info.dockerStatus, isCoreSynced, mnRRSoftFork); + } catch (e) { + if (e instanceof ContainerIsNotPresentError) { + info.dockerStatus = DockerStatusEnum.not_started; + } - if (info.serviceStatus === ServiceStatusEnum.up) { - const driveEchoResult = await dockerCompose.execCommand( + throw e; + } + + info.serviceStatus = determineStatus.platform(info.dockerStatus, isCoreSynced, mnRRSoftFork); + + // Get Drive status to make sure it's responding + if (info.serviceStatus === ServiceStatusEnum.up) { + try { + await dockerCompose.execCommand( config, 'drive_abci', 'drive-abci status', ); - - if (driveEchoResult.exitCode !== 0) { + } catch (e) { + if (e instanceof DockerComposeError + && e.dockerComposeExecutionResult + && e.dockerComposeExecutionResult.exitCode !== 0) { info.serviceStatus = ServiceStatusEnum.error; } - } - return info; - } catch (e) { - if (e instanceof ContainerIsNotPresentError) { - return { - dockerStatus: DockerStatusEnum.not_started, - serviceStatus: ServiceStatusEnum.stopped, - }; + throw e; } - - return info; } }; diff --git a/packages/rs-drive-abci/src/main.rs b/packages/rs-drive-abci/src/main.rs index aab50e3e06..d0580a114c 100644 --- a/packages/rs-drive-abci/src/main.rs +++ b/packages/rs-drive-abci/src/main.rs @@ -3,6 +3,10 @@ //! RS-Drive-ABCI server starts a single-threaded server and listens to connections from Tenderdash. use clap::{Parser, Subcommand}; +use dapi_grpc::platform::v0::get_status_request; +use dapi_grpc::platform::v0::get_status_request::GetStatusRequestV0; +use dapi_grpc::platform::v0::platform_client::PlatformClient; +use dapi_grpc::tonic::transport::Uri; use dpp::version::PlatformVersion; use drive_abci::config::{FromEnv, PlatformConfig}; use drive_abci::core::wait_for_core_to_sync::v0::wait_for_core_to_sync_v0; @@ -17,6 +21,7 @@ use std::fs::remove_file; use std::net::SocketAddr; use std::path::PathBuf; use std::process::ExitCode; +use std::str::FromStr; use std::sync::Arc; use tokio::runtime::{Builder, Runtime}; use tokio::signal::unix::{signal, SignalKind}; @@ -98,6 +103,14 @@ impl Cli { ) -> Result<(), String> { match self.command { Commands::Start => { + // Start runtime in the main thread + tracing::info!( + version = env!("CARGO_PKG_VERSION"), + features = list_enabled_features().join(","), + rust = env!("CARGO_PKG_RUST_VERSION"), + "drive-abci server initializing", + ); + if config.drive.grovedb_verify_on_startup { verify_grovedb(&config.db_path, false)?; } @@ -129,10 +142,12 @@ impl Cli { server::start(runtime, Arc::new(platform), config, cancel); + tracing::info!("drive-abci server is stopped"); + return Ok(()); } Commands::Config => dump_config(&config)?, - Commands::Status => check_status(&config)?, + Commands::Status => runtime.block_on(check_status(&config))?, Commands::Verify => verify_grovedb(&config.db_path, true)?, }; @@ -201,14 +216,6 @@ fn main() -> Result<(), ExitCode> { install_panic_hook(cancel.clone()); - // Start runtime in the main thread - tracing::info!( - version = env!("CARGO_PKG_VERSION"), - features = list_enabled_features().join(","), - rust = env!("CARGO_PKG_RUST_VERSION"), - "drive-abci server initializing", - ); - let runtime_guard = runtime.enter(); runtime.spawn(handle_signals(cancel.clone(), loggers)); @@ -226,8 +233,6 @@ fn main() -> Result<(), ExitCode> { drop(runtime_guard); runtime.shutdown_timeout(Duration::from_millis(SHUTDOWN_TIMEOUT_MILIS)); - tracing::info!("drive-abci server is stopped"); - result } @@ -303,31 +308,26 @@ fn list_enabled_features() -> Vec<&'static str> { } /// Check status of ABCI server. -fn check_status(config: &PlatformConfig) -> Result<(), String> { - if let Some(prometheus_addr) = &config.prometheus_bind_address { - let url = - url::Url::parse(prometheus_addr).expect("cannot parse ABCI_PROMETHEUS_BIND_ADDRESS"); - - let addr = format!( - "{}://{}:{}/metrics", - url.scheme(), - url.host() - .ok_or("ABCI_PROMETHEUS_BIND_ADDRESS must contain valid host".to_string())?, - url.port().unwrap_or(DEFAULT_PROMETHEUS_PORT) - ); - - let body: String = ureq::get(&addr) - .set("Content-type", "text/plain") - .call() - .map_err(|e| e.to_string())? - .into_string() - .map_err(|e| e.to_string())?; +async fn check_status(config: &PlatformConfig) -> Result<(), String> { + // Convert the gRPC bind address string to a Uri + let uri = Uri::from_str(&config.grpc_bind_address).map_err(|e| e.to_string())?; + + // Connect to the gRPC server + let mut client = PlatformClient::connect(uri) + .await + .map_err(|e| e.to_string())?; + + // Make a request to the server + let request = dapi_grpc::platform::v0::GetStatusRequest { + version: Some(get_status_request::Version::V0(GetStatusRequestV0 {})), + }; - println!("{}", body); - Ok(()) - } else { - Err("ABCI_PROMETHEUS_BIND_ADDRESS not defined, cannot check status".to_string()) - } + // Should return non-zero error code if Drive is not responding + client + .get_status(request) + .await + .map(|_| ()) + .map_err(|e| e.to_string()) } /// Verify GroveDB integrity. @@ -442,15 +442,16 @@ fn install_panic_hook(cancel: CancellationToken) { #[cfg(test)] mod test { + use ::drive::{drive::Drive, query::Element}; + use dpp::block::epoch::Epoch; + use drive::drive::credit_pools::epochs::epoch_key_constants; + use std::str::FromStr; use std::{ fs, path::{Path, PathBuf}, }; - use ::drive::{drive::Drive, query::Element}; - use dpp::block::epoch::Epoch; - use drive::drive::credit_pools::epochs::epoch_key_constants; - + use dapi_grpc::tonic::transport::Uri; use dpp::version::PlatformVersion; use drive::drive::credit_pools::epochs::paths::EpochProposers; use drive_abci::logging::LogLevel; @@ -541,4 +542,9 @@ mod test { println!("db path: {:?}", &db_path); } + + #[test] + fn test_uri_conversion_from_server_bind_address() { + Uri::from_str("0.0.0.0:1324").expect("should parse"); + } } From 09d1c35e51584b986c4c733cf07ad4aab4e9bc89 Mon Sep 17 00:00:00 2001 From: Ivan Shumkov Date: Thu, 17 Oct 2024 14:09:23 +0700 Subject: [PATCH 2/4] fix: drive status report --- .../dashmate/src/commands/status/index.js | 6 ++++- .../dashmate/src/status/scopes/overview.js | 3 ++- .../dashmate/src/status/scopes/platform.js | 10 ++++---- packages/rs-drive-abci/src/main.rs | 24 +++++++------------ 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/packages/dashmate/src/commands/status/index.js b/packages/dashmate/src/commands/status/index.js index 6276bb9796..c22269ccdc 100644 --- a/packages/dashmate/src/commands/status/index.js +++ b/packages/dashmate/src/commands/status/index.js @@ -108,8 +108,12 @@ export default class StatusCommand extends ConfigBaseCommand { plain['Platform Enabled'] = platform.enabled || 'n/a'; + const platformStatus = platform.tenderdash.serviceStatus !== ServiceStatusEnum.up + ? platform.tenderdash.serviceStatus + : platform.drive.serviceStatus; + if (platform.enabled) { - plain['Platform Status'] = colors.status(platform.tenderdash.serviceStatus)(platform.tenderdash.serviceStatus) || 'n/a'; + plain['Platform Status'] = colors.status(platformStatus)(platformStatus) || 'n/a'; if (platform.tenderdash.serviceStatus === ServiceStatusEnum.up) { plain['Platform Version'] = platform.tenderdash.version || 'n/a'; diff --git a/packages/dashmate/src/status/scopes/overview.js b/packages/dashmate/src/status/scopes/overview.js index a38d9ffb5e..2a56bd3678 100644 --- a/packages/dashmate/src/status/scopes/overview.js +++ b/packages/dashmate/src/status/scopes/overview.js @@ -57,8 +57,9 @@ export default function getOverviewScopeFactory( } if (config.get('platform.enable')) { - const { tenderdash } = await getPlatformScope(config); + const { drive, tenderdash } = await getPlatformScope(config); + platform.drive = drive; platform.tenderdash = tenderdash; } diff --git a/packages/dashmate/src/status/scopes/platform.js b/packages/dashmate/src/status/scopes/platform.js index 9c78f5fcec..9943e35b10 100644 --- a/packages/dashmate/src/status/scopes/platform.js +++ b/packages/dashmate/src/status/scopes/platform.js @@ -166,9 +166,9 @@ export default function getPlatformScopeFactory( } catch (e) { if (e instanceof ContainerIsNotPresentError) { info.dockerStatus = DockerStatusEnum.not_started; + } else { + throw e; } - - throw e; } info.serviceStatus = determineStatus.platform(info.dockerStatus, isCoreSynced, mnRRSoftFork); @@ -186,11 +186,13 @@ export default function getPlatformScopeFactory( && e.dockerComposeExecutionResult && e.dockerComposeExecutionResult.exitCode !== 0) { info.serviceStatus = ServiceStatusEnum.error; + } else { + throw e; } - - throw e; } } + + return info; }; /** diff --git a/packages/rs-drive-abci/src/main.rs b/packages/rs-drive-abci/src/main.rs index d0580a114c..a76fffd1c7 100644 --- a/packages/rs-drive-abci/src/main.rs +++ b/packages/rs-drive-abci/src/main.rs @@ -11,7 +11,7 @@ use dpp::version::PlatformVersion; use drive_abci::config::{FromEnv, PlatformConfig}; use drive_abci::core::wait_for_core_to_sync::v0::wait_for_core_to_sync_v0; use drive_abci::logging::{LogBuilder, LogConfig, LogDestination, Loggers}; -use drive_abci::metrics::{Prometheus, DEFAULT_PROMETHEUS_PORT}; +use drive_abci::metrics::Prometheus; use drive_abci::platform_types::platform::Platform; use drive_abci::rpc::core::DefaultCoreRPC; use drive_abci::{logging, server}; @@ -103,7 +103,6 @@ impl Cli { ) -> Result<(), String> { match self.command { Commands::Start => { - // Start runtime in the main thread tracing::info!( version = env!("CARGO_PKG_VERSION"), features = list_enabled_features().join(","), @@ -216,6 +215,7 @@ fn main() -> Result<(), ExitCode> { install_panic_hook(cancel.clone()); + // Start runtime in the main thread let runtime_guard = runtime.enter(); runtime.spawn(handle_signals(cancel.clone(), loggers)); @@ -226,7 +226,7 @@ fn main() -> Result<(), ExitCode> { Ok(()) } Err(e) => { - tracing::error!(error = e, "drive-abci failed"); + tracing::error!(error = e, "drive-abci failed: {e}"); Err(ExitCode::FAILURE) } }; @@ -310,12 +310,13 @@ fn list_enabled_features() -> Vec<&'static str> { /// Check status of ABCI server. async fn check_status(config: &PlatformConfig) -> Result<(), String> { // Convert the gRPC bind address string to a Uri - let uri = Uri::from_str(&config.grpc_bind_address).map_err(|e| e.to_string())?; + let uri = Uri::from_str(&format!("http://{}", config.grpc_bind_address)) + .map_err(|e| format!("invalid url: {e}"))?; // Connect to the gRPC server - let mut client = PlatformClient::connect(uri) + let mut client = PlatformClient::connect(uri.clone()) .await - .map_err(|e| e.to_string())?; + .map_err(|e| format!("can't connect to grpc server {uri}: {e}"))?; // Make a request to the server let request = dapi_grpc::platform::v0::GetStatusRequest { @@ -327,7 +328,7 @@ async fn check_status(config: &PlatformConfig) -> Result<(), String> { .get_status(request) .await .map(|_| ()) - .map_err(|e| e.to_string()) + .map_err(|e| format!("can't request status: {e}")) } /// Verify GroveDB integrity. @@ -415,7 +416,7 @@ fn configure_logging(cli: &Cli, config: &PlatformConfig) -> Result 0 { let cli_config = LogConfig { destination: LogDestination::StdOut, - level: cli.verbose.try_into().unwrap(), + level: cli.verbose.try_into()?, color: cli.color, ..Default::default() }; @@ -445,13 +446,11 @@ mod test { use ::drive::{drive::Drive, query::Element}; use dpp::block::epoch::Epoch; use drive::drive::credit_pools::epochs::epoch_key_constants; - use std::str::FromStr; use std::{ fs, path::{Path, PathBuf}, }; - use dapi_grpc::tonic::transport::Uri; use dpp::version::PlatformVersion; use drive::drive::credit_pools::epochs::paths::EpochProposers; use drive_abci::logging::LogLevel; @@ -542,9 +541,4 @@ mod test { println!("db path: {:?}", &db_path); } - - #[test] - fn test_uri_conversion_from_server_bind_address() { - Uri::from_str("0.0.0.0:1324").expect("should parse"); - } } From b082afe7b5db2d3e199c169b97bbf3de95421842 Mon Sep 17 00:00:00 2001 From: Ivan Shumkov Date: Thu, 17 Oct 2024 14:17:21 +0700 Subject: [PATCH 3/4] fix: drive status report --- packages/dashmate/src/status/determineStatus.js | 4 ++++ .../dashmate/test/unit/status/scopes/platform.spec.js | 8 +++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/dashmate/src/status/determineStatus.js b/packages/dashmate/src/status/determineStatus.js index 723302a006..b089f1f7f7 100644 --- a/packages/dashmate/src/status/determineStatus.js +++ b/packages/dashmate/src/status/determineStatus.js @@ -49,6 +49,10 @@ export default { return coreIsSynced ? ServiceStatusEnum.up : ServiceStatusEnum.wait_for_core; } + if (dockerStatus === DockerStatusEnum.not_started) { + return ServiceStatusEnum.stopped; + } + return ServiceStatusEnum.error; }, }; diff --git a/packages/dashmate/test/unit/status/scopes/platform.spec.js b/packages/dashmate/test/unit/status/scopes/platform.spec.js index c3a1c28ff1..b94f0da636 100644 --- a/packages/dashmate/test/unit/status/scopes/platform.spec.js +++ b/packages/dashmate/test/unit/status/scopes/platform.spec.js @@ -1,3 +1,5 @@ +import ContainerIsNotPresentError + from '../../../../src/docker/errors/ContainerIsNotPresentError.js'; import providers from '../../../../src/status/providers.js'; import determineStatus from '../../../../src/status/determineStatus.js'; import getConfigMock from '../../../../src/test/mock/getConfigMock.js'; @@ -377,7 +379,7 @@ describe('getPlatformScopeFactory', () => { mockDetermineDockerStatus.withArgs(mockDockerCompose, config, 'drive_tenderdash') .returns(DockerStatusEnum.running); mockDetermineDockerStatus.withArgs(mockDockerCompose, config, 'drive_abci') - .throws(); + .throws(new ContainerIsNotPresentError('drive_abci')); mockDockerCompose.execCommand.returns({ exitCode: 0, out: '' }); mockMNOWatchProvider.returns(Promise.resolve('OPEN')); @@ -430,8 +432,8 @@ describe('getPlatformScopeFactory', () => { network: 'test', }, drive: { - dockerStatus: null, - serviceStatus: null, + dockerStatus: DockerStatusEnum.not_started, + serviceStatus: ServiceStatusEnum.stopped, }, }; From c482a37c09d44ae2d87336adab595a9e01bf5b6e Mon Sep 17 00:00:00 2001 From: Ivan Shumkov Date: Thu, 17 Oct 2024 15:58:17 +0700 Subject: [PATCH 4/4] chore: remove unused ureq dependency --- Cargo.lock | 1 - packages/rs-drive-abci/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8ee9c7c2ac..1afcedfd02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1498,7 +1498,6 @@ dependencies = [ "tokio-util", "tracing", "tracing-subscriber", - "ureq", "url", ] diff --git a/packages/rs-drive-abci/Cargo.toml b/packages/rs-drive-abci/Cargo.toml index dfcd0e0e25..99889a6c9e 100644 --- a/packages/rs-drive-abci/Cargo.toml +++ b/packages/rs-drive-abci/Cargo.toml @@ -63,7 +63,6 @@ metrics-exporter-prometheus = { version = "0.15", default-features = false, feat "http-listener", ] } url = { version = "2.3.1" } -ureq = { "version" = "2.6.2" } tokio = { version = "1.40", features = [ "macros", "signal",