Health endpoint (#9847)
* Health endpoint. * Fix chain handling. * Update to latest json-rpc. * Change dev chain detection. * Fix test.
This commit is contained in:
parent
f6dcca3ebb
commit
f680eacdf2
@ -471,6 +471,10 @@ impl Configuration {
|
|||||||
Ok(name.parse()?)
|
Ok(name.parse()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_dev_chain(&self) -> Result<bool, String> {
|
||||||
|
Ok(self.chain()? == SpecType::Dev)
|
||||||
|
}
|
||||||
|
|
||||||
fn max_peers(&self) -> u32 {
|
fn max_peers(&self) -> u32 {
|
||||||
self.args.arg_max_peers
|
self.args.arg_max_peers
|
||||||
.or(cmp::max(self.args.arg_min_peers, Some(DEFAULT_MAX_PEERS)))
|
.or(cmp::max(self.args.arg_min_peers, Some(DEFAULT_MAX_PEERS)))
|
||||||
@ -528,7 +532,7 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn miner_options(&self) -> Result<MinerOptions, String> {
|
fn miner_options(&self) -> Result<MinerOptions, String> {
|
||||||
let is_dev_chain = self.chain()? == SpecType::Dev;
|
let is_dev_chain = self.is_dev_chain()?;
|
||||||
if is_dev_chain && self.args.flag_force_sealing && self.args.arg_reseal_min_period == 0 {
|
if is_dev_chain && self.args.flag_force_sealing && self.args.arg_reseal_min_period == 0 {
|
||||||
return Err("Force sealing can't be used with reseal_min_period = 0".into());
|
return Err("Force sealing can't be used with reseal_min_period = 0".into());
|
||||||
}
|
}
|
||||||
@ -922,6 +926,7 @@ impl Configuration {
|
|||||||
Ok(NetworkSettings {
|
Ok(NetworkSettings {
|
||||||
name: self.args.arg_identity.clone(),
|
name: self.args.arg_identity.clone(),
|
||||||
chain: format!("{}", self.chain()?),
|
chain: format!("{}", self.chain()?),
|
||||||
|
is_dev_chain: self.is_dev_chain()?,
|
||||||
network_port: net_addresses.0.port(),
|
network_port: net_addresses.0.port(),
|
||||||
rpc_enabled: http_conf.enabled,
|
rpc_enabled: http_conf.enabled,
|
||||||
rpc_interface: http_conf.interface,
|
rpc_interface: http_conf.interface,
|
||||||
@ -1522,6 +1527,7 @@ mod tests {
|
|||||||
assert_eq!(conf.network_settings(), Ok(NetworkSettings {
|
assert_eq!(conf.network_settings(), Ok(NetworkSettings {
|
||||||
name: "testname".to_owned(),
|
name: "testname".to_owned(),
|
||||||
chain: "kovan".to_owned(),
|
chain: "kovan".to_owned(),
|
||||||
|
is_dev_chain: false,
|
||||||
network_port: 30303,
|
network_port: 30303,
|
||||||
rpc_enabled: true,
|
rpc_enabled: true,
|
||||||
rpc_interface: "127.0.0.1".to_owned(),
|
rpc_interface: "127.0.0.1".to_owned(),
|
||||||
|
@ -338,6 +338,7 @@ impl FullDependencies {
|
|||||||
self.settings.clone(),
|
self.settings.clone(),
|
||||||
signer,
|
signer,
|
||||||
self.ws_address.clone(),
|
self.ws_address.clone(),
|
||||||
|
self.snapshot.clone().into(),
|
||||||
).to_delegate());
|
).to_delegate());
|
||||||
|
|
||||||
if !for_generic_pubsub {
|
if !for_generic_pubsub {
|
||||||
|
@ -150,6 +150,7 @@ pub fn start_http<M, S, H, T>(
|
|||||||
.threads(threads)
|
.threads(threads)
|
||||||
.cors(cors_domains.into())
|
.cors(cors_domains.into())
|
||||||
.allowed_hosts(allowed_hosts.into())
|
.allowed_hosts(allowed_hosts.into())
|
||||||
|
.health_api(("/api/health", "parity_nodeStatus"))
|
||||||
.max_request_body_size(max_payload * 1024 * 1024)
|
.max_request_body_size(max_payload * 1024 * 1024)
|
||||||
.start_http(addr)?)
|
.start_http(addr)?)
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,7 @@ mod codes {
|
|||||||
pub const ENCODING_ERROR: i64 = -32058;
|
pub const ENCODING_ERROR: i64 = -32058;
|
||||||
pub const FETCH_ERROR: i64 = -32060;
|
pub const FETCH_ERROR: i64 = -32060;
|
||||||
pub const NO_LIGHT_PEERS: i64 = -32065;
|
pub const NO_LIGHT_PEERS: i64 = -32065;
|
||||||
|
pub const NO_PEERS: i64 = -32066;
|
||||||
pub const DEPRECATED: i64 = -32070;
|
pub const DEPRECATED: i64 = -32070;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,3 +501,15 @@ pub fn on_demand_others(err: &OnDemandError) -> Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn status_error(has_peers: bool) -> Error {
|
||||||
|
if has_peers {
|
||||||
|
no_work()
|
||||||
|
} else {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(codes::NO_PEERS),
|
||||||
|
message: "Node is not connected to any peers.".into(),
|
||||||
|
data: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,8 @@ pub struct NetworkSettings {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
/// Name of the chain we are connected to
|
/// Name of the chain we are connected to
|
||||||
pub chain: String,
|
pub chain: String,
|
||||||
|
/// Is development chain
|
||||||
|
pub is_dev_chain: bool,
|
||||||
/// Networking port
|
/// Networking port
|
||||||
pub network_port: u16,
|
pub network_port: u16,
|
||||||
/// Is JSON-RPC server enabled?
|
/// Is JSON-RPC server enabled?
|
||||||
@ -38,6 +40,7 @@ impl Default for NetworkSettings {
|
|||||||
NetworkSettings {
|
NetworkSettings {
|
||||||
name: "".into(),
|
name: "".into(),
|
||||||
chain: "foundation".into(),
|
chain: "foundation".into(),
|
||||||
|
is_dev_chain: false,
|
||||||
network_port: 30303,
|
network_port: 30303,
|
||||||
rpc_enabled: true,
|
rpc_enabled: true,
|
||||||
rpc_interface: "127.0.0.1".into(),
|
rpc_interface: "127.0.0.1".into(),
|
||||||
|
@ -414,4 +414,15 @@ impl Parity for ParityClient {
|
|||||||
fn submit_work_detail(&self, _nonce: H64, _pow_hash: H256, _mix_hash: H256) -> Result<H256> {
|
fn submit_work_detail(&self, _nonce: H64, _pow_hash: H256, _mix_hash: H256) -> Result<H256> {
|
||||||
Err(errors::light_unimplemented(None))
|
Err(errors::light_unimplemented(None))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn status(&self) -> Result<()> {
|
||||||
|
let has_peers = self.settings.is_dev_chain || self.light_dispatch.sync.peer_numbers().connected > 0;
|
||||||
|
let is_importing = self.light_dispatch.sync.is_major_importing();
|
||||||
|
|
||||||
|
if has_peers && !is_importing {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(errors::status_error(has_peers))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,13 +30,16 @@ use ethcore::account_provider::AccountProvider;
|
|||||||
use ethcore::client::{BlockChainClient, StateClient, Call};
|
use ethcore::client::{BlockChainClient, StateClient, Call};
|
||||||
use ethcore::ids::BlockId;
|
use ethcore::ids::BlockId;
|
||||||
use ethcore::miner::{self, MinerService};
|
use ethcore::miner::{self, MinerService};
|
||||||
|
use ethcore::snapshot::{SnapshotService, RestorationStatus};
|
||||||
use ethcore::state::StateInfo;
|
use ethcore::state::StateInfo;
|
||||||
use ethcore_logger::RotatingLogger;
|
use ethcore_logger::RotatingLogger;
|
||||||
use updater::{Service as UpdateService};
|
use updater::{Service as UpdateService};
|
||||||
use jsonrpc_core::{BoxFuture, Result};
|
use jsonrpc_core::{BoxFuture, Result};
|
||||||
use jsonrpc_core::futures::future;
|
use jsonrpc_core::futures::future;
|
||||||
use jsonrpc_macros::Trailing;
|
use jsonrpc_macros::Trailing;
|
||||||
|
|
||||||
use v1::helpers::{self, errors, fake_sign, ipfs, SigningQueue, SignerService, NetworkSettings};
|
use v1::helpers::{self, errors, fake_sign, ipfs, SigningQueue, SignerService, NetworkSettings};
|
||||||
|
use v1::helpers::block_import::is_major_importing;
|
||||||
use v1::metadata::Metadata;
|
use v1::metadata::Metadata;
|
||||||
use v1::traits::Parity;
|
use v1::traits::Parity;
|
||||||
use v1::types::{
|
use v1::types::{
|
||||||
@ -62,6 +65,7 @@ pub struct ParityClient<C, M, U> {
|
|||||||
settings: Arc<NetworkSettings>,
|
settings: Arc<NetworkSettings>,
|
||||||
signer: Option<Arc<SignerService>>,
|
signer: Option<Arc<SignerService>>,
|
||||||
ws_address: Option<Host>,
|
ws_address: Option<Host>,
|
||||||
|
snapshot: Option<Arc<SnapshotService>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, M, U> ParityClient<C, M, U> where
|
impl<C, M, U> ParityClient<C, M, U> where
|
||||||
@ -79,6 +83,7 @@ impl<C, M, U> ParityClient<C, M, U> where
|
|||||||
settings: Arc<NetworkSettings>,
|
settings: Arc<NetworkSettings>,
|
||||||
signer: Option<Arc<SignerService>>,
|
signer: Option<Arc<SignerService>>,
|
||||||
ws_address: Option<Host>,
|
ws_address: Option<Host>,
|
||||||
|
snapshot: Option<Arc<SnapshotService>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
ParityClient {
|
ParityClient {
|
||||||
client,
|
client,
|
||||||
@ -91,6 +96,7 @@ impl<C, M, U> ParityClient<C, M, U> where
|
|||||||
settings,
|
settings,
|
||||||
signer,
|
signer,
|
||||||
ws_address,
|
ws_address,
|
||||||
|
snapshot,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -481,4 +487,21 @@ impl<C, M, U, S> Parity for ParityClient<C, M, U> where
|
|||||||
fn submit_work_detail(&self, nonce: H64, pow_hash: H256, mix_hash: H256) -> Result<H256> {
|
fn submit_work_detail(&self, nonce: H64, pow_hash: H256, mix_hash: H256) -> Result<H256> {
|
||||||
helpers::submit_work_detail(&self.client, &self.miner, nonce, pow_hash, mix_hash)
|
helpers::submit_work_detail(&self.client, &self.miner, nonce, pow_hash, mix_hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn status(&self) -> Result<()> {
|
||||||
|
let has_peers = self.settings.is_dev_chain || self.sync.status().num_peers > 0;
|
||||||
|
let is_warping = match self.snapshot.as_ref().map(|s| s.status()) {
|
||||||
|
Some(RestorationStatus::Ongoing { .. }) => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
let is_not_syncing =
|
||||||
|
!is_warping &&
|
||||||
|
!is_major_importing(Some(self.sync.status().state), self.client.queue_info());
|
||||||
|
|
||||||
|
if has_peers && is_not_syncing {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(errors::status_error(has_peers))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@ impl Dependencies {
|
|||||||
settings: Arc::new(NetworkSettings {
|
settings: Arc::new(NetworkSettings {
|
||||||
name: "mynode".to_owned(),
|
name: "mynode".to_owned(),
|
||||||
chain: "testchain".to_owned(),
|
chain: "testchain".to_owned(),
|
||||||
|
is_dev_chain: false,
|
||||||
network_port: 30303,
|
network_port: 30303,
|
||||||
rpc_enabled: true,
|
rpc_enabled: true,
|
||||||
rpc_interface: "all".to_owned(),
|
rpc_interface: "all".to_owned(),
|
||||||
@ -83,6 +84,7 @@ impl Dependencies {
|
|||||||
self.settings.clone(),
|
self.settings.clone(),
|
||||||
signer,
|
signer,
|
||||||
self.ws_address.clone(),
|
self.ws_address.clone(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -552,3 +554,53 @@ fn rpc_parity_block_receipts() {
|
|||||||
|
|
||||||
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
|
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_status_ok() {
|
||||||
|
let deps = Dependencies::new();
|
||||||
|
let io = deps.default_client();
|
||||||
|
|
||||||
|
let request = r#"{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "parity_nodeStatus",
|
||||||
|
"params": [],
|
||||||
|
"id": 1
|
||||||
|
}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#;
|
||||||
|
|
||||||
|
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_status_error_peers() {
|
||||||
|
let deps = Dependencies::new();
|
||||||
|
deps.sync.status.write().num_peers = 0;
|
||||||
|
let io = deps.default_client();
|
||||||
|
|
||||||
|
let request = r#"{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "parity_nodeStatus",
|
||||||
|
"params": [],
|
||||||
|
"id": 1
|
||||||
|
}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","error":{"code":-32066,"message":"Node is not connected to any peers."},"id":1}"#;
|
||||||
|
|
||||||
|
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_status_error_sync() {
|
||||||
|
let deps = Dependencies::new();
|
||||||
|
deps.sync.status.write().state = ::sync::SyncState::Blocks;
|
||||||
|
let io = deps.default_client();
|
||||||
|
|
||||||
|
let request = r#"{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "parity_nodeStatus",
|
||||||
|
"params": [],
|
||||||
|
"id": 1
|
||||||
|
}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","error":{"code":-32001,"message":"Still syncing."},"id":1}"#;
|
||||||
|
|
||||||
|
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
@ -227,5 +227,15 @@ build_rpc_trait! {
|
|||||||
/// but returns block hash on success, and returns an explicit error message on failure).
|
/// but returns block hash on success, and returns an explicit error message on failure).
|
||||||
#[rpc(name = "parity_submitWorkDetail")]
|
#[rpc(name = "parity_submitWorkDetail")]
|
||||||
fn submit_work_detail(&self, H64, H256, H256) -> Result<H256>;
|
fn submit_work_detail(&self, H64, H256, H256) -> Result<H256>;
|
||||||
|
|
||||||
|
/// Returns the status of the node. Used as the health endpoint.
|
||||||
|
///
|
||||||
|
/// The RPC returns successful response if:
|
||||||
|
/// - The node have a peer (unless running a dev chain)
|
||||||
|
/// - The node is not syncing.
|
||||||
|
///
|
||||||
|
/// Otherwise the RPC returns error.
|
||||||
|
#[rpc(name = "parity_nodeStatus")]
|
||||||
|
fn status(&self) -> Result<()>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user