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

(Remove backend server from CLI) - 01 - Do not use backend server for hostos-rollout cmd CLI #193

Merged
merged 5 commits into from
Feb 27, 2024
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
14 changes: 9 additions & 5 deletions Cargo.Bazel.lock
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"checksum": "872ac0e7ad6530e912d6f269ea87a37b5f210f23b0528c8c8e9d194d980e5c0a",
"checksum": "a044555754d8eefdcc382189f179b58618ff62d8ca51899ba64aa08c64e78472",
"crates": {
"actix-codec 0.5.2": {
"name": "actix-codec",
Expand Down Expand Up @@ -11569,6 +11569,10 @@
"id": "futures 0.3.30",
"target": "futures"
},
{
"id": "futures-util 0.3.30",
"target": "futures_util"
},
{
"id": "ic-base-types 0.9.0",
"target": "ic_base_types"
Expand Down Expand Up @@ -11704,6 +11708,10 @@
"edition": "2021",
"proc_macro_deps": {
"common": [
{
"id": "async-recursion 1.0.5",
"target": "async_recursion"
},
{
"id": "async-trait 0.1.77",
"target": "async_trait"
Expand Down Expand Up @@ -21788,10 +21796,6 @@
"edition": "2021",
"proc_macro_deps": {
"common": [
{
"id": "async-recursion 1.0.5",
"target": "async_recursion"
},
{
"id": "async-trait 0.1.77",
"target": "async_trait"
Expand Down
3 changes: 2 additions & 1 deletion Cargo.lock

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

2 changes: 2 additions & 0 deletions rs/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ documentation.workspace = true

[dependencies]
anyhow = { workspace = true }
async-recursion = { workspace = true }
async-trait = { workspace = true }
candid = { workspace = true }
clap = { workspace = true }
Expand All @@ -24,6 +25,7 @@ dotenv = { workspace = true }
edit = { workspace = true }
flate2 = { workspace = true }
futures = { workspace = true }
futures-util = { workspace = true }
ic-base-types = { workspace = true }
ic-canister-client = { workspace = true }
ic-canisters = { workspace = true }
Expand Down
3 changes: 2 additions & 1 deletion rs/cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,9 +305,10 @@ pub(crate) mod version {
}

pub(crate) mod hostos {
use crate::operations::hostos_rollout::{NodeAssignment, NodeOwner};

use super::*;
use ic_base_types::PrincipalId;
use ic_management_types::{NodeAssignment, NodeOwner};

#[derive(Parser, Clone)]
pub struct Cmd {
Expand Down
22 changes: 1 addition & 21 deletions rs/cli/src/clients.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use async_trait::async_trait;
use decentralization::SubnetChangeResponse;
use ic_base_types::PrincipalId;
use ic_management_types::requests::HostosRolloutResponse;
use ic_management_types::{
requests::{
HostosRolloutRequest, MembershipReplaceRequest, NodesRemoveRequest, NodesRemoveResponse, SubnetCreateRequest,
MembershipReplaceRequest, NodesRemoveRequest, NodesRemoveResponse, SubnetCreateRequest,
SubnetResizeRequest,
},
Artifact, Network, NetworkError, Release, TopologyProposal,
Expand Down Expand Up @@ -106,25 +105,6 @@ impl DashboardBackendClient {
.await
}

pub async fn get_blessed_versions(&self, release_artifact: &Artifact) -> anyhow::Result<Option<Vec<String>>> {
reqwest::Client::new()
.get(
self.url
.join(&format!("release/versions/blessed/{}", release_artifact))
.map_err(|e| anyhow::anyhow!(e))?,
)
.rest_send()
.await
}

pub async fn hostos_rollout_nodes(&self, request: HostosRolloutRequest) -> anyhow::Result<HostosRolloutResponse> {
reqwest::Client::new()
.post(self.url.join("hostos/rollout_nodes").map_err(|e| anyhow::anyhow!(e))?)
.json(&request)
.rest_send()
.await
}

pub async fn remove_nodes(&self, request: NodesRemoveRequest) -> anyhow::Result<NodesRemoveResponse> {
reqwest::Client::new()
.post(self.url.join("nodes/remove").map_err(|e| anyhow::anyhow!(e))?)
Expand Down
9 changes: 6 additions & 3 deletions rs/cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use crate::general::{get_node_metrics_history, vote_on_proposals};
use crate::ic_admin::IcAdminWrapper;
use crate::operations::hostos_rollout::{NodeGroupUpdate, NumberOfNodes};
use clap::{error::ErrorKind, CommandFactory, Parser};
use dotenv::dotenv;
use ic_base_types::CanisterId;
use ic_canisters::governance::governance_canister_version;
use ic_management_backend::endpoints;
use ic_management_types::requests::NodesRemoveRequest;
use ic_management_types::{Artifact, MinNakamotoCoefficients, Network, NodeFeature, NodeGroupUpdate, NumberOfNodes};
use ic_management_types::{Artifact, MinNakamotoCoefficients, Network, NodeFeature};
use log::info;
use std::collections::BTreeMap;
use std::str::FromStr;
Expand All @@ -22,6 +23,7 @@ mod ic_admin;
mod ops_subnet_node_replace;
mod registry_dump;
mod runner;
mod operations;

const STAGING_NEURON_ID: u64 = 49;

Expand All @@ -41,10 +43,11 @@ async fn main() -> Result<(), anyhow::Error> {
let (tx, rx) = mpsc::channel();

let backend_port = local_unused_port();
let target_network_backend = target_network.clone();
thread::spawn(move || {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async move {
endpoints::run_backend(target_network, "127.0.0.1", backend_port, true, Some(tx))
endpoints::run_backend(target_network_backend, "127.0.0.1", backend_port, true, Some(tx))
.await
.expect("failed")
});
Expand Down Expand Up @@ -239,7 +242,7 @@ async fn main() -> Result<(), anyhow::Error> {
},
cli::hostos::Commands::RolloutFromNodeGroup {version, assignment, owner, nodes_in_group, exclude } => {
let update_group = NodeGroupUpdate::new(*assignment, *owner, NumberOfNodes::from_str(nodes_in_group)?);
let runner = runner::Runner::new_with_network_url(cli::Cli::from_opts(&cli_opts, true).await?.into(), backend_port).await?;
let runner = runner::Runner::new(cli::Cli::from_opts(&cli_opts, true).await?.into(), target_network.clone()).await?;
if let Some((nodes_to_update, summary)) = runner.hostos_rollout_nodes(update_group, version, exclude).await? {
return runner.hostos_rollout(nodes_to_update, version, simulate, Some(summary)).await
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,167 @@
use anyhow::anyhow;
use async_recursion::async_recursion;
use clap::{Parser, ValueEnum};
use futures_util::future::try_join;
use ic_base_types::{NodeId, PrincipalId};
use ic_management_types::requests::{HostosRolloutReason, HostosRolloutResponse, HostosRolloutSubnetAffected};
use ic_management_backend::health;
use ic_management_backend::proposal::ProposalAgent;
use ic_management_types::{
Network, Node, NodeAssignment, NodeGroup, NodeGroupUpdate, NodeOwner, Status, Subnet,
UpdateNodesHostosVersionsProposal,
Network, Node, Status, Subnet, UpdateNodesHostosVersionsProposal
};
use log::{debug, info};
use proposal::ProposalAgent;
use std::collections::BTreeMap;
use std::{collections::BTreeMap, fmt::Display, str::FromStr};

pub enum HostosRolloutResponse {
Ok(Vec<Node>, Option<Vec<HostosRolloutSubnetAffected>>),
None(Vec<(NodeGroup, HostosRolloutReason)>),
}

impl HostosRolloutResponse {
pub fn unwrap(self) -> Vec<Node> {
match self {
HostosRolloutResponse::Ok(val, _) => val,
_ => panic!("called `Option::unwrap()` on a `None` value"),
}
}
}

#[derive(Clone, Eq, PartialEq)]
pub struct HostosRolloutSubnetAffected {
pub subnet_id: PrincipalId,
pub subnet_size: usize,
}

#[derive(Eq, PartialEq)]
pub enum HostosRolloutReason {
NoNodeHealthy,
NoNodeWithoutProposal,
AllAlreadyUpdated,
NoNodeSelected,
}

impl Display for HostosRolloutReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NoNodeHealthy => write!(f, "No healthy node found in the group"),
Self::NoNodeWithoutProposal => write!(f, "No node without open proposals found in the group"),
Self::AllAlreadyUpdated => write!(f, "All candidate nodes have been already updated"),
Self::NoNodeSelected => write!(f, "No candidate nodes have been selected"),
}
}
}

#[derive(ValueEnum, Copy, Clone, Debug, Ord, Eq, PartialEq, PartialOrd, Parser, Default)]
pub enum NodeOwner {
Dfinity,
Others,
#[default]
All,
}

#[derive(ValueEnum, Copy, Clone, Debug, Ord, Eq, PartialEq, PartialOrd, Default)]
pub enum NodeAssignment {
Unassigned,
Assigned,
#[default]
All,
}

#[derive(Copy, Clone, Debug, Ord, Eq, PartialEq, PartialOrd)]
pub struct NodeGroup {
pub assignment: NodeAssignment,
pub owner: NodeOwner,
}

impl NodeGroup {
pub fn new(assignment: NodeAssignment, owner: NodeOwner) -> Self {
NodeGroup { assignment, owner }
}
}
impl std::fmt::Display for NodeGroup {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "GROUP {{ subnet: {:?}, owner: {:?} }}", self.assignment, self.owner)
}
}
#[derive(Copy, Clone, Debug, Ord, Eq, PartialEq, PartialOrd)]
pub enum NumberOfNodes {
Percentage(i32),
Absolute(i32),
}
impl FromStr for NumberOfNodes {
type Err = anyhow::Error;
fn from_str(input: &str) -> Result<Self, Self::Err> {
if input.ends_with('%') {
let percentage = i32::from_str(input.trim_end_matches('%'))?;
if (0..=100).contains(&percentage) {
Ok(NumberOfNodes::Percentage(percentage))
} else {
Err(anyhow!("Percentage must be between 0 and 100"))
}
} else {
Ok(NumberOfNodes::Absolute(i32::from_str(input)?))
}
}
}

impl Default for NumberOfNodes {
fn default() -> Self {
NumberOfNodes::Percentage(100)
}
}
impl NumberOfNodes {
pub fn get_value(&self) -> i32 {
match self {
NumberOfNodes::Percentage(value) | NumberOfNodes::Absolute(value) => *value,
}
}
}
impl std::fmt::Display for NumberOfNodes {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
NumberOfNodes::Percentage(x) => write!(f, "{}% of", x),
NumberOfNodes::Absolute(x) => write!(f, "{}", x),
}
}
}

#[derive(Copy, Clone, Debug, Ord, Eq, PartialEq, PartialOrd)]
pub struct NodeGroupUpdate {
pub node_group: NodeGroup,
pub maybe_number_nodes: Option<NumberOfNodes>,
}
impl NodeGroupUpdate {
pub fn new(assignment: Option<NodeAssignment>, owner: Option<NodeOwner>, nodes_per_subnet: NumberOfNodes) -> Self {
NodeGroupUpdate {
node_group: NodeGroup::new(assignment.unwrap_or_default(), owner.unwrap_or_default()),
maybe_number_nodes: Some(nodes_per_subnet),
}
}
pub fn new_all(assignment: NodeAssignment, owner: NodeOwner) -> Self {
NodeGroupUpdate {
node_group: NodeGroup::new(assignment, owner),
maybe_number_nodes: None,
}
}

pub fn with_assignment(&self, assignment: NodeAssignment) -> Self {
Self {
node_group: NodeGroup {
assignment,
owner: self.node_group.owner,
},
maybe_number_nodes: self.maybe_number_nodes,
}
}
pub fn nodes_to_take(&self, group_size: usize) -> usize {
match self.maybe_number_nodes.unwrap_or_default() {
NumberOfNodes::Percentage(percent_to_update) => {
(group_size as f32 * percent_to_update as f32 / 100.0).floor() as usize
}
NumberOfNodes::Absolute(number_nodes) => number_nodes as usize,
}
}
}

use crate::health;
use crate::proposal;

enum CandidatesSelection {
Ok(Vec<Node>),
Expand Down Expand Up @@ -385,9 +534,9 @@ impl HostosRollout {

#[cfg(test)]
pub mod test {
use crate::hostos_rollout::NodeAssignment::{Assigned, Unassigned};
use crate::hostos_rollout::NodeOwner::{Dfinity, Others};
use ic_management_types::{Network, Node, NumberOfNodes, Operator, Provider, Subnet};
use ic_management_types::{Network, Node, Operator, Provider, Subnet};
use crate::operations::hostos_rollout::NodeOwner::{Dfinity, Others};
use crate::operations::hostos_rollout::NodeAssignment::{Assigned, Unassigned};
use std::net::Ipv6Addr;

use super::*;
Expand Down
1 change: 1 addition & 0 deletions rs/cli/src/operations/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod hostos_rollout;
Loading
Loading