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/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/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/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/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 7605104ecb..9943e35b10 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,31 +163,36 @@ 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; + } else { + throw e; + } + } + + info.serviceStatus = determineStatus.platform(info.dockerStatus, isCoreSynced, mnRRSoftFork); - if (info.serviceStatus === ServiceStatusEnum.up) { - const driveEchoResult = await dockerCompose.execCommand( + // 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; + } else { + throw e; } } - - return info; - } catch (e) { - if (e instanceof ContainerIsNotPresentError) { - return { - dockerStatus: DockerStatusEnum.not_started, - serviceStatus: ServiceStatusEnum.stopped, - }; - } - - return info; } + + return info; }; /** 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, }, }; 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", diff --git a/packages/rs-drive-abci/src/main.rs b/packages/rs-drive-abci/src/main.rs index aab50e3e06..a76fffd1c7 100644 --- a/packages/rs-drive-abci/src/main.rs +++ b/packages/rs-drive-abci/src/main.rs @@ -3,11 +3,15 @@ //! 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; 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}; @@ -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,13 @@ impl Cli { ) -> Result<(), String> { match self.command { Commands::Start => { + 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 +141,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)?, }; @@ -202,13 +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)); @@ -219,15 +226,13 @@ 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) } }; drop(runtime_guard); runtime.shutdown_timeout(Duration::from_millis(SHUTDOWN_TIMEOUT_MILIS)); - tracing::info!("drive-abci server is stopped"); - result } @@ -303,31 +308,27 @@ 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(&format!("http://{}", config.grpc_bind_address)) + .map_err(|e| format!("invalid url: {e}"))?; + + // Connect to the gRPC server + let mut client = PlatformClient::connect(uri.clone()) + .await + .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 { + 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| 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() }; @@ -442,15 +443,14 @@ 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::{ 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 dpp::version::PlatformVersion; use drive::drive::credit_pools::epochs::paths::EpochProposers; use drive_abci::logging::LogLevel;