diff --git a/rs/boundary_node/rate_limits/canister_client/src/lib.rs b/rs/boundary_node/rate_limits/canister_client/src/lib.rs index 2502251b3ef..d91193ef5fb 100644 --- a/rs/boundary_node/rate_limits/canister_client/src/lib.rs +++ b/rs/boundary_node/rate_limits/canister_client/src/lib.rs @@ -8,21 +8,13 @@ use rate_limits_api::{ use serde::Deserialize; use std::{fs, path::PathBuf, str}; use tracing::{debug, info}; -use tracing_subscriber::EnvFilter; use uuid::Uuid; pub async fn submit_config( config_file: PathBuf, canister_id: Principal, agent: Agent, - debug: bool, ) -> Result<(), Error> { - // initialize tracing subscriber with corresponding log level - let log_level = if debug { "debug" } else { "info" }; - tracing_subscriber::fmt() - .with_env_filter(EnvFilter::new(log_level)) - .init(); - // read the rules let rules = read_yaml_file(&config_file)?; @@ -83,6 +75,11 @@ pub async fn submit_config( Ok(()) } +pub fn check_config(config_file: PathBuf) -> Result<(), Error> { + let _ = read_yaml_file(&config_file)?; + Ok(()) +} + #[derive(Debug, Deserialize)] struct YamlRule { #[serde(flatten)] @@ -95,8 +92,7 @@ fn read_yaml_file(file_path: &PathBuf) -> Result, Error> { let yaml_str = fs::read_to_string(file_path).context("Unable to read file")?; // Deserialize directly into `YamlRule` and transform `InputRule` - let yaml_rules: Vec = - serde_yaml::from_str(&yaml_str).context("Failed to parse YAML")?; + let yaml_rules: Vec = serde_yaml::from_str(&yaml_str)?; let input_rules = yaml_rules .into_iter() .map(|entry| InputRule { diff --git a/rs/boundary_node/rate_limits/canister_client/src/main.rs b/rs/boundary_node/rate_limits/canister_client/src/main.rs index 3e8d8df7d53..04be8b669ae 100644 --- a/rs/boundary_node/rate_limits/canister_client/src/main.rs +++ b/rs/boundary_node/rate_limits/canister_client/src/main.rs @@ -1,9 +1,12 @@ +use anyhow::{bail, Context, Error}; use candid::Principal; use clap::Parser; use ic_agent::{identity::Secp256k1Identity, Agent}; use k256::elliptic_curve::SecretKey; -use rate_limiting_canister_client::submit_config; +use rate_limiting_canister_client::{check_config, submit_config}; use std::{path::PathBuf, str}; +use tracing::info; +use tracing_subscriber::EnvFilter; const SERVICE_NAME: &str = "rate-limiting-canister-client"; @@ -12,7 +15,7 @@ const SERVICE_NAME: &str = "rate-limiting-canister-client"; struct Cli { /// ID of the rate-limiting canister #[arg(long)] - canister_id: Principal, + canister_id: Option, /// Path to the file containing all the rules #[arg(long)] @@ -20,33 +23,55 @@ struct Cli { /// Identity key #[arg(long)] - identity_key: String, + identity_key: Option, /// IC domain URL #[arg(long, default_value = "https://icp-api.io")] ic_domain: String, + /// Only check the config file + #[arg(long)] + check: bool, + /// Enable debug logging #[arg(long)] debug: bool, } #[tokio::main] -async fn main() { +async fn main() -> Result<(), Error> { // parse command-line arguments let cli = Cli::parse(); - // create the agent - let identity = Secp256k1Identity::from_private_key( - SecretKey::from_sec1_pem(&cli.identity_key).expect("failed to parse the identity key"), - ); - let agent = Agent::builder() - .with_url(cli.ic_domain) - .with_identity(identity) - .build() - .expect("failed to build the agent"); - - submit_config(cli.config_file, cli.canister_id, agent, cli.debug) - .await - .expect("failed to submit the config to the canister"); + // initialize tracing subscriber with corresponding log level + let log_level = if cli.debug { "debug" } else { "info" }; + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::new(log_level)) + .init(); + + if cli.check { + check_config(cli.config_file).context("Failed to parse the config")?; + info!("Config file is correctly formatted"); + } else { + if cli.identity_key.is_none() || cli.canister_id.is_none() { + bail!("Canister ID and identity key are required to submit the configuration to the canister!"); + } + let identity_key = cli.identity_key.unwrap(); + let canister_id = cli.canister_id.unwrap(); + + // create the agent + let identity = Secp256k1Identity::from_private_key( + SecretKey::from_sec1_pem(&identity_key).context("failed to parse the identity key")?, + ); + let agent = Agent::builder() + .with_url(cli.ic_domain) + .with_identity(identity) + .build() + .context("failed to build the agent")?; + + submit_config(cli.config_file, canister_id, agent) + .await + .context("failed to submit the config to the canister")?; + } + Ok(()) }