Skip to content
Open
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
9 changes: 4 additions & 5 deletions rs/cli/src/commands/hostos/rollout_from_node_group.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::str::FromStr;

use clap::{Args, ValueEnum};

use crate::{
Expand Down Expand Up @@ -74,9 +72,10 @@ pub struct RolloutFromNodeGroup {
#[clap(
long,
help = r#"How many nodes in the group to update with the version specified
supported values are absolute numbers (10) or percentage (10%)"#
supported values are absolute numbers (10) or percentage (10%)"#,
default_value = "100%"
)]
pub nodes_in_group: String,
pub nodes_in_group: NumberOfNodes,

#[clap(flatten)]
pub submission_parameters: SubmissionParameters,
Expand All @@ -88,7 +87,7 @@ impl ExecutableCommand for RolloutFromNodeGroup {
}

async fn execute(&self, ctx: crate::ctx::DreContext) -> anyhow::Result<()> {
let update_group = NodeGroupUpdate::new(self.assignment, self.owner, NumberOfNodes::from_str(&self.nodes_in_group)?);
let update_group = NodeGroupUpdate::new(self.assignment, self.owner, self.nodes_in_group);
let runner = ctx.runner().await?;

let (nodes_to_update, summary) = match runner
Expand Down
3 changes: 2 additions & 1 deletion rs/cli/src/commands/propose.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use clap::{error::ErrorKind, Args};

use crate::auth::AuthRequirement;
use crate::confirm::DryRunType;
use crate::exe::args::GlobalArgs;
use crate::exe::ExecutableCommand;
use crate::{
Expand Down Expand Up @@ -49,7 +50,7 @@ impl ExecutableCommand for Propose {
// This automatically bypasses interactive prompts, as they are unnecessary and unexpected when displaying help.
let mut submission_params = self.submission_parameters.clone();
if args.contains(&String::from("--help")) {
submission_params.confirmation_mode.dry_run = true;
submission_params.confirmation_mode.dry_run = DryRunType::HumanReadable;
}

Submitter::from(&submission_params)
Expand Down
2 changes: 1 addition & 1 deletion rs/cli/src/commands/update_default_subnets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ impl ExecutableCommand for UpdateDefaultSubnets {
.collect();

if new_authorized == default_subnets {
println!("There are no diffs. Skipping proposal creation.");
info!("There are no diffs. Skipping proposal creation.");
return Ok(());
}

Expand Down
6 changes: 5 additions & 1 deletion rs/cli/src/commands/vote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use log::info;
use spinners::{Spinner, Spinners};

use crate::auth::AuthRequirement;
use crate::confirm::DryRunFormat;
use crate::exe::{args::GlobalArgs, ExecutableCommand};
use crate::{
confirm::{ConfirmationModeOptions, HowToProceed},
Expand Down Expand Up @@ -119,7 +120,10 @@ impl ExecutableCommand for Vote {
}
}
}
HowToProceed::DryRun => info!("Would have voted for proposal {}", prop_id),
HowToProceed::DryRun(format) => match format {
DryRunFormat::HumanReadable => info!("Would have voted for proposal {}", prop_id),
DryRunFormat::Json => println!("{}", prop_id),
},
}
voted_proposals.insert(prop_id);
}
Expand Down
56 changes: 45 additions & 11 deletions rs/cli/src/confirm.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,50 @@
use clap::Args as ClapArgs;
use std::str::FromStr;

use clap::{Args as ClapArgs, Parser};

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DryRunFormat {
HumanReadable,
Json,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HowToProceed {
Confirm,
Unconditional,
DryRun,
DryRun(DryRunFormat),
#[allow(dead_code)]
UnitTests, // Necessary for unit tests, otherwise confirmation is requested.
// Generally this is hit when DreContext (created by get_mocked_ctx) has
// both dry_run and proceed_without_confirmation set to true.
// The net effect is that both the dry run and the final command are run.
}

#[derive(ClapArgs, Debug, Clone)]
#[derive(Debug, Clone, Parser)]
pub(crate) enum DryRunType {
NoDryRun,
HumanReadable,
Json,
}

impl FromStr for DryRunType {
type Err = clap::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"no" => Ok(DryRunType::NoDryRun),
"yes" => Ok(DryRunType::HumanReadable),
"json" => Ok(DryRunType::Json),
_ => {
let mut cmd = clap::Command::new("dre");
Err(cmd.error(clap::error::ErrorKind::InvalidValue, format!("invalid value {} for --dry-run", s)))
}
}
}
}

/// Options for commands that may require confirmation.
#[derive(ClapArgs, Debug, Clone)]
pub struct ConfirmationModeOptions {
/// To skip the confirmation prompt
#[clap(
Expand All @@ -28,25 +58,29 @@ pub struct ConfirmationModeOptions {
)]
yes: bool,

#[clap(long, aliases = [ "dry-run", "dryrun", "simulate", "no"], env = "DRY_RUN", global = true, conflicts_with = "yes", help = r#"Dry-run, or simulate operation. If specified will not make any changes; instead, it will show what would be done or submitted."#,help_heading = "Options on how to proceed")]
pub(crate) dry_run: bool,
#[clap(long, aliases = [ "dry-run", "dryrun", "simulate", "no"], env = "DRY_RUN", global = true, conflicts_with = "yes", help = r#"Dry-run, or simulate operation. If specified will not make any changes; instead, it will show what would be done or submitted. If specified as --dry-run=json, it will print machine-readable JSON to standard output."#, help_heading = "Options on how to proceed", num_args = 0..=1, default_value="no", default_missing_value="yes")]
pub(crate) dry_run: DryRunType,
}

#[cfg(test)]
impl ConfirmationModeOptions {
/// Return an option set for unit tests, not instantiable via command line due to conflict.
pub fn for_unit_tests() -> Self {
ConfirmationModeOptions { yes: true, dry_run: true }
ConfirmationModeOptions {
yes: true,
dry_run: DryRunType::HumanReadable,
}
}
}

impl From<&ConfirmationModeOptions> for HowToProceed {
fn from(o: &ConfirmationModeOptions) -> Self {
match (o.dry_run, o.yes) {
(false, true) => Self::Unconditional,
(true, false) => Self::DryRun,
(false, false) => Self::Confirm,
(true, true) => Self::UnitTests, // This variant cannot be instantiated via the command line.
match (&o.dry_run, o.yes) {
(DryRunType::NoDryRun, true) => Self::Unconditional,
(DryRunType::NoDryRun, false) => Self::Confirm,
(DryRunType::HumanReadable, false) => Self::DryRun(DryRunFormat::HumanReadable),
(DryRunType::Json, false) => Self::DryRun(DryRunFormat::Json),
(_, true) => Self::UnitTests, // These variants cannot be instantiated via the command line.
}
}
}
2 changes: 1 addition & 1 deletion rs/cli/src/forum/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ impl Discourse {
warn!("While creating a new post in topic {}, Discourse returned an error: {:?}", topic_url, e);
}
warn!("Please create a post in topic {} with the following content", topic_url);
println!("{}", body);
eprintln!("{}", body);
let forum_post_link = dialoguer::Input::<String>::new()
.with_prompt("Forum post link")
.allow_empty(false)
Expand Down
21 changes: 17 additions & 4 deletions rs/cli/src/governance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use futures::future::BoxFuture;
use ic_canisters::governance::GovernanceCanisterWrapper;
use ic_nns_common::pb::v1::NeuronId;
use ic_nns_governance_api::MakeProposalRequest;
use serde_json::json;
use url::Url;

use crate::proposal_executors::{ProposableViaGovernanceCanister, ProposalExecution, ProposalResponseWithId};
Expand Down Expand Up @@ -32,14 +33,22 @@ impl GovernanceCanisterProposalExecutor {
pub fn simulate<'c, 'd, W: ProposableViaGovernanceCanister + 'c>(
&'d self,
cmd: &'c W,
machine_readable: bool,
forum_post_link_description: Option<String>,
) -> BoxFuture<'c, anyhow::Result<()>>
where
'd: 'c,
{
Box::pin(async move {
println!("Proposal that would be submitted:\n{:#?}", cmd);
println!("Forum post link: {}", forum_post_link_description.unwrap_or("None".to_string()));
match machine_readable {
false => {
println!("Proposal that would be submitted:\n{:#?}", cmd);
println!("Forum post link: {}", forum_post_link_description.unwrap_or("None".to_string()));
}
true => {
println!("{}", json!(cmd));
}
}
Ok(())
})
}
Expand Down Expand Up @@ -85,8 +94,12 @@ impl<T> ProposalExecution for ProposalExecutionViaGovernanceCanister<T>
where
T: ProposableViaGovernanceCanister<ProposalResult = ProposalResponseWithId>,
{
fn simulate(&self, forum_post_link_description: Option<String>) -> BoxFuture<'_, anyhow::Result<()>> {
Box::pin(async { self.executor.simulate(&self.proposal, forum_post_link_description).await })
fn simulate(&self, machine_readable: bool, forum_post_link_description: Option<String>) -> BoxFuture<'_, anyhow::Result<()>> {
Box::pin(async move {
self.executor
.simulate(&self.proposal, machine_readable, forum_post_link_description)
.await
})
}

fn submit<'a, 'b>(&'a self, forum_post_link: Option<Url>) -> BoxFuture<'b, anyhow::Result<ProposalResponseWithId>>
Expand Down
33 changes: 27 additions & 6 deletions rs/cli/src/ic_admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ pub trait IcAdmin: Send + Sync + Debug {
fn ic_admin_path(&self) -> Option<String>;

/// Runs the proposal in simulation mode (--dry-run). Prints out the result.
fn simulate_proposal(&self, cmd: Vec<String>, forum_post_link_description: Option<String>) -> BoxFuture<'_, anyhow::Result<()>>;
fn simulate_proposal(
&self,
cmd: Vec<String>,
machine_readable: bool,
forum_post_link_description: Option<String>,
) -> BoxFuture<'_, anyhow::Result<()>>;

/// Runs the proposal in forrealz mode. Result is returned and logged at debug level.
fn submit_proposal<'a, 'b>(&'a self, cmd: Vec<String>, forum_post_link: Option<Url>) -> BoxFuture<'b, anyhow::Result<String>>
Expand Down Expand Up @@ -148,7 +153,12 @@ impl IcAdmin for IcAdminImpl {
})
}

fn simulate_proposal(&self, cmd: Vec<String>, forum_post_link_description: Option<String>) -> BoxFuture<'_, anyhow::Result<()>> {
fn simulate_proposal(
&self,
cmd: Vec<String>,
machine_readable: bool,
forum_post_link_description: Option<String>,
) -> BoxFuture<'_, anyhow::Result<()>> {
Box::pin(async move {
debug!("Simulating proposal {:?}.", cmd);

Expand All @@ -168,11 +178,15 @@ impl IcAdmin for IcAdminImpl {
if !args.contains(&String::from("--dry-run")) {
args.push("--dry-run".into())
};
// When we want to simulate as JSON.
if machine_readable {
args.push("--json".into())
}

self.run(args.as_slice(), true).await.map(|r| r.trim().to_string())?;

// Add the forum post link description as a string after the ic-admin command.
if forum_post_link_description.is_some() && !is_forum_post_link_description_url {
if forum_post_link_description.is_some() && !is_forum_post_link_description_url && !machine_readable {
println!("Forum post link: {}", forum_post_link_description.unwrap());
}

Expand Down Expand Up @@ -546,14 +560,17 @@ impl IcAdminProposalExecutor {
pub fn simulate<'c, 'd, T: RunnableViaIcAdmin + 'c>(
&'d self,
cmd: &'c T,
machine_readable: bool,
forum_post_link_description: Option<String>,
) -> BoxFuture<'c, anyhow::Result<()>>
where
'd: 'c,
{
Box::pin(async move {
let propose_command = cmd.to_ic_admin_arguments()?;
self.ic_admin.simulate_proposal(propose_command, forum_post_link_description).await?;
self.ic_admin
.simulate_proposal(propose_command, machine_readable, forum_post_link_description)
.await?;
Ok(())
})
}
Expand Down Expand Up @@ -587,8 +604,12 @@ where
T: RunnableViaIcAdmin<Output = ProposalResponseWithId>,
T: ProducesProposalResult<ProposalResult = ProposalResponseWithId>,
{
fn simulate(&self, forum_post_link_description: Option<String>) -> BoxFuture<'_, anyhow::Result<()>> {
Box::pin(async { self.executor.simulate(&self.proposal, forum_post_link_description).await })
fn simulate(&self, machine_readable: bool, forum_post_link_description: Option<String>) -> BoxFuture<'_, anyhow::Result<()>> {
Box::pin(async move {
self.executor
.simulate(&self.proposal, machine_readable, forum_post_link_description)
.await
})
}

fn submit<'a, 'b>(&'a self, forum_post_link: Option<Url>) -> BoxFuture<'b, anyhow::Result<ProposalResponseWithId>>
Expand Down
Loading
Loading