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
7 changes: 6 additions & 1 deletion Cargo.Bazel.lock
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"checksum": "bd3768c7540f371110a824f1818960830b97d9a44b195334c3ea0bd91c5f6545",
"checksum": "630ae45dd163f4f28931058bc3e916e8e1f9b672ffe9065093ddb7e4ef749d7d",
"crates": {
"actix-codec 0.5.2": {
"name": "actix-codec",
Expand Down Expand Up @@ -12068,6 +12068,10 @@
"id": "comfy-table 7.1.4",
"target": "comfy_table"
},
{
"id": "csv 1.3.1",
"target": "csv"
},
{
"id": "cycles-minting-canister 0.9.0",
"target": "cycles_minting_canister"
Expand Down Expand Up @@ -60120,6 +60124,7 @@
"crossbeam 0.8.4",
"crossbeam-channel 0.5.15",
"cryptoki 0.7.0",
"csv 1.3.1",
"custom_error 1.9.2",
"cycles-minting-canister 0.9.0",
"dfn_core 0.9.0",
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ url = "2.5.4"
wiremock = "0.6.4"
human_bytes = "0.4"
mockall = "0.13.1"
csv = "1.3.1"

# dre-canisters dependencies
ic-cdk = { version = "^0.17.2" }
Expand Down
1 change: 1 addition & 0 deletions rs/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ tabular = { workspace = true }
tempfile = { workspace = true }
tokio = { workspace = true }
url = { workspace = true }
csv = { workspace = true }

[dev-dependencies]
actix-rt = { workspace = true }
Expand Down
55 changes: 46 additions & 9 deletions rs/cli/src/commands/subnet/force_replace.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
use std::collections::BTreeSet;

use clap::Args;
use decentralization::{network::SubnetChange, SubnetChangeResponse};
use dialoguer::Confirm;
use ic_management_types::Node;
use ic_types::PrincipalId;
use indexmap::IndexMap;
use itertools::Itertools;
use log::warn;
use registry_canister::mutations::do_change_subnet_membership::ChangeSubnetMembershipPayload;

use crate::{
exe::ExecutableCommand,
forum::ForumPostKind,
submitter::{SubmissionParameters, Submitter},
target_topology::TargetTopologyOption,
};

#[derive(Args, Debug)]
Expand All @@ -35,6 +36,10 @@ pub struct ForceReplace {

#[clap(flatten)]
pub submission_parameters: SubmissionParameters,

// TODO: add actual handling of `current` once we merge the code
#[clap(long)]
target_topology: Option<TargetTopologyOption>,
}

impl ExecutableCommand for ForceReplace {
Expand All @@ -43,6 +48,16 @@ impl ExecutableCommand for ForceReplace {
}

fn validate(&self, _args: &crate::exe::args::GlobalArgs, cmd: &mut clap::Command) {
if let Some(TargetTopologyOption::Path(path)) = &self.target_topology {
if !path.exists() {
cmd.error(
clap::error::ErrorKind::InvalidValue,
format!("path `{}` not found locally.", path.display()),
)
.exit();
}
}

let from: BTreeSet<PrincipalId> = self.from.iter().cloned().collect();
let to: BTreeSet<PrincipalId> = self.to.iter().cloned().collect();

Expand Down Expand Up @@ -143,15 +158,31 @@ impl ExecutableCommand for ForceReplace {
}

// Create a request
let runner = ctx.runner().await?;
let registry = ctx.registry().await;

let subnet = registry
.subnet(decentralization::network::SubnetQueryBy::SubnetId(self.subnet_id))
.await?;

let change_membership = ChangeSubnetMembershipPayload {
let old_nodes = subnet.nodes.clone();
let all_nodes = registry.nodes().await?;

let subnet_change = SubnetChange {
subnet_id: self.subnet_id,
node_ids_add: self.to.iter().map(|id| (*id).into()).collect(),
node_ids_remove: self.from.iter().map(|id| (*id).into()).collect(),
old_nodes: old_nodes.clone(),
new_nodes: old_nodes
.iter()
.filter(|node| !self.from.contains(&node.principal))
.chain(self.to.iter().map(|p| all_nodes.get(p).unwrap()))
.cloned()
.collect(),
removed_nodes: self.from.iter().map(|key| all_nodes.get(key).unwrap()).cloned().collect(),
added_nodes: self.to.iter().map(|key| all_nodes.get(key).unwrap()).cloned().collect(),
..Default::default()
};

let subnet_change_response = runner.decentralization_change(&change_membership, None, self.motivation.clone()).await?;
let health = ctx.health_client();
let node_health = health.nodes().await?;
let response = SubnetChangeResponse::new(&subnet_change, &node_health, None);

if !warnings.is_empty() {
warn!("Careful! There are warnings related to this action!");
Expand All @@ -168,6 +199,7 @@ impl ExecutableCommand for ForceReplace {
}

if !self.submission_parameters.confirmation_mode.dry_run
&& !self.submission_parameters.confirmation_mode.yes
&& !Confirm::new()
.with_prompt("Accept warnings mentioned above?")
.default(false)
Expand All @@ -178,15 +210,20 @@ impl ExecutableCommand for ForceReplace {
}
}

let runner_proposal = match ctx.runner().await?.propose_subnet_change(&subnet_change_response, true).await? {
let runner_proposal = match ctx
.runner()
.await?
.propose_force_subnet_change(&response, ctx.target_topology(self.target_topology.clone().unwrap_or_default()).await?)
.await?
{
Some(runner_proposal) => runner_proposal,
None => return Ok(()),
};

Submitter::from(&self.submission_parameters)
.propose_and_print(
ctx.ic_admin_executor().await?.execution(runner_proposal.clone()),
match subnet_change_response.subnet_id {
match response.subnet_id {
Some(id) => ForumPostKind::ReplaceNodes {
subnet_id: id,
body: match (&runner_proposal.options.motivation, &runner_proposal.options.summary) {
Expand Down
2 changes: 1 addition & 1 deletion rs/cli/src/commands/subnet/replace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ impl ExecutableCommand for Replace {
)
.await?;

let runner_proposal = match ctx.runner().await?.propose_subnet_change(&subnet_change_response, false).await? {
let runner_proposal = match ctx.runner().await?.propose_subnet_change(&subnet_change_response).await? {
Some(runner_proposal) => runner_proposal,
None => return Ok(()),
};
Expand Down
2 changes: 1 addition & 1 deletion rs/cli/src/commands/subnet/resize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl ExecutableCommand for Resize {
)
.await?;

let runner_proposal = match runner.propose_subnet_change(&subnet_change_response, false).await? {
let runner_proposal = match runner.propose_subnet_change(&subnet_change_response).await? {
Some(runner_proposal) => runner_proposal,
None => return Ok(()),
};
Expand Down
2 changes: 1 addition & 1 deletion rs/cli/src/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub struct ConfirmationModeOptions {
help_heading = "Options on how to proceed",
help = "Do not ask for confirmation. If specified, the operation will be performed without requesting any confirmation from you."
)]
yes: bool,
pub(crate) 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,
Expand Down
7 changes: 6 additions & 1 deletion rs/cli/src/ctx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ use crate::{
artifact_downloader::{ArtifactDownloader, ArtifactDownloaderImpl},
auth::{AuthOpts, AuthRequirement, Neuron},
cordoned_feature_fetcher::CordonedFeatureFetcher,
exe::{args::GlobalArgs, args::IcAdminVersion},
exe::args::{GlobalArgs, IcAdminVersion},
governance::GovernanceCanisterProposalExecutor,
ic_admin::{IcAdmin, IcAdminImpl, IcAdminProposalExecutor},
runner::Runner,
store::Store,
subnet_manager::SubnetManager,
target_topology::{TargetTopology, TargetTopologyOption},
};

#[cfg(test)]
Expand Down Expand Up @@ -133,6 +134,10 @@ impl DreContext {
self.store.is_offline()
}

pub async fn target_topology(&self, target_topology_option: TargetTopologyOption) -> anyhow::Result<TargetTopology> {
TargetTopology::from_option(target_topology_option, self.store.clone()).await
}

pub async fn registry(&self) -> Arc<dyn LazyRegistry> {
self.registry_with_version(None).await
}
Expand Down
1 change: 1 addition & 0 deletions rs/cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod runner;
mod store;
mod submitter;
mod subnet_manager;
mod target_topology;
mod util;

#[cfg(test)]
Expand Down
Loading
Loading