Skip to content

Commit

Permalink
Add JSON output for template initialization from non-tty env
Browse files Browse the repository at this point in the history
  • Loading branch information
Rexagon committed Apr 5, 2023
1 parent 8e91d17 commit 8f51a86
Show file tree
Hide file tree
Showing 8 changed files with 379 additions and 103 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# 0.2.3 (2023-04-05)

### Added

- JSON output for template initialization from non-tty environment.

### Changed

- Intermediate messages are now printed to the `stderr`.

# 0.2.2 (2023-03-27)

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "nodekeeper"
description = "All-in-one node management tool."
version = "0.2.2"
version = "0.2.3"
authors = ["Ivan Kalinin <i.kalinin@dexpa.io>"]
repository = "https://github.com/broxus/nodekeeper"
edition = "2021"
Expand Down
174 changes: 131 additions & 43 deletions src/cli/init/contracts.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use std::path::Path;
use std::path::{Path, PathBuf};
use std::str::FromStr;

use anyhow::{Context, Result};
use argh::FromArgs;
use broxus_util::serde_string;
use console::style;
use dialoguer::theme::Theme;
use dialoguer::{Input, Select};
use nekoton_utils::serde_address;
use serde::Serialize;

use super::{Template, TemplateValidator, TemplateValidatorDePool, TemplateValidatorSingle};
use crate::cli::{CliContext, ProjectDirs};
Expand All @@ -31,13 +34,13 @@ impl Cmd {
theme: &dyn Theme,
ctx: &CliContext,
template: &Option<Template>,
) -> Result<()> {
) -> Result<Option<Output>> {
let template = match template {
Some(template) => match &template.validator {
Some(validator) => Some(validator),
None => {
println!("`validator` info is empty in the provided template");
return Ok(());
eprintln!("`validator` info is empty in the provided template");
return Ok(None);
}
},
None => None,
Expand All @@ -52,7 +55,7 @@ impl Cmd {
}

// Check whether validation was already configured
if config.validator.is_some() {
if let Some(validator) = &config.validator {
let overwrite = match template {
Some(TemplateValidator::Single(t)) => t.overwrite,
Some(TemplateValidator::DePool(t)) => t.overwrite,
Expand All @@ -63,44 +66,91 @@ impl Cmd {
)?,
};
if !overwrite {
return Ok(());
return Ok(Some(Output::from_existing(dirs, validator)));
}

if template.is_some() && overwrite {
println!("Overwriting validator config");
eprintln!("Overwriting validator config");
}
}

match template {
Some(TemplateValidator::Single(template)) => {
prepare_single_validator(theme, dirs, Some(template), &mut config)
}
Some(TemplateValidator::DePool(template)) => {
prepare_depool_validator(theme, dirs, Some(template), &mut config)
}
// Select validator type
None => match Select::with_theme(theme)
.with_prompt("Select validator type")
.item("Single")
.item("DePool")
.default(0)
.interact()?
{
// Prepare validator as a single node
0 => prepare_single_validator(theme, dirs, None, &mut config),
// Prepare validator as a depool
_ => prepare_depool_validator(theme, dirs, None, &mut config),
},
let output =
match template {
Some(TemplateValidator::Single(template)) => {
prepare_single_validator(theme, dirs, Some(template), &mut config)
.map(Output::Single)?
}
Some(TemplateValidator::DePool(template)) => {
prepare_depool_validator(theme, dirs, Some(template), &mut config)
.map(Output::DePool)?
}
// Select validator type
None => match Select::with_theme(theme)
.with_prompt("Select validator type")
.item("Single")
.item("DePool")
.default(0)
.interact()?
{
// Prepare validator as a single node
0 => prepare_single_validator(theme, dirs, None, &mut config)
.map(Output::Single)?,
// Prepare validator as a depool
_ => prepare_depool_validator(theme, dirs, None, &mut config)
.map(Output::DePool)?,
},
};

Ok(Some(output))
}
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "lowercase", tag = "type")]
pub enum Output {
Single(OutputSingle),
DePool(OutputDePool),
}

impl Output {
fn from_existing(dirs: &ProjectDirs, validator: &AppConfigValidator) -> Self {
match validator {
AppConfigValidator::Single(single) => Self::Single(OutputSingle {
validator_wallet: single.address.clone(),
target_balance: compute_target_balance_for_single(single.stake_per_round),
validator_wallet_keys_path: dirs.validator_keys.clone(),
}),
AppConfigValidator::DePool(depool) => Self::DePool(OutputDePool {
validator_wallet: depool.owner.clone(),
depool: depool.depool.clone(),
target_balance: compute_target_balance_for_depool(
depool
.deploy
.as_ref()
.map(|deploy| deploy.validator_assurance),
),
validator_wallet_keys_path: dirs.validator_keys.clone(),
depool_keys_path: dirs.depool_keys.clone(),
}),
}
}
}

#[derive(Debug, Serialize)]
pub struct OutputSingle {
#[serde(with = "serde_address")]
pub validator_wallet: ton_block::MsgAddressInt,
#[serde(with = "serde_string")]
pub target_balance: u128,
pub validator_wallet_keys_path: PathBuf,
}

fn prepare_single_validator(
theme: &dyn Theme,
dirs: &ProjectDirs,
template: Option<&TemplateValidatorSingle>,
app_config: &mut AppConfig,
) -> Result<()> {
) -> Result<OutputSingle> {
use crate::contracts::*;

const MIN_STAKE: u64 = 10_000 * ONE_EVER as u64;
Expand Down Expand Up @@ -173,12 +223,12 @@ fn prepare_single_validator(
// Done
steps.next("Validator configured successfully. Great!");

let target_balance = stake_per_round as u128 * 2 + Wallet::INITIAL_BALANCE;
let target_balance = compute_target_balance_for_single(stake_per_round);

println!(
eprintln!(
"\n{}\n{}\n\n{} {}{}\n\n{}\n{}",
style("Validator wallet address:").green().bold(),
style(wallet_address).bold(),
style(&wallet_address).bold(),
style("Required validator wallet balance:").green().bold(),
style(format!("{} {currency}", Tokens(target_balance))).bold(),
style(format!(
Expand All @@ -192,15 +242,35 @@ fn prepare_single_validator(
style(dirs.validator_keys.display()).bold()
);

Ok(())
Ok(OutputSingle {
validator_wallet: wallet_address,
target_balance,
validator_wallet_keys_path: dirs.validator_keys.clone(),
})
}

fn compute_target_balance_for_single(stake_per_round: u64) -> u128 {
Wallet::INITIAL_BALANCE + stake_per_round as u128 * 2
}

#[derive(Debug, Serialize)]
pub struct OutputDePool {
#[serde(with = "serde_address")]
pub validator_wallet: ton_block::MsgAddressInt,
#[serde(with = "serde_address")]
pub depool: ton_block::MsgAddressInt,
#[serde(with = "serde_string")]
pub target_balance: u128,
pub validator_wallet_keys_path: PathBuf,
pub depool_keys_path: PathBuf,
}

fn prepare_depool_validator(
theme: &dyn Theme,
dirs: &ProjectDirs,
template: Option<&TemplateValidatorDePool>,
app_config: &mut AppConfig,
) -> Result<()> {
) -> Result<OutputDePool> {
use crate::contracts::*;

let currency = &app_config.currency().to_owned();
Expand All @@ -225,20 +295,23 @@ fn prepare_depool_validator(
// Done
steps.next("Everything is ready for the validation!");

println!(
let target_balance = compute_target_balance_for_depool(
params
.deploy
.as_ref()
.map(|deploy| deploy.validator_assurance),
);

eprintln!(
"\n{}\n{}\n\n{}\n{}",
style("Validator wallet address:").green().bold(),
style(params.owner).bold(),
style(&params.owner).bold(),
style("DePool address:").green().bold(),
style(params.depool).bold(),
style(&params.depool).bold(),
);

if let Some(deployment) = params.deploy {
let target_balance = deployment.validator_assurance as u128 * 2
+ Wallet::INITIAL_BALANCE
+ DePool::INITIAL_BALANCE;

println!(
eprintln!(
"\n{} {}{}",
style("Required validator wallet balance:").green().bold(),
style(format!("{} {currency}", Tokens(target_balance))).bold(),
Expand All @@ -254,14 +327,29 @@ fn prepare_depool_validator(
);
}

println!(
eprintln!(
"\n{}\n{}\n{}",
style("Make sure you back up your keys:").yellow().bold(),
style(dirs.validator_keys.display()).bold(),
style(dirs.depool_keys.display()).bold(),
);

Ok(())
Ok(OutputDePool {
validator_wallet: params.owner,
depool: params.depool,
target_balance,
validator_wallet_keys_path: dirs.validator_keys.clone(),
depool_keys_path: dirs.depool_keys.clone(),
})
}

fn compute_target_balance_for_depool(validator_assurance: Option<u64>) -> u128 {
match validator_assurance {
Some(assurance) => {
Wallet::INITIAL_BALANCE + DePool::INITIAL_BALANCE + assurance as u128 * 2
}
None => Wallet::INITIAL_BALANCE,
}
}

fn prepare_new_depool(
Expand Down
34 changes: 29 additions & 5 deletions src/cli/init/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use serde::{Deserialize, Serialize};
use super::{CliContext, ProjectDirs};
use crate::config::{AppConfig, AppConfigDePoolDeploymentParams, DePoolType, NodeConfig};
use crate::defaults;
use crate::util::{is_terminal, print_output};

mod contracts;
mod node;
Expand Down Expand Up @@ -53,21 +54,44 @@ impl Cmd {
None => {
let template = load_template(self.template)?;

node::Cmd {
let node = node::Cmd {
rebuild: self.rebuild,
}
.run(theme, &ctx, &template)
.await?;
println!();
contracts::Cmd {}.run(theme, &ctx, &template).await

let contracts = contracts::Cmd {}.run(theme, &ctx, &template).await?;

if template.is_some() && !is_terminal() {
print_output(serde_json::json!({
"node": node,
"contracts": contracts,
}));
}

Ok(())
}
Some(SubCmd::Node(cmd)) => {
let template = load_template(self.template)?;
cmd.run(theme, &ctx, &template).await

let node = cmd.run(theme, &ctx, &template).await?;

if template.is_some() && !is_terminal() {
print_output(serde_json::to_value(node).unwrap());
}

Ok(())
}
Some(SubCmd::Contracts(cmd)) => {
let template = load_template(self.template)?;
cmd.run(theme, &ctx, &template).await

let contracts = cmd.run(theme, &ctx, &template).await?;

if template.is_some() && !is_terminal() {
print_output(serde_json::to_value(contracts).unwrap());
}

Ok(())
}
#[cfg(not(feature = "packaged"))]
Some(SubCmd::Systemd(cmd)) => {
Expand Down
Loading

0 comments on commit 8f51a86

Please # to comment.