Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(watcher): validator watcher cli and config #1114

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions 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
Expand Up @@ -56,7 +56,7 @@ members = [
"utilities/tariswap_test_bench",
"utilities/transaction_submitter",
"utilities/transaction_submitter",
"utilities/generate_ristretto_value_lookup",
"utilities/generate_ristretto_value_lookup", "applications/tari_watcher",
]
resolver = "2"

Expand Down
20 changes: 20 additions & 0 deletions applications/tari_watcher/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "tari_watcher"
version.workspace = true
edition.workspace = true
authors.workspace = true
repository.workspace = true
license.workspace = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
#tari_common = { workspace = true }

clap = { workspace = true, features = ["derive"] }
serde = { workspace = true, features = ["derive"] }
anyhow = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "macros", "signal", "process", "time", "fs", "io-util"] }
log = { workspace = true }

toml = "0.8.12"
60 changes: 60 additions & 0 deletions applications/tari_watcher/src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2024 The Tari Project
// SPDX-License-Identifier: BSD-3-Clause

use std::path::PathBuf;

use clap::Parser;

use crate::config::Config;

#[derive(Clone, Debug, Parser)]
pub struct Cli {
#[clap(flatten)]
pub common: CommonCli,
#[clap(subcommand)]
pub command: Commands,
}

impl Cli {
pub fn init() -> Self {
Self::parse()
}

pub fn get_config_path(&self) -> PathBuf {
let Some(ref base_dir) = self.common.base_dir else {
return self.common.config_path.clone();
};
if self.common.config_path.is_relative() {
base_dir.join(&self.common.config_path)
} else {
self.common.config_path.clone()
}
}
}

#[derive(Debug, Clone, clap::Args)]
pub struct CommonCli {
#[clap(short = 'b', long, parse(from_os_str))]
pub base_dir: Option<PathBuf>,
#[clap(short = 'c', long, parse(from_os_str), default_value = "./data/watcher/config.toml")]
pub config_path: PathBuf,
}

#[derive(Clone, Debug, clap::Subcommand)]
pub enum Commands {
Init(InitArgs),
Start,
}

#[derive(Clone, Debug, clap::Args)]
pub struct InitArgs {
#[clap(long)]
/// Disable initial and auto registration of the validator node
pub no_auto_register: bool,
}

impl InitArgs {
pub fn apply(&self, config: &mut Config) {
config.auto_register = !self.no_auto_register;
}
}
180 changes: 180 additions & 0 deletions applications/tari_watcher/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// Copyright 2024 The Tari Project
// SPDX-License-Identifier: BSD-3-Clause

use std::{
collections::HashMap,
fmt::{self, Display},
path::PathBuf,
};

use tokio::io::{self, AsyncWriteExt};

use crate::Cli;

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Config {
/// Allow watcher to submit a new validator node registration transaction initially and before
/// the current registration expires
pub auto_register: bool,

/// The Minotari node gRPC address
pub base_node_grpc_address: String,

/// The Minotari console wallet gRPC address
pub base_wallet_grpc_address: String,

/// The path of the validator node registration file, containing signed information required to
/// submit a registration transaction on behalf of the node
pub vn_registration_file: PathBuf,

/// The sidechain ID to use. If not provided, the default Tari sidechain ID will be used.
pub sidechain_id: Option<String>,

/// The configuration for managing one or multiple processes
pub instance_config: Vec<InstanceConfig>,

/// The process specific configuration for the executables
pub executable_config: Vec<ExecutableConfig>,

/// The channel configuration for alerting and monitoring
pub channel_config: Vec<ChannelConfig>,
}

impl Config {
pub(crate) async fn write<W: io::AsyncWrite + Unpin>(&self, mut writer: W) -> anyhow::Result<()> {
let toml = toml::to_string_pretty(self)?;
writer.write_all(toml.as_bytes()).await?;
Ok(())
}
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum InstanceType {
TariValidatorNode,
MinoTariConsoleWallet,
}

impl Display for InstanceType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ChannelConfig {
pub name: String,
pub enabled: bool,
pub credentials: String,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ExecutableConfig {
pub instance_type: InstanceType,
pub executable_path: Option<PathBuf>,
pub compile: Option<CompileConfig>,
pub env: Vec<(String, String)>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CompileConfig {
pub working_dir: Option<PathBuf>,
pub package_name: String,
pub target_dir: Option<PathBuf>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct InstanceConfig {
pub name: String,
pub instance_type: InstanceType,
pub num_instances: u32,
#[serde(alias = "extra_args")]
pub settings: HashMap<String, String>,
}

impl InstanceConfig {
pub fn new(instance_type: InstanceType) -> Self {
Self {
name: instance_type.to_string(),
instance_type,
num_instances: 1,
settings: HashMap::new(),
}
}

pub fn with_name<S: Into<String>>(mut self, name: S) -> Self {
self.name = name.into();
self
}

pub fn with_num_instances(mut self, num_instances: u32) -> Self {
self.num_instances = num_instances;
self
}
}

pub fn get_base_config(cli: &Cli) -> anyhow::Result<Config> {
let executables = vec![
ExecutableConfig {
instance_type: InstanceType::TariValidatorNode,
executable_path: Some("target/release/minotari_node".into()),
therealdannzor marked this conversation as resolved.
Show resolved Hide resolved
compile: Some(CompileConfig {
therealdannzor marked this conversation as resolved.
Show resolved Hide resolved
working_dir: Some("../tari".into()),
package_name: "minotari_node".to_string(),
therealdannzor marked this conversation as resolved.
Show resolved Hide resolved
target_dir: None,
}),
env: vec![],
},
ExecutableConfig {
instance_type: InstanceType::MinoTariConsoleWallet,
executable_path: Some("target/release/minotari_wallet".into()),
compile: Some(CompileConfig {
working_dir: Some("../tari".into()),
package_name: "minotari_wallet".to_string(),
target_dir: None,
}),
env: vec![],
},
];
let instances = [
InstanceConfig::new(InstanceType::TariValidatorNode)
.with_name("tari_validator_node")
.with_num_instances(1),
InstanceConfig::new(InstanceType::MinoTariConsoleWallet)
.with_name("minotari_wallet")
.with_num_instances(1),
];

let base_dir = cli
.common
.base_dir
.clone()
.or_else(|| {
cli.get_config_path()
.canonicalize()
.ok()
.and_then(|p| p.parent().map(|p| p.to_path_buf()))
})
.unwrap_or_else(|| std::env::current_dir().unwrap());

Ok(Config {
auto_register: true,
base_node_grpc_address: "localhost:18142".to_string(),
base_wallet_grpc_address: "localhost:18143".to_string(),
sidechain_id: None,
vn_registration_file: base_dir.join("vn_registration.toml"),
therealdannzor marked this conversation as resolved.
Show resolved Hide resolved
instance_config: instances.to_vec(),
executable_config: executables,
channel_config: vec![
ChannelConfig {
name: "mattermost".to_string(),
enabled: true,
credentials: "".to_string(),
},
ChannelConfig {
name: "telegram".to_string(),
enabled: true,
credentials: "".to_string(),
},
],
})
}
49 changes: 49 additions & 0 deletions applications/tari_watcher/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright 2024 The Tari Project
// SPDX-License-Identifier: BSD-3-Clause

use anyhow::{anyhow, Context};
use tokio::fs;

use crate::{
cli::{Cli, Commands},
config::get_base_config,
};

mod cli;
mod config;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let cli = Cli::init();
let config_path = cli.get_config_path();

match cli.command {
Commands::Init(ref args) => {
// set by default in CommonCli
let parent = config_path.parent().unwrap();
fs::create_dir_all(parent).await?;

let mut config = get_base_config(&cli)?;
// optionally disables auto register
args.apply(&mut config);

let file = fs::File::create(&config_path)
.await
.with_context(|| anyhow!("Failed to open config path {}", config_path.display()))?;
config.write(file).await.context("Writing config failed")?;

let config_path = config_path
.canonicalize()
.context("Failed to canonicalize config path")?;

// TODO: use standardised logging
// if let Err(e) = initialize_logging(..)
log::info!("Config file created at {}", config_path.display());
},
Commands::Start => {
unimplemented!("Start command not implemented");
},
}

Ok(())
}
Loading