diff --git a/rs/cli/src/cli.rs b/rs/cli/src/cli.rs index 643e1597..ebe278e1 100644 --- a/rs/cli/src/cli.rs +++ b/rs/cli/src/cli.rs @@ -67,6 +67,17 @@ pub(crate) enum Commands { #[clap(allow_hyphen_values = true)] args: Vec, }, + + /// Place a proposal for updating unassigned nodes config + UpdateUnassignedNodes { + /// NNS subnet id. By default 'tdb26-jop6k-aogll-7ltgs-eruif-6kk7m-qpktf-gdiqx-mxtrf-vb5e6-eqe' + #[clap( + long, + default_value = "tdb26-jop6k-aogll-7ltgs-eruif-6kk7m-qpktf-gdiqx-mxtrf-vb5e6-eqe" + )] + nns_subnet_id: String, + }, + /// Manage replica/host-os versions blessing #[clap(subcommand)] Version(version::Cmd), diff --git a/rs/cli/src/ic_admin.rs b/rs/cli/src/ic_admin.rs index 5f8973fb..9911c3c8 100644 --- a/rs/cli/src/ic_admin.rs +++ b/rs/cli/src/ic_admin.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, Error, Result}; use cli::UpdateVersion; use colored::Colorize; use dialoguer::Confirm; @@ -6,7 +6,10 @@ use flate2::read::GzDecoder; use futures::stream::{self, StreamExt}; use futures::Future; use ic_base_types::PrincipalId; -use ic_management_types::Artifact; +use ic_management_backend::registry::{local_registry_path, RegistryFamilyEntries, RegistryState}; +use ic_management_types::{Artifact, Network}; +use ic_protobuf::registry::subnet::v1::SubnetRecord; +use ic_registry_local_registry::LocalRegistry; use itertools::Itertools; use log::{error, info, warn}; use regex::Regex; @@ -15,6 +18,7 @@ use sha2::{Digest, Sha256}; use std::fs::File; use std::io::Write; use std::os::unix::fs::PermissionsExt; +use std::time::Duration; use std::{path::Path, process::Command}; use strum::Display; @@ -529,6 +533,56 @@ must be identical, and must match the SHA256 from the payload of the NNS proposa }) } } + + pub async fn update_unassigned_nodes( + &self, + nns_subned_id: &String, + network: Network, + simulate: bool, + ) -> Result<(), Error> { + let local_registry_path = local_registry_path(network.clone()); + let local_registry = LocalRegistry::new(local_registry_path, Duration::from_secs(10)) + .map_err(|e| anyhow::anyhow!("Error in creating local registry instance: {:?}", e))?; + + local_registry + .sync_with_nns() + .await + .map_err(|e| anyhow::anyhow!("Error when syncing with NNS: {:?}", e))?; + + let subnets = local_registry.get_family_entries::()?; + + let nns = match subnets.get_key_value(nns_subned_id) { + Some((_, value)) => value, + None => return Err(anyhow::anyhow!("Couldn't find nns subnet with id '{}'", nns_subned_id)), + }; + + let registry_state = RegistryState::new(network, true).await; + let unassigned_version = registry_state.get_unassigned_nodes_replica_version().await?; + + if nns.replica_version_id.eq(&unassigned_version) { + info!( + "Unassigned nodes and nns are of the same version '{}', skipping proposal submition.", + unassigned_version + ); + return Ok(()); + } + + info!( + "NNS version '{}' and Unassigned nodes '{}' differ", + nns.replica_version_id, unassigned_version + ); + + let command = ProposeCommand::UpdateUnassignedNodes { + replica_version: unassigned_version, + }; + let options = ProposeOptions { + summary: Some("Update the unassigned nodes to the latest rolled-out version".to_string()), + motivation: None, + title: Some("Update all unassigned nodes".to_string()), + }; + + self.propose_run(command, options, simulate) + } } #[derive(Display, Clone)] @@ -562,6 +616,9 @@ pub(crate) enum ProposeCommand { node_ids: Vec, replica_version: String, }, + UpdateUnassignedNodes { + replica_version: String, + }, } impl ProposeCommand { @@ -575,6 +632,7 @@ impl ProposeCommand { release_artifact, args: _, } => format!("update-elected-{}-versions", release_artifact), + Self::UpdateUnassignedNodes { replica_version: _ } => "update-unassigned-nodes-config".to_string(), _ => self.to_string(), } ) @@ -638,6 +696,9 @@ impl ProposeCommand { } args } + Self::UpdateUnassignedNodes { replica_version } => { + vec!["--replica-version-id".to_string(), replica_version.clone()] + } } } } diff --git a/rs/cli/src/main.rs b/rs/cli/src/main.rs index 60fef14b..78737e03 100644 --- a/rs/cli/src/main.rs +++ b/rs/cli/src/main.rs @@ -195,6 +195,11 @@ async fn main() -> Result<(), anyhow::Error> { ic_admin.run_passthrough_propose(args, simulate) }, + cli::Commands::UpdateUnassignedNodes { nns_subnet_id } => { + let ic_admin: IcAdminWrapper = cli::Cli::from_opts(&cli_opts, true).await?.into(); + ic_admin.update_unassigned_nodes( nns_subnet_id, cli_opts.network, simulate).await + }, + cli::Commands::Version(version_command) => { match &version_command { cli::version::Cmd::Update(update_command) => {