Skip to content

Commit

Permalink
Implement
Browse files Browse the repository at this point in the history
Signed-off-by: s8sato <49983831+s8sato@users.noreply.github.com>
  • Loading branch information
s8sato committed Jan 30, 2022
1 parent d252f01 commit bc4ddd1
Show file tree
Hide file tree
Showing 6 changed files with 288 additions and 16 deletions.
5 changes: 4 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion core/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,6 @@ impl ValidBlock {
///
/// # Panics
/// If generating keys or block signing fails.
#[cfg(test)]
#[allow(clippy::restriction)]
pub fn new_dummy() -> Self {
Self {
Expand Down
7 changes: 6 additions & 1 deletion tools/kura_inspector/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ edition = "2021"

[dependencies]
iroha_core = { path = "../../core" }
iroha_version = { path = "../../version" }
iroha_config = { path = "../../config" }

clap = { version = "3.0", features = ["derive"] }
futures-util = "0.3"
tokio = { version = "1.6.0", features = ["rt"]}

[dev-dependencies]
tempfile = "3"
81 changes: 80 additions & 1 deletion tools/kura_inspector/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,80 @@
use iroha_core::kura::BlockStore;
//! General objects independent from executables.

use std::path::Path;

use iroha_config::Configurable;
use iroha_core::kura;

pub mod print;

#[allow(missing_docs)]
#[derive(Clone, Copy)]
pub enum Config {
Print(print::Config),
}

/// Where to write the results of the inspection.
pub struct Output<T, E>
where
T: std::io::Write + Send,
E: std::io::Write + Send,
{
/// Writer for valid data
pub ok: T,
/// Writer for invalid data
pub err: E,
}

impl Config {
/// Configure [`kura::BlockStore`] and route to the subcommand.
///
/// # Errors
/// Fails if
/// 1. Fails to configure [`kura::BlockStore`].
/// 2. Fails to run the subcommand.
pub async fn run<T, E>(&self, output: &mut Output<T, E>) -> Result<(), Error>
where
T: std::io::Write + Send,
E: std::io::Write + Send,
{
let block_store = block_store().await?;
match self {
Self::Print(cfg) => cfg.run(&block_store, output).await.map_err(Error::Print)?,
}
Ok(())
}
}

async fn block_store() -> Result<kura::BlockStore<kura::DefaultIO>, Error> {
let mut kura_config = kura::config::KuraConfiguration::default();
kura_config.load_environment().map_err(Error::KuraConfig)?;
kura::BlockStore::new(
Path::new(&kura_config.block_store_path),
kura_config.blocks_per_storage_file,
kura::DefaultIO,
)
.await
.map_err(Error::SetBlockStore)
}

/// [`Output`] for CLI use.
pub type DefaultOutput = Output<std::io::Stdout, std::io::Stderr>;

impl DefaultOutput {
/// Construct [`DefaultOutput`].
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self {
ok: std::io::stdout(),
err: std::io::stderr(),
}
}
}

#[derive(Debug)]
#[allow(missing_docs)]
pub enum Error {
KuraConfig(iroha_config::derive::Error),
SetBlockStore(kura::Error),
Print(print::Error),
}
34 changes: 22 additions & 12 deletions tools/kura_inspector/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
use std::path::PathBuf;

use clap::{Parser, Subcommand};
use kura_inspector::{print, Config, DefaultOutput};

/// Kura inspector
#[derive(Parser)]
#[clap(version = env!("CARGO_PKG_VERSION"), author = env!("CARGO_PKG_AUTHORS"))]
struct Opts {
/// Height of the block up to which exclude from the inspection.
/// Defaults to the previous one from the current top
/// Defaults to exclude the all except the latest block
#[clap(short, long, name = "BLOCK_HEIGHT")]
skip_to: Option<u64>,
/// Find blocks whose data collapsed
#[clap(long)]
scan: bool,
skip_to: Option<usize>,
#[clap(subcommand)]
command: Command,
}
Expand All @@ -24,13 +20,27 @@ enum Command {
/// Number of the blocks to print.
/// The excess will be truncated
#[clap(short = 'n', long, default_value_t = 1)]
length: u64,
length: usize,
},
/// Listen for additions to the storage and report it
Follow,
}

fn main() {
#[tokio::main]
#[allow(clippy::use_debug, clippy::print_stderr)]
async fn main() {
let opts = Opts::parse();
// kura_inspector::run()
let mut output = DefaultOutput::new();
Config::from(opts)
.run(&mut output)
.await
.unwrap_or_else(|e| eprintln!("{:?}", e))
}

impl From<Opts> for Config {
fn from(src: Opts) -> Self {
let Opts { skip_to, command } = src;

match command {
Command::Print { length } => Config::Print(print::Config { skip_to, length }),
}
}
}
176 changes: 176 additions & 0 deletions tools/kura_inspector/src/print.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
//! Objects for the `print` subcommand.

use futures_util::StreamExt;
use iroha_core::{kura, prelude::VersionedCommittedBlock};

use crate::Output;

/// Configuration for the `print` subcommand.
#[derive(Clone, Copy)]
pub struct Config {
/// Height of the block up to which exclude from the printing.
/// `None` means excluding the all except the latest block.
pub skip_to: Option<usize>,
/// Number of the blocks to print.
/// The excess will be truncated.
pub length: usize,
}

impl Config {
/// Read `block_store` and print the results to `output`.
///
/// # Errors
/// Fails if
/// 1. Fails to read `block_store`.
/// 2. Fails to print to `output`.
/// 3. Tries to print the latest block and there is none.
pub async fn run<T, E>(
&self,
block_store: &kura::BlockStore<kura::DefaultIO>,
output: &mut Output<T, E>,
) -> Result<(), Error>
where
T: std::io::Write + Send,
E: std::io::Write + Send,
{
let block_stream = block_store
.read_all()
.await
.map_err(Box::new)
.map_err(Error::ReadBlockStore)?;
tokio::pin!(block_stream);

if let Some(height) = self.skip_to {
let mut block_stream = block_stream.skip(height).take(self.length);
while let Some(block_result) = block_stream.next().await {
output.print(block_result).map_err(Error::Output)?
}
} else {
let mut last;
match block_stream.next().await {
Some(block_result) => {
last = block_result;
}
None => return Err(Error::NoBlock),
}
while let Some(block_result) = block_stream.next().await {
last = block_result;
}
output.print(last).map_err(Error::Output)?
}
Ok(())
}
}

impl<T, E> Output<T, E>
where
T: std::io::Write + Send,
E: std::io::Write + Send,
{
#[allow(clippy::use_debug)]
fn print(
&mut self,
block_result: Result<VersionedCommittedBlock, kura::Error>,
) -> Result<(), std::io::Error> {
match block_result {
Ok(block) => writeln!(self.ok, "{:#?}", block),
Err(error) => writeln!(self.err, "{:#?}", error),
}
}
}

#[derive(Debug)]
#[allow(missing_docs)]
pub enum Error {
ReadBlockStore(Box<kura::Error>),
Output(std::io::Error),
NoBlock,
}

#[cfg(test)]
#[allow(clippy::restriction)]
mod tests {
use std::io::Write;

use iroha_core::prelude::ValidBlock;

use super::*;

type TestOutput = Output<Vec<u8>, Vec<u8>>;
const BLOCKS_PER_FILE: u64 = 3;

impl TestOutput {
fn new() -> Self {
Self {
ok: Vec::new(),
err: Vec::new(),
}
}
}

async fn block_store(dir: &tempfile::TempDir) -> kura::BlockStore<kura::DefaultIO> {
kura::BlockStore::new(
dir.path(),
std::num::NonZeroU64::new(BLOCKS_PER_FILE).unwrap(),
kura::DefaultIO,
)
.await
.unwrap()
}

#[tokio::test]
/// Confirm that `print` command defaults to print the latest block.
async fn test_print_default() {
const BLOCK_COUNT: usize = 10;

let dir = tempfile::tempdir().unwrap();
let brock_store = block_store(&dir).await;
let mut output = TestOutput::new();

let mut tester = Vec::new();
for height in 1..=BLOCK_COUNT {
let mut block: VersionedCommittedBlock = ValidBlock::new_dummy().commit().into();
block.as_mut_v1().header.height = height as u64;
if BLOCK_COUNT == height {
writeln!(tester, "{:#?}", block).unwrap()
}
brock_store.write(&block).await.unwrap();
}
let cfg = Config {
skip_to: None,
length: 1,
};
cfg.run(&brock_store, &mut output).await.unwrap();

assert_eq!(tester, output.ok)
}

#[tokio::test]
/// Confirm that `skip_to` and `length` options work properly.
async fn test_print_range() {
const BLOCK_COUNT: usize = 10;
const SKIP_TO: usize = 2;
const LENGTH: usize = 5;

let dir = tempfile::tempdir().unwrap();
let brock_store = block_store(&dir).await;
let mut output = TestOutput::new();

let mut tester = Vec::new();
for height in 1..=BLOCK_COUNT {
let mut block: VersionedCommittedBlock = ValidBlock::new_dummy().commit().into();
block.as_mut_v1().header.height = height as u64;
if (SKIP_TO + 1..=SKIP_TO + LENGTH).contains(&height) {
writeln!(tester, "{:#?}", block).unwrap()
}
brock_store.write(&block).await.unwrap();
}
let cfg = Config {
skip_to: Some(SKIP_TO),
length: LENGTH,
};
cfg.run(&brock_store, &mut output).await.unwrap();

assert_eq!(tester, output.ok)
}
}

0 comments on commit bc4ddd1

Please sign in to comment.