diff --git a/vendor/pezkuwi-subxt/subxt/src/blocks/extrinsic_types.rs b/vendor/pezkuwi-subxt/subxt/src/blocks/extrinsic_types.rs index 2bc3ac60..aeefb11d 100644 --- a/vendor/pezkuwi-subxt/subxt/src/blocks/extrinsic_types.rs +++ b/vendor/pezkuwi-subxt/subxt/src/blocks/extrinsic_types.rs @@ -302,7 +302,9 @@ impl ExtrinsicEvents { /// /// This works in the same way that [`events::Events::find()`] does, with the /// exception that it filters out events not related to the submitted extrinsic. - pub fn find<'a, Ev: events::StaticEvent + 'a>(&'a self) -> impl Iterator> + 'a { + pub fn find<'a, Ev: events::StaticEvent + 'a>( + &'a self, + ) -> impl Iterator> + 'a { self.iter().filter_map(|ev| ev.and_then(|ev| ev.as_event::()).transpose()) } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/global_settings.rs b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/global_settings.rs index 21b57190..7a656e82 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/global_settings.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/global_settings.rs @@ -1,383 +1,339 @@ use std::{ - error::Error, - fmt::Display, - net::IpAddr, - path::{Path, PathBuf}, - str::FromStr, + error::Error, + fmt::Display, + net::IpAddr, + path::{Path, PathBuf}, + str::FromStr, }; use multiaddr::Multiaddr; use serde::{Deserialize, Serialize}; use crate::{ - shared::{ - errors::{ConfigError, FieldError}, - helpers::{merge_errors, merge_errors_vecs}, - types::Duration, - }, - utils::{default_as_true, default_node_spawn_timeout, default_timeout}, + shared::{ + errors::{ConfigError, FieldError}, + helpers::{merge_errors, merge_errors_vecs}, + types::Duration, + }, + utils::{default_as_true, default_node_spawn_timeout, default_timeout}, }; /// Global settings applied to an entire network. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct GlobalSettings { - /// Global bootnodes to use (we will then add more) - #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] - bootnodes_addresses: Vec, - // TODO: parse both case in zombienet node version to avoid renamed ? - /// Global spawn timeout - #[serde(rename = "timeout", default = "default_timeout")] - network_spawn_timeout: Duration, - // TODO: not used yet - /// Node spawn timeout - #[serde(default = "default_node_spawn_timeout")] - node_spawn_timeout: Duration, - // TODO: not used yet - /// Local ip to use for construct the direct links - local_ip: Option, - /// Directory to use as base dir - /// Used to reuse the same files (database) from a previous run, - /// also note that we will override the content of some of those files. - base_dir: Option, - /// Number of concurrent spawning process to launch, None means try to spawn all at the same time. - spawn_concurrency: Option, - /// If enabled, will launch a task to monitor nodes' liveness and tear down the network if there are any. - #[serde(default = "default_as_true")] - tear_down_on_failure: bool, + /// Global bootnodes to use (we will then add more) + #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] + bootnodes_addresses: Vec, + // TODO: parse both case in zombienet node version to avoid renamed ? + /// Global spawn timeout + #[serde(rename = "timeout", default = "default_timeout")] + network_spawn_timeout: Duration, + // TODO: not used yet + /// Node spawn timeout + #[serde(default = "default_node_spawn_timeout")] + node_spawn_timeout: Duration, + // TODO: not used yet + /// Local ip to use for construct the direct links + local_ip: Option, + /// Directory to use as base dir + /// Used to reuse the same files (database) from a previous run, + /// also note that we will override the content of some of those files. + base_dir: Option, + /// Number of concurrent spawning process to launch, None means try to spawn all at the same time. + spawn_concurrency: Option, + /// If enabled, will launch a task to monitor nodes' liveness and tear down the network if there are any. + #[serde(default = "default_as_true")] + tear_down_on_failure: bool, } impl GlobalSettings { - /// External bootnode address. - pub fn bootnodes_addresses(&self) -> Vec<&Multiaddr> { - self.bootnodes_addresses.iter().collect() - } + /// External bootnode address. + pub fn bootnodes_addresses(&self) -> Vec<&Multiaddr> { + self.bootnodes_addresses.iter().collect() + } - /// Global spawn timeout in seconds. - pub fn network_spawn_timeout(&self) -> Duration { - self.network_spawn_timeout - } + /// Global spawn timeout in seconds. + pub fn network_spawn_timeout(&self) -> Duration { + self.network_spawn_timeout + } - /// Individual node spawn timeout in seconds. - pub fn node_spawn_timeout(&self) -> Duration { - self.node_spawn_timeout - } + /// Individual node spawn timeout in seconds. + pub fn node_spawn_timeout(&self) -> Duration { + self.node_spawn_timeout + } - /// Local IP used to expose local services (including RPC, metrics and monitoring). - pub fn local_ip(&self) -> Option<&IpAddr> { - self.local_ip.as_ref() - } + /// Local IP used to expose local services (including RPC, metrics and monitoring). + pub fn local_ip(&self) -> Option<&IpAddr> { + self.local_ip.as_ref() + } - /// Base directory to use (instead a random tmp one) - /// All the artifacts will be created in this directory. - pub fn base_dir(&self) -> Option<&Path> { - self.base_dir.as_deref() - } + /// Base directory to use (instead a random tmp one) + /// All the artifacts will be created in this directory. + pub fn base_dir(&self) -> Option<&Path> { + self.base_dir.as_deref() + } - /// Number of concurrent spawning process to launch - pub fn spawn_concurrency(&self) -> Option { - self.spawn_concurrency - } + /// Number of concurrent spawning process to launch + pub fn spawn_concurrency(&self) -> Option { + self.spawn_concurrency + } - /// A flag to tear down the network if there are any unresponsive nodes detected. - pub fn tear_down_on_failure(&self) -> bool { - self.tear_down_on_failure - } + /// A flag to tear down the network if there are any unresponsive nodes detected. + pub fn tear_down_on_failure(&self) -> bool { + self.tear_down_on_failure + } } impl Default for GlobalSettings { - fn default() -> Self { - Self { - bootnodes_addresses: Default::default(), - network_spawn_timeout: default_timeout(), - node_spawn_timeout: default_node_spawn_timeout(), - local_ip: Default::default(), - base_dir: Default::default(), - spawn_concurrency: Default::default(), - tear_down_on_failure: true, - } - } + fn default() -> Self { + Self { + bootnodes_addresses: Default::default(), + network_spawn_timeout: default_timeout(), + node_spawn_timeout: default_node_spawn_timeout(), + local_ip: Default::default(), + base_dir: Default::default(), + spawn_concurrency: Default::default(), + tear_down_on_failure: true, + } + } } /// A global settings builder, used to build [`GlobalSettings`] declaratively with fields validation. #[derive(Default)] pub struct GlobalSettingsBuilder { - config: GlobalSettings, - errors: Vec, + config: GlobalSettings, + errors: Vec, } impl GlobalSettingsBuilder { - pub fn new() -> Self { - Self::default() - } + pub fn new() -> Self { + Self::default() + } - // Transition to the next state of the builder. - fn transition(config: GlobalSettings, errors: Vec) -> Self { - Self { config, errors } - } + // Transition to the next state of the builder. + fn transition(config: GlobalSettings, errors: Vec) -> Self { + Self { config, errors } + } - /// Set the external bootnode address. - /// - /// Note: Bootnode address replacements are NOT supported here. - /// Only arguments (`args`) support dynamic replacements. Bootnode addresses must be a valid address. - pub fn with_raw_bootnodes_addresses(self, bootnodes_addresses: Vec) -> Self - where - T: TryInto + Display + Copy, - T::Error: Error + Send + Sync + 'static, - { - let mut addrs = vec![]; - let mut errors = vec![]; + /// Set the external bootnode address. + /// + /// Note: Bootnode address replacements are NOT supported here. + /// Only arguments (`args`) support dynamic replacements. Bootnode addresses must be a valid address. + pub fn with_raw_bootnodes_addresses(self, bootnodes_addresses: Vec) -> Self + where + T: TryInto + Display + Copy, + T::Error: Error + Send + Sync + 'static, + { + let mut addrs = vec![]; + let mut errors = vec![]; - for (index, addr) in bootnodes_addresses.into_iter().enumerate() { - match addr.try_into() { - Ok(addr) => addrs.push(addr), - Err(error) => errors.push( - FieldError::BootnodesAddress(index, addr.to_string(), error.into()).into(), - ), - } - } + for (index, addr) in bootnodes_addresses.into_iter().enumerate() { + match addr.try_into() { + Ok(addr) => addrs.push(addr), + Err(error) => errors.push( + FieldError::BootnodesAddress(index, addr.to_string(), error.into()).into(), + ), + } + } - Self::transition( - GlobalSettings { - bootnodes_addresses: addrs, - ..self.config - }, - merge_errors_vecs(self.errors, errors), - ) - } + Self::transition( + GlobalSettings { bootnodes_addresses: addrs, ..self.config }, + merge_errors_vecs(self.errors, errors), + ) + } - /// Set global spawn timeout in seconds. - pub fn with_network_spawn_timeout(self, timeout: Duration) -> Self { - Self::transition( - GlobalSettings { - network_spawn_timeout: timeout, - ..self.config - }, - self.errors, - ) - } + /// Set global spawn timeout in seconds. + pub fn with_network_spawn_timeout(self, timeout: Duration) -> Self { + Self::transition( + GlobalSettings { network_spawn_timeout: timeout, ..self.config }, + self.errors, + ) + } - /// Set individual node spawn timeout in seconds. - pub fn with_node_spawn_timeout(self, timeout: Duration) -> Self { - Self::transition( - GlobalSettings { - node_spawn_timeout: timeout, - ..self.config - }, - self.errors, - ) - } + /// Set individual node spawn timeout in seconds. + pub fn with_node_spawn_timeout(self, timeout: Duration) -> Self { + Self::transition(GlobalSettings { node_spawn_timeout: timeout, ..self.config }, self.errors) + } - /// Set local IP used to expose local services (including RPC, metrics and monitoring). - pub fn with_local_ip(self, local_ip: &str) -> Self { - match IpAddr::from_str(local_ip) { - Ok(local_ip) => Self::transition( - GlobalSettings { - local_ip: Some(local_ip), - ..self.config - }, - self.errors, - ), - Err(error) => Self::transition( - self.config, - merge_errors(self.errors, FieldError::LocalIp(error.into()).into()), - ), - } - } + /// Set local IP used to expose local services (including RPC, metrics and monitoring). + pub fn with_local_ip(self, local_ip: &str) -> Self { + match IpAddr::from_str(local_ip) { + Ok(local_ip) => Self::transition( + GlobalSettings { local_ip: Some(local_ip), ..self.config }, + self.errors, + ), + Err(error) => Self::transition( + self.config, + merge_errors(self.errors, FieldError::LocalIp(error.into()).into()), + ), + } + } - /// Set the directory to use as base (instead of a random tmp one). - pub fn with_base_dir(self, base_dir: impl Into) -> Self { - Self::transition( - GlobalSettings { - base_dir: Some(base_dir.into()), - ..self.config - }, - self.errors, - ) - } + /// Set the directory to use as base (instead of a random tmp one). + pub fn with_base_dir(self, base_dir: impl Into) -> Self { + Self::transition( + GlobalSettings { base_dir: Some(base_dir.into()), ..self.config }, + self.errors, + ) + } - /// Set the spawn concurrency - pub fn with_spawn_concurrency(self, spawn_concurrency: usize) -> Self { - Self::transition( - GlobalSettings { - spawn_concurrency: Some(spawn_concurrency), - ..self.config - }, - self.errors, - ) - } + /// Set the spawn concurrency + pub fn with_spawn_concurrency(self, spawn_concurrency: usize) -> Self { + Self::transition( + GlobalSettings { spawn_concurrency: Some(spawn_concurrency), ..self.config }, + self.errors, + ) + } - /// Set the `tear_down_on_failure` flag - pub fn with_tear_down_on_failure(self, tear_down_on_failure: bool) -> Self { - Self::transition( - GlobalSettings { - tear_down_on_failure, - ..self.config - }, - self.errors, - ) - } + /// Set the `tear_down_on_failure` flag + pub fn with_tear_down_on_failure(self, tear_down_on_failure: bool) -> Self { + Self::transition(GlobalSettings { tear_down_on_failure, ..self.config }, self.errors) + } - /// Seals the builder and returns a [`GlobalSettings`] if there are no validation errors, else returns errors. - pub fn build(self) -> Result> { - if !self.errors.is_empty() { - return Err(self - .errors - .into_iter() - .map(|error| ConfigError::GlobalSettings(error).into()) - .collect::>()); - } + /// Seals the builder and returns a [`GlobalSettings`] if there are no validation errors, else returns errors. + pub fn build(self) -> Result> { + if !self.errors.is_empty() { + return Err(self + .errors + .into_iter() + .map(|error| ConfigError::GlobalSettings(error).into()) + .collect::>()); + } - Ok(self.config) - } + Ok(self.config) + } } #[cfg(test)] mod tests { - use super::*; + use super::*; - #[test] - fn global_settings_config_builder_should_succeeds_and_returns_a_global_settings_config() { - let global_settings_config = GlobalSettingsBuilder::new() - .with_raw_bootnodes_addresses(vec![ - "/ip4/10.41.122.55/tcp/45421", - "/ip4/51.144.222.10/tcp/2333", - ]) - .with_network_spawn_timeout(600) - .with_node_spawn_timeout(120) - .with_local_ip("10.0.0.1") - .with_base_dir("/home/nonroot/mynetwork") - .with_spawn_concurrency(5) - .with_tear_down_on_failure(true) - .build() - .unwrap(); + #[test] + fn global_settings_config_builder_should_succeeds_and_returns_a_global_settings_config() { + let global_settings_config = GlobalSettingsBuilder::new() + .with_raw_bootnodes_addresses(vec![ + "/ip4/10.41.122.55/tcp/45421", + "/ip4/51.144.222.10/tcp/2333", + ]) + .with_network_spawn_timeout(600) + .with_node_spawn_timeout(120) + .with_local_ip("10.0.0.1") + .with_base_dir("/home/nonroot/mynetwork") + .with_spawn_concurrency(5) + .with_tear_down_on_failure(true) + .build() + .unwrap(); - let bootnodes_addresses: Vec = vec![ - "/ip4/10.41.122.55/tcp/45421".try_into().unwrap(), - "/ip4/51.144.222.10/tcp/2333".try_into().unwrap(), - ]; - assert_eq!( - global_settings_config.bootnodes_addresses(), - bootnodes_addresses.iter().collect::>() - ); - assert_eq!(global_settings_config.network_spawn_timeout(), 600); - assert_eq!(global_settings_config.node_spawn_timeout(), 120); - assert_eq!( - global_settings_config - .local_ip() - .unwrap() - .to_string() - .as_str(), - "10.0.0.1" - ); - assert_eq!( - global_settings_config.base_dir().unwrap(), - Path::new("/home/nonroot/mynetwork") - ); - assert_eq!(global_settings_config.spawn_concurrency().unwrap(), 5); - assert!(global_settings_config.tear_down_on_failure()); - } + let bootnodes_addresses: Vec = vec![ + "/ip4/10.41.122.55/tcp/45421".try_into().unwrap(), + "/ip4/51.144.222.10/tcp/2333".try_into().unwrap(), + ]; + assert_eq!( + global_settings_config.bootnodes_addresses(), + bootnodes_addresses.iter().collect::>() + ); + assert_eq!(global_settings_config.network_spawn_timeout(), 600); + assert_eq!(global_settings_config.node_spawn_timeout(), 120); + assert_eq!(global_settings_config.local_ip().unwrap().to_string().as_str(), "10.0.0.1"); + assert_eq!( + global_settings_config.base_dir().unwrap(), + Path::new("/home/nonroot/mynetwork") + ); + assert_eq!(global_settings_config.spawn_concurrency().unwrap(), 5); + assert!(global_settings_config.tear_down_on_failure()); + } - #[test] - fn global_settings_config_builder_should_succeeds_when_node_spawn_timeout_is_missing() { - let global_settings_config = GlobalSettingsBuilder::new() - .with_raw_bootnodes_addresses(vec![ - "/ip4/10.41.122.55/tcp/45421", - "/ip4/51.144.222.10/tcp/2333", - ]) - .with_network_spawn_timeout(600) - .with_local_ip("10.0.0.1") - .build() - .unwrap(); + #[test] + fn global_settings_config_builder_should_succeeds_when_node_spawn_timeout_is_missing() { + let global_settings_config = GlobalSettingsBuilder::new() + .with_raw_bootnodes_addresses(vec![ + "/ip4/10.41.122.55/tcp/45421", + "/ip4/51.144.222.10/tcp/2333", + ]) + .with_network_spawn_timeout(600) + .with_local_ip("10.0.0.1") + .build() + .unwrap(); - let bootnodes_addresses: Vec = vec![ - "/ip4/10.41.122.55/tcp/45421".try_into().unwrap(), - "/ip4/51.144.222.10/tcp/2333".try_into().unwrap(), - ]; - assert_eq!( - global_settings_config.bootnodes_addresses(), - bootnodes_addresses.iter().collect::>() - ); - assert_eq!(global_settings_config.network_spawn_timeout(), 600); - assert_eq!(global_settings_config.node_spawn_timeout(), 600); - assert_eq!( - global_settings_config - .local_ip() - .unwrap() - .to_string() - .as_str(), - "10.0.0.1" - ); - } + let bootnodes_addresses: Vec = vec![ + "/ip4/10.41.122.55/tcp/45421".try_into().unwrap(), + "/ip4/51.144.222.10/tcp/2333".try_into().unwrap(), + ]; + assert_eq!( + global_settings_config.bootnodes_addresses(), + bootnodes_addresses.iter().collect::>() + ); + assert_eq!(global_settings_config.network_spawn_timeout(), 600); + assert_eq!(global_settings_config.node_spawn_timeout(), 600); + assert_eq!(global_settings_config.local_ip().unwrap().to_string().as_str(), "10.0.0.1"); + } - #[test] - fn global_settings_builder_should_fails_and_returns_an_error_if_one_bootnode_address_is_invalid( - ) { - let errors = GlobalSettingsBuilder::new() - .with_raw_bootnodes_addresses(vec!["/ip4//tcp/45421"]) - .build() - .unwrap_err(); + #[test] + fn global_settings_builder_should_fails_and_returns_an_error_if_one_bootnode_address_is_invalid( + ) { + let errors = GlobalSettingsBuilder::new() + .with_raw_bootnodes_addresses(vec!["/ip4//tcp/45421"]) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 1); - assert_eq!( + assert_eq!(errors.len(), 1); + assert_eq!( errors.first().unwrap().to_string(), "global_settings.bootnodes_addresses[0]: '/ip4//tcp/45421' failed to parse: invalid IPv4 address syntax" ); - } + } - #[test] - fn global_settings_builder_should_fails_and_returns_multiple_errors_if_multiple_bootnodes_addresses_are_invalid( - ) { - let errors = GlobalSettingsBuilder::new() - .with_raw_bootnodes_addresses(vec!["/ip4//tcp/45421", "//10.42.153.10/tcp/43111"]) - .build() - .unwrap_err(); + #[test] + fn global_settings_builder_should_fails_and_returns_multiple_errors_if_multiple_bootnodes_addresses_are_invalid( + ) { + let errors = GlobalSettingsBuilder::new() + .with_raw_bootnodes_addresses(vec!["/ip4//tcp/45421", "//10.42.153.10/tcp/43111"]) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 2); - assert_eq!( + assert_eq!(errors.len(), 2); + assert_eq!( errors.first().unwrap().to_string(), "global_settings.bootnodes_addresses[0]: '/ip4//tcp/45421' failed to parse: invalid IPv4 address syntax" ); - assert_eq!( + assert_eq!( errors.get(1).unwrap().to_string(), "global_settings.bootnodes_addresses[1]: '//10.42.153.10/tcp/43111' unknown protocol string: " ); - } + } - #[test] - fn global_settings_builder_should_fails_and_returns_an_error_if_local_ip_is_invalid() { - let errors = GlobalSettingsBuilder::new() - .with_local_ip("invalid") - .build() - .unwrap_err(); + #[test] + fn global_settings_builder_should_fails_and_returns_an_error_if_local_ip_is_invalid() { + let errors = GlobalSettingsBuilder::new().with_local_ip("invalid").build().unwrap_err(); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - "global_settings.local_ip: invalid IP address syntax" - ); - } + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + "global_settings.local_ip: invalid IP address syntax" + ); + } - #[test] - fn global_settings_builder_should_fails_and_returns_multiple_errors_if_multiple_fields_are_invalid( - ) { - let errors = GlobalSettingsBuilder::new() - .with_raw_bootnodes_addresses(vec!["/ip4//tcp/45421", "//10.42.153.10/tcp/43111"]) - .with_local_ip("invalid") - .build() - .unwrap_err(); + #[test] + fn global_settings_builder_should_fails_and_returns_multiple_errors_if_multiple_fields_are_invalid( + ) { + let errors = GlobalSettingsBuilder::new() + .with_raw_bootnodes_addresses(vec!["/ip4//tcp/45421", "//10.42.153.10/tcp/43111"]) + .with_local_ip("invalid") + .build() + .unwrap_err(); - assert_eq!(errors.len(), 3); - assert_eq!( + assert_eq!(errors.len(), 3); + assert_eq!( errors.first().unwrap().to_string(), "global_settings.bootnodes_addresses[0]: '/ip4//tcp/45421' failed to parse: invalid IPv4 address syntax" ); - assert_eq!( + assert_eq!( errors.get(1).unwrap().to_string(), "global_settings.bootnodes_addresses[1]: '//10.42.153.10/tcp/43111' unknown protocol string: " ); - assert_eq!( - errors.get(2).unwrap().to_string(), - "global_settings.local_ip: invalid IP address syntax" - ); - } + assert_eq!( + errors.get(2).unwrap().to_string(), + "global_settings.local_ip: invalid IP address syntax" + ); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/hrmp_channel.rs b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/hrmp_channel.rs index 228ff2ef..47a7a0fc 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/hrmp_channel.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/hrmp_channel.rs @@ -7,131 +7,116 @@ use crate::shared::{macros::states, types::ParaId}; /// HRMP channel configuration, with fine-grained configuration options. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct HrmpChannelConfig { - sender: ParaId, - recipient: ParaId, - max_capacity: u32, - max_message_size: u32, + sender: ParaId, + recipient: ParaId, + max_capacity: u32, + max_message_size: u32, } impl HrmpChannelConfig { - /// The sending parachain ID. - pub fn sender(&self) -> ParaId { - self.sender - } + /// The sending parachain ID. + pub fn sender(&self) -> ParaId { + self.sender + } - /// The receiving parachain ID. - pub fn recipient(&self) -> ParaId { - self.recipient - } + /// The receiving parachain ID. + pub fn recipient(&self) -> ParaId { + self.recipient + } - /// The maximum capacity of messages in the channel. - pub fn max_capacity(&self) -> u32 { - self.max_capacity - } + /// The maximum capacity of messages in the channel. + pub fn max_capacity(&self) -> u32 { + self.max_capacity + } - /// The maximum size of a message in the channel. - pub fn max_message_size(&self) -> u32 { - self.max_message_size - } + /// The maximum size of a message in the channel. + pub fn max_message_size(&self) -> u32 { + self.max_message_size + } } states! { - Initial, - WithSender, - WithRecipient + Initial, + WithSender, + WithRecipient } /// HRMP channel configuration builder, used to build an [`HrmpChannelConfig`] declaratively with fields validation. pub struct HrmpChannelConfigBuilder { - config: HrmpChannelConfig, - _state: PhantomData, + config: HrmpChannelConfig, + _state: PhantomData, } impl Default for HrmpChannelConfigBuilder { - fn default() -> Self { - Self { - config: HrmpChannelConfig { - sender: 0, - recipient: 0, - max_capacity: 8, - max_message_size: 512, - }, - _state: PhantomData, - } - } + fn default() -> Self { + Self { + config: HrmpChannelConfig { + sender: 0, + recipient: 0, + max_capacity: 8, + max_message_size: 512, + }, + _state: PhantomData, + } + } } impl HrmpChannelConfigBuilder { - fn transition(&self, config: HrmpChannelConfig) -> HrmpChannelConfigBuilder { - HrmpChannelConfigBuilder { - config, - _state: PhantomData, - } - } + fn transition(&self, config: HrmpChannelConfig) -> HrmpChannelConfigBuilder { + HrmpChannelConfigBuilder { config, _state: PhantomData } + } } impl HrmpChannelConfigBuilder { - pub fn new() -> Self { - Self::default() - } + pub fn new() -> Self { + Self::default() + } - /// Set the sending parachain ID. - pub fn with_sender(self, sender: ParaId) -> HrmpChannelConfigBuilder { - self.transition(HrmpChannelConfig { - sender, - ..self.config - }) - } + /// Set the sending parachain ID. + pub fn with_sender(self, sender: ParaId) -> HrmpChannelConfigBuilder { + self.transition(HrmpChannelConfig { sender, ..self.config }) + } } impl HrmpChannelConfigBuilder { - /// Set the receiving parachain ID. - pub fn with_recipient(self, recipient: ParaId) -> HrmpChannelConfigBuilder { - self.transition(HrmpChannelConfig { - recipient, - ..self.config - }) - } + /// Set the receiving parachain ID. + pub fn with_recipient(self, recipient: ParaId) -> HrmpChannelConfigBuilder { + self.transition(HrmpChannelConfig { recipient, ..self.config }) + } } impl HrmpChannelConfigBuilder { - /// Set the max capacity of messages in the channel. - pub fn with_max_capacity(self, max_capacity: u32) -> Self { - self.transition(HrmpChannelConfig { - max_capacity, - ..self.config - }) - } + /// Set the max capacity of messages in the channel. + pub fn with_max_capacity(self, max_capacity: u32) -> Self { + self.transition(HrmpChannelConfig { max_capacity, ..self.config }) + } - /// Set the maximum size of a message in the channel. - pub fn with_max_message_size(self, max_message_size: u32) -> Self { - self.transition(HrmpChannelConfig { - max_message_size, - ..self.config - }) - } + /// Set the maximum size of a message in the channel. + pub fn with_max_message_size(self, max_message_size: u32) -> Self { + self.transition(HrmpChannelConfig { max_message_size, ..self.config }) + } - pub fn build(self) -> HrmpChannelConfig { - self.config - } + pub fn build(self) -> HrmpChannelConfig { + self.config + } } #[cfg(test)] mod tests { - use super::*; + use super::*; - #[test] - fn hrmp_channel_config_builder_should_build_a_new_hrmp_channel_config_correctly() { - let hrmp_channel_config = HrmpChannelConfigBuilder::new() - .with_sender(1000) - .with_recipient(2000) - .with_max_capacity(50) - .with_max_message_size(100) - .build(); + #[test] + fn hrmp_channel_config_builder_should_build_a_new_hrmp_channel_config_correctly() { + let hrmp_channel_config = HrmpChannelConfigBuilder::new() + .with_sender(1000) + .with_recipient(2000) + .with_max_capacity(50) + .with_max_message_size(100) + .build(); - assert_eq!(hrmp_channel_config.sender(), 1000); - assert_eq!(hrmp_channel_config.recipient(), 2000); - assert_eq!(hrmp_channel_config.max_capacity(), 50); - assert_eq!(hrmp_channel_config.max_message_size(), 100); - } + assert_eq!(hrmp_channel_config.sender(), 1000); + assert_eq!(hrmp_channel_config.recipient(), 2000); + assert_eq!(hrmp_channel_config.max_capacity(), 50); + assert_eq!(hrmp_channel_config.max_message_size(), 100); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/lib.rs b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/lib.rs index eff9ee31..0e5588da 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/lib.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/lib.rs @@ -93,7 +93,7 @@ pub use relaychain::{RelaychainConfig, RelaychainConfigBuilder}; // re-export shared pub use shared::{node::NodeConfig, types}; pub use teyrchain::{ - states as para_states, RegistrationStrategy, TeyrchainConfig, TeyrchainConfigBuilder, + states as para_states, RegistrationStrategy, TeyrchainConfig, TeyrchainConfigBuilder, }; // Backward compatibility aliases for external crates that use Polkadot SDK terminology diff --git a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/network.rs b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/network.rs index 115d4866..661b3721 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/network.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/network.rs @@ -4,302 +4,283 @@ use anyhow::anyhow; use regex::Regex; use serde::{Deserialize, Serialize}; use support::{ - constants::{ - NO_ERR_DEF_BUILDER, RELAY_NOT_NONE, RW_FAILED, THIS_IS_A_BUG, VALIDATION_CHECK, VALID_REGEX, - }, - replacer::apply_env_replacements, + constants::{ + NO_ERR_DEF_BUILDER, RELAY_NOT_NONE, RW_FAILED, THIS_IS_A_BUG, VALIDATION_CHECK, VALID_REGEX, + }, + replacer::apply_env_replacements, }; use tracing::trace; use crate::{ - global_settings::{GlobalSettings, GlobalSettingsBuilder}, - hrmp_channel::{self, HrmpChannelConfig, HrmpChannelConfigBuilder}, - relaychain::{self, RelaychainConfig, RelaychainConfigBuilder}, - shared::{ - errors::{ConfigError, ValidationError}, - helpers::{generate_unique_node_name_from_names, merge_errors, merge_errors_vecs}, - macros::states, - node::{GroupNodeConfig, NodeConfig}, - types::{Arg, AssetLocation, Chain, Command, Image, ValidationContext}, - }, - teyrchain::{self, TeyrchainConfig, TeyrchainConfigBuilder}, - types::ParaId, - RegistrationStrategy, + global_settings::{GlobalSettings, GlobalSettingsBuilder}, + hrmp_channel::{self, HrmpChannelConfig, HrmpChannelConfigBuilder}, + relaychain::{self, RelaychainConfig, RelaychainConfigBuilder}, + shared::{ + errors::{ConfigError, ValidationError}, + helpers::{generate_unique_node_name_from_names, merge_errors, merge_errors_vecs}, + macros::states, + node::{GroupNodeConfig, NodeConfig}, + types::{Arg, AssetLocation, Chain, Command, Image, ValidationContext}, + }, + teyrchain::{self, TeyrchainConfig, TeyrchainConfigBuilder}, + types::ParaId, + RegistrationStrategy, }; /// A network configuration, composed of a relaychain, teyrchains and HRMP channels. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct NetworkConfig { - #[serde(rename = "settings", default = "GlobalSettings::default")] - global_settings: GlobalSettings, - relaychain: Option, - #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] - teyrchains: Vec, - #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] - hrmp_channels: Vec, + #[serde(rename = "settings", default = "GlobalSettings::default")] + global_settings: GlobalSettings, + relaychain: Option, + #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] + teyrchains: Vec, + #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] + hrmp_channels: Vec, } impl NetworkConfig { - /// The global settings of the network. - pub fn global_settings(&self) -> &GlobalSettings { - &self.global_settings - } + /// The global settings of the network. + pub fn global_settings(&self) -> &GlobalSettings { + &self.global_settings + } - /// The relay chain of the network. - pub fn relaychain(&self) -> &RelaychainConfig { - self.relaychain - .as_ref() - .expect(&format!("{RELAY_NOT_NONE}, {THIS_IS_A_BUG}")) - } + /// The relay chain of the network. + pub fn relaychain(&self) -> &RelaychainConfig { + self.relaychain.as_ref().expect(&format!("{RELAY_NOT_NONE}, {THIS_IS_A_BUG}")) + } - /// The teyrchains of the network. - pub fn teyrchains(&self) -> Vec<&TeyrchainConfig> { - self.teyrchains.iter().collect::>() - } + /// The teyrchains of the network. + pub fn teyrchains(&self) -> Vec<&TeyrchainConfig> { + self.teyrchains.iter().collect::>() + } - /// Backward compatibility alias for teyrchains(). - pub fn parachains(&self) -> Vec<&TeyrchainConfig> { - self.teyrchains() - } + /// Backward compatibility alias for teyrchains(). + pub fn parachains(&self) -> Vec<&TeyrchainConfig> { + self.teyrchains() + } - /// The HRMP channels of the network. - pub fn hrmp_channels(&self) -> Vec<&HrmpChannelConfig> { - self.hrmp_channels.iter().collect::>() - } + /// The HRMP channels of the network. + pub fn hrmp_channels(&self) -> Vec<&HrmpChannelConfig> { + self.hrmp_channels.iter().collect::>() + } - fn set_teyrchains(&mut self, teyrchains: Vec) { - self.teyrchains = teyrchains; - } + fn set_teyrchains(&mut self, teyrchains: Vec) { + self.teyrchains = teyrchains; + } - /// A helper function to dump the network configuration to a TOML string. - pub fn dump_to_toml(&self) -> Result { - // This regex is used to replace the "" enclosed u128 value to a raw u128 because u128 is not supported for TOML serialization/deserialization. - let re = Regex::new(r#""U128%(?\d+)""#) - .expect(&format!("{VALID_REGEX} {THIS_IS_A_BUG}")); - let toml_string = toml::to_string_pretty(&self)?; + /// A helper function to dump the network configuration to a TOML string. + pub fn dump_to_toml(&self) -> Result { + // This regex is used to replace the "" enclosed u128 value to a raw u128 because u128 is not supported for TOML serialization/deserialization. + let re = Regex::new(r#""U128%(?\d+)""#) + .expect(&format!("{VALID_REGEX} {THIS_IS_A_BUG}")); + let toml_string = toml::to_string_pretty(&self)?; - Ok(re.replace_all(&toml_string, "$u128_value").to_string()) - } + Ok(re.replace_all(&toml_string, "$u128_value").to_string()) + } - /// A helper function to load a network configuration from a TOML file. - pub fn load_from_toml_with_settings( - path: &str, - settings: &GlobalSettings, - ) -> Result { - let mut network_config = NetworkConfig::load_from_toml(path)?; - network_config.global_settings = settings.clone(); - Ok(network_config) - } + /// A helper function to load a network configuration from a TOML file. + pub fn load_from_toml_with_settings( + path: &str, + settings: &GlobalSettings, + ) -> Result { + let mut network_config = NetworkConfig::load_from_toml(path)?; + network_config.global_settings = settings.clone(); + Ok(network_config) + } - /// A helper function to load a network configuration from a TOML file. - pub fn load_from_toml(path: &str) -> Result { - let file_str = fs::read_to_string(path).expect(&format!("{RW_FAILED} {THIS_IS_A_BUG}")); - let re: Regex = Regex::new(r"(?(initial_)?balance)\s+=\s+(?\d+)") - .expect(&format!("{VALID_REGEX} {THIS_IS_A_BUG}")); + /// A helper function to load a network configuration from a TOML file. + pub fn load_from_toml(path: &str) -> Result { + let file_str = fs::read_to_string(path).expect(&format!("{RW_FAILED} {THIS_IS_A_BUG}")); + let re: Regex = Regex::new(r"(?(initial_)?balance)\s+=\s+(?\d+)") + .expect(&format!("{VALID_REGEX} {THIS_IS_A_BUG}")); - let toml_text = re.replace_all(&file_str, "$field_name = \"$u128_value\""); - trace!("toml text to parse: {}", toml_text); - // apply replacements from env in toml - let toml_text = apply_env_replacements(&toml_text); - trace!("toml text after replacements: {}", toml_text); - let mut network_config: NetworkConfig = toml::from_str(&toml_text)?; - trace!("parsed config {network_config:#?}"); + let toml_text = re.replace_all(&file_str, "$field_name = \"$u128_value\""); + trace!("toml text to parse: {}", toml_text); + // apply replacements from env in toml + let toml_text = apply_env_replacements(&toml_text); + trace!("toml text after replacements: {}", toml_text); + let mut network_config: NetworkConfig = toml::from_str(&toml_text)?; + trace!("parsed config {network_config:#?}"); - // All unwraps below are safe, because we ensure that the relaychain is not None at this point - if network_config.relaychain.is_none() { - Err(anyhow!("Relay chain does not exist."))? - } + // All unwraps below are safe, because we ensure that the relaychain is not None at this point + if network_config.relaychain.is_none() { + Err(anyhow!("Relay chain does not exist."))? + } - // retrieve the defaults relaychain for assigning to nodes if needed - let mut relaychain_default_command: Option = - network_config.relaychain().default_command().cloned(); + // retrieve the defaults relaychain for assigning to nodes if needed + let mut relaychain_default_command: Option = + network_config.relaychain().default_command().cloned(); - if relaychain_default_command.is_none() { - relaychain_default_command = network_config.relaychain().command().cloned(); - } - let relaychain_default_image: Option = - network_config.relaychain().default_image().cloned(); + if relaychain_default_command.is_none() { + relaychain_default_command = network_config.relaychain().command().cloned(); + } + let relaychain_default_image: Option = + network_config.relaychain().default_image().cloned(); - let relaychain_default_db_snapshot: Option = - network_config.relaychain().default_db_snapshot().cloned(); + let relaychain_default_db_snapshot: Option = + network_config.relaychain().default_db_snapshot().cloned(); - let default_args: Vec = network_config - .relaychain() - .default_args() - .into_iter() - .cloned() - .collect(); + let default_args: Vec = + network_config.relaychain().default_args().into_iter().cloned().collect(); - let mut nodes: Vec = network_config - .relaychain() - .nodes() - .into_iter() - .cloned() - .collect(); + let mut nodes: Vec = + network_config.relaychain().nodes().into_iter().cloned().collect(); - let group_nodes: Vec = network_config - .relaychain() - .group_node_configs() - .into_iter() - .cloned() - .collect(); + let group_nodes: Vec = + network_config.relaychain().group_node_configs().into_iter().cloned().collect(); - if let Some(group) = group_nodes.iter().find(|n| n.count == 0) { - return Err(anyhow!( - "Group node '{}' must have a count greater than 0.", - group.base_config.name() - )); - } + if let Some(group) = group_nodes.iter().find(|n| n.count == 0) { + return Err(anyhow!( + "Group node '{}' must have a count greater than 0.", + group.base_config.name() + )); + } - let mut teyrchains: Vec = - network_config.teyrchains().into_iter().cloned().collect(); + let mut teyrchains: Vec = + network_config.teyrchains().into_iter().cloned().collect(); - // Validation checks for relay - TryInto::::try_into(network_config.relaychain().chain().as_str())?; - if relaychain_default_image.is_some() { - TryInto::::try_into(relaychain_default_image.clone().expect(VALIDATION_CHECK))?; - } - if relaychain_default_command.is_some() { - TryInto::::try_into( - relaychain_default_command.clone().expect(VALIDATION_CHECK), - )?; - } + // Validation checks for relay + TryInto::::try_into(network_config.relaychain().chain().as_str())?; + if relaychain_default_image.is_some() { + TryInto::::try_into(relaychain_default_image.clone().expect(VALIDATION_CHECK))?; + } + if relaychain_default_command.is_some() { + TryInto::::try_into( + relaychain_default_command.clone().expect(VALIDATION_CHECK), + )?; + } - // Keep track of node names to ensure uniqueness - let mut names = HashSet::new(); + // Keep track of node names to ensure uniqueness + let mut names = HashSet::new(); - for node in nodes.iter_mut() { - if relaychain_default_command.is_some() { - // we modify only nodes which don't already have a command - if node.command.is_none() { - node.command.clone_from(&relaychain_default_command); - } - } + for node in nodes.iter_mut() { + if relaychain_default_command.is_some() { + // we modify only nodes which don't already have a command + if node.command.is_none() { + node.command.clone_from(&relaychain_default_command); + } + } - if relaychain_default_image.is_some() && node.image.is_none() { - node.image.clone_from(&relaychain_default_image); - } + if relaychain_default_image.is_some() && node.image.is_none() { + node.image.clone_from(&relaychain_default_image); + } - if relaychain_default_db_snapshot.is_some() && node.db_snapshot.is_none() { - node.db_snapshot.clone_from(&relaychain_default_db_snapshot); - } + if relaychain_default_db_snapshot.is_some() && node.db_snapshot.is_none() { + node.db_snapshot.clone_from(&relaychain_default_db_snapshot); + } - if !default_args.is_empty() && node.args().is_empty() { - node.set_args(default_args.clone()); - } + if !default_args.is_empty() && node.args().is_empty() { + node.set_args(default_args.clone()); + } - let unique_name = generate_unique_node_name_from_names(node.name(), &mut names); - node.name = unique_name; - } + let unique_name = generate_unique_node_name_from_names(node.name(), &mut names); + node.name = unique_name; + } - for para in teyrchains.iter_mut() { - // retrieve the defaults parachain for assigning to collators if needed - let teyrchain_default_command: Option = para.default_command().cloned(); + for para in teyrchains.iter_mut() { + // retrieve the defaults parachain for assigning to collators if needed + let teyrchain_default_command: Option = para.default_command().cloned(); - let teyrchain_default_image: Option = para.default_image().cloned(); + let teyrchain_default_image: Option = para.default_image().cloned(); - let teyrchain_default_db_snapshot: Option = - para.default_db_snapshot().cloned(); + let teyrchain_default_db_snapshot: Option = + para.default_db_snapshot().cloned(); - let default_args: Vec = para.default_args().into_iter().cloned().collect(); + let default_args: Vec = para.default_args().into_iter().cloned().collect(); - let group_collators: Vec = para - .group_collators_configs() - .into_iter() - .cloned() - .collect(); + let group_collators: Vec = + para.group_collators_configs().into_iter().cloned().collect(); - if let Some(group) = group_collators.iter().find(|n| n.count == 0) { - return Err(anyhow!( - "Group node '{}' must have a count greater than 0.", - group.base_config.name() - )); - } + if let Some(group) = group_collators.iter().find(|n| n.count == 0) { + return Err(anyhow!( + "Group node '{}' must have a count greater than 0.", + group.base_config.name() + )); + } - let mut collators: Vec = para.collators.clone(); + let mut collators: Vec = para.collators.clone(); - for collator in collators.iter_mut() { - populate_collator_with_defaults( - collator, - &teyrchain_default_command, - &teyrchain_default_image, - &teyrchain_default_db_snapshot, - &default_args, - ); - let unique_name = generate_unique_node_name_from_names(collator.name(), &mut names); - collator.name = unique_name; - } + for collator in collators.iter_mut() { + populate_collator_with_defaults( + collator, + &teyrchain_default_command, + &teyrchain_default_image, + &teyrchain_default_db_snapshot, + &default_args, + ); + let unique_name = generate_unique_node_name_from_names(collator.name(), &mut names); + collator.name = unique_name; + } - para.collators = collators; + para.collators = collators; - if para.collator.is_some() { - let mut collator = para.collator.clone().unwrap(); - populate_collator_with_defaults( - &mut collator, - &teyrchain_default_command, - &teyrchain_default_image, - &teyrchain_default_db_snapshot, - &default_args, - ); - let unique_name = generate_unique_node_name_from_names(collator.name(), &mut names); - collator.name = unique_name; - para.collator = Some(collator); - } - } + if para.collator.is_some() { + let mut collator = para.collator.clone().unwrap(); + populate_collator_with_defaults( + &mut collator, + &teyrchain_default_command, + &teyrchain_default_image, + &teyrchain_default_db_snapshot, + &default_args, + ); + let unique_name = generate_unique_node_name_from_names(collator.name(), &mut names); + collator.name = unique_name; + para.collator = Some(collator); + } + } - network_config - .relaychain - .as_mut() - .expect(&format!("{NO_ERR_DEF_BUILDER}, {THIS_IS_A_BUG}")) - .set_nodes(nodes); + network_config + .relaychain + .as_mut() + .expect(&format!("{NO_ERR_DEF_BUILDER}, {THIS_IS_A_BUG}")) + .set_nodes(nodes); - network_config.set_teyrchains(teyrchains); + network_config.set_teyrchains(teyrchains); - // Validation checks for teyrchains - network_config.teyrchains().iter().for_each(|parachain| { - if parachain.default_image().is_some() { - let _ = TryInto::::try_into(parachain.default_image().unwrap().as_str()); - } - if parachain.default_command().is_some() { - let _ = TryInto::::try_into(parachain.default_command().unwrap().as_str()); - } - }); - Ok(network_config) - } + // Validation checks for teyrchains + network_config.teyrchains().iter().for_each(|parachain| { + if parachain.default_image().is_some() { + let _ = TryInto::::try_into(parachain.default_image().unwrap().as_str()); + } + if parachain.default_command().is_some() { + let _ = TryInto::::try_into(parachain.default_command().unwrap().as_str()); + } + }); + Ok(network_config) + } } fn populate_collator_with_defaults( - collator: &mut NodeConfig, - teyrchain_default_command: &Option, - teyrchain_default_image: &Option, - teyrchain_default_db_snapshot: &Option, - default_args: &[Arg], + collator: &mut NodeConfig, + teyrchain_default_command: &Option, + teyrchain_default_image: &Option, + teyrchain_default_db_snapshot: &Option, + default_args: &[Arg], ) { - if teyrchain_default_command.is_some() { - // we modify only nodes which don't already have a command - if collator.command.is_none() { - collator.command.clone_from(teyrchain_default_command); - } - } + if teyrchain_default_command.is_some() { + // we modify only nodes which don't already have a command + if collator.command.is_none() { + collator.command.clone_from(teyrchain_default_command); + } + } - if teyrchain_default_image.is_some() && collator.image.is_none() { - collator.image.clone_from(teyrchain_default_image); - } + if teyrchain_default_image.is_some() && collator.image.is_none() { + collator.image.clone_from(teyrchain_default_image); + } - if teyrchain_default_db_snapshot.is_some() && collator.db_snapshot.is_none() { - collator - .db_snapshot - .clone_from(teyrchain_default_db_snapshot); - } + if teyrchain_default_db_snapshot.is_some() && collator.db_snapshot.is_none() { + collator.db_snapshot.clone_from(teyrchain_default_db_snapshot); + } - if !default_args.is_empty() && collator.args().is_empty() { - collator.set_args(default_args.to_owned()); - } + if !default_args.is_empty() && collator.args().is_empty() { + collator.set_args(default_args.to_owned()); + } } states! { - Initial, - WithRelaychain + Initial, + WithRelaychain } /// A network configuration builder, used to build a [`NetworkConfig`] declaratively with fields validation. @@ -375,1461 +356,1370 @@ states! { /// assert!(network_config.is_ok()) /// ``` pub struct NetworkConfigBuilder { - config: NetworkConfig, - validation_context: Rc>, - errors: Vec, - _state: PhantomData, + config: NetworkConfig, + validation_context: Rc>, + errors: Vec, + _state: PhantomData, } impl Default for NetworkConfigBuilder { - fn default() -> Self { - Self { - config: NetworkConfig { - global_settings: GlobalSettingsBuilder::new() - .build() - .expect(&format!("{NO_ERR_DEF_BUILDER}, {THIS_IS_A_BUG}")), - relaychain: None, - teyrchains: vec![], - hrmp_channels: vec![], - }, - validation_context: Default::default(), - errors: vec![], - _state: PhantomData, - } - } + fn default() -> Self { + Self { + config: NetworkConfig { + global_settings: GlobalSettingsBuilder::new() + .build() + .expect(&format!("{NO_ERR_DEF_BUILDER}, {THIS_IS_A_BUG}")), + relaychain: None, + teyrchains: vec![], + hrmp_channels: vec![], + }, + validation_context: Default::default(), + errors: vec![], + _state: PhantomData, + } + } } impl NetworkConfigBuilder { - fn transition( - config: NetworkConfig, - validation_context: Rc>, - errors: Vec, - ) -> NetworkConfigBuilder { - NetworkConfigBuilder { - config, - errors, - validation_context, - _state: PhantomData, - } - } + fn transition( + config: NetworkConfig, + validation_context: Rc>, + errors: Vec, + ) -> NetworkConfigBuilder { + NetworkConfigBuilder { config, errors, validation_context, _state: PhantomData } + } } impl NetworkConfigBuilder { - pub fn new() -> NetworkConfigBuilder { - Self::default() - } + pub fn new() -> NetworkConfigBuilder { + Self::default() + } - /// uses the default options for both the relay chain and the validator nodes - /// the only required fields are the name of the validator nodes, - /// and the name of the relay chain ("rococo-local", "polkadot", etc.) - pub fn with_chain_and_nodes( - relay_name: &str, - node_names: Vec, - ) -> NetworkConfigBuilder { - let network_config = NetworkConfigBuilder::new().with_relaychain(|relaychain| { - let mut relaychain_with_node = - relaychain.with_chain(relay_name).with_validator(|node| { - node.with_name(node_names.first().unwrap_or(&"".to_string())) - }); + /// uses the default options for both the relay chain and the validator nodes + /// the only required fields are the name of the validator nodes, + /// and the name of the relay chain ("rococo-local", "polkadot", etc.) + pub fn with_chain_and_nodes( + relay_name: &str, + node_names: Vec, + ) -> NetworkConfigBuilder { + let network_config = NetworkConfigBuilder::new().with_relaychain(|relaychain| { + let mut relaychain_with_node = + relaychain.with_chain(relay_name).with_validator(|node| { + node.with_name(node_names.first().unwrap_or(&"".to_string())) + }); - for node_name in node_names.iter().skip(1) { - relaychain_with_node = relaychain_with_node - .with_validator(|node_builder| node_builder.with_name(node_name)); - } - relaychain_with_node - }); + for node_name in node_names.iter().skip(1) { + relaychain_with_node = relaychain_with_node + .with_validator(|node_builder| node_builder.with_name(node_name)); + } + relaychain_with_node + }); - Self::transition( - network_config.config, - network_config.validation_context, - network_config.errors, - ) - } + Self::transition( + network_config.config, + network_config.validation_context, + network_config.errors, + ) + } - /// Set the relay chain using a nested [`RelaychainConfigBuilder`]. - pub fn with_relaychain( - self, - f: impl FnOnce( - RelaychainConfigBuilder, - ) -> RelaychainConfigBuilder, - ) -> NetworkConfigBuilder { - match f(RelaychainConfigBuilder::new( - self.validation_context.clone(), - )) - .build() - { - Ok(relaychain) => Self::transition( - NetworkConfig { - relaychain: Some(relaychain), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(errors) => Self::transition(self.config, self.validation_context, errors), - } - } + /// Set the relay chain using a nested [`RelaychainConfigBuilder`]. + pub fn with_relaychain( + self, + f: impl FnOnce( + RelaychainConfigBuilder, + ) -> RelaychainConfigBuilder, + ) -> NetworkConfigBuilder { + match f(RelaychainConfigBuilder::new(self.validation_context.clone())).build() { + Ok(relaychain) => Self::transition( + NetworkConfig { relaychain: Some(relaychain), ..self.config }, + self.validation_context, + self.errors, + ), + Err(errors) => Self::transition(self.config, self.validation_context, errors), + } + } } impl NetworkConfigBuilder { - /// Set the global settings using a nested [`GlobalSettingsBuilder`]. - pub fn with_global_settings( - self, - f: impl FnOnce(GlobalSettingsBuilder) -> GlobalSettingsBuilder, - ) -> Self { - match f(GlobalSettingsBuilder::new()).build() { - Ok(global_settings) => Self::transition( - NetworkConfig { - global_settings, - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(errors) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs(self.errors, errors), - ), - } - } + /// Set the global settings using a nested [`GlobalSettingsBuilder`]. + pub fn with_global_settings( + self, + f: impl FnOnce(GlobalSettingsBuilder) -> GlobalSettingsBuilder, + ) -> Self { + match f(GlobalSettingsBuilder::new()).build() { + Ok(global_settings) => Self::transition( + NetworkConfig { global_settings, ..self.config }, + self.validation_context, + self.errors, + ), + Err(errors) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs(self.errors, errors), + ), + } + } - /// Add a teyrchain using a nested [`TeyrchainConfigBuilder`]. - pub fn with_teyrchain( - self, - f: impl FnOnce( - TeyrchainConfigBuilder, - ) -> TeyrchainConfigBuilder< - teyrchain::states::WithAtLeastOneCollator, - teyrchain::states::Bootstrap, - >, - ) -> Self { - match f(TeyrchainConfigBuilder::new(self.validation_context.clone())).build() { - Ok(teyrchain) => Self::transition( - NetworkConfig { - teyrchains: [self.config.teyrchains, vec![teyrchain]].concat(), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(errors) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs(self.errors, errors), - ), - } - } + /// Add a teyrchain using a nested [`TeyrchainConfigBuilder`]. + pub fn with_teyrchain( + self, + f: impl FnOnce( + TeyrchainConfigBuilder, + ) -> TeyrchainConfigBuilder< + teyrchain::states::WithAtLeastOneCollator, + teyrchain::states::Bootstrap, + >, + ) -> Self { + match f(TeyrchainConfigBuilder::new(self.validation_context.clone())).build() { + Ok(teyrchain) => Self::transition( + NetworkConfig { + teyrchains: [self.config.teyrchains, vec![teyrchain]].concat(), + ..self.config + }, + self.validation_context, + self.errors, + ), + Err(errors) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs(self.errors, errors), + ), + } + } - /// Backward compatibility alias for with_teyrchain(). - pub fn with_parachain( - self, - f: impl FnOnce( - TeyrchainConfigBuilder, - ) -> TeyrchainConfigBuilder< - teyrchain::states::WithAtLeastOneCollator, - teyrchain::states::Bootstrap, - >, - ) -> Self { - self.with_teyrchain(f) - } + /// Backward compatibility alias for with_teyrchain(). + pub fn with_parachain( + self, + f: impl FnOnce( + TeyrchainConfigBuilder, + ) -> TeyrchainConfigBuilder< + teyrchain::states::WithAtLeastOneCollator, + teyrchain::states::Bootstrap, + >, + ) -> Self { + self.with_teyrchain(f) + } - /// uses default settings for setting for: - /// - the parachain, - /// - the global settings - /// - the hrmp channels - /// - /// the only required parameters are the names of the collators as a vector, - /// and the id of the parachain - pub fn with_parachain_id_and_collators(self, id: u32, collator_names: Vec) -> Self { - if collator_names.is_empty() { - return Self::transition( - self.config, - self.validation_context, - merge_errors( - self.errors, - ConfigError::Teyrchain(id, ValidationError::CantBeEmpty().into()).into(), - ), - ); - } + /// uses default settings for setting for: + /// - the parachain, + /// - the global settings + /// - the hrmp channels + /// + /// the only required parameters are the names of the collators as a vector, + /// and the id of the parachain + pub fn with_parachain_id_and_collators(self, id: u32, collator_names: Vec) -> Self { + if collator_names.is_empty() { + return Self::transition( + self.config, + self.validation_context, + merge_errors( + self.errors, + ConfigError::Teyrchain(id, ValidationError::CantBeEmpty().into()).into(), + ), + ); + } - self.with_parachain(|parachain| { - let mut parachain_config = parachain.with_id(id).with_collator(|collator| { - collator - .with_name(collator_names.first().unwrap_or(&"".to_string())) - .validator(true) - }); + self.with_parachain(|parachain| { + let mut parachain_config = parachain.with_id(id).with_collator(|collator| { + collator + .with_name(collator_names.first().unwrap_or(&"".to_string())) + .validator(true) + }); - for collator_name in collator_names.iter().skip(1) { - parachain_config = parachain_config - .with_collator(|collator| collator.with_name(collator_name).validator(true)); - } - parachain_config - }) + for collator_name in collator_names.iter().skip(1) { + parachain_config = parachain_config + .with_collator(|collator| collator.with_name(collator_name).validator(true)); + } + parachain_config + }) - // TODO: if need to set global settings and hrmp channels - // we can also do in here - } + // TODO: if need to set global settings and hrmp channels + // we can also do in here + } - /// Add an HRMP channel using a nested [`HrmpChannelConfigBuilder`]. - pub fn with_hrmp_channel( - self, - f: impl FnOnce( - HrmpChannelConfigBuilder, - ) -> HrmpChannelConfigBuilder, - ) -> Self { - let new_hrmp_channel = f(HrmpChannelConfigBuilder::new()).build(); + /// Add an HRMP channel using a nested [`HrmpChannelConfigBuilder`]. + pub fn with_hrmp_channel( + self, + f: impl FnOnce( + HrmpChannelConfigBuilder, + ) -> HrmpChannelConfigBuilder, + ) -> Self { + let new_hrmp_channel = f(HrmpChannelConfigBuilder::new()).build(); - Self::transition( - NetworkConfig { - hrmp_channels: [self.config.hrmp_channels, vec![new_hrmp_channel]].concat(), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + Self::transition( + NetworkConfig { + hrmp_channels: [self.config.hrmp_channels, vec![new_hrmp_channel]].concat(), + ..self.config + }, + self.validation_context, + self.errors, + ) + } - /// Seals the builder and returns a [`NetworkConfig`] if there are no validation errors, else returns errors. - pub fn build(self) -> Result> { - let mut paras_to_register: HashSet = Default::default(); - let mut errs: Vec = self - .config - .teyrchains - .iter() - .filter_map(|para| { - if let Some(RegistrationStrategy::Manual) = para.registration_strategy() { - return None; - }; + /// Seals the builder and returns a [`NetworkConfig`] if there are no validation errors, else returns errors. + pub fn build(self) -> Result> { + let mut paras_to_register: HashSet = Default::default(); + let mut errs: Vec = self + .config + .teyrchains + .iter() + .filter_map(|para| { + if let Some(RegistrationStrategy::Manual) = para.registration_strategy() { + return None; + }; - if paras_to_register.insert(para.id()) { - None - } else { - // already in the set - Some(anyhow!( - "ParaId {} already set to be registered, only one should be.", - para.id() - )) - } - }) - .collect(); + if paras_to_register.insert(para.id()) { + None + } else { + // already in the set + Some(anyhow!( + "ParaId {} already set to be registered, only one should be.", + para.id() + )) + } + }) + .collect(); - if !self.errors.is_empty() || !errs.is_empty() { - let mut ret_errs = self.errors; - ret_errs.append(&mut errs); - return Err(ret_errs); - } + if !self.errors.is_empty() || !errs.is_empty() { + let mut ret_errs = self.errors; + ret_errs.append(&mut errs); + return Err(ret_errs); + } - Ok(self.config) - } + Ok(self.config) + } } #[cfg(test)] mod tests { - use std::path::PathBuf; + use std::path::PathBuf; - use super::*; - use crate::teyrchain::RegistrationStrategy; + use super::*; + use crate::teyrchain::RegistrationStrategy; - #[test] - fn network_config_builder_should_succeeds_and_returns_a_network_config() { - let network_config = NetworkConfigBuilder::new() - .with_relaychain(|relaychain| { - relaychain - .with_chain("polkadot") - .with_random_nominators_count(10) - .with_validator(|node| node.with_name("node").with_command("command")) - }) - .with_parachain(|parachain| { - parachain - .with_id(1) - .with_chain("myparachain1") - .with_initial_balance(100_000) - .with_collator(|collator| { - collator - .with_name("collator1") - .with_command("command1") - .validator(true) - }) - }) - .with_parachain(|parachain| { - parachain - .with_id(2) - .with_chain("myparachain2") - .with_initial_balance(0) - .with_collator(|collator| { - collator - .with_name("collator2") - .with_command("command2") - .validator(true) - }) - }) - .with_hrmp_channel(|hrmp_channel1| { - hrmp_channel1 - .with_sender(1) - .with_recipient(2) - .with_max_capacity(200) - .with_max_message_size(500) - }) - .with_hrmp_channel(|hrmp_channel2| { - hrmp_channel2 - .with_sender(2) - .with_recipient(1) - .with_max_capacity(100) - .with_max_message_size(250) - }) - .with_global_settings(|global_settings| { - global_settings - .with_network_spawn_timeout(1200) - .with_node_spawn_timeout(240) - }) - .build() - .unwrap(); + #[test] + fn network_config_builder_should_succeeds_and_returns_a_network_config() { + let network_config = NetworkConfigBuilder::new() + .with_relaychain(|relaychain| { + relaychain + .with_chain("polkadot") + .with_random_nominators_count(10) + .with_validator(|node| node.with_name("node").with_command("command")) + }) + .with_parachain(|parachain| { + parachain + .with_id(1) + .with_chain("myparachain1") + .with_initial_balance(100_000) + .with_collator(|collator| { + collator.with_name("collator1").with_command("command1").validator(true) + }) + }) + .with_parachain(|parachain| { + parachain + .with_id(2) + .with_chain("myparachain2") + .with_initial_balance(0) + .with_collator(|collator| { + collator.with_name("collator2").with_command("command2").validator(true) + }) + }) + .with_hrmp_channel(|hrmp_channel1| { + hrmp_channel1 + .with_sender(1) + .with_recipient(2) + .with_max_capacity(200) + .with_max_message_size(500) + }) + .with_hrmp_channel(|hrmp_channel2| { + hrmp_channel2 + .with_sender(2) + .with_recipient(1) + .with_max_capacity(100) + .with_max_message_size(250) + }) + .with_global_settings(|global_settings| { + global_settings.with_network_spawn_timeout(1200).with_node_spawn_timeout(240) + }) + .build() + .unwrap(); - // relaychain - assert_eq!(network_config.relaychain().chain().as_str(), "polkadot"); - assert_eq!(network_config.relaychain().nodes().len(), 1); - let &node = network_config.relaychain().nodes().first().unwrap(); - assert_eq!(node.name(), "node"); - assert_eq!(node.command().unwrap().as_str(), "command"); - assert!(node.is_validator()); - assert_eq!( - network_config - .relaychain() - .random_nominators_count() - .unwrap(), - 10 - ); + // relaychain + assert_eq!(network_config.relaychain().chain().as_str(), "polkadot"); + assert_eq!(network_config.relaychain().nodes().len(), 1); + let &node = network_config.relaychain().nodes().first().unwrap(); + assert_eq!(node.name(), "node"); + assert_eq!(node.command().unwrap().as_str(), "command"); + assert!(node.is_validator()); + assert_eq!(network_config.relaychain().random_nominators_count().unwrap(), 10); - // teyrchains - assert_eq!(network_config.teyrchains().len(), 2); + // teyrchains + assert_eq!(network_config.teyrchains().len(), 2); - // parachain1 - let ¶chain1 = network_config.teyrchains().first().unwrap(); - assert_eq!(parachain1.id(), 1); - assert_eq!(parachain1.collators().len(), 1); - let &collator = parachain1.collators().first().unwrap(); - assert_eq!(collator.name(), "collator1"); - assert_eq!(collator.command().unwrap().as_str(), "command1"); - assert!(collator.is_validator()); - assert_eq!(parachain1.initial_balance(), 100_000); - assert_eq!(parachain1.unique_id(), "1"); + // parachain1 + let ¶chain1 = network_config.teyrchains().first().unwrap(); + assert_eq!(parachain1.id(), 1); + assert_eq!(parachain1.collators().len(), 1); + let &collator = parachain1.collators().first().unwrap(); + assert_eq!(collator.name(), "collator1"); + assert_eq!(collator.command().unwrap().as_str(), "command1"); + assert!(collator.is_validator()); + assert_eq!(parachain1.initial_balance(), 100_000); + assert_eq!(parachain1.unique_id(), "1"); - // parachain2 - let ¶chain2 = network_config.teyrchains().last().unwrap(); - assert_eq!(parachain2.id(), 2); - assert_eq!(parachain2.collators().len(), 1); - let &collator = parachain2.collators().first().unwrap(); - assert_eq!(collator.name(), "collator2"); - assert_eq!(collator.command().unwrap().as_str(), "command2"); - assert!(collator.is_validator()); - assert_eq!(parachain2.initial_balance(), 0); + // parachain2 + let ¶chain2 = network_config.teyrchains().last().unwrap(); + assert_eq!(parachain2.id(), 2); + assert_eq!(parachain2.collators().len(), 1); + let &collator = parachain2.collators().first().unwrap(); + assert_eq!(collator.name(), "collator2"); + assert_eq!(collator.command().unwrap().as_str(), "command2"); + assert!(collator.is_validator()); + assert_eq!(parachain2.initial_balance(), 0); - // hrmp_channels - assert_eq!(network_config.hrmp_channels().len(), 2); + // hrmp_channels + assert_eq!(network_config.hrmp_channels().len(), 2); - // hrmp_channel1 - let &hrmp_channel1 = network_config.hrmp_channels().first().unwrap(); - assert_eq!(hrmp_channel1.sender(), 1); - assert_eq!(hrmp_channel1.recipient(), 2); - assert_eq!(hrmp_channel1.max_capacity(), 200); - assert_eq!(hrmp_channel1.max_message_size(), 500); + // hrmp_channel1 + let &hrmp_channel1 = network_config.hrmp_channels().first().unwrap(); + assert_eq!(hrmp_channel1.sender(), 1); + assert_eq!(hrmp_channel1.recipient(), 2); + assert_eq!(hrmp_channel1.max_capacity(), 200); + assert_eq!(hrmp_channel1.max_message_size(), 500); - // hrmp_channel2 - let &hrmp_channel2 = network_config.hrmp_channels().last().unwrap(); - assert_eq!(hrmp_channel2.sender(), 2); - assert_eq!(hrmp_channel2.recipient(), 1); - assert_eq!(hrmp_channel2.max_capacity(), 100); - assert_eq!(hrmp_channel2.max_message_size(), 250); + // hrmp_channel2 + let &hrmp_channel2 = network_config.hrmp_channels().last().unwrap(); + assert_eq!(hrmp_channel2.sender(), 2); + assert_eq!(hrmp_channel2.recipient(), 1); + assert_eq!(hrmp_channel2.max_capacity(), 100); + assert_eq!(hrmp_channel2.max_message_size(), 250); - // global settings - assert_eq!( - network_config.global_settings().network_spawn_timeout(), - 1200 - ); - assert_eq!(network_config.global_settings().node_spawn_timeout(), 240); - assert!(network_config.global_settings().tear_down_on_failure()); - } + // global settings + assert_eq!(network_config.global_settings().network_spawn_timeout(), 1200); + assert_eq!(network_config.global_settings().node_spawn_timeout(), 240); + assert!(network_config.global_settings().tear_down_on_failure()); + } - #[test] - fn network_config_builder_should_fails_and_returns_multiple_errors_if_relaychain_is_invalid() { - let errors = NetworkConfigBuilder::new() - .with_relaychain(|relaychain| { - relaychain - .with_chain("polkadot") - .with_random_nominators_count(10) - .with_default_image("invalid.image") - .with_validator(|node| node.with_name("node").with_command("invalid command")) - }) - .with_parachain(|parachain| { - parachain - .with_id(1) - .with_chain("myparachain") - .with_initial_balance(100_000) - .with_collator(|collator| { - collator - .with_name("collator1") - .with_command("command1") - .validator(true) - }) - }) - .build() - .unwrap_err(); + #[test] + fn network_config_builder_should_fails_and_returns_multiple_errors_if_relaychain_is_invalid() { + let errors = NetworkConfigBuilder::new() + .with_relaychain(|relaychain| { + relaychain + .with_chain("polkadot") + .with_random_nominators_count(10) + .with_default_image("invalid.image") + .with_validator(|node| node.with_name("node").with_command("invalid command")) + }) + .with_parachain(|parachain| { + parachain + .with_id(1) + .with_chain("myparachain") + .with_initial_balance(100_000) + .with_collator(|collator| { + collator.with_name("collator1").with_command("command1").validator(true) + }) + }) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 2); - assert_eq!( + assert_eq!(errors.len(), 2); + assert_eq!( errors.first().unwrap().to_string(), "relaychain.default_image: 'invalid.image' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'" ); - assert_eq!( - errors.get(1).unwrap().to_string(), - "relaychain.nodes['node'].command: 'invalid command' shouldn't contains whitespace" - ); - } + assert_eq!( + errors.get(1).unwrap().to_string(), + "relaychain.nodes['node'].command: 'invalid command' shouldn't contains whitespace" + ); + } - #[test] - fn network_config_builder_should_fails_and_returns_multiple_errors_if_parachain_is_invalid() { - let errors = NetworkConfigBuilder::new() - .with_relaychain(|relaychain| { - relaychain - .with_chain("polkadot") - .with_random_nominators_count(10) - .with_validator(|node| node.with_name("node").with_command("command")) - }) - .with_parachain(|parachain| { - parachain - .with_id(1000) - .with_chain("myparachain") - .with_initial_balance(100_000) - .with_collator(|collator| { - collator - .with_name("collator1") - .with_command("invalid command") - .with_image("invalid.image") - .validator(true) - }) - }) - .build() - .unwrap_err(); + #[test] + fn network_config_builder_should_fails_and_returns_multiple_errors_if_parachain_is_invalid() { + let errors = NetworkConfigBuilder::new() + .with_relaychain(|relaychain| { + relaychain + .with_chain("polkadot") + .with_random_nominators_count(10) + .with_validator(|node| node.with_name("node").with_command("command")) + }) + .with_parachain(|parachain| { + parachain + .with_id(1000) + .with_chain("myparachain") + .with_initial_balance(100_000) + .with_collator(|collator| { + collator + .with_name("collator1") + .with_command("invalid command") + .with_image("invalid.image") + .validator(true) + }) + }) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 2); - assert_eq!( + assert_eq!(errors.len(), 2); + assert_eq!( errors.first().unwrap().to_string(), "teyrchain[1000].collators['collator1'].command: 'invalid command' shouldn't contains whitespace" ); - assert_eq!( + assert_eq!( errors.get(1).unwrap().to_string(), "teyrchain[1000].collators['collator1'].image: 'invalid.image' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'" ); - } + } - #[test] - fn network_config_builder_should_fails_and_returns_multiple_errors_if_multiple_teyrchains_are_invalid( - ) { - let errors = NetworkConfigBuilder::new() - .with_relaychain(|relaychain| { - relaychain - .with_chain("polkadot") - .with_random_nominators_count(10) - .with_validator(|node| node.with_name("node").with_command("command")) - }) - .with_parachain(|parachain| { - parachain - .with_id(1000) - .with_chain("myparachain1") - .with_initial_balance(100_000) - .with_collator(|collator| { - collator - .with_name("collator1") - .with_command("invalid command") - }) - }) - .with_parachain(|parachain| { - parachain - .with_id(2000) - .with_chain("myparachain2") - .with_initial_balance(100_000) - .with_collator(|collator| { - collator - .with_name("collator2") - .validator(true) - .with_resources(|resources| { - resources - .with_limit_cpu("1000m") - .with_request_memory("1Gi") - .with_request_cpu("invalid") - }) - }) - }) - .build() - .unwrap_err(); + #[test] + fn network_config_builder_should_fails_and_returns_multiple_errors_if_multiple_teyrchains_are_invalid( + ) { + let errors = NetworkConfigBuilder::new() + .with_relaychain(|relaychain| { + relaychain + .with_chain("polkadot") + .with_random_nominators_count(10) + .with_validator(|node| node.with_name("node").with_command("command")) + }) + .with_parachain(|parachain| { + parachain + .with_id(1000) + .with_chain("myparachain1") + .with_initial_balance(100_000) + .with_collator(|collator| { + collator.with_name("collator1").with_command("invalid command") + }) + }) + .with_parachain(|parachain| { + parachain + .with_id(2000) + .with_chain("myparachain2") + .with_initial_balance(100_000) + .with_collator(|collator| { + collator.with_name("collator2").validator(true).with_resources( + |resources| { + resources + .with_limit_cpu("1000m") + .with_request_memory("1Gi") + .with_request_cpu("invalid") + }, + ) + }) + }) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 2); - assert_eq!( + assert_eq!(errors.len(), 2); + assert_eq!( errors.first().unwrap().to_string(), "teyrchain[1000].collators['collator1'].command: 'invalid command' shouldn't contains whitespace" ); - assert_eq!( + assert_eq!( errors.get(1).unwrap().to_string(), "teyrchain[2000].collators['collator2'].resources.request_cpu: 'invalid' doesn't match regex '^\\d+(.\\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" ); - } + } - #[test] - fn network_config_builder_should_fails_and_returns_multiple_errors_if_global_settings_is_invalid( - ) { - let errors = NetworkConfigBuilder::new() - .with_relaychain(|relaychain| { - relaychain - .with_chain("polkadot") - .with_random_nominators_count(10) - .with_validator(|node| node.with_name("node").with_command("command")) - }) - .with_parachain(|parachain| { - parachain - .with_id(1000) - .with_chain("myparachain") - .with_initial_balance(100_000) - .with_collator(|collator| { - collator - .with_name("collator") - .with_command("command") - .validator(true) - }) - }) - .with_global_settings(|global_settings| { - global_settings - .with_local_ip("127.0.0000.1") - .with_raw_bootnodes_addresses(vec!["/ip4//tcp/45421"]) - }) - .build() - .unwrap_err(); + #[test] + fn network_config_builder_should_fails_and_returns_multiple_errors_if_global_settings_is_invalid( + ) { + let errors = NetworkConfigBuilder::new() + .with_relaychain(|relaychain| { + relaychain + .with_chain("polkadot") + .with_random_nominators_count(10) + .with_validator(|node| node.with_name("node").with_command("command")) + }) + .with_parachain(|parachain| { + parachain + .with_id(1000) + .with_chain("myparachain") + .with_initial_balance(100_000) + .with_collator(|collator| { + collator.with_name("collator").with_command("command").validator(true) + }) + }) + .with_global_settings(|global_settings| { + global_settings + .with_local_ip("127.0.0000.1") + .with_raw_bootnodes_addresses(vec!["/ip4//tcp/45421"]) + }) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 2); - assert_eq!( - errors.first().unwrap().to_string(), - "global_settings.local_ip: invalid IP address syntax" - ); - assert_eq!( + assert_eq!(errors.len(), 2); + assert_eq!( + errors.first().unwrap().to_string(), + "global_settings.local_ip: invalid IP address syntax" + ); + assert_eq!( errors.get(1).unwrap().to_string(), "global_settings.bootnodes_addresses[0]: '/ip4//tcp/45421' failed to parse: invalid IPv4 address syntax" ); - } + } - #[test] - fn network_config_builder_should_fails_and_returns_multiple_errors_if_multiple_fields_are_invalid( - ) { - let errors = NetworkConfigBuilder::new() - .with_relaychain(|relaychain| { - relaychain - .with_chain("polkadot") - .with_random_nominators_count(10) - .with_validator(|node| node.with_name("node").with_command("invalid command")) - }) - .with_parachain(|parachain| { - parachain - .with_id(1000) - .with_chain("myparachain") - .with_initial_balance(100_000) - .with_collator(|collator| { - collator - .with_name("collator") - .with_command("command") - .with_image("invalid.image") - }) - }) - .with_global_settings(|global_settings| global_settings.with_local_ip("127.0.0000.1")) - .build() - .unwrap_err(); + #[test] + fn network_config_builder_should_fails_and_returns_multiple_errors_if_multiple_fields_are_invalid( + ) { + let errors = NetworkConfigBuilder::new() + .with_relaychain(|relaychain| { + relaychain + .with_chain("polkadot") + .with_random_nominators_count(10) + .with_validator(|node| node.with_name("node").with_command("invalid command")) + }) + .with_parachain(|parachain| { + parachain + .with_id(1000) + .with_chain("myparachain") + .with_initial_balance(100_000) + .with_collator(|collator| { + collator + .with_name("collator") + .with_command("command") + .with_image("invalid.image") + }) + }) + .with_global_settings(|global_settings| global_settings.with_local_ip("127.0.0000.1")) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 3); - assert_eq!( - errors.first().unwrap().to_string(), - "relaychain.nodes['node'].command: 'invalid command' shouldn't contains whitespace" - ); - assert_eq!( + assert_eq!(errors.len(), 3); + assert_eq!( + errors.first().unwrap().to_string(), + "relaychain.nodes['node'].command: 'invalid command' shouldn't contains whitespace" + ); + assert_eq!( errors.get(1).unwrap().to_string(), "teyrchain[1000].collators['collator'].image: 'invalid.image' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'" ); - assert_eq!( - errors.get(2).unwrap().to_string(), - "global_settings.local_ip: invalid IP address syntax" - ); - } + assert_eq!( + errors.get(2).unwrap().to_string(), + "global_settings.local_ip: invalid IP address syntax" + ); + } - #[test] - fn network_config_should_be_dumpable_to_a_toml_config_for_a_small_network() { - let network_config = NetworkConfigBuilder::new() - .with_relaychain(|relaychain| { - relaychain - .with_chain("rococo-local") - .with_default_command("polkadot") - .with_default_image("docker.io/parity/polkadot:latest") - .with_default_args(vec![("-lparachain", "debug").into()]) - .with_validator(|node| node.with_name("alice")) - .with_validator(|node| { - node.with_name("bob") - .invulnerable(false) - .bootnode(true) - .with_args(vec![("--database", "paritydb-experimental").into()]) - }) - }) - .build() - .unwrap(); + #[test] + fn network_config_should_be_dumpable_to_a_toml_config_for_a_small_network() { + let network_config = NetworkConfigBuilder::new() + .with_relaychain(|relaychain| { + relaychain + .with_chain("rococo-local") + .with_default_command("polkadot") + .with_default_image("docker.io/parity/polkadot:latest") + .with_default_args(vec![("-lparachain", "debug").into()]) + .with_validator(|node| node.with_name("alice")) + .with_validator(|node| { + node.with_name("bob").invulnerable(false).bootnode(true).with_args(vec![( + "--database", + "paritydb-experimental", + ) + .into()]) + }) + }) + .build() + .unwrap(); - let got = network_config.dump_to_toml().unwrap(); - let expected = fs::read_to_string("./testing/snapshots/0000-small-network.toml").unwrap(); - assert_eq!(got, expected); - } + let got = network_config.dump_to_toml().unwrap(); + let expected = fs::read_to_string("./testing/snapshots/0000-small-network.toml").unwrap(); + assert_eq!(got, expected); + } - #[test] - fn network_config_should_be_dumpable_to_a_toml_config_for_a_big_network() { - let network_config = NetworkConfigBuilder::new() - .with_relaychain(|relaychain| { - relaychain - .with_chain("polkadot") - .with_default_command("polkadot") - .with_default_image("docker.io/parity/polkadot:latest") - .with_default_resources(|resources| { - resources - .with_request_cpu(100000) - .with_request_memory("500M") - .with_limit_cpu("10Gi") - .with_limit_memory("4000M") - }) - .with_validator(|node| { - node.with_name("alice") - .with_initial_balance(1_000_000_000) - .bootnode(true) - .invulnerable(true) - }) - .with_validator(|node| node.with_name("bob").invulnerable(true).bootnode(true)) - }) - .with_parachain(|parachain| { - parachain - .with_id(1000) - .with_chain("myparachain") - .with_chain_spec_path("/path/to/my/chain/spec.json") - .with_registration_strategy(RegistrationStrategy::UsingExtrinsic) - .onboard_as_parachain(false) - .with_default_db_snapshot("https://storage.com/path/to/db_snapshot.tgz") - .with_collator(|collator| { - collator - .with_name("john") - .bootnode(true) - .invulnerable(true) - .with_initial_balance(5_000_000_000) - }) - .with_fullnode(|collator| { - collator - .with_name("charles") - .bootnode(true) - .invulnerable(true) - .with_initial_balance(0) - }) - .with_collator(|collator| { - collator - .with_name("frank") - .invulnerable(false) - .bootnode(true) - .with_initial_balance(1_000_000_000) - }) - }) - .with_parachain(|parachain| { - parachain - .with_id(2000) - .with_chain("myotherparachain") - .with_chain_spec_path("/path/to/my/other/chain/spec.json") - .with_collator(|collator| { - collator - .with_name("mike") - .bootnode(true) - .invulnerable(true) - .with_initial_balance(5_000_000_000) - }) - .with_fullnode(|collator| { - collator - .with_name("georges") - .bootnode(true) - .invulnerable(true) - .with_initial_balance(0) - }) - .with_collator(|collator| { - collator - .with_name("victor") - .invulnerable(false) - .bootnode(true) - .with_initial_balance(1_000_000_000) - }) - }) - .with_hrmp_channel(|hrmp_channel| { - hrmp_channel - .with_sender(1000) - .with_recipient(2000) - .with_max_capacity(150) - .with_max_message_size(5000) - }) - .with_hrmp_channel(|hrmp_channel| { - hrmp_channel - .with_sender(2000) - .with_recipient(1000) - .with_max_capacity(200) - .with_max_message_size(8000) - }) - .build() - .unwrap(); + #[test] + fn network_config_should_be_dumpable_to_a_toml_config_for_a_big_network() { + let network_config = NetworkConfigBuilder::new() + .with_relaychain(|relaychain| { + relaychain + .with_chain("polkadot") + .with_default_command("polkadot") + .with_default_image("docker.io/parity/polkadot:latest") + .with_default_resources(|resources| { + resources + .with_request_cpu(100000) + .with_request_memory("500M") + .with_limit_cpu("10Gi") + .with_limit_memory("4000M") + }) + .with_validator(|node| { + node.with_name("alice") + .with_initial_balance(1_000_000_000) + .bootnode(true) + .invulnerable(true) + }) + .with_validator(|node| node.with_name("bob").invulnerable(true).bootnode(true)) + }) + .with_parachain(|parachain| { + parachain + .with_id(1000) + .with_chain("myparachain") + .with_chain_spec_path("/path/to/my/chain/spec.json") + .with_registration_strategy(RegistrationStrategy::UsingExtrinsic) + .onboard_as_parachain(false) + .with_default_db_snapshot("https://storage.com/path/to/db_snapshot.tgz") + .with_collator(|collator| { + collator + .with_name("john") + .bootnode(true) + .invulnerable(true) + .with_initial_balance(5_000_000_000) + }) + .with_fullnode(|collator| { + collator + .with_name("charles") + .bootnode(true) + .invulnerable(true) + .with_initial_balance(0) + }) + .with_collator(|collator| { + collator + .with_name("frank") + .invulnerable(false) + .bootnode(true) + .with_initial_balance(1_000_000_000) + }) + }) + .with_parachain(|parachain| { + parachain + .with_id(2000) + .with_chain("myotherparachain") + .with_chain_spec_path("/path/to/my/other/chain/spec.json") + .with_collator(|collator| { + collator + .with_name("mike") + .bootnode(true) + .invulnerable(true) + .with_initial_balance(5_000_000_000) + }) + .with_fullnode(|collator| { + collator + .with_name("georges") + .bootnode(true) + .invulnerable(true) + .with_initial_balance(0) + }) + .with_collator(|collator| { + collator + .with_name("victor") + .invulnerable(false) + .bootnode(true) + .with_initial_balance(1_000_000_000) + }) + }) + .with_hrmp_channel(|hrmp_channel| { + hrmp_channel + .with_sender(1000) + .with_recipient(2000) + .with_max_capacity(150) + .with_max_message_size(5000) + }) + .with_hrmp_channel(|hrmp_channel| { + hrmp_channel + .with_sender(2000) + .with_recipient(1000) + .with_max_capacity(200) + .with_max_message_size(8000) + }) + .build() + .unwrap(); - let got = network_config.dump_to_toml().unwrap(); - let expected = fs::read_to_string("./testing/snapshots/0001-big-network.toml").unwrap(); - assert_eq!(got, expected); - } + let got = network_config.dump_to_toml().unwrap(); + let expected = fs::read_to_string("./testing/snapshots/0001-big-network.toml").unwrap(); + assert_eq!(got, expected); + } - #[test] - fn network_config_builder_should_be_dumplable_to_a_toml_config_a_overrides_default_correctly() { - let network_config = NetworkConfigBuilder::new() - .with_relaychain(|relaychain| { - relaychain - .with_chain("polkadot") - .with_default_command("polkadot") - .with_default_image("docker.io/parity/polkadot:latest") - .with_default_args(vec![("-name", "value").into(), "--flag".into()]) - .with_default_db_snapshot("https://storage.com/path/to/db_snapshot.tgz") - .with_default_resources(|resources| { - resources - .with_request_cpu(100000) - .with_request_memory("500M") - .with_limit_cpu("10Gi") - .with_limit_memory("4000M") - }) - .with_validator(|node| { - node.with_name("alice") - .with_initial_balance(1_000_000_000) - .bootnode(true) - .invulnerable(true) - }) - .with_validator(|node| { - node.with_name("bob") - .invulnerable(true) - .bootnode(true) - .with_image("mycustomimage:latest") - .with_command("my-custom-command") - .with_db_snapshot("https://storage.com/path/to/other/db_snapshot.tgz") - .with_resources(|resources| { - resources - .with_request_cpu(1000) - .with_request_memory("250Mi") - .with_limit_cpu("5Gi") - .with_limit_memory("2Gi") - }) - .with_args(vec![("-myothername", "value").into()]) - }) - }) - .with_parachain(|parachain| { - parachain - .with_id(1000) - .with_chain("myparachain") - .with_chain_spec_path("/path/to/my/chain/spec.json") - .with_default_db_snapshot("https://storage.com/path/to/other_snapshot.tgz") - .with_default_command("my-default-command") - .with_default_image("mydefaultimage:latest") - .with_collator(|collator| { - collator - .with_name("john") - .bootnode(true) - .invulnerable(true) - .with_initial_balance(5_000_000_000) - .with_command("my-non-default-command") - .with_image("anotherimage:latest") - }) - .with_fullnode(|collator| { - collator - .with_name("charles") - .bootnode(true) - .invulnerable(true) - .with_initial_balance(0) - }) - }) - .build() - .unwrap(); + #[test] + fn network_config_builder_should_be_dumplable_to_a_toml_config_a_overrides_default_correctly() { + let network_config = NetworkConfigBuilder::new() + .with_relaychain(|relaychain| { + relaychain + .with_chain("polkadot") + .with_default_command("polkadot") + .with_default_image("docker.io/parity/polkadot:latest") + .with_default_args(vec![("-name", "value").into(), "--flag".into()]) + .with_default_db_snapshot("https://storage.com/path/to/db_snapshot.tgz") + .with_default_resources(|resources| { + resources + .with_request_cpu(100000) + .with_request_memory("500M") + .with_limit_cpu("10Gi") + .with_limit_memory("4000M") + }) + .with_validator(|node| { + node.with_name("alice") + .with_initial_balance(1_000_000_000) + .bootnode(true) + .invulnerable(true) + }) + .with_validator(|node| { + node.with_name("bob") + .invulnerable(true) + .bootnode(true) + .with_image("mycustomimage:latest") + .with_command("my-custom-command") + .with_db_snapshot("https://storage.com/path/to/other/db_snapshot.tgz") + .with_resources(|resources| { + resources + .with_request_cpu(1000) + .with_request_memory("250Mi") + .with_limit_cpu("5Gi") + .with_limit_memory("2Gi") + }) + .with_args(vec![("-myothername", "value").into()]) + }) + }) + .with_parachain(|parachain| { + parachain + .with_id(1000) + .with_chain("myparachain") + .with_chain_spec_path("/path/to/my/chain/spec.json") + .with_default_db_snapshot("https://storage.com/path/to/other_snapshot.tgz") + .with_default_command("my-default-command") + .with_default_image("mydefaultimage:latest") + .with_collator(|collator| { + collator + .with_name("john") + .bootnode(true) + .invulnerable(true) + .with_initial_balance(5_000_000_000) + .with_command("my-non-default-command") + .with_image("anotherimage:latest") + }) + .with_fullnode(|collator| { + collator + .with_name("charles") + .bootnode(true) + .invulnerable(true) + .with_initial_balance(0) + }) + }) + .build() + .unwrap(); - let got = network_config.dump_to_toml().unwrap(); - let expected = - fs::read_to_string("./testing/snapshots/0002-overridden-defaults.toml").unwrap(); - assert_eq!(got, expected); - } + let got = network_config.dump_to_toml().unwrap(); + let expected = + fs::read_to_string("./testing/snapshots/0002-overridden-defaults.toml").unwrap(); + assert_eq!(got, expected); + } - #[test] - fn the_toml_config_with_custom_settings() { - let settings = GlobalSettingsBuilder::new() - .with_base_dir("/tmp/test-demo") - .build() - .unwrap(); + #[test] + fn the_toml_config_with_custom_settings() { + let settings = + GlobalSettingsBuilder::new().with_base_dir("/tmp/test-demo").build().unwrap(); - let load_from_toml = NetworkConfig::load_from_toml_with_settings( - "./testing/snapshots/0000-small-network.toml", - &settings, - ) - .unwrap(); + let load_from_toml = NetworkConfig::load_from_toml_with_settings( + "./testing/snapshots/0000-small-network.toml", + &settings, + ) + .unwrap(); - assert_eq!( - Some(PathBuf::from("/tmp/test-demo").as_path()), - load_from_toml.global_settings.base_dir() - ); - } + assert_eq!( + Some(PathBuf::from("/tmp/test-demo").as_path()), + load_from_toml.global_settings.base_dir() + ); + } - #[test] - fn the_toml_config_should_be_imported_and_match_a_network() { - let load_from_toml = - NetworkConfig::load_from_toml("./testing/snapshots/0000-small-network.toml").unwrap(); + #[test] + fn the_toml_config_should_be_imported_and_match_a_network() { + let load_from_toml = + NetworkConfig::load_from_toml("./testing/snapshots/0000-small-network.toml").unwrap(); - let expected = NetworkConfigBuilder::new() - .with_relaychain(|relaychain| { - relaychain - .with_chain("rococo-local") - .with_default_command("polkadot") - .with_default_image("docker.io/parity/polkadot:latest") - .with_default_args(vec![("-lparachain=debug").into()]) - .with_validator(|node| { - node.with_name("alice") - .validator(true) - .invulnerable(true) - .bootnode(false) - .with_initial_balance(2000000000000) - }) - .with_validator(|node| { - node.with_name("bob") - .with_args(vec![("--database", "paritydb-experimental").into()]) - .invulnerable(false) - .bootnode(true) - .with_initial_balance(2000000000000) - }) - }) - .build() - .unwrap(); + let expected = NetworkConfigBuilder::new() + .with_relaychain(|relaychain| { + relaychain + .with_chain("rococo-local") + .with_default_command("polkadot") + .with_default_image("docker.io/parity/polkadot:latest") + .with_default_args(vec![("-lparachain=debug").into()]) + .with_validator(|node| { + node.with_name("alice") + .validator(true) + .invulnerable(true) + .bootnode(false) + .with_initial_balance(2000000000000) + }) + .with_validator(|node| { + node.with_name("bob") + .with_args(vec![("--database", "paritydb-experimental").into()]) + .invulnerable(false) + .bootnode(true) + .with_initial_balance(2000000000000) + }) + }) + .build() + .unwrap(); - // We need to assert parts of the network config separately because the expected one contains the chain default context which - // is used for dumbing to tomp while the - // while loaded - assert_eq!( - expected.relaychain().chain(), - load_from_toml.relaychain().chain() - ); - assert_eq!( - expected.relaychain().default_args(), - load_from_toml.relaychain().default_args() - ); - assert_eq!( - expected.relaychain().default_command(), - load_from_toml.relaychain().default_command() - ); - assert_eq!( - expected.relaychain().default_image(), - load_from_toml.relaychain().default_image() - ); + // We need to assert parts of the network config separately because the expected one contains the chain default context which + // is used for dumbing to tomp while the + // while loaded + assert_eq!(expected.relaychain().chain(), load_from_toml.relaychain().chain()); + assert_eq!( + expected.relaychain().default_args(), + load_from_toml.relaychain().default_args() + ); + assert_eq!( + expected.relaychain().default_command(), + load_from_toml.relaychain().default_command() + ); + assert_eq!( + expected.relaychain().default_image(), + load_from_toml.relaychain().default_image() + ); - // Check the nodes without the Chain Default Context - expected - .relaychain() - .nodes() - .iter() - .zip(load_from_toml.relaychain().nodes().iter()) - .for_each(|(expected_node, loaded_node)| { - assert_eq!(expected_node.name(), loaded_node.name()); - assert_eq!(expected_node.command(), loaded_node.command()); - assert_eq!(expected_node.args(), loaded_node.args()); - assert_eq!( - expected_node.is_invulnerable(), - loaded_node.is_invulnerable() - ); - assert_eq!(expected_node.is_validator(), loaded_node.is_validator()); - assert_eq!(expected_node.is_bootnode(), loaded_node.is_bootnode()); - assert_eq!( - expected_node.initial_balance(), - loaded_node.initial_balance() - ); - }); - } + // Check the nodes without the Chain Default Context + expected + .relaychain() + .nodes() + .iter() + .zip(load_from_toml.relaychain().nodes().iter()) + .for_each(|(expected_node, loaded_node)| { + assert_eq!(expected_node.name(), loaded_node.name()); + assert_eq!(expected_node.command(), loaded_node.command()); + assert_eq!(expected_node.args(), loaded_node.args()); + assert_eq!(expected_node.is_invulnerable(), loaded_node.is_invulnerable()); + assert_eq!(expected_node.is_validator(), loaded_node.is_validator()); + assert_eq!(expected_node.is_bootnode(), loaded_node.is_bootnode()); + assert_eq!(expected_node.initial_balance(), loaded_node.initial_balance()); + }); + } - #[test] - fn the_toml_config_without_settings_should_be_imported_and_match_a_network() { - let load_from_toml = NetworkConfig::load_from_toml( - "./testing/snapshots/0004-small-network-without-settings.toml", - ) - .unwrap(); + #[test] + fn the_toml_config_without_settings_should_be_imported_and_match_a_network() { + let load_from_toml = NetworkConfig::load_from_toml( + "./testing/snapshots/0004-small-network-without-settings.toml", + ) + .unwrap(); - let expected = NetworkConfigBuilder::new() - .with_relaychain(|relaychain| { - relaychain - .with_chain("rococo-local") - .with_default_command("polkadot") - .with_validator(|node| node.with_name("alice")) - .with_validator(|node| node.with_name("bob")) - }) - .build() - .unwrap(); + let expected = NetworkConfigBuilder::new() + .with_relaychain(|relaychain| { + relaychain + .with_chain("rococo-local") + .with_default_command("polkadot") + .with_validator(|node| node.with_name("alice")) + .with_validator(|node| node.with_name("bob")) + }) + .build() + .unwrap(); - assert_eq!( - load_from_toml.global_settings().network_spawn_timeout(), - expected.global_settings().network_spawn_timeout() - ) - } + assert_eq!( + load_from_toml.global_settings().network_spawn_timeout(), + expected.global_settings().network_spawn_timeout() + ) + } - #[test] - fn the_toml_config_should_be_imported_and_match_a_network_with_teyrchains() { - let load_from_toml = - NetworkConfig::load_from_toml("./testing/snapshots/0001-big-network.toml").unwrap(); + #[test] + fn the_toml_config_should_be_imported_and_match_a_network_with_teyrchains() { + let load_from_toml = + NetworkConfig::load_from_toml("./testing/snapshots/0001-big-network.toml").unwrap(); - let expected = NetworkConfigBuilder::new() - .with_relaychain(|relaychain| { - relaychain - .with_chain("polkadot") - .with_default_command("polkadot") - .with_default_image("docker.io/parity/polkadot:latest") - .with_default_resources(|resources| { - resources - .with_request_cpu(100000) - .with_request_memory("500M") - .with_limit_cpu("10Gi") - .with_limit_memory("4000M") - }) - .with_validator(|node| { - node.with_name("alice") - .with_initial_balance(1_000_000_000) - .bootnode(true) - .invulnerable(true) - }) - .with_validator(|node| node.with_name("bob").invulnerable(true).bootnode(true)) - }) - .with_parachain(|parachain| { - parachain - .with_id(1000) - .with_chain("myparachain") - .with_chain_spec_path("/path/to/my/chain/spec.json") - .with_registration_strategy(RegistrationStrategy::UsingExtrinsic) - .onboard_as_parachain(false) - .with_default_db_snapshot("https://storage.com/path/to/db_snapshot.tgz") - .with_collator(|collator| { - collator - .with_name("john") - .bootnode(true) - .invulnerable(true) - .with_initial_balance(5_000_000_000) - }) - .with_fullnode(|collator| { - collator - .with_name("charles") - .bootnode(true) - .invulnerable(true) - .with_initial_balance(0) - }) - .with_collator(|collator| { - collator - .with_name("frank") - .invulnerable(false) - .bootnode(true) - .with_initial_balance(1_000_000_000) - }) - }) - .with_parachain(|parachain| { - parachain - .with_id(2000) - .with_chain("myotherparachain") - .with_chain_spec_path("/path/to/my/other/chain/spec.json") - .with_collator(|collator| { - collator - .with_name("mike") - .bootnode(true) - .invulnerable(true) - .with_initial_balance(5_000_000_000) - }) - .with_fullnode(|collator| { - collator - .with_name("georges") - .bootnode(true) - .invulnerable(true) - .with_initial_balance(0) - }) - .with_collator(|collator| { - collator - .with_name("victor") - .invulnerable(false) - .bootnode(true) - .with_initial_balance(1_000_000_000) - }) - }) - .with_hrmp_channel(|hrmp_channel| { - hrmp_channel - .with_sender(1000) - .with_recipient(2000) - .with_max_capacity(150) - .with_max_message_size(5000) - }) - .with_hrmp_channel(|hrmp_channel| { - hrmp_channel - .with_sender(2000) - .with_recipient(1000) - .with_max_capacity(200) - .with_max_message_size(8000) - }) - .build() - .unwrap(); + let expected = NetworkConfigBuilder::new() + .with_relaychain(|relaychain| { + relaychain + .with_chain("polkadot") + .with_default_command("polkadot") + .with_default_image("docker.io/parity/polkadot:latest") + .with_default_resources(|resources| { + resources + .with_request_cpu(100000) + .with_request_memory("500M") + .with_limit_cpu("10Gi") + .with_limit_memory("4000M") + }) + .with_validator(|node| { + node.with_name("alice") + .with_initial_balance(1_000_000_000) + .bootnode(true) + .invulnerable(true) + }) + .with_validator(|node| node.with_name("bob").invulnerable(true).bootnode(true)) + }) + .with_parachain(|parachain| { + parachain + .with_id(1000) + .with_chain("myparachain") + .with_chain_spec_path("/path/to/my/chain/spec.json") + .with_registration_strategy(RegistrationStrategy::UsingExtrinsic) + .onboard_as_parachain(false) + .with_default_db_snapshot("https://storage.com/path/to/db_snapshot.tgz") + .with_collator(|collator| { + collator + .with_name("john") + .bootnode(true) + .invulnerable(true) + .with_initial_balance(5_000_000_000) + }) + .with_fullnode(|collator| { + collator + .with_name("charles") + .bootnode(true) + .invulnerable(true) + .with_initial_balance(0) + }) + .with_collator(|collator| { + collator + .with_name("frank") + .invulnerable(false) + .bootnode(true) + .with_initial_balance(1_000_000_000) + }) + }) + .with_parachain(|parachain| { + parachain + .with_id(2000) + .with_chain("myotherparachain") + .with_chain_spec_path("/path/to/my/other/chain/spec.json") + .with_collator(|collator| { + collator + .with_name("mike") + .bootnode(true) + .invulnerable(true) + .with_initial_balance(5_000_000_000) + }) + .with_fullnode(|collator| { + collator + .with_name("georges") + .bootnode(true) + .invulnerable(true) + .with_initial_balance(0) + }) + .with_collator(|collator| { + collator + .with_name("victor") + .invulnerable(false) + .bootnode(true) + .with_initial_balance(1_000_000_000) + }) + }) + .with_hrmp_channel(|hrmp_channel| { + hrmp_channel + .with_sender(1000) + .with_recipient(2000) + .with_max_capacity(150) + .with_max_message_size(5000) + }) + .with_hrmp_channel(|hrmp_channel| { + hrmp_channel + .with_sender(2000) + .with_recipient(1000) + .with_max_capacity(200) + .with_max_message_size(8000) + }) + .build() + .unwrap(); - // Check the relay chain - assert_eq!( - expected.relaychain().default_resources(), - load_from_toml.relaychain().default_resources() - ); + // Check the relay chain + assert_eq!( + expected.relaychain().default_resources(), + load_from_toml.relaychain().default_resources() + ); - // Check the nodes without the Chain Default Context - expected - .relaychain() - .nodes() - .iter() - .zip(load_from_toml.relaychain().nodes().iter()) - .for_each(|(expected_node, loaded_node)| { - assert_eq!(expected_node.name(), loaded_node.name()); - assert_eq!(expected_node.command(), loaded_node.command()); - assert_eq!(expected_node.args(), loaded_node.args()); - assert_eq!(expected_node.is_validator(), loaded_node.is_validator()); - assert_eq!(expected_node.is_bootnode(), loaded_node.is_bootnode()); - assert_eq!( - expected_node.initial_balance(), - loaded_node.initial_balance() - ); - assert_eq!( - expected_node.is_invulnerable(), - loaded_node.is_invulnerable() - ); - }); + // Check the nodes without the Chain Default Context + expected + .relaychain() + .nodes() + .iter() + .zip(load_from_toml.relaychain().nodes().iter()) + .for_each(|(expected_node, loaded_node)| { + assert_eq!(expected_node.name(), loaded_node.name()); + assert_eq!(expected_node.command(), loaded_node.command()); + assert_eq!(expected_node.args(), loaded_node.args()); + assert_eq!(expected_node.is_validator(), loaded_node.is_validator()); + assert_eq!(expected_node.is_bootnode(), loaded_node.is_bootnode()); + assert_eq!(expected_node.initial_balance(), loaded_node.initial_balance()); + assert_eq!(expected_node.is_invulnerable(), loaded_node.is_invulnerable()); + }); - expected - .teyrchains() - .iter() - .zip(load_from_toml.teyrchains().iter()) - .for_each(|(expected_parachain, loaded_parachain)| { - assert_eq!(expected_parachain.id(), loaded_parachain.id()); - assert_eq!(expected_parachain.chain(), loaded_parachain.chain()); - assert_eq!( - expected_parachain.chain_spec_path(), - loaded_parachain.chain_spec_path() - ); - assert_eq!( - expected_parachain.registration_strategy(), - loaded_parachain.registration_strategy() - ); - assert_eq!( - expected_parachain.onboard_as_parachain(), - loaded_parachain.onboard_as_parachain() - ); - assert_eq!( - expected_parachain.default_db_snapshot(), - loaded_parachain.default_db_snapshot() - ); - assert_eq!( - expected_parachain.default_command(), - loaded_parachain.default_command() - ); - assert_eq!( - expected_parachain.default_image(), - loaded_parachain.default_image() - ); - assert_eq!( - expected_parachain.collators().len(), - loaded_parachain.collators().len() - ); - expected_parachain - .collators() - .iter() - .zip(loaded_parachain.collators().iter()) - .for_each(|(expected_collator, loaded_collator)| { - assert_eq!(expected_collator.name(), loaded_collator.name()); - assert_eq!(expected_collator.command(), loaded_collator.command()); - assert_eq!(expected_collator.image(), loaded_collator.image()); - assert_eq!( - expected_collator.is_validator(), - loaded_collator.is_validator() - ); - assert_eq!( - expected_collator.is_bootnode(), - loaded_collator.is_bootnode() - ); - assert_eq!( - expected_collator.is_invulnerable(), - loaded_collator.is_invulnerable() - ); - assert_eq!( - expected_collator.initial_balance(), - loaded_collator.initial_balance() - ); - }); - }); + expected.teyrchains().iter().zip(load_from_toml.teyrchains().iter()).for_each( + |(expected_parachain, loaded_parachain)| { + assert_eq!(expected_parachain.id(), loaded_parachain.id()); + assert_eq!(expected_parachain.chain(), loaded_parachain.chain()); + assert_eq!( + expected_parachain.chain_spec_path(), + loaded_parachain.chain_spec_path() + ); + assert_eq!( + expected_parachain.registration_strategy(), + loaded_parachain.registration_strategy() + ); + assert_eq!( + expected_parachain.onboard_as_parachain(), + loaded_parachain.onboard_as_parachain() + ); + assert_eq!( + expected_parachain.default_db_snapshot(), + loaded_parachain.default_db_snapshot() + ); + assert_eq!( + expected_parachain.default_command(), + loaded_parachain.default_command() + ); + assert_eq!(expected_parachain.default_image(), loaded_parachain.default_image()); + assert_eq!( + expected_parachain.collators().len(), + loaded_parachain.collators().len() + ); + expected_parachain + .collators() + .iter() + .zip(loaded_parachain.collators().iter()) + .for_each(|(expected_collator, loaded_collator)| { + assert_eq!(expected_collator.name(), loaded_collator.name()); + assert_eq!(expected_collator.command(), loaded_collator.command()); + assert_eq!(expected_collator.image(), loaded_collator.image()); + assert_eq!( + expected_collator.is_validator(), + loaded_collator.is_validator() + ); + assert_eq!(expected_collator.is_bootnode(), loaded_collator.is_bootnode()); + assert_eq!( + expected_collator.is_invulnerable(), + loaded_collator.is_invulnerable() + ); + assert_eq!( + expected_collator.initial_balance(), + loaded_collator.initial_balance() + ); + }); + }, + ); - expected - .hrmp_channels() - .iter() - .zip(load_from_toml.hrmp_channels().iter()) - .for_each(|(expected_hrmp_channel, loaded_hrmp_channel)| { - assert_eq!(expected_hrmp_channel.sender(), loaded_hrmp_channel.sender()); - assert_eq!( - expected_hrmp_channel.recipient(), - loaded_hrmp_channel.recipient() - ); - assert_eq!( - expected_hrmp_channel.max_capacity(), - loaded_hrmp_channel.max_capacity() - ); - assert_eq!( - expected_hrmp_channel.max_message_size(), - loaded_hrmp_channel.max_message_size() - ); - }); - } + expected.hrmp_channels().iter().zip(load_from_toml.hrmp_channels().iter()).for_each( + |(expected_hrmp_channel, loaded_hrmp_channel)| { + assert_eq!(expected_hrmp_channel.sender(), loaded_hrmp_channel.sender()); + assert_eq!(expected_hrmp_channel.recipient(), loaded_hrmp_channel.recipient()); + assert_eq!( + expected_hrmp_channel.max_capacity(), + loaded_hrmp_channel.max_capacity() + ); + assert_eq!( + expected_hrmp_channel.max_message_size(), + loaded_hrmp_channel.max_message_size() + ); + }, + ); + } - #[test] - fn the_toml_config_should_be_imported_and_match_a_network_with_overriden_defaults() { - let load_from_toml = - NetworkConfig::load_from_toml("./testing/snapshots/0002-overridden-defaults.toml") - .unwrap(); + #[test] + fn the_toml_config_should_be_imported_and_match_a_network_with_overriden_defaults() { + let load_from_toml = + NetworkConfig::load_from_toml("./testing/snapshots/0002-overridden-defaults.toml") + .unwrap(); - let expected = NetworkConfigBuilder::new() - .with_relaychain(|relaychain| { - relaychain - .with_chain("polkadot") - .with_default_command("polkadot") - .with_default_image("docker.io/parity/polkadot:latest") - .with_default_args(vec![("-name", "value").into(), "--flag".into()]) - .with_default_db_snapshot("https://storage.com/path/to/db_snapshot.tgz") - .with_default_resources(|resources| { - resources - .with_request_cpu(100000) - .with_request_memory("500M") - .with_limit_cpu("10Gi") - .with_limit_memory("4000M") - }) - .with_validator(|node| { - node.with_name("alice") - .with_initial_balance(1_000_000_000) - .bootnode(true) - .invulnerable(true) - }) - .with_validator(|node| { - node.with_name("bob") - .invulnerable(true) - .bootnode(true) - .with_image("mycustomimage:latest") - .with_command("my-custom-command") - .with_db_snapshot("https://storage.com/path/to/other/db_snapshot.tgz") - .with_resources(|resources| { - resources - .with_request_cpu(1000) - .with_request_memory("250Mi") - .with_limit_cpu("5Gi") - .with_limit_memory("2Gi") - }) - .with_args(vec![("-myothername", "value").into()]) - }) - }) - .with_parachain(|parachain| { - parachain - .with_id(1000) - .with_chain("myparachain") - .with_chain_spec_path("/path/to/my/chain/spec.json") - .with_default_db_snapshot("https://storage.com/path/to/other_snapshot.tgz") - .with_default_command("my-default-command") - .with_default_image("mydefaultimage:latest") - .with_collator(|collator| { - collator - .with_name("john") - .bootnode(true) - .validator(true) - .invulnerable(true) - .with_initial_balance(5_000_000_000) - .with_command("my-non-default-command") - .with_image("anotherimage:latest") - }) - .with_fullnode(|collator| { - collator - .with_name("charles") - .bootnode(true) - .invulnerable(true) - .with_initial_balance(0) - }) - }) - .build() - .unwrap(); + let expected = NetworkConfigBuilder::new() + .with_relaychain(|relaychain| { + relaychain + .with_chain("polkadot") + .with_default_command("polkadot") + .with_default_image("docker.io/parity/polkadot:latest") + .with_default_args(vec![("-name", "value").into(), "--flag".into()]) + .with_default_db_snapshot("https://storage.com/path/to/db_snapshot.tgz") + .with_default_resources(|resources| { + resources + .with_request_cpu(100000) + .with_request_memory("500M") + .with_limit_cpu("10Gi") + .with_limit_memory("4000M") + }) + .with_validator(|node| { + node.with_name("alice") + .with_initial_balance(1_000_000_000) + .bootnode(true) + .invulnerable(true) + }) + .with_validator(|node| { + node.with_name("bob") + .invulnerable(true) + .bootnode(true) + .with_image("mycustomimage:latest") + .with_command("my-custom-command") + .with_db_snapshot("https://storage.com/path/to/other/db_snapshot.tgz") + .with_resources(|resources| { + resources + .with_request_cpu(1000) + .with_request_memory("250Mi") + .with_limit_cpu("5Gi") + .with_limit_memory("2Gi") + }) + .with_args(vec![("-myothername", "value").into()]) + }) + }) + .with_parachain(|parachain| { + parachain + .with_id(1000) + .with_chain("myparachain") + .with_chain_spec_path("/path/to/my/chain/spec.json") + .with_default_db_snapshot("https://storage.com/path/to/other_snapshot.tgz") + .with_default_command("my-default-command") + .with_default_image("mydefaultimage:latest") + .with_collator(|collator| { + collator + .with_name("john") + .bootnode(true) + .validator(true) + .invulnerable(true) + .with_initial_balance(5_000_000_000) + .with_command("my-non-default-command") + .with_image("anotherimage:latest") + }) + .with_fullnode(|collator| { + collator + .with_name("charles") + .bootnode(true) + .invulnerable(true) + .with_initial_balance(0) + }) + }) + .build() + .unwrap(); - expected - .teyrchains() - .iter() - .zip(load_from_toml.teyrchains().iter()) - .for_each(|(expected_parachain, loaded_parachain)| { - assert_eq!(expected_parachain.id(), loaded_parachain.id()); - assert_eq!(expected_parachain.chain(), loaded_parachain.chain()); - assert_eq!( - expected_parachain.chain_spec_path(), - loaded_parachain.chain_spec_path() - ); - assert_eq!( - expected_parachain.registration_strategy(), - loaded_parachain.registration_strategy() - ); - assert_eq!( - expected_parachain.onboard_as_parachain(), - loaded_parachain.onboard_as_parachain() - ); - assert_eq!( - expected_parachain.default_db_snapshot(), - loaded_parachain.default_db_snapshot() - ); - assert_eq!( - expected_parachain.default_command(), - loaded_parachain.default_command() - ); - assert_eq!( - expected_parachain.default_image(), - loaded_parachain.default_image() - ); - assert_eq!( - expected_parachain.collators().len(), - loaded_parachain.collators().len() - ); - expected_parachain - .collators() - .iter() - .zip(loaded_parachain.collators().iter()) - .for_each(|(expected_collator, loaded_collator)| { - assert_eq!(expected_collator.name(), loaded_collator.name()); - assert_eq!(expected_collator.command(), loaded_collator.command()); - assert_eq!(expected_collator.image(), loaded_collator.image()); - assert_eq!( - expected_collator.is_validator(), - loaded_collator.is_validator() - ); - assert_eq!( - expected_collator.is_bootnode(), - loaded_collator.is_bootnode() - ); - assert_eq!( - expected_collator.is_invulnerable(), - loaded_collator.is_invulnerable() - ); - assert_eq!( - expected_collator.initial_balance(), - loaded_collator.initial_balance() - ); - }); - }); - } + expected.teyrchains().iter().zip(load_from_toml.teyrchains().iter()).for_each( + |(expected_parachain, loaded_parachain)| { + assert_eq!(expected_parachain.id(), loaded_parachain.id()); + assert_eq!(expected_parachain.chain(), loaded_parachain.chain()); + assert_eq!( + expected_parachain.chain_spec_path(), + loaded_parachain.chain_spec_path() + ); + assert_eq!( + expected_parachain.registration_strategy(), + loaded_parachain.registration_strategy() + ); + assert_eq!( + expected_parachain.onboard_as_parachain(), + loaded_parachain.onboard_as_parachain() + ); + assert_eq!( + expected_parachain.default_db_snapshot(), + loaded_parachain.default_db_snapshot() + ); + assert_eq!( + expected_parachain.default_command(), + loaded_parachain.default_command() + ); + assert_eq!(expected_parachain.default_image(), loaded_parachain.default_image()); + assert_eq!( + expected_parachain.collators().len(), + loaded_parachain.collators().len() + ); + expected_parachain + .collators() + .iter() + .zip(loaded_parachain.collators().iter()) + .for_each(|(expected_collator, loaded_collator)| { + assert_eq!(expected_collator.name(), loaded_collator.name()); + assert_eq!(expected_collator.command(), loaded_collator.command()); + assert_eq!(expected_collator.image(), loaded_collator.image()); + assert_eq!( + expected_collator.is_validator(), + loaded_collator.is_validator() + ); + assert_eq!(expected_collator.is_bootnode(), loaded_collator.is_bootnode()); + assert_eq!( + expected_collator.is_invulnerable(), + loaded_collator.is_invulnerable() + ); + assert_eq!( + expected_collator.initial_balance(), + loaded_collator.initial_balance() + ); + }); + }, + ); + } - #[test] - fn with_chain_and_nodes_works() { - let network_config = NetworkConfigBuilder::with_chain_and_nodes( - "rococo-local", - vec!["alice".to_string(), "bob".to_string()], - ) - .build() - .unwrap(); + #[test] + fn with_chain_and_nodes_works() { + let network_config = NetworkConfigBuilder::with_chain_and_nodes( + "rococo-local", + vec!["alice".to_string(), "bob".to_string()], + ) + .build() + .unwrap(); - // relaychain - assert_eq!(network_config.relaychain().chain().as_str(), "rococo-local"); - assert_eq!(network_config.relaychain().nodes().len(), 2); - let mut node_names = network_config.relaychain().nodes().into_iter(); - let node1 = node_names.next().unwrap().name(); - assert_eq!(node1, "alice"); - let node2 = node_names.next().unwrap().name(); - assert_eq!(node2, "bob"); + // relaychain + assert_eq!(network_config.relaychain().chain().as_str(), "rococo-local"); + assert_eq!(network_config.relaychain().nodes().len(), 2); + let mut node_names = network_config.relaychain().nodes().into_iter(); + let node1 = node_names.next().unwrap().name(); + assert_eq!(node1, "alice"); + let node2 = node_names.next().unwrap().name(); + assert_eq!(node2, "bob"); - // teyrchains - assert_eq!(network_config.teyrchains().len(), 0); - } + // teyrchains + assert_eq!(network_config.teyrchains().len(), 0); + } - #[test] - fn with_chain_and_nodes_should_fail_with_empty_relay_name() { - let errors = NetworkConfigBuilder::with_chain_and_nodes("", vec!["alice".to_string()]) - .build() - .unwrap_err(); + #[test] + fn with_chain_and_nodes_should_fail_with_empty_relay_name() { + let errors = NetworkConfigBuilder::with_chain_and_nodes("", vec!["alice".to_string()]) + .build() + .unwrap_err(); - assert_eq!( - errors.first().unwrap().to_string(), - "relaychain.chain: can't be empty" - ); - } + assert_eq!(errors.first().unwrap().to_string(), "relaychain.chain: can't be empty"); + } - #[test] - fn with_chain_and_nodes_should_fail_with_empty_node_list() { - let errors = NetworkConfigBuilder::with_chain_and_nodes("rococo-local", vec![]) - .build() - .unwrap_err(); + #[test] + fn with_chain_and_nodes_should_fail_with_empty_node_list() { + let errors = + NetworkConfigBuilder::with_chain_and_nodes("rococo-local", vec![]).build().unwrap_err(); - assert_eq!( - errors.first().unwrap().to_string(), - "relaychain.nodes[''].name: can't be empty" - ); - } + assert_eq!( + errors.first().unwrap().to_string(), + "relaychain.nodes[''].name: can't be empty" + ); + } - #[test] - fn with_chain_and_nodes_should_fail_with_empty_node_name() { - let errors = NetworkConfigBuilder::with_chain_and_nodes( - "rococo-local", - vec!["alice".to_string(), "".to_string()], - ) - .build() - .unwrap_err(); + #[test] + fn with_chain_and_nodes_should_fail_with_empty_node_name() { + let errors = NetworkConfigBuilder::with_chain_and_nodes( + "rococo-local", + vec!["alice".to_string(), "".to_string()], + ) + .build() + .unwrap_err(); - assert_eq!( - errors.first().unwrap().to_string(), - "relaychain.nodes[''].name: can't be empty" - ); - } + assert_eq!( + errors.first().unwrap().to_string(), + "relaychain.nodes[''].name: can't be empty" + ); + } - #[test] - fn with_parachain_id_and_collators_works() { - let network_config = NetworkConfigBuilder::with_chain_and_nodes( - "rococo-local", - vec!["alice".to_string(), "bob".to_string()], - ) - .with_parachain_id_and_collators( - 100, - vec!["collator1".to_string(), "collator2".to_string()], - ) - .build() - .unwrap(); + #[test] + fn with_parachain_id_and_collators_works() { + let network_config = NetworkConfigBuilder::with_chain_and_nodes( + "rococo-local", + vec!["alice".to_string(), "bob".to_string()], + ) + .with_parachain_id_and_collators( + 100, + vec!["collator1".to_string(), "collator2".to_string()], + ) + .build() + .unwrap(); - // relaychain - assert_eq!(network_config.relaychain().chain().as_str(), "rococo-local"); - assert_eq!(network_config.relaychain().nodes().len(), 2); - let mut node_names = network_config.relaychain().nodes().into_iter(); - let node1 = node_names.next().unwrap().name(); - assert_eq!(node1, "alice"); - let node2 = node_names.next().unwrap().name(); - assert_eq!(node2, "bob"); + // relaychain + assert_eq!(network_config.relaychain().chain().as_str(), "rococo-local"); + assert_eq!(network_config.relaychain().nodes().len(), 2); + let mut node_names = network_config.relaychain().nodes().into_iter(); + let node1 = node_names.next().unwrap().name(); + assert_eq!(node1, "alice"); + let node2 = node_names.next().unwrap().name(); + assert_eq!(node2, "bob"); - // teyrchains - assert_eq!(network_config.teyrchains().len(), 1); - let ¶chain1 = network_config.teyrchains().first().unwrap(); - assert_eq!(parachain1.id(), 100); - assert_eq!(parachain1.collators().len(), 2); - let mut collator_names = parachain1.collators().into_iter(); - let collator1 = collator_names.next().unwrap().name(); - assert_eq!(collator1, "collator1"); - let collator2 = collator_names.next().unwrap().name(); - assert_eq!(collator2, "collator2"); + // teyrchains + assert_eq!(network_config.teyrchains().len(), 1); + let ¶chain1 = network_config.teyrchains().first().unwrap(); + assert_eq!(parachain1.id(), 100); + assert_eq!(parachain1.collators().len(), 2); + let mut collator_names = parachain1.collators().into_iter(); + let collator1 = collator_names.next().unwrap().name(); + assert_eq!(collator1, "collator1"); + let collator2 = collator_names.next().unwrap().name(); + assert_eq!(collator2, "collator2"); - assert_eq!(parachain1.initial_balance(), 2_000_000_000_000); - } + assert_eq!(parachain1.initial_balance(), 2_000_000_000_000); + } - #[test] - fn with_parachain_id_and_collators_should_fail_with_empty_collator_list() { - let errors = - NetworkConfigBuilder::with_chain_and_nodes("polkadot", vec!["alice".to_string()]) - .with_parachain_id_and_collators(1, vec![]) - .build() - .unwrap_err(); + #[test] + fn with_parachain_id_and_collators_should_fail_with_empty_collator_list() { + let errors = + NetworkConfigBuilder::with_chain_and_nodes("polkadot", vec!["alice".to_string()]) + .with_parachain_id_and_collators(1, vec![]) + .build() + .unwrap_err(); - assert_eq!( - errors.first().unwrap().to_string(), - "teyrchain[1].can't be empty" - ); - } + assert_eq!(errors.first().unwrap().to_string(), "teyrchain[1].can't be empty"); + } - #[test] - fn with_parachain_id_and_collators_should_fail_with_empty_collator_name() { - let errors = - NetworkConfigBuilder::with_chain_and_nodes("polkadot", vec!["alice".to_string()]) - .with_parachain_id_and_collators(1, vec!["collator1".to_string(), "".to_string()]) - .build() - .unwrap_err(); + #[test] + fn with_parachain_id_and_collators_should_fail_with_empty_collator_name() { + let errors = + NetworkConfigBuilder::with_chain_and_nodes("polkadot", vec!["alice".to_string()]) + .with_parachain_id_and_collators(1, vec!["collator1".to_string(), "".to_string()]) + .build() + .unwrap_err(); - assert_eq!( - errors.first().unwrap().to_string(), - "teyrchain[1].collators[''].name: can't be empty" - ); - } + assert_eq!( + errors.first().unwrap().to_string(), + "teyrchain[1].collators[''].name: can't be empty" + ); + } - #[test] - fn wasm_override_in_toml_should_work() { - let load_from_toml = NetworkConfig::load_from_toml( - "./testing/snapshots/0005-small-networl-with-wasm-override.toml", - ) - .unwrap(); + #[test] + fn wasm_override_in_toml_should_work() { + let load_from_toml = NetworkConfig::load_from_toml( + "./testing/snapshots/0005-small-networl-with-wasm-override.toml", + ) + .unwrap(); - let expected = NetworkConfigBuilder::new() - .with_relaychain(|relaychain| { - relaychain - .with_chain("rococo-local") - .with_default_command("polkadot") - .with_wasm_override("/some/path/runtime.wasm") - .with_validator(|node| node.with_name("alice")) - .with_validator(|node| node.with_name("bob")) - }) - .with_parachain(|p| { - p.with_id(1000) - .with_wasm_override("https://some.com/runtime.wasm") - .with_collator(|c| c.with_name("john")) - }) - .build() - .unwrap(); + let expected = NetworkConfigBuilder::new() + .with_relaychain(|relaychain| { + relaychain + .with_chain("rococo-local") + .with_default_command("polkadot") + .with_wasm_override("/some/path/runtime.wasm") + .with_validator(|node| node.with_name("alice")) + .with_validator(|node| node.with_name("bob")) + }) + .with_parachain(|p| { + p.with_id(1000) + .with_wasm_override("https://some.com/runtime.wasm") + .with_collator(|c| c.with_name("john")) + }) + .build() + .unwrap(); - assert_eq!( - load_from_toml.relaychain().wasm_override(), - expected.relaychain().wasm_override() - ); - assert_eq!( - load_from_toml.teyrchains()[0].wasm_override(), - expected.teyrchains()[0].wasm_override() - ); - } + assert_eq!( + load_from_toml.relaychain().wasm_override(), + expected.relaychain().wasm_override() + ); + assert_eq!( + load_from_toml.teyrchains()[0].wasm_override(), + expected.teyrchains()[0].wasm_override() + ); + } - #[test] - fn multiple_paras_with_same_id_should_work() { - let network_config = NetworkConfigBuilder::new() - .with_relaychain(|relaychain| { - relaychain - .with_chain("polkadot") - .with_fullnode(|node| node.with_name("node").with_command("command")) - }) - .with_parachain(|parachain| { - parachain - .with_id(1) - .with_chain("myparachain1") - .with_collator(|collator| { - collator.with_name("collator1").with_command("command1") - }) - }) - .with_parachain(|parachain| { - parachain - .with_id(1) - .with_chain("myparachain1") - .with_registration_strategy(RegistrationStrategy::Manual) - .with_collator(|collator| { - collator.with_name("collator2").with_command("command1") - }) - }) - .build() - .unwrap(); + #[test] + fn multiple_paras_with_same_id_should_work() { + let network_config = NetworkConfigBuilder::new() + .with_relaychain(|relaychain| { + relaychain + .with_chain("polkadot") + .with_fullnode(|node| node.with_name("node").with_command("command")) + }) + .with_parachain(|parachain| { + parachain.with_id(1).with_chain("myparachain1").with_collator(|collator| { + collator.with_name("collator1").with_command("command1") + }) + }) + .with_parachain(|parachain| { + parachain + .with_id(1) + .with_chain("myparachain1") + .with_registration_strategy(RegistrationStrategy::Manual) + .with_collator(|collator| { + collator.with_name("collator2").with_command("command1") + }) + }) + .build() + .unwrap(); - let ¶chain2 = network_config.teyrchains().last().unwrap(); - assert_eq!(parachain2.unique_id(), "1-1"); - } + let ¶chain2 = network_config.teyrchains().last().unwrap(); + assert_eq!(parachain2.unique_id(), "1-1"); + } - #[test] - fn multiple_paras_with_same_id_both_for_register_should_fail() { - let errors = NetworkConfigBuilder::new() - .with_relaychain(|relaychain| { - relaychain - .with_chain("polkadot") - .with_fullnode(|node| node.with_name("node").with_command("command")) - }) - .with_parachain(|parachain| { - parachain - .with_id(1) - .with_chain("myparachain1") - .with_collator(|collator| { - collator.with_name("collator1").with_command("command1") - }) - }) - .with_parachain(|parachain| { - parachain + #[test] + fn multiple_paras_with_same_id_both_for_register_should_fail() { + let errors = NetworkConfigBuilder::new() + .with_relaychain(|relaychain| { + relaychain + .with_chain("polkadot") + .with_fullnode(|node| node.with_name("node").with_command("command")) + }) + .with_parachain(|parachain| { + parachain.with_id(1).with_chain("myparachain1").with_collator(|collator| { + collator.with_name("collator1").with_command("command1") + }) + }) + .with_parachain(|parachain| { + parachain .with_id(1) .with_chain("myparachain1") // .with_registration_strategy(RegistrationStrategy::UsingExtrinsic) @@ -1838,86 +1728,78 @@ mod tests { .with_name("collator2") .with_command("command1") }) - }) - .build() - .unwrap_err(); + }) + .build() + .unwrap_err(); - assert_eq!( - errors.first().unwrap().to_string(), - "ParaId 1 already set to be registered, only one should be." - ); - } + assert_eq!( + errors.first().unwrap().to_string(), + "ParaId 1 already set to be registered, only one should be." + ); + } - #[test] - fn network_config_should_work_from_toml_without_chain_name() { - let loaded_from_toml = - NetworkConfig::load_from_toml("./testing/snapshots/0006-without-rc-chain-name.toml") - .unwrap(); + #[test] + fn network_config_should_work_from_toml_without_chain_name() { + let loaded_from_toml = + NetworkConfig::load_from_toml("./testing/snapshots/0006-without-rc-chain-name.toml") + .unwrap(); - assert_eq!( - "rococo-local", - loaded_from_toml.relaychain().chain().as_str() - ); - } + assert_eq!("rococo-local", loaded_from_toml.relaychain().chain().as_str()); + } - #[test] - fn network_config_should_work_from_toml_with_duplicate_name_between_collator_and_relay_node() { - let loaded_from_toml = NetworkConfig::load_from_toml( - "./testing/snapshots/0007-small-network_w_teyrchain_w_duplicate_node_names.toml", - ) - .unwrap(); + #[test] + fn network_config_should_work_from_toml_with_duplicate_name_between_collator_and_relay_node() { + let loaded_from_toml = NetworkConfig::load_from_toml( + "./testing/snapshots/0007-small-network_w_teyrchain_w_duplicate_node_names.toml", + ) + .unwrap(); - assert_eq!( - loaded_from_toml - .relaychain() - .nodes() - .iter() - .filter(|n| n.name() == "alice") - .count(), - 1 - ); - assert_eq!( - loaded_from_toml - .teyrchains() - .iter() - .flat_map(|para| para.collators()) - .filter(|n| n.name() == "alice-1") - .count(), - 1 - ); - } + assert_eq!( + loaded_from_toml.relaychain().nodes().iter().filter(|n| n.name() == "alice").count(), + 1 + ); + assert_eq!( + loaded_from_toml + .teyrchains() + .iter() + .flat_map(|para| para.collators()) + .filter(|n| n.name() == "alice-1") + .count(), + 1 + ); + } - #[test] - fn raw_spec_override_in_toml_should_work() { - let load_from_toml = NetworkConfig::load_from_toml( - "./testing/snapshots/0008-small-network-with-raw-spec-override.toml", - ) - .unwrap(); + #[test] + fn raw_spec_override_in_toml_should_work() { + let load_from_toml = NetworkConfig::load_from_toml( + "./testing/snapshots/0008-small-network-with-raw-spec-override.toml", + ) + .unwrap(); - let expected = NetworkConfigBuilder::new() - .with_relaychain(|relaychain| { - relaychain - .with_chain("rococo-local") - .with_default_command("polkadot") - .with_raw_spec_override("/some/path/raw_spec_override.json") - .with_validator(|node| node.with_name("alice")) - .with_validator(|node| node.with_name("bob")) - }) - .with_parachain(|p| { - p.with_id(1000) - .with_raw_spec_override("https://some.com/raw_spec_override.json") - .with_collator(|c| c.with_name("john")) - }) - .build() - .unwrap(); + let expected = NetworkConfigBuilder::new() + .with_relaychain(|relaychain| { + relaychain + .with_chain("rococo-local") + .with_default_command("polkadot") + .with_raw_spec_override("/some/path/raw_spec_override.json") + .with_validator(|node| node.with_name("alice")) + .with_validator(|node| node.with_name("bob")) + }) + .with_parachain(|p| { + p.with_id(1000) + .with_raw_spec_override("https://some.com/raw_spec_override.json") + .with_collator(|c| c.with_name("john")) + }) + .build() + .unwrap(); - assert_eq!( - load_from_toml.relaychain().raw_spec_override(), - expected.relaychain().raw_spec_override() - ); - assert_eq!( - load_from_toml.teyrchains()[0].raw_spec_override(), - expected.teyrchains()[0].raw_spec_override() - ); - } + assert_eq!( + load_from_toml.relaychain().raw_spec_override(), + expected.relaychain().raw_spec_override() + ); + assert_eq!( + load_from_toml.teyrchains()[0].raw_spec_override(), + expected.teyrchains()[0].raw_spec_override() + ); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/relaychain.rs b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/relaychain.rs index 603ed1d5..0fe05394 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/relaychain.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/relaychain.rs @@ -4,1093 +4,989 @@ use serde::{Deserialize, Serialize}; use support::constants::{DEFAULT_TYPESTATE, THIS_IS_A_BUG}; use crate::{ - shared::{ - errors::{ConfigError, FieldError}, - helpers::{merge_errors, merge_errors_vecs}, - macros::states, - node::{self, GroupNodeConfig, GroupNodeConfigBuilder, NodeConfig, NodeConfigBuilder}, - resources::{Resources, ResourcesBuilder}, - types::{ - Arg, AssetLocation, Chain, ChainDefaultContext, Command, Image, ValidationContext, - }, - }, - types::{ChainSpecRuntime, JsonOverrides}, - utils::{default_command_polkadot, default_relaychain_chain, is_false}, + shared::{ + errors::{ConfigError, FieldError}, + helpers::{merge_errors, merge_errors_vecs}, + macros::states, + node::{self, GroupNodeConfig, GroupNodeConfigBuilder, NodeConfig, NodeConfigBuilder}, + resources::{Resources, ResourcesBuilder}, + types::{ + Arg, AssetLocation, Chain, ChainDefaultContext, Command, Image, ValidationContext, + }, + }, + types::{ChainSpecRuntime, JsonOverrides}, + utils::{default_command_polkadot, default_relaychain_chain, is_false}, }; /// A relay chain configuration, composed of nodes and fine-grained configuration options. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct RelaychainConfig { - #[serde(default = "default_relaychain_chain")] - chain: Chain, - #[serde(default = "default_command_polkadot")] - default_command: Option, - default_image: Option, - default_resources: Option, - default_db_snapshot: Option, - #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] - default_args: Vec, - /// chain-spec to use (location can be url or file path) - chain_spec_path: Option, - /// Full _template_ command, will be rendered (using custom token replacements) - /// and executed for generate the chain-spec. - /// available tokens {{chainName}} / {{disableBootnodes}} - chain_spec_command: Option, - /// runtime to use for generating the chain-spec. - /// Location can be url or file path and an optional preset - chain_spec_runtime: Option, - #[serde(skip_serializing_if = "is_false", default)] - chain_spec_command_is_local: bool, - chain_spec_command_output_path: Option, - random_nominators_count: Option, - max_nominations: Option, - #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] - nodes: Vec, - #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] - node_groups: Vec, - #[serde(rename = "genesis", skip_serializing_if = "Option::is_none")] - runtime_genesis_patch: Option, - // Path or url to override the runtime (:code) in the chain-spec - wasm_override: Option, - command: Option, - // Inline json or asset location to override raw chainspec - raw_spec_override: Option, + #[serde(default = "default_relaychain_chain")] + chain: Chain, + #[serde(default = "default_command_polkadot")] + default_command: Option, + default_image: Option, + default_resources: Option, + default_db_snapshot: Option, + #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] + default_args: Vec, + /// chain-spec to use (location can be url or file path) + chain_spec_path: Option, + /// Full _template_ command, will be rendered (using custom token replacements) + /// and executed for generate the chain-spec. + /// available tokens {{chainName}} / {{disableBootnodes}} + chain_spec_command: Option, + /// runtime to use for generating the chain-spec. + /// Location can be url or file path and an optional preset + chain_spec_runtime: Option, + #[serde(skip_serializing_if = "is_false", default)] + chain_spec_command_is_local: bool, + chain_spec_command_output_path: Option, + random_nominators_count: Option, + max_nominations: Option, + #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] + nodes: Vec, + #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] + node_groups: Vec, + #[serde(rename = "genesis", skip_serializing_if = "Option::is_none")] + runtime_genesis_patch: Option, + // Path or url to override the runtime (:code) in the chain-spec + wasm_override: Option, + command: Option, + // Inline json or asset location to override raw chainspec + raw_spec_override: Option, } impl RelaychainConfig { - /// The chain name. - pub fn chain(&self) -> &Chain { - &self.chain - } + /// The chain name. + pub fn chain(&self) -> &Chain { + &self.chain + } - /// The default command used for nodes. - pub fn default_command(&self) -> Option<&Command> { - self.default_command.as_ref() - } + /// The default command used for nodes. + pub fn default_command(&self) -> Option<&Command> { + self.default_command.as_ref() + } - /// The default container image used for nodes. - pub fn default_image(&self) -> Option<&Image> { - self.default_image.as_ref() - } + /// The default container image used for nodes. + pub fn default_image(&self) -> Option<&Image> { + self.default_image.as_ref() + } - /// The default resources limits used for nodes. - pub fn default_resources(&self) -> Option<&Resources> { - self.default_resources.as_ref() - } + /// The default resources limits used for nodes. + pub fn default_resources(&self) -> Option<&Resources> { + self.default_resources.as_ref() + } - /// The default database snapshot location that will be used for state. - pub fn default_db_snapshot(&self) -> Option<&AssetLocation> { - self.default_db_snapshot.as_ref() - } + /// The default database snapshot location that will be used for state. + pub fn default_db_snapshot(&self) -> Option<&AssetLocation> { + self.default_db_snapshot.as_ref() + } - /// The default arguments that will be used to launch the node command. - pub fn default_args(&self) -> Vec<&Arg> { - self.default_args.iter().collect::>() - } + /// The default arguments that will be used to launch the node command. + pub fn default_args(&self) -> Vec<&Arg> { + self.default_args.iter().collect::>() + } - /// The location of an pre-existing chain specification for the relay chain. - pub fn chain_spec_path(&self) -> Option<&AssetLocation> { - self.chain_spec_path.as_ref() - } + /// The location of an pre-existing chain specification for the relay chain. + pub fn chain_spec_path(&self) -> Option<&AssetLocation> { + self.chain_spec_path.as_ref() + } - /// The location of a wasm runtime to override in the chain-spec. - pub fn wasm_override(&self) -> Option<&AssetLocation> { - self.wasm_override.as_ref() - } + /// The location of a wasm runtime to override in the chain-spec. + pub fn wasm_override(&self) -> Option<&AssetLocation> { + self.wasm_override.as_ref() + } - /// The full _template_ command to genera the chain-spec - pub fn chain_spec_command(&self) -> Option<&str> { - self.chain_spec_command.as_deref() - } + /// The full _template_ command to genera the chain-spec + pub fn chain_spec_command(&self) -> Option<&str> { + self.chain_spec_command.as_deref() + } - /// Does the chain_spec_command needs to be run locally - pub fn chain_spec_command_is_local(&self) -> bool { - self.chain_spec_command_is_local - } + /// Does the chain_spec_command needs to be run locally + pub fn chain_spec_command_is_local(&self) -> bool { + self.chain_spec_command_is_local + } - /// The file where the `chain_spec_command` will write the chain-spec into. - /// Defaults to /dev/stdout. - pub fn chain_spec_command_output_path(&self) -> Option<&str> { - self.chain_spec_command_output_path.as_deref() - } + /// The file where the `chain_spec_command` will write the chain-spec into. + /// Defaults to /dev/stdout. + pub fn chain_spec_command_output_path(&self) -> Option<&str> { + self.chain_spec_command_output_path.as_deref() + } - /// The non-default command used for nodes. - pub fn command(&self) -> Option<&Command> { - self.command.as_ref() - } + /// The non-default command used for nodes. + pub fn command(&self) -> Option<&Command> { + self.command.as_ref() + } - /// The number of `random nominators` to create for chains using staking, this is used in tandem with `max_nominations` to simulate the amount of nominators and nominations. - pub fn random_nominators_count(&self) -> Option { - self.random_nominators_count - } + /// The number of `random nominators` to create for chains using staking, this is used in tandem with `max_nominations` to simulate the amount of nominators and nominations. + pub fn random_nominators_count(&self) -> Option { + self.random_nominators_count + } - /// The maximum number of nominations to create per nominator. - pub fn max_nominations(&self) -> Option { - self.max_nominations - } + /// The maximum number of nominations to create per nominator. + pub fn max_nominations(&self) -> Option { + self.max_nominations + } - /// The genesis overrides as a JSON value. - pub fn runtime_genesis_patch(&self) -> Option<&serde_json::Value> { - self.runtime_genesis_patch.as_ref() - } + /// The genesis overrides as a JSON value. + pub fn runtime_genesis_patch(&self) -> Option<&serde_json::Value> { + self.runtime_genesis_patch.as_ref() + } - /// The nodes of the relay chain. - pub fn nodes(&self) -> Vec<&NodeConfig> { - self.nodes.iter().collect::>() - } + /// The nodes of the relay chain. + pub fn nodes(&self) -> Vec<&NodeConfig> { + self.nodes.iter().collect::>() + } - /// The group nodes of the relay chain. - pub fn group_node_configs(&self) -> Vec<&GroupNodeConfig> { - self.node_groups.iter().collect::>() - } + /// The group nodes of the relay chain. + pub fn group_node_configs(&self) -> Vec<&GroupNodeConfig> { + self.node_groups.iter().collect::>() + } - /// The location of a file or inline json to override raw chain-spec. - pub fn raw_spec_override(&self) -> Option<&JsonOverrides> { - self.raw_spec_override.as_ref() - } + /// The location of a file or inline json to override raw chain-spec. + pub fn raw_spec_override(&self) -> Option<&JsonOverrides> { + self.raw_spec_override.as_ref() + } - /// Set the nodes to build - pub(crate) fn set_nodes(&mut self, nodes: Vec) { - self.nodes = nodes; - } + /// Set the nodes to build + pub(crate) fn set_nodes(&mut self, nodes: Vec) { + self.nodes = nodes; + } - /// The location of runtime to use by chain-spec builder lib (from `sc-chain-spec` crate) - pub fn chain_spec_runtime(&self) -> Option<&ChainSpecRuntime> { - self.chain_spec_runtime.as_ref() - } + /// The location of runtime to use by chain-spec builder lib (from `sc-chain-spec` crate) + pub fn chain_spec_runtime(&self) -> Option<&ChainSpecRuntime> { + self.chain_spec_runtime.as_ref() + } } states! { - Initial, - WithChain, - WithAtLeastOneNode + Initial, + WithChain, + WithAtLeastOneNode } /// A relay chain configuration builder, used to build a [`RelaychainConfig`] declaratively with fields validation. pub struct RelaychainConfigBuilder { - config: RelaychainConfig, - validation_context: Rc>, - errors: Vec, - _state: PhantomData, + config: RelaychainConfig, + validation_context: Rc>, + errors: Vec, + _state: PhantomData, } impl Default for RelaychainConfigBuilder { - fn default() -> Self { - Self { - config: RelaychainConfig { - chain: "default" - .try_into() - .expect(&format!("{DEFAULT_TYPESTATE} {THIS_IS_A_BUG}")), - default_command: None, - default_image: None, - default_resources: None, - default_db_snapshot: None, - default_args: vec![], - chain_spec_path: None, - chain_spec_command: None, - chain_spec_command_output_path: None, - chain_spec_runtime: None, - wasm_override: None, - chain_spec_command_is_local: false, // remote cmd by default - command: None, - random_nominators_count: None, - max_nominations: None, - runtime_genesis_patch: None, - nodes: vec![], - node_groups: vec![], - raw_spec_override: None, - }, - validation_context: Default::default(), - errors: vec![], - _state: PhantomData, - } - } + fn default() -> Self { + Self { + config: RelaychainConfig { + chain: "default".try_into().expect(&format!("{DEFAULT_TYPESTATE} {THIS_IS_A_BUG}")), + default_command: None, + default_image: None, + default_resources: None, + default_db_snapshot: None, + default_args: vec![], + chain_spec_path: None, + chain_spec_command: None, + chain_spec_command_output_path: None, + chain_spec_runtime: None, + wasm_override: None, + chain_spec_command_is_local: false, // remote cmd by default + command: None, + random_nominators_count: None, + max_nominations: None, + runtime_genesis_patch: None, + nodes: vec![], + node_groups: vec![], + raw_spec_override: None, + }, + validation_context: Default::default(), + errors: vec![], + _state: PhantomData, + } + } } impl RelaychainConfigBuilder { - fn transition( - config: RelaychainConfig, - validation_context: Rc>, - errors: Vec, - ) -> RelaychainConfigBuilder { - RelaychainConfigBuilder { - config, - validation_context, - errors, - _state: PhantomData, - } - } + fn transition( + config: RelaychainConfig, + validation_context: Rc>, + errors: Vec, + ) -> RelaychainConfigBuilder { + RelaychainConfigBuilder { config, validation_context, errors, _state: PhantomData } + } - fn default_chain_context(&self) -> ChainDefaultContext { - ChainDefaultContext { - default_command: self.config.default_command.clone(), - default_image: self.config.default_image.clone(), - default_resources: self.config.default_resources.clone(), - default_db_snapshot: self.config.default_db_snapshot.clone(), - default_args: self.config.default_args.clone(), - } - } + fn default_chain_context(&self) -> ChainDefaultContext { + ChainDefaultContext { + default_command: self.config.default_command.clone(), + default_image: self.config.default_image.clone(), + default_resources: self.config.default_resources.clone(), + default_db_snapshot: self.config.default_db_snapshot.clone(), + default_args: self.config.default_args.clone(), + } + } - fn create_node_builder(&self, f: F) -> NodeConfigBuilder - where - F: FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, - { - f(NodeConfigBuilder::new( - self.default_chain_context(), - self.validation_context.clone(), - )) - } + fn create_node_builder(&self, f: F) -> NodeConfigBuilder + where + F: FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, + { + f(NodeConfigBuilder::new(self.default_chain_context(), self.validation_context.clone())) + } } impl RelaychainConfigBuilder { - pub fn new( - validation_context: Rc>, - ) -> RelaychainConfigBuilder { - Self { - validation_context, - ..Self::default() - } - } + pub fn new( + validation_context: Rc>, + ) -> RelaychainConfigBuilder { + Self { validation_context, ..Self::default() } + } - /// Set the chain name (e.g. rococo-local). - pub fn with_chain(self, chain: T) -> RelaychainConfigBuilder - where - T: TryInto, - T::Error: Error + Send + Sync + 'static, - { - match chain.try_into() { - Ok(chain) => Self::transition( - RelaychainConfig { - chain, - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(error) => Self::transition( - self.config, - self.validation_context, - merge_errors(self.errors, FieldError::Chain(error.into()).into()), - ), - } - } + /// Set the chain name (e.g. rococo-local). + pub fn with_chain(self, chain: T) -> RelaychainConfigBuilder + where + T: TryInto, + T::Error: Error + Send + Sync + 'static, + { + match chain.try_into() { + Ok(chain) => Self::transition( + RelaychainConfig { chain, ..self.config }, + self.validation_context, + self.errors, + ), + Err(error) => Self::transition( + self.config, + self.validation_context, + merge_errors(self.errors, FieldError::Chain(error.into()).into()), + ), + } + } } impl RelaychainConfigBuilder { - /// Set the default command used for nodes. Can be overridden. - pub fn with_default_command(self, command: T) -> Self - where - T: TryInto, - T::Error: Error + Send + Sync + 'static, - { - match command.try_into() { - Ok(command) => Self::transition( - RelaychainConfig { - default_command: Some(command), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(error) => Self::transition( - self.config, - self.validation_context, - merge_errors(self.errors, FieldError::DefaultCommand(error.into()).into()), - ), - } - } + /// Set the default command used for nodes. Can be overridden. + pub fn with_default_command(self, command: T) -> Self + where + T: TryInto, + T::Error: Error + Send + Sync + 'static, + { + match command.try_into() { + Ok(command) => Self::transition( + RelaychainConfig { default_command: Some(command), ..self.config }, + self.validation_context, + self.errors, + ), + Err(error) => Self::transition( + self.config, + self.validation_context, + merge_errors(self.errors, FieldError::DefaultCommand(error.into()).into()), + ), + } + } - /// Set the default container image used for nodes. Can be overridden. - pub fn with_default_image(self, image: T) -> Self - where - T: TryInto, - T::Error: Error + Send + Sync + 'static, - { - match image.try_into() { - Ok(image) => Self::transition( - RelaychainConfig { - default_image: Some(image), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(error) => Self::transition( - self.config, - self.validation_context, - merge_errors(self.errors, FieldError::DefaultImage(error.into()).into()), - ), - } - } + /// Set the default container image used for nodes. Can be overridden. + pub fn with_default_image(self, image: T) -> Self + where + T: TryInto, + T::Error: Error + Send + Sync + 'static, + { + match image.try_into() { + Ok(image) => Self::transition( + RelaychainConfig { default_image: Some(image), ..self.config }, + self.validation_context, + self.errors, + ), + Err(error) => Self::transition( + self.config, + self.validation_context, + merge_errors(self.errors, FieldError::DefaultImage(error.into()).into()), + ), + } + } - /// Set the default resources limits used for nodes. Can be overridden. - pub fn with_default_resources( - self, - f: impl FnOnce(ResourcesBuilder) -> ResourcesBuilder, - ) -> Self { - match f(ResourcesBuilder::new()).build() { - Ok(default_resources) => Self::transition( - RelaychainConfig { - default_resources: Some(default_resources), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(errors) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| FieldError::DefaultResources(error).into()) - .collect::>(), - ), - ), - } - } + /// Set the default resources limits used for nodes. Can be overridden. + pub fn with_default_resources( + self, + f: impl FnOnce(ResourcesBuilder) -> ResourcesBuilder, + ) -> Self { + match f(ResourcesBuilder::new()).build() { + Ok(default_resources) => Self::transition( + RelaychainConfig { default_resources: Some(default_resources), ..self.config }, + self.validation_context, + self.errors, + ), + Err(errors) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| FieldError::DefaultResources(error).into()) + .collect::>(), + ), + ), + } + } - /// Set the default database snapshot location that will be used for state. Can be overridden. - pub fn with_default_db_snapshot(self, location: impl Into) -> Self { - Self::transition( - RelaychainConfig { - default_db_snapshot: Some(location.into()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the default database snapshot location that will be used for state. Can be overridden. + pub fn with_default_db_snapshot(self, location: impl Into) -> Self { + Self::transition( + RelaychainConfig { default_db_snapshot: Some(location.into()), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the default arguments that will be used to execute the node command. Can be overridden. - pub fn with_default_args(self, args: Vec) -> Self { - Self::transition( - RelaychainConfig { - default_args: args, - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the default arguments that will be used to execute the node command. Can be overridden. + pub fn with_default_args(self, args: Vec) -> Self { + Self::transition( + RelaychainConfig { default_args: args, ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the location of a pre-existing chain specification for the relay chain. - pub fn with_chain_spec_path(self, location: impl Into) -> Self { - Self::transition( - RelaychainConfig { - chain_spec_path: Some(location.into()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the location of a pre-existing chain specification for the relay chain. + pub fn with_chain_spec_path(self, location: impl Into) -> Self { + Self::transition( + RelaychainConfig { chain_spec_path: Some(location.into()), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the location of a wasm to override the chain-spec. - pub fn with_wasm_override(self, location: impl Into) -> Self { - Self::transition( - RelaychainConfig { - wasm_override: Some(location.into()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the location of a wasm to override the chain-spec. + pub fn with_wasm_override(self, location: impl Into) -> Self { + Self::transition( + RelaychainConfig { wasm_override: Some(location.into()), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the chain-spec command _template_ for the relay chain. - pub fn with_chain_spec_command(self, cmd_template: impl Into) -> Self { - Self::transition( - RelaychainConfig { - chain_spec_command: Some(cmd_template.into()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the chain-spec command _template_ for the relay chain. + pub fn with_chain_spec_command(self, cmd_template: impl Into) -> Self { + Self::transition( + RelaychainConfig { chain_spec_command: Some(cmd_template.into()), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the runtime path to use for generating the chain-spec and an optiona preset. - /// If the preset is not set, we will try to match [`local_testnet`, `development`, `dev`] - /// with the available ones and fallback to the default configuration as last option. - pub fn with_chain_spec_runtime( - self, - location: impl Into, - preset: Option<&str>, - ) -> Self { - let chain_spec_runtime = if let Some(preset) = preset { - ChainSpecRuntime::with_preset(location.into(), preset.to_string()) - } else { - ChainSpecRuntime::new(location.into()) - }; - Self::transition( - RelaychainConfig { - chain_spec_runtime: Some(chain_spec_runtime), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the runtime path to use for generating the chain-spec and an optiona preset. + /// If the preset is not set, we will try to match [`local_testnet`, `development`, `dev`] + /// with the available ones and fallback to the default configuration as last option. + pub fn with_chain_spec_runtime( + self, + location: impl Into, + preset: Option<&str>, + ) -> Self { + let chain_spec_runtime = if let Some(preset) = preset { + ChainSpecRuntime::with_preset(location.into(), preset.to_string()) + } else { + ChainSpecRuntime::new(location.into()) + }; + Self::transition( + RelaychainConfig { chain_spec_runtime: Some(chain_spec_runtime), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set if the chain-spec command needs to be run locally or not (false by default) - pub fn chain_spec_command_is_local(self, choice: bool) -> Self { - Self::transition( - RelaychainConfig { - chain_spec_command_is_local: choice, - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set if the chain-spec command needs to be run locally or not (false by default) + pub fn chain_spec_command_is_local(self, choice: bool) -> Self { + Self::transition( + RelaychainConfig { chain_spec_command_is_local: choice, ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the output path for the chain-spec command. - pub fn with_chain_spec_command_output_path(self, output_path: &str) -> Self { - Self::transition( - RelaychainConfig { - chain_spec_command_output_path: Some(output_path.to_string()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the output path for the chain-spec command. + pub fn with_chain_spec_command_output_path(self, output_path: &str) -> Self { + Self::transition( + RelaychainConfig { + chain_spec_command_output_path: Some(output_path.to_string()), + ..self.config + }, + self.validation_context, + self.errors, + ) + } - /// Set the number of `random nominators` to create for chains using staking, this is used in tandem with `max_nominations` to simulate the amount of nominators and nominations. - pub fn with_random_nominators_count(self, random_nominators_count: u32) -> Self { - Self::transition( - RelaychainConfig { - random_nominators_count: Some(random_nominators_count), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the number of `random nominators` to create for chains using staking, this is used in tandem with `max_nominations` to simulate the amount of nominators and nominations. + pub fn with_random_nominators_count(self, random_nominators_count: u32) -> Self { + Self::transition( + RelaychainConfig { + random_nominators_count: Some(random_nominators_count), + ..self.config + }, + self.validation_context, + self.errors, + ) + } - /// Set the maximum number of nominations to create per nominator. - pub fn with_max_nominations(self, max_nominations: u8) -> Self { - Self::transition( - RelaychainConfig { - max_nominations: Some(max_nominations), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the maximum number of nominations to create per nominator. + pub fn with_max_nominations(self, max_nominations: u8) -> Self { + Self::transition( + RelaychainConfig { max_nominations: Some(max_nominations), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the genesis overrides as a JSON object. - pub fn with_genesis_overrides(self, genesis_overrides: impl Into) -> Self { - Self::transition( - RelaychainConfig { - runtime_genesis_patch: Some(genesis_overrides.into()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the genesis overrides as a JSON object. + pub fn with_genesis_overrides(self, genesis_overrides: impl Into) -> Self { + Self::transition( + RelaychainConfig { + runtime_genesis_patch: Some(genesis_overrides.into()), + ..self.config + }, + self.validation_context, + self.errors, + ) + } - /// Add a new validator node using a nested [`NodeConfigBuilder`]. - /// The node will be configured as a validator (authority) with the --validator flag. - pub fn with_validator( - self, - f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, - ) -> RelaychainConfigBuilder { - match self.create_node_builder(f).validator(true).build() { - Ok(node) => Self::transition( - RelaychainConfig { - nodes: [self.config.nodes, vec![node]].concat(), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err((name, errors)) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| ConfigError::Node(name.clone(), error).into()) - .collect::>(), - ), - ), - } - } + /// Add a new validator node using a nested [`NodeConfigBuilder`]. + /// The node will be configured as a validator (authority) with the --validator flag. + pub fn with_validator( + self, + f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, + ) -> RelaychainConfigBuilder { + match self.create_node_builder(f).validator(true).build() { + Ok(node) => Self::transition( + RelaychainConfig { nodes: [self.config.nodes, vec![node]].concat(), ..self.config }, + self.validation_context, + self.errors, + ), + Err((name, errors)) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| ConfigError::Node(name.clone(), error).into()) + .collect::>(), + ), + ), + } + } - /// Add a new full node using a nested [`NodeConfigBuilder`]. - /// The node will be configured as a full node (non-validator). - pub fn with_fullnode( - self, - f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, - ) -> RelaychainConfigBuilder { - match self.create_node_builder(f).validator(false).build() { - Ok(node) => Self::transition( - RelaychainConfig { - nodes: [self.config.nodes, vec![node]].concat(), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err((name, errors)) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| ConfigError::Node(name.clone(), error).into()) - .collect::>(), - ), - ), - } - } + /// Add a new full node using a nested [`NodeConfigBuilder`]. + /// The node will be configured as a full node (non-validator). + pub fn with_fullnode( + self, + f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, + ) -> RelaychainConfigBuilder { + match self.create_node_builder(f).validator(false).build() { + Ok(node) => Self::transition( + RelaychainConfig { nodes: [self.config.nodes, vec![node]].concat(), ..self.config }, + self.validation_context, + self.errors, + ), + Err((name, errors)) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| ConfigError::Node(name.clone(), error).into()) + .collect::>(), + ), + ), + } + } - /// Add a new node using a nested [`NodeConfigBuilder`]. - /// - /// **Deprecated**: Use [`with_validator`] for validator nodes or [`with_fullnode`] for full nodes instead. - #[deprecated( - since = "0.4.0", - note = "Use `with_validator()` for validator nodes or `with_fullnode()` for full nodes instead" - )] - pub fn with_node( - self, - f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, - ) -> RelaychainConfigBuilder { - match self.create_node_builder(f).build() { - Ok(node) => Self::transition( - RelaychainConfig { - nodes: vec![node], - ..self.config - }, - self.validation_context, - self.errors, - ), - Err((name, errors)) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| ConfigError::Node(name.clone(), error).into()) - .collect::>(), - ), - ), - } - } + /// Add a new node using a nested [`NodeConfigBuilder`]. + /// + /// **Deprecated**: Use [`with_validator`] for validator nodes or [`with_fullnode`] for full nodes instead. + #[deprecated( + since = "0.4.0", + note = "Use `with_validator()` for validator nodes or `with_fullnode()` for full nodes instead" + )] + pub fn with_node( + self, + f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, + ) -> RelaychainConfigBuilder { + match self.create_node_builder(f).build() { + Ok(node) => Self::transition( + RelaychainConfig { nodes: vec![node], ..self.config }, + self.validation_context, + self.errors, + ), + Err((name, errors)) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| ConfigError::Node(name.clone(), error).into()) + .collect::>(), + ), + ), + } + } - /// Add a new group node using a nested [`GroupNodeConfigBuilder`]. - pub fn with_node_group( - self, - f: impl FnOnce(GroupNodeConfigBuilder) -> GroupNodeConfigBuilder, - ) -> RelaychainConfigBuilder { - match f(GroupNodeConfigBuilder::new( - self.default_chain_context(), - self.validation_context.clone(), - )) - .build() - { - Ok(group_node) => Self::transition( - RelaychainConfig { - node_groups: vec![group_node], - ..self.config - }, - self.validation_context, - self.errors, - ), - Err((name, errors)) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| ConfigError::Node(name.clone(), error).into()) - .collect::>(), - ), - ), - } - } + /// Add a new group node using a nested [`GroupNodeConfigBuilder`]. + pub fn with_node_group( + self, + f: impl FnOnce(GroupNodeConfigBuilder) -> GroupNodeConfigBuilder, + ) -> RelaychainConfigBuilder { + match f(GroupNodeConfigBuilder::new( + self.default_chain_context(), + self.validation_context.clone(), + )) + .build() + { + Ok(group_node) => Self::transition( + RelaychainConfig { node_groups: vec![group_node], ..self.config }, + self.validation_context, + self.errors, + ), + Err((name, errors)) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| ConfigError::Node(name.clone(), error).into()) + .collect::>(), + ), + ), + } + } - /// Set the location or inline value of a json to override the raw chain-spec. - pub fn with_raw_spec_override(self, overrides: impl Into) -> Self { - Self::transition( - RelaychainConfig { - raw_spec_override: Some(overrides.into()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the location or inline value of a json to override the raw chain-spec. + pub fn with_raw_spec_override(self, overrides: impl Into) -> Self { + Self::transition( + RelaychainConfig { raw_spec_override: Some(overrides.into()), ..self.config }, + self.validation_context, + self.errors, + ) + } } impl RelaychainConfigBuilder { - /// Add a new validator node using a nested [`NodeConfigBuilder`]. - /// The node will be configured as a validator (authority) with the --validator flag. - pub fn with_validator( - self, - f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, - ) -> RelaychainConfigBuilder { - match self.create_node_builder(f).validator(true).build() { - Ok(node) => Self::transition( - RelaychainConfig { - nodes: [self.config.nodes, vec![node]].concat(), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err((name, errors)) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| ConfigError::Node(name.clone(), error).into()) - .collect::>(), - ), - ), - } - } + /// Add a new validator node using a nested [`NodeConfigBuilder`]. + /// The node will be configured as a validator (authority) with the --validator flag. + pub fn with_validator( + self, + f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, + ) -> RelaychainConfigBuilder { + match self.create_node_builder(f).validator(true).build() { + Ok(node) => Self::transition( + RelaychainConfig { nodes: [self.config.nodes, vec![node]].concat(), ..self.config }, + self.validation_context, + self.errors, + ), + Err((name, errors)) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| ConfigError::Node(name.clone(), error).into()) + .collect::>(), + ), + ), + } + } - /// Add a new full node using a nested [`NodeConfigBuilder`]. - /// The node will be configured as a full node (non-validator). - pub fn with_fullnode( - self, - f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, - ) -> Self { - match self.create_node_builder(f).validator(false).build() { - Ok(node) => Self::transition( - RelaychainConfig { - nodes: [self.config.nodes, vec![node]].concat(), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err((name, errors)) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| ConfigError::Node(name.clone(), error).into()) - .collect::>(), - ), - ), - } - } + /// Add a new full node using a nested [`NodeConfigBuilder`]. + /// The node will be configured as a full node (non-validator). + pub fn with_fullnode( + self, + f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, + ) -> Self { + match self.create_node_builder(f).validator(false).build() { + Ok(node) => Self::transition( + RelaychainConfig { nodes: [self.config.nodes, vec![node]].concat(), ..self.config }, + self.validation_context, + self.errors, + ), + Err((name, errors)) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| ConfigError::Node(name.clone(), error).into()) + .collect::>(), + ), + ), + } + } - /// Add a new node using a nested [`NodeConfigBuilder`]. - /// - /// **Deprecated**: Use [`with_validator`] for validator nodes or [`with_fullnode`] for full nodes instead. - #[deprecated( - since = "0.4.0", - note = "Use `with_validator()` for validator nodes or `with_fullnode()` for full nodes instead" - )] - pub fn with_node( - self, - f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, - ) -> Self { - match self.create_node_builder(f).build() { - Ok(node) => Self::transition( - RelaychainConfig { - nodes: [self.config.nodes, vec![node]].concat(), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err((name, errors)) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| ConfigError::Node(name.clone(), error).into()) - .collect::>(), - ), - ), - } - } + /// Add a new node using a nested [`NodeConfigBuilder`]. + /// + /// **Deprecated**: Use [`with_validator`] for validator nodes or [`with_fullnode`] for full nodes instead. + #[deprecated( + since = "0.4.0", + note = "Use `with_validator()` for validator nodes or `with_fullnode()` for full nodes instead" + )] + pub fn with_node( + self, + f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, + ) -> Self { + match self.create_node_builder(f).build() { + Ok(node) => Self::transition( + RelaychainConfig { nodes: [self.config.nodes, vec![node]].concat(), ..self.config }, + self.validation_context, + self.errors, + ), + Err((name, errors)) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| ConfigError::Node(name.clone(), error).into()) + .collect::>(), + ), + ), + } + } - /// Add a new group node using a nested [`GroupNodeConfigBuilder`]. - pub fn with_node_group( - self, - f: impl FnOnce(GroupNodeConfigBuilder) -> GroupNodeConfigBuilder, - ) -> Self { - match f(GroupNodeConfigBuilder::new( - self.default_chain_context(), - self.validation_context.clone(), - )) - .build() - { - Ok(group_node) => Self::transition( - RelaychainConfig { - node_groups: [self.config.node_groups, vec![group_node]].concat(), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err((name, errors)) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| ConfigError::Node(name.clone(), error).into()) - .collect::>(), - ), - ), - } - } + /// Add a new group node using a nested [`GroupNodeConfigBuilder`]. + pub fn with_node_group( + self, + f: impl FnOnce(GroupNodeConfigBuilder) -> GroupNodeConfigBuilder, + ) -> Self { + match f(GroupNodeConfigBuilder::new( + self.default_chain_context(), + self.validation_context.clone(), + )) + .build() + { + Ok(group_node) => Self::transition( + RelaychainConfig { + node_groups: [self.config.node_groups, vec![group_node]].concat(), + ..self.config + }, + self.validation_context, + self.errors, + ), + Err((name, errors)) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| ConfigError::Node(name.clone(), error).into()) + .collect::>(), + ), + ), + } + } - /// Seals the builder and returns a [`RelaychainConfig`] if there are no validation errors, else returns errors. - pub fn build(self) -> Result> { - if !self.errors.is_empty() { - return Err(self - .errors - .into_iter() - .map(|error| ConfigError::Relaychain(error).into()) - .collect::>()); - } + /// Seals the builder and returns a [`RelaychainConfig`] if there are no validation errors, else returns errors. + pub fn build(self) -> Result> { + if !self.errors.is_empty() { + return Err(self + .errors + .into_iter() + .map(|error| ConfigError::Relaychain(error).into()) + .collect::>()); + } - Ok(self.config) - } + Ok(self.config) + } } #[cfg(test)] mod tests { - use super::*; + use super::*; - #[test] - fn relaychain_config_builder_should_succeeds_and_returns_a_relaychain_config() { - let relaychain_config = RelaychainConfigBuilder::new(Default::default()) - .with_chain("polkadot") - .with_default_image("myrepo:myimage") - .with_default_command("default_command") - .with_default_resources(|resources| { - resources - .with_limit_cpu("500M") - .with_limit_memory("1G") - .with_request_cpu("250M") - }) - .with_default_db_snapshot("https://www.urltomysnapshot.com/file.tgz") - .with_chain_spec_path("./path/to/chain/spec.json") - .with_chain_spec_runtime("./path/to/runtime.wasm", Some("local_testnet")) - .with_wasm_override("./path/to/override/runtime.wasm") - .with_raw_spec_override(serde_json::json!({"some_override_key": "some_override_val"})) - .with_default_args(vec![("--arg1", "value1").into(), "--option2".into()]) - .with_random_nominators_count(42) - .with_max_nominations(5) - .with_fullnode(|node| node.with_name("node1").bootnode(true)) - .with_validator(|node| node.with_name("node2").with_command("command2")) - .build() - .unwrap(); + #[test] + fn relaychain_config_builder_should_succeeds_and_returns_a_relaychain_config() { + let relaychain_config = RelaychainConfigBuilder::new(Default::default()) + .with_chain("polkadot") + .with_default_image("myrepo:myimage") + .with_default_command("default_command") + .with_default_resources(|resources| { + resources.with_limit_cpu("500M").with_limit_memory("1G").with_request_cpu("250M") + }) + .with_default_db_snapshot("https://www.urltomysnapshot.com/file.tgz") + .with_chain_spec_path("./path/to/chain/spec.json") + .with_chain_spec_runtime("./path/to/runtime.wasm", Some("local_testnet")) + .with_wasm_override("./path/to/override/runtime.wasm") + .with_raw_spec_override(serde_json::json!({"some_override_key": "some_override_val"})) + .with_default_args(vec![("--arg1", "value1").into(), "--option2".into()]) + .with_random_nominators_count(42) + .with_max_nominations(5) + .with_fullnode(|node| node.with_name("node1").bootnode(true)) + .with_validator(|node| node.with_name("node2").with_command("command2")) + .build() + .unwrap(); - assert_eq!(relaychain_config.chain().as_str(), "polkadot"); - assert_eq!(relaychain_config.nodes().len(), 2); - let &node1 = relaychain_config.nodes().first().unwrap(); - assert_eq!(node1.name(), "node1"); - assert_eq!(node1.command().unwrap().as_str(), "default_command"); - assert!(node1.is_bootnode()); - let &node2 = relaychain_config.nodes().last().unwrap(); - assert_eq!(node2.name(), "node2"); - assert_eq!(node2.command().unwrap().as_str(), "command2"); - assert!(node2.is_validator()); - assert_eq!( - relaychain_config.default_command().unwrap().as_str(), - "default_command" - ); - assert_eq!( - relaychain_config.default_image().unwrap().as_str(), - "myrepo:myimage" - ); - let default_resources = relaychain_config.default_resources().unwrap(); - assert_eq!(default_resources.limit_cpu().unwrap().as_str(), "500M"); - assert_eq!(default_resources.limit_memory().unwrap().as_str(), "1G"); - assert_eq!(default_resources.request_cpu().unwrap().as_str(), "250M"); - assert!(matches!( - relaychain_config.default_db_snapshot().unwrap(), - AssetLocation::Url(value) if value.as_str() == "https://www.urltomysnapshot.com/file.tgz", - )); - assert!(matches!( - relaychain_config.chain_spec_path().unwrap(), - AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/chain/spec.json" - )); - assert!(matches!( - &relaychain_config.chain_spec_runtime().unwrap().location, - AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/runtime.wasm" - )); - assert_eq!( - relaychain_config - .chain_spec_runtime() - .unwrap() - .preset - .as_deref(), - Some("local_testnet") - ); - assert!(matches!( - relaychain_config.wasm_override().unwrap(), - AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/override/runtime.wasm" - )); - let args: Vec = vec![("--arg1", "value1").into(), "--option2".into()]; - assert_eq!( - relaychain_config.default_args(), - args.iter().collect::>() - ); - assert_eq!(relaychain_config.random_nominators_count().unwrap(), 42); - assert_eq!(relaychain_config.max_nominations().unwrap(), 5); + assert_eq!(relaychain_config.chain().as_str(), "polkadot"); + assert_eq!(relaychain_config.nodes().len(), 2); + let &node1 = relaychain_config.nodes().first().unwrap(); + assert_eq!(node1.name(), "node1"); + assert_eq!(node1.command().unwrap().as_str(), "default_command"); + assert!(node1.is_bootnode()); + let &node2 = relaychain_config.nodes().last().unwrap(); + assert_eq!(node2.name(), "node2"); + assert_eq!(node2.command().unwrap().as_str(), "command2"); + assert!(node2.is_validator()); + assert_eq!(relaychain_config.default_command().unwrap().as_str(), "default_command"); + assert_eq!(relaychain_config.default_image().unwrap().as_str(), "myrepo:myimage"); + let default_resources = relaychain_config.default_resources().unwrap(); + assert_eq!(default_resources.limit_cpu().unwrap().as_str(), "500M"); + assert_eq!(default_resources.limit_memory().unwrap().as_str(), "1G"); + assert_eq!(default_resources.request_cpu().unwrap().as_str(), "250M"); + assert!(matches!( + relaychain_config.default_db_snapshot().unwrap(), + AssetLocation::Url(value) if value.as_str() == "https://www.urltomysnapshot.com/file.tgz", + )); + assert!(matches!( + relaychain_config.chain_spec_path().unwrap(), + AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/chain/spec.json" + )); + assert!(matches!( + &relaychain_config.chain_spec_runtime().unwrap().location, + AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/runtime.wasm" + )); + assert_eq!( + relaychain_config.chain_spec_runtime().unwrap().preset.as_deref(), + Some("local_testnet") + ); + assert!(matches!( + relaychain_config.wasm_override().unwrap(), + AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/override/runtime.wasm" + )); + let args: Vec = vec![("--arg1", "value1").into(), "--option2".into()]; + assert_eq!(relaychain_config.default_args(), args.iter().collect::>()); + assert_eq!(relaychain_config.random_nominators_count().unwrap(), 42); + assert_eq!(relaychain_config.max_nominations().unwrap(), 5); - assert!(matches!( - relaychain_config.raw_spec_override().unwrap(), - JsonOverrides::Json(value) if *value == serde_json::json!({"some_override_key": "some_override_val"}) - )); - } + assert!(matches!( + relaychain_config.raw_spec_override().unwrap(), + JsonOverrides::Json(value) if *value == serde_json::json!({"some_override_key": "some_override_val"}) + )); + } - #[test] - fn relaychain_config_builder_should_fails_and_returns_an_error_if_chain_is_invalid() { - let errors = RelaychainConfigBuilder::new(Default::default()) - .with_chain("invalid chain") - .with_validator(|node| node.with_name("node").with_command("command")) - .build() - .unwrap_err(); + #[test] + fn relaychain_config_builder_should_fails_and_returns_an_error_if_chain_is_invalid() { + let errors = RelaychainConfigBuilder::new(Default::default()) + .with_chain("invalid chain") + .with_validator(|node| node.with_name("node").with_command("command")) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - "relaychain.chain: 'invalid chain' shouldn't contains whitespace" - ); - } + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + "relaychain.chain: 'invalid chain' shouldn't contains whitespace" + ); + } - #[test] - fn relaychain_config_builder_should_fails_and_returns_an_error_if_default_command_is_invalid() { - let errors = RelaychainConfigBuilder::new(Default::default()) - .with_chain("chain") - .with_default_command("invalid command") - .with_validator(|node| node.with_name("node").with_command("command")) - .build() - .unwrap_err(); + #[test] + fn relaychain_config_builder_should_fails_and_returns_an_error_if_default_command_is_invalid() { + let errors = RelaychainConfigBuilder::new(Default::default()) + .with_chain("chain") + .with_default_command("invalid command") + .with_validator(|node| node.with_name("node").with_command("command")) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - "relaychain.default_command: 'invalid command' shouldn't contains whitespace" - ); - } + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + "relaychain.default_command: 'invalid command' shouldn't contains whitespace" + ); + } - #[test] - fn relaychain_config_builder_should_fails_and_returns_an_error_if_default_image_is_invalid() { - let errors = RelaychainConfigBuilder::new(Default::default()) - .with_chain("chain") - .with_default_image("invalid image") - .with_validator(|node| node.with_name("node").with_command("command")) - .build() - .unwrap_err(); + #[test] + fn relaychain_config_builder_should_fails_and_returns_an_error_if_default_image_is_invalid() { + let errors = RelaychainConfigBuilder::new(Default::default()) + .with_chain("chain") + .with_default_image("invalid image") + .with_validator(|node| node.with_name("node").with_command("command")) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - r"relaychain.default_image: 'invalid image' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'" - ); - } + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + r"relaychain.default_image: 'invalid image' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'" + ); + } - #[test] - fn relaychain_config_builder_should_fails_and_returns_an_error_if_default_resources_are_invalid( - ) { - let errors = RelaychainConfigBuilder::new(Default::default()) - .with_chain("chain") - .with_default_resources(|default_resources| { - default_resources - .with_limit_memory("100m") - .with_request_cpu("invalid") - }) - .with_validator(|node| node.with_name("node").with_command("command")) - .build() - .unwrap_err(); + #[test] + fn relaychain_config_builder_should_fails_and_returns_an_error_if_default_resources_are_invalid( + ) { + let errors = RelaychainConfigBuilder::new(Default::default()) + .with_chain("chain") + .with_default_resources(|default_resources| { + default_resources.with_limit_memory("100m").with_request_cpu("invalid") + }) + .with_validator(|node| node.with_name("node").with_command("command")) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - r"relaychain.default_resources.request_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" - ); - } + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + r"relaychain.default_resources.request_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" + ); + } - #[test] - fn relaychain_config_builder_should_fails_and_returns_an_error_if_first_node_is_invalid() { - let errors = RelaychainConfigBuilder::new(Default::default()) - .with_chain("chain") - .with_validator(|node| node.with_name("node").with_command("invalid command")) - .build() - .unwrap_err(); + #[test] + fn relaychain_config_builder_should_fails_and_returns_an_error_if_first_node_is_invalid() { + let errors = RelaychainConfigBuilder::new(Default::default()) + .with_chain("chain") + .with_validator(|node| node.with_name("node").with_command("invalid command")) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - "relaychain.nodes['node'].command: 'invalid command' shouldn't contains whitespace" - ); - } + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + "relaychain.nodes['node'].command: 'invalid command' shouldn't contains whitespace" + ); + } - #[test] - fn relaychain_config_builder_with_at_least_one_node_should_fails_and_returns_an_error_if_second_node_is_invalid( - ) { - let errors = RelaychainConfigBuilder::new(Default::default()) - .with_chain("chain") - .with_validator(|node| node.with_name("node1").with_command("command1")) - .with_validator(|node| node.with_name("node2").with_command("invalid command")) - .build() - .unwrap_err(); + #[test] + fn relaychain_config_builder_with_at_least_one_node_should_fails_and_returns_an_error_if_second_node_is_invalid( + ) { + let errors = RelaychainConfigBuilder::new(Default::default()) + .with_chain("chain") + .with_validator(|node| node.with_name("node1").with_command("command1")) + .with_validator(|node| node.with_name("node2").with_command("invalid command")) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - "relaychain.nodes['node2'].command: 'invalid command' shouldn't contains whitespace" - ); - } + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + "relaychain.nodes['node2'].command: 'invalid command' shouldn't contains whitespace" + ); + } - #[test] - fn relaychain_config_builder_should_fails_returns_multiple_errors_if_a_node_and_default_resources_are_invalid( - ) { - let errors = RelaychainConfigBuilder::new(Default::default()) - .with_chain("chain") - .with_default_resources(|resources| { - resources - .with_request_cpu("100Mi") - .with_limit_memory("1Gi") - .with_limit_cpu("invalid") - }) - .with_validator(|node| node.with_name("node").with_image("invalid image")) - .build() - .unwrap_err(); + #[test] + fn relaychain_config_builder_should_fails_returns_multiple_errors_if_a_node_and_default_resources_are_invalid( + ) { + let errors = RelaychainConfigBuilder::new(Default::default()) + .with_chain("chain") + .with_default_resources(|resources| { + resources + .with_request_cpu("100Mi") + .with_limit_memory("1Gi") + .with_limit_cpu("invalid") + }) + .with_validator(|node| node.with_name("node").with_image("invalid image")) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 2); - assert_eq!( + assert_eq!(errors.len(), 2); + assert_eq!( errors.first().unwrap().to_string(), "relaychain.default_resources.limit_cpu: 'invalid' doesn't match regex '^\\d+(.\\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" ); - assert_eq!( + assert_eq!( errors.get(1).unwrap().to_string(), "relaychain.nodes['node'].image: 'invalid image' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'" ); - } + } - #[test] - fn relaychain_config_builder_should_works_with_chain_spec_command() { - const CMD_TPL: &str = "./bin/chain-spec-generator {% raw %} {{chainName}} {% endraw %}"; - let config = RelaychainConfigBuilder::new(Default::default()) - .with_chain("polkadot") - .with_default_image("myrepo:myimage") - .with_default_command("default_command") - .with_chain_spec_command(CMD_TPL) - .with_fullnode(|node| node.with_name("node1").bootnode(true)) - .build() - .unwrap(); + #[test] + fn relaychain_config_builder_should_works_with_chain_spec_command() { + const CMD_TPL: &str = "./bin/chain-spec-generator {% raw %} {{chainName}} {% endraw %}"; + let config = RelaychainConfigBuilder::new(Default::default()) + .with_chain("polkadot") + .with_default_image("myrepo:myimage") + .with_default_command("default_command") + .with_chain_spec_command(CMD_TPL) + .with_fullnode(|node| node.with_name("node1").bootnode(true)) + .build() + .unwrap(); - assert_eq!(config.chain_spec_command(), Some(CMD_TPL)); - assert!(!config.chain_spec_command_is_local()); - } + assert_eq!(config.chain_spec_command(), Some(CMD_TPL)); + assert!(!config.chain_spec_command_is_local()); + } - #[test] - fn relaychain_config_builder_should_works_with_chain_spec_command_locally() { - const CMD_TPL: &str = "./bin/chain-spec-generator {% raw %} {{chainName}} {% endraw %}"; - let config = RelaychainConfigBuilder::new(Default::default()) - .with_chain("polkadot") - .with_default_image("myrepo:myimage") - .with_default_command("default_command") - .with_chain_spec_command(CMD_TPL) - .chain_spec_command_is_local(true) - .with_fullnode(|node| node.with_name("node1").bootnode(true)) - .build() - .unwrap(); + #[test] + fn relaychain_config_builder_should_works_with_chain_spec_command_locally() { + const CMD_TPL: &str = "./bin/chain-spec-generator {% raw %} {{chainName}} {% endraw %}"; + let config = RelaychainConfigBuilder::new(Default::default()) + .with_chain("polkadot") + .with_default_image("myrepo:myimage") + .with_default_command("default_command") + .with_chain_spec_command(CMD_TPL) + .chain_spec_command_is_local(true) + .with_fullnode(|node| node.with_name("node1").bootnode(true)) + .build() + .unwrap(); - assert_eq!(config.chain_spec_command(), Some(CMD_TPL)); - assert!(config.chain_spec_command_is_local()); - } + assert_eq!(config.chain_spec_command(), Some(CMD_TPL)); + assert!(config.chain_spec_command_is_local()); + } - #[test] - fn relaychain_with_group_config_should_succeeds_and_returns_a_relaychain_config() { - let relaychain_config = RelaychainConfigBuilder::new(Default::default()) - .with_chain("chain") - .with_default_command("command") - .with_validator(|node| node.with_name("node").with_command("node_command")) - .with_node_group(|group| { - group.with_count(2).with_base_node(|base| { - base.with_name("group_node") - .with_command("some_command") - .with_image("repo:image") - .validator(true) - }) - }) - .build() - .unwrap(); + #[test] + fn relaychain_with_group_config_should_succeeds_and_returns_a_relaychain_config() { + let relaychain_config = RelaychainConfigBuilder::new(Default::default()) + .with_chain("chain") + .with_default_command("command") + .with_validator(|node| node.with_name("node").with_command("node_command")) + .with_node_group(|group| { + group.with_count(2).with_base_node(|base| { + base.with_name("group_node") + .with_command("some_command") + .with_image("repo:image") + .validator(true) + }) + }) + .build() + .unwrap(); - assert_eq!(relaychain_config.chain().as_str(), "chain"); - assert_eq!(relaychain_config.nodes().len(), 1); - assert_eq!(relaychain_config.group_node_configs().len(), 1); - assert_eq!( - relaychain_config - .group_node_configs() - .first() - .unwrap() - .count, - 2 - ); - let &node = relaychain_config.nodes().first().unwrap(); - assert_eq!(node.name(), "node"); - assert_eq!(node.command().unwrap().as_str(), "node_command"); + assert_eq!(relaychain_config.chain().as_str(), "chain"); + assert_eq!(relaychain_config.nodes().len(), 1); + assert_eq!(relaychain_config.group_node_configs().len(), 1); + assert_eq!(relaychain_config.group_node_configs().first().unwrap().count, 2); + let &node = relaychain_config.nodes().first().unwrap(); + assert_eq!(node.name(), "node"); + assert_eq!(node.command().unwrap().as_str(), "node_command"); - let group_nodes = relaychain_config.group_node_configs(); - let group_base_node = group_nodes.first().unwrap(); - assert_eq!(group_base_node.base_config.name(), "group_node"); - assert_eq!( - group_base_node.base_config.command().unwrap().as_str(), - "some_command" - ); - assert_eq!( - group_base_node.base_config.image().unwrap().as_str(), - "repo:image" - ); - assert!(group_base_node.base_config.is_validator()); - } + let group_nodes = relaychain_config.group_node_configs(); + let group_base_node = group_nodes.first().unwrap(); + assert_eq!(group_base_node.base_config.name(), "group_node"); + assert_eq!(group_base_node.base_config.command().unwrap().as_str(), "some_command"); + assert_eq!(group_base_node.base_config.image().unwrap().as_str(), "repo:image"); + assert!(group_base_node.base_config.is_validator()); + } - #[test] - fn relaychain_with_group_count_0_config_should_fail() { - let relaychain_config = RelaychainConfigBuilder::new(Default::default()) - .with_chain("chain") - .with_default_command("command") - .with_validator(|node| node.with_name("node").with_command("node_command")) - .with_node_group(|group| { - group.with_count(0).with_base_node(|base| { - base.with_name("group_node") - .with_command("some_command") - .with_image("repo:image") - .validator(true) - }) - }) - .build(); + #[test] + fn relaychain_with_group_count_0_config_should_fail() { + let relaychain_config = RelaychainConfigBuilder::new(Default::default()) + .with_chain("chain") + .with_default_command("command") + .with_validator(|node| node.with_name("node").with_command("node_command")) + .with_node_group(|group| { + group.with_count(0).with_base_node(|base| { + base.with_name("group_node") + .with_command("some_command") + .with_image("repo:image") + .validator(true) + }) + }) + .build(); - let errors: Vec = match relaychain_config { - Ok(_) => vec![], - Err(errs) => errs, - }; + let errors: Vec = match relaychain_config { + Ok(_) => vec![], + Err(errs) => errs, + }; - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - "relaychain.nodes['group_node'].Count cannot be zero" - ); - } + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + "relaychain.nodes['group_node'].Count cannot be zero" + ); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/errors.rs b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/errors.rs index 1a5fd6a3..7d072acd 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/errors.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/errors.rs @@ -3,114 +3,114 @@ use super::types::{ParaId, Port}; /// An error at the configuration level. #[derive(thiserror::Error, Debug)] pub enum ConfigError { - #[error("relaychain.{0}")] - Relaychain(anyhow::Error), + #[error("relaychain.{0}")] + Relaychain(anyhow::Error), - #[error("teyrchain[{0}].{1}")] - Teyrchain(ParaId, anyhow::Error), + #[error("teyrchain[{0}].{1}")] + Teyrchain(ParaId, anyhow::Error), - #[error("global_settings.{0}")] - GlobalSettings(anyhow::Error), + #[error("global_settings.{0}")] + GlobalSettings(anyhow::Error), - #[error("nodes['{0}'].{1}")] - Node(String, anyhow::Error), + #[error("nodes['{0}'].{1}")] + Node(String, anyhow::Error), - #[error("collators['{0}'].{1}")] - Collator(String, anyhow::Error), + #[error("collators['{0}'].{1}")] + Collator(String, anyhow::Error), } /// An error at the field level. #[derive(thiserror::Error, Debug)] pub enum FieldError { - #[error("name: {0}")] - Name(anyhow::Error), + #[error("name: {0}")] + Name(anyhow::Error), - #[error("chain: {0}")] - Chain(anyhow::Error), + #[error("chain: {0}")] + Chain(anyhow::Error), - #[error("image: {0}")] - Image(anyhow::Error), + #[error("image: {0}")] + Image(anyhow::Error), - #[error("default_image: {0}")] - DefaultImage(anyhow::Error), + #[error("default_image: {0}")] + DefaultImage(anyhow::Error), - #[error("command: {0}")] - Command(anyhow::Error), + #[error("command: {0}")] + Command(anyhow::Error), - #[error("default_command: {0}")] - DefaultCommand(anyhow::Error), + #[error("default_command: {0}")] + DefaultCommand(anyhow::Error), - #[error("bootnodes_addresses[{0}]: '{1}' {2}")] - BootnodesAddress(usize, String, anyhow::Error), + #[error("bootnodes_addresses[{0}]: '{1}' {2}")] + BootnodesAddress(usize, String, anyhow::Error), - #[error("genesis_wasm_generator: {0}")] - GenesisWasmGenerator(anyhow::Error), + #[error("genesis_wasm_generator: {0}")] + GenesisWasmGenerator(anyhow::Error), - #[error("genesis_state_generator: {0}")] - GenesisStateGenerator(anyhow::Error), + #[error("genesis_state_generator: {0}")] + GenesisStateGenerator(anyhow::Error), - #[error("local_ip: {0}")] - LocalIp(anyhow::Error), + #[error("local_ip: {0}")] + LocalIp(anyhow::Error), - #[error("default_resources.{0}")] - DefaultResources(anyhow::Error), + #[error("default_resources.{0}")] + DefaultResources(anyhow::Error), - #[error("resources.{0}")] - Resources(anyhow::Error), + #[error("resources.{0}")] + Resources(anyhow::Error), - #[error("request_memory: {0}")] - RequestMemory(anyhow::Error), + #[error("request_memory: {0}")] + RequestMemory(anyhow::Error), - #[error("request_cpu: {0}")] - RequestCpu(anyhow::Error), + #[error("request_cpu: {0}")] + RequestCpu(anyhow::Error), - #[error("limit_memory: {0}")] - LimitMemory(anyhow::Error), + #[error("limit_memory: {0}")] + LimitMemory(anyhow::Error), - #[error("limit_cpu: {0}")] - LimitCpu(anyhow::Error), + #[error("limit_cpu: {0}")] + LimitCpu(anyhow::Error), - #[error("ws_port: {0}")] - WsPort(anyhow::Error), + #[error("ws_port: {0}")] + WsPort(anyhow::Error), - #[error("rpc_port: {0}")] - RpcPort(anyhow::Error), + #[error("rpc_port: {0}")] + RpcPort(anyhow::Error), - #[error("prometheus_port: {0}")] - PrometheusPort(anyhow::Error), + #[error("prometheus_port: {0}")] + PrometheusPort(anyhow::Error), - #[error("p2p_port: {0}")] - P2pPort(anyhow::Error), + #[error("p2p_port: {0}")] + P2pPort(anyhow::Error), - #[error("session_key: {0}")] - SessionKey(anyhow::Error), + #[error("session_key: {0}")] + SessionKey(anyhow::Error), - #[error("registration_strategy: {0}")] - RegistrationStrategy(anyhow::Error), + #[error("registration_strategy: {0}")] + RegistrationStrategy(anyhow::Error), } /// A conversion error for shared types across fields. #[derive(thiserror::Error, Debug, Clone)] pub enum ConversionError { - #[error("'{0}' shouldn't contains whitespace")] - ContainsWhitespaces(String), + #[error("'{0}' shouldn't contains whitespace")] + ContainsWhitespaces(String), - #[error("'{}' doesn't match regex '{}'", .value, .regex)] - DoesntMatchRegex { value: String, regex: String }, + #[error("'{}' doesn't match regex '{}'", .value, .regex)] + DoesntMatchRegex { value: String, regex: String }, - #[error("can't be empty")] - CantBeEmpty, + #[error("can't be empty")] + CantBeEmpty, - #[error("deserialize error")] - DeserializeError(String), + #[error("deserialize error")] + DeserializeError(String), } /// A validation error for shared types across fields. #[derive(thiserror::Error, Debug, Clone)] pub enum ValidationError { - #[error("'{0}' is already used across config")] - PortAlreadyUsed(Port), + #[error("'{0}' is already used across config")] + PortAlreadyUsed(Port), - #[error("can't be empty")] - CantBeEmpty(), + #[error("can't be empty")] + CantBeEmpty(), } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/helpers.rs b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/helpers.rs index 5fa50d95..22ee0495 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/helpers.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/helpers.rs @@ -4,28 +4,28 @@ use support::constants::{BORROWABLE, THIS_IS_A_BUG}; use tracing::warn; use super::{ - errors::ValidationError, - types::{ParaId, Port, ValidationContext}, + errors::ValidationError, + types::{ParaId, Port, ValidationContext}, }; pub fn merge_errors(errors: Vec, new_error: anyhow::Error) -> Vec { - let mut errors = errors; - errors.push(new_error); + let mut errors = errors; + errors.push(new_error); - errors + errors } pub fn merge_errors_vecs( - errors: Vec, - new_errors: Vec, + errors: Vec, + new_errors: Vec, ) -> Vec { - let mut errors = errors; + let mut errors = errors; - for new_error in new_errors.into_iter() { - errors.push(new_error); - } + for new_error in new_errors.into_iter() { + errors.push(new_error); + } - errors + errors } /// Generates a unique name from a base name and the names already present in a @@ -34,14 +34,13 @@ pub fn merge_errors_vecs( /// Uses [`generate_unique_node_name_from_names()`] internally to ensure uniqueness. /// Logs a warning if the generated name differs from the original due to duplicates. pub fn generate_unique_node_name( - node_name: impl Into, - validation_context: Rc>, + node_name: impl Into, + validation_context: Rc>, ) -> String { - let mut context = validation_context - .try_borrow_mut() - .expect(&format!("{BORROWABLE}, {THIS_IS_A_BUG}")); + let mut context = + validation_context.try_borrow_mut().expect(&format!("{BORROWABLE}, {THIS_IS_A_BUG}")); - generate_unique_node_name_from_names(node_name, &mut context.used_nodes_names) + generate_unique_node_name_from_names(node_name, &mut context.used_nodes_names) } /// Returns `node_name` if it is not already in `names`. @@ -49,70 +48,68 @@ pub fn generate_unique_node_name( /// Otherwise, appends an incrementing `-{counter}` suffix until a unique name is found, /// then returns it. Logs a warning when a duplicate is detected. pub fn generate_unique_node_name_from_names( - node_name: impl Into, - names: &mut HashSet, + node_name: impl Into, + names: &mut HashSet, ) -> String { - let node_name = node_name.into(); + let node_name = node_name.into(); - if names.insert(node_name.clone()) { - return node_name; - } + if names.insert(node_name.clone()) { + return node_name; + } - let mut counter = 1; - let mut candidate = node_name.clone(); - while names.contains(&candidate) { - candidate = format!("{node_name}-{counter}"); - counter += 1; - } + let mut counter = 1; + let mut candidate = node_name.clone(); + while names.contains(&candidate) { + candidate = format!("{node_name}-{counter}"); + counter += 1; + } - warn!( - original = %node_name, - adjusted = %candidate, - "Duplicate node name detected." - ); + warn!( + original = %node_name, + adjusted = %candidate, + "Duplicate node name detected." + ); - names.insert(candidate.clone()); - candidate + names.insert(candidate.clone()); + candidate } pub fn ensure_value_is_not_empty(value: &str) -> Result<(), anyhow::Error> { - if value.is_empty() { - Err(ValidationError::CantBeEmpty().into()) - } else { - Ok(()) - } + if value.is_empty() { + Err(ValidationError::CantBeEmpty().into()) + } else { + Ok(()) + } } pub fn ensure_port_unique( - port: Port, - validation_context: Rc>, + port: Port, + validation_context: Rc>, ) -> Result<(), anyhow::Error> { - let mut context = validation_context - .try_borrow_mut() - .expect(&format!("{BORROWABLE}, {THIS_IS_A_BUG}")); + let mut context = + validation_context.try_borrow_mut().expect(&format!("{BORROWABLE}, {THIS_IS_A_BUG}")); - if !context.used_ports.contains(&port) { - context.used_ports.push(port); - return Ok(()); - } + if !context.used_ports.contains(&port) { + context.used_ports.push(port); + return Ok(()); + } - Err(ValidationError::PortAlreadyUsed(port).into()) + Err(ValidationError::PortAlreadyUsed(port).into()) } pub fn generate_unique_para_id( - para_id: ParaId, - validation_context: Rc>, + para_id: ParaId, + validation_context: Rc>, ) -> String { - let mut context = validation_context - .try_borrow_mut() - .expect(&format!("{BORROWABLE}, {THIS_IS_A_BUG}")); + let mut context = + validation_context.try_borrow_mut().expect(&format!("{BORROWABLE}, {THIS_IS_A_BUG}")); - if let Some(suffix) = context.used_para_ids.get_mut(¶_id) { - *suffix += 1; - format!("{para_id}-{suffix}") - } else { - // insert 0, since will be used next time. - context.used_para_ids.insert(para_id, 0); - para_id.to_string() - } + if let Some(suffix) = context.used_para_ids.get_mut(¶_id) { + *suffix += 1; + format!("{para_id}-{suffix}") + } else { + // insert 0, since will be used next time. + context.used_para_ids.insert(para_id, 0); + para_id.to_string() + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/node.rs b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/node.rs index 03613d28..b684fcf9 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/node.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/node.rs @@ -4,26 +4,26 @@ use multiaddr::Multiaddr; use serde::{ser::SerializeStruct, Deserialize, Serialize}; use super::{ - errors::FieldError, - helpers::{ - ensure_port_unique, ensure_value_is_not_empty, generate_unique_node_name, - generate_unique_node_name_from_names, merge_errors, merge_errors_vecs, - }, - macros::states, - resources::ResourcesBuilder, - types::{AssetLocation, ChainDefaultContext, Command, Image, ValidationContext, U128}, + errors::FieldError, + helpers::{ + ensure_port_unique, ensure_value_is_not_empty, generate_unique_node_name, + generate_unique_node_name_from_names, merge_errors, merge_errors_vecs, + }, + macros::states, + resources::ResourcesBuilder, + types::{AssetLocation, ChainDefaultContext, Command, Image, ValidationContext, U128}, }; use crate::{ - shared::{ - resources::Resources, - types::{Arg, Port}, - }, - utils::{default_as_true, default_initial_balance}, + shared::{ + resources::Resources, + types::{Arg, Port}, + }, + utils::{default_as_true, default_initial_balance}, }; states! { - Buildable, - Initial + Buildable, + Initial } /// An environment variable with a name and a value. @@ -46,1310 +46,1211 @@ states! { /// ``` #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct EnvVar { - /// The name of the environment variable. - pub name: String, + /// The name of the environment variable. + pub name: String, - /// The value of the environment variable. - pub value: String, + /// The value of the environment variable. + pub value: String, } impl From<(&str, &str)> for EnvVar { - fn from((name, value): (&str, &str)) -> Self { - Self { - name: name.to_owned(), - value: value.to_owned(), - } - } + fn from((name, value): (&str, &str)) -> Self { + Self { name: name.to_owned(), value: value.to_owned() } + } } /// A node configuration, with fine-grained configuration options. #[derive(Debug, Clone, Default, PartialEq, Deserialize)] pub struct NodeConfig { - pub(crate) name: String, - pub(crate) image: Option, - pub(crate) command: Option, - pub(crate) subcommand: Option, - #[serde(default)] - args: Vec, - #[serde(alias = "validator", default = "default_as_true")] - pub(crate) is_validator: bool, - #[serde(alias = "invulnerable", default = "default_as_true")] - pub(crate) is_invulnerable: bool, - #[serde(alias = "bootnode", default)] - pub(crate) is_bootnode: bool, - #[serde(alias = "balance")] - #[serde(default = "default_initial_balance")] - initial_balance: U128, - #[serde(default)] - env: Vec, - #[serde(default)] - bootnodes_addresses: Vec, - pub(crate) resources: Option, - ws_port: Option, - rpc_port: Option, - prometheus_port: Option, - p2p_port: Option, - p2p_cert_hash: Option, - pub(crate) db_snapshot: Option, - /// Optional override for the automatically generated EVM (eth) session key. - /// When set, override the auto-generated key so the seed will not be part of the resulting zombie.json - #[serde(default, skip_serializing_if = "Option::is_none")] - override_eth_key: Option, - #[serde(default)] - // used to skip serialization of fields with defaults to avoid duplication - pub(crate) chain_context: ChainDefaultContext, - pub(crate) node_log_path: Option, - // optional node keystore path override - keystore_path: Option, - /// Keystore key types to generate. - /// Supports short form (e.g., "audi") using predefined schemas, - /// or long form (e.g., "audi_sr") with explicit schema (sr, ed, ec). - #[serde(default)] - keystore_key_types: Vec, + pub(crate) name: String, + pub(crate) image: Option, + pub(crate) command: Option, + pub(crate) subcommand: Option, + #[serde(default)] + args: Vec, + #[serde(alias = "validator", default = "default_as_true")] + pub(crate) is_validator: bool, + #[serde(alias = "invulnerable", default = "default_as_true")] + pub(crate) is_invulnerable: bool, + #[serde(alias = "bootnode", default)] + pub(crate) is_bootnode: bool, + #[serde(alias = "balance")] + #[serde(default = "default_initial_balance")] + initial_balance: U128, + #[serde(default)] + env: Vec, + #[serde(default)] + bootnodes_addresses: Vec, + pub(crate) resources: Option, + ws_port: Option, + rpc_port: Option, + prometheus_port: Option, + p2p_port: Option, + p2p_cert_hash: Option, + pub(crate) db_snapshot: Option, + /// Optional override for the automatically generated EVM (eth) session key. + /// When set, override the auto-generated key so the seed will not be part of the resulting zombie.json + #[serde(default, skip_serializing_if = "Option::is_none")] + override_eth_key: Option, + #[serde(default)] + // used to skip serialization of fields with defaults to avoid duplication + pub(crate) chain_context: ChainDefaultContext, + pub(crate) node_log_path: Option, + // optional node keystore path override + keystore_path: Option, + /// Keystore key types to generate. + /// Supports short form (e.g., "audi") using predefined schemas, + /// or long form (e.g., "audi_sr") with explicit schema (sr, ed, ec). + #[serde(default)] + keystore_key_types: Vec, } impl Serialize for NodeConfig { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut state = serializer.serialize_struct("NodeConfig", 19)?; - state.serialize_field("name", &self.name)?; + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("NodeConfig", 19)?; + state.serialize_field("name", &self.name)?; - if self.image == self.chain_context.default_image { - state.skip_field("image")?; - } else { - state.serialize_field("image", &self.image)?; - } + if self.image == self.chain_context.default_image { + state.skip_field("image")?; + } else { + state.serialize_field("image", &self.image)?; + } - if self.command == self.chain_context.default_command { - state.skip_field("command")?; - } else { - state.serialize_field("command", &self.command)?; - } + if self.command == self.chain_context.default_command { + state.skip_field("command")?; + } else { + state.serialize_field("command", &self.command)?; + } - if self.subcommand.is_none() { - state.skip_field("subcommand")?; - } else { - state.serialize_field("subcommand", &self.subcommand)?; - } + if self.subcommand.is_none() { + state.skip_field("subcommand")?; + } else { + state.serialize_field("subcommand", &self.subcommand)?; + } - if self.args.is_empty() || self.args == self.chain_context.default_args { - state.skip_field("args")?; - } else { - state.serialize_field("args", &self.args)?; - } + if self.args.is_empty() || self.args == self.chain_context.default_args { + state.skip_field("args")?; + } else { + state.serialize_field("args", &self.args)?; + } - state.serialize_field("validator", &self.is_validator)?; - state.serialize_field("invulnerable", &self.is_invulnerable)?; - state.serialize_field("bootnode", &self.is_bootnode)?; - state.serialize_field("balance", &self.initial_balance)?; + state.serialize_field("validator", &self.is_validator)?; + state.serialize_field("invulnerable", &self.is_invulnerable)?; + state.serialize_field("bootnode", &self.is_bootnode)?; + state.serialize_field("balance", &self.initial_balance)?; - if self.env.is_empty() { - state.skip_field("env")?; - } else { - state.serialize_field("env", &self.env)?; - } + if self.env.is_empty() { + state.skip_field("env")?; + } else { + state.serialize_field("env", &self.env)?; + } - if self.bootnodes_addresses.is_empty() { - state.skip_field("bootnodes_addresses")?; - } else { - state.serialize_field("bootnodes_addresses", &self.bootnodes_addresses)?; - } + if self.bootnodes_addresses.is_empty() { + state.skip_field("bootnodes_addresses")?; + } else { + state.serialize_field("bootnodes_addresses", &self.bootnodes_addresses)?; + } - if self.resources == self.chain_context.default_resources { - state.skip_field("resources")?; - } else { - state.serialize_field("resources", &self.resources)?; - } + if self.resources == self.chain_context.default_resources { + state.skip_field("resources")?; + } else { + state.serialize_field("resources", &self.resources)?; + } - state.serialize_field("ws_port", &self.ws_port)?; - state.serialize_field("rpc_port", &self.rpc_port)?; - state.serialize_field("prometheus_port", &self.prometheus_port)?; - state.serialize_field("p2p_port", &self.p2p_port)?; - state.serialize_field("p2p_cert_hash", &self.p2p_cert_hash)?; - state.serialize_field("override_eth_key", &self.override_eth_key)?; + state.serialize_field("ws_port", &self.ws_port)?; + state.serialize_field("rpc_port", &self.rpc_port)?; + state.serialize_field("prometheus_port", &self.prometheus_port)?; + state.serialize_field("p2p_port", &self.p2p_port)?; + state.serialize_field("p2p_cert_hash", &self.p2p_cert_hash)?; + state.serialize_field("override_eth_key", &self.override_eth_key)?; - if self.db_snapshot == self.chain_context.default_db_snapshot { - state.skip_field("db_snapshot")?; - } else { - state.serialize_field("db_snapshot", &self.db_snapshot)?; - } + if self.db_snapshot == self.chain_context.default_db_snapshot { + state.skip_field("db_snapshot")?; + } else { + state.serialize_field("db_snapshot", &self.db_snapshot)?; + } - if self.node_log_path.is_none() { - state.skip_field("node_log_path")?; - } else { - state.serialize_field("node_log_path", &self.node_log_path)?; - } + if self.node_log_path.is_none() { + state.skip_field("node_log_path")?; + } else { + state.serialize_field("node_log_path", &self.node_log_path)?; + } - if self.keystore_path.is_none() { - state.skip_field("keystore_path")?; - } else { - state.serialize_field("keystore_path", &self.keystore_path)?; - } + if self.keystore_path.is_none() { + state.skip_field("keystore_path")?; + } else { + state.serialize_field("keystore_path", &self.keystore_path)?; + } - if self.keystore_key_types.is_empty() { - state.skip_field("keystore_key_types")?; - } else { - state.serialize_field("keystore_key_types", &self.keystore_key_types)?; - } + if self.keystore_key_types.is_empty() { + state.skip_field("keystore_key_types")?; + } else { + state.serialize_field("keystore_key_types", &self.keystore_key_types)?; + } - state.skip_field("chain_context")?; - state.end() - } + state.skip_field("chain_context")?; + state.end() + } } /// A group of nodes configuration #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct GroupNodeConfig { - #[serde(flatten)] - pub(crate) base_config: NodeConfig, - pub(crate) count: usize, + #[serde(flatten)] + pub(crate) base_config: NodeConfig, + pub(crate) count: usize, } impl GroupNodeConfig { - /// Expands the group into individual node configs. - /// Each node will have the same base configuration, but with unique names and log paths. - pub fn expand_group_configs(&self) -> Vec { - let mut used_names = std::collections::HashSet::new(); + /// Expands the group into individual node configs. + /// Each node will have the same base configuration, but with unique names and log paths. + pub fn expand_group_configs(&self) -> Vec { + let mut used_names = std::collections::HashSet::new(); - (0..self.count) - .map(|_index| { - let mut node = self.base_config.clone(); + (0..self.count) + .map(|_index| { + let mut node = self.base_config.clone(); - let unique_name = generate_unique_node_name_from_names(node.name, &mut used_names); - node.name = unique_name; + let unique_name = generate_unique_node_name_from_names(node.name, &mut used_names); + node.name = unique_name; - // If base config has a log path, generate unique log path for each node - if let Some(ref base_log_path) = node.node_log_path { - let unique_log_path = if let Some(parent) = base_log_path.parent() { - parent.join(format!("{}.log", node.name)) - } else { - PathBuf::from(format!("{}.log", node.name)) - }; - node.node_log_path = Some(unique_log_path); - } + // If base config has a log path, generate unique log path for each node + if let Some(ref base_log_path) = node.node_log_path { + let unique_log_path = if let Some(parent) = base_log_path.parent() { + parent.join(format!("{}.log", node.name)) + } else { + PathBuf::from(format!("{}.log", node.name)) + }; + node.node_log_path = Some(unique_log_path); + } - node - }) - .collect() - } + node + }) + .collect() + } } impl Serialize for GroupNodeConfig { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut state = serializer.serialize_struct("GroupNodeConfig", 18)?; - state.serialize_field("NodeConfig", &self.base_config)?; - state.serialize_field("count", &self.count)?; - state.end() - } + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("GroupNodeConfig", 18)?; + state.serialize_field("NodeConfig", &self.base_config)?; + state.serialize_field("count", &self.count)?; + state.end() + } } impl NodeConfig { - /// Node name (should be unique). - pub fn name(&self) -> &str { - &self.name - } + /// Node name (should be unique). + pub fn name(&self) -> &str { + &self.name + } - /// Image to run (only podman/k8s). - pub fn image(&self) -> Option<&Image> { - self.image.as_ref() - } + /// Image to run (only podman/k8s). + pub fn image(&self) -> Option<&Image> { + self.image.as_ref() + } - /// Command to run the node. - pub fn command(&self) -> Option<&Command> { - self.command.as_ref() - } + /// Command to run the node. + pub fn command(&self) -> Option<&Command> { + self.command.as_ref() + } - /// Subcommand to run the node. - pub fn subcommand(&self) -> Option<&Command> { - self.subcommand.as_ref() - } + /// Subcommand to run the node. + pub fn subcommand(&self) -> Option<&Command> { + self.subcommand.as_ref() + } - /// Arguments to use for node. - pub fn args(&self) -> Vec<&Arg> { - self.args.iter().collect() - } + /// Arguments to use for node. + pub fn args(&self) -> Vec<&Arg> { + self.args.iter().collect() + } - /// Arguments to use for node. - pub(crate) fn set_args(&mut self, args: Vec) { - self.args = args; - } + /// Arguments to use for node. + pub(crate) fn set_args(&mut self, args: Vec) { + self.args = args; + } - /// Whether the node is a validator. - pub fn is_validator(&self) -> bool { - self.is_validator - } + /// Whether the node is a validator. + pub fn is_validator(&self) -> bool { + self.is_validator + } - /// Whether the node keys must be added to invulnerables. - pub fn is_invulnerable(&self) -> bool { - self.is_invulnerable - } + /// Whether the node keys must be added to invulnerables. + pub fn is_invulnerable(&self) -> bool { + self.is_invulnerable + } - /// Whether the node is a bootnode. - pub fn is_bootnode(&self) -> bool { - self.is_bootnode - } + /// Whether the node is a bootnode. + pub fn is_bootnode(&self) -> bool { + self.is_bootnode + } - /// Node initial balance present in genesis. - pub fn initial_balance(&self) -> u128 { - self.initial_balance.0 - } + /// Node initial balance present in genesis. + pub fn initial_balance(&self) -> u128 { + self.initial_balance.0 + } - /// Environment variables to set (inside pod for podman/k8s, inside shell for native). - pub fn env(&self) -> Vec<&EnvVar> { - self.env.iter().collect() - } + /// Environment variables to set (inside pod for podman/k8s, inside shell for native). + pub fn env(&self) -> Vec<&EnvVar> { + self.env.iter().collect() + } - /// List of node's bootnodes addresses to use. - pub fn bootnodes_addresses(&self) -> Vec<&Multiaddr> { - self.bootnodes_addresses.iter().collect() - } + /// List of node's bootnodes addresses to use. + pub fn bootnodes_addresses(&self) -> Vec<&Multiaddr> { + self.bootnodes_addresses.iter().collect() + } - /// Default resources. - pub fn resources(&self) -> Option<&Resources> { - self.resources.as_ref() - } + /// Default resources. + pub fn resources(&self) -> Option<&Resources> { + self.resources.as_ref() + } - /// Websocket port to use. - pub fn ws_port(&self) -> Option { - self.ws_port - } + /// Websocket port to use. + pub fn ws_port(&self) -> Option { + self.ws_port + } - /// RPC port to use. - pub fn rpc_port(&self) -> Option { - self.rpc_port - } + /// RPC port to use. + pub fn rpc_port(&self) -> Option { + self.rpc_port + } - /// Prometheus port to use. - pub fn prometheus_port(&self) -> Option { - self.prometheus_port - } + /// Prometheus port to use. + pub fn prometheus_port(&self) -> Option { + self.prometheus_port + } - /// P2P port to use. - pub fn p2p_port(&self) -> Option { - self.p2p_port - } + /// P2P port to use. + pub fn p2p_port(&self) -> Option { + self.p2p_port + } - /// `libp2p` cert hash to use with `WebRTC` transport. - pub fn p2p_cert_hash(&self) -> Option<&str> { - self.p2p_cert_hash.as_deref() - } + /// `libp2p` cert hash to use with `WebRTC` transport. + pub fn p2p_cert_hash(&self) -> Option<&str> { + self.p2p_cert_hash.as_deref() + } - /// Database snapshot. - pub fn db_snapshot(&self) -> Option<&AssetLocation> { - self.db_snapshot.as_ref() - } + /// Database snapshot. + pub fn db_snapshot(&self) -> Option<&AssetLocation> { + self.db_snapshot.as_ref() + } - /// Node log path - pub fn node_log_path(&self) -> Option<&PathBuf> { - self.node_log_path.as_ref() - } + /// Node log path + pub fn node_log_path(&self) -> Option<&PathBuf> { + self.node_log_path.as_ref() + } - /// Keystore path - pub fn keystore_path(&self) -> Option<&PathBuf> { - self.keystore_path.as_ref() - } + /// Keystore path + pub fn keystore_path(&self) -> Option<&PathBuf> { + self.keystore_path.as_ref() + } - /// Override EVM session key to use for the node - pub fn override_eth_key(&self) -> Option<&str> { - self.override_eth_key.as_deref() - } + /// Override EVM session key to use for the node + pub fn override_eth_key(&self) -> Option<&str> { + self.override_eth_key.as_deref() + } - /// Keystore key types to generate. - /// Returns the list of key type specifications (short form like "audi" or long form like "audi_sr"). - pub fn keystore_key_types(&self) -> Vec<&str> { - self.keystore_key_types.iter().map(String::as_str).collect() - } + /// Keystore key types to generate. + /// Returns the list of key type specifications (short form like "audi" or long form like "audi_sr"). + pub fn keystore_key_types(&self) -> Vec<&str> { + self.keystore_key_types.iter().map(String::as_str).collect() + } } /// A node configuration builder, used to build a [`NodeConfig`] declaratively with fields validation. pub struct NodeConfigBuilder { - config: NodeConfig, - validation_context: Rc>, - errors: Vec, - _state: PhantomData, + config: NodeConfig, + validation_context: Rc>, + errors: Vec, + _state: PhantomData, } impl Default for NodeConfigBuilder { - fn default() -> Self { - Self { - config: NodeConfig { - name: "".into(), - image: None, - command: None, - subcommand: None, - args: vec![], - is_validator: true, - is_invulnerable: true, - is_bootnode: false, - initial_balance: 2_000_000_000_000.into(), - env: vec![], - bootnodes_addresses: vec![], - resources: None, - ws_port: None, - rpc_port: None, - prometheus_port: None, - p2p_port: None, - p2p_cert_hash: None, - db_snapshot: None, - override_eth_key: None, - chain_context: Default::default(), - node_log_path: None, - keystore_path: None, - keystore_key_types: vec![], - }, - validation_context: Default::default(), - errors: vec![], - _state: PhantomData, - } - } + fn default() -> Self { + Self { + config: NodeConfig { + name: "".into(), + image: None, + command: None, + subcommand: None, + args: vec![], + is_validator: true, + is_invulnerable: true, + is_bootnode: false, + initial_balance: 2_000_000_000_000.into(), + env: vec![], + bootnodes_addresses: vec![], + resources: None, + ws_port: None, + rpc_port: None, + prometheus_port: None, + p2p_port: None, + p2p_cert_hash: None, + db_snapshot: None, + override_eth_key: None, + chain_context: Default::default(), + node_log_path: None, + keystore_path: None, + keystore_key_types: vec![], + }, + validation_context: Default::default(), + errors: vec![], + _state: PhantomData, + } + } } impl NodeConfigBuilder { - fn transition( - config: NodeConfig, - validation_context: Rc>, - errors: Vec, - ) -> NodeConfigBuilder { - NodeConfigBuilder { - config, - validation_context, - errors, - _state: PhantomData, - } - } + fn transition( + config: NodeConfig, + validation_context: Rc>, + errors: Vec, + ) -> NodeConfigBuilder { + NodeConfigBuilder { config, validation_context, errors, _state: PhantomData } + } } impl NodeConfigBuilder { - pub fn new( - chain_context: ChainDefaultContext, - validation_context: Rc>, - ) -> Self { - Self::transition( - NodeConfig { - command: chain_context.default_command.clone(), - image: chain_context.default_image.clone(), - resources: chain_context.default_resources.clone(), - db_snapshot: chain_context.default_db_snapshot.clone(), - args: chain_context.default_args.clone(), - chain_context, - ..Self::default().config - }, - validation_context, - vec![], - ) - } + pub fn new( + chain_context: ChainDefaultContext, + validation_context: Rc>, + ) -> Self { + Self::transition( + NodeConfig { + command: chain_context.default_command.clone(), + image: chain_context.default_image.clone(), + resources: chain_context.default_resources.clone(), + db_snapshot: chain_context.default_db_snapshot.clone(), + args: chain_context.default_args.clone(), + chain_context, + ..Self::default().config + }, + validation_context, + vec![], + ) + } - /// Set the name of the node. - pub fn with_name + Copy>(self, name: T) -> NodeConfigBuilder { - let name: String = generate_unique_node_name(name, self.validation_context.clone()); + /// Set the name of the node. + pub fn with_name + Copy>(self, name: T) -> NodeConfigBuilder { + let name: String = generate_unique_node_name(name, self.validation_context.clone()); - match ensure_value_is_not_empty(&name) { - Ok(_) => Self::transition( - NodeConfig { - name, - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(e) => Self::transition( - NodeConfig { - // we still set the name in error case to display error path - name, - ..self.config - }, - self.validation_context, - merge_errors(self.errors, FieldError::Name(e).into()), - ), - } - } + match ensure_value_is_not_empty(&name) { + Ok(_) => Self::transition( + NodeConfig { name, ..self.config }, + self.validation_context, + self.errors, + ), + Err(e) => Self::transition( + NodeConfig { + // we still set the name in error case to display error path + name, + ..self.config + }, + self.validation_context, + merge_errors(self.errors, FieldError::Name(e).into()), + ), + } + } } impl NodeConfigBuilder { - /// Set the command that will be executed to launch the node. Override the default. - pub fn with_command(self, command: T) -> Self - where - T: TryInto, - T::Error: Error + Send + Sync + 'static, - { - match command.try_into() { - Ok(command) => Self::transition( - NodeConfig { - command: Some(command), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(error) => Self::transition( - self.config, - self.validation_context, - merge_errors(self.errors, FieldError::Command(error.into()).into()), - ), - } - } + /// Set the command that will be executed to launch the node. Override the default. + pub fn with_command(self, command: T) -> Self + where + T: TryInto, + T::Error: Error + Send + Sync + 'static, + { + match command.try_into() { + Ok(command) => Self::transition( + NodeConfig { command: Some(command), ..self.config }, + self.validation_context, + self.errors, + ), + Err(error) => Self::transition( + self.config, + self.validation_context, + merge_errors(self.errors, FieldError::Command(error.into()).into()), + ), + } + } - /// Set the subcommand that will be executed to launch the node. - pub fn with_subcommand(self, subcommand: T) -> Self - where - T: TryInto, - T::Error: Error + Send + Sync + 'static, - { - match subcommand.try_into() { - Ok(subcommand) => Self::transition( - NodeConfig { - subcommand: Some(subcommand), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(error) => Self::transition( - self.config, - self.validation_context, - merge_errors(self.errors, FieldError::Command(error.into()).into()), - ), - } - } + /// Set the subcommand that will be executed to launch the node. + pub fn with_subcommand(self, subcommand: T) -> Self + where + T: TryInto, + T::Error: Error + Send + Sync + 'static, + { + match subcommand.try_into() { + Ok(subcommand) => Self::transition( + NodeConfig { subcommand: Some(subcommand), ..self.config }, + self.validation_context, + self.errors, + ), + Err(error) => Self::transition( + self.config, + self.validation_context, + merge_errors(self.errors, FieldError::Command(error.into()).into()), + ), + } + } - /// Set the image that will be used for the node (only podman/k8s). Override the default. - pub fn with_image(self, image: T) -> Self - where - T: TryInto, - T::Error: Error + Send + Sync + 'static, - { - match image.try_into() { - Ok(image) => Self::transition( - NodeConfig { - image: Some(image), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(error) => Self::transition( - self.config, - self.validation_context, - merge_errors(self.errors, FieldError::Image(error.into()).into()), - ), - } - } + /// Set the image that will be used for the node (only podman/k8s). Override the default. + pub fn with_image(self, image: T) -> Self + where + T: TryInto, + T::Error: Error + Send + Sync + 'static, + { + match image.try_into() { + Ok(image) => Self::transition( + NodeConfig { image: Some(image), ..self.config }, + self.validation_context, + self.errors, + ), + Err(error) => Self::transition( + self.config, + self.validation_context, + merge_errors(self.errors, FieldError::Image(error.into()).into()), + ), + } + } - /// Set the arguments that will be used when launching the node. Override the default. - pub fn with_args(self, args: Vec) -> Self { - Self::transition( - NodeConfig { - args, - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the arguments that will be used when launching the node. Override the default. + pub fn with_args(self, args: Vec) -> Self { + Self::transition(NodeConfig { args, ..self.config }, self.validation_context, self.errors) + } - /// Set whether the node is a validator. - pub fn validator(self, choice: bool) -> Self { - Self::transition( - NodeConfig { - is_validator: choice, - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set whether the node is a validator. + pub fn validator(self, choice: bool) -> Self { + Self::transition( + NodeConfig { is_validator: choice, ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set whether the node is invulnerable. - pub fn invulnerable(self, choice: bool) -> Self { - Self::transition( - NodeConfig { - is_invulnerable: choice, - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set whether the node is invulnerable. + pub fn invulnerable(self, choice: bool) -> Self { + Self::transition( + NodeConfig { is_invulnerable: choice, ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set whether the node is a bootnode. - pub fn bootnode(self, choice: bool) -> Self { - Self::transition( - NodeConfig { - is_bootnode: choice, - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set whether the node is a bootnode. + pub fn bootnode(self, choice: bool) -> Self { + Self::transition( + NodeConfig { is_bootnode: choice, ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Override the EVM session key to use for the node - pub fn with_override_eth_key(self, session_key: impl Into) -> Self { - Self::transition( - NodeConfig { - override_eth_key: Some(session_key.into()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Override the EVM session key to use for the node + pub fn with_override_eth_key(self, session_key: impl Into) -> Self { + Self::transition( + NodeConfig { override_eth_key: Some(session_key.into()), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the node initial balance. - pub fn with_initial_balance(self, initial_balance: u128) -> Self { - Self::transition( - NodeConfig { - initial_balance: initial_balance.into(), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the node initial balance. + pub fn with_initial_balance(self, initial_balance: u128) -> Self { + Self::transition( + NodeConfig { initial_balance: initial_balance.into(), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the node environment variables that will be used when launched. Override the default. - pub fn with_env(self, env: Vec>) -> Self { - let env = env.into_iter().map(|var| var.into()).collect::>(); + /// Set the node environment variables that will be used when launched. Override the default. + pub fn with_env(self, env: Vec>) -> Self { + let env = env.into_iter().map(|var| var.into()).collect::>(); - Self::transition( - NodeConfig { env, ..self.config }, - self.validation_context, - self.errors, - ) - } + Self::transition(NodeConfig { env, ..self.config }, self.validation_context, self.errors) + } - /// Set the bootnodes addresses that the node will try to connect to. Override the default. - /// - /// Note: Bootnode address replacements are NOT supported here. - /// Only arguments (`args`) support dynamic replacements. Bootnode addresses must be a valid address. - pub fn with_raw_bootnodes_addresses(self, bootnodes_addresses: Vec) -> Self - where - T: TryInto + Display + Copy, - T::Error: Error + Send + Sync + 'static, - { - let mut addrs = vec![]; - let mut errors = vec![]; + /// Set the bootnodes addresses that the node will try to connect to. Override the default. + /// + /// Note: Bootnode address replacements are NOT supported here. + /// Only arguments (`args`) support dynamic replacements. Bootnode addresses must be a valid address. + pub fn with_raw_bootnodes_addresses(self, bootnodes_addresses: Vec) -> Self + where + T: TryInto + Display + Copy, + T::Error: Error + Send + Sync + 'static, + { + let mut addrs = vec![]; + let mut errors = vec![]; - for (index, addr) in bootnodes_addresses.into_iter().enumerate() { - match addr.try_into() { - Ok(addr) => addrs.push(addr), - Err(error) => errors.push( - FieldError::BootnodesAddress(index, addr.to_string(), error.into()).into(), - ), - } - } + for (index, addr) in bootnodes_addresses.into_iter().enumerate() { + match addr.try_into() { + Ok(addr) => addrs.push(addr), + Err(error) => errors.push( + FieldError::BootnodesAddress(index, addr.to_string(), error.into()).into(), + ), + } + } - Self::transition( - NodeConfig { - bootnodes_addresses: addrs, - ..self.config - }, - self.validation_context, - merge_errors_vecs(self.errors, errors), - ) - } + Self::transition( + NodeConfig { bootnodes_addresses: addrs, ..self.config }, + self.validation_context, + merge_errors_vecs(self.errors, errors), + ) + } - /// Set the resources limits what will be used for the node (only podman/k8s). Override the default. - pub fn with_resources(self, f: impl FnOnce(ResourcesBuilder) -> ResourcesBuilder) -> Self { - match f(ResourcesBuilder::new()).build() { - Ok(resources) => Self::transition( - NodeConfig { - resources: Some(resources), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(errors) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| FieldError::Resources(error).into()) - .collect::>(), - ), - ), - } - } + /// Set the resources limits what will be used for the node (only podman/k8s). Override the default. + pub fn with_resources(self, f: impl FnOnce(ResourcesBuilder) -> ResourcesBuilder) -> Self { + match f(ResourcesBuilder::new()).build() { + Ok(resources) => Self::transition( + NodeConfig { resources: Some(resources), ..self.config }, + self.validation_context, + self.errors, + ), + Err(errors) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| FieldError::Resources(error).into()) + .collect::>(), + ), + ), + } + } - /// Set the websocket port that will be exposed. Uniqueness across config will be checked. - pub fn with_ws_port(self, ws_port: Port) -> Self { - match ensure_port_unique(ws_port, self.validation_context.clone()) { - Ok(_) => Self::transition( - NodeConfig { - ws_port: Some(ws_port), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(error) => Self::transition( - self.config, - self.validation_context, - merge_errors(self.errors, FieldError::WsPort(error).into()), - ), - } - } + /// Set the websocket port that will be exposed. Uniqueness across config will be checked. + pub fn with_ws_port(self, ws_port: Port) -> Self { + match ensure_port_unique(ws_port, self.validation_context.clone()) { + Ok(_) => Self::transition( + NodeConfig { ws_port: Some(ws_port), ..self.config }, + self.validation_context, + self.errors, + ), + Err(error) => Self::transition( + self.config, + self.validation_context, + merge_errors(self.errors, FieldError::WsPort(error).into()), + ), + } + } - /// Set the RPC port that will be exposed. Uniqueness across config will be checked. - pub fn with_rpc_port(self, rpc_port: Port) -> Self { - match ensure_port_unique(rpc_port, self.validation_context.clone()) { - Ok(_) => Self::transition( - NodeConfig { - rpc_port: Some(rpc_port), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(error) => Self::transition( - self.config, - self.validation_context, - merge_errors(self.errors, FieldError::RpcPort(error).into()), - ), - } - } + /// Set the RPC port that will be exposed. Uniqueness across config will be checked. + pub fn with_rpc_port(self, rpc_port: Port) -> Self { + match ensure_port_unique(rpc_port, self.validation_context.clone()) { + Ok(_) => Self::transition( + NodeConfig { rpc_port: Some(rpc_port), ..self.config }, + self.validation_context, + self.errors, + ), + Err(error) => Self::transition( + self.config, + self.validation_context, + merge_errors(self.errors, FieldError::RpcPort(error).into()), + ), + } + } - /// Set the Prometheus port that will be exposed for metrics. Uniqueness across config will be checked. - pub fn with_prometheus_port(self, prometheus_port: Port) -> Self { - match ensure_port_unique(prometheus_port, self.validation_context.clone()) { - Ok(_) => Self::transition( - NodeConfig { - prometheus_port: Some(prometheus_port), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(error) => Self::transition( - self.config, - self.validation_context, - merge_errors(self.errors, FieldError::PrometheusPort(error).into()), - ), - } - } + /// Set the Prometheus port that will be exposed for metrics. Uniqueness across config will be checked. + pub fn with_prometheus_port(self, prometheus_port: Port) -> Self { + match ensure_port_unique(prometheus_port, self.validation_context.clone()) { + Ok(_) => Self::transition( + NodeConfig { prometheus_port: Some(prometheus_port), ..self.config }, + self.validation_context, + self.errors, + ), + Err(error) => Self::transition( + self.config, + self.validation_context, + merge_errors(self.errors, FieldError::PrometheusPort(error).into()), + ), + } + } - /// Set the P2P port that will be exposed. Uniqueness across config will be checked. - pub fn with_p2p_port(self, p2p_port: Port) -> Self { - match ensure_port_unique(p2p_port, self.validation_context.clone()) { - Ok(_) => Self::transition( - NodeConfig { - p2p_port: Some(p2p_port), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(error) => Self::transition( - self.config, - self.validation_context, - merge_errors(self.errors, FieldError::P2pPort(error).into()), - ), - } - } + /// Set the P2P port that will be exposed. Uniqueness across config will be checked. + pub fn with_p2p_port(self, p2p_port: Port) -> Self { + match ensure_port_unique(p2p_port, self.validation_context.clone()) { + Ok(_) => Self::transition( + NodeConfig { p2p_port: Some(p2p_port), ..self.config }, + self.validation_context, + self.errors, + ), + Err(error) => Self::transition( + self.config, + self.validation_context, + merge_errors(self.errors, FieldError::P2pPort(error).into()), + ), + } + } - /// Set the P2P cert hash that will be used as part of the multiaddress - /// if and only if the multiaddress is set to use `webrtc`. - pub fn with_p2p_cert_hash(self, p2p_cert_hash: impl Into) -> Self { - Self::transition( - NodeConfig { - p2p_cert_hash: Some(p2p_cert_hash.into()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the P2P cert hash that will be used as part of the multiaddress + /// if and only if the multiaddress is set to use `webrtc`. + pub fn with_p2p_cert_hash(self, p2p_cert_hash: impl Into) -> Self { + Self::transition( + NodeConfig { p2p_cert_hash: Some(p2p_cert_hash.into()), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the database snapshot that will be used to launch the node. Override the default. - pub fn with_db_snapshot(self, location: impl Into) -> Self { - Self::transition( - NodeConfig { - db_snapshot: Some(location.into()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the database snapshot that will be used to launch the node. Override the default. + pub fn with_db_snapshot(self, location: impl Into) -> Self { + Self::transition( + NodeConfig { db_snapshot: Some(location.into()), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the node log path that will be used to launch the node. - pub fn with_log_path(self, log_path: impl Into) -> Self { - Self::transition( - NodeConfig { - node_log_path: Some(log_path.into()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the node log path that will be used to launch the node. + pub fn with_log_path(self, log_path: impl Into) -> Self { + Self::transition( + NodeConfig { node_log_path: Some(log_path.into()), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the keystore path override. - pub fn with_keystore_path(self, keystore_path: impl Into) -> Self { - Self::transition( - NodeConfig { - keystore_path: Some(keystore_path.into()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the keystore path override. + pub fn with_keystore_path(self, keystore_path: impl Into) -> Self { + Self::transition( + NodeConfig { keystore_path: Some(keystore_path.into()), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the keystore key types to generate. - /// - /// Each key type can be specified in short form (e.g., "audi") using predefined schemas - /// (defaults to `sr` if no predefined schema exists for the key type), - /// or in long form (e.g., "audi_sr") with an explicit schema (sr, ed, ec). - /// - /// # Examples - /// - /// ``` - /// use zombienet_configuration::shared::{node::NodeConfigBuilder, types::ChainDefaultContext}; - /// - /// let config = NodeConfigBuilder::new(ChainDefaultContext::default(), Default::default()) - /// .with_name("node") - /// .with_keystore_key_types(vec!["audi", "gran", "cust_sr"]) - /// .build() - /// .unwrap(); - /// - /// assert_eq!(config.keystore_key_types(), &["audi", "gran", "cust_sr"]); - /// ``` - pub fn with_keystore_key_types(self, key_types: Vec>) -> Self { - Self::transition( - NodeConfig { - keystore_key_types: key_types.into_iter().map(|k| k.into()).collect(), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the keystore key types to generate. + /// + /// Each key type can be specified in short form (e.g., "audi") using predefined schemas + /// (defaults to `sr` if no predefined schema exists for the key type), + /// or in long form (e.g., "audi_sr") with an explicit schema (sr, ed, ec). + /// + /// # Examples + /// + /// ``` + /// use zombienet_configuration::shared::{node::NodeConfigBuilder, types::ChainDefaultContext}; + /// + /// let config = NodeConfigBuilder::new(ChainDefaultContext::default(), Default::default()) + /// .with_name("node") + /// .with_keystore_key_types(vec!["audi", "gran", "cust_sr"]) + /// .build() + /// .unwrap(); + /// + /// assert_eq!(config.keystore_key_types(), &["audi", "gran", "cust_sr"]); + /// ``` + pub fn with_keystore_key_types(self, key_types: Vec>) -> Self { + Self::transition( + NodeConfig { + keystore_key_types: key_types.into_iter().map(|k| k.into()).collect(), + ..self.config + }, + self.validation_context, + self.errors, + ) + } - /// Seals the builder and returns a [`NodeConfig`] if there are no validation errors, else returns errors. - pub fn build(self) -> Result)> { - if !self.errors.is_empty() { - return Err((self.config.name.clone(), self.errors)); - } + /// Seals the builder and returns a [`NodeConfig`] if there are no validation errors, else returns errors. + pub fn build(self) -> Result)> { + if !self.errors.is_empty() { + return Err((self.config.name.clone(), self.errors)); + } - Ok(self.config) - } + Ok(self.config) + } } /// A group node configuration builder, used to build a [`GroupNodeConfig`] declaratively with fields validation. pub struct GroupNodeConfigBuilder { - base_config: NodeConfig, - count: usize, - validation_context: Rc>, - errors: Vec, - _state: PhantomData, + base_config: NodeConfig, + count: usize, + validation_context: Rc>, + errors: Vec, + _state: PhantomData, } impl GroupNodeConfigBuilder { - pub fn new( - chain_context: ChainDefaultContext, - validation_context: Rc>, - ) -> Self { - let (errors, base_config) = match NodeConfigBuilder::new( + pub fn new( + chain_context: ChainDefaultContext, + validation_context: Rc>, + ) -> Self { + let (errors, base_config) = match NodeConfigBuilder::new( chain_context.clone(), validation_context.clone(), ) .with_name(" ") // placeholder .build() - { - Ok(base_config) => (vec![], base_config), - Err((_name, errors)) => (errors, NodeConfig::default()), - }; + { + Ok(base_config) => (vec![], base_config), + Err((_name, errors)) => (errors, NodeConfig::default()), + }; - Self { - base_config, - count: 1, - validation_context, - errors, - _state: PhantomData, - } - } + Self { base_config, count: 1, validation_context, errors, _state: PhantomData } + } - /// Set the base node config using a closure. - pub fn with_base_node( - mut self, - f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, - ) -> GroupNodeConfigBuilder { - match f(NodeConfigBuilder::new( - ChainDefaultContext::default(), - self.validation_context.clone(), - )) - .build() - { - Ok(node) => { - self.base_config = node; - GroupNodeConfigBuilder { - base_config: self.base_config, - count: self.count, - validation_context: self.validation_context, - errors: self.errors, - _state: PhantomData, - } - }, - Err((_name, errors)) => { - self.errors.extend(errors); - GroupNodeConfigBuilder { - base_config: self.base_config, - count: self.count, - validation_context: self.validation_context, - errors: self.errors, - _state: PhantomData, - } - }, - } - } + /// Set the base node config using a closure. + pub fn with_base_node( + mut self, + f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, + ) -> GroupNodeConfigBuilder { + match f(NodeConfigBuilder::new( + ChainDefaultContext::default(), + self.validation_context.clone(), + )) + .build() + { + Ok(node) => { + self.base_config = node; + GroupNodeConfigBuilder { + base_config: self.base_config, + count: self.count, + validation_context: self.validation_context, + errors: self.errors, + _state: PhantomData, + } + }, + Err((_name, errors)) => { + self.errors.extend(errors); + GroupNodeConfigBuilder { + base_config: self.base_config, + count: self.count, + validation_context: self.validation_context, + errors: self.errors, + _state: PhantomData, + } + }, + } + } - /// Set the number of nodes in the group. - pub fn with_count(mut self, count: usize) -> Self { - self.count = count; - self - } + /// Set the number of nodes in the group. + pub fn with_count(mut self, count: usize) -> Self { + self.count = count; + self + } } impl GroupNodeConfigBuilder { - pub fn build(self) -> Result)> { - if self.count == 0 { - return Err(( - self.base_config.name().to_string(), - vec![anyhow::anyhow!("Count cannot be zero")], - )); - } + pub fn build(self) -> Result)> { + if self.count == 0 { + return Err(( + self.base_config.name().to_string(), + vec![anyhow::anyhow!("Count cannot be zero")], + )); + } - if !self.errors.is_empty() { - return Err((self.base_config.name().to_string(), self.errors)); - } + if !self.errors.is_empty() { + return Err((self.base_config.name().to_string(), self.errors)); + } - Ok(GroupNodeConfig { - base_config: self.base_config, - count: self.count, - }) - } + Ok(GroupNodeConfig { base_config: self.base_config, count: self.count }) + } } #[cfg(test)] mod tests { - use std::collections::HashSet; + use std::collections::HashSet; - use super::*; + use super::*; - #[test] - fn node_config_builder_should_succeeds_and_returns_a_node_config() { - let node_config = - NodeConfigBuilder::new(ChainDefaultContext::default(), Default::default()) - .with_name("node") - .with_command("mycommand") - .with_image("myrepo:myimage") - .with_args(vec![("--arg1", "value1").into(), "--option2".into()]) - .validator(true) - .invulnerable(true) - .bootnode(true) - .with_override_eth_key("0x0123456789abcdef0123456789abcdef01234567") - .with_initial_balance(100_000_042) - .with_env(vec![("VAR1", "VALUE1"), ("VAR2", "VALUE2")]) - .with_raw_bootnodes_addresses(vec![ - "/ip4/10.41.122.55/tcp/45421", - "/ip4/51.144.222.10/tcp/2333", - ]) - .with_resources(|resources| { - resources - .with_request_cpu("200M") - .with_request_memory("500M") - .with_limit_cpu("1G") - .with_limit_memory("2G") - }) - .with_ws_port(5000) - .with_rpc_port(6000) - .with_prometheus_port(7000) - .with_p2p_port(8000) - .with_p2p_cert_hash( - "ec8d6467180a4b72a52b24c53aa1e53b76c05602fa96f5d0961bf720edda267f", - ) - .with_db_snapshot("/tmp/mysnapshot") - .with_keystore_path("/tmp/mykeystore") - .build() - .unwrap(); + #[test] + fn node_config_builder_should_succeeds_and_returns_a_node_config() { + let node_config = + NodeConfigBuilder::new(ChainDefaultContext::default(), Default::default()) + .with_name("node") + .with_command("mycommand") + .with_image("myrepo:myimage") + .with_args(vec![("--arg1", "value1").into(), "--option2".into()]) + .validator(true) + .invulnerable(true) + .bootnode(true) + .with_override_eth_key("0x0123456789abcdef0123456789abcdef01234567") + .with_initial_balance(100_000_042) + .with_env(vec![("VAR1", "VALUE1"), ("VAR2", "VALUE2")]) + .with_raw_bootnodes_addresses(vec![ + "/ip4/10.41.122.55/tcp/45421", + "/ip4/51.144.222.10/tcp/2333", + ]) + .with_resources(|resources| { + resources + .with_request_cpu("200M") + .with_request_memory("500M") + .with_limit_cpu("1G") + .with_limit_memory("2G") + }) + .with_ws_port(5000) + .with_rpc_port(6000) + .with_prometheus_port(7000) + .with_p2p_port(8000) + .with_p2p_cert_hash( + "ec8d6467180a4b72a52b24c53aa1e53b76c05602fa96f5d0961bf720edda267f", + ) + .with_db_snapshot("/tmp/mysnapshot") + .with_keystore_path("/tmp/mykeystore") + .build() + .unwrap(); - assert_eq!(node_config.name(), "node"); - assert_eq!(node_config.command().unwrap().as_str(), "mycommand"); - assert_eq!(node_config.image().unwrap().as_str(), "myrepo:myimage"); - let args: Vec = vec![("--arg1", "value1").into(), "--option2".into()]; - assert_eq!(node_config.args(), args.iter().collect::>()); - assert!(node_config.is_validator()); - assert!(node_config.is_invulnerable()); - assert!(node_config.is_bootnode()); - assert_eq!( - node_config.override_eth_key(), - Some("0x0123456789abcdef0123456789abcdef01234567") - ); - assert_eq!(node_config.initial_balance(), 100_000_042); - let env: Vec = vec![("VAR1", "VALUE1").into(), ("VAR2", "VALUE2").into()]; - assert_eq!(node_config.env(), env.iter().collect::>()); - let bootnodes_addresses: Vec = vec![ - "/ip4/10.41.122.55/tcp/45421".try_into().unwrap(), - "/ip4/51.144.222.10/tcp/2333".try_into().unwrap(), - ]; - assert_eq!( - node_config.bootnodes_addresses(), - bootnodes_addresses.iter().collect::>() - ); - let resources = node_config.resources().unwrap(); - assert_eq!(resources.request_cpu().unwrap().as_str(), "200M"); - assert_eq!(resources.request_memory().unwrap().as_str(), "500M"); - assert_eq!(resources.limit_cpu().unwrap().as_str(), "1G"); - assert_eq!(resources.limit_memory().unwrap().as_str(), "2G"); - assert_eq!(node_config.ws_port().unwrap(), 5000); - assert_eq!(node_config.rpc_port().unwrap(), 6000); - assert_eq!(node_config.prometheus_port().unwrap(), 7000); - assert_eq!(node_config.p2p_port().unwrap(), 8000); - assert_eq!( - node_config.p2p_cert_hash().unwrap(), - "ec8d6467180a4b72a52b24c53aa1e53b76c05602fa96f5d0961bf720edda267f" - ); - assert!(matches!( - node_config.db_snapshot().unwrap(), AssetLocation::FilePath(value) if value.to_str().unwrap() == "/tmp/mysnapshot" - )); - assert!(matches!( - node_config.keystore_path().unwrap().to_str().unwrap(), - "/tmp/mykeystore" - )); - } + assert_eq!(node_config.name(), "node"); + assert_eq!(node_config.command().unwrap().as_str(), "mycommand"); + assert_eq!(node_config.image().unwrap().as_str(), "myrepo:myimage"); + let args: Vec = vec![("--arg1", "value1").into(), "--option2".into()]; + assert_eq!(node_config.args(), args.iter().collect::>()); + assert!(node_config.is_validator()); + assert!(node_config.is_invulnerable()); + assert!(node_config.is_bootnode()); + assert_eq!( + node_config.override_eth_key(), + Some("0x0123456789abcdef0123456789abcdef01234567") + ); + assert_eq!(node_config.initial_balance(), 100_000_042); + let env: Vec = vec![("VAR1", "VALUE1").into(), ("VAR2", "VALUE2").into()]; + assert_eq!(node_config.env(), env.iter().collect::>()); + let bootnodes_addresses: Vec = vec![ + "/ip4/10.41.122.55/tcp/45421".try_into().unwrap(), + "/ip4/51.144.222.10/tcp/2333".try_into().unwrap(), + ]; + assert_eq!( + node_config.bootnodes_addresses(), + bootnodes_addresses.iter().collect::>() + ); + let resources = node_config.resources().unwrap(); + assert_eq!(resources.request_cpu().unwrap().as_str(), "200M"); + assert_eq!(resources.request_memory().unwrap().as_str(), "500M"); + assert_eq!(resources.limit_cpu().unwrap().as_str(), "1G"); + assert_eq!(resources.limit_memory().unwrap().as_str(), "2G"); + assert_eq!(node_config.ws_port().unwrap(), 5000); + assert_eq!(node_config.rpc_port().unwrap(), 6000); + assert_eq!(node_config.prometheus_port().unwrap(), 7000); + assert_eq!(node_config.p2p_port().unwrap(), 8000); + assert_eq!( + node_config.p2p_cert_hash().unwrap(), + "ec8d6467180a4b72a52b24c53aa1e53b76c05602fa96f5d0961bf720edda267f" + ); + assert!(matches!( + node_config.db_snapshot().unwrap(), AssetLocation::FilePath(value) if value.to_str().unwrap() == "/tmp/mysnapshot" + )); + assert!(matches!( + node_config.keystore_path().unwrap().to_str().unwrap(), + "/tmp/mykeystore" + )); + } - #[test] - fn node_config_builder_should_use_unique_name_if_node_name_already_used() { - let mut used_nodes_names = HashSet::new(); - used_nodes_names.insert("mynode".into()); - let validation_context = Rc::new(RefCell::new(ValidationContext { - used_nodes_names, - ..Default::default() - })); - let node_config = - NodeConfigBuilder::new(ChainDefaultContext::default(), validation_context) - .with_name("mynode") - .build() - .unwrap(); + #[test] + fn node_config_builder_should_use_unique_name_if_node_name_already_used() { + let mut used_nodes_names = HashSet::new(); + used_nodes_names.insert("mynode".into()); + let validation_context = + Rc::new(RefCell::new(ValidationContext { used_nodes_names, ..Default::default() })); + let node_config = + NodeConfigBuilder::new(ChainDefaultContext::default(), validation_context) + .with_name("mynode") + .build() + .unwrap(); - assert_eq!(node_config.name, "mynode-1"); - } + assert_eq!(node_config.name, "mynode-1"); + } - #[test] - fn node_config_builder_should_fails_and_returns_an_error_and_node_name_if_command_is_invalid() { - let (node_name, errors) = - NodeConfigBuilder::new(ChainDefaultContext::default(), Default::default()) - .with_name("node") - .with_command("invalid command") - .build() - .unwrap_err(); + #[test] + fn node_config_builder_should_fails_and_returns_an_error_and_node_name_if_command_is_invalid() { + let (node_name, errors) = + NodeConfigBuilder::new(ChainDefaultContext::default(), Default::default()) + .with_name("node") + .with_command("invalid command") + .build() + .unwrap_err(); - assert_eq!(node_name, "node"); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - "command: 'invalid command' shouldn't contains whitespace" - ); - } + assert_eq!(node_name, "node"); + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + "command: 'invalid command' shouldn't contains whitespace" + ); + } - #[test] - fn node_config_builder_should_fails_and_returns_an_error_and_node_name_if_image_is_invalid() { - let (node_name, errors) = - NodeConfigBuilder::new(ChainDefaultContext::default(), Default::default()) - .with_name("node") - .with_image("myinvalid.image") - .build() - .unwrap_err(); + #[test] + fn node_config_builder_should_fails_and_returns_an_error_and_node_name_if_image_is_invalid() { + let (node_name, errors) = + NodeConfigBuilder::new(ChainDefaultContext::default(), Default::default()) + .with_name("node") + .with_image("myinvalid.image") + .build() + .unwrap_err(); - assert_eq!(node_name, "node"); - assert_eq!(errors.len(), 1); - assert_eq!( + assert_eq!(node_name, "node"); + assert_eq!(errors.len(), 1); + assert_eq!( errors.first().unwrap().to_string(), "image: 'myinvalid.image' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'" ); - } + } - #[test] - fn node_config_builder_should_fails_and_returns_an_error_and_node_name_if_one_bootnode_address_is_invalid( - ) { - let (node_name, errors) = - NodeConfigBuilder::new(ChainDefaultContext::default(), Default::default()) - .with_name("node") - .with_raw_bootnodes_addresses(vec!["/ip4//tcp/45421"]) - .build() - .unwrap_err(); + #[test] + fn node_config_builder_should_fails_and_returns_an_error_and_node_name_if_one_bootnode_address_is_invalid( + ) { + let (node_name, errors) = + NodeConfigBuilder::new(ChainDefaultContext::default(), Default::default()) + .with_name("node") + .with_raw_bootnodes_addresses(vec!["/ip4//tcp/45421"]) + .build() + .unwrap_err(); - assert_eq!(node_name, "node"); - assert_eq!(errors.len(), 1); - assert_eq!( + assert_eq!(node_name, "node"); + assert_eq!(errors.len(), 1); + assert_eq!( errors.first().unwrap().to_string(), "bootnodes_addresses[0]: '/ip4//tcp/45421' failed to parse: invalid IPv4 address syntax" ); - } + } - #[test] - fn node_config_builder_should_fails_and_returns_mulitle_errors_and_node_name_if_multiple_bootnode_address_are_invalid( - ) { - let (node_name, errors) = - NodeConfigBuilder::new(ChainDefaultContext::default(), Default::default()) - .with_name("node") - .with_raw_bootnodes_addresses(vec!["/ip4//tcp/45421", "//10.42.153.10/tcp/43111"]) - .build() - .unwrap_err(); + #[test] + fn node_config_builder_should_fails_and_returns_mulitle_errors_and_node_name_if_multiple_bootnode_address_are_invalid( + ) { + let (node_name, errors) = + NodeConfigBuilder::new(ChainDefaultContext::default(), Default::default()) + .with_name("node") + .with_raw_bootnodes_addresses(vec!["/ip4//tcp/45421", "//10.42.153.10/tcp/43111"]) + .build() + .unwrap_err(); - assert_eq!(node_name, "node"); - assert_eq!(errors.len(), 2); - assert_eq!( + assert_eq!(node_name, "node"); + assert_eq!(errors.len(), 2); + assert_eq!( errors.first().unwrap().to_string(), "bootnodes_addresses[0]: '/ip4//tcp/45421' failed to parse: invalid IPv4 address syntax" ); - assert_eq!( - errors.get(1).unwrap().to_string(), - "bootnodes_addresses[1]: '//10.42.153.10/tcp/43111' unknown protocol string: " - ); - } + assert_eq!( + errors.get(1).unwrap().to_string(), + "bootnodes_addresses[1]: '//10.42.153.10/tcp/43111' unknown protocol string: " + ); + } - #[test] - fn node_config_builder_should_fails_and_returns_an_error_and_node_name_if_resources_has_an_error( - ) { - let (node_name, errors) = - NodeConfigBuilder::new(ChainDefaultContext::default(), Default::default()) - .with_name("node") - .with_resources(|resources| resources.with_limit_cpu("invalid")) - .build() - .unwrap_err(); + #[test] + fn node_config_builder_should_fails_and_returns_an_error_and_node_name_if_resources_has_an_error( + ) { + let (node_name, errors) = + NodeConfigBuilder::new(ChainDefaultContext::default(), Default::default()) + .with_name("node") + .with_resources(|resources| resources.with_limit_cpu("invalid")) + .build() + .unwrap_err(); - assert_eq!(node_name, "node"); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - r"resources.limit_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" - ); - } + assert_eq!(node_name, "node"); + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + r"resources.limit_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" + ); + } - #[test] - fn node_config_builder_should_fails_and_returns_multiple_errors_and_node_name_if_resources_has_multiple_errors( - ) { - let (node_name, errors) = - NodeConfigBuilder::new(ChainDefaultContext::default(), Default::default()) - .with_name("node") - .with_resources(|resources| { - resources - .with_limit_cpu("invalid") - .with_request_memory("invalid") - }) - .build() - .unwrap_err(); + #[test] + fn node_config_builder_should_fails_and_returns_multiple_errors_and_node_name_if_resources_has_multiple_errors( + ) { + let (node_name, errors) = + NodeConfigBuilder::new(ChainDefaultContext::default(), Default::default()) + .with_name("node") + .with_resources(|resources| { + resources.with_limit_cpu("invalid").with_request_memory("invalid") + }) + .build() + .unwrap_err(); - assert_eq!(node_name, "node"); - assert_eq!(errors.len(), 2); - assert_eq!( - errors.first().unwrap().to_string(), - r"resources.limit_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" - ); - assert_eq!( - errors.get(1).unwrap().to_string(), - r"resources.request_memory: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" - ); - } + assert_eq!(node_name, "node"); + assert_eq!(errors.len(), 2); + assert_eq!( + errors.first().unwrap().to_string(), + r"resources.limit_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" + ); + assert_eq!( + errors.get(1).unwrap().to_string(), + r"resources.request_memory: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" + ); + } - #[test] - fn node_config_builder_should_fails_and_returns_multiple_errors_and_node_name_if_multiple_fields_have_errors( - ) { - let (node_name, errors) = - NodeConfigBuilder::new(ChainDefaultContext::default(), Default::default()) - .with_name("node") - .with_command("invalid command") - .with_image("myinvalid.image") - .with_resources(|resources| { - resources - .with_limit_cpu("invalid") - .with_request_memory("invalid") - }) - .build() - .unwrap_err(); + #[test] + fn node_config_builder_should_fails_and_returns_multiple_errors_and_node_name_if_multiple_fields_have_errors( + ) { + let (node_name, errors) = + NodeConfigBuilder::new(ChainDefaultContext::default(), Default::default()) + .with_name("node") + .with_command("invalid command") + .with_image("myinvalid.image") + .with_resources(|resources| { + resources.with_limit_cpu("invalid").with_request_memory("invalid") + }) + .build() + .unwrap_err(); - assert_eq!(node_name, "node"); - assert_eq!(errors.len(), 4); - assert_eq!( - errors.first().unwrap().to_string(), - "command: 'invalid command' shouldn't contains whitespace" - ); - assert_eq!( + assert_eq!(node_name, "node"); + assert_eq!(errors.len(), 4); + assert_eq!( + errors.first().unwrap().to_string(), + "command: 'invalid command' shouldn't contains whitespace" + ); + assert_eq!( errors.get(1).unwrap().to_string(), "image: 'myinvalid.image' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'" ); - assert_eq!( - errors.get(2).unwrap().to_string(), - r"resources.limit_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" - ); - assert_eq!( - errors.get(3).unwrap().to_string(), - r"resources.request_memory: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" - ); - } + assert_eq!( + errors.get(2).unwrap().to_string(), + r"resources.limit_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" + ); + assert_eq!( + errors.get(3).unwrap().to_string(), + r"resources.request_memory: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" + ); + } - #[test] - fn node_config_builder_should_fails_and_returns_an_error_and_node_name_if_ws_port_is_already_used( - ) { - let validation_context = Rc::new(RefCell::new(ValidationContext { - used_ports: vec![30333], - ..Default::default() - })); - let (node_name, errors) = - NodeConfigBuilder::new(ChainDefaultContext::default(), validation_context) - .with_name("node") - .with_ws_port(30333) - .build() - .unwrap_err(); + #[test] + fn node_config_builder_should_fails_and_returns_an_error_and_node_name_if_ws_port_is_already_used( + ) { + let validation_context = Rc::new(RefCell::new(ValidationContext { + used_ports: vec![30333], + ..Default::default() + })); + let (node_name, errors) = + NodeConfigBuilder::new(ChainDefaultContext::default(), validation_context) + .with_name("node") + .with_ws_port(30333) + .build() + .unwrap_err(); - assert_eq!(node_name, "node"); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - "ws_port: '30333' is already used across config" - ); - } + assert_eq!(node_name, "node"); + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + "ws_port: '30333' is already used across config" + ); + } - #[test] - fn node_config_builder_should_fails_and_returns_an_error_and_node_name_if_rpc_port_is_already_used( - ) { - let validation_context = Rc::new(RefCell::new(ValidationContext { - used_ports: vec![4444], - ..Default::default() - })); - let (node_name, errors) = - NodeConfigBuilder::new(ChainDefaultContext::default(), validation_context) - .with_name("node") - .with_rpc_port(4444) - .build() - .unwrap_err(); + #[test] + fn node_config_builder_should_fails_and_returns_an_error_and_node_name_if_rpc_port_is_already_used( + ) { + let validation_context = Rc::new(RefCell::new(ValidationContext { + used_ports: vec![4444], + ..Default::default() + })); + let (node_name, errors) = + NodeConfigBuilder::new(ChainDefaultContext::default(), validation_context) + .with_name("node") + .with_rpc_port(4444) + .build() + .unwrap_err(); - assert_eq!(node_name, "node"); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - "rpc_port: '4444' is already used across config" - ); - } + assert_eq!(node_name, "node"); + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + "rpc_port: '4444' is already used across config" + ); + } - #[test] - fn node_config_builder_should_fails_and_returns_an_error_and_node_name_if_prometheus_port_is_already_used( - ) { - let validation_context = Rc::new(RefCell::new(ValidationContext { - used_ports: vec![9089], - ..Default::default() - })); - let (node_name, errors) = - NodeConfigBuilder::new(ChainDefaultContext::default(), validation_context) - .with_name("node") - .with_prometheus_port(9089) - .build() - .unwrap_err(); + #[test] + fn node_config_builder_should_fails_and_returns_an_error_and_node_name_if_prometheus_port_is_already_used( + ) { + let validation_context = Rc::new(RefCell::new(ValidationContext { + used_ports: vec![9089], + ..Default::default() + })); + let (node_name, errors) = + NodeConfigBuilder::new(ChainDefaultContext::default(), validation_context) + .with_name("node") + .with_prometheus_port(9089) + .build() + .unwrap_err(); - assert_eq!(node_name, "node"); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - "prometheus_port: '9089' is already used across config" - ); - } + assert_eq!(node_name, "node"); + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + "prometheus_port: '9089' is already used across config" + ); + } - #[test] - fn node_config_builder_should_fails_and_returns_and_error_and_node_name_if_p2p_port_is_already_used( - ) { - let validation_context = Rc::new(RefCell::new(ValidationContext { - used_ports: vec![45093], - ..Default::default() - })); - let (node_name, errors) = - NodeConfigBuilder::new(ChainDefaultContext::default(), validation_context) - .with_name("node") - .with_p2p_port(45093) - .build() - .unwrap_err(); + #[test] + fn node_config_builder_should_fails_and_returns_and_error_and_node_name_if_p2p_port_is_already_used( + ) { + let validation_context = Rc::new(RefCell::new(ValidationContext { + used_ports: vec![45093], + ..Default::default() + })); + let (node_name, errors) = + NodeConfigBuilder::new(ChainDefaultContext::default(), validation_context) + .with_name("node") + .with_p2p_port(45093) + .build() + .unwrap_err(); - assert_eq!(node_name, "node"); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - "p2p_port: '45093' is already used across config" - ); - } + assert_eq!(node_name, "node"); + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + "p2p_port: '45093' is already used across config" + ); + } - #[test] - fn node_config_builder_should_fails_if_node_name_is_empty() { - let validation_context = Rc::new(RefCell::new(ValidationContext { - ..Default::default() - })); + #[test] + fn node_config_builder_should_fails_if_node_name_is_empty() { + let validation_context = Rc::new(RefCell::new(ValidationContext { ..Default::default() })); - let (_, errors) = - NodeConfigBuilder::new(ChainDefaultContext::default(), validation_context) - .with_name("") - .build() - .unwrap_err(); + let (_, errors) = + NodeConfigBuilder::new(ChainDefaultContext::default(), validation_context) + .with_name("") + .build() + .unwrap_err(); - assert_eq!(errors.len(), 1); - assert_eq!(errors.first().unwrap().to_string(), "name: can't be empty"); - } + assert_eq!(errors.len(), 1); + assert_eq!(errors.first().unwrap().to_string(), "name: can't be empty"); + } - #[test] - fn group_default_base_node() { - let validation_context = Rc::new(RefCell::new(ValidationContext::default())); + #[test] + fn group_default_base_node() { + let validation_context = Rc::new(RefCell::new(ValidationContext::default())); - let group_config = - GroupNodeConfigBuilder::new(ChainDefaultContext::default(), validation_context.clone()) - .with_base_node(|node| node.with_name("validator")) - .build() - .unwrap(); + let group_config = + GroupNodeConfigBuilder::new(ChainDefaultContext::default(), validation_context.clone()) + .with_base_node(|node| node.with_name("validator")) + .build() + .unwrap(); - // Check group config - assert_eq!(group_config.count, 1); - assert_eq!(group_config.base_config.name(), "validator"); - } + // Check group config + assert_eq!(group_config.count, 1); + assert_eq!(group_config.base_config.name(), "validator"); + } - #[test] - fn group_custom_base_node() { - let validation_context = Rc::new(RefCell::new(ValidationContext::default())); - let node_config = - NodeConfigBuilder::new(ChainDefaultContext::default(), validation_context.clone()) - .with_name("node") - .with_command("some_command") - .with_image("repo:image") - .validator(true) - .invulnerable(true) - .bootnode(true); + #[test] + fn group_custom_base_node() { + let validation_context = Rc::new(RefCell::new(ValidationContext::default())); + let node_config = + NodeConfigBuilder::new(ChainDefaultContext::default(), validation_context.clone()) + .with_name("node") + .with_command("some_command") + .with_image("repo:image") + .validator(true) + .invulnerable(true) + .bootnode(true); - let group_config = - GroupNodeConfigBuilder::new(ChainDefaultContext::default(), validation_context.clone()) - .with_count(5) - .with_base_node(|_node| node_config) - .build() - .unwrap(); + let group_config = + GroupNodeConfigBuilder::new(ChainDefaultContext::default(), validation_context.clone()) + .with_count(5) + .with_base_node(|_node| node_config) + .build() + .unwrap(); - // Check group config - assert_eq!(group_config.count, 5); + // Check group config + assert_eq!(group_config.count, 5); - assert_eq!(group_config.base_config.name(), "node"); - assert_eq!( - group_config.base_config.command().unwrap().as_str(), - "some_command" - ); - assert_eq!( - group_config.base_config.image().unwrap().as_str(), - "repo:image" - ); - assert!(group_config.base_config.is_validator()); - assert!(group_config.base_config.is_invulnerable()); - assert!(group_config.base_config.is_bootnode()); - } + assert_eq!(group_config.base_config.name(), "node"); + assert_eq!(group_config.base_config.command().unwrap().as_str(), "some_command"); + assert_eq!(group_config.base_config.image().unwrap().as_str(), "repo:image"); + assert!(group_config.base_config.is_validator()); + assert!(group_config.base_config.is_invulnerable()); + assert!(group_config.base_config.is_bootnode()); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/resources.rs b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/resources.rs index 11e9723f..e6e5225f 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/resources.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/resources.rs @@ -3,15 +3,15 @@ use std::error::Error; use lazy_static::lazy_static; use regex::Regex; use serde::{ - de::{self}, - ser::SerializeStruct, - Deserialize, Serialize, + de::{self}, + ser::SerializeStruct, + Deserialize, Serialize, }; use support::constants::{SHOULD_COMPILE, THIS_IS_A_BUG}; use super::{ - errors::{ConversionError, FieldError}, - helpers::merge_errors, + errors::{ConversionError, FieldError}, + helpers::merge_errors, }; /// A resource quantity used to define limits (k8s/podman only). @@ -37,453 +37,434 @@ use super::{ pub struct ResourceQuantity(String); impl ResourceQuantity { - pub fn as_str(&self) -> &str { - &self.0 - } + pub fn as_str(&self) -> &str { + &self.0 + } } impl TryFrom<&str> for ResourceQuantity { - type Error = ConversionError; + type Error = ConversionError; - fn try_from(value: &str) -> Result { - lazy_static! { - static ref RE: Regex = Regex::new(r"^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$") - .expect(&format!("{SHOULD_COMPILE}, {THIS_IS_A_BUG}")); - } + fn try_from(value: &str) -> Result { + lazy_static! { + static ref RE: Regex = Regex::new(r"^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$") + .expect(&format!("{SHOULD_COMPILE}, {THIS_IS_A_BUG}")); + } - if !RE.is_match(value) { - return Err(ConversionError::DoesntMatchRegex { - value: value.to_string(), - regex: r"^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$".to_string(), - }); - } + if !RE.is_match(value) { + return Err(ConversionError::DoesntMatchRegex { + value: value.to_string(), + regex: r"^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$".to_string(), + }); + } - Ok(Self(value.to_string())) - } + Ok(Self(value.to_string())) + } } impl From for ResourceQuantity { - fn from(value: u64) -> Self { - Self(value.to_string()) - } + fn from(value: u64) -> Self { + Self(value.to_string()) + } } /// Resources limits used in the context of podman/k8s. #[derive(Debug, Default, Clone, PartialEq)] pub struct Resources { - request_memory: Option, - request_cpu: Option, - limit_memory: Option, - limit_cpu: Option, + request_memory: Option, + request_cpu: Option, + limit_memory: Option, + limit_cpu: Option, } #[derive(Serialize, Deserialize)] struct ResourcesField { - memory: Option, - cpu: Option, + memory: Option, + cpu: Option, } impl Serialize for Resources { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut state = serializer.serialize_struct("Resources", 2)?; + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("Resources", 2)?; - if self.request_memory.is_some() || self.request_memory.is_some() { - state.serialize_field( - "requests", - &ResourcesField { - memory: self.request_memory.clone(), - cpu: self.request_cpu.clone(), - }, - )?; - } else { - state.skip_field("requests")?; - } + if self.request_memory.is_some() || self.request_memory.is_some() { + state.serialize_field( + "requests", + &ResourcesField { + memory: self.request_memory.clone(), + cpu: self.request_cpu.clone(), + }, + )?; + } else { + state.skip_field("requests")?; + } - if self.limit_memory.is_some() || self.limit_memory.is_some() { - state.serialize_field( - "limits", - &ResourcesField { - memory: self.limit_memory.clone(), - cpu: self.limit_cpu.clone(), - }, - )?; - } else { - state.skip_field("limits")?; - } + if self.limit_memory.is_some() || self.limit_memory.is_some() { + state.serialize_field( + "limits", + &ResourcesField { memory: self.limit_memory.clone(), cpu: self.limit_cpu.clone() }, + )?; + } else { + state.skip_field("limits")?; + } - state.end() - } + state.end() + } } struct ResourcesVisitor; impl<'de> de::Visitor<'de> for ResourcesVisitor { - type Value = Resources; + type Value = Resources; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a resources object") - } + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a resources object") + } - fn visit_map(self, mut map: A) -> Result - where - A: de::MapAccess<'de>, - { - let mut resources: Resources = Resources::default(); + fn visit_map(self, mut map: A) -> Result + where + A: de::MapAccess<'de>, + { + let mut resources: Resources = Resources::default(); - while let Some((key, value)) = map.next_entry::()? { - match key.as_str() { - "requests" => { - resources.request_memory = value.memory; - resources.request_cpu = value.cpu; - }, - "limits" => { - resources.limit_memory = value.memory; - resources.limit_cpu = value.cpu; - }, - _ => { - return Err(de::Error::unknown_field( - &key, - &["requests", "limits", "cpu", "memory"], - )) - }, - } - } - Ok(resources) - } + while let Some((key, value)) = map.next_entry::()? { + match key.as_str() { + "requests" => { + resources.request_memory = value.memory; + resources.request_cpu = value.cpu; + }, + "limits" => { + resources.limit_memory = value.memory; + resources.limit_cpu = value.cpu; + }, + _ => { + return Err(de::Error::unknown_field( + &key, + &["requests", "limits", "cpu", "memory"], + )) + }, + } + } + Ok(resources) + } } impl<'de> Deserialize<'de> for Resources { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - deserializer.deserialize_any(ResourcesVisitor) - } + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_any(ResourcesVisitor) + } } impl Resources { - /// Memory limit applied to requests. - pub fn request_memory(&self) -> Option<&ResourceQuantity> { - self.request_memory.as_ref() - } + /// Memory limit applied to requests. + pub fn request_memory(&self) -> Option<&ResourceQuantity> { + self.request_memory.as_ref() + } - /// CPU limit applied to requests. - pub fn request_cpu(&self) -> Option<&ResourceQuantity> { - self.request_cpu.as_ref() - } + /// CPU limit applied to requests. + pub fn request_cpu(&self) -> Option<&ResourceQuantity> { + self.request_cpu.as_ref() + } - /// Overall memory limit applied. - pub fn limit_memory(&self) -> Option<&ResourceQuantity> { - self.limit_memory.as_ref() - } + /// Overall memory limit applied. + pub fn limit_memory(&self) -> Option<&ResourceQuantity> { + self.limit_memory.as_ref() + } - /// Overall CPU limit applied. - pub fn limit_cpu(&self) -> Option<&ResourceQuantity> { - self.limit_cpu.as_ref() - } + /// Overall CPU limit applied. + pub fn limit_cpu(&self) -> Option<&ResourceQuantity> { + self.limit_cpu.as_ref() + } } /// A resources builder, used to build a [`Resources`] declaratively with fields validation. #[derive(Debug, Default)] pub struct ResourcesBuilder { - config: Resources, - errors: Vec, + config: Resources, + errors: Vec, } impl ResourcesBuilder { - pub fn new() -> ResourcesBuilder { - Self::default() - } + pub fn new() -> ResourcesBuilder { + Self::default() + } - fn transition(config: Resources, errors: Vec) -> Self { - Self { config, errors } - } + fn transition(config: Resources, errors: Vec) -> Self { + Self { config, errors } + } - /// Set the requested memory for a pod. This is the minimum memory allocated for a pod. - pub fn with_request_memory(self, quantity: T) -> Self - where - T: TryInto, - T::Error: Error + Send + Sync + 'static, - { - match quantity.try_into() { - Ok(quantity) => Self::transition( - Resources { - request_memory: Some(quantity), - ..self.config - }, - self.errors, - ), - Err(error) => Self::transition( - self.config, - merge_errors(self.errors, FieldError::RequestMemory(error.into()).into()), - ), - } - } + /// Set the requested memory for a pod. This is the minimum memory allocated for a pod. + pub fn with_request_memory(self, quantity: T) -> Self + where + T: TryInto, + T::Error: Error + Send + Sync + 'static, + { + match quantity.try_into() { + Ok(quantity) => Self::transition( + Resources { request_memory: Some(quantity), ..self.config }, + self.errors, + ), + Err(error) => Self::transition( + self.config, + merge_errors(self.errors, FieldError::RequestMemory(error.into()).into()), + ), + } + } - /// Set the requested CPU limit for a pod. This is the minimum CPU allocated for a pod. - pub fn with_request_cpu(self, quantity: T) -> Self - where - T: TryInto, - T::Error: Error + Send + Sync + 'static, - { - match quantity.try_into() { - Ok(quantity) => Self::transition( - Resources { - request_cpu: Some(quantity), - ..self.config - }, - self.errors, - ), - Err(error) => Self::transition( - self.config, - merge_errors(self.errors, FieldError::RequestCpu(error.into()).into()), - ), - } - } + /// Set the requested CPU limit for a pod. This is the minimum CPU allocated for a pod. + pub fn with_request_cpu(self, quantity: T) -> Self + where + T: TryInto, + T::Error: Error + Send + Sync + 'static, + { + match quantity.try_into() { + Ok(quantity) => Self::transition( + Resources { request_cpu: Some(quantity), ..self.config }, + self.errors, + ), + Err(error) => Self::transition( + self.config, + merge_errors(self.errors, FieldError::RequestCpu(error.into()).into()), + ), + } + } - /// Set the overall memory limit for a pod. This is the maximum memory threshold for a pod. - pub fn with_limit_memory(self, quantity: T) -> Self - where - T: TryInto, - T::Error: Error + Send + Sync + 'static, - { - match quantity.try_into() { - Ok(quantity) => Self::transition( - Resources { - limit_memory: Some(quantity), - ..self.config - }, - self.errors, - ), - Err(error) => Self::transition( - self.config, - merge_errors(self.errors, FieldError::LimitMemory(error.into()).into()), - ), - } - } + /// Set the overall memory limit for a pod. This is the maximum memory threshold for a pod. + pub fn with_limit_memory(self, quantity: T) -> Self + where + T: TryInto, + T::Error: Error + Send + Sync + 'static, + { + match quantity.try_into() { + Ok(quantity) => Self::transition( + Resources { limit_memory: Some(quantity), ..self.config }, + self.errors, + ), + Err(error) => Self::transition( + self.config, + merge_errors(self.errors, FieldError::LimitMemory(error.into()).into()), + ), + } + } - /// Set the overall CPU limit for a pod. This is the maximum CPU threshold for a pod. - pub fn with_limit_cpu(self, quantity: T) -> Self - where - T: TryInto, - T::Error: Error + Send + Sync + 'static, - { - match quantity.try_into() { - Ok(quantity) => Self::transition( - Resources { - limit_cpu: Some(quantity), - ..self.config - }, - self.errors, - ), - Err(error) => Self::transition( - self.config, - merge_errors(self.errors, FieldError::LimitCpu(error.into()).into()), - ), - } - } + /// Set the overall CPU limit for a pod. This is the maximum CPU threshold for a pod. + pub fn with_limit_cpu(self, quantity: T) -> Self + where + T: TryInto, + T::Error: Error + Send + Sync + 'static, + { + match quantity.try_into() { + Ok(quantity) => Self::transition( + Resources { limit_cpu: Some(quantity), ..self.config }, + self.errors, + ), + Err(error) => Self::transition( + self.config, + merge_errors(self.errors, FieldError::LimitCpu(error.into()).into()), + ), + } + } - /// Seals the builder and returns a [`Resources`] if there are no validation errors, else returns errors. - pub fn build(self) -> Result> { - if !self.errors.is_empty() { - return Err(self.errors); - } + /// Seals the builder and returns a [`Resources`] if there are no validation errors, else returns errors. + pub fn build(self) -> Result> { + if !self.errors.is_empty() { + return Err(self.errors); + } - Ok(self.config) - } + Ok(self.config) + } } #[cfg(test)] #[allow(non_snake_case)] mod tests { - use super::*; - use crate::NetworkConfig; + use super::*; + use crate::NetworkConfig; - macro_rules! impl_resources_quantity_unit_test { - ($val:literal) => {{ - let resources = ResourcesBuilder::new() - .with_request_memory($val) - .build() - .unwrap(); + macro_rules! impl_resources_quantity_unit_test { + ($val:literal) => {{ + let resources = ResourcesBuilder::new().with_request_memory($val).build().unwrap(); - assert_eq!(resources.request_memory().unwrap().as_str(), $val); - assert_eq!(resources.request_cpu(), None); - assert_eq!(resources.limit_cpu(), None); - assert_eq!(resources.limit_memory(), None); - }}; - } + assert_eq!(resources.request_memory().unwrap().as_str(), $val); + assert_eq!(resources.request_cpu(), None); + assert_eq!(resources.limit_cpu(), None); + assert_eq!(resources.limit_memory(), None); + }}; + } - #[test] - fn converting_a_string_a_resource_quantity_without_unit_should_succeeds() { - impl_resources_quantity_unit_test!("1000"); - } + #[test] + fn converting_a_string_a_resource_quantity_without_unit_should_succeeds() { + impl_resources_quantity_unit_test!("1000"); + } - #[test] - fn converting_a_str_with_m_unit_into_a_resource_quantity_should_succeeds() { - impl_resources_quantity_unit_test!("100m"); - } + #[test] + fn converting_a_str_with_m_unit_into_a_resource_quantity_should_succeeds() { + impl_resources_quantity_unit_test!("100m"); + } - #[test] - fn converting_a_str_with_K_unit_into_a_resource_quantity_should_succeeds() { - impl_resources_quantity_unit_test!("50K"); - } + #[test] + fn converting_a_str_with_K_unit_into_a_resource_quantity_should_succeeds() { + impl_resources_quantity_unit_test!("50K"); + } - #[test] - fn converting_a_str_with_M_unit_into_a_resource_quantity_should_succeeds() { - impl_resources_quantity_unit_test!("100M"); - } + #[test] + fn converting_a_str_with_M_unit_into_a_resource_quantity_should_succeeds() { + impl_resources_quantity_unit_test!("100M"); + } - #[test] - fn converting_a_str_with_G_unit_into_a_resource_quantity_should_succeeds() { - impl_resources_quantity_unit_test!("1G"); - } + #[test] + fn converting_a_str_with_G_unit_into_a_resource_quantity_should_succeeds() { + impl_resources_quantity_unit_test!("1G"); + } - #[test] - fn converting_a_str_with_T_unit_into_a_resource_quantity_should_succeeds() { - impl_resources_quantity_unit_test!("0.01T"); - } + #[test] + fn converting_a_str_with_T_unit_into_a_resource_quantity_should_succeeds() { + impl_resources_quantity_unit_test!("0.01T"); + } - #[test] - fn converting_a_str_with_P_unit_into_a_resource_quantity_should_succeeds() { - impl_resources_quantity_unit_test!("0.00001P"); - } + #[test] + fn converting_a_str_with_P_unit_into_a_resource_quantity_should_succeeds() { + impl_resources_quantity_unit_test!("0.00001P"); + } - #[test] - fn converting_a_str_with_E_unit_into_a_resource_quantity_should_succeeds() { - impl_resources_quantity_unit_test!("0.000000001E"); - } + #[test] + fn converting_a_str_with_E_unit_into_a_resource_quantity_should_succeeds() { + impl_resources_quantity_unit_test!("0.000000001E"); + } - #[test] - fn converting_a_str_with_Ki_unit_into_a_resource_quantity_should_succeeds() { - impl_resources_quantity_unit_test!("50Ki"); - } + #[test] + fn converting_a_str_with_Ki_unit_into_a_resource_quantity_should_succeeds() { + impl_resources_quantity_unit_test!("50Ki"); + } - #[test] - fn converting_a_str_with_Mi_unit_into_a_resource_quantity_should_succeeds() { - impl_resources_quantity_unit_test!("100Mi"); - } + #[test] + fn converting_a_str_with_Mi_unit_into_a_resource_quantity_should_succeeds() { + impl_resources_quantity_unit_test!("100Mi"); + } - #[test] - fn converting_a_str_with_Gi_unit_into_a_resource_quantity_should_succeeds() { - impl_resources_quantity_unit_test!("1Gi"); - } + #[test] + fn converting_a_str_with_Gi_unit_into_a_resource_quantity_should_succeeds() { + impl_resources_quantity_unit_test!("1Gi"); + } - #[test] - fn converting_a_str_with_Ti_unit_into_a_resource_quantity_should_succeeds() { - impl_resources_quantity_unit_test!("0.01Ti"); - } + #[test] + fn converting_a_str_with_Ti_unit_into_a_resource_quantity_should_succeeds() { + impl_resources_quantity_unit_test!("0.01Ti"); + } - #[test] - fn converting_a_str_with_Pi_unit_into_a_resource_quantity_should_succeeds() { - impl_resources_quantity_unit_test!("0.00001Pi"); - } + #[test] + fn converting_a_str_with_Pi_unit_into_a_resource_quantity_should_succeeds() { + impl_resources_quantity_unit_test!("0.00001Pi"); + } - #[test] - fn converting_a_str_with_Ei_unit_into_a_resource_quantity_should_succeeds() { - impl_resources_quantity_unit_test!("0.000000001Ei"); - } + #[test] + fn converting_a_str_with_Ei_unit_into_a_resource_quantity_should_succeeds() { + impl_resources_quantity_unit_test!("0.000000001Ei"); + } - #[test] - fn resources_config_builder_should_succeeds_and_returns_a_resources_config() { - let resources = ResourcesBuilder::new() - .with_request_memory("200M") - .with_request_cpu("1G") - .with_limit_cpu("500M") - .with_limit_memory("2G") - .build() - .unwrap(); + #[test] + fn resources_config_builder_should_succeeds_and_returns_a_resources_config() { + let resources = ResourcesBuilder::new() + .with_request_memory("200M") + .with_request_cpu("1G") + .with_limit_cpu("500M") + .with_limit_memory("2G") + .build() + .unwrap(); - assert_eq!(resources.request_memory().unwrap().as_str(), "200M"); - assert_eq!(resources.request_cpu().unwrap().as_str(), "1G"); - assert_eq!(resources.limit_cpu().unwrap().as_str(), "500M"); - assert_eq!(resources.limit_memory().unwrap().as_str(), "2G"); - } + assert_eq!(resources.request_memory().unwrap().as_str(), "200M"); + assert_eq!(resources.request_cpu().unwrap().as_str(), "1G"); + assert_eq!(resources.limit_cpu().unwrap().as_str(), "500M"); + assert_eq!(resources.limit_memory().unwrap().as_str(), "2G"); + } - #[test] - fn resources_config_toml_import_should_succeeds_and_returns_a_resources_config() { - let load_from_toml = - NetworkConfig::load_from_toml("./testing/snapshots/0001-big-network.toml").unwrap(); + #[test] + fn resources_config_toml_import_should_succeeds_and_returns_a_resources_config() { + let load_from_toml = + NetworkConfig::load_from_toml("./testing/snapshots/0001-big-network.toml").unwrap(); - let resources = load_from_toml.relaychain().default_resources().unwrap(); - assert_eq!(resources.request_memory().unwrap().as_str(), "500M"); - assert_eq!(resources.request_cpu().unwrap().as_str(), "100000"); - assert_eq!(resources.limit_cpu().unwrap().as_str(), "10Gi"); - assert_eq!(resources.limit_memory().unwrap().as_str(), "4000M"); - } + let resources = load_from_toml.relaychain().default_resources().unwrap(); + assert_eq!(resources.request_memory().unwrap().as_str(), "500M"); + assert_eq!(resources.request_cpu().unwrap().as_str(), "100000"); + assert_eq!(resources.limit_cpu().unwrap().as_str(), "10Gi"); + assert_eq!(resources.limit_memory().unwrap().as_str(), "4000M"); + } - #[test] - fn resources_config_builder_should_fails_and_returns_an_error_if_couldnt_parse_request_memory() - { - let resources_builder = ResourcesBuilder::new().with_request_memory("invalid"); + #[test] + fn resources_config_builder_should_fails_and_returns_an_error_if_couldnt_parse_request_memory() + { + let resources_builder = ResourcesBuilder::new().with_request_memory("invalid"); - let errors = resources_builder.build().err().unwrap(); + let errors = resources_builder.build().err().unwrap(); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - r"request_memory: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" - ); - } + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + r"request_memory: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" + ); + } - #[test] - fn resources_config_builder_should_fails_and_returns_an_error_if_couldnt_parse_request_cpu() { - let resources_builder = ResourcesBuilder::new().with_request_cpu("invalid"); + #[test] + fn resources_config_builder_should_fails_and_returns_an_error_if_couldnt_parse_request_cpu() { + let resources_builder = ResourcesBuilder::new().with_request_cpu("invalid"); - let errors = resources_builder.build().err().unwrap(); + let errors = resources_builder.build().err().unwrap(); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - r"request_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" - ); - } + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + r"request_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" + ); + } - #[test] - fn resources_config_builder_should_fails_and_returns_an_error_if_couldnt_parse_limit_memory() { - let resources_builder = ResourcesBuilder::new().with_limit_memory("invalid"); + #[test] + fn resources_config_builder_should_fails_and_returns_an_error_if_couldnt_parse_limit_memory() { + let resources_builder = ResourcesBuilder::new().with_limit_memory("invalid"); - let errors = resources_builder.build().err().unwrap(); + let errors = resources_builder.build().err().unwrap(); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - r"limit_memory: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" - ); - } + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + r"limit_memory: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" + ); + } - #[test] - fn resources_config_builder_should_fails_and_returns_an_error_if_couldnt_parse_limit_cpu() { - let resources_builder = ResourcesBuilder::new().with_limit_cpu("invalid"); + #[test] + fn resources_config_builder_should_fails_and_returns_an_error_if_couldnt_parse_limit_cpu() { + let resources_builder = ResourcesBuilder::new().with_limit_cpu("invalid"); - let errors = resources_builder.build().err().unwrap(); + let errors = resources_builder.build().err().unwrap(); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - r"limit_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" - ); - } + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + r"limit_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" + ); + } - #[test] - fn resources_config_builder_should_fails_and_returns_multiple_error_if_couldnt_parse_multiple_fields( - ) { - let resources_builder = ResourcesBuilder::new() - .with_limit_cpu("invalid") - .with_request_memory("invalid"); + #[test] + fn resources_config_builder_should_fails_and_returns_multiple_error_if_couldnt_parse_multiple_fields( + ) { + let resources_builder = + ResourcesBuilder::new().with_limit_cpu("invalid").with_request_memory("invalid"); - let errors = resources_builder.build().err().unwrap(); + let errors = resources_builder.build().err().unwrap(); - assert_eq!(errors.len(), 2); - assert_eq!( - errors.first().unwrap().to_string(), - r"limit_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" - ); - assert_eq!( - errors.get(1).unwrap().to_string(), - r"request_memory: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" - ); - } + assert_eq!(errors.len(), 2); + assert_eq!( + errors.first().unwrap().to_string(), + r"limit_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" + ); + assert_eq!( + errors.get(1).unwrap().to_string(), + r"request_memory: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" + ); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/types.rs b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/types.rs index 765143e3..5a241e27 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/types.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/shared/types.rs @@ -1,17 +1,17 @@ use std::{ - collections::{HashMap, HashSet}, - error::Error, - fmt::{self, Display}, - path::PathBuf, - str::FromStr, + collections::{HashMap, HashSet}, + error::Error, + fmt::{self, Display}, + path::PathBuf, + str::FromStr, }; use anyhow::anyhow; use lazy_static::lazy_static; use regex::Regex; use serde::{ - de::{self, IntoDeserializer}, - Deserialize, Deserializer, Serialize, + de::{self, IntoDeserializer}, + Deserialize, Deserializer, Serialize, }; use support::constants::{INFAILABLE, SHOULD_COMPILE, THIS_IS_A_BUG}; use tokio::fs; @@ -34,54 +34,54 @@ pub type ParaId = u32; pub struct U128(pub(crate) u128); impl From for U128 { - fn from(value: u128) -> Self { - Self(value) - } + fn from(value: u128) -> Self { + Self(value) + } } impl TryFrom<&str> for U128 { - type Error = Box; + type Error = Box; - fn try_from(value: &str) -> Result { - Ok(Self(value.to_string().parse::()?)) - } + fn try_from(value: &str) -> Result { + Ok(Self(value.to_string().parse::()?)) + } } impl Serialize for U128 { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - // here we add a prefix to the string to be able to replace the wrapped - // value with "" to a value without "" in the TOML string - serializer.serialize_str(&format!("U128%{}", self.0)) - } + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + // here we add a prefix to the string to be able to replace the wrapped + // value with "" to a value without "" in the TOML string + serializer.serialize_str(&format!("U128%{}", self.0)) + } } struct U128Visitor; impl de::Visitor<'_> for U128Visitor { - type Value = U128; + type Value = U128; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("an integer between 0 and 2^128 − 1.") - } + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an integer between 0 and 2^128 − 1.") + } - fn visit_str(self, v: &str) -> Result - where - E: de::Error, - { - v.try_into().map_err(de::Error::custom) - } + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + v.try_into().map_err(de::Error::custom) + } } impl<'de> Deserialize<'de> for U128 { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_str(U128Visitor) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(U128Visitor) + } } /// A chain name. @@ -103,25 +103,25 @@ impl<'de> Deserialize<'de> for U128 { pub struct Chain(String); impl TryFrom<&str> for Chain { - type Error = ConversionError; + type Error = ConversionError; - fn try_from(value: &str) -> Result { - if value.contains(char::is_whitespace) { - return Err(ConversionError::ContainsWhitespaces(value.to_string())); - } + fn try_from(value: &str) -> Result { + if value.contains(char::is_whitespace) { + return Err(ConversionError::ContainsWhitespaces(value.to_string())); + } - if value.is_empty() { - return Err(ConversionError::CantBeEmpty); - } + if value.is_empty() { + return Err(ConversionError::CantBeEmpty); + } - Ok(Self(value.to_string())) - } + Ok(Self(value.to_string())) + } } impl Chain { - pub fn as_str(&self) -> &str { - &self.0 - } + pub fn as_str(&self) -> &str { + &self.0 + } } /// A container image. @@ -145,35 +145,35 @@ impl Chain { pub struct Image(String); impl TryFrom<&str> for Image { - type Error = ConversionError; + type Error = ConversionError; - fn try_from(value: &str) -> Result { - static IP_PART: &str = "((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))"; - static HOSTNAME_PART: &str = "((([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]).)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9]))"; - static TAG_NAME_PART: &str = "([a-z0-9](-*[a-z0-9])*)"; - static TAG_VERSION_PART: &str = "([a-z0-9_]([-._a-z0-9])*)"; - lazy_static! { - static ref RE: Regex = Regex::new(&format!( - "^({IP_PART}|{HOSTNAME_PART}/)?{TAG_NAME_PART}(:{TAG_VERSION_PART})?$", - )) - .expect(&format!("{SHOULD_COMPILE}, {THIS_IS_A_BUG}")); - }; + fn try_from(value: &str) -> Result { + static IP_PART: &str = "((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))"; + static HOSTNAME_PART: &str = "((([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]).)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9]))"; + static TAG_NAME_PART: &str = "([a-z0-9](-*[a-z0-9])*)"; + static TAG_VERSION_PART: &str = "([a-z0-9_]([-._a-z0-9])*)"; + lazy_static! { + static ref RE: Regex = Regex::new(&format!( + "^({IP_PART}|{HOSTNAME_PART}/)?{TAG_NAME_PART}(:{TAG_VERSION_PART})?$", + )) + .expect(&format!("{SHOULD_COMPILE}, {THIS_IS_A_BUG}")); + }; - if !RE.is_match(value) { - return Err(ConversionError::DoesntMatchRegex { - value: value.to_string(), - regex: "^([ip]|[hostname]/)?[tag_name]:[tag_version]?$".to_string(), - }); - } + if !RE.is_match(value) { + return Err(ConversionError::DoesntMatchRegex { + value: value.to_string(), + regex: "^([ip]|[hostname]/)?[tag_name]:[tag_version]?$".to_string(), + }); + } - Ok(Self(value.to_string())) - } + Ok(Self(value.to_string())) + } } impl Image { - pub fn as_str(&self) -> &str { - &self.0 - } + pub fn as_str(&self) -> &str { + &self.0 + } } /// A command that will be executed natively (native provider) or in a container (podman/k8s). @@ -193,26 +193,26 @@ impl Image { pub struct Command(String); impl TryFrom<&str> for Command { - type Error = ConversionError; + type Error = ConversionError; - fn try_from(value: &str) -> Result { - if value.contains(char::is_whitespace) { - return Err(ConversionError::ContainsWhitespaces(value.to_string())); - } + fn try_from(value: &str) -> Result { + if value.contains(char::is_whitespace) { + return Err(ConversionError::ContainsWhitespaces(value.to_string())); + } - Ok(Self(value.to_string())) - } + Ok(Self(value.to_string())) + } } impl Default for Command { - fn default() -> Self { - Self(String::from("polkadot")) - } + fn default() -> Self { + Self(String::from("polkadot")) + } } impl Command { - pub fn as_str(&self) -> &str { - &self.0 - } + pub fn as_str(&self) -> &str { + &self.0 + } } /// A command with optional custom arguments, the command will be executed natively (native provider) or in a container (podman/k8s). @@ -232,41 +232,41 @@ impl Command { pub struct CommandWithCustomArgs(Command, Vec); impl TryFrom<&str> for CommandWithCustomArgs { - type Error = ConversionError; + type Error = ConversionError; - fn try_from(value: &str) -> Result { - if value.is_empty() { - return Err(ConversionError::CantBeEmpty); - } + fn try_from(value: &str) -> Result { + if value.is_empty() { + return Err(ConversionError::CantBeEmpty); + } - let mut parts = value.split_whitespace().collect::>(); - let cmd = parts.remove(0).try_into().unwrap(); - let args = parts - .iter() - .map(|x| { - Arg::deserialize(x.into_deserializer()).map_err(|_: serde_json::Error| { - ConversionError::DeserializeError(String::from(*x)) - }) - }) - .collect::, _>>()?; + let mut parts = value.split_whitespace().collect::>(); + let cmd = parts.remove(0).try_into().unwrap(); + let args = parts + .iter() + .map(|x| { + Arg::deserialize(x.into_deserializer()).map_err(|_: serde_json::Error| { + ConversionError::DeserializeError(String::from(*x)) + }) + }) + .collect::, _>>()?; - Ok(Self(cmd, args)) - } + Ok(Self(cmd, args)) + } } impl Default for CommandWithCustomArgs { - fn default() -> Self { - Self("polkadot".try_into().unwrap(), vec![]) - } + fn default() -> Self { + Self("polkadot".try_into().unwrap(), vec![]) + } } impl CommandWithCustomArgs { - pub fn cmd(&self) -> &Command { - &self.0 - } + pub fn cmd(&self) -> &Command { + &self.0 + } - pub fn args(&self) -> &Vec { - &self.1 - } + pub fn args(&self) -> &Vec { + &self.1 + } } /// A location for a locally or remotely stored asset. @@ -290,107 +290,107 @@ impl CommandWithCustomArgs { /// ``` #[derive(Debug, Clone, PartialEq)] pub enum AssetLocation { - Url(Url), - FilePath(PathBuf), + Url(Url), + FilePath(PathBuf), } impl From for AssetLocation { - fn from(value: Url) -> Self { - Self::Url(value) - } + fn from(value: Url) -> Self { + Self::Url(value) + } } impl From for AssetLocation { - fn from(value: PathBuf) -> Self { - Self::FilePath(value) - } + fn from(value: PathBuf) -> Self { + Self::FilePath(value) + } } impl From<&str> for AssetLocation { - fn from(value: &str) -> Self { - if let Ok(parsed_url) = Url::parse(value) { - return Self::Url(parsed_url); - } + fn from(value: &str) -> Self { + if let Ok(parsed_url) = Url::parse(value) { + return Self::Url(parsed_url); + } - Self::FilePath(PathBuf::from_str(value).expect(&format!("{INFAILABLE}, {THIS_IS_A_BUG}"))) - } + Self::FilePath(PathBuf::from_str(value).expect(&format!("{INFAILABLE}, {THIS_IS_A_BUG}"))) + } } impl Display for AssetLocation { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - AssetLocation::Url(value) => write!(f, "{}", value.as_str()), - AssetLocation::FilePath(value) => write!(f, "{}", value.display()), - } - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AssetLocation::Url(value) => write!(f, "{}", value.as_str()), + AssetLocation::FilePath(value) => write!(f, "{}", value.display()), + } + } } impl AssetLocation { - /// Get the current asset (from file or url) and return the content - pub async fn get_asset(&self) -> Result, anyhow::Error> { - let contents = match self { - AssetLocation::Url(location) => { - let res = reqwest::get(location.as_ref()).await.map_err(|err| { - anyhow!("Error dowinloding asset from url {location} - {err}") - })?; + /// Get the current asset (from file or url) and return the content + pub async fn get_asset(&self) -> Result, anyhow::Error> { + let contents = match self { + AssetLocation::Url(location) => { + let res = reqwest::get(location.as_ref()).await.map_err(|err| { + anyhow!("Error dowinloding asset from url {location} - {err}") + })?; - res.bytes().await.unwrap().into() - }, - AssetLocation::FilePath(filepath) => { - tokio::fs::read(filepath).await.map_err(|err| { - anyhow!( - "Error reading asset from path {} - {}", - filepath.to_string_lossy(), - err - ) - })? - }, - }; + res.bytes().await.unwrap().into() + }, + AssetLocation::FilePath(filepath) => { + tokio::fs::read(filepath).await.map_err(|err| { + anyhow!( + "Error reading asset from path {} - {}", + filepath.to_string_lossy(), + err + ) + })? + }, + }; - Ok(contents) - } + Ok(contents) + } - /// Write asset (from file or url) to the destination path. - pub async fn dump_asset(&self, dst_path: impl Into) -> Result<(), anyhow::Error> { - let contents = self.get_asset().await?; - fs::write(dst_path.into(), contents).await?; - Ok(()) - } + /// Write asset (from file or url) to the destination path. + pub async fn dump_asset(&self, dst_path: impl Into) -> Result<(), anyhow::Error> { + let contents = self.get_asset().await?; + fs::write(dst_path.into(), contents).await?; + Ok(()) + } } impl Serialize for AssetLocation { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.to_string()) - } + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } } struct AssetLocationVisitor; impl de::Visitor<'_> for AssetLocationVisitor { - type Value = AssetLocation; + type Value = AssetLocation; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a string") - } + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string") + } - fn visit_str(self, v: &str) -> Result - where - E: de::Error, - { - Ok(AssetLocation::from(v)) - } + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Ok(AssetLocation::from(v)) + } } impl<'de> Deserialize<'de> for AssetLocation { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_any(AssetLocationVisitor) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_any(AssetLocationVisitor) + } } /// A CLI argument passed to an executed command, can be an option with an assigned value or a simple flag to enable/disable a feature. @@ -408,169 +408,157 @@ impl<'de> Deserialize<'de> for AssetLocation { /// ``` #[derive(Debug, Clone, PartialEq)] pub enum Arg { - Flag(String), - Option(String, String), - Array(String, Vec), + Flag(String), + Option(String, String), + Array(String, Vec), } impl From<&str> for Arg { - fn from(flag: &str) -> Self { - Self::Flag(flag.to_owned()) - } + fn from(flag: &str) -> Self { + Self::Flag(flag.to_owned()) + } } impl From<(&str, &str)> for Arg { - fn from((option, value): (&str, &str)) -> Self { - Self::Option(option.to_owned(), value.to_owned()) - } + fn from((option, value): (&str, &str)) -> Self { + Self::Option(option.to_owned(), value.to_owned()) + } } impl From<(&str, &[T])> for Arg where - T: AsRef + Clone, + T: AsRef + Clone, { - fn from((option, values): (&str, &[T])) -> Self { - Self::Array( - option.to_owned(), - values.iter().map(|v| v.as_ref().to_string()).collect(), - ) - } + fn from((option, values): (&str, &[T])) -> Self { + Self::Array(option.to_owned(), values.iter().map(|v| v.as_ref().to_string()).collect()) + } } impl From<(&str, Vec)> for Arg where - T: AsRef, + T: AsRef, { - fn from((option, values): (&str, Vec)) -> Self { - Self::Array( - option.to_owned(), - values.into_iter().map(|v| v.as_ref().to_string()).collect(), - ) - } + fn from((option, values): (&str, Vec)) -> Self { + Self::Array(option.to_owned(), values.into_iter().map(|v| v.as_ref().to_string()).collect()) + } } impl Serialize for Arg { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - match self { - Arg::Flag(value) => serializer.serialize_str(value), - Arg::Option(option, value) => serializer.serialize_str(&format!("{option}={value}")), - Arg::Array(option, values) => { - serializer.serialize_str(&format!("{}=[{}]", option, values.join(","))) - }, - } - } + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + Arg::Flag(value) => serializer.serialize_str(value), + Arg::Option(option, value) => serializer.serialize_str(&format!("{option}={value}")), + Arg::Array(option, values) => { + serializer.serialize_str(&format!("{}=[{}]", option, values.join(","))) + }, + } + } } struct ArgVisitor; impl de::Visitor<'_> for ArgVisitor { - type Value = Arg; + type Value = Arg; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a string") - } + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string") + } - fn visit_str(self, v: &str) -> Result - where - E: de::Error, - { - // covers the "-lruntime=debug,parachain=trace" case - // TODO: Make this more generic by adding the scenario in the regex below - if v.starts_with("-l") || v.starts_with("-log") { - return Ok(Arg::Flag(v.to_string())); - } - // Handle argument removal syntax: -:--flag-name - if v.starts_with("-:") { - return Ok(Arg::Flag(v.to_string())); - } - let re = Regex::new("^(?(?-{1,2})?(?[a-zA-Z]+(-[a-zA-Z]+)*))((?=| )(?\\[[^\\]]*\\]|[^ ]+))?$").unwrap(); + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + // covers the "-lruntime=debug,parachain=trace" case + // TODO: Make this more generic by adding the scenario in the regex below + if v.starts_with("-l") || v.starts_with("-log") { + return Ok(Arg::Flag(v.to_string())); + } + // Handle argument removal syntax: -:--flag-name + if v.starts_with("-:") { + return Ok(Arg::Flag(v.to_string())); + } + let re = Regex::new("^(?(?-{1,2})?(?[a-zA-Z]+(-[a-zA-Z]+)*))((?=| )(?\\[[^\\]]*\\]|[^ ]+))?$").unwrap(); - let captures = re.captures(v); - if let Some(captures) = captures { - if let Some(value) = captures.name("value") { - let name_prefix = captures - .name("name_prefix") - .expect("BUG: name_prefix capture group missing") - .as_str() - .to_string(); + let captures = re.captures(v); + if let Some(captures) = captures { + if let Some(value) = captures.name("value") { + let name_prefix = captures + .name("name_prefix") + .expect("BUG: name_prefix capture group missing") + .as_str() + .to_string(); - let val = value.as_str(); - if val.starts_with('[') && val.ends_with(']') { - // Remove brackets and split by comma - let inner = &val[1..val.len() - 1]; - let items: Vec = inner - .split(',') - .map(|s| s.trim().to_string()) - .filter(|s| !s.is_empty()) - .collect(); - return Ok(Arg::Array(name_prefix, items)); - } else { - return Ok(Arg::Option(name_prefix, val.to_string())); - } - } - if let Some(name_prefix) = captures.name("name_prefix") { - return Ok(Arg::Flag(name_prefix.as_str().to_string())); - } - } + let val = value.as_str(); + if val.starts_with('[') && val.ends_with(']') { + // Remove brackets and split by comma + let inner = &val[1..val.len() - 1]; + let items: Vec = inner + .split(',') + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .collect(); + return Ok(Arg::Array(name_prefix, items)); + } else { + return Ok(Arg::Option(name_prefix, val.to_string())); + } + } + if let Some(name_prefix) = captures.name("name_prefix") { + return Ok(Arg::Flag(name_prefix.as_str().to_string())); + } + } - Err(de::Error::custom( + Err(de::Error::custom( "the provided argument is invalid and doesn't match Arg::Option, Arg::Flag or Arg::Array", )) - } + } } impl<'de> Deserialize<'de> for Arg { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_any(ArgVisitor) - } + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_any(ArgVisitor) + } } #[derive(Debug, Default, Clone)] pub struct ValidationContext { - pub used_ports: Vec, - pub used_nodes_names: HashSet, - // Store para_id already used - pub used_para_ids: HashMap, + pub used_ports: Vec, + pub used_nodes_names: HashSet, + // Store para_id already used + pub used_para_ids: HashMap, } #[derive(Default, Debug, Clone, PartialEq, Deserialize)] pub struct ChainDefaultContext { - pub(crate) default_command: Option, - pub(crate) default_image: Option, - pub(crate) default_resources: Option, - pub(crate) default_db_snapshot: Option, - #[serde(default)] - pub(crate) default_args: Vec, + pub(crate) default_command: Option, + pub(crate) default_image: Option, + pub(crate) default_resources: Option, + pub(crate) default_db_snapshot: Option, + #[serde(default)] + pub(crate) default_args: Vec, } /// Represent a runtime (.wasm) asset location and an /// optional preset to use for chain-spec generation. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ChainSpecRuntime { - pub location: AssetLocation, - pub preset: Option, + pub location: AssetLocation, + pub preset: Option, } impl ChainSpecRuntime { - pub fn new(location: AssetLocation) -> Self { - ChainSpecRuntime { - location, - preset: None, - } - } + pub fn new(location: AssetLocation) -> Self { + ChainSpecRuntime { location, preset: None } + } - pub fn with_preset(location: AssetLocation, preset: impl Into) -> Self { - ChainSpecRuntime { - location, - preset: Some(preset.into()), - } - } + pub fn with_preset(location: AssetLocation, preset: impl Into) -> Self { + ChainSpecRuntime { location, preset: Some(preset.into()) } + } } /// Represents a set of JSON overrides for a configuration. @@ -580,351 +568,327 @@ impl ChainSpecRuntime { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(untagged)] pub enum JsonOverrides { - /// A path or URL pointing to a JSON file containing the overrides. - Location(AssetLocation), - /// An inline JSON value representing the overrides. - Json(serde_json::Value), + /// A path or URL pointing to a JSON file containing the overrides. + Location(AssetLocation), + /// An inline JSON value representing the overrides. + Json(serde_json::Value), } impl From for JsonOverrides { - fn from(value: AssetLocation) -> Self { - Self::Location(value) - } + fn from(value: AssetLocation) -> Self { + Self::Location(value) + } } impl From for JsonOverrides { - fn from(value: serde_json::Value) -> Self { - Self::Json(value) - } + fn from(value: serde_json::Value) -> Self { + Self::Json(value) + } } impl From<&str> for JsonOverrides { - fn from(value: &str) -> Self { - Self::Location(AssetLocation::from(value)) - } + fn from(value: &str) -> Self { + Self::Location(AssetLocation::from(value)) + } } impl Display for JsonOverrides { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - JsonOverrides::Location(location) => write!(f, "{location}"), - JsonOverrides::Json(json) => write!(f, "{json}"), - } - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + JsonOverrides::Location(location) => write!(f, "{location}"), + JsonOverrides::Json(json) => write!(f, "{json}"), + } + } } impl JsonOverrides { - pub async fn get(&self) -> Result { - let contents = match self { - Self::Location(location) => serde_json::from_slice(&location.get_asset().await?) - .map_err(|err| anyhow!("Error converting asset to json {location} - {err}")), - Self::Json(json) => Ok(json.clone()), - }; + pub async fn get(&self) -> Result { + let contents = match self { + Self::Location(location) => serde_json::from_slice(&location.get_asset().await?) + .map_err(|err| anyhow!("Error converting asset to json {location} - {err}")), + Self::Json(json) => Ok(json.clone()), + }; - contents - } + contents + } } #[cfg(test)] mod tests { - use super::*; + use super::*; - #[test] - fn test_arg_flag_roundtrip() { - let arg = Arg::from("verbose"); - let serialized = serde_json::to_string(&arg).unwrap(); - let deserialized: Arg = serde_json::from_str(&serialized).unwrap(); - assert_eq!(arg, deserialized); - } - #[test] - fn test_arg_option_roundtrip() { - let arg = Arg::from(("mode", "fast")); - let serialized = serde_json::to_string(&arg).unwrap(); - let deserialized: Arg = serde_json::from_str(&serialized).unwrap(); - assert_eq!(arg, deserialized); - } + #[test] + fn test_arg_flag_roundtrip() { + let arg = Arg::from("verbose"); + let serialized = serde_json::to_string(&arg).unwrap(); + let deserialized: Arg = serde_json::from_str(&serialized).unwrap(); + assert_eq!(arg, deserialized); + } + #[test] + fn test_arg_option_roundtrip() { + let arg = Arg::from(("mode", "fast")); + let serialized = serde_json::to_string(&arg).unwrap(); + let deserialized: Arg = serde_json::from_str(&serialized).unwrap(); + assert_eq!(arg, deserialized); + } - #[test] - fn test_arg_array_roundtrip() { - let arg = Arg::from(("items", ["a", "b", "c"].as_slice())); + #[test] + fn test_arg_array_roundtrip() { + let arg = Arg::from(("items", ["a", "b", "c"].as_slice())); - let serialized = serde_json::to_string(&arg).unwrap(); - println!("serialized = {serialized}"); - let deserialized: Arg = serde_json::from_str(&serialized).unwrap(); - assert_eq!(arg, deserialized); - } + let serialized = serde_json::to_string(&arg).unwrap(); + println!("serialized = {serialized}"); + let deserialized: Arg = serde_json::from_str(&serialized).unwrap(); + assert_eq!(arg, deserialized); + } - #[test] - fn test_arg_option_valid_input() { - let expected = Arg::from(("--foo", "bar")); + #[test] + fn test_arg_option_valid_input() { + let expected = Arg::from(("--foo", "bar")); - // name and value delimited with = - let valid = "\"--foo=bar\""; - let result: Result = serde_json::from_str(valid); - assert_eq!(result.unwrap(), expected); + // name and value delimited with = + let valid = "\"--foo=bar\""; + let result: Result = serde_json::from_str(valid); + assert_eq!(result.unwrap(), expected); - // name and value delimited with space - let valid = "\"--foo bar\""; - let result: Result = serde_json::from_str(valid); - assert_eq!(result.unwrap(), expected); + // name and value delimited with space + let valid = "\"--foo bar\""; + let result: Result = serde_json::from_str(valid); + assert_eq!(result.unwrap(), expected); - // value contains = - let expected = Arg::from(("--foo", "bar=baz")); - let valid = "\"--foo=bar=baz\""; - let result: Result = serde_json::from_str(valid); - assert_eq!(result.unwrap(), expected); - } + // value contains = + let expected = Arg::from(("--foo", "bar=baz")); + let valid = "\"--foo=bar=baz\""; + let result: Result = serde_json::from_str(valid); + assert_eq!(result.unwrap(), expected); + } - #[test] - fn test_arg_array_valid_input() { - let expected = Arg::from(("--foo", vec!["bar", "baz"])); + #[test] + fn test_arg_array_valid_input() { + let expected = Arg::from(("--foo", vec!["bar", "baz"])); - // name and values delimited with = - let valid = "\"--foo=[bar,baz]\""; - let result: Result = serde_json::from_str(valid); - assert_eq!(result.unwrap(), expected); + // name and values delimited with = + let valid = "\"--foo=[bar,baz]\""; + let result: Result = serde_json::from_str(valid); + assert_eq!(result.unwrap(), expected); - // name and values delimited with space - let valid = "\"--foo [bar,baz]\""; - let result: Result = serde_json::from_str(valid); - assert_eq!(result.unwrap(), expected); + // name and values delimited with space + let valid = "\"--foo [bar,baz]\""; + let result: Result = serde_json::from_str(valid); + assert_eq!(result.unwrap(), expected); - // values delimited with commas and space - let valid = "\"--foo [bar , baz]\""; - let result: Result = serde_json::from_str(valid); - assert_eq!(result.unwrap(), expected); + // values delimited with commas and space + let valid = "\"--foo [bar , baz]\""; + let result: Result = serde_json::from_str(valid); + assert_eq!(result.unwrap(), expected); - // empty values array - let expected = Arg::from(("--foo", Vec::<&str>::new())); - let valid = "\"--foo []\""; - let result: Result = serde_json::from_str(valid); - assert_eq!(result.unwrap(), expected); - } + // empty values array + let expected = Arg::from(("--foo", Vec::<&str>::new())); + let valid = "\"--foo []\""; + let result: Result = serde_json::from_str(valid); + assert_eq!(result.unwrap(), expected); + } - #[test] - fn test_arg_invalid_input() { - // missing = or space - let invalid = "\"--foo[bar]\""; - let result: Result = serde_json::from_str(invalid); - assert!(result.is_err()); + #[test] + fn test_arg_invalid_input() { + // missing = or space + let invalid = "\"--foo[bar]\""; + let result: Result = serde_json::from_str(invalid); + assert!(result.is_err()); - // value contains space - let invalid = "\"--foo=bar baz\""; - let result: Result = serde_json::from_str(invalid); - println!("result = {result:?}"); - assert!(result.is_err()); - } + // value contains space + let invalid = "\"--foo=bar baz\""; + let result: Result = serde_json::from_str(invalid); + println!("result = {result:?}"); + assert!(result.is_err()); + } - #[test] - fn converting_a_str_without_whitespaces_into_a_chain_should_succeeds() { - let got: Result = "mychain".try_into(); + #[test] + fn converting_a_str_without_whitespaces_into_a_chain_should_succeeds() { + let got: Result = "mychain".try_into(); - assert_eq!(got.unwrap().as_str(), "mychain"); - } + assert_eq!(got.unwrap().as_str(), "mychain"); + } - #[test] - fn converting_a_str_containing_tag_name_into_an_image_should_succeeds() { - let got: Result = "myimage".try_into(); + #[test] + fn converting_a_str_containing_tag_name_into_an_image_should_succeeds() { + let got: Result = "myimage".try_into(); - assert_eq!(got.unwrap().as_str(), "myimage"); - } + assert_eq!(got.unwrap().as_str(), "myimage"); + } - #[test] - fn converting_a_str_containing_tag_name_and_tag_version_into_an_image_should_succeeds() { - let got: Result = "myimage:version".try_into(); + #[test] + fn converting_a_str_containing_tag_name_and_tag_version_into_an_image_should_succeeds() { + let got: Result = "myimage:version".try_into(); - assert_eq!(got.unwrap().as_str(), "myimage:version"); - } + assert_eq!(got.unwrap().as_str(), "myimage:version"); + } - #[test] - fn converting_a_str_containing_hostname_and_tag_name_into_an_image_should_succeeds() { - let got: Result = "myrepository.com/myimage".try_into(); + #[test] + fn converting_a_str_containing_hostname_and_tag_name_into_an_image_should_succeeds() { + let got: Result = "myrepository.com/myimage".try_into(); - assert_eq!(got.unwrap().as_str(), "myrepository.com/myimage"); - } + assert_eq!(got.unwrap().as_str(), "myrepository.com/myimage"); + } - #[test] - fn converting_a_str_containing_hostname_tag_name_and_tag_version_into_an_image_should_succeeds() - { - let got: Result = "myrepository.com/myimage:version".try_into(); + #[test] + fn converting_a_str_containing_hostname_tag_name_and_tag_version_into_an_image_should_succeeds() + { + let got: Result = "myrepository.com/myimage:version".try_into(); - assert_eq!(got.unwrap().as_str(), "myrepository.com/myimage:version"); - } + assert_eq!(got.unwrap().as_str(), "myrepository.com/myimage:version"); + } - #[test] - fn converting_a_str_containing_ip_and_tag_name_into_an_image_should_succeeds() { - let got: Result = "myrepository.com/myimage".try_into(); + #[test] + fn converting_a_str_containing_ip_and_tag_name_into_an_image_should_succeeds() { + let got: Result = "myrepository.com/myimage".try_into(); - assert_eq!(got.unwrap().as_str(), "myrepository.com/myimage"); - } + assert_eq!(got.unwrap().as_str(), "myrepository.com/myimage"); + } - #[test] - fn converting_a_str_containing_ip_tag_name_and_tag_version_into_an_image_should_succeeds() { - let got: Result = "127.0.0.1/myimage:version".try_into(); + #[test] + fn converting_a_str_containing_ip_tag_name_and_tag_version_into_an_image_should_succeeds() { + let got: Result = "127.0.0.1/myimage:version".try_into(); - assert_eq!(got.unwrap().as_str(), "127.0.0.1/myimage:version"); - } + assert_eq!(got.unwrap().as_str(), "127.0.0.1/myimage:version"); + } - #[test] - fn converting_a_str_without_whitespaces_into_a_command_should_succeeds() { - let got: Result = "mycommand".try_into(); + #[test] + fn converting_a_str_without_whitespaces_into_a_command_should_succeeds() { + let got: Result = "mycommand".try_into(); - assert_eq!(got.unwrap().as_str(), "mycommand"); - } + assert_eq!(got.unwrap().as_str(), "mycommand"); + } - #[test] - fn converting_an_url_into_an_asset_location_should_succeeds() { - let url = Url::from_str("https://mycloudstorage.com/path/to/my/file.tgz").unwrap(); - let got: AssetLocation = url.clone().into(); + #[test] + fn converting_an_url_into_an_asset_location_should_succeeds() { + let url = Url::from_str("https://mycloudstorage.com/path/to/my/file.tgz").unwrap(); + let got: AssetLocation = url.clone().into(); - assert!(matches!(got, AssetLocation::Url(value) if value == url)); - } + assert!(matches!(got, AssetLocation::Url(value) if value == url)); + } - #[test] - fn converting_a_pathbuf_into_an_asset_location_should_succeeds() { - let pathbuf = PathBuf::from_str("/tmp/path/to/my/file").unwrap(); - let got: AssetLocation = pathbuf.clone().into(); + #[test] + fn converting_a_pathbuf_into_an_asset_location_should_succeeds() { + let pathbuf = PathBuf::from_str("/tmp/path/to/my/file").unwrap(); + let got: AssetLocation = pathbuf.clone().into(); - assert!(matches!(got, AssetLocation::FilePath(value) if value == pathbuf)); - } + assert!(matches!(got, AssetLocation::FilePath(value) if value == pathbuf)); + } - #[test] - fn converting_a_str_into_an_url_asset_location_should_succeeds() { - let url = "https://mycloudstorage.com/path/to/my/file.tgz"; - let got: AssetLocation = url.into(); + #[test] + fn converting_a_str_into_an_url_asset_location_should_succeeds() { + let url = "https://mycloudstorage.com/path/to/my/file.tgz"; + let got: AssetLocation = url.into(); - assert!(matches!(got, AssetLocation::Url(value) if value == Url::from_str(url).unwrap())); - } + assert!(matches!(got, AssetLocation::Url(value) if value == Url::from_str(url).unwrap())); + } - #[test] - fn converting_a_str_into_an_filepath_asset_location_should_succeeds() { - let filepath = "/tmp/path/to/my/file"; - let got: AssetLocation = filepath.into(); + #[test] + fn converting_a_str_into_an_filepath_asset_location_should_succeeds() { + let filepath = "/tmp/path/to/my/file"; + let got: AssetLocation = filepath.into(); - assert!(matches!( - got, - AssetLocation::FilePath(value) if value == PathBuf::from_str(filepath).unwrap() - )); - } + assert!(matches!( + got, + AssetLocation::FilePath(value) if value == PathBuf::from_str(filepath).unwrap() + )); + } - #[test] - fn converting_a_str_into_an_flag_arg_should_succeeds() { - let got: Arg = "myflag".into(); + #[test] + fn converting_a_str_into_an_flag_arg_should_succeeds() { + let got: Arg = "myflag".into(); - assert!(matches!(got, Arg::Flag(flag) if flag == "myflag")); - } + assert!(matches!(got, Arg::Flag(flag) if flag == "myflag")); + } - #[test] - fn converting_a_str_tuple_into_an_option_arg_should_succeeds() { - let got: Arg = ("name", "value").into(); + #[test] + fn converting_a_str_tuple_into_an_option_arg_should_succeeds() { + let got: Arg = ("name", "value").into(); - assert!(matches!(got, Arg::Option(name, value) if name == "name" && value == "value")); - } + assert!(matches!(got, Arg::Option(name, value) if name == "name" && value == "value")); + } - #[test] - fn converting_a_str_with_whitespaces_into_a_chain_should_fails() { - let got: Result = "my chain".try_into(); + #[test] + fn converting_a_str_with_whitespaces_into_a_chain_should_fails() { + let got: Result = "my chain".try_into(); - assert!(matches!( - got.clone().unwrap_err(), - ConversionError::ContainsWhitespaces(_) - )); - assert_eq!( - got.unwrap_err().to_string(), - "'my chain' shouldn't contains whitespace" - ); - } + assert!(matches!(got.clone().unwrap_err(), ConversionError::ContainsWhitespaces(_))); + assert_eq!(got.unwrap_err().to_string(), "'my chain' shouldn't contains whitespace"); + } - #[test] - fn converting_an_empty_str_into_a_chain_should_fails() { - let got: Result = "".try_into(); + #[test] + fn converting_an_empty_str_into_a_chain_should_fails() { + let got: Result = "".try_into(); - assert!(matches!( - got.clone().unwrap_err(), - ConversionError::CantBeEmpty - )); - assert_eq!(got.unwrap_err().to_string(), "can't be empty"); - } + assert!(matches!(got.clone().unwrap_err(), ConversionError::CantBeEmpty)); + assert_eq!(got.unwrap_err().to_string(), "can't be empty"); + } - #[test] - fn converting_a_str_containing_only_ip_into_an_image_should_fails() { - let got: Result = "127.0.0.1".try_into(); + #[test] + fn converting_a_str_containing_only_ip_into_an_image_should_fails() { + let got: Result = "127.0.0.1".try_into(); - assert!(matches!( - got.clone().unwrap_err(), - ConversionError::DoesntMatchRegex { value: _, regex: _ } - )); - assert_eq!( - got.unwrap_err().to_string(), - "'127.0.0.1' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'" - ); - } + assert!(matches!( + got.clone().unwrap_err(), + ConversionError::DoesntMatchRegex { value: _, regex: _ } + )); + assert_eq!( + got.unwrap_err().to_string(), + "'127.0.0.1' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'" + ); + } - #[test] - fn converting_a_str_containing_only_ip_and_tag_version_into_an_image_should_fails() { - let got: Result = "127.0.0.1:version".try_into(); + #[test] + fn converting_a_str_containing_only_ip_and_tag_version_into_an_image_should_fails() { + let got: Result = "127.0.0.1:version".try_into(); - assert!(matches!( - got.clone().unwrap_err(), - ConversionError::DoesntMatchRegex { value: _, regex: _ } - )); - assert_eq!(got.unwrap_err().to_string(), "'127.0.0.1:version' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'"); - } + assert!(matches!( + got.clone().unwrap_err(), + ConversionError::DoesntMatchRegex { value: _, regex: _ } + )); + assert_eq!(got.unwrap_err().to_string(), "'127.0.0.1:version' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'"); + } - #[test] - fn converting_a_str_containing_only_hostname_into_an_image_should_fails() { - let got: Result = "myrepository.com".try_into(); + #[test] + fn converting_a_str_containing_only_hostname_into_an_image_should_fails() { + let got: Result = "myrepository.com".try_into(); - assert!(matches!( - got.clone().unwrap_err(), - ConversionError::DoesntMatchRegex { value: _, regex: _ } - )); - assert_eq!(got.unwrap_err().to_string(), "'myrepository.com' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'"); - } + assert!(matches!( + got.clone().unwrap_err(), + ConversionError::DoesntMatchRegex { value: _, regex: _ } + )); + assert_eq!(got.unwrap_err().to_string(), "'myrepository.com' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'"); + } - #[test] - fn converting_a_str_containing_only_hostname_and_tag_version_into_an_image_should_fails() { - let got: Result = "myrepository.com:version".try_into(); + #[test] + fn converting_a_str_containing_only_hostname_and_tag_version_into_an_image_should_fails() { + let got: Result = "myrepository.com:version".try_into(); - assert!(matches!( - got.clone().unwrap_err(), - ConversionError::DoesntMatchRegex { value: _, regex: _ } - )); - assert_eq!(got.unwrap_err().to_string(), "'myrepository.com:version' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'"); - } + assert!(matches!( + got.clone().unwrap_err(), + ConversionError::DoesntMatchRegex { value: _, regex: _ } + )); + assert_eq!(got.unwrap_err().to_string(), "'myrepository.com:version' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'"); + } - #[test] - fn converting_a_str_with_whitespaces_into_a_command_should_fails() { - let got: Result = "my command".try_into(); + #[test] + fn converting_a_str_with_whitespaces_into_a_command_should_fails() { + let got: Result = "my command".try_into(); - assert!(matches!( - got.clone().unwrap_err(), - ConversionError::ContainsWhitespaces(_) - )); - assert_eq!( - got.unwrap_err().to_string(), - "'my command' shouldn't contains whitespace" - ); - } + assert!(matches!(got.clone().unwrap_err(), ConversionError::ContainsWhitespaces(_))); + assert_eq!(got.unwrap_err().to_string(), "'my command' shouldn't contains whitespace"); + } - #[test] - fn test_convert_to_json_overrides() { - let url: AssetLocation = "https://example.com/overrides.json".into(); - assert!(matches!( - url.into(), - JsonOverrides::Location(AssetLocation::Url(_)) - )); + #[test] + fn test_convert_to_json_overrides() { + let url: AssetLocation = "https://example.com/overrides.json".into(); + assert!(matches!(url.into(), JsonOverrides::Location(AssetLocation::Url(_)))); - let path: AssetLocation = "/path/to/overrides.json".into(); - assert!(matches!( - path.into(), - JsonOverrides::Location(AssetLocation::FilePath(_)) - )); + let path: AssetLocation = "/path/to/overrides.json".into(); + assert!(matches!(path.into(), JsonOverrides::Location(AssetLocation::FilePath(_)))); - let inline = serde_json::json!({ "para_id": 2000}); - assert!(matches!( - inline.into(), - JsonOverrides::Json(serde_json::Value::Object(_)) - )); - } + let inline = serde_json::json!({ "para_id": 2000}); + assert!(matches!(inline.into(), JsonOverrides::Json(serde_json::Value::Object(_)))); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/teyrchain.rs b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/teyrchain.rs index a9de4587..46f5a183 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/teyrchain.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/teyrchain.rs @@ -3,1871 +3,1675 @@ use std::{cell::RefCell, error::Error, fmt::Display, marker::PhantomData, rc::Rc use anyhow::anyhow; use multiaddr::Multiaddr; use serde::{ - de::{self, Visitor}, - ser::SerializeStruct, - Deserialize, Serialize, + de::{self, Visitor}, + ser::SerializeStruct, + Deserialize, Serialize, }; use crate::{ - shared::{ - errors::{ConfigError, FieldError}, - helpers::{generate_unique_para_id, merge_errors, merge_errors_vecs}, - node::{self, GroupNodeConfig, GroupNodeConfigBuilder, NodeConfig, NodeConfigBuilder}, - resources::{Resources, ResourcesBuilder}, - types::{ - Arg, AssetLocation, Chain, ChainDefaultContext, Command, Image, ValidationContext, U128, - }, - }, - types::{ChainSpecRuntime, CommandWithCustomArgs, JsonOverrides}, - utils::{default_as_false, default_as_true, default_initial_balance, is_false}, + shared::{ + errors::{ConfigError, FieldError}, + helpers::{generate_unique_para_id, merge_errors, merge_errors_vecs}, + node::{self, GroupNodeConfig, GroupNodeConfigBuilder, NodeConfig, NodeConfigBuilder}, + resources::{Resources, ResourcesBuilder}, + types::{ + Arg, AssetLocation, Chain, ChainDefaultContext, Command, Image, ValidationContext, U128, + }, + }, + types::{ChainSpecRuntime, CommandWithCustomArgs, JsonOverrides}, + utils::{default_as_false, default_as_true, default_initial_balance, is_false}, }; /// The registration strategy that will be used for the teyrchain. #[derive(Debug, Clone, PartialEq)] pub enum RegistrationStrategy { - /// The teyrchain will be added to the genesis before spawning. - InGenesis, - /// The teyrchain will be registered using an extrinsic after spawning. - UsingExtrinsic, - /// The teyrchaing will not be registered and the user can doit after spawning manually. - Manual, + /// The teyrchain will be added to the genesis before spawning. + InGenesis, + /// The teyrchain will be registered using an extrinsic after spawning. + UsingExtrinsic, + /// The teyrchaing will not be registered and the user can doit after spawning manually. + Manual, } impl Serialize for RegistrationStrategy { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut state = serializer.serialize_struct("RegistrationStrategy", 1)?; + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("RegistrationStrategy", 1)?; - match self { - Self::InGenesis => state.serialize_field("add_to_genesis", &true)?, - Self::UsingExtrinsic => state.serialize_field("register_para", &true)?, - Self::Manual => { - state.serialize_field("add_to_genesis", &false)?; - state.serialize_field("register_para", &false)?; - }, - } + match self { + Self::InGenesis => state.serialize_field("add_to_genesis", &true)?, + Self::UsingExtrinsic => state.serialize_field("register_para", &true)?, + Self::Manual => { + state.serialize_field("add_to_genesis", &false)?; + state.serialize_field("register_para", &false)?; + }, + } - state.end() - } + state.end() + } } struct RegistrationStrategyVisitor; impl<'de> Visitor<'de> for RegistrationStrategyVisitor { - type Value = RegistrationStrategy; + type Value = RegistrationStrategy; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("struct RegistrationStrategy") - } + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct RegistrationStrategy") + } - fn visit_map(self, mut map: A) -> Result - where - A: serde::de::MapAccess<'de>, - { - let mut add_to_genesis = false; - let mut register_para = false; + fn visit_map(self, mut map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + let mut add_to_genesis = false; + let mut register_para = false; - while let Some(key) = map.next_key::()? { - match key.as_str() { - "addToGenesis" | "add_to_genesis" => add_to_genesis = map.next_value()?, - "registerPara" | "register_para" => register_para = map.next_value()?, - _ => { - return Err(de::Error::unknown_field( - &key, - &["add_to_genesis", "register_para"], - )) - }, - } - } + while let Some(key) = map.next_key::()? { + match key.as_str() { + "addToGenesis" | "add_to_genesis" => add_to_genesis = map.next_value()?, + "registerPara" | "register_para" => register_para = map.next_value()?, + _ => { + return Err(de::Error::unknown_field( + &key, + &["add_to_genesis", "register_para"], + )) + }, + } + } - match (add_to_genesis, register_para) { - (true, false) => Ok(RegistrationStrategy::InGenesis), - (false, true) => Ok(RegistrationStrategy::UsingExtrinsic), - _ => Err(de::Error::missing_field("add_to_genesis or register_para")), - } - } + match (add_to_genesis, register_para) { + (true, false) => Ok(RegistrationStrategy::InGenesis), + (false, true) => Ok(RegistrationStrategy::UsingExtrinsic), + _ => Err(de::Error::missing_field("add_to_genesis or register_para")), + } + } } impl<'de> Deserialize<'de> for RegistrationStrategy { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - deserializer.deserialize_struct( - "RegistrationStrategy", - &["add_to_genesis", "register_para"], - RegistrationStrategyVisitor, - ) - } + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_struct( + "RegistrationStrategy", + &["add_to_genesis", "register_para"], + RegistrationStrategyVisitor, + ) + } } /// A teyrchain configuration, composed of collators and fine-grained configuration options. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct TeyrchainConfig { - id: u32, - #[serde(skip)] - // unique_id is internally used to allow multiple teyrchains with the same id - // BUT, only one of them could be register automatically at spawn - unique_id: String, - chain: Option, - #[serde(flatten)] - registration_strategy: Option, - #[serde( - skip_serializing_if = "super::utils::is_true", - default = "default_as_true" - )] - onboard_as_teyrchain: bool, - #[serde(rename = "balance", default = "default_initial_balance")] - initial_balance: U128, - default_command: Option, - default_image: Option, - default_resources: Option, - default_db_snapshot: Option, - #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] - default_args: Vec, - genesis_wasm_path: Option, - genesis_wasm_generator: Option, - genesis_state_path: Option, - genesis_state_generator: Option, - /// chain-spec to use (location can be url or file path) - chain_spec_path: Option, - /// runtime to use for generating the chain-spec. - /// Location can be url or file path and an optional preset - chain_spec_runtime: Option, - // Path or url to override the runtime (:code) in the chain-spec - wasm_override: Option, - // Full _template_ command, will be rendered using [tera] - // and executed for generate the chain-spec. - // available tokens {{chainName}} / {{disableBootnodes}} - chain_spec_command: Option, - // Does the chain_spec_command needs to be run locally - #[serde(skip_serializing_if = "is_false", default)] - chain_spec_command_is_local: bool, - // Path to the file where the `chain_spec_command` will write the chain-spec into. - // Defaults to /dev/stdout. - chain_spec_command_output_path: Option, - #[serde(rename = "cumulus_based", default = "default_as_true")] - is_cumulus_based: bool, - #[serde(rename = "evm_based", default = "default_as_false")] - is_evm_based: bool, - #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] - bootnodes_addresses: Vec, - #[serde(skip_serializing_if = "is_false", default)] - no_default_bootnodes: bool, - #[serde(rename = "genesis", skip_serializing_if = "Option::is_none")] - genesis_overrides: Option, - #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] - pub(crate) collators: Vec, - // Single collator config, added for backward compatibility - // with `toml` networks definitions from v1. - // This field can only be set loading an old `toml` definition - // with `[teyrchain.collator]` key. - // NOTE: if the file also contains multiple collators defined in - // `[[teyrchain.collators]], the single configuration will be added to the bottom. - pub(crate) collator: Option, - #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] - pub(crate) collator_groups: Vec, - // Inline json or asset location to override raw chainspec - raw_spec_override: Option, + id: u32, + #[serde(skip)] + // unique_id is internally used to allow multiple teyrchains with the same id + // BUT, only one of them could be register automatically at spawn + unique_id: String, + chain: Option, + #[serde(flatten)] + registration_strategy: Option, + #[serde(skip_serializing_if = "super::utils::is_true", default = "default_as_true")] + onboard_as_teyrchain: bool, + #[serde(rename = "balance", default = "default_initial_balance")] + initial_balance: U128, + default_command: Option, + default_image: Option, + default_resources: Option, + default_db_snapshot: Option, + #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] + default_args: Vec, + genesis_wasm_path: Option, + genesis_wasm_generator: Option, + genesis_state_path: Option, + genesis_state_generator: Option, + /// chain-spec to use (location can be url or file path) + chain_spec_path: Option, + /// runtime to use for generating the chain-spec. + /// Location can be url or file path and an optional preset + chain_spec_runtime: Option, + // Path or url to override the runtime (:code) in the chain-spec + wasm_override: Option, + // Full _template_ command, will be rendered using [tera] + // and executed for generate the chain-spec. + // available tokens {{chainName}} / {{disableBootnodes}} + chain_spec_command: Option, + // Does the chain_spec_command needs to be run locally + #[serde(skip_serializing_if = "is_false", default)] + chain_spec_command_is_local: bool, + // Path to the file where the `chain_spec_command` will write the chain-spec into. + // Defaults to /dev/stdout. + chain_spec_command_output_path: Option, + #[serde(rename = "cumulus_based", default = "default_as_true")] + is_cumulus_based: bool, + #[serde(rename = "evm_based", default = "default_as_false")] + is_evm_based: bool, + #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] + bootnodes_addresses: Vec, + #[serde(skip_serializing_if = "is_false", default)] + no_default_bootnodes: bool, + #[serde(rename = "genesis", skip_serializing_if = "Option::is_none")] + genesis_overrides: Option, + #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] + pub(crate) collators: Vec, + // Single collator config, added for backward compatibility + // with `toml` networks definitions from v1. + // This field can only be set loading an old `toml` definition + // with `[teyrchain.collator]` key. + // NOTE: if the file also contains multiple collators defined in + // `[[teyrchain.collators]], the single configuration will be added to the bottom. + pub(crate) collator: Option, + #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)] + pub(crate) collator_groups: Vec, + // Inline json or asset location to override raw chainspec + raw_spec_override: Option, } impl TeyrchainConfig { - /// The teyrchain ID. - pub fn id(&self) -> u32 { - self.id - } + /// The teyrchain ID. + pub fn id(&self) -> u32 { + self.id + } - /// The teyrchain unique ID. - pub fn unique_id(&self) -> &str { - &self.unique_id - } + /// The teyrchain unique ID. + pub fn unique_id(&self) -> &str { + &self.unique_id + } - /// The chain name. - pub fn chain(&self) -> Option<&Chain> { - self.chain.as_ref() - } + /// The chain name. + pub fn chain(&self) -> Option<&Chain> { + self.chain.as_ref() + } - /// The registration strategy for the teyrchain. - pub fn registration_strategy(&self) -> Option<&RegistrationStrategy> { - self.registration_strategy.as_ref() - } + /// The registration strategy for the teyrchain. + pub fn registration_strategy(&self) -> Option<&RegistrationStrategy> { + self.registration_strategy.as_ref() + } - /// Whether the teyrchain should be onboarded or stay a parathread - pub fn onboard_as_teyrchain(&self) -> bool { - self.onboard_as_teyrchain - } + /// Whether the teyrchain should be onboarded or stay a parathread + pub fn onboard_as_teyrchain(&self) -> bool { + self.onboard_as_teyrchain + } - /// Backward compatibility alias for onboard_as_teyrchain(). - pub fn onboard_as_parachain(&self) -> bool { - self.onboard_as_teyrchain() - } + /// Backward compatibility alias for onboard_as_teyrchain(). + pub fn onboard_as_parachain(&self) -> bool { + self.onboard_as_teyrchain() + } - /// The initial balance of the teyrchain account. - pub fn initial_balance(&self) -> u128 { - self.initial_balance.0 - } + /// The initial balance of the teyrchain account. + pub fn initial_balance(&self) -> u128 { + self.initial_balance.0 + } - /// The default command used for collators. - pub fn default_command(&self) -> Option<&Command> { - self.default_command.as_ref() - } + /// The default command used for collators. + pub fn default_command(&self) -> Option<&Command> { + self.default_command.as_ref() + } - /// The default container image used for collators. - pub fn default_image(&self) -> Option<&Image> { - self.default_image.as_ref() - } + /// The default container image used for collators. + pub fn default_image(&self) -> Option<&Image> { + self.default_image.as_ref() + } - /// The default resources limits used for collators. - pub fn default_resources(&self) -> Option<&Resources> { - self.default_resources.as_ref() - } + /// The default resources limits used for collators. + pub fn default_resources(&self) -> Option<&Resources> { + self.default_resources.as_ref() + } - /// The default database snapshot location that will be used for state. - pub fn default_db_snapshot(&self) -> Option<&AssetLocation> { - self.default_db_snapshot.as_ref() - } + /// The default database snapshot location that will be used for state. + pub fn default_db_snapshot(&self) -> Option<&AssetLocation> { + self.default_db_snapshot.as_ref() + } - /// The default arguments that will be used to execute the collator command. - pub fn default_args(&self) -> Vec<&Arg> { - self.default_args.iter().collect::>() - } + /// The default arguments that will be used to execute the collator command. + pub fn default_args(&self) -> Vec<&Arg> { + self.default_args.iter().collect::>() + } - /// The location of a pre-existing genesis WASM runtime blob of the teyrchain. - pub fn genesis_wasm_path(&self) -> Option<&AssetLocation> { - self.genesis_wasm_path.as_ref() - } + /// The location of a pre-existing genesis WASM runtime blob of the teyrchain. + pub fn genesis_wasm_path(&self) -> Option<&AssetLocation> { + self.genesis_wasm_path.as_ref() + } - /// The generator command used to create the genesis WASM runtime blob of the teyrchain. - pub fn genesis_wasm_generator(&self) -> Option<&Command> { - self.genesis_wasm_generator.as_ref() - } + /// The generator command used to create the genesis WASM runtime blob of the teyrchain. + pub fn genesis_wasm_generator(&self) -> Option<&Command> { + self.genesis_wasm_generator.as_ref() + } - /// The location of a pre-existing genesis state of the teyrchain. - pub fn genesis_state_path(&self) -> Option<&AssetLocation> { - self.genesis_state_path.as_ref() - } + /// The location of a pre-existing genesis state of the teyrchain. + pub fn genesis_state_path(&self) -> Option<&AssetLocation> { + self.genesis_state_path.as_ref() + } - /// The generator command used to create the genesis state of the teyrchain. - pub fn genesis_state_generator(&self) -> Option<&CommandWithCustomArgs> { - self.genesis_state_generator.as_ref() - } + /// The generator command used to create the genesis state of the teyrchain. + pub fn genesis_state_generator(&self) -> Option<&CommandWithCustomArgs> { + self.genesis_state_generator.as_ref() + } - /// The genesis overrides as a JSON value. - pub fn genesis_overrides(&self) -> Option<&serde_json::Value> { - self.genesis_overrides.as_ref() - } + /// The genesis overrides as a JSON value. + pub fn genesis_overrides(&self) -> Option<&serde_json::Value> { + self.genesis_overrides.as_ref() + } - /// The location of a pre-existing chain specification for the teyrchain. - pub fn chain_spec_path(&self) -> Option<&AssetLocation> { - self.chain_spec_path.as_ref() - } + /// The location of a pre-existing chain specification for the teyrchain. + pub fn chain_spec_path(&self) -> Option<&AssetLocation> { + self.chain_spec_path.as_ref() + } - /// The full _template_ command to genera the chain-spec - pub fn chain_spec_command(&self) -> Option<&str> { - self.chain_spec_command.as_deref() - } + /// The full _template_ command to genera the chain-spec + pub fn chain_spec_command(&self) -> Option<&str> { + self.chain_spec_command.as_deref() + } - /// Does the chain_spec_command needs to be run locally - pub fn chain_spec_command_is_local(&self) -> bool { - self.chain_spec_command_is_local - } + /// Does the chain_spec_command needs to be run locally + pub fn chain_spec_command_is_local(&self) -> bool { + self.chain_spec_command_is_local + } - /// The file where the `chain_spec_command` will write the chain-spec into. - /// Defaults to /dev/stdout. - pub fn chain_spec_command_output_path(&self) -> Option<&str> { - self.chain_spec_command_output_path.as_deref() - } + /// The file where the `chain_spec_command` will write the chain-spec into. + /// Defaults to /dev/stdout. + pub fn chain_spec_command_output_path(&self) -> Option<&str> { + self.chain_spec_command_output_path.as_deref() + } - /// Whether the teyrchain is based on cumulus. - pub fn is_cumulus_based(&self) -> bool { - self.is_cumulus_based - } + /// Whether the teyrchain is based on cumulus. + pub fn is_cumulus_based(&self) -> bool { + self.is_cumulus_based + } - /// Whether the teyrchain is evm based (e.g frontier). - pub fn is_evm_based(&self) -> bool { - self.is_evm_based - } + /// Whether the teyrchain is evm based (e.g frontier). + pub fn is_evm_based(&self) -> bool { + self.is_evm_based + } - /// The bootnodes addresses the collators will connect to. - pub fn bootnodes_addresses(&self) -> Vec<&Multiaddr> { - self.bootnodes_addresses.iter().collect::>() - } + /// The bootnodes addresses the collators will connect to. + pub fn bootnodes_addresses(&self) -> Vec<&Multiaddr> { + self.bootnodes_addresses.iter().collect::>() + } - /// Whether to not automatically assign a bootnode role if none of the nodes are marked - /// as bootnodes. - pub fn no_default_bootnodes(&self) -> bool { - self.no_default_bootnodes - } + /// Whether to not automatically assign a bootnode role if none of the nodes are marked + /// as bootnodes. + pub fn no_default_bootnodes(&self) -> bool { + self.no_default_bootnodes + } - /// The collators of the teyrchain. - pub fn collators(&self) -> Vec<&NodeConfig> { - let mut cols = self.collators.iter().collect::>(); - if let Some(col) = self.collator.as_ref() { - cols.push(col); - } - cols - } + /// The collators of the teyrchain. + pub fn collators(&self) -> Vec<&NodeConfig> { + let mut cols = self.collators.iter().collect::>(); + if let Some(col) = self.collator.as_ref() { + cols.push(col); + } + cols + } - /// The grouped collators of the teyrchain. - pub fn group_collators_configs(&self) -> Vec<&GroupNodeConfig> { - self.collator_groups.iter().collect::>() - } + /// The grouped collators of the teyrchain. + pub fn group_collators_configs(&self) -> Vec<&GroupNodeConfig> { + self.collator_groups.iter().collect::>() + } - /// The location of a wasm runtime to override in the chain-spec. - pub fn wasm_override(&self) -> Option<&AssetLocation> { - self.wasm_override.as_ref() - } + /// The location of a wasm runtime to override in the chain-spec. + pub fn wasm_override(&self) -> Option<&AssetLocation> { + self.wasm_override.as_ref() + } - /// The location of a file or inline json to override raw chain-spec. - pub fn raw_spec_override(&self) -> Option<&JsonOverrides> { - self.raw_spec_override.as_ref() - } + /// The location of a file or inline json to override raw chain-spec. + pub fn raw_spec_override(&self) -> Option<&JsonOverrides> { + self.raw_spec_override.as_ref() + } - /// The location of runtime to use by chain-spec builder lib (from `sc-chain-spec` crate) - pub fn chain_spec_runtime(&self) -> Option<&ChainSpecRuntime> { - self.chain_spec_runtime.as_ref() - } + /// The location of runtime to use by chain-spec builder lib (from `sc-chain-spec` crate) + pub fn chain_spec_runtime(&self) -> Option<&ChainSpecRuntime> { + self.chain_spec_runtime.as_ref() + } } pub mod states { - use crate::shared::macros::states; + use crate::shared::macros::states; - states! { - Initial, - WithId, - WithAtLeastOneCollator - } + states! { + Initial, + WithId, + WithAtLeastOneCollator + } - states! { - Bootstrap, - Running - } + states! { + Bootstrap, + Running + } - pub trait Context {} - impl Context for Bootstrap {} - impl Context for Running {} + pub trait Context {} + impl Context for Bootstrap {} + impl Context for Running {} } use states::{Bootstrap, Context, Initial, Running, WithAtLeastOneCollator, WithId}; /// A teyrchain configuration builder, used to build a [`TeyrchainConfig`] declaratively with fields validation. pub struct TeyrchainConfigBuilder { - config: TeyrchainConfig, - validation_context: Rc>, - errors: Vec, - _state: PhantomData, - _context: PhantomData, + config: TeyrchainConfig, + validation_context: Rc>, + errors: Vec, + _state: PhantomData, + _context: PhantomData, } impl Default for TeyrchainConfigBuilder { - fn default() -> Self { - Self { - config: TeyrchainConfig { - id: 100, - unique_id: String::from("100"), - chain: None, - registration_strategy: Some(RegistrationStrategy::InGenesis), - onboard_as_teyrchain: true, - initial_balance: 2_000_000_000_000.into(), - default_command: None, - default_image: None, - default_resources: None, - default_db_snapshot: None, - default_args: vec![], - genesis_wasm_path: None, - genesis_wasm_generator: None, - genesis_state_path: None, - genesis_state_generator: None, - genesis_overrides: None, - chain_spec_path: None, - chain_spec_runtime: None, - chain_spec_command: None, - chain_spec_command_output_path: None, - wasm_override: None, - chain_spec_command_is_local: false, // remote by default - is_cumulus_based: true, - is_evm_based: false, - bootnodes_addresses: vec![], - no_default_bootnodes: false, - collators: vec![], - collator: None, - collator_groups: vec![], - raw_spec_override: None, - }, - validation_context: Default::default(), - errors: vec![], - _state: PhantomData, - _context: PhantomData, - } - } + fn default() -> Self { + Self { + config: TeyrchainConfig { + id: 100, + unique_id: String::from("100"), + chain: None, + registration_strategy: Some(RegistrationStrategy::InGenesis), + onboard_as_teyrchain: true, + initial_balance: 2_000_000_000_000.into(), + default_command: None, + default_image: None, + default_resources: None, + default_db_snapshot: None, + default_args: vec![], + genesis_wasm_path: None, + genesis_wasm_generator: None, + genesis_state_path: None, + genesis_state_generator: None, + genesis_overrides: None, + chain_spec_path: None, + chain_spec_runtime: None, + chain_spec_command: None, + chain_spec_command_output_path: None, + wasm_override: None, + chain_spec_command_is_local: false, // remote by default + is_cumulus_based: true, + is_evm_based: false, + bootnodes_addresses: vec![], + no_default_bootnodes: false, + collators: vec![], + collator: None, + collator_groups: vec![], + raw_spec_override: None, + }, + validation_context: Default::default(), + errors: vec![], + _state: PhantomData, + _context: PhantomData, + } + } } impl TeyrchainConfigBuilder { - fn transition( - config: TeyrchainConfig, - validation_context: Rc>, - errors: Vec, - ) -> TeyrchainConfigBuilder { - TeyrchainConfigBuilder { - config, - validation_context, - errors, - _state: PhantomData, - _context: PhantomData, - } - } + fn transition( + config: TeyrchainConfig, + validation_context: Rc>, + errors: Vec, + ) -> TeyrchainConfigBuilder { + TeyrchainConfigBuilder { + config, + validation_context, + errors, + _state: PhantomData, + _context: PhantomData, + } + } - fn default_chain_context(&self) -> ChainDefaultContext { - ChainDefaultContext { - default_command: self.config.default_command.clone(), - default_image: self.config.default_image.clone(), - default_resources: self.config.default_resources.clone(), - default_db_snapshot: self.config.default_db_snapshot.clone(), - default_args: self.config.default_args.clone(), - } - } + fn default_chain_context(&self) -> ChainDefaultContext { + ChainDefaultContext { + default_command: self.config.default_command.clone(), + default_image: self.config.default_image.clone(), + default_resources: self.config.default_resources.clone(), + default_db_snapshot: self.config.default_db_snapshot.clone(), + default_args: self.config.default_args.clone(), + } + } - fn create_node_builder(&self, f: F) -> NodeConfigBuilder - where - F: FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, - { - f(NodeConfigBuilder::new( - self.default_chain_context(), - self.validation_context.clone(), - )) - } + fn create_node_builder(&self, f: F) -> NodeConfigBuilder + where + F: FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, + { + f(NodeConfigBuilder::new(self.default_chain_context(), self.validation_context.clone())) + } } impl TeyrchainConfigBuilder { - /// Instantiate a new builder that can be used to build a [`TeyrchainConfig`] during the bootstrap phase. - pub fn new( - validation_context: Rc>, - ) -> TeyrchainConfigBuilder { - Self { - validation_context, - ..Self::default() - } - } + /// Instantiate a new builder that can be used to build a [`TeyrchainConfig`] during the bootstrap phase. + pub fn new( + validation_context: Rc>, + ) -> TeyrchainConfigBuilder { + Self { validation_context, ..Self::default() } + } } impl TeyrchainConfigBuilder { - /// Set the registration strategy for the teyrchain, could be Manual (no registered by zombienet) or automatic - /// using an extrinsic or in genesis. - pub fn with_registration_strategy(self, strategy: RegistrationStrategy) -> Self { - Self::transition( - TeyrchainConfig { - registration_strategy: Some(strategy), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the registration strategy for the teyrchain, could be Manual (no registered by zombienet) or automatic + /// using an extrinsic or in genesis. + pub fn with_registration_strategy(self, strategy: RegistrationStrategy) -> Self { + Self::transition( + TeyrchainConfig { registration_strategy: Some(strategy), ..self.config }, + self.validation_context, + self.errors, + ) + } } impl TeyrchainConfigBuilder { - /// Set the registration strategy for the teyrchain, could be Manual (no registered by zombienet) or automatic - /// Using an extrinsic. Genesis option is not allowed in `Running` context. - pub fn with_registration_strategy(self, strategy: RegistrationStrategy) -> Self { - match strategy { - RegistrationStrategy::InGenesis => Self::transition( - self.config, - self.validation_context, - merge_errors( - self.errors, - FieldError::RegistrationStrategy(anyhow!( - "Can be set to InGenesis in Running context" - )) - .into(), - ), - ), - RegistrationStrategy::Manual | RegistrationStrategy::UsingExtrinsic => { - Self::transition( - TeyrchainConfig { - registration_strategy: Some(strategy), - ..self.config - }, - self.validation_context, - self.errors, - ) - }, - } - } + /// Set the registration strategy for the teyrchain, could be Manual (no registered by zombienet) or automatic + /// Using an extrinsic. Genesis option is not allowed in `Running` context. + pub fn with_registration_strategy(self, strategy: RegistrationStrategy) -> Self { + match strategy { + RegistrationStrategy::InGenesis => Self::transition( + self.config, + self.validation_context, + merge_errors( + self.errors, + FieldError::RegistrationStrategy(anyhow!( + "Can be set to InGenesis in Running context" + )) + .into(), + ), + ), + RegistrationStrategy::Manual | RegistrationStrategy::UsingExtrinsic => { + Self::transition( + TeyrchainConfig { registration_strategy: Some(strategy), ..self.config }, + self.validation_context, + self.errors, + ) + }, + } + } } impl TeyrchainConfigBuilder { - /// Start a new builder in the context of a running network - pub fn new_with_running( - validation_context: Rc>, - ) -> TeyrchainConfigBuilder { - let mut builder = Self { - validation_context, - ..Self::default() - }; + /// Start a new builder in the context of a running network + pub fn new_with_running( + validation_context: Rc>, + ) -> TeyrchainConfigBuilder { + let mut builder = Self { validation_context, ..Self::default() }; - // override the registration strategy - builder.config.registration_strategy = Some(RegistrationStrategy::UsingExtrinsic); - builder - } + // override the registration strategy + builder.config.registration_strategy = Some(RegistrationStrategy::UsingExtrinsic); + builder + } } impl TeyrchainConfigBuilder { - /// Set the teyrchain ID and the unique_id (with the suffix `-x` if the id is already used) - pub fn with_id(self, id: u32) -> TeyrchainConfigBuilder { - let unique_id = generate_unique_para_id(id, self.validation_context.clone()); - Self::transition( - TeyrchainConfig { - id, - unique_id, - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the teyrchain ID and the unique_id (with the suffix `-x` if the id is already used) + pub fn with_id(self, id: u32) -> TeyrchainConfigBuilder { + let unique_id = generate_unique_para_id(id, self.validation_context.clone()); + Self::transition( + TeyrchainConfig { id, unique_id, ..self.config }, + self.validation_context, + self.errors, + ) + } } impl TeyrchainConfigBuilder { - /// Set the chain name (e.g. rococo-local). - /// Use [`None`], if you are running adder-collator or undying-collator). - pub fn with_chain(self, chain: T) -> Self - where - T: TryInto, - T::Error: Error + Send + Sync + 'static, - { - match chain.try_into() { - Ok(chain) => Self::transition( - TeyrchainConfig { - chain: Some(chain), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(error) => Self::transition( - self.config, - self.validation_context, - merge_errors(self.errors, FieldError::Chain(error.into()).into()), - ), - } - } + /// Set the chain name (e.g. rococo-local). + /// Use [`None`], if you are running adder-collator or undying-collator). + pub fn with_chain(self, chain: T) -> Self + where + T: TryInto, + T::Error: Error + Send + Sync + 'static, + { + match chain.try_into() { + Ok(chain) => Self::transition( + TeyrchainConfig { chain: Some(chain), ..self.config }, + self.validation_context, + self.errors, + ), + Err(error) => Self::transition( + self.config, + self.validation_context, + merge_errors(self.errors, FieldError::Chain(error.into()).into()), + ), + } + } - /// Set whether the teyrchain should be onboarded or stay a parathread. Default is ```true```. - pub fn onboard_as_teyrchain(self, choice: bool) -> Self { - Self::transition( - TeyrchainConfig { - onboard_as_teyrchain: choice, - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set whether the teyrchain should be onboarded or stay a parathread. Default is ```true```. + pub fn onboard_as_teyrchain(self, choice: bool) -> Self { + Self::transition( + TeyrchainConfig { onboard_as_teyrchain: choice, ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Backward compatibility alias for onboard_as_teyrchain(). - pub fn onboard_as_parachain(self, choice: bool) -> Self { - self.onboard_as_teyrchain(choice) - } + /// Backward compatibility alias for onboard_as_teyrchain(). + pub fn onboard_as_parachain(self, choice: bool) -> Self { + self.onboard_as_teyrchain(choice) + } - /// Set the initial balance of the teyrchain account. - pub fn with_initial_balance(self, initial_balance: u128) -> Self { - Self::transition( - TeyrchainConfig { - initial_balance: initial_balance.into(), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the initial balance of the teyrchain account. + pub fn with_initial_balance(self, initial_balance: u128) -> Self { + Self::transition( + TeyrchainConfig { initial_balance: initial_balance.into(), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the default command used for collators. Can be overridden. - pub fn with_default_command(self, command: T) -> Self - where - T: TryInto, - T::Error: Error + Send + Sync + 'static, - { - match command.try_into() { - Ok(command) => Self::transition( - TeyrchainConfig { - default_command: Some(command), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(error) => Self::transition( - self.config, - self.validation_context, - merge_errors(self.errors, FieldError::DefaultCommand(error.into()).into()), - ), - } - } + /// Set the default command used for collators. Can be overridden. + pub fn with_default_command(self, command: T) -> Self + where + T: TryInto, + T::Error: Error + Send + Sync + 'static, + { + match command.try_into() { + Ok(command) => Self::transition( + TeyrchainConfig { default_command: Some(command), ..self.config }, + self.validation_context, + self.errors, + ), + Err(error) => Self::transition( + self.config, + self.validation_context, + merge_errors(self.errors, FieldError::DefaultCommand(error.into()).into()), + ), + } + } - /// Set the default container image used for collators. Can be overridden. - pub fn with_default_image(self, image: T) -> Self - where - T: TryInto, - T::Error: Error + Send + Sync + 'static, - { - match image.try_into() { - Ok(image) => Self::transition( - TeyrchainConfig { - default_image: Some(image), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(error) => Self::transition( - self.config, - self.validation_context, - merge_errors(self.errors, FieldError::DefaultImage(error.into()).into()), - ), - } - } + /// Set the default container image used for collators. Can be overridden. + pub fn with_default_image(self, image: T) -> Self + where + T: TryInto, + T::Error: Error + Send + Sync + 'static, + { + match image.try_into() { + Ok(image) => Self::transition( + TeyrchainConfig { default_image: Some(image), ..self.config }, + self.validation_context, + self.errors, + ), + Err(error) => Self::transition( + self.config, + self.validation_context, + merge_errors(self.errors, FieldError::DefaultImage(error.into()).into()), + ), + } + } - /// Set the default resources limits used for collators. Can be overridden. - pub fn with_default_resources( - self, - f: impl FnOnce(ResourcesBuilder) -> ResourcesBuilder, - ) -> Self { - match f(ResourcesBuilder::new()).build() { - Ok(default_resources) => Self::transition( - TeyrchainConfig { - default_resources: Some(default_resources), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(errors) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| FieldError::DefaultResources(error).into()) - .collect::>(), - ), - ), - } - } + /// Set the default resources limits used for collators. Can be overridden. + pub fn with_default_resources( + self, + f: impl FnOnce(ResourcesBuilder) -> ResourcesBuilder, + ) -> Self { + match f(ResourcesBuilder::new()).build() { + Ok(default_resources) => Self::transition( + TeyrchainConfig { default_resources: Some(default_resources), ..self.config }, + self.validation_context, + self.errors, + ), + Err(errors) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| FieldError::DefaultResources(error).into()) + .collect::>(), + ), + ), + } + } - /// Set the default database snapshot location that will be used for state. Can be overridden. - pub fn with_default_db_snapshot(self, location: impl Into) -> Self { - Self::transition( - TeyrchainConfig { - default_db_snapshot: Some(location.into()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the default database snapshot location that will be used for state. Can be overridden. + pub fn with_default_db_snapshot(self, location: impl Into) -> Self { + Self::transition( + TeyrchainConfig { default_db_snapshot: Some(location.into()), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the default arguments that will be used to execute the collator command. Can be overridden. - pub fn with_default_args(self, args: Vec) -> Self { - Self::transition( - TeyrchainConfig { - default_args: args, - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the default arguments that will be used to execute the collator command. Can be overridden. + pub fn with_default_args(self, args: Vec) -> Self { + Self::transition( + TeyrchainConfig { default_args: args, ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the location of a pre-existing genesis WASM runtime blob of the teyrchain. - pub fn with_genesis_wasm_path(self, location: impl Into) -> Self { - Self::transition( - TeyrchainConfig { - genesis_wasm_path: Some(location.into()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the location of a pre-existing genesis WASM runtime blob of the teyrchain. + pub fn with_genesis_wasm_path(self, location: impl Into) -> Self { + Self::transition( + TeyrchainConfig { genesis_wasm_path: Some(location.into()), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the generator command used to create the genesis WASM runtime blob of the teyrchain. - pub fn with_genesis_wasm_generator(self, command: T) -> Self - where - T: TryInto, - T::Error: Error + Send + Sync + 'static, - { - match command.try_into() { - Ok(command) => Self::transition( - TeyrchainConfig { - genesis_wasm_generator: Some(command), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(error) => Self::transition( - self.config, - self.validation_context, - merge_errors( - self.errors, - FieldError::GenesisWasmGenerator(error.into()).into(), - ), - ), - } - } + /// Set the generator command used to create the genesis WASM runtime blob of the teyrchain. + pub fn with_genesis_wasm_generator(self, command: T) -> Self + where + T: TryInto, + T::Error: Error + Send + Sync + 'static, + { + match command.try_into() { + Ok(command) => Self::transition( + TeyrchainConfig { genesis_wasm_generator: Some(command), ..self.config }, + self.validation_context, + self.errors, + ), + Err(error) => Self::transition( + self.config, + self.validation_context, + merge_errors(self.errors, FieldError::GenesisWasmGenerator(error.into()).into()), + ), + } + } - /// Set the location of a pre-existing genesis state of the teyrchain. - pub fn with_genesis_state_path(self, location: impl Into) -> Self { - Self::transition( - TeyrchainConfig { - genesis_state_path: Some(location.into()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the location of a pre-existing genesis state of the teyrchain. + pub fn with_genesis_state_path(self, location: impl Into) -> Self { + Self::transition( + TeyrchainConfig { genesis_state_path: Some(location.into()), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the generator command used to create the genesis state of the teyrchain. - pub fn with_genesis_state_generator(self, command: T) -> Self - where - T: TryInto, - T::Error: Error + Send + Sync + 'static, - { - match command.try_into() { - Ok(command) => Self::transition( - TeyrchainConfig { - genesis_state_generator: Some(command), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err(error) => Self::transition( - self.config, - self.validation_context, - merge_errors( - self.errors, - FieldError::GenesisStateGenerator(error.into()).into(), - ), - ), - } - } + /// Set the generator command used to create the genesis state of the teyrchain. + pub fn with_genesis_state_generator(self, command: T) -> Self + where + T: TryInto, + T::Error: Error + Send + Sync + 'static, + { + match command.try_into() { + Ok(command) => Self::transition( + TeyrchainConfig { genesis_state_generator: Some(command), ..self.config }, + self.validation_context, + self.errors, + ), + Err(error) => Self::transition( + self.config, + self.validation_context, + merge_errors(self.errors, FieldError::GenesisStateGenerator(error.into()).into()), + ), + } + } - /// Set the genesis overrides as a JSON object. - pub fn with_genesis_overrides(self, genesis_overrides: impl Into) -> Self { - Self::transition( - TeyrchainConfig { - genesis_overrides: Some(genesis_overrides.into()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the genesis overrides as a JSON object. + pub fn with_genesis_overrides(self, genesis_overrides: impl Into) -> Self { + Self::transition( + TeyrchainConfig { genesis_overrides: Some(genesis_overrides.into()), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the location of a pre-existing chain specification for the teyrchain. - pub fn with_chain_spec_path(self, location: impl Into) -> Self { - Self::transition( - TeyrchainConfig { - chain_spec_path: Some(location.into()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the location of a pre-existing chain specification for the teyrchain. + pub fn with_chain_spec_path(self, location: impl Into) -> Self { + Self::transition( + TeyrchainConfig { chain_spec_path: Some(location.into()), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the chain-spec command _template_ for the relay chain. - pub fn with_chain_spec_command(self, cmd_template: impl Into) -> Self { - Self::transition( - TeyrchainConfig { - chain_spec_command: Some(cmd_template.into()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the chain-spec command _template_ for the relay chain. + pub fn with_chain_spec_command(self, cmd_template: impl Into) -> Self { + Self::transition( + TeyrchainConfig { chain_spec_command: Some(cmd_template.into()), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the runtime path to use for generating the chain-spec and an optiona preset. - /// If the preset is not set, we will try to match [`local_testnet`, `development`, `dev`] - /// with the available ones and fallback to the default configuration as last option. - pub fn with_chain_spec_runtime( - self, - location: impl Into, - preset: Option<&str>, - ) -> Self { - let chain_spec_runtime = if let Some(preset) = preset { - ChainSpecRuntime::with_preset(location.into(), preset.to_string()) - } else { - ChainSpecRuntime::new(location.into()) - }; - Self::transition( - TeyrchainConfig { - chain_spec_runtime: Some(chain_spec_runtime), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the runtime path to use for generating the chain-spec and an optiona preset. + /// If the preset is not set, we will try to match [`local_testnet`, `development`, `dev`] + /// with the available ones and fallback to the default configuration as last option. + pub fn with_chain_spec_runtime( + self, + location: impl Into, + preset: Option<&str>, + ) -> Self { + let chain_spec_runtime = if let Some(preset) = preset { + ChainSpecRuntime::with_preset(location.into(), preset.to_string()) + } else { + ChainSpecRuntime::new(location.into()) + }; + Self::transition( + TeyrchainConfig { chain_spec_runtime: Some(chain_spec_runtime), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the location of a wasm to override the chain-spec. - pub fn with_wasm_override(self, location: impl Into) -> Self { - Self::transition( - TeyrchainConfig { - wasm_override: Some(location.into()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the location of a wasm to override the chain-spec. + pub fn with_wasm_override(self, location: impl Into) -> Self { + Self::transition( + TeyrchainConfig { wasm_override: Some(location.into()), ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set if the chain-spec command needs to be run locally or not (false by default) - pub fn chain_spec_command_is_local(self, choice: bool) -> Self { - Self::transition( - TeyrchainConfig { - chain_spec_command_is_local: choice, - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set if the chain-spec command needs to be run locally or not (false by default) + pub fn chain_spec_command_is_local(self, choice: bool) -> Self { + Self::transition( + TeyrchainConfig { chain_spec_command_is_local: choice, ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the output path for the chain-spec command. - pub fn with_chain_spec_command_output_path(self, output_path: &str) -> Self { - Self::transition( - TeyrchainConfig { - chain_spec_command_output_path: Some(output_path.to_string()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the output path for the chain-spec command. + pub fn with_chain_spec_command_output_path(self, output_path: &str) -> Self { + Self::transition( + TeyrchainConfig { + chain_spec_command_output_path: Some(output_path.to_string()), + ..self.config + }, + self.validation_context, + self.errors, + ) + } - /// Set whether the teyrchain is based on cumulus (true in a majority of case, except adder or undying collators). - pub fn cumulus_based(self, choice: bool) -> Self { - Self::transition( - TeyrchainConfig { - is_cumulus_based: choice, - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set whether the teyrchain is based on cumulus (true in a majority of case, except adder or undying collators). + pub fn cumulus_based(self, choice: bool) -> Self { + Self::transition( + TeyrchainConfig { is_cumulus_based: choice, ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Alias for cumulus_based - Set whether the teyrchain is based on pezcumulus/cumulus. - pub fn pezcumulus_based(self, choice: bool) -> Self { - self.cumulus_based(choice) - } + /// Alias for cumulus_based - Set whether the teyrchain is based on pezcumulus/cumulus. + pub fn pezcumulus_based(self, choice: bool) -> Self { + self.cumulus_based(choice) + } - /// Set whether the teyrchain is evm based (e.g frontier /evm template) - pub fn evm_based(self, choice: bool) -> Self { - Self::transition( - TeyrchainConfig { - is_evm_based: choice, - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set whether the teyrchain is evm based (e.g frontier /evm template) + pub fn evm_based(self, choice: bool) -> Self { + Self::transition( + TeyrchainConfig { is_evm_based: choice, ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Set the bootnodes addresses the collators will connect to. - /// - /// Note: Bootnode address replacements are NOT supported here. - /// Only arguments (`args`) support dynamic replacements. Bootnode addresses must be a valid address. - pub fn with_raw_bootnodes_addresses(self, bootnodes_addresses: Vec) -> Self - where - T: TryInto + Display + Copy, - T::Error: Error + Send + Sync + 'static, - { - let mut addrs = vec![]; - let mut errors = vec![]; + /// Set the bootnodes addresses the collators will connect to. + /// + /// Note: Bootnode address replacements are NOT supported here. + /// Only arguments (`args`) support dynamic replacements. Bootnode addresses must be a valid address. + pub fn with_raw_bootnodes_addresses(self, bootnodes_addresses: Vec) -> Self + where + T: TryInto + Display + Copy, + T::Error: Error + Send + Sync + 'static, + { + let mut addrs = vec![]; + let mut errors = vec![]; - for (index, addr) in bootnodes_addresses.into_iter().enumerate() { - match addr.try_into() { - Ok(addr) => addrs.push(addr), - Err(error) => errors.push( - FieldError::BootnodesAddress(index, addr.to_string(), error.into()).into(), - ), - } - } + for (index, addr) in bootnodes_addresses.into_iter().enumerate() { + match addr.try_into() { + Ok(addr) => addrs.push(addr), + Err(error) => errors.push( + FieldError::BootnodesAddress(index, addr.to_string(), error.into()).into(), + ), + } + } - Self::transition( - TeyrchainConfig { - bootnodes_addresses: addrs, - ..self.config - }, - self.validation_context, - merge_errors_vecs(self.errors, errors), - ) - } + Self::transition( + TeyrchainConfig { bootnodes_addresses: addrs, ..self.config }, + self.validation_context, + merge_errors_vecs(self.errors, errors), + ) + } - /// Do not assign a bootnode role automatically if no nodes are marked as bootnodes. - pub fn without_default_bootnodes(self) -> Self { - Self::transition( - TeyrchainConfig { - no_default_bootnodes: true, - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Do not assign a bootnode role automatically if no nodes are marked as bootnodes. + pub fn without_default_bootnodes(self) -> Self { + Self::transition( + TeyrchainConfig { no_default_bootnodes: true, ..self.config }, + self.validation_context, + self.errors, + ) + } - /// Add a new collator using a nested [`NodeConfigBuilder`]. - pub fn with_collator( - self, - f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, - ) -> TeyrchainConfigBuilder { - match self.create_node_builder(f).validator(true).build() { - Ok(collator) => Self::transition( - TeyrchainConfig { - collators: vec![collator], - ..self.config - }, - self.validation_context, - self.errors, - ), - Err((name, errors)) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| ConfigError::Collator(name.clone(), error).into()) - .collect::>(), - ), - ), - } - } + /// Add a new collator using a nested [`NodeConfigBuilder`]. + pub fn with_collator( + self, + f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, + ) -> TeyrchainConfigBuilder { + match self.create_node_builder(f).validator(true).build() { + Ok(collator) => Self::transition( + TeyrchainConfig { collators: vec![collator], ..self.config }, + self.validation_context, + self.errors, + ), + Err((name, errors)) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| ConfigError::Collator(name.clone(), error).into()) + .collect::>(), + ), + ), + } + } - /// Add a new full node using a nested [`NodeConfigBuilder`]. - /// The node will be configured as a full node (non-validator). - pub fn with_fullnode( - self, - f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, - ) -> TeyrchainConfigBuilder { - match self.create_node_builder(f).validator(false).build() { - Ok(node) => Self::transition( - TeyrchainConfig { - collators: vec![node], - ..self.config - }, - self.validation_context, - self.errors, - ), - Err((name, errors)) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| ConfigError::Collator(name.clone(), error).into()) - .collect::>(), - ), - ), - } - } + /// Add a new full node using a nested [`NodeConfigBuilder`]. + /// The node will be configured as a full node (non-validator). + pub fn with_fullnode( + self, + f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, + ) -> TeyrchainConfigBuilder { + match self.create_node_builder(f).validator(false).build() { + Ok(node) => Self::transition( + TeyrchainConfig { collators: vec![node], ..self.config }, + self.validation_context, + self.errors, + ), + Err((name, errors)) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| ConfigError::Collator(name.clone(), error).into()) + .collect::>(), + ), + ), + } + } - /// Add a new node using a nested [`NodeConfigBuilder`]. - /// - /// **Deprecated**: Use [`with_collator`] for collator nodes or [`with_fullnode`] for full nodes instead. - #[deprecated( - since = "0.4.0", - note = "Use `with_collator()` for collator nodes or `with_fullnode()` for full nodes instead" - )] - pub fn with_node( - self, - f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, - ) -> TeyrchainConfigBuilder { - match self.create_node_builder(f).build() { - Ok(node) => Self::transition( - TeyrchainConfig { - collators: vec![node], - ..self.config - }, - self.validation_context, - self.errors, - ), - Err((name, errors)) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| ConfigError::Collator(name.clone(), error).into()) - .collect::>(), - ), - ), - } - } + /// Add a new node using a nested [`NodeConfigBuilder`]. + /// + /// **Deprecated**: Use [`with_collator`] for collator nodes or [`with_fullnode`] for full nodes instead. + #[deprecated( + since = "0.4.0", + note = "Use `with_collator()` for collator nodes or `with_fullnode()` for full nodes instead" + )] + pub fn with_node( + self, + f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, + ) -> TeyrchainConfigBuilder { + match self.create_node_builder(f).build() { + Ok(node) => Self::transition( + TeyrchainConfig { collators: vec![node], ..self.config }, + self.validation_context, + self.errors, + ), + Err((name, errors)) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| ConfigError::Collator(name.clone(), error).into()) + .collect::>(), + ), + ), + } + } - /// Add a new collator group using a nested [`GroupNodeConfigBuilder`]. - pub fn with_collator_group( - self, - f: impl FnOnce(GroupNodeConfigBuilder) -> GroupNodeConfigBuilder, - ) -> TeyrchainConfigBuilder { - match f(GroupNodeConfigBuilder::new( - self.default_chain_context(), - self.validation_context.clone(), - )) - .build() - { - Ok(group) => Self::transition( - TeyrchainConfig { - collator_groups: [self.config.collator_groups, vec![group]].concat(), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err((name, errors)) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| ConfigError::Collator(name.clone(), error).into()) - .collect::>(), - ), - ), - } - } + /// Add a new collator group using a nested [`GroupNodeConfigBuilder`]. + pub fn with_collator_group( + self, + f: impl FnOnce(GroupNodeConfigBuilder) -> GroupNodeConfigBuilder, + ) -> TeyrchainConfigBuilder { + match f(GroupNodeConfigBuilder::new( + self.default_chain_context(), + self.validation_context.clone(), + )) + .build() + { + Ok(group) => Self::transition( + TeyrchainConfig { + collator_groups: [self.config.collator_groups, vec![group]].concat(), + ..self.config + }, + self.validation_context, + self.errors, + ), + Err((name, errors)) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| ConfigError::Collator(name.clone(), error).into()) + .collect::>(), + ), + ), + } + } - /// Set the location or inline value of json to override the raw chain-spec. - pub fn with_raw_spec_override(self, overrides: impl Into) -> Self { - Self::transition( - TeyrchainConfig { - raw_spec_override: Some(overrides.into()), - ..self.config - }, - self.validation_context, - self.errors, - ) - } + /// Set the location or inline value of json to override the raw chain-spec. + pub fn with_raw_spec_override(self, overrides: impl Into) -> Self { + Self::transition( + TeyrchainConfig { raw_spec_override: Some(overrides.into()), ..self.config }, + self.validation_context, + self.errors, + ) + } } impl TeyrchainConfigBuilder { - /// Add a new collator using a nested [`NodeConfigBuilder`]. - pub fn with_collator( - self, - f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, - ) -> Self { - match self.create_node_builder(f).validator(true).build() { - Ok(collator) => Self::transition( - TeyrchainConfig { - collators: [self.config.collators, vec![collator]].concat(), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err((name, errors)) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| ConfigError::Collator(name.clone(), error).into()) - .collect::>(), - ), - ), - } - } + /// Add a new collator using a nested [`NodeConfigBuilder`]. + pub fn with_collator( + self, + f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, + ) -> Self { + match self.create_node_builder(f).validator(true).build() { + Ok(collator) => Self::transition( + TeyrchainConfig { + collators: [self.config.collators, vec![collator]].concat(), + ..self.config + }, + self.validation_context, + self.errors, + ), + Err((name, errors)) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| ConfigError::Collator(name.clone(), error).into()) + .collect::>(), + ), + ), + } + } - /// Add a new full node using a nested [`NodeConfigBuilder`]. - /// The node will be configured as a full node (non-validator). - pub fn with_fullnode( - self, - f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, - ) -> Self { - match self.create_node_builder(f).validator(false).build() { - Ok(node) => Self::transition( - TeyrchainConfig { - collators: [self.config.collators, vec![node]].concat(), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err((name, errors)) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| ConfigError::Collator(name.clone(), error).into()) - .collect::>(), - ), - ), - } - } + /// Add a new full node using a nested [`NodeConfigBuilder`]. + /// The node will be configured as a full node (non-validator). + pub fn with_fullnode( + self, + f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, + ) -> Self { + match self.create_node_builder(f).validator(false).build() { + Ok(node) => Self::transition( + TeyrchainConfig { + collators: [self.config.collators, vec![node]].concat(), + ..self.config + }, + self.validation_context, + self.errors, + ), + Err((name, errors)) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| ConfigError::Collator(name.clone(), error).into()) + .collect::>(), + ), + ), + } + } - /// Add a new node using a nested [`NodeConfigBuilder`]. - /// - /// **Deprecated**: Use [`with_collator`] for collator nodes or [`with_fullnode`] for full nodes instead. - #[deprecated( - since = "0.4.0", - note = "Use `with_collator()` for collator nodes or `with_fullnode()` for full nodes instead" - )] - pub fn with_node( - self, - f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, - ) -> Self { - match self.create_node_builder(f).build() { - Ok(node) => Self::transition( - TeyrchainConfig { - collators: [self.config.collators, vec![node]].concat(), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err((name, errors)) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| ConfigError::Collator(name.clone(), error).into()) - .collect::>(), - ), - ), - } - } + /// Add a new node using a nested [`NodeConfigBuilder`]. + /// + /// **Deprecated**: Use [`with_collator`] for collator nodes or [`with_fullnode`] for full nodes instead. + #[deprecated( + since = "0.4.0", + note = "Use `with_collator()` for collator nodes or `with_fullnode()` for full nodes instead" + )] + pub fn with_node( + self, + f: impl FnOnce(NodeConfigBuilder) -> NodeConfigBuilder, + ) -> Self { + match self.create_node_builder(f).build() { + Ok(node) => Self::transition( + TeyrchainConfig { + collators: [self.config.collators, vec![node]].concat(), + ..self.config + }, + self.validation_context, + self.errors, + ), + Err((name, errors)) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| ConfigError::Collator(name.clone(), error).into()) + .collect::>(), + ), + ), + } + } - /// Add a new collator group using a nested [`GroupNodeConfigBuilder`]. - pub fn with_collator_group( - self, - f: impl FnOnce(GroupNodeConfigBuilder) -> GroupNodeConfigBuilder, - ) -> Self { - match f(GroupNodeConfigBuilder::new( - self.default_chain_context(), - self.validation_context.clone(), - )) - .build() - { - Ok(group) => Self::transition( - TeyrchainConfig { - collator_groups: [self.config.collator_groups, vec![group]].concat(), - ..self.config - }, - self.validation_context, - self.errors, - ), - Err((name, errors)) => Self::transition( - self.config, - self.validation_context, - merge_errors_vecs( - self.errors, - errors - .into_iter() - .map(|error| ConfigError::Collator(name.clone(), error).into()) - .collect::>(), - ), - ), - } - } + /// Add a new collator group using a nested [`GroupNodeConfigBuilder`]. + pub fn with_collator_group( + self, + f: impl FnOnce(GroupNodeConfigBuilder) -> GroupNodeConfigBuilder, + ) -> Self { + match f(GroupNodeConfigBuilder::new( + self.default_chain_context(), + self.validation_context.clone(), + )) + .build() + { + Ok(group) => Self::transition( + TeyrchainConfig { + collator_groups: [self.config.collator_groups, vec![group]].concat(), + ..self.config + }, + self.validation_context, + self.errors, + ), + Err((name, errors)) => Self::transition( + self.config, + self.validation_context, + merge_errors_vecs( + self.errors, + errors + .into_iter() + .map(|error| ConfigError::Collator(name.clone(), error).into()) + .collect::>(), + ), + ), + } + } - /// Seals the builder and returns a [`TeyrchainConfig`] if there are no validation errors, else returns errors. - pub fn build(self) -> Result> { - if !self.errors.is_empty() { - return Err(self - .errors - .into_iter() - .map(|error| ConfigError::Teyrchain(self.config.id, error).into()) - .collect::>()); - } + /// Seals the builder and returns a [`TeyrchainConfig`] if there are no validation errors, else returns errors. + pub fn build(self) -> Result> { + if !self.errors.is_empty() { + return Err(self + .errors + .into_iter() + .map(|error| ConfigError::Teyrchain(self.config.id, error).into()) + .collect::>()); + } - Ok(self.config) - } + Ok(self.config) + } } #[cfg(test)] mod tests { - use super::*; - use crate::NetworkConfig; + use super::*; + use crate::NetworkConfig; - #[test] - fn teyrchain_config_builder_should_succeeds_and_returns_a_new_teyrchain_config() { - let teyrchain_config = TeyrchainConfigBuilder::new(Default::default()) - .with_id(1000) - .with_chain("mychainname") - .with_registration_strategy(RegistrationStrategy::UsingExtrinsic) - .onboard_as_teyrchain(false) - .with_initial_balance(100_000_042) - .with_default_image("myrepo:myimage") - .with_default_command("default_command") - .with_default_resources(|resources| { - resources - .with_limit_cpu("500M") - .with_limit_memory("1G") - .with_request_cpu("250M") - }) - .with_default_db_snapshot("https://www.urltomysnapshot.com/file.tgz") - .with_default_args(vec![("--arg1", "value1").into(), "--option2".into()]) - .with_genesis_wasm_path("https://www.backupsite.com/my/wasm/file.tgz") - .with_genesis_wasm_generator("generator_wasm") - .with_genesis_state_path("./path/to/genesis/state") - .with_genesis_state_generator( - "undying-collator export-genesis-state --pov-size=10000 --pvf-complexity=1", - ) - .with_chain_spec_path("./path/to/chain/spec.json") - .with_chain_spec_runtime("./path/to/runtime.wasm", Some("dev")) - .with_wasm_override("./path/to/override/runtime.wasm") - .with_raw_spec_override("./path/to/override/rawspec.json") - .cumulus_based(false) - .evm_based(false) - .with_raw_bootnodes_addresses(vec![ - "/ip4/10.41.122.55/tcp/45421", - "/ip4/51.144.222.10/tcp/2333", - ]) - .without_default_bootnodes() - .with_collator(|collator| { - collator - .with_name("collator1") - .with_command("command1") - .bootnode(true) - }) - .with_collator(|collator| { - collator - .with_name("collator2") - .with_command("command2") - .validator(true) - }) - .build() - .unwrap(); + #[test] + fn teyrchain_config_builder_should_succeeds_and_returns_a_new_teyrchain_config() { + let teyrchain_config = TeyrchainConfigBuilder::new(Default::default()) + .with_id(1000) + .with_chain("mychainname") + .with_registration_strategy(RegistrationStrategy::UsingExtrinsic) + .onboard_as_teyrchain(false) + .with_initial_balance(100_000_042) + .with_default_image("myrepo:myimage") + .with_default_command("default_command") + .with_default_resources(|resources| { + resources.with_limit_cpu("500M").with_limit_memory("1G").with_request_cpu("250M") + }) + .with_default_db_snapshot("https://www.urltomysnapshot.com/file.tgz") + .with_default_args(vec![("--arg1", "value1").into(), "--option2".into()]) + .with_genesis_wasm_path("https://www.backupsite.com/my/wasm/file.tgz") + .with_genesis_wasm_generator("generator_wasm") + .with_genesis_state_path("./path/to/genesis/state") + .with_genesis_state_generator( + "undying-collator export-genesis-state --pov-size=10000 --pvf-complexity=1", + ) + .with_chain_spec_path("./path/to/chain/spec.json") + .with_chain_spec_runtime("./path/to/runtime.wasm", Some("dev")) + .with_wasm_override("./path/to/override/runtime.wasm") + .with_raw_spec_override("./path/to/override/rawspec.json") + .cumulus_based(false) + .evm_based(false) + .with_raw_bootnodes_addresses(vec![ + "/ip4/10.41.122.55/tcp/45421", + "/ip4/51.144.222.10/tcp/2333", + ]) + .without_default_bootnodes() + .with_collator(|collator| { + collator.with_name("collator1").with_command("command1").bootnode(true) + }) + .with_collator(|collator| { + collator.with_name("collator2").with_command("command2").validator(true) + }) + .build() + .unwrap(); - assert_eq!(teyrchain_config.id(), 1000); - assert_eq!(teyrchain_config.collators().len(), 2); - let &collator1 = teyrchain_config.collators().first().unwrap(); - assert_eq!(collator1.name(), "collator1"); - assert_eq!(collator1.command().unwrap().as_str(), "command1"); - assert!(collator1.is_bootnode()); - let &collator2 = teyrchain_config.collators().last().unwrap(); - assert_eq!(collator2.name(), "collator2"); - assert_eq!(collator2.command().unwrap().as_str(), "command2"); - assert!(collator2.is_validator()); - assert_eq!(teyrchain_config.chain().unwrap().as_str(), "mychainname"); + assert_eq!(teyrchain_config.id(), 1000); + assert_eq!(teyrchain_config.collators().len(), 2); + let &collator1 = teyrchain_config.collators().first().unwrap(); + assert_eq!(collator1.name(), "collator1"); + assert_eq!(collator1.command().unwrap().as_str(), "command1"); + assert!(collator1.is_bootnode()); + let &collator2 = teyrchain_config.collators().last().unwrap(); + assert_eq!(collator2.name(), "collator2"); + assert_eq!(collator2.command().unwrap().as_str(), "command2"); + assert!(collator2.is_validator()); + assert_eq!(teyrchain_config.chain().unwrap().as_str(), "mychainname"); - assert_eq!( - teyrchain_config.registration_strategy().unwrap(), - &RegistrationStrategy::UsingExtrinsic - ); - assert!(!teyrchain_config.onboard_as_teyrchain()); - assert_eq!(teyrchain_config.initial_balance(), 100_000_042); - assert_eq!( - teyrchain_config.default_command().unwrap().as_str(), - "default_command" - ); - assert_eq!( - teyrchain_config.default_image().unwrap().as_str(), - "myrepo:myimage" - ); - let default_resources = teyrchain_config.default_resources().unwrap(); - assert_eq!(default_resources.limit_cpu().unwrap().as_str(), "500M"); - assert_eq!(default_resources.limit_memory().unwrap().as_str(), "1G"); - assert_eq!(default_resources.request_cpu().unwrap().as_str(), "250M"); - assert!(matches!( - teyrchain_config.default_db_snapshot().unwrap(), - AssetLocation::Url(value) if value.as_str() == "https://www.urltomysnapshot.com/file.tgz", - )); - assert!(matches!( - teyrchain_config.chain_spec_path().unwrap(), - AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/chain/spec.json" - )); - assert!(matches!( - teyrchain_config.wasm_override().unwrap(), - AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/override/runtime.wasm" - )); - assert!(matches!( - &teyrchain_config.chain_spec_runtime().unwrap().location, - AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/runtime.wasm" - )); - assert_eq!( - teyrchain_config - .chain_spec_runtime() - .unwrap() - .preset - .as_deref(), - Some("dev") - ); + assert_eq!( + teyrchain_config.registration_strategy().unwrap(), + &RegistrationStrategy::UsingExtrinsic + ); + assert!(!teyrchain_config.onboard_as_teyrchain()); + assert_eq!(teyrchain_config.initial_balance(), 100_000_042); + assert_eq!(teyrchain_config.default_command().unwrap().as_str(), "default_command"); + assert_eq!(teyrchain_config.default_image().unwrap().as_str(), "myrepo:myimage"); + let default_resources = teyrchain_config.default_resources().unwrap(); + assert_eq!(default_resources.limit_cpu().unwrap().as_str(), "500M"); + assert_eq!(default_resources.limit_memory().unwrap().as_str(), "1G"); + assert_eq!(default_resources.request_cpu().unwrap().as_str(), "250M"); + assert!(matches!( + teyrchain_config.default_db_snapshot().unwrap(), + AssetLocation::Url(value) if value.as_str() == "https://www.urltomysnapshot.com/file.tgz", + )); + assert!(matches!( + teyrchain_config.chain_spec_path().unwrap(), + AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/chain/spec.json" + )); + assert!(matches!( + teyrchain_config.wasm_override().unwrap(), + AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/override/runtime.wasm" + )); + assert!(matches!( + &teyrchain_config.chain_spec_runtime().unwrap().location, + AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/runtime.wasm" + )); + assert_eq!(teyrchain_config.chain_spec_runtime().unwrap().preset.as_deref(), Some("dev")); - let args: Vec = vec![("--arg1", "value1").into(), "--option2".into()]; - assert_eq!( - teyrchain_config.default_args(), - args.iter().collect::>() - ); - assert!(matches!( - teyrchain_config.genesis_wasm_path().unwrap(), - AssetLocation::Url(value) if value.as_str() == "https://www.backupsite.com/my/wasm/file.tgz" - )); - assert_eq!( - teyrchain_config.genesis_wasm_generator().unwrap().as_str(), - "generator_wasm" - ); - assert!(matches!( - teyrchain_config.genesis_state_path().unwrap(), - AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/genesis/state" - )); - assert_eq!( - teyrchain_config - .genesis_state_generator() - .unwrap() - .cmd() - .as_str(), - "undying-collator" - ); + let args: Vec = vec![("--arg1", "value1").into(), "--option2".into()]; + assert_eq!(teyrchain_config.default_args(), args.iter().collect::>()); + assert!(matches!( + teyrchain_config.genesis_wasm_path().unwrap(), + AssetLocation::Url(value) if value.as_str() == "https://www.backupsite.com/my/wasm/file.tgz" + )); + assert_eq!(teyrchain_config.genesis_wasm_generator().unwrap().as_str(), "generator_wasm"); + assert!(matches!( + teyrchain_config.genesis_state_path().unwrap(), + AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/genesis/state" + )); + assert_eq!( + teyrchain_config.genesis_state_generator().unwrap().cmd().as_str(), + "undying-collator" + ); - assert_eq!( - teyrchain_config.genesis_state_generator().unwrap().args(), - &vec![ - "export-genesis-state".into(), - ("--pov-size", "10000").into(), - ("--pvf-complexity", "1").into() - ] - ); + assert_eq!( + teyrchain_config.genesis_state_generator().unwrap().args(), + &vec![ + "export-genesis-state".into(), + ("--pov-size", "10000").into(), + ("--pvf-complexity", "1").into() + ] + ); - assert!(matches!( - teyrchain_config.chain_spec_path().unwrap(), - AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/chain/spec.json" - )); - assert!(!teyrchain_config.is_cumulus_based()); - let bootnodes_addresses: Vec = vec![ - "/ip4/10.41.122.55/tcp/45421".try_into().unwrap(), - "/ip4/51.144.222.10/tcp/2333".try_into().unwrap(), - ]; - assert!(teyrchain_config.no_default_bootnodes()); - assert_eq!( - teyrchain_config.bootnodes_addresses(), - bootnodes_addresses.iter().collect::>() - ); - assert!(!teyrchain_config.is_evm_based()); - assert!(matches!( - teyrchain_config.raw_spec_override().unwrap(), - JsonOverrides::Location(AssetLocation::FilePath(value)) if value.to_str().unwrap() == "./path/to/override/rawspec.json" - )); - } + assert!(matches!( + teyrchain_config.chain_spec_path().unwrap(), + AssetLocation::FilePath(value) if value.to_str().unwrap() == "./path/to/chain/spec.json" + )); + assert!(!teyrchain_config.is_cumulus_based()); + let bootnodes_addresses: Vec = vec![ + "/ip4/10.41.122.55/tcp/45421".try_into().unwrap(), + "/ip4/51.144.222.10/tcp/2333".try_into().unwrap(), + ]; + assert!(teyrchain_config.no_default_bootnodes()); + assert_eq!( + teyrchain_config.bootnodes_addresses(), + bootnodes_addresses.iter().collect::>() + ); + assert!(!teyrchain_config.is_evm_based()); + assert!(matches!( + teyrchain_config.raw_spec_override().unwrap(), + JsonOverrides::Location(AssetLocation::FilePath(value)) if value.to_str().unwrap() == "./path/to/override/rawspec.json" + )); + } - #[test] - fn teyrchain_config_builder_should_works_when_genesis_state_generator_contains_args() { - let teyrchain_config = TeyrchainConfigBuilder::new(Default::default()) - .with_id(1000) - .with_chain("myteyrchain") - .with_genesis_state_generator("generator_state --simple-flag --flag=value") - .with_collator(|collator| { - collator - .with_name("collator") - .with_command("command") - .validator(true) - }) - .build() - .unwrap(); + #[test] + fn teyrchain_config_builder_should_works_when_genesis_state_generator_contains_args() { + let teyrchain_config = TeyrchainConfigBuilder::new(Default::default()) + .with_id(1000) + .with_chain("myteyrchain") + .with_genesis_state_generator("generator_state --simple-flag --flag=value") + .with_collator(|collator| { + collator.with_name("collator").with_command("command").validator(true) + }) + .build() + .unwrap(); - assert_eq!( - teyrchain_config - .genesis_state_generator() - .unwrap() - .cmd() - .as_str(), - "generator_state" - ); + assert_eq!( + teyrchain_config.genesis_state_generator().unwrap().cmd().as_str(), + "generator_state" + ); - assert_eq!( - teyrchain_config - .genesis_state_generator() - .unwrap() - .args() - .len(), - 2 - ); + assert_eq!(teyrchain_config.genesis_state_generator().unwrap().args().len(), 2); - let args = teyrchain_config.genesis_state_generator().unwrap().args(); + let args = teyrchain_config.genesis_state_generator().unwrap().args(); - assert_eq!( - args, - &vec![ - Arg::Flag("--simple-flag".into()), - Arg::Option("--flag".into(), "value".into()) - ] - ); - } + assert_eq!( + args, + &vec![Arg::Flag("--simple-flag".into()), Arg::Option("--flag".into(), "value".into())] + ); + } - #[test] - fn teyrchain_config_builder_should_fails_and_returns_an_error_if_chain_is_invalid() { - let errors = TeyrchainConfigBuilder::new(Default::default()) - .with_id(1000) - .with_chain("invalid chain") - .with_collator(|collator| { - collator - .with_name("collator") - .with_command("command") - .validator(true) - }) - .build() - .unwrap_err(); + #[test] + fn teyrchain_config_builder_should_fails_and_returns_an_error_if_chain_is_invalid() { + let errors = TeyrchainConfigBuilder::new(Default::default()) + .with_id(1000) + .with_chain("invalid chain") + .with_collator(|collator| { + collator.with_name("collator").with_command("command").validator(true) + }) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - "teyrchain[1000].chain: 'invalid chain' shouldn't contains whitespace" - ); - } + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + "teyrchain[1000].chain: 'invalid chain' shouldn't contains whitespace" + ); + } - #[test] - fn teyrchain_config_builder_should_fails_and_returns_an_error_if_default_command_is_invalid() { - let errors = TeyrchainConfigBuilder::new(Default::default()) - .with_id(1000) - .with_chain("chain") - .with_default_command("invalid command") - .with_collator(|collator| { - collator - .with_name("node") - .with_command("command") - .validator(true) - }) - .build() - .unwrap_err(); + #[test] + fn teyrchain_config_builder_should_fails_and_returns_an_error_if_default_command_is_invalid() { + let errors = TeyrchainConfigBuilder::new(Default::default()) + .with_id(1000) + .with_chain("chain") + .with_default_command("invalid command") + .with_collator(|collator| { + collator.with_name("node").with_command("command").validator(true) + }) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - "teyrchain[1000].default_command: 'invalid command' shouldn't contains whitespace" - ); - } + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + "teyrchain[1000].default_command: 'invalid command' shouldn't contains whitespace" + ); + } - #[test] - fn teyrchain_config_builder_should_fails_and_returns_an_error_if_default_image_is_invalid() { - let errors = TeyrchainConfigBuilder::new(Default::default()) - .with_id(1000) - .with_chain("chain") - .with_default_image("invalid image") - .with_collator(|collator| { - collator - .with_name("node") - .with_command("command") - .validator(true) - }) - .build() - .unwrap_err(); + #[test] + fn teyrchain_config_builder_should_fails_and_returns_an_error_if_default_image_is_invalid() { + let errors = TeyrchainConfigBuilder::new(Default::default()) + .with_id(1000) + .with_chain("chain") + .with_default_image("invalid image") + .with_collator(|collator| { + collator.with_name("node").with_command("command").validator(true) + }) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - r"teyrchain[1000].default_image: 'invalid image' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'" - ); - } + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + r"teyrchain[1000].default_image: 'invalid image' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'" + ); + } - #[test] - fn teyrchain_config_builder_should_fails_and_returns_an_error_if_default_resources_are_invalid() - { - let errors = TeyrchainConfigBuilder::new(Default::default()) - .with_id(1000) - .with_chain("chain") - .with_default_resources(|default_resources| { - default_resources - .with_limit_memory("100m") - .with_request_cpu("invalid") - }) - .with_collator(|collator| { - collator - .with_name("node") - .with_command("command") - .validator(true) - }) - .build() - .unwrap_err(); + #[test] + fn teyrchain_config_builder_should_fails_and_returns_an_error_if_default_resources_are_invalid() + { + let errors = TeyrchainConfigBuilder::new(Default::default()) + .with_id(1000) + .with_chain("chain") + .with_default_resources(|default_resources| { + default_resources.with_limit_memory("100m").with_request_cpu("invalid") + }) + .with_collator(|collator| { + collator.with_name("node").with_command("command").validator(true) + }) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - r"teyrchain[1000].default_resources.request_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" - ); - } + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + r"teyrchain[1000].default_resources.request_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'" + ); + } - #[test] - fn teyrchain_config_builder_should_fails_and_returns_an_error_if_genesis_wasm_generator_is_invalid( - ) { - let errors = TeyrchainConfigBuilder::new(Default::default()) - .with_id(2000) - .with_chain("myteyrchain") - .with_genesis_wasm_generator("invalid command") - .with_collator(|collator| { - collator - .with_name("collator") - .with_command("command") - .validator(true) - }) - .build() - .unwrap_err(); + #[test] + fn teyrchain_config_builder_should_fails_and_returns_an_error_if_genesis_wasm_generator_is_invalid( + ) { + let errors = TeyrchainConfigBuilder::new(Default::default()) + .with_id(2000) + .with_chain("myteyrchain") + .with_genesis_wasm_generator("invalid command") + .with_collator(|collator| { + collator.with_name("collator").with_command("command").validator(true) + }) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 1); - assert_eq!( + assert_eq!(errors.len(), 1); + assert_eq!( errors.first().unwrap().to_string(), "teyrchain[2000].genesis_wasm_generator: 'invalid command' shouldn't contains whitespace" ); - } + } - #[test] - fn teyrchain_config_builder_should_fails_and_returns_an_error_if_bootnodes_addresses_are_invalid( - ) { - let errors = TeyrchainConfigBuilder::new(Default::default()) - .with_id(2000) - .with_chain("myteyrchain") - .with_raw_bootnodes_addresses(vec!["/ip4//tcp/45421", "//10.42.153.10/tcp/43111"]) - .with_collator(|collator| { - collator - .with_name("collator") - .with_command("command") - .validator(true) - }) - .build() - .unwrap_err(); + #[test] + fn teyrchain_config_builder_should_fails_and_returns_an_error_if_bootnodes_addresses_are_invalid( + ) { + let errors = TeyrchainConfigBuilder::new(Default::default()) + .with_id(2000) + .with_chain("myteyrchain") + .with_raw_bootnodes_addresses(vec!["/ip4//tcp/45421", "//10.42.153.10/tcp/43111"]) + .with_collator(|collator| { + collator.with_name("collator").with_command("command").validator(true) + }) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 2); - assert_eq!( + assert_eq!(errors.len(), 2); + assert_eq!( errors.first().unwrap().to_string(), "teyrchain[2000].bootnodes_addresses[0]: '/ip4//tcp/45421' failed to parse: invalid IPv4 address syntax" ); - assert_eq!( + assert_eq!( errors.get(1).unwrap().to_string(), "teyrchain[2000].bootnodes_addresses[1]: '//10.42.153.10/tcp/43111' unknown protocol string: " ); - } + } - #[test] - fn teyrchain_config_builder_should_fails_and_returns_an_error_if_first_collator_is_invalid() { - let errors = TeyrchainConfigBuilder::new(Default::default()) - .with_id(1000) - .with_chain("myteyrchain") - .with_collator(|collator| { - collator - .with_name("collator") - .with_command("invalid command") - }) - .build() - .unwrap_err(); + #[test] + fn teyrchain_config_builder_should_fails_and_returns_an_error_if_first_collator_is_invalid() { + let errors = TeyrchainConfigBuilder::new(Default::default()) + .with_id(1000) + .with_chain("myteyrchain") + .with_collator(|collator| { + collator.with_name("collator").with_command("invalid command") + }) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 1); - assert_eq!( + assert_eq!(errors.len(), 1); + assert_eq!( errors.first().unwrap().to_string(), "teyrchain[1000].collators['collator'].command: 'invalid command' shouldn't contains whitespace" ); - } + } - #[test] - fn teyrchain_config_builder_with_at_least_one_collator_should_fails_and_returns_an_error_if_second_collator_is_invalid( - ) { - let errors = TeyrchainConfigBuilder::new(Default::default()) - .with_id(2000) - .with_chain("myteyrchain") - .with_collator(|collator| { - collator - .with_name("collator1") - .with_command("command1") - .invulnerable(true) - .bootnode(true) - }) - .with_collator(|collator| { - collator - .with_name("collator2") - .with_command("invalid command") - .with_initial_balance(20000000) - }) - .build() - .unwrap_err(); + #[test] + fn teyrchain_config_builder_with_at_least_one_collator_should_fails_and_returns_an_error_if_second_collator_is_invalid( + ) { + let errors = TeyrchainConfigBuilder::new(Default::default()) + .with_id(2000) + .with_chain("myteyrchain") + .with_collator(|collator| { + collator + .with_name("collator1") + .with_command("command1") + .invulnerable(true) + .bootnode(true) + }) + .with_collator(|collator| { + collator + .with_name("collator2") + .with_command("invalid command") + .with_initial_balance(20000000) + }) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 1); - assert_eq!( + assert_eq!(errors.len(), 1); + assert_eq!( errors.first().unwrap().to_string(), "teyrchain[2000].collators['collator2'].command: 'invalid command' shouldn't contains whitespace" ); - } + } - #[test] - fn teyrchain_config_builder_should_fails_and_returns_multiple_errors_if_multiple_fields_are_invalid( - ) { - let errors = TeyrchainConfigBuilder::new(Default::default()) - .with_id(2000) - .with_chain("myteyrchain") - .with_raw_bootnodes_addresses(vec!["/ip4//tcp/45421", "//10.42.153.10/tcp/43111"]) - .with_collator(|collator| { - collator - .with_name("collator1") - .with_command("invalid command") - .invulnerable(true) - .bootnode(true) - .with_resources(|resources| { - resources - .with_limit_cpu("invalid") - .with_request_memory("1G") - }) - }) - .with_collator(|collator| { - collator - .with_name("collator2") - .with_command("command2") - .with_image("invalid.image") - .with_initial_balance(20000000) - }) - .build() - .unwrap_err(); + #[test] + fn teyrchain_config_builder_should_fails_and_returns_multiple_errors_if_multiple_fields_are_invalid( + ) { + let errors = TeyrchainConfigBuilder::new(Default::default()) + .with_id(2000) + .with_chain("myteyrchain") + .with_raw_bootnodes_addresses(vec!["/ip4//tcp/45421", "//10.42.153.10/tcp/43111"]) + .with_collator(|collator| { + collator + .with_name("collator1") + .with_command("invalid command") + .invulnerable(true) + .bootnode(true) + .with_resources(|resources| { + resources.with_limit_cpu("invalid").with_request_memory("1G") + }) + }) + .with_collator(|collator| { + collator + .with_name("collator2") + .with_command("command2") + .with_image("invalid.image") + .with_initial_balance(20000000) + }) + .build() + .unwrap_err(); - assert_eq!(errors.len(), 5); - assert_eq!( + assert_eq!(errors.len(), 5); + assert_eq!( errors.first().unwrap().to_string(), "teyrchain[2000].bootnodes_addresses[0]: '/ip4//tcp/45421' failed to parse: invalid IPv4 address syntax" ); - assert_eq!( + assert_eq!( errors.get(1).unwrap().to_string(), "teyrchain[2000].bootnodes_addresses[1]: '//10.42.153.10/tcp/43111' unknown protocol string: " ); - assert_eq!( + assert_eq!( errors.get(2).unwrap().to_string(), "teyrchain[2000].collators['collator1'].command: 'invalid command' shouldn't contains whitespace" ); - assert_eq!( - errors.get(3).unwrap().to_string(), - r"teyrchain[2000].collators['collator1'].resources.limit_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'", - ); - assert_eq!( + assert_eq!( + errors.get(3).unwrap().to_string(), + r"teyrchain[2000].collators['collator1'].resources.limit_cpu: 'invalid' doesn't match regex '^\d+(.\d+)?(m|K|M|G|T|P|E|Ki|Mi|Gi|Ti|Pi|Ei)?$'", + ); + assert_eq!( errors.get(4).unwrap().to_string(), "teyrchain[2000].collators['collator2'].image: 'invalid.image' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'" ); - } + } - #[test] - fn import_toml_registration_strategy_should_deserialize() { - let load_from_toml = - NetworkConfig::load_from_toml("./testing/snapshots/0001-big-network.toml").unwrap(); + #[test] + fn import_toml_registration_strategy_should_deserialize() { + let load_from_toml = + NetworkConfig::load_from_toml("./testing/snapshots/0001-big-network.toml").unwrap(); - for teyrchain in load_from_toml.teyrchains().iter() { - if teyrchain.id() == 1000 { - assert_eq!( - teyrchain.registration_strategy(), - Some(&RegistrationStrategy::UsingExtrinsic) - ); - } - if teyrchain.id() == 2000 { - assert_eq!( - teyrchain.registration_strategy(), - Some(&RegistrationStrategy::InGenesis) - ); - } - } + for teyrchain in load_from_toml.teyrchains().iter() { + if teyrchain.id() == 1000 { + assert_eq!( + teyrchain.registration_strategy(), + Some(&RegistrationStrategy::UsingExtrinsic) + ); + } + if teyrchain.id() == 2000 { + assert_eq!( + teyrchain.registration_strategy(), + Some(&RegistrationStrategy::InGenesis) + ); + } + } - let load_from_toml_small = NetworkConfig::load_from_toml( - "./testing/snapshots/0003-small-network_w_teyrchain.toml", - ) - .unwrap(); + let load_from_toml_small = NetworkConfig::load_from_toml( + "./testing/snapshots/0003-small-network_w_teyrchain.toml", + ) + .unwrap(); - let teyrchain = load_from_toml_small.teyrchains()[0]; - let teyrchain_evm = load_from_toml_small.teyrchains()[1]; + let teyrchain = load_from_toml_small.teyrchains()[0]; + let teyrchain_evm = load_from_toml_small.teyrchains()[1]; - assert_eq!(teyrchain.registration_strategy(), None); - assert!(!teyrchain.is_evm_based()); - assert_eq!(teyrchain.collators().len(), 1); - assert!(teyrchain_evm.is_evm_based()); - } + assert_eq!(teyrchain.registration_strategy(), None); + assert!(!teyrchain.is_evm_based()); + assert_eq!(teyrchain.collators().len(), 1); + assert!(teyrchain_evm.is_evm_based()); + } - #[test] - fn onboard_as_teyrchain_should_default_to_true() { - let config = TeyrchainConfigBuilder::new(Default::default()) - .with_id(2000) - .with_chain("myteyrchain") - .with_collator(|collator| collator.with_name("collator")) - .build() - .unwrap(); + #[test] + fn onboard_as_teyrchain_should_default_to_true() { + let config = TeyrchainConfigBuilder::new(Default::default()) + .with_id(2000) + .with_chain("myteyrchain") + .with_collator(|collator| collator.with_name("collator")) + .build() + .unwrap(); - assert!(config.onboard_as_teyrchain()); - } + assert!(config.onboard_as_teyrchain()); + } - #[test] - fn evm_based_default_to_false() { - let config = TeyrchainConfigBuilder::new(Default::default()) - .with_id(2000) - .with_chain("myteyrchain") - .with_collator(|collator| collator.with_name("collator")) - .build() - .unwrap(); + #[test] + fn evm_based_default_to_false() { + let config = TeyrchainConfigBuilder::new(Default::default()) + .with_id(2000) + .with_chain("myteyrchain") + .with_collator(|collator| collator.with_name("collator")) + .build() + .unwrap(); - assert!(!config.is_evm_based()); - } + assert!(!config.is_evm_based()); + } - #[test] - fn evm_based() { - let config = TeyrchainConfigBuilder::new(Default::default()) - .with_id(2000) - .with_chain("myteyrchain") - .evm_based(true) - .with_collator(|collator| collator.with_name("collator")) - .build() - .unwrap(); + #[test] + fn evm_based() { + let config = TeyrchainConfigBuilder::new(Default::default()) + .with_id(2000) + .with_chain("myteyrchain") + .evm_based(true) + .with_collator(|collator| collator.with_name("collator")) + .build() + .unwrap(); - assert!(config.is_evm_based()); - } + assert!(config.is_evm_based()); + } - #[test] - fn build_config_in_running_context() { - let config = TeyrchainConfigBuilder::new_with_running(Default::default()) - .with_id(2000) - .with_chain("myteyrchain") - .with_collator(|collator| collator.with_name("collator")) - .build() - .unwrap(); + #[test] + fn build_config_in_running_context() { + let config = TeyrchainConfigBuilder::new_with_running(Default::default()) + .with_id(2000) + .with_chain("myteyrchain") + .with_collator(|collator| collator.with_name("collator")) + .build() + .unwrap(); - assert_eq!( - config.registration_strategy(), - Some(&RegistrationStrategy::UsingExtrinsic) - ); - } + assert_eq!(config.registration_strategy(), Some(&RegistrationStrategy::UsingExtrinsic)); + } - #[test] - fn teyrchain_config_builder_should_works_with_chain_spec_command() { - const CMD_TPL: &str = "./bin/chain-spec-generator {% raw %} {{chainName}} {% endraw %}"; - let config = TeyrchainConfigBuilder::new(Default::default()) - .with_id(2000) - .with_chain("some-chain") - .with_default_image("myrepo:myimage") - .with_default_command("default_command") - .with_chain_spec_command(CMD_TPL) - .with_collator(|collator| collator.with_name("collator")) - .build() - .unwrap(); + #[test] + fn teyrchain_config_builder_should_works_with_chain_spec_command() { + const CMD_TPL: &str = "./bin/chain-spec-generator {% raw %} {{chainName}} {% endraw %}"; + let config = TeyrchainConfigBuilder::new(Default::default()) + .with_id(2000) + .with_chain("some-chain") + .with_default_image("myrepo:myimage") + .with_default_command("default_command") + .with_chain_spec_command(CMD_TPL) + .with_collator(|collator| collator.with_name("collator")) + .build() + .unwrap(); - assert_eq!(config.chain_spec_command(), Some(CMD_TPL)); - assert!(!config.chain_spec_command_is_local()); - } + assert_eq!(config.chain_spec_command(), Some(CMD_TPL)); + assert!(!config.chain_spec_command_is_local()); + } - #[test] - fn teyrchain_config_builder_should_works_with_chain_spec_command_and_local() { - const CMD_TPL: &str = "./bin/chain-spec-generator {% raw %} {{chainName}} {% endraw %}"; - let config = TeyrchainConfigBuilder::new(Default::default()) - .with_id(2000) - .with_chain("some-chain") - .with_default_image("myrepo:myimage") - .with_default_command("default_command") - .with_chain_spec_command(CMD_TPL) - .chain_spec_command_is_local(true) - .with_collator(|collator| collator.with_name("collator")) - .build() - .unwrap(); + #[test] + fn teyrchain_config_builder_should_works_with_chain_spec_command_and_local() { + const CMD_TPL: &str = "./bin/chain-spec-generator {% raw %} {{chainName}} {% endraw %}"; + let config = TeyrchainConfigBuilder::new(Default::default()) + .with_id(2000) + .with_chain("some-chain") + .with_default_image("myrepo:myimage") + .with_default_command("default_command") + .with_chain_spec_command(CMD_TPL) + .chain_spec_command_is_local(true) + .with_collator(|collator| collator.with_name("collator")) + .build() + .unwrap(); - assert_eq!(config.chain_spec_command(), Some(CMD_TPL)); - assert!(config.chain_spec_command_is_local()); - } + assert_eq!(config.chain_spec_command(), Some(CMD_TPL)); + assert!(config.chain_spec_command_is_local()); + } - #[test] - fn teyrchain_with_group_config_builder_should_succeeds_and_returns_a_new_teyrchain_config() { - let teyrchain_config = TeyrchainConfigBuilder::new(Default::default()) - .with_id(1000) - .with_chain("mychainname") - .with_registration_strategy(RegistrationStrategy::UsingExtrinsic) - .onboard_as_teyrchain(false) - .with_initial_balance(100_000_042) - .with_default_image("myrepo:myimage") - .with_default_command("default_command") - .without_default_bootnodes() - .with_collator(|collator| { - collator - .with_name("collator1") - .with_command("command1") - .bootnode(true) - }) - .with_collator_group(|group| { - group.with_count(2).with_base_node(|base| { - base.with_name("collator_group1") - .with_command("group_command1") - .bootnode(true) - }) - }) - .with_collator_group(|group| { - group.with_count(3).with_base_node(|base| { - base.with_name("collator_group2") - .with_command("group_command2") - .bootnode(false) - }) - }) - .build() - .unwrap(); + #[test] + fn teyrchain_with_group_config_builder_should_succeeds_and_returns_a_new_teyrchain_config() { + let teyrchain_config = TeyrchainConfigBuilder::new(Default::default()) + .with_id(1000) + .with_chain("mychainname") + .with_registration_strategy(RegistrationStrategy::UsingExtrinsic) + .onboard_as_teyrchain(false) + .with_initial_balance(100_000_042) + .with_default_image("myrepo:myimage") + .with_default_command("default_command") + .without_default_bootnodes() + .with_collator(|collator| { + collator.with_name("collator1").with_command("command1").bootnode(true) + }) + .with_collator_group(|group| { + group.with_count(2).with_base_node(|base| { + base.with_name("collator_group1").with_command("group_command1").bootnode(true) + }) + }) + .with_collator_group(|group| { + group.with_count(3).with_base_node(|base| { + base.with_name("collator_group2").with_command("group_command2").bootnode(false) + }) + }) + .build() + .unwrap(); - assert_eq!(teyrchain_config.id(), 1000); - assert_eq!(teyrchain_config.collators().len(), 1); - assert_eq!(teyrchain_config.group_collators_configs().len(), 2); + assert_eq!(teyrchain_config.id(), 1000); + assert_eq!(teyrchain_config.collators().len(), 1); + assert_eq!(teyrchain_config.group_collators_configs().len(), 2); - let group_collator1 = teyrchain_config.group_collators_configs()[0].clone(); - assert_eq!(group_collator1.count, 2); - let base_config1 = group_collator1.base_config; - assert_eq!(base_config1.name(), "collator_group1"); - assert_eq!(base_config1.command().unwrap().as_str(), "group_command1"); - assert!(base_config1.is_bootnode()); + let group_collator1 = teyrchain_config.group_collators_configs()[0].clone(); + assert_eq!(group_collator1.count, 2); + let base_config1 = group_collator1.base_config; + assert_eq!(base_config1.name(), "collator_group1"); + assert_eq!(base_config1.command().unwrap().as_str(), "group_command1"); + assert!(base_config1.is_bootnode()); - let group_collator2 = teyrchain_config.group_collators_configs()[1].clone(); - assert_eq!(group_collator2.count, 3); - let base_config2 = group_collator2.base_config; - assert_eq!(base_config2.name(), "collator_group2"); - assert_eq!(base_config2.command().unwrap().as_str(), "group_command2"); - assert!(!base_config2.is_bootnode()); - } + let group_collator2 = teyrchain_config.group_collators_configs()[1].clone(); + assert_eq!(group_collator2.count, 3); + let base_config2 = group_collator2.base_config; + assert_eq!(base_config2.name(), "collator_group2"); + assert_eq!(base_config2.command().unwrap().as_str(), "group_command2"); + assert!(!base_config2.is_bootnode()); + } - #[test] - fn teyrchain_with_group_count_0_config_builder_should_fail() { - let teyrchain_config = TeyrchainConfigBuilder::new(Default::default()) - .with_id(1000) - .with_chain("mychainname") - .with_registration_strategy(RegistrationStrategy::UsingExtrinsic) - .onboard_as_teyrchain(false) - .with_initial_balance(100_000_042) - .with_default_image("myrepo:myimage") - .with_default_command("default_command") - .without_default_bootnodes() - .with_collator(|collator| { - collator - .with_name("collator1") - .with_command("command1") - .bootnode(true) - }) - .with_collator_group(|group| { - group.with_count(2).with_base_node(|base| { - base.with_name("collator_group1") - .with_command("group_command1") - .bootnode(true) - }) - }) - .with_collator_group(|group| { - group.with_count(0).with_base_node(|base| { - base.with_name("collator_group2") - .with_command("group_command2") - .bootnode(false) - }) - }) - .build(); + #[test] + fn teyrchain_with_group_count_0_config_builder_should_fail() { + let teyrchain_config = TeyrchainConfigBuilder::new(Default::default()) + .with_id(1000) + .with_chain("mychainname") + .with_registration_strategy(RegistrationStrategy::UsingExtrinsic) + .onboard_as_teyrchain(false) + .with_initial_balance(100_000_042) + .with_default_image("myrepo:myimage") + .with_default_command("default_command") + .without_default_bootnodes() + .with_collator(|collator| { + collator.with_name("collator1").with_command("command1").bootnode(true) + }) + .with_collator_group(|group| { + group.with_count(2).with_base_node(|base| { + base.with_name("collator_group1").with_command("group_command1").bootnode(true) + }) + }) + .with_collator_group(|group| { + group.with_count(0).with_base_node(|base| { + base.with_name("collator_group2").with_command("group_command2").bootnode(false) + }) + }) + .build(); - let errors: Vec = match teyrchain_config { - Ok(_) => vec![], - Err(errs) => errs, - }; + let errors: Vec = match teyrchain_config { + Ok(_) => vec![], + Err(errs) => errs, + }; - assert_eq!(errors.len(), 1); - assert_eq!( - errors.first().unwrap().to_string(), - "teyrchain[1000].collators['collator_group2'].Count cannot be zero" - ); - } + assert_eq!(errors.len(), 1); + assert_eq!( + errors.first().unwrap().to_string(), + "teyrchain[1000].collators['collator_group2'].Count cannot be zero" + ); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/utils.rs b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/utils.rs index 676c4738..c1bf297c 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/utils.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/configuration/src/utils.rs @@ -5,61 +5,61 @@ use support::constants::ZOMBIE_NODE_SPAWN_TIMEOUT_SECONDS; use crate::types::{Chain, Command, Duration}; pub(crate) fn is_true(value: &bool) -> bool { - *value + *value } pub(crate) fn is_false(value: &bool) -> bool { - !(*value) + !(*value) } pub(crate) fn default_as_true() -> bool { - true + true } pub(crate) fn default_as_false() -> bool { - false + false } pub(crate) fn default_initial_balance() -> crate::types::U128 { - 2_000_000_000_000.into() + 2_000_000_000_000.into() } /// Default timeout for spawning a node (10mins) pub(crate) fn default_node_spawn_timeout() -> Duration { - env::var(ZOMBIE_NODE_SPAWN_TIMEOUT_SECONDS) - .ok() - .and_then(|s| s.parse::().ok()) - .unwrap_or(600) + env::var(ZOMBIE_NODE_SPAWN_TIMEOUT_SECONDS) + .ok() + .and_then(|s| s.parse::().ok()) + .unwrap_or(600) } /// Default timeout for spawning the whole network (1hr) pub(crate) fn default_timeout() -> Duration { - 3600 + 3600 } pub(crate) fn default_command_polkadot() -> Option { - TryInto::::try_into("polkadot").ok() + TryInto::::try_into("polkadot").ok() } pub(crate) fn default_relaychain_chain() -> Chain { - TryInto::::try_into("rococo-local").expect("'rococo-local' should be a valid chain") + TryInto::::try_into("rococo-local").expect("'rococo-local' should be a valid chain") } #[cfg(test)] mod tests { - use super::*; + use super::*; - #[test] - fn default_node_spawn_timeout_works_before_and_after_env_is_set() { - // The default should be 600 seconds if the env var is not set - assert_eq!(default_node_spawn_timeout(), 600); + #[test] + fn default_node_spawn_timeout_works_before_and_after_env_is_set() { + // The default should be 600 seconds if the env var is not set + assert_eq!(default_node_spawn_timeout(), 600); - // If env var is set to a valid number, it should return that number - env::set_var(ZOMBIE_NODE_SPAWN_TIMEOUT_SECONDS, "123"); - assert_eq!(default_node_spawn_timeout(), 123); + // If env var is set to a valid number, it should return that number + env::set_var(ZOMBIE_NODE_SPAWN_TIMEOUT_SECONDS, "123"); + assert_eq!(default_node_spawn_timeout(), 123); - // If env var is set to a NOT valid number, it should return 600 - env::set_var(ZOMBIE_NODE_SPAWN_TIMEOUT_SECONDS, "NOT_A_NUMBER"); - assert_eq!(default_node_spawn_timeout(), 600); - } + // If env var is set to a NOT valid number, it should return 600 + env::set_var(ZOMBIE_NODE_SPAWN_TIMEOUT_SECONDS, "NOT_A_NUMBER"); + assert_eq!(default_node_spawn_timeout(), 600); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/errors.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/errors.rs index 46f49c31..fa6deebe 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/errors.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/errors.rs @@ -7,25 +7,25 @@ use crate::generators; #[derive(Debug, thiserror::Error)] pub enum OrchestratorError { - // TODO: improve invalid config reporting - #[error("Invalid network configuration: {0}")] - InvalidConfig(String), - #[error("Invalid network config to use provider {0}: {1}")] - InvalidConfigForProvider(String, String), - #[error("Invalid configuration for node: {0}, field: {1}")] - InvalidNodeConfig(String, String), - #[error("Invariant not fulfilled {0}")] - InvariantError(&'static str), - #[error("Global network spawn timeout: {0} secs")] - GlobalTimeOut(u32), - #[error("Generator error: {0}")] - GeneratorError(#[from] generators::errors::GeneratorError), - #[error("Provider error")] - ProviderError(#[from] ProviderError), - #[error("FileSystem error")] - FileSystemError(#[from] FileSystemError), - #[error("Serialization error")] - SerializationError(#[from] serde_json::Error), - #[error(transparent)] - SpawnerError(#[from] anyhow::Error), + // TODO: improve invalid config reporting + #[error("Invalid network configuration: {0}")] + InvalidConfig(String), + #[error("Invalid network config to use provider {0}: {1}")] + InvalidConfigForProvider(String, String), + #[error("Invalid configuration for node: {0}, field: {1}")] + InvalidNodeConfig(String, String), + #[error("Invariant not fulfilled {0}")] + InvariantError(&'static str), + #[error("Global network spawn timeout: {0} secs")] + GlobalTimeOut(u32), + #[error("Generator error: {0}")] + GeneratorError(#[from] generators::errors::GeneratorError), + #[error("Provider error")] + ProviderError(#[from] ProviderError), + #[error("FileSystem error")] + FileSystemError(#[from] FileSystemError), + #[error("Serialization error")] + SerializationError(#[from] serde_json::Error), + #[error(transparent)] + SpawnerError(#[from] anyhow::Error), } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators.rs index e7913eab..8ffdaef6 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators.rs @@ -13,8 +13,8 @@ mod port; pub use bootnode_addr::generate as generate_node_bootnode_addr; pub use command::{ - generate_for_cumulus_node as generate_node_command_cumulus, - generate_for_node as generate_node_command, GenCmdOptions, + generate_for_cumulus_node as generate_node_command_cumulus, + generate_for_node as generate_node_command, GenCmdOptions, }; pub use identity::generate as generate_node_identity; pub use key::generate as generate_node_keys; diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/arg_filter.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/arg_filter.rs index ad619959..8afbc583 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/arg_filter.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/arg_filter.rs @@ -8,21 +8,21 @@ use configuration::types::Arg; /// - `-:insecure-validator` -> removes `--insecure-validator` (normalized) /// - `-:--prometheus-port` -> removes `--prometheus-port` pub fn parse_removal_args(args: &[Arg]) -> Vec { - args.iter() - .filter_map(|arg| match arg { - Arg::Flag(flag) if flag.starts_with("-:") => { - let mut flag_to_exclude = flag[2..].to_string(); + args.iter() + .filter_map(|arg| match arg { + Arg::Flag(flag) if flag.starts_with("-:") => { + let mut flag_to_exclude = flag[2..].to_string(); - // Normalize flag format - ensure it starts with -- - if !flag_to_exclude.starts_with("--") { - flag_to_exclude = format!("--{flag_to_exclude}"); - } + // Normalize flag format - ensure it starts with -- + if !flag_to_exclude.starts_with("--") { + flag_to_exclude = format!("--{flag_to_exclude}"); + } - Some(flag_to_exclude) - }, - _ => None, - }) - .collect() + Some(flag_to_exclude) + }, + _ => None, + }) + .collect() } /// Apply arg removals to a vector of string arguments. @@ -35,104 +35,104 @@ pub fn parse_removal_args(args: &[Arg]) -> Vec { /// # Returns /// Filtered vector with specified args removed pub fn apply_arg_removals(args: Vec, removals: &[String]) -> Vec { - if removals.is_empty() { - return args; - } + if removals.is_empty() { + return args; + } - let mut res = Vec::new(); - let mut skip_next = false; + let mut res = Vec::new(); + let mut skip_next = false; - for (i, arg) in args.iter().enumerate() { - if skip_next { - skip_next = false; - continue; - } + for (i, arg) in args.iter().enumerate() { + if skip_next { + skip_next = false; + continue; + } - let should_remove = removals - .iter() - .any(|removal| arg == removal || arg.starts_with(&format!("{removal}="))); + let should_remove = removals + .iter() + .any(|removal| arg == removal || arg.starts_with(&format!("{removal}="))); - if should_remove { - // Only skip next if this looks like an option (starts with --) and next arg doesn't start with -- - if !arg.contains("=") && i + 1 < args.len() { - let next_arg = &args[i + 1]; - if !next_arg.starts_with("-") { - skip_next = true; - } - } - continue; - } + if should_remove { + // Only skip next if this looks like an option (starts with --) and next arg doesn't start with -- + if !arg.contains("=") && i + 1 < args.len() { + let next_arg = &args[i + 1]; + if !next_arg.starts_with("-") { + skip_next = true; + } + } + continue; + } - res.push(arg.clone()); - } + res.push(arg.clone()); + } - res + res } #[cfg(test)] mod tests { - use super::*; + use super::*; - #[test] - fn test_parse_removal_args() { - let args = vec![ - Arg::Flag("-:--insecure-validator-i-know-what-i-do".to_string()), - Arg::Flag("--validator".to_string()), - Arg::Flag("-:--no-telemetry".to_string()), - ]; + #[test] + fn test_parse_removal_args() { + let args = vec![ + Arg::Flag("-:--insecure-validator-i-know-what-i-do".to_string()), + Arg::Flag("--validator".to_string()), + Arg::Flag("-:--no-telemetry".to_string()), + ]; - let removals = parse_removal_args(&args); - assert_eq!(removals.len(), 2); - assert!(removals.contains(&"--insecure-validator-i-know-what-i-do".to_string())); - assert!(removals.contains(&"--no-telemetry".to_string())); - } + let removals = parse_removal_args(&args); + assert_eq!(removals.len(), 2); + assert!(removals.contains(&"--insecure-validator-i-know-what-i-do".to_string())); + assert!(removals.contains(&"--no-telemetry".to_string())); + } - #[test] - fn test_apply_arg_removals_flag() { - let args = vec![ - "--validator".to_string(), - "--insecure-validator-i-know-what-i-do".to_string(), - "--no-telemetry".to_string(), - ]; - let removals = vec!["--insecure-validator-i-know-what-i-do".to_string()]; - let res = apply_arg_removals(args, &removals); - assert_eq!(res.len(), 2); - assert!(res.contains(&"--validator".to_string())); - assert!(res.contains(&"--no-telemetry".to_string())); - assert!(!res.contains(&"--insecure-validator-i-know-what-i-do".to_string())); - } + #[test] + fn test_apply_arg_removals_flag() { + let args = vec![ + "--validator".to_string(), + "--insecure-validator-i-know-what-i-do".to_string(), + "--no-telemetry".to_string(), + ]; + let removals = vec!["--insecure-validator-i-know-what-i-do".to_string()]; + let res = apply_arg_removals(args, &removals); + assert_eq!(res.len(), 2); + assert!(res.contains(&"--validator".to_string())); + assert!(res.contains(&"--no-telemetry".to_string())); + assert!(!res.contains(&"--insecure-validator-i-know-what-i-do".to_string())); + } - #[test] - fn test_apply_arg_removals_option_with_equals() { - let args = vec!["--name=alice".to_string(), "--port=30333".to_string()]; - let removals = vec!["--port".to_string()]; - let res = apply_arg_removals(args, &removals); - assert_eq!(res.len(), 1); - assert_eq!(res[0], "--name=alice"); - } + #[test] + fn test_apply_arg_removals_option_with_equals() { + let args = vec!["--name=alice".to_string(), "--port=30333".to_string()]; + let removals = vec!["--port".to_string()]; + let res = apply_arg_removals(args, &removals); + assert_eq!(res.len(), 1); + assert_eq!(res[0], "--name=alice"); + } - #[test] - fn test_apply_arg_removals_option_with_space() { - let args = vec![ - "--name".to_string(), - "alice".to_string(), - "--port".to_string(), - "30333".to_string(), - ]; - let removals = vec!["--port".to_string()]; + #[test] + fn test_apply_arg_removals_option_with_space() { + let args = vec![ + "--name".to_string(), + "alice".to_string(), + "--port".to_string(), + "30333".to_string(), + ]; + let removals = vec!["--port".to_string()]; - let res = apply_arg_removals(args, &removals); - assert_eq!(res.len(), 2); - assert_eq!(res[0], "--name"); - assert_eq!(res[1], "alice"); - } + let res = apply_arg_removals(args, &removals); + assert_eq!(res.len(), 2); + assert_eq!(res[0], "--name"); + assert_eq!(res[1], "alice"); + } - #[test] - fn test_apply_arg_removals_empty() { - let args = vec!["--validator".to_string()]; - let removals = vec![]; + #[test] + fn test_apply_arg_removals_empty() { + let args = vec!["--validator".to_string()]; + let removals = vec![]; - let res = apply_arg_removals(args, &removals); - assert_eq!(res, vec!["--validator".to_string()]); - } + let res = apply_arg_removals(args, &removals); + assert_eq!(res, vec!["--validator".to_string()]); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/bootnode_addr.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/bootnode_addr.rs index 3efa1eac..6ad6a1e0 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/bootnode_addr.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/bootnode_addr.rs @@ -3,109 +3,94 @@ use std::{fmt::Display, net::IpAddr}; use super::errors::GeneratorError; pub fn generate + Display>( - peer_id: &str, - ip: &IpAddr, - port: u16, - args: &[T], - p2p_cert: &Option, + peer_id: &str, + ip: &IpAddr, + port: u16, + args: &[T], + p2p_cert: &Option, ) -> Result { - let addr = if let Some(index) = args.iter().position(|arg| arg.as_ref().eq("--listen-addr")) { - let listen_value = args - .as_ref() - .get(index + 1) - .ok_or(GeneratorError::BootnodeAddrGeneration( - "can not generate bootnode address from args".into(), - ))? - .to_string(); + let addr = if let Some(index) = args.iter().position(|arg| arg.as_ref().eq("--listen-addr")) { + let listen_value = args + .as_ref() + .get(index + 1) + .ok_or(GeneratorError::BootnodeAddrGeneration( + "can not generate bootnode address from args".into(), + ))? + .to_string(); - let ip_str = ip.to_string(); - let port_str = port.to_string(); - let mut parts = listen_value.split('/').collect::>(); - parts[2] = &ip_str; - parts[4] = port_str.as_str(); - parts.join("/") - } else { - format!("/ip4/{ip}/tcp/{port}/ws") - }; + let ip_str = ip.to_string(); + let port_str = port.to_string(); + let mut parts = listen_value.split('/').collect::>(); + parts[2] = &ip_str; + parts[4] = port_str.as_str(); + parts.join("/") + } else { + format!("/ip4/{ip}/tcp/{port}/ws") + }; - let mut addr_with_peer = format!("{addr}/p2p/{peer_id}"); - if let Some(p2p_cert) = p2p_cert { - addr_with_peer.push_str("/certhash/"); - addr_with_peer.push_str(p2p_cert) - } - Ok(addr_with_peer) + let mut addr_with_peer = format!("{addr}/p2p/{peer_id}"); + if let Some(p2p_cert) = p2p_cert { + addr_with_peer.push_str("/certhash/"); + addr_with_peer.push_str(p2p_cert) + } + Ok(addr_with_peer) } #[cfg(test)] mod tests { - use provider::constants::LOCALHOST; + use provider::constants::LOCALHOST; - use super::*; - #[test] - fn generate_for_alice_without_args() { - let peer_id = "12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"; // from alice as seed - let args: Vec<&str> = vec![]; - let bootnode_addr = generate(peer_id, &LOCALHOST, 5678, &args, &None).unwrap(); - assert_eq!( - &bootnode_addr, - "/ip4/127.0.0.1/tcp/5678/ws/p2p/12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm" - ); - } + use super::*; + #[test] + fn generate_for_alice_without_args() { + let peer_id = "12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"; // from alice as seed + let args: Vec<&str> = vec![]; + let bootnode_addr = generate(peer_id, &LOCALHOST, 5678, &args, &None).unwrap(); + assert_eq!( + &bootnode_addr, + "/ip4/127.0.0.1/tcp/5678/ws/p2p/12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm" + ); + } - #[test] - fn generate_for_alice_with_listen_addr() { - // Should override the ip/port - let peer_id = "12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"; // from alice as seed - let args: Vec = [ - "--some", - "other", - "--listen-addr", - "/ip4/192.168.100.1/tcp/30333/ws", - ] - .iter() - .map(|x| x.to_string()) - .collect(); - let bootnode_addr = - generate(peer_id, &LOCALHOST, 5678, args.iter().as_ref(), &None).unwrap(); - assert_eq!( - &bootnode_addr, - "/ip4/127.0.0.1/tcp/5678/ws/p2p/12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm" - ); - } + #[test] + fn generate_for_alice_with_listen_addr() { + // Should override the ip/port + let peer_id = "12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"; // from alice as seed + let args: Vec = + ["--some", "other", "--listen-addr", "/ip4/192.168.100.1/tcp/30333/ws"] + .iter() + .map(|x| x.to_string()) + .collect(); + let bootnode_addr = + generate(peer_id, &LOCALHOST, 5678, args.iter().as_ref(), &None).unwrap(); + assert_eq!( + &bootnode_addr, + "/ip4/127.0.0.1/tcp/5678/ws/p2p/12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm" + ); + } - #[test] - fn generate_for_alice_with_listen_addr_without_value_must_fail() { - // Should override the ip/port - let peer_id = "12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"; // from alice as seed - let args: Vec = ["--some", "other", "--listen-addr"] - .iter() - .map(|x| x.to_string()) - .collect(); - let bootnode_addr = generate(peer_id, &LOCALHOST, 5678, args.iter().as_ref(), &None); + #[test] + fn generate_for_alice_with_listen_addr_without_value_must_fail() { + // Should override the ip/port + let peer_id = "12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"; // from alice as seed + let args: Vec = + ["--some", "other", "--listen-addr"].iter().map(|x| x.to_string()).collect(); + let bootnode_addr = generate(peer_id, &LOCALHOST, 5678, args.iter().as_ref(), &None); - assert!(bootnode_addr.is_err()); - assert!(matches!( - bootnode_addr, - Err(GeneratorError::BootnodeAddrGeneration(_)) - )); - } + assert!(bootnode_addr.is_err()); + assert!(matches!(bootnode_addr, Err(GeneratorError::BootnodeAddrGeneration(_)))); + } - #[test] - fn generate_for_alice_withcert() { - let peer_id = "12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"; // from alice as seed - let args: Vec<&str> = vec![]; - let bootnode_addr = generate( - peer_id, - &LOCALHOST, - 5678, - &args, - &Some(String::from("data")), - ) - .unwrap(); - assert_eq!( + #[test] + fn generate_for_alice_withcert() { + let peer_id = "12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"; // from alice as seed + let args: Vec<&str> = vec![]; + let bootnode_addr = + generate(peer_id, &LOCALHOST, 5678, &args, &Some(String::from("data"))).unwrap(); + assert_eq!( &bootnode_addr, "/ip4/127.0.0.1/tcp/5678/ws/p2p/12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm/certhash/data" ); - } + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/chain_spec.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/chain_spec.rs index 845122e8..425d8009 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/chain_spec.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/chain_spec.rs @@ -1,19 +1,19 @@ use std::{ - collections::HashMap, - path::{Path, PathBuf}, + collections::HashMap, + path::{Path, PathBuf}, }; use anyhow::anyhow; use configuration::{ - types::{AssetLocation, Chain, ChainSpecRuntime, JsonOverrides, ParaId}, - HrmpChannelConfig, -}; -use provider::{ - constants::NODE_CONFIG_DIR, - types::{GenerateFileCommand, GenerateFilesOptions, TransferedFile}, - DynNamespace, ProviderError, + types::{AssetLocation, Chain, ChainSpecRuntime, JsonOverrides, ParaId}, + HrmpChannelConfig, }; use pezsc_chain_spec::{GenericChainSpec, GenesisConfigBuilderRuntimeCaller}; +use provider::{ + constants::NODE_CONFIG_DIR, + types::{GenerateFileCommand, GenerateFilesOptions, TransferedFile}, + DynNamespace, ProviderError, +}; use serde::{Deserialize, Serialize}; use serde_json::json; use support::{constants::THIS_IS_A_BUG, fs::FileSystem, replacer::apply_replacements}; @@ -22,64 +22,64 @@ use tracing::{debug, info, trace, warn}; use super::errors::GeneratorError; use crate::{ - network_spec::{node::NodeSpec, relaychain::RelaychainSpec, teyrchain::TeyrchainSpec}, - ScopedFilesystem, + network_spec::{node::NodeSpec, relaychain::RelaychainSpec, teyrchain::TeyrchainSpec}, + ScopedFilesystem, }; // TODO: (javier) move to state #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum Context { - Relay, - Para { relay_chain: Chain, para_id: ParaId }, + Relay, + Para { relay_chain: Chain, para_id: ParaId }, } /// Posible chain-spec formats #[derive(Debug, Clone, Copy)] enum ChainSpecFormat { - Plain, - Raw, + Plain, + Raw, } /// Key types to replace in spec #[derive(Debug, Clone, Copy)] enum KeyType { - Session, - Aura, - Grandpa, + Session, + Aura, + Grandpa, } #[derive(Debug, Clone, Copy, Default)] enum SessionKeyType { - // Default derivarion (e.g `//`) - #[default] - Default, - // Stash detivarion (e.g `///stash`) - Stash, - // EVM session type - Evm, + // Default derivarion (e.g `//`) + #[default] + Default, + // Stash detivarion (e.g `///stash`) + Stash, + // EVM session type + Evm, } type MaybeExpectedPath = Option; #[derive(Debug, Clone, Serialize, Deserialize)] pub enum CommandInContext { - Local(String, MaybeExpectedPath), - Remote(String, MaybeExpectedPath), + Local(String, MaybeExpectedPath), + Remote(String, MaybeExpectedPath), } impl CommandInContext { - fn cmd(&self) -> &str { - match self { - CommandInContext::Local(cmd, _) | CommandInContext::Remote(cmd, _) => cmd.as_ref(), - } - } + fn cmd(&self) -> &str { + match self { + CommandInContext::Local(cmd, _) | CommandInContext::Remote(cmd, _) => cmd.as_ref(), + } + } } #[derive(Debug)] pub struct ParaGenesisConfig> { - pub(crate) state_path: T, - pub(crate) wasm_path: T, - pub(crate) id: u32, - pub(crate) as_parachain: bool, + pub(crate) state_path: T, + pub(crate) wasm_path: T, + pub(crate) id: u32, + pub(crate) as_parachain: bool, } /// Presets to check if is not set by the user. @@ -96,1498 +96,1396 @@ const DEFAULT_PRESETS_TO_CHECK: [&str; 3] = ["local_testnet", "development", "de #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ChainSpec { - // Name of the spec file, most of the times could be the same as the chain_name. (e.g rococo-local) - chain_spec_name: String, - // Location of the chain-spec to use - asset_location: Option, - // Location of the runtime to use - runtime: Option, - maybe_plain_path: Option, - chain_name: Option, - raw_path: Option, - // The binary to build the chain-spec - command: Option, - // Imgae to use for build the chain-spec - image: Option, - // Contex of the network (e.g relay or para) - context: Context, + // Name of the spec file, most of the times could be the same as the chain_name. (e.g rococo-local) + chain_spec_name: String, + // Location of the chain-spec to use + asset_location: Option, + // Location of the runtime to use + runtime: Option, + maybe_plain_path: Option, + chain_name: Option, + raw_path: Option, + // The binary to build the chain-spec + command: Option, + // Imgae to use for build the chain-spec + image: Option, + // Contex of the network (e.g relay or para) + context: Context, } impl ChainSpec { - pub(crate) fn new(chain_spec_name: impl Into, context: Context) -> Self { - Self { - chain_spec_name: chain_spec_name.into(), - chain_name: None, - maybe_plain_path: None, - asset_location: None, - runtime: None, - raw_path: None, - command: None, - image: None, - context, - } - } + pub(crate) fn new(chain_spec_name: impl Into, context: Context) -> Self { + Self { + chain_spec_name: chain_spec_name.into(), + chain_name: None, + maybe_plain_path: None, + asset_location: None, + runtime: None, + raw_path: None, + command: None, + image: None, + context, + } + } - pub(crate) fn chain_spec_name(&self) -> &str { - self.chain_spec_name.as_ref() - } + pub(crate) fn chain_spec_name(&self) -> &str { + self.chain_spec_name.as_ref() + } - pub(crate) fn chain_name(&self) -> Option<&str> { - self.chain_name.as_deref() - } + pub(crate) fn chain_name(&self) -> Option<&str> { + self.chain_name.as_deref() + } - pub(crate) fn set_chain_name(mut self, chain_name: impl Into) -> Self { - self.chain_name = Some(chain_name.into()); - self - } + pub(crate) fn set_chain_name(mut self, chain_name: impl Into) -> Self { + self.chain_name = Some(chain_name.into()); + self + } - pub(crate) fn asset_location(mut self, location: AssetLocation) -> Self { - self.asset_location = Some(location); - self - } + pub(crate) fn asset_location(mut self, location: AssetLocation) -> Self { + self.asset_location = Some(location); + self + } - pub(crate) fn runtime(mut self, chain_spec_runtime: ChainSpecRuntime) -> Self { - self.runtime = Some(chain_spec_runtime); - self - } + pub(crate) fn runtime(mut self, chain_spec_runtime: ChainSpecRuntime) -> Self { + self.runtime = Some(chain_spec_runtime); + self + } - pub(crate) fn command( - mut self, - command: impl Into, - is_local: bool, - expected_path: Option<&str>, - ) -> Self { - let maybe_expected_path = expected_path.map(PathBuf::from); - let cmd = if is_local { - CommandInContext::Local(command.into(), maybe_expected_path) - } else { - CommandInContext::Remote(command.into(), maybe_expected_path) - }; - self.command = Some(cmd); - self - } + pub(crate) fn command( + mut self, + command: impl Into, + is_local: bool, + expected_path: Option<&str>, + ) -> Self { + let maybe_expected_path = expected_path.map(PathBuf::from); + let cmd = if is_local { + CommandInContext::Local(command.into(), maybe_expected_path) + } else { + CommandInContext::Remote(command.into(), maybe_expected_path) + }; + self.command = Some(cmd); + self + } - pub(crate) fn image(mut self, image: Option) -> Self { - self.image = image; - self - } + pub(crate) fn image(mut self, image: Option) -> Self { + self.image = image; + self + } - /// Build the chain-spec - /// - /// Chain spec generation flow: - /// if chain_spec_path is set -> use this chain_spec - /// else if runtime_path is set and cmd is compatible with chain-spec-builder -> use the chain-spec-builder - /// else if chain_spec_command is set -> use this cmd for generate the chain_spec - /// else -> use the default command. - pub async fn build<'a, T>( - &mut self, - ns: &DynNamespace, - scoped_fs: &ScopedFilesystem<'a, T>, - ) -> Result<(), GeneratorError> - where - T: FileSystem, - { - if self.asset_location.is_none() && self.command.is_none() && self.runtime.is_none() { - return Err(GeneratorError::ChainSpecGeneration( - "Can not build the chain spec without set the command, asset_location or runtime" - .to_string(), - )); - } + /// Build the chain-spec + /// + /// Chain spec generation flow: + /// if chain_spec_path is set -> use this chain_spec + /// else if runtime_path is set and cmd is compatible with chain-spec-builder -> use the chain-spec-builder + /// else if chain_spec_command is set -> use this cmd for generate the chain_spec + /// else -> use the default command. + pub async fn build<'a, T>( + &mut self, + ns: &DynNamespace, + scoped_fs: &ScopedFilesystem<'a, T>, + ) -> Result<(), GeneratorError> + where + T: FileSystem, + { + if self.asset_location.is_none() && self.command.is_none() && self.runtime.is_none() { + return Err(GeneratorError::ChainSpecGeneration( + "Can not build the chain spec without set the command, asset_location or runtime" + .to_string(), + )); + } - let maybe_plain_spec_path = PathBuf::from(format!("{}-plain.json", self.chain_spec_name)); + let maybe_plain_spec_path = PathBuf::from(format!("{}-plain.json", self.chain_spec_name)); - // if asset_location is some, then copy the asset to the `base_dir` of the ns with the name `-plain.json` - if let Some(location) = self.asset_location.as_ref() { - let maybe_plain_spec_full_path = scoped_fs.full_path(maybe_plain_spec_path.as_path()); - location - .dump_asset(maybe_plain_spec_full_path) - .await - .map_err(|e| { - GeneratorError::ChainSpecGeneration(format!( - "Error {e} dumping location {location:?}" - )) - })?; - } else if let Some(runtime) = self.runtime.as_ref() { - trace!( - "Creating chain-spec with runtime from localtion: {}", - runtime.location - ); - // First dump the runtime into the ns scoped fs, since we want to easily reproduce - let runtime_file_name = PathBuf::from(format!("{}-runtime.wasm", self.chain_spec_name)); - let runtime_path_ns = scoped_fs.full_path(runtime_file_name.as_path()); - runtime - .location - .dump_asset(runtime_path_ns) - .await - .map_err(|e| { - GeneratorError::ChainSpecGeneration(format!( - "Error {e} dumping location {:?}", - runtime.location - )) - })?; + // if asset_location is some, then copy the asset to the `base_dir` of the ns with the name `-plain.json` + if let Some(location) = self.asset_location.as_ref() { + let maybe_plain_spec_full_path = scoped_fs.full_path(maybe_plain_spec_path.as_path()); + location.dump_asset(maybe_plain_spec_full_path).await.map_err(|e| { + GeneratorError::ChainSpecGeneration(format!( + "Error {e} dumping location {location:?}" + )) + })?; + } else if let Some(runtime) = self.runtime.as_ref() { + trace!("Creating chain-spec with runtime from localtion: {}", runtime.location); + // First dump the runtime into the ns scoped fs, since we want to easily reproduce + let runtime_file_name = PathBuf::from(format!("{}-runtime.wasm", self.chain_spec_name)); + let runtime_path_ns = scoped_fs.full_path(runtime_file_name.as_path()); + runtime.location.dump_asset(runtime_path_ns).await.map_err(|e| { + GeneratorError::ChainSpecGeneration(format!( + "Error {e} dumping location {:?}", + runtime.location + )) + })?; - // list the presets to check if match with the supplied one or one of the defaults - let runtime_code = scoped_fs.read(runtime_file_name.as_path()).await?; + // list the presets to check if match with the supplied one or one of the defaults + let runtime_code = scoped_fs.read(runtime_file_name.as_path()).await?; - let caller: GenesisConfigBuilderRuntimeCaller = - GenesisConfigBuilderRuntimeCaller::new(&runtime_code[..]); - let presets = caller.preset_names().map_err(|e| { - GeneratorError::ChainSpecGeneration(format!( - "getting default config from runtime should work: {e}" - )) - })?; + let caller: GenesisConfigBuilderRuntimeCaller = + GenesisConfigBuilderRuntimeCaller::new(&runtime_code[..]); + let presets = caller.preset_names().map_err(|e| { + GeneratorError::ChainSpecGeneration(format!( + "getting default config from runtime should work: {e}" + )) + })?; - // check the preset to use with this priorities: - // - IF user provide a preset (and if present) use it - // - else (user don't provide preset or the provided one isn't preset) - // check the [`DEFAULT_PRESETS_TO_CHECK`] in order to find one valid - // - If we can't find any valid preset use the `default config` from the runtime + // check the preset to use with this priorities: + // - IF user provide a preset (and if present) use it + // - else (user don't provide preset or the provided one isn't preset) + // check the [`DEFAULT_PRESETS_TO_CHECK`] in order to find one valid + // - If we can't find any valid preset use the `default config` from the runtime - let preset_to_check = if let Some(preset) = &runtime.preset { - [vec![preset.as_str()], DEFAULT_PRESETS_TO_CHECK.to_vec()].concat() - } else { - DEFAULT_PRESETS_TO_CHECK.to_vec() - }; - let preset = preset_to_check - .iter() - .find(|preset| presets.iter().any(|item| item == *preset)); + let preset_to_check = if let Some(preset) = &runtime.preset { + [vec![preset.as_str()], DEFAULT_PRESETS_TO_CHECK.to_vec()].concat() + } else { + DEFAULT_PRESETS_TO_CHECK.to_vec() + }; + let preset = + preset_to_check.iter().find(|preset| presets.iter().any(|item| item == *preset)); - trace!("presets: {:?} - preset to use: {:?}", presets, preset); - let builder = if let Some(preset) = preset { - GenericChainSpec::<()>::builder(&runtime_code[..], ()) - .with_genesis_config_preset_name(preset) - } else { - // default config - let default_config = caller.get_default_config().map_err(|e| { - GeneratorError::ChainSpecGeneration(format!( - "getting default config from runtime should work: {e}" - )) - })?; + trace!("presets: {:?} - preset to use: {:?}", presets, preset); + let builder = if let Some(preset) = preset { + GenericChainSpec::<()>::builder(&runtime_code[..], ()) + .with_genesis_config_preset_name(preset) + } else { + // default config + let default_config = caller.get_default_config().map_err(|e| { + GeneratorError::ChainSpecGeneration(format!( + "getting default config from runtime should work: {e}" + )) + })?; - GenericChainSpec::<()>::builder(&runtime_code[..], ()) - .with_genesis_config(default_config) - }; + GenericChainSpec::<()>::builder(&runtime_code[..], ()) + .with_genesis_config(default_config) + }; - let builder = if let Context::Para { - relay_chain: _, - para_id: _, - } = &self.context - { - builder.with_id(self.chain_spec_name()) - } else { - builder - }; + let builder = if let Context::Para { relay_chain: _, para_id: _ } = &self.context { + builder.with_id(self.chain_spec_name()) + } else { + builder + }; - let builder = if let Some(chain_name) = self.chain_name.as_ref() { - builder.with_name(chain_name) - } else { - builder - }; + let builder = if let Some(chain_name) = self.chain_name.as_ref() { + builder.with_name(chain_name) + } else { + builder + }; - let chain_spec = builder.build(); + let chain_spec = builder.build(); - let contents = chain_spec.as_json(false).map_err(|e| { - GeneratorError::ChainSpecGeneration(format!( - "getting chain-spec as json should work, err: {e}" - )) - })?; + let contents = chain_spec.as_json(false).map_err(|e| { + GeneratorError::ChainSpecGeneration(format!( + "getting chain-spec as json should work, err: {e}" + )) + })?; - scoped_fs.write(&maybe_plain_spec_path, contents).await?; - } else { - trace!("Creating chain-spec with command"); - // we should create the chain-spec using command. - let mut replacement_value = String::default(); - if let Some(chain_name) = self.chain_name.as_ref() { - if !chain_name.is_empty() { - replacement_value.clone_from(chain_name); - } - }; + scoped_fs.write(&maybe_plain_spec_path, contents).await?; + } else { + trace!("Creating chain-spec with command"); + // we should create the chain-spec using command. + let mut replacement_value = String::default(); + if let Some(chain_name) = self.chain_name.as_ref() { + if !chain_name.is_empty() { + replacement_value.clone_from(chain_name); + } + }; - // SAFETY: we ensure that command is some with the first check of the fn - // default as empty - let sanitized_cmd = if replacement_value.is_empty() { - // we need to remove the `--chain` flag - self.command.as_ref().unwrap().cmd().replace("--chain", "") - } else { - self.command.as_ref().unwrap().cmd().to_owned() - }; + // SAFETY: we ensure that command is some with the first check of the fn + // default as empty + let sanitized_cmd = if replacement_value.is_empty() { + // we need to remove the `--chain` flag + self.command.as_ref().unwrap().cmd().replace("--chain", "") + } else { + self.command.as_ref().unwrap().cmd().to_owned() + }; - let full_cmd = apply_replacements( - &sanitized_cmd, - &HashMap::from([("chainName", replacement_value.as_str())]), - ); - trace!("full_cmd: {:?}", full_cmd); + let full_cmd = apply_replacements( + &sanitized_cmd, + &HashMap::from([("chainName", replacement_value.as_str())]), + ); + trace!("full_cmd: {:?}", full_cmd); - let parts: Vec<&str> = full_cmd.split_whitespace().collect(); - let Some((cmd, args)) = parts.split_first() else { - return Err(GeneratorError::ChainSpecGeneration(format!( - "Invalid generator command: {full_cmd}" - ))); - }; - trace!("cmd: {:?} - args: {:?}", cmd, args); + let parts: Vec<&str> = full_cmd.split_whitespace().collect(); + let Some((cmd, args)) = parts.split_first() else { + return Err(GeneratorError::ChainSpecGeneration(format!( + "Invalid generator command: {full_cmd}" + ))); + }; + trace!("cmd: {:?} - args: {:?}", cmd, args); - let generate_command = - GenerateFileCommand::new(cmd, maybe_plain_spec_path.clone()).args(args); - if let Some(cmd) = &self.command { - match cmd { - CommandInContext::Local(_, expected_path) => { - build_locally(generate_command, scoped_fs, expected_path.as_deref()).await? - }, - CommandInContext::Remote(_, expected_path) => { - let options = GenerateFilesOptions::new( - vec![generate_command], - self.image.clone(), - expected_path.clone(), - ); - ns.generate_files(options).await?; - }, - } - } - } + let generate_command = + GenerateFileCommand::new(cmd, maybe_plain_spec_path.clone()).args(args); + if let Some(cmd) = &self.command { + match cmd { + CommandInContext::Local(_, expected_path) => { + build_locally(generate_command, scoped_fs, expected_path.as_deref()).await? + }, + CommandInContext::Remote(_, expected_path) => { + let options = GenerateFilesOptions::new( + vec![generate_command], + self.image.clone(), + expected_path.clone(), + ); + ns.generate_files(options).await?; + }, + } + } + } - // check if the _generated_ spec is in raw mode. - if is_raw(maybe_plain_spec_path.clone(), scoped_fs).await? { - let spec_path = PathBuf::from(format!("{}.json", self.chain_spec_name)); - let tf_file = TransferedFile::new( - &PathBuf::from_iter([ns.base_dir(), &maybe_plain_spec_path]), - &spec_path, - ); - scoped_fs.copy_files(vec![&tf_file]).await.map_err(|e| { - GeneratorError::ChainSpecGeneration(format!( - "Error copying file: {tf_file}, err: {e}" - )) - })?; + // check if the _generated_ spec is in raw mode. + if is_raw(maybe_plain_spec_path.clone(), scoped_fs).await? { + let spec_path = PathBuf::from(format!("{}.json", self.chain_spec_name)); + let tf_file = TransferedFile::new( + &PathBuf::from_iter([ns.base_dir(), &maybe_plain_spec_path]), + &spec_path, + ); + scoped_fs.copy_files(vec![&tf_file]).await.map_err(|e| { + GeneratorError::ChainSpecGeneration(format!( + "Error copying file: {tf_file}, err: {e}" + )) + })?; - self.raw_path = Some(spec_path); - } else { - self.maybe_plain_path = Some(maybe_plain_spec_path); - } - Ok(()) - } + self.raw_path = Some(spec_path); + } else { + self.maybe_plain_path = Some(maybe_plain_spec_path); + } + Ok(()) + } - pub async fn build_raw<'a, T>( - &mut self, - ns: &DynNamespace, - scoped_fs: &ScopedFilesystem<'a, T>, - relay_chain_id: Option, - ) -> Result<(), GeneratorError> - where - T: FileSystem, - { - warn!("Building raw version from {:?}", self); - // raw path already set, no more work to do here... - let None = self.raw_path else { - return Ok(()); - }; + pub async fn build_raw<'a, T>( + &mut self, + ns: &DynNamespace, + scoped_fs: &ScopedFilesystem<'a, T>, + relay_chain_id: Option, + ) -> Result<(), GeneratorError> + where + T: FileSystem, + { + warn!("Building raw version from {:?}", self); + // raw path already set, no more work to do here... + let None = self.raw_path else { + return Ok(()); + }; - // expected raw path - let raw_spec_path = PathBuf::from(format!("{}.json", self.chain_spec_name)); + // expected raw path + let raw_spec_path = PathBuf::from(format!("{}.json", self.chain_spec_name)); - match self - .try_build_raw_with_generic(scoped_fs, relay_chain_id.clone(), raw_spec_path.as_path()) - .await - { - Ok(_) => return Ok(()), - Err(err) => { - if Self::should_retry_with_command(&err) && self.command.is_some() { - warn!( + match self + .try_build_raw_with_generic(scoped_fs, relay_chain_id.clone(), raw_spec_path.as_path()) + .await + { + Ok(_) => return Ok(()), + Err(err) => { + if Self::should_retry_with_command(&err) && self.command.is_some() { + warn!( "GenericChainSpec raw generation failed ({}). Falling back to command execution.", err ); - } else { - return Err(err); - } - }, - } - - self.build_raw_with_command(ns, scoped_fs, raw_spec_path, relay_chain_id) - .await?; - - Ok(()) - } - - async fn try_build_raw_with_generic<'a, T>( - &mut self, - scoped_fs: &ScopedFilesystem<'a, T>, - relay_chain_id: Option, - raw_spec_path: &Path, - ) -> Result<(), GeneratorError> - where - T: FileSystem, - { - // `build_raw` is always called after `build`, so `maybe_plain_path` must be set at this point - let (json_content, _) = self.read_spec(scoped_fs).await?; - let json_bytes: Vec = json_content.as_bytes().into(); - let chain_spec = GenericChainSpec::<()>::from_json_bytes(json_bytes).map_err(|e| { - GeneratorError::ChainSpecGeneration(format!( - "Error loading chain-spec from json_bytes, err: {e}" - )) - })?; - - self.raw_path = Some(raw_spec_path.to_path_buf()); - let contents = chain_spec.as_json(true).map_err(|e| { - GeneratorError::ChainSpecGeneration(format!( - "getting chain-spec as json should work, err: {e}" - )) - })?; - let contents = self - .ensure_para_fields_in_raw(&contents, relay_chain_id) - .await?; - self.write_spec(scoped_fs, contents).await?; - - Ok(()) - } - - async fn build_raw_with_command<'a, T>( - &mut self, - ns: &DynNamespace, - scoped_fs: &ScopedFilesystem<'a, T>, - raw_spec_path: PathBuf, - relay_chain_id: Option, - ) -> Result<(), GeneratorError> - where - T: FileSystem, - { - // fallback to use _cmd_ for raw creation - let temp_name = format!( - "temp-build-raw-{}-{}", - self.chain_spec_name, - rand::random::() - ); - - let cmd = self - .command - .as_ref() - .ok_or(GeneratorError::ChainSpecGeneration( - "Invalid command".into(), - ))?; - let maybe_plain_path = - self.maybe_plain_path - .as_ref() - .ok_or(GeneratorError::ChainSpecGeneration( - "Invalid plain path".into(), - ))?; - - // TODO: we should get the full path from the scoped filesystem - let chain_spec_path_local = format!( - "{}/{}", - ns.base_dir().to_string_lossy(), - maybe_plain_path.display() - ); - // Remote path to be injected - let chain_spec_path_in_pod = format!("{}/{}", NODE_CONFIG_DIR, maybe_plain_path.display()); - // Path in the context of the node, this can be different in the context of the providers (e.g native) - let chain_spec_path_in_args = if matches!(self.command, Some(CommandInContext::Local(_, _))) - { - chain_spec_path_local.clone() - } else if ns.capabilities().prefix_with_full_path { - // In native - format!( - "{}/{}{}", - ns.base_dir().to_string_lossy(), - &temp_name, - &chain_spec_path_in_pod - ) - } else { - chain_spec_path_in_pod.clone() - }; - - let mut full_cmd = apply_replacements( - cmd.cmd(), - &HashMap::from([("chainName", chain_spec_path_in_args.as_str())]), - ); - - if !full_cmd.contains("--raw") { - full_cmd = format!("{full_cmd} --raw"); - } - trace!("full_cmd: {:?}", full_cmd); - - let parts: Vec<&str> = full_cmd.split_whitespace().collect(); - let Some((cmd, args)) = parts.split_first() else { - return Err(GeneratorError::ChainSpecGeneration(format!( - "Invalid generator command: {full_cmd}" - ))); - }; - trace!("cmd: {:?} - args: {:?}", cmd, args); - - let generate_command = GenerateFileCommand::new(cmd, raw_spec_path.clone()).args(args); - - if let Some(cmd) = &self.command { - match cmd { - CommandInContext::Local(_, expected_path) => { - build_locally(generate_command, scoped_fs, expected_path.as_deref()).await? - }, - CommandInContext::Remote(_, expected_path) => { - let options = GenerateFilesOptions::with_files( - vec![generate_command], - self.image.clone(), - &[TransferedFile::new( - chain_spec_path_local, - chain_spec_path_in_pod, - )], - expected_path.clone(), - ) - .temp_name(temp_name); - trace!("calling generate_files with options: {:#?}", options); - ns.generate_files(options).await?; - }, - } - } - - self.raw_path = Some(raw_spec_path.clone()); - let (content, _) = self.read_spec(scoped_fs).await?; - let content = self - .ensure_para_fields_in_raw(&content, relay_chain_id) - .await?; - self.write_spec(scoped_fs, content).await?; - - Ok(()) - } - - async fn ensure_para_fields_in_raw( - &mut self, - content: &str, - relay_chain_id: Option, - ) -> Result { - if let Context::Para { - relay_chain: _, - para_id, - } = &self.context - { - let mut chain_spec_json: serde_json::Value = - serde_json::from_str(content).map_err(|e| { - GeneratorError::ChainSpecGeneration(format!( - "getting chain-spec as json should work, err: {e}" - )) - })?; - - let mut needs_write = false; - - if chain_spec_json["relay_chain"].is_null() { - chain_spec_json["relay_chain"] = json!(relay_chain_id); - needs_write = true; - } - - if chain_spec_json["para_id"].is_null() { - chain_spec_json["para_id"] = json!(para_id); - needs_write = true; - } - - if needs_write { - let contents = serde_json::to_string_pretty(&chain_spec_json).map_err(|e| { - GeneratorError::ChainSpecGeneration(format!( - "getting chain-spec json as pretty string should work, err: {e}" - )) - })?; - return Ok(contents); - } - } - - Ok(content.to_string()) - } - - fn should_retry_with_command(err: &GeneratorError) -> bool { - match err { - GeneratorError::ChainSpecGeneration(msg) => { - let msg_lower = msg.to_lowercase(); - msg_lower.contains("genesisbuilder_get_preset") || msg_lower.contains("_get_preset") - }, - _ => false, - } - } - - /// Override the :code in chain-spec raw version - pub async fn override_code<'a, T>( - &mut self, - scoped_fs: &ScopedFilesystem<'a, T>, - wasm_override: &AssetLocation, - ) -> Result<(), GeneratorError> - where - T: FileSystem, - { - // first ensure we have the raw version of the chain-spec - let Some(_) = self.raw_path else { - return Err(GeneratorError::OverridingWasm(String::from( - "Raw path should be set at this point.", - ))); - }; - let (content, _) = self.read_spec(scoped_fs).await?; - // read override wasm - let override_content = wasm_override.get_asset().await.map_err(|_| { - GeneratorError::OverridingWasm(format!( - "Can not get asset to override wasm, asset: {wasm_override}" - )) - })?; - - // read spec to json value - let mut chain_spec_json: serde_json::Value = - serde_json::from_str(&content).map_err(|_| { - GeneratorError::ChainSpecGeneration("Can not parse chain-spec as json".into()) - })?; - - // override :code - let Some(code) = chain_spec_json.pointer_mut("/genesis/raw/top/0x3a636f6465") else { - return Err(GeneratorError::OverridingWasm(String::from( - "Pointer '/genesis/raw/top/0x3a636f6465' should be valid in the raw spec.", - ))); - }; - - info!( - "🖋 Overriding ':code' (0x3a636f6465) in raw chain-spec with content of {}", - wasm_override - ); - *code = json!(format!("0x{}", hex::encode(override_content))); - - let overrided_content = serde_json::to_string_pretty(&chain_spec_json).map_err(|_| { - GeneratorError::ChainSpecGeneration("can not parse chain-spec value as json".into()) - })?; - // save it - self.write_spec(scoped_fs, overrided_content).await?; - - Ok(()) - } - - pub async fn override_raw_spec<'a, T>( - &mut self, - scoped_fs: &ScopedFilesystem<'a, T>, - raw_spec_overrides: &JsonOverrides, - ) -> Result<(), GeneratorError> - where - T: FileSystem, - { - // first ensure we have the raw version of the chain-spec - let Some(_) = self.raw_path else { - return Err(GeneratorError::OverridingRawSpec(String::from( - "Raw path should be set at this point.", - ))); - }; - - let (content, _) = self.read_spec(scoped_fs).await?; - - // read overrides to json value - let override_content: serde_json::Value = raw_spec_overrides.get().await.map_err(|_| { - GeneratorError::OverridingRawSpec(format!( - "Can not parse raw_spec_override contents as json: {raw_spec_overrides}" - )) - })?; - - // read spec to json value - let mut chain_spec_json: serde_json::Value = - serde_json::from_str(&content).map_err(|_| { - GeneratorError::ChainSpecGeneration("Can not parse chain-spec as json".into()) - })?; - - // merge overrides with existing spec - merge(&mut chain_spec_json, &override_content); - - // save changes - let overrided_content = serde_json::to_string_pretty(&chain_spec_json).map_err(|_| { - GeneratorError::ChainSpecGeneration("can not parse chain-spec value as json".into()) - })?; - self.write_spec(scoped_fs, overrided_content).await?; - - Ok(()) - } - - pub fn raw_path(&self) -> Option<&Path> { - self.raw_path.as_deref() - } - - pub fn set_asset_location(&mut self, location: AssetLocation) { - self.asset_location = Some(location) - } - - pub async fn read_chain_id<'a, T>( - &self, - scoped_fs: &ScopedFilesystem<'a, T>, - ) -> Result - where - T: FileSystem, - { - let (content, _) = self.read_spec(scoped_fs).await?; - ChainSpec::chain_id_from_spec(&content) - } - - async fn read_spec<'a, T>( - &self, - scoped_fs: &ScopedFilesystem<'a, T>, - ) -> Result<(String, ChainSpecFormat), GeneratorError> - where - T: FileSystem, - { - let (path, format) = match (self.maybe_plain_path.as_ref(), self.raw_path.as_ref()) { - (Some(path), None) => (path, ChainSpecFormat::Plain), - (None, Some(path)) => (path, ChainSpecFormat::Raw), - (Some(_), Some(path)) => { - // if we have both paths return the raw - (path, ChainSpecFormat::Raw) - }, - (None, None) => unreachable!(), - }; - - let content = scoped_fs.read_to_string(path.clone()).await.map_err(|_| { - GeneratorError::ChainSpecGeneration(format!( - "Can not read chain-spec from {}", - path.to_string_lossy() - )) - })?; - - Ok((content, format)) - } - - async fn write_spec<'a, T>( - &self, - scoped_fs: &ScopedFilesystem<'a, T>, - content: impl Into, - ) -> Result<(), GeneratorError> - where - T: FileSystem, - { - let (path, _format) = match (self.maybe_plain_path.as_ref(), self.raw_path.as_ref()) { - (Some(path), None) => (path, ChainSpecFormat::Plain), - (None, Some(path)) => (path, ChainSpecFormat::Raw), - (Some(_), Some(path)) => { - // if we have both paths return the raw - (path, ChainSpecFormat::Raw) - }, - (None, None) => unreachable!(), - }; - - scoped_fs.write(path, content.into()).await.map_err(|_| { - GeneratorError::ChainSpecGeneration(format!( - "Can not write chain-spec from {}", - path.to_string_lossy() - )) - })?; - - Ok(()) - } - - // TODO: (javier) move this fns to state aware - pub async fn customize_para<'a, T>( - &self, - para: &TeyrchainSpec, - relay_chain_id: &str, - scoped_fs: &ScopedFilesystem<'a, T>, - ) -> Result<(), GeneratorError> - where - T: FileSystem, - { - let (content, format) = self.read_spec(scoped_fs).await?; - let mut chain_spec_json: serde_json::Value = - serde_json::from_str(&content).map_err(|_| { - GeneratorError::ChainSpecGeneration("Can not parse chain-spec as json".into()) - })?; - - if let Some(para_id) = chain_spec_json.get_mut("para_id") { - *para_id = json!(para.id); - }; - if let Some(para_id) = chain_spec_json.get_mut("paraId") { - *para_id = json!(para.id); - }; - - if let Some(relay_chain_id_field) = chain_spec_json.get_mut("relay_chain") { - *relay_chain_id_field = json!(relay_chain_id); - }; - - if let ChainSpecFormat::Plain = format { - let pointer = get_runtime_config_pointer(&chain_spec_json) - .map_err(GeneratorError::ChainSpecGeneration)?; - - // make genesis overrides first. - if let Some(overrides) = ¶.genesis_overrides { - let percolated_overrides = percolate_overrides(&pointer, overrides) - .map_err(|e| GeneratorError::ChainSpecGeneration(e.to_string()))?; - if let Some(genesis) = chain_spec_json.pointer_mut(&pointer) { - merge(genesis, percolated_overrides); - } - } - - clear_authorities(&pointer, &mut chain_spec_json, &self.context); - - let key_type_to_use = if para.is_evm_based { - SessionKeyType::Evm - } else { - SessionKeyType::Default - }; - - // Get validators to add as authorities - let validators: Vec<&NodeSpec> = para - .collators - .iter() - .filter(|node| node.is_validator) - .collect(); - - // check chain key types - if chain_spec_json - .pointer(&format!("{pointer}/session")) - .is_some() - { - add_authorities(&pointer, &mut chain_spec_json, &validators, key_type_to_use); - } else if chain_spec_json - .pointer(&format!("{pointer}/aura")) - .is_some() - { - add_aura_authorities(&pointer, &mut chain_spec_json, &validators, KeyType::Aura); - } else { - warn!("Can't customize keys, not `session` or `aura` find in the chain-spec file"); - }; - - // Add nodes to collator - let invulnerables: Vec<&NodeSpec> = para - .collators - .iter() - .filter(|node| node.is_invulnerable) - .collect(); - - add_collator_selection( - &pointer, - &mut chain_spec_json, - &invulnerables, - key_type_to_use, - ); - - // override `parachainInfo/parachainId` - override_parachain_info(&pointer, &mut chain_spec_json, para.id); - - // write spec - let content = serde_json::to_string_pretty(&chain_spec_json).map_err(|_| { - GeneratorError::ChainSpecGeneration("can not parse chain-spec value as json".into()) - })?; - self.write_spec(scoped_fs, content).await?; - } else { - warn!("⚠️ Chain spec for para_id: {} is in raw mode", para.id); - } - Ok(()) - } - - pub async fn customize_relay<'a, T, U>( - &self, - relaychain: &RelaychainSpec, - hrmp_channels: &[HrmpChannelConfig], - para_artifacts: Vec>, - scoped_fs: &ScopedFilesystem<'a, T>, - ) -> Result<(), GeneratorError> - where - T: FileSystem, - U: AsRef, - { - let (content, format) = self.read_spec(scoped_fs).await?; - let mut chain_spec_json: serde_json::Value = - serde_json::from_str(&content).map_err(|_| { - GeneratorError::ChainSpecGeneration("Can not parse chain-spec as json".into()) - })?; - - if let ChainSpecFormat::Plain = format { - // get the tokenDecimals property or set the default (12) - let token_decimals = - if let Some(val) = chain_spec_json.pointer("/properties/tokenDecimals") { - let val = val.as_u64().unwrap_or(12); - if val > u8::MAX as u64 { - 12 - } else { - val as u8 - } - } else { - 12 - }; - // get the config pointer - let pointer = get_runtime_config_pointer(&chain_spec_json) - .map_err(GeneratorError::ChainSpecGeneration)?; - - // make genesis overrides first. - if let Some(overrides) = &relaychain.runtime_genesis_patch { - let percolated_overrides = percolate_overrides(&pointer, overrides) - .map_err(|e| GeneratorError::ChainSpecGeneration(e.to_string()))?; - if let Some(patch_section) = chain_spec_json.pointer_mut(&pointer) { - merge(patch_section, percolated_overrides); - } - } - - // get min stake (to store if neede later) - let staking_min = get_staking_min(&pointer, &mut chain_spec_json); - - // Clear authorities - clear_authorities(&pointer, &mut chain_spec_json, &self.context); - - // add balances - add_balances( - &pointer, - &mut chain_spec_json, - &relaychain.nodes, - token_decimals, - staking_min, - ); - - // add staking - add_staking( - &pointer, - &mut chain_spec_json, - &relaychain.nodes, - staking_min, - ); - - // Get validators to add as authorities - let validators: Vec<&NodeSpec> = relaychain - .nodes - .iter() - .filter(|node| node.is_validator) - .collect(); - - // check chain key types - if chain_spec_json - .pointer(&format!("{pointer}/session")) - .is_some() - { - add_authorities( - &pointer, - &mut chain_spec_json, - &validators, - SessionKeyType::Stash, - ); - } else { - add_aura_authorities(&pointer, &mut chain_spec_json, &validators, KeyType::Aura); - add_grandpa_authorities(&pointer, &mut chain_spec_json, &validators, KeyType::Aura); - } - - // staking && nominators - - if !hrmp_channels.is_empty() { - add_hrmp_channels(&pointer, &mut chain_spec_json, hrmp_channels); - } - - // paras - for para_genesis_config in para_artifacts.iter() { - add_parachain_to_genesis( - &pointer, - &mut chain_spec_json, - para_genesis_config, - scoped_fs, - ) - .await - .map_err(|e| GeneratorError::ChainSpecGeneration(e.to_string()))?; - } - - // TODO: - // - staking - // - nominators - - // write spec - let content = serde_json::to_string_pretty(&chain_spec_json).map_err(|_| { - GeneratorError::ChainSpecGeneration("can not parse chain-spec value as json".into()) - })?; - self.write_spec(scoped_fs, content).await?; - } else { - warn!( - "⚠️ Chain Spec for chain {} is in raw mode, can't customize.", - self.chain_spec_name - ); - } - Ok(()) - } - - pub async fn add_bootnodes<'a, T>( - &self, - scoped_fs: &ScopedFilesystem<'a, T>, - bootnodes: &[String], - ) -> Result<(), GeneratorError> - where - T: FileSystem, - { - let (content, _) = self.read_spec(scoped_fs).await?; - let mut chain_spec_json: serde_json::Value = - serde_json::from_str(&content).map_err(|_| { - GeneratorError::ChainSpecGeneration("Can not parse chain-spec as json".into()) - })?; - - if let Some(bootnodes_on_file) = chain_spec_json.get_mut("bootNodes") { - if let Some(bootnodes_on_file) = bootnodes_on_file.as_array_mut() { - let mut bootnodes_to_add = - bootnodes.iter().map(|bootnode| json!(bootnode)).collect(); - bootnodes_on_file.append(&mut bootnodes_to_add); - } else { - return Err(GeneratorError::ChainSpecGeneration( - "id should be an string in the chain-spec, this is a bug".into(), - )); - }; - } else { - return Err(GeneratorError::ChainSpecGeneration( - "'bootNodes' should be a fields in the chain-spec of the relaychain".into(), - )); - }; - - // write spec - let content = serde_json::to_string_pretty(&chain_spec_json).map_err(|_| { - GeneratorError::ChainSpecGeneration("can not parse chain-spec value as json".into()) - })?; - self.write_spec(scoped_fs, content).await?; - - Ok(()) - } - - /// Get the chain_is from the json content of a chain-spec file. - pub fn chain_id_from_spec(spec_content: &str) -> Result { - let chain_spec_json: serde_json::Value = - serde_json::from_str(spec_content).map_err(|_| { - GeneratorError::ChainSpecGeneration("Can not parse chain-spec as json".into()) - })?; - if let Some(chain_id) = chain_spec_json.get("id") { - if let Some(chain_id) = chain_id.as_str() { - Ok(chain_id.to_string()) - } else { - Err(GeneratorError::ChainSpecGeneration( - "id should be an string in the chain-spec, this is a bug".into(), - )) - } - } else { - Err(GeneratorError::ChainSpecGeneration( - "'id' should be a fields in the chain-spec of the relaychain".into(), - )) - } - } + } else { + return Err(err); + } + }, + } + + self.build_raw_with_command(ns, scoped_fs, raw_spec_path, relay_chain_id).await?; + + Ok(()) + } + + async fn try_build_raw_with_generic<'a, T>( + &mut self, + scoped_fs: &ScopedFilesystem<'a, T>, + relay_chain_id: Option, + raw_spec_path: &Path, + ) -> Result<(), GeneratorError> + where + T: FileSystem, + { + // `build_raw` is always called after `build`, so `maybe_plain_path` must be set at this point + let (json_content, _) = self.read_spec(scoped_fs).await?; + let json_bytes: Vec = json_content.as_bytes().into(); + let chain_spec = GenericChainSpec::<()>::from_json_bytes(json_bytes).map_err(|e| { + GeneratorError::ChainSpecGeneration(format!( + "Error loading chain-spec from json_bytes, err: {e}" + )) + })?; + + self.raw_path = Some(raw_spec_path.to_path_buf()); + let contents = chain_spec.as_json(true).map_err(|e| { + GeneratorError::ChainSpecGeneration(format!( + "getting chain-spec as json should work, err: {e}" + )) + })?; + let contents = self.ensure_para_fields_in_raw(&contents, relay_chain_id).await?; + self.write_spec(scoped_fs, contents).await?; + + Ok(()) + } + + async fn build_raw_with_command<'a, T>( + &mut self, + ns: &DynNamespace, + scoped_fs: &ScopedFilesystem<'a, T>, + raw_spec_path: PathBuf, + relay_chain_id: Option, + ) -> Result<(), GeneratorError> + where + T: FileSystem, + { + // fallback to use _cmd_ for raw creation + let temp_name = format!("temp-build-raw-{}-{}", self.chain_spec_name, rand::random::()); + + let cmd = self + .command + .as_ref() + .ok_or(GeneratorError::ChainSpecGeneration("Invalid command".into()))?; + let maybe_plain_path = self + .maybe_plain_path + .as_ref() + .ok_or(GeneratorError::ChainSpecGeneration("Invalid plain path".into()))?; + + // TODO: we should get the full path from the scoped filesystem + let chain_spec_path_local = + format!("{}/{}", ns.base_dir().to_string_lossy(), maybe_plain_path.display()); + // Remote path to be injected + let chain_spec_path_in_pod = format!("{}/{}", NODE_CONFIG_DIR, maybe_plain_path.display()); + // Path in the context of the node, this can be different in the context of the providers (e.g native) + let chain_spec_path_in_args = if matches!(self.command, Some(CommandInContext::Local(_, _))) + { + chain_spec_path_local.clone() + } else if ns.capabilities().prefix_with_full_path { + // In native + format!("{}/{}{}", ns.base_dir().to_string_lossy(), &temp_name, &chain_spec_path_in_pod) + } else { + chain_spec_path_in_pod.clone() + }; + + let mut full_cmd = apply_replacements( + cmd.cmd(), + &HashMap::from([("chainName", chain_spec_path_in_args.as_str())]), + ); + + if !full_cmd.contains("--raw") { + full_cmd = format!("{full_cmd} --raw"); + } + trace!("full_cmd: {:?}", full_cmd); + + let parts: Vec<&str> = full_cmd.split_whitespace().collect(); + let Some((cmd, args)) = parts.split_first() else { + return Err(GeneratorError::ChainSpecGeneration(format!( + "Invalid generator command: {full_cmd}" + ))); + }; + trace!("cmd: {:?} - args: {:?}", cmd, args); + + let generate_command = GenerateFileCommand::new(cmd, raw_spec_path.clone()).args(args); + + if let Some(cmd) = &self.command { + match cmd { + CommandInContext::Local(_, expected_path) => { + build_locally(generate_command, scoped_fs, expected_path.as_deref()).await? + }, + CommandInContext::Remote(_, expected_path) => { + let options = GenerateFilesOptions::with_files( + vec![generate_command], + self.image.clone(), + &[TransferedFile::new(chain_spec_path_local, chain_spec_path_in_pod)], + expected_path.clone(), + ) + .temp_name(temp_name); + trace!("calling generate_files with options: {:#?}", options); + ns.generate_files(options).await?; + }, + } + } + + self.raw_path = Some(raw_spec_path.clone()); + let (content, _) = self.read_spec(scoped_fs).await?; + let content = self.ensure_para_fields_in_raw(&content, relay_chain_id).await?; + self.write_spec(scoped_fs, content).await?; + + Ok(()) + } + + async fn ensure_para_fields_in_raw( + &mut self, + content: &str, + relay_chain_id: Option, + ) -> Result { + if let Context::Para { relay_chain: _, para_id } = &self.context { + let mut chain_spec_json: serde_json::Value = + serde_json::from_str(content).map_err(|e| { + GeneratorError::ChainSpecGeneration(format!( + "getting chain-spec as json should work, err: {e}" + )) + })?; + + let mut needs_write = false; + + if chain_spec_json["relay_chain"].is_null() { + chain_spec_json["relay_chain"] = json!(relay_chain_id); + needs_write = true; + } + + if chain_spec_json["para_id"].is_null() { + chain_spec_json["para_id"] = json!(para_id); + needs_write = true; + } + + if needs_write { + let contents = serde_json::to_string_pretty(&chain_spec_json).map_err(|e| { + GeneratorError::ChainSpecGeneration(format!( + "getting chain-spec json as pretty string should work, err: {e}" + )) + })?; + return Ok(contents); + } + } + + Ok(content.to_string()) + } + + fn should_retry_with_command(err: &GeneratorError) -> bool { + match err { + GeneratorError::ChainSpecGeneration(msg) => { + let msg_lower = msg.to_lowercase(); + msg_lower.contains("genesisbuilder_get_preset") || msg_lower.contains("_get_preset") + }, + _ => false, + } + } + + /// Override the :code in chain-spec raw version + pub async fn override_code<'a, T>( + &mut self, + scoped_fs: &ScopedFilesystem<'a, T>, + wasm_override: &AssetLocation, + ) -> Result<(), GeneratorError> + where + T: FileSystem, + { + // first ensure we have the raw version of the chain-spec + let Some(_) = self.raw_path else { + return Err(GeneratorError::OverridingWasm(String::from( + "Raw path should be set at this point.", + ))); + }; + let (content, _) = self.read_spec(scoped_fs).await?; + // read override wasm + let override_content = wasm_override.get_asset().await.map_err(|_| { + GeneratorError::OverridingWasm(format!( + "Can not get asset to override wasm, asset: {wasm_override}" + )) + })?; + + // read spec to json value + let mut chain_spec_json: serde_json::Value = + serde_json::from_str(&content).map_err(|_| { + GeneratorError::ChainSpecGeneration("Can not parse chain-spec as json".into()) + })?; + + // override :code + let Some(code) = chain_spec_json.pointer_mut("/genesis/raw/top/0x3a636f6465") else { + return Err(GeneratorError::OverridingWasm(String::from( + "Pointer '/genesis/raw/top/0x3a636f6465' should be valid in the raw spec.", + ))); + }; + + info!( + "🖋 Overriding ':code' (0x3a636f6465) in raw chain-spec with content of {}", + wasm_override + ); + *code = json!(format!("0x{}", hex::encode(override_content))); + + let overrided_content = serde_json::to_string_pretty(&chain_spec_json).map_err(|_| { + GeneratorError::ChainSpecGeneration("can not parse chain-spec value as json".into()) + })?; + // save it + self.write_spec(scoped_fs, overrided_content).await?; + + Ok(()) + } + + pub async fn override_raw_spec<'a, T>( + &mut self, + scoped_fs: &ScopedFilesystem<'a, T>, + raw_spec_overrides: &JsonOverrides, + ) -> Result<(), GeneratorError> + where + T: FileSystem, + { + // first ensure we have the raw version of the chain-spec + let Some(_) = self.raw_path else { + return Err(GeneratorError::OverridingRawSpec(String::from( + "Raw path should be set at this point.", + ))); + }; + + let (content, _) = self.read_spec(scoped_fs).await?; + + // read overrides to json value + let override_content: serde_json::Value = raw_spec_overrides.get().await.map_err(|_| { + GeneratorError::OverridingRawSpec(format!( + "Can not parse raw_spec_override contents as json: {raw_spec_overrides}" + )) + })?; + + // read spec to json value + let mut chain_spec_json: serde_json::Value = + serde_json::from_str(&content).map_err(|_| { + GeneratorError::ChainSpecGeneration("Can not parse chain-spec as json".into()) + })?; + + // merge overrides with existing spec + merge(&mut chain_spec_json, &override_content); + + // save changes + let overrided_content = serde_json::to_string_pretty(&chain_spec_json).map_err(|_| { + GeneratorError::ChainSpecGeneration("can not parse chain-spec value as json".into()) + })?; + self.write_spec(scoped_fs, overrided_content).await?; + + Ok(()) + } + + pub fn raw_path(&self) -> Option<&Path> { + self.raw_path.as_deref() + } + + pub fn set_asset_location(&mut self, location: AssetLocation) { + self.asset_location = Some(location) + } + + pub async fn read_chain_id<'a, T>( + &self, + scoped_fs: &ScopedFilesystem<'a, T>, + ) -> Result + where + T: FileSystem, + { + let (content, _) = self.read_spec(scoped_fs).await?; + ChainSpec::chain_id_from_spec(&content) + } + + async fn read_spec<'a, T>( + &self, + scoped_fs: &ScopedFilesystem<'a, T>, + ) -> Result<(String, ChainSpecFormat), GeneratorError> + where + T: FileSystem, + { + let (path, format) = match (self.maybe_plain_path.as_ref(), self.raw_path.as_ref()) { + (Some(path), None) => (path, ChainSpecFormat::Plain), + (None, Some(path)) => (path, ChainSpecFormat::Raw), + (Some(_), Some(path)) => { + // if we have both paths return the raw + (path, ChainSpecFormat::Raw) + }, + (None, None) => unreachable!(), + }; + + let content = scoped_fs.read_to_string(path.clone()).await.map_err(|_| { + GeneratorError::ChainSpecGeneration(format!( + "Can not read chain-spec from {}", + path.to_string_lossy() + )) + })?; + + Ok((content, format)) + } + + async fn write_spec<'a, T>( + &self, + scoped_fs: &ScopedFilesystem<'a, T>, + content: impl Into, + ) -> Result<(), GeneratorError> + where + T: FileSystem, + { + let (path, _format) = match (self.maybe_plain_path.as_ref(), self.raw_path.as_ref()) { + (Some(path), None) => (path, ChainSpecFormat::Plain), + (None, Some(path)) => (path, ChainSpecFormat::Raw), + (Some(_), Some(path)) => { + // if we have both paths return the raw + (path, ChainSpecFormat::Raw) + }, + (None, None) => unreachable!(), + }; + + scoped_fs.write(path, content.into()).await.map_err(|_| { + GeneratorError::ChainSpecGeneration(format!( + "Can not write chain-spec from {}", + path.to_string_lossy() + )) + })?; + + Ok(()) + } + + // TODO: (javier) move this fns to state aware + pub async fn customize_para<'a, T>( + &self, + para: &TeyrchainSpec, + relay_chain_id: &str, + scoped_fs: &ScopedFilesystem<'a, T>, + ) -> Result<(), GeneratorError> + where + T: FileSystem, + { + let (content, format) = self.read_spec(scoped_fs).await?; + let mut chain_spec_json: serde_json::Value = + serde_json::from_str(&content).map_err(|_| { + GeneratorError::ChainSpecGeneration("Can not parse chain-spec as json".into()) + })?; + + if let Some(para_id) = chain_spec_json.get_mut("para_id") { + *para_id = json!(para.id); + }; + if let Some(para_id) = chain_spec_json.get_mut("paraId") { + *para_id = json!(para.id); + }; + + if let Some(relay_chain_id_field) = chain_spec_json.get_mut("relay_chain") { + *relay_chain_id_field = json!(relay_chain_id); + }; + + if let ChainSpecFormat::Plain = format { + let pointer = get_runtime_config_pointer(&chain_spec_json) + .map_err(GeneratorError::ChainSpecGeneration)?; + + // make genesis overrides first. + if let Some(overrides) = ¶.genesis_overrides { + let percolated_overrides = percolate_overrides(&pointer, overrides) + .map_err(|e| GeneratorError::ChainSpecGeneration(e.to_string()))?; + if let Some(genesis) = chain_spec_json.pointer_mut(&pointer) { + merge(genesis, percolated_overrides); + } + } + + clear_authorities(&pointer, &mut chain_spec_json, &self.context); + + let key_type_to_use = + if para.is_evm_based { SessionKeyType::Evm } else { SessionKeyType::Default }; + + // Get validators to add as authorities + let validators: Vec<&NodeSpec> = + para.collators.iter().filter(|node| node.is_validator).collect(); + + // check chain key types + if chain_spec_json.pointer(&format!("{pointer}/session")).is_some() { + add_authorities(&pointer, &mut chain_spec_json, &validators, key_type_to_use); + } else if chain_spec_json.pointer(&format!("{pointer}/aura")).is_some() { + add_aura_authorities(&pointer, &mut chain_spec_json, &validators, KeyType::Aura); + } else { + warn!("Can't customize keys, not `session` or `aura` find in the chain-spec file"); + }; + + // Add nodes to collator + let invulnerables: Vec<&NodeSpec> = + para.collators.iter().filter(|node| node.is_invulnerable).collect(); + + add_collator_selection(&pointer, &mut chain_spec_json, &invulnerables, key_type_to_use); + + // override `parachainInfo/parachainId` + override_parachain_info(&pointer, &mut chain_spec_json, para.id); + + // write spec + let content = serde_json::to_string_pretty(&chain_spec_json).map_err(|_| { + GeneratorError::ChainSpecGeneration("can not parse chain-spec value as json".into()) + })?; + self.write_spec(scoped_fs, content).await?; + } else { + warn!("⚠️ Chain spec for para_id: {} is in raw mode", para.id); + } + Ok(()) + } + + pub async fn customize_relay<'a, T, U>( + &self, + relaychain: &RelaychainSpec, + hrmp_channels: &[HrmpChannelConfig], + para_artifacts: Vec>, + scoped_fs: &ScopedFilesystem<'a, T>, + ) -> Result<(), GeneratorError> + where + T: FileSystem, + U: AsRef, + { + let (content, format) = self.read_spec(scoped_fs).await?; + let mut chain_spec_json: serde_json::Value = + serde_json::from_str(&content).map_err(|_| { + GeneratorError::ChainSpecGeneration("Can not parse chain-spec as json".into()) + })?; + + if let ChainSpecFormat::Plain = format { + // get the tokenDecimals property or set the default (12) + let token_decimals = + if let Some(val) = chain_spec_json.pointer("/properties/tokenDecimals") { + let val = val.as_u64().unwrap_or(12); + if val > u8::MAX as u64 { + 12 + } else { + val as u8 + } + } else { + 12 + }; + // get the config pointer + let pointer = get_runtime_config_pointer(&chain_spec_json) + .map_err(GeneratorError::ChainSpecGeneration)?; + + // make genesis overrides first. + if let Some(overrides) = &relaychain.runtime_genesis_patch { + let percolated_overrides = percolate_overrides(&pointer, overrides) + .map_err(|e| GeneratorError::ChainSpecGeneration(e.to_string()))?; + if let Some(patch_section) = chain_spec_json.pointer_mut(&pointer) { + merge(patch_section, percolated_overrides); + } + } + + // get min stake (to store if neede later) + let staking_min = get_staking_min(&pointer, &mut chain_spec_json); + + // Clear authorities + clear_authorities(&pointer, &mut chain_spec_json, &self.context); + + // add balances + add_balances( + &pointer, + &mut chain_spec_json, + &relaychain.nodes, + token_decimals, + staking_min, + ); + + // add staking + add_staking(&pointer, &mut chain_spec_json, &relaychain.nodes, staking_min); + + // Get validators to add as authorities + let validators: Vec<&NodeSpec> = + relaychain.nodes.iter().filter(|node| node.is_validator).collect(); + + // check chain key types + if chain_spec_json.pointer(&format!("{pointer}/session")).is_some() { + add_authorities(&pointer, &mut chain_spec_json, &validators, SessionKeyType::Stash); + } else { + add_aura_authorities(&pointer, &mut chain_spec_json, &validators, KeyType::Aura); + add_grandpa_authorities(&pointer, &mut chain_spec_json, &validators, KeyType::Aura); + } + + // staking && nominators + + if !hrmp_channels.is_empty() { + add_hrmp_channels(&pointer, &mut chain_spec_json, hrmp_channels); + } + + // paras + for para_genesis_config in para_artifacts.iter() { + add_parachain_to_genesis( + &pointer, + &mut chain_spec_json, + para_genesis_config, + scoped_fs, + ) + .await + .map_err(|e| GeneratorError::ChainSpecGeneration(e.to_string()))?; + } + + // TODO: + // - staking + // - nominators + + // write spec + let content = serde_json::to_string_pretty(&chain_spec_json).map_err(|_| { + GeneratorError::ChainSpecGeneration("can not parse chain-spec value as json".into()) + })?; + self.write_spec(scoped_fs, content).await?; + } else { + warn!( + "⚠️ Chain Spec for chain {} is in raw mode, can't customize.", + self.chain_spec_name + ); + } + Ok(()) + } + + pub async fn add_bootnodes<'a, T>( + &self, + scoped_fs: &ScopedFilesystem<'a, T>, + bootnodes: &[String], + ) -> Result<(), GeneratorError> + where + T: FileSystem, + { + let (content, _) = self.read_spec(scoped_fs).await?; + let mut chain_spec_json: serde_json::Value = + serde_json::from_str(&content).map_err(|_| { + GeneratorError::ChainSpecGeneration("Can not parse chain-spec as json".into()) + })?; + + if let Some(bootnodes_on_file) = chain_spec_json.get_mut("bootNodes") { + if let Some(bootnodes_on_file) = bootnodes_on_file.as_array_mut() { + let mut bootnodes_to_add = + bootnodes.iter().map(|bootnode| json!(bootnode)).collect(); + bootnodes_on_file.append(&mut bootnodes_to_add); + } else { + return Err(GeneratorError::ChainSpecGeneration( + "id should be an string in the chain-spec, this is a bug".into(), + )); + }; + } else { + return Err(GeneratorError::ChainSpecGeneration( + "'bootNodes' should be a fields in the chain-spec of the relaychain".into(), + )); + }; + + // write spec + let content = serde_json::to_string_pretty(&chain_spec_json).map_err(|_| { + GeneratorError::ChainSpecGeneration("can not parse chain-spec value as json".into()) + })?; + self.write_spec(scoped_fs, content).await?; + + Ok(()) + } + + /// Get the chain_is from the json content of a chain-spec file. + pub fn chain_id_from_spec(spec_content: &str) -> Result { + let chain_spec_json: serde_json::Value = + serde_json::from_str(spec_content).map_err(|_| { + GeneratorError::ChainSpecGeneration("Can not parse chain-spec as json".into()) + })?; + if let Some(chain_id) = chain_spec_json.get("id") { + if let Some(chain_id) = chain_id.as_str() { + Ok(chain_id.to_string()) + } else { + Err(GeneratorError::ChainSpecGeneration( + "id should be an string in the chain-spec, this is a bug".into(), + )) + } + } else { + Err(GeneratorError::ChainSpecGeneration( + "'id' should be a fields in the chain-spec of the relaychain".into(), + )) + } + } } type GenesisNodeKey = (String, String, HashMap); async fn build_locally<'a, T>( - generate_command: GenerateFileCommand, - scoped_fs: &ScopedFilesystem<'a, T>, - maybe_output: Option<&Path>, + generate_command: GenerateFileCommand, + scoped_fs: &ScopedFilesystem<'a, T>, + maybe_output: Option<&Path>, ) -> Result<(), GeneratorError> where - T: FileSystem, + T: FileSystem, { - // generate_command. + // generate_command. - let result = Command::new(generate_command.program.clone()) - .args(generate_command.args.clone()) - .output() - .await - .map_err(|err| { - GeneratorError::ChainSpecGeneration(format!( - "Error running cmd: {} args: {}, err: {}", - &generate_command.program, - &generate_command.args.join(" "), - err - )) - })?; + let result = Command::new(generate_command.program.clone()) + .args(generate_command.args.clone()) + .output() + .await + .map_err(|err| { + GeneratorError::ChainSpecGeneration(format!( + "Error running cmd: {} args: {}, err: {}", + &generate_command.program, + &generate_command.args.join(" "), + err + )) + })?; - if result.status.success() { - let raw_output = if let Some(output_path) = maybe_output { - tokio::fs::read(output_path).await.map_err(|err| { - GeneratorError::ChainSpecGeneration(format!( - "Error reading output file at {}: {}", - output_path.display(), - err - )) - })? - } else { - result.stdout - }; - scoped_fs - .write( - generate_command.local_output_path, - String::from_utf8_lossy(&raw_output).to_string(), - ) - .await?; - Ok(()) - } else { - Err(GeneratorError::ChainSpecGeneration(format!( - "Error running cmd: {} args: {}, err: {}", - &generate_command.program, - &generate_command.args.join(" "), - String::from_utf8_lossy(&result.stderr) - ))) - } + if result.status.success() { + let raw_output = if let Some(output_path) = maybe_output { + tokio::fs::read(output_path).await.map_err(|err| { + GeneratorError::ChainSpecGeneration(format!( + "Error reading output file at {}: {}", + output_path.display(), + err + )) + })? + } else { + result.stdout + }; + scoped_fs + .write( + generate_command.local_output_path, + String::from_utf8_lossy(&raw_output).to_string(), + ) + .await?; + Ok(()) + } else { + Err(GeneratorError::ChainSpecGeneration(format!( + "Error running cmd: {} args: {}, err: {}", + &generate_command.program, + &generate_command.args.join(" "), + String::from_utf8_lossy(&result.stderr) + ))) + } } async fn is_raw<'a, T>( - file: PathBuf, - scoped_fs: &ScopedFilesystem<'a, T>, + file: PathBuf, + scoped_fs: &ScopedFilesystem<'a, T>, ) -> Result where - T: FileSystem, + T: FileSystem, { - let content = scoped_fs.read_to_string(file).await?; - let chain_spec_json: serde_json::Value = serde_json::from_str(&content).unwrap(); + let content = scoped_fs.read_to_string(file).await?; + let chain_spec_json: serde_json::Value = serde_json::from_str(&content).unwrap(); - Ok(chain_spec_json.pointer("/genesis/raw/top").is_some()) + Ok(chain_spec_json.pointer("/genesis/raw/top").is_some()) } // Internal Chain-spec customizations async fn add_parachain_to_genesis<'a, T, U>( - runtime_config_ptr: &str, - chain_spec_json: &mut serde_json::Value, - para_genesis_config: &ParaGenesisConfig, - scoped_fs: &ScopedFilesystem<'a, T>, + runtime_config_ptr: &str, + chain_spec_json: &mut serde_json::Value, + para_genesis_config: &ParaGenesisConfig, + scoped_fs: &ScopedFilesystem<'a, T>, ) -> Result<(), anyhow::Error> where - T: FileSystem, - U: AsRef, + T: FileSystem, + U: AsRef, { - if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { - let paras_pointer = if val.get("paras").is_some() { - "/paras/paras" - } else if val.get("parachainsParas").is_some() { - // For retro-compatibility with substrate pre Polkadot 0.9.5 - "/parachainsParas/paras" - } else { - // The config may not contain paras. Since chainspec allows to contain the RuntimeGenesisConfig patch we can inject it. - val["paras"] = json!({ "paras": [] }); - "/paras/paras" - }; + if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { + let paras_pointer = if val.get("paras").is_some() { + "/paras/paras" + } else if val.get("parachainsParas").is_some() { + // For retro-compatibility with substrate pre Polkadot 0.9.5 + "/parachainsParas/paras" + } else { + // The config may not contain paras. Since chainspec allows to contain the RuntimeGenesisConfig patch we can inject it. + val["paras"] = json!({ "paras": [] }); + "/paras/paras" + }; - let paras = val - .pointer_mut(paras_pointer) - .ok_or(anyhow!("paras pointer should be valid {paras_pointer:?} "))?; - let paras_vec = paras - .as_array_mut() - .ok_or(anyhow!("paras should be an array"))?; + let paras = val + .pointer_mut(paras_pointer) + .ok_or(anyhow!("paras pointer should be valid {paras_pointer:?} "))?; + let paras_vec = paras.as_array_mut().ok_or(anyhow!("paras should be an array"))?; - let head = scoped_fs - .read_to_string(para_genesis_config.state_path.as_ref()) - .await?; - let wasm = scoped_fs - .read_to_string(para_genesis_config.wasm_path.as_ref()) - .await?; + let head = scoped_fs.read_to_string(para_genesis_config.state_path.as_ref()).await?; + let wasm = scoped_fs.read_to_string(para_genesis_config.wasm_path.as_ref()).await?; - paras_vec.push(json!([ - para_genesis_config.id, - [head.trim(), wasm.trim(), para_genesis_config.as_parachain] - ])); + paras_vec.push(json!([ + para_genesis_config.id, + [head.trim(), wasm.trim(), para_genesis_config.as_parachain] + ])); - Ok(()) - } else { - unreachable!("pointer to runtime config should be valid!") - } + Ok(()) + } else { + unreachable!("pointer to runtime config should be valid!") + } } fn get_runtime_config_pointer(chain_spec_json: &serde_json::Value) -> Result { - // runtime_genesis_config is no longer in ChainSpec after rococo runtime rework (refer to: https://github.com/paritytech/polkadot-sdk/pull/1256) - // ChainSpec may contain a RuntimeGenesisConfigPatch - let pointers = [ - "/genesis/runtimeGenesis/config", - "/genesis/runtimeGenesis/patch", - "/genesis/runtimeGenesisConfigPatch", - "/genesis/runtime/runtime_genesis_config", - "/genesis/runtime", - ]; + // runtime_genesis_config is no longer in ChainSpec after rococo runtime rework (refer to: https://github.com/paritytech/polkadot-sdk/pull/1256) + // ChainSpec may contain a RuntimeGenesisConfigPatch + let pointers = [ + "/genesis/runtimeGenesis/config", + "/genesis/runtimeGenesis/patch", + "/genesis/runtimeGenesisConfigPatch", + "/genesis/runtime/runtime_genesis_config", + "/genesis/runtime", + ]; - for pointer in pointers { - if chain_spec_json.pointer(pointer).is_some() { - return Ok(pointer.to_string()); - } - } + for pointer in pointers { + if chain_spec_json.pointer(pointer).is_some() { + return Ok(pointer.to_string()); + } + } - Err("Can not find the runtime pointer".into()) + Err("Can not find the runtime pointer".into()) } fn percolate_overrides<'a>( - pointer: &str, - overrides: &'a serde_json::Value, + pointer: &str, + overrides: &'a serde_json::Value, ) -> Result<&'a serde_json::Value, anyhow::Error> { - let pointer_parts = pointer.split('/').collect::>(); - trace!("pointer_parts: {pointer_parts:?}"); + let pointer_parts = pointer.split('/').collect::>(); + trace!("pointer_parts: {pointer_parts:?}"); - let top_level = overrides - .as_object() - .ok_or_else(|| anyhow!("Overrides must be an object"))?; - let top_level_key = top_level - .keys() - .next() - .ok_or_else(|| anyhow!("Invalid override value: {overrides:?}"))?; - trace!("top_level_key: {top_level_key}"); - let index = pointer_parts.iter().position(|x| *x == top_level_key); - let Some(i) = index else { - warn!("Top level key '{top_level_key}' isn't part of the pointer ({pointer}), returning without percolating"); - return Ok(overrides); - }; + let top_level = overrides.as_object().ok_or_else(|| anyhow!("Overrides must be an object"))?; + let top_level_key = + top_level.keys().next().ok_or_else(|| anyhow!("Invalid override value: {overrides:?}"))?; + trace!("top_level_key: {top_level_key}"); + let index = pointer_parts.iter().position(|x| *x == top_level_key); + let Some(i) = index else { + warn!("Top level key '{top_level_key}' isn't part of the pointer ({pointer}), returning without percolating"); + return Ok(overrides); + }; - let p = if i == pointer_parts.len() - 1 { - // top level key is at end of the pointer - let p = format!("/{}", pointer_parts[i]); - trace!("overrides pointer {p}"); - p - } else { - // example: pointer is `/genesis/runtimeGenesis/patch` and the overrides start at `runtimeGenesis` - let p = format!("/{}", pointer_parts[i..].join("/")); - trace!("overrides pointer {p}"); - p - }; - let overrides_to_use = overrides - .pointer(&p) - .ok_or_else(|| anyhow!("Invalid override value: {overrides:?}"))?; - Ok(overrides_to_use) + let p = if i == pointer_parts.len() - 1 { + // top level key is at end of the pointer + let p = format!("/{}", pointer_parts[i]); + trace!("overrides pointer {p}"); + p + } else { + // example: pointer is `/genesis/runtimeGenesis/patch` and the overrides start at `runtimeGenesis` + let p = format!("/{}", pointer_parts[i..].join("/")); + trace!("overrides pointer {p}"); + p + }; + let overrides_to_use = + overrides.pointer(&p).ok_or_else(|| anyhow!("Invalid override value: {overrides:?}"))?; + Ok(overrides_to_use) } #[allow(dead_code)] fn construct_runtime_pointer_from_overrides( - overrides: &serde_json::Value, + overrides: &serde_json::Value, ) -> Result { - if overrides.get("genesis").is_some() { - // overrides already start with /genesis - return Ok("/genesis".into()); - } else { - // check if we are one level inner - if let Some(top_level) = overrides.as_object() { - let k = top_level - .keys() - .next() - .ok_or_else(|| anyhow!("Invalid override value: {overrides:?}"))?; - match k.as_str() { - "runtimeGenesisConfigPatch" | "runtime" | "runtimeGenesis" => { - return Ok(("/genesis").into()) - }, - "config" | "path" => { - return Ok(("/genesis/runtimeGenesis").into()); - }, - "runtime_genesis_config" => { - return Ok(("/genesis/runtime").into()); - }, - _ => {}, - } - } - } + if overrides.get("genesis").is_some() { + // overrides already start with /genesis + return Ok("/genesis".into()); + } else { + // check if we are one level inner + if let Some(top_level) = overrides.as_object() { + let k = top_level + .keys() + .next() + .ok_or_else(|| anyhow!("Invalid override value: {overrides:?}"))?; + match k.as_str() { + "runtimeGenesisConfigPatch" | "runtime" | "runtimeGenesis" => { + return Ok(("/genesis").into()) + }, + "config" | "path" => { + return Ok(("/genesis/runtimeGenesis").into()); + }, + "runtime_genesis_config" => { + return Ok(("/genesis/runtime").into()); + }, + _ => {}, + } + } + } - Err(anyhow!("Can not find the runtime pointer")) + Err(anyhow!("Can not find the runtime pointer")) } // Merge `patch_section` with `overrides`. fn merge(patch_section: &mut serde_json::Value, overrides: &serde_json::Value) { - trace!("patch: {:?}", patch_section); - trace!("overrides: {:?}", overrides); - if let (Some(genesis_obj), Some(overrides_obj)) = - (patch_section.as_object_mut(), overrides.as_object()) - { - for overrides_key in overrides_obj.keys() { - trace!("overrides_key: {:?}", overrides_key); - // we only want to override keys present in the genesis object - if let Some(genesis_value) = genesis_obj.get_mut(overrides_key) { - match (&genesis_value, overrides_obj.get(overrides_key)) { - // recurse if genesis value is an object - (serde_json::Value::Object(_), Some(overrides_value)) - if overrides_value.is_object() => - { - merge(genesis_value, overrides_value); - }, - // override if genesis value not an object - (_, Some(overrides_value)) => { - trace!("overriding: {:?} / {:?}", genesis_value, overrides_value); - *genesis_value = overrides_value.clone(); - }, - _ => { - trace!("not match!"); - }, - } - } else { - // Allow to add keys, see (https://github.com/paritytech/zombienet/issues/1614) - warn!( - "key: {overrides_key} not present in genesis_obj: {:?} (adding key)", - genesis_obj - ); - let overrides_value = overrides_obj.get(overrides_key).expect(&format!( - "overrides_key {overrides_key} should be present in the overrides obj. qed" - )); - genesis_obj.insert(overrides_key.clone(), overrides_value.clone()); - } - } - } + trace!("patch: {:?}", patch_section); + trace!("overrides: {:?}", overrides); + if let (Some(genesis_obj), Some(overrides_obj)) = + (patch_section.as_object_mut(), overrides.as_object()) + { + for overrides_key in overrides_obj.keys() { + trace!("overrides_key: {:?}", overrides_key); + // we only want to override keys present in the genesis object + if let Some(genesis_value) = genesis_obj.get_mut(overrides_key) { + match (&genesis_value, overrides_obj.get(overrides_key)) { + // recurse if genesis value is an object + (serde_json::Value::Object(_), Some(overrides_value)) + if overrides_value.is_object() => + { + merge(genesis_value, overrides_value); + }, + // override if genesis value not an object + (_, Some(overrides_value)) => { + trace!("overriding: {:?} / {:?}", genesis_value, overrides_value); + *genesis_value = overrides_value.clone(); + }, + _ => { + trace!("not match!"); + }, + } + } else { + // Allow to add keys, see (https://github.com/paritytech/zombienet/issues/1614) + warn!( + "key: {overrides_key} not present in genesis_obj: {:?} (adding key)", + genesis_obj + ); + let overrides_value = overrides_obj.get(overrides_key).expect(&format!( + "overrides_key {overrides_key} should be present in the overrides obj. qed" + )); + genesis_obj.insert(overrides_key.clone(), overrides_value.clone()); + } + } + } } fn clear_authorities( - runtime_config_ptr: &str, - chain_spec_json: &mut serde_json::Value, - ctx: &Context, + runtime_config_ptr: &str, + chain_spec_json: &mut serde_json::Value, + ctx: &Context, ) { - if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { - // clear keys (session, aura, grandpa) - if val.get("session").is_some() { - val["session"]["keys"] = json!([]); - } + if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { + // clear keys (session, aura, grandpa) + if val.get("session").is_some() { + val["session"]["keys"] = json!([]); + } - if val.get("aura").is_some() { - val["aura"]["authorities"] = json!([]); - } + if val.get("aura").is_some() { + val["aura"]["authorities"] = json!([]); + } - if val.get("grandpa").is_some() { - val["grandpa"]["authorities"] = json!([]); - } + if val.get("grandpa").is_some() { + val["grandpa"]["authorities"] = json!([]); + } - // clear collatorSelector - if val.get("collatorSelection").is_some() { - val["collatorSelection"]["invulnerables"] = json!([]); - } + // clear collatorSelector + if val.get("collatorSelection").is_some() { + val["collatorSelection"]["invulnerables"] = json!([]); + } - // clear staking but not `validatorCount` if `devStakers` is set - if val.get("staking").is_some() && ctx == &Context::Relay { - val["staking"]["invulnerables"] = json!([]); - val["staking"]["stakers"] = json!([]); + // clear staking but not `validatorCount` if `devStakers` is set + if val.get("staking").is_some() && ctx == &Context::Relay { + val["staking"]["invulnerables"] = json!([]); + val["staking"]["stakers"] = json!([]); - if val["staking"]["devStakers"] == json!(null) { - val["staking"]["validatorCount"] = json!(0); - } - } - } else { - unreachable!("pointer to runtime config should be valid!") - } + if val["staking"]["devStakers"] == json!(null) { + val["staking"]["validatorCount"] = json!(0); + } + } + } else { + unreachable!("pointer to runtime config should be valid!") + } } fn get_staking_min(runtime_config_ptr: &str, chain_spec_json: &mut serde_json::Value) -> u128 { - // get min staking - let staking_ptr = format!("{runtime_config_ptr}/staking/stakers"); - if let Some(stakers) = chain_spec_json.pointer(&staking_ptr) { - // stakers should be an array - let min = stakers[0][2].clone(); - min.as_u64().unwrap_or(0).into() - } else { - 0 - } + // get min staking + let staking_ptr = format!("{runtime_config_ptr}/staking/stakers"); + if let Some(stakers) = chain_spec_json.pointer(&staking_ptr) { + // stakers should be an array + let min = stakers[0][2].clone(); + min.as_u64().unwrap_or(0).into() + } else { + 0 + } } fn add_balances( - runtime_config_ptr: &str, - chain_spec_json: &mut serde_json::Value, - nodes: &Vec, - token_decimals: u8, - staking_min: u128, + runtime_config_ptr: &str, + chain_spec_json: &mut serde_json::Value, + nodes: &Vec, + token_decimals: u8, + staking_min: u128, ) { - if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { - let Some(balances) = val.pointer("/balances/balances") else { - // should be a info log - warn!("NO 'balances' key in runtime config, skipping..."); - return; - }; + if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { + let Some(balances) = val.pointer("/balances/balances") else { + // should be a info log + warn!("NO 'balances' key in runtime config, skipping..."); + return; + }; - // create a balance map - let mut balances_map = generate_balance_map(balances); - for node in nodes { - if node.initial_balance.eq(&0) { - continue; - }; + // create a balance map + let mut balances_map = generate_balance_map(balances); + for node in nodes { + if node.initial_balance.eq(&0) { + continue; + }; - // TODO: handle error here and check the `accounts.accounts` design - // Double down the minimal stake defined - let balance = std::cmp::max(node.initial_balance, staking_min * 2); - for k in ["sr", "sr_stash"] { - let account = node.accounts.accounts.get(k).unwrap(); - balances_map.insert(account.address.clone(), balance); - } - } + // TODO: handle error here and check the `accounts.accounts` design + // Double down the minimal stake defined + let balance = std::cmp::max(node.initial_balance, staking_min * 2); + for k in ["sr", "sr_stash"] { + let account = node.accounts.accounts.get(k).unwrap(); + balances_map.insert(account.address.clone(), balance); + } + } - // ensure zombie account (//Zombie) have funds - // we will use for internal usage (e.g new validators) - balances_map.insert( - "5FTcLfwFc7ctvqp3RhbEig6UuHLHcHVRujuUm8r21wy4dAR8".to_string(), - 1000 * 10_u128.pow(token_decimals as u32), - ); + // ensure zombie account (//Zombie) have funds + // we will use for internal usage (e.g new validators) + balances_map.insert( + "5FTcLfwFc7ctvqp3RhbEig6UuHLHcHVRujuUm8r21wy4dAR8".to_string(), + 1000 * 10_u128.pow(token_decimals as u32), + ); - // convert the map and store again - let new_balances: Vec<(&String, &u128)> = - balances_map.iter().collect::>(); + // convert the map and store again + let new_balances: Vec<(&String, &u128)> = + balances_map.iter().collect::>(); - val["balances"]["balances"] = json!(new_balances); - } else { - unreachable!("pointer to runtime config should be valid!") - } + val["balances"]["balances"] = json!(new_balances); + } else { + unreachable!("pointer to runtime config should be valid!") + } } fn get_node_keys( - node: &NodeSpec, - session_key: SessionKeyType, - asset_hub_polkadot: bool, + node: &NodeSpec, + session_key: SessionKeyType, + asset_hub_polkadot: bool, ) -> GenesisNodeKey { - let sr_account = node.accounts.accounts.get("sr").unwrap(); - let sr_stash = node.accounts.accounts.get("sr_stash").unwrap(); - let ed_account = node.accounts.accounts.get("ed").unwrap(); - let ec_account = node.accounts.accounts.get("ec").unwrap(); - let eth_account = node.accounts.accounts.get("eth").unwrap(); - let mut keys = HashMap::new(); - for k in [ - "babe", - "im_online", - "parachain_validator", - "authority_discovery", - "para_validator", - "para_assignment", - "aura", - "nimbus", - "vrf", - ] { - if k == "aura" && asset_hub_polkadot { - keys.insert(k.to_string(), ed_account.address.clone()); - continue; - } - keys.insert(k.to_string(), sr_account.address.clone()); - } + let sr_account = node.accounts.accounts.get("sr").unwrap(); + let sr_stash = node.accounts.accounts.get("sr_stash").unwrap(); + let ed_account = node.accounts.accounts.get("ed").unwrap(); + let ec_account = node.accounts.accounts.get("ec").unwrap(); + let eth_account = node.accounts.accounts.get("eth").unwrap(); + let mut keys = HashMap::new(); + for k in [ + "babe", + "im_online", + "parachain_validator", + "authority_discovery", + "para_validator", + "para_assignment", + "aura", + "nimbus", + "vrf", + ] { + if k == "aura" && asset_hub_polkadot { + keys.insert(k.to_string(), ed_account.address.clone()); + continue; + } + keys.insert(k.to_string(), sr_account.address.clone()); + } - keys.insert("grandpa".to_string(), ed_account.address.clone()); - keys.insert("beefy".to_string(), ec_account.address.clone()); - keys.insert("eth".to_string(), eth_account.public_key.clone()); + keys.insert("grandpa".to_string(), ed_account.address.clone()); + keys.insert("beefy".to_string(), ec_account.address.clone()); + keys.insert("eth".to_string(), eth_account.public_key.clone()); - let account_to_use = match session_key { - SessionKeyType::Default => sr_account.address.clone(), - SessionKeyType::Stash => sr_stash.address.clone(), - SessionKeyType::Evm => format!("0x{}", eth_account.public_key), - }; + let account_to_use = match session_key { + SessionKeyType::Default => sr_account.address.clone(), + SessionKeyType::Stash => sr_stash.address.clone(), + SessionKeyType::Evm => format!("0x{}", eth_account.public_key), + }; - (account_to_use.clone(), account_to_use, keys) + (account_to_use.clone(), account_to_use, keys) } fn add_authorities( - runtime_config_ptr: &str, - chain_spec_json: &mut serde_json::Value, - nodes: &[&NodeSpec], - session_key: SessionKeyType, + runtime_config_ptr: &str, + chain_spec_json: &mut serde_json::Value, + nodes: &[&NodeSpec], + session_key: SessionKeyType, ) { - let asset_hub_polkadot = chain_spec_json - .get("id") - .and_then(|v| v.as_str()) - .map(|id| id.starts_with("asset-hub-polkadot")) - .unwrap_or_default(); - if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { - if let Some(session_keys) = val.pointer_mut("/session/keys") { - let keys: Vec = nodes - .iter() - .map(|node| get_node_keys(node, session_key, asset_hub_polkadot)) - .collect(); - *session_keys = json!(keys); - } else { - warn!("⚠️ 'session/keys' key not present in runtime config."); - } - } else { - unreachable!("pointer to runtime config should be valid!") - } + let asset_hub_polkadot = chain_spec_json + .get("id") + .and_then(|v| v.as_str()) + .map(|id| id.starts_with("asset-hub-polkadot")) + .unwrap_or_default(); + if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { + if let Some(session_keys) = val.pointer_mut("/session/keys") { + let keys: Vec = nodes + .iter() + .map(|node| get_node_keys(node, session_key, asset_hub_polkadot)) + .collect(); + *session_keys = json!(keys); + } else { + warn!("⚠️ 'session/keys' key not present in runtime config."); + } + } else { + unreachable!("pointer to runtime config should be valid!") + } } fn add_hrmp_channels( - runtime_config_ptr: &str, - chain_spec_json: &mut serde_json::Value, - hrmp_channels: &[HrmpChannelConfig], + runtime_config_ptr: &str, + chain_spec_json: &mut serde_json::Value, + hrmp_channels: &[HrmpChannelConfig], ) { - if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { - if let Some(preopen_hrmp_channels) = val.pointer_mut("/hrmp/preopenHrmpChannels") { - let hrmp_channels = hrmp_channels - .iter() - .map(|c| { - ( - c.sender(), - c.recipient(), - c.max_capacity(), - c.max_message_size(), - ) - }) - .collect::>(); - *preopen_hrmp_channels = json!(hrmp_channels); - } else { - warn!("⚠️ 'hrmp/preopenHrmpChannels' key not present in runtime config."); - } - } else { - unreachable!("pointer to runtime config should be valid!") - } + if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { + if let Some(preopen_hrmp_channels) = val.pointer_mut("/hrmp/preopenHrmpChannels") { + let hrmp_channels = hrmp_channels + .iter() + .map(|c| (c.sender(), c.recipient(), c.max_capacity(), c.max_message_size())) + .collect::>(); + *preopen_hrmp_channels = json!(hrmp_channels); + } else { + warn!("⚠️ 'hrmp/preopenHrmpChannels' key not present in runtime config."); + } + } else { + unreachable!("pointer to runtime config should be valid!") + } } fn add_aura_authorities( - runtime_config_ptr: &str, - chain_spec_json: &mut serde_json::Value, - nodes: &[&NodeSpec], - _key_type: KeyType, + runtime_config_ptr: &str, + chain_spec_json: &mut serde_json::Value, + nodes: &[&NodeSpec], + _key_type: KeyType, ) { - if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { - if let Some(aura_authorities) = val.pointer_mut("/aura/authorities") { - let keys: Vec = nodes - .iter() - .map(|node| { - node.accounts - .accounts - .get("sr") - .expect(&format!( - "'sr' account should be set at spec computation {THIS_IS_A_BUG}" - )) - .address - .clone() - }) - .collect(); - *aura_authorities = json!(keys); - } else { - warn!("⚠️ 'aura/authorities' key not present in runtime config."); - } - } else { - unreachable!("pointer to runtime config should be valid!") - } + if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { + if let Some(aura_authorities) = val.pointer_mut("/aura/authorities") { + let keys: Vec = nodes + .iter() + .map(|node| { + node.accounts + .accounts + .get("sr") + .expect(&format!( + "'sr' account should be set at spec computation {THIS_IS_A_BUG}" + )) + .address + .clone() + }) + .collect(); + *aura_authorities = json!(keys); + } else { + warn!("⚠️ 'aura/authorities' key not present in runtime config."); + } + } else { + unreachable!("pointer to runtime config should be valid!") + } } fn add_grandpa_authorities( - runtime_config_ptr: &str, - chain_spec_json: &mut serde_json::Value, - nodes: &[&NodeSpec], - _key_type: KeyType, + runtime_config_ptr: &str, + chain_spec_json: &mut serde_json::Value, + nodes: &[&NodeSpec], + _key_type: KeyType, ) { - if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { - if let Some(grandpa_authorities) = val.pointer_mut("/grandpa/authorities") { - let keys: Vec<(String, usize)> = nodes - .iter() - .map(|node| { - ( - node.accounts - .accounts - .get("ed") - .expect(&format!( - "'ed' account should be set at spec computation {THIS_IS_A_BUG}" - )) - .address - .clone(), - 1, - ) - }) - .collect(); - *grandpa_authorities = json!(keys); - } else { - warn!("⚠️ 'grandpa/authorities' key not present in runtime config."); - } - } else { - unreachable!("pointer to runtime config should be valid!") - } + if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { + if let Some(grandpa_authorities) = val.pointer_mut("/grandpa/authorities") { + let keys: Vec<(String, usize)> = nodes + .iter() + .map(|node| { + ( + node.accounts + .accounts + .get("ed") + .expect(&format!( + "'ed' account should be set at spec computation {THIS_IS_A_BUG}" + )) + .address + .clone(), + 1, + ) + }) + .collect(); + *grandpa_authorities = json!(keys); + } else { + warn!("⚠️ 'grandpa/authorities' key not present in runtime config."); + } + } else { + unreachable!("pointer to runtime config should be valid!") + } } fn add_staking( - runtime_config_ptr: &str, - chain_spec_json: &mut serde_json::Value, - nodes: &Vec, - staking_min: u128, + runtime_config_ptr: &str, + chain_spec_json: &mut serde_json::Value, + nodes: &Vec, + staking_min: u128, ) { - if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { - let Some(_) = val.pointer("/staking") else { - // should be a info log - warn!("NO 'staking' key in runtime config, skipping..."); - return; - }; + if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { + let Some(_) = val.pointer("/staking") else { + // should be a info log + warn!("NO 'staking' key in runtime config, skipping..."); + return; + }; - let mut stakers = vec![]; - let mut invulnerables = vec![]; - for node in nodes { - let sr_stash_addr = &node - .accounts - .accounts - .get("sr_stash") - .expect("'sr_stash account should be defined for the node. qed") - .address; - stakers.push(json!([ - sr_stash_addr, - sr_stash_addr, - staking_min, - "Validator" - ])); + let mut stakers = vec![]; + let mut invulnerables = vec![]; + for node in nodes { + let sr_stash_addr = &node + .accounts + .accounts + .get("sr_stash") + .expect("'sr_stash account should be defined for the node. qed") + .address; + stakers.push(json!([sr_stash_addr, sr_stash_addr, staking_min, "Validator"])); - if node.is_invulnerable { - invulnerables.push(sr_stash_addr); - } - } + if node.is_invulnerable { + invulnerables.push(sr_stash_addr); + } + } - val["staking"]["validatorCount"] = json!(stakers.len()); - val["staking"]["stakers"] = json!(stakers); - val["staking"]["invulnerables"] = json!(invulnerables); - } else { - unreachable!("pointer to runtime config should be valid!") - } + val["staking"]["validatorCount"] = json!(stakers.len()); + val["staking"]["stakers"] = json!(stakers); + val["staking"]["invulnerables"] = json!(invulnerables); + } else { + unreachable!("pointer to runtime config should be valid!") + } } // TODO: (team) @@ -1597,477 +1495,413 @@ fn add_staking( // // current version (ts). // fn para_custom() { todo!() } fn override_parachain_info( - runtime_config_ptr: &str, - chain_spec_json: &mut serde_json::Value, - para_id: u32, + runtime_config_ptr: &str, + chain_spec_json: &mut serde_json::Value, + para_id: u32, ) { - if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { - if let Some(parachain_id) = val.pointer_mut("/parachainInfo/parachainId") { - *parachain_id = json!(para_id) - } else { - // Add warning here! - } - } else { - unreachable!("pointer to runtime config should be valid!") - } + if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { + if let Some(parachain_id) = val.pointer_mut("/parachainInfo/parachainId") { + *parachain_id = json!(para_id) + } else { + // Add warning here! + } + } else { + unreachable!("pointer to runtime config should be valid!") + } } fn add_collator_selection( - runtime_config_ptr: &str, - chain_spec_json: &mut serde_json::Value, - nodes: &[&NodeSpec], - session_key: SessionKeyType, + runtime_config_ptr: &str, + chain_spec_json: &mut serde_json::Value, + nodes: &[&NodeSpec], + session_key: SessionKeyType, ) { - if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { - let key_type = if let SessionKeyType::Evm = session_key { - "eth" - } else { - "sr" - }; - let keys: Vec = nodes - .iter() - .map(|node| { - node.accounts - .accounts - .get(key_type) - .expect(&format!( - "'sr' account should be set at spec computation {THIS_IS_A_BUG}" - )) - .address - .clone() - }) - .collect(); + if let Some(val) = chain_spec_json.pointer_mut(runtime_config_ptr) { + let key_type = if let SessionKeyType::Evm = session_key { "eth" } else { "sr" }; + let keys: Vec = nodes + .iter() + .map(|node| { + node.accounts + .accounts + .get(key_type) + .expect(&format!( + "'sr' account should be set at spec computation {THIS_IS_A_BUG}" + )) + .address + .clone() + }) + .collect(); - // collatorSelection.invulnerables - if let Some(invulnerables) = val.pointer_mut("/collatorSelection/invulnerables") { - *invulnerables = json!(keys); - } else { - // TODO: add a nice warning here. - debug!("⚠️ 'invulnerables' not present in spec, will not be customized"); - } - } else { - unreachable!("pointer to runtime config should be valid!") - } + // collatorSelection.invulnerables + if let Some(invulnerables) = val.pointer_mut("/collatorSelection/invulnerables") { + *invulnerables = json!(keys); + } else { + // TODO: add a nice warning here. + debug!("⚠️ 'invulnerables' not present in spec, will not be customized"); + } + } else { + unreachable!("pointer to runtime config should be valid!") + } } // Helpers fn generate_balance_map(balances: &serde_json::Value) -> HashMap { - // SAFETY: balances is always an array in chain-spec with items [k,v] - let balances_map: HashMap = - serde_json::from_value::>(balances.to_owned()) - .unwrap() - .iter() - .fold(HashMap::new(), |mut memo, balance| { - memo.insert(balance.0.clone(), balance.1); - memo - }); - balances_map + // SAFETY: balances is always an array in chain-spec with items [k,v] + let balances_map: HashMap = + serde_json::from_value::>(balances.to_owned()).unwrap().iter().fold( + HashMap::new(), + |mut memo, balance| { + memo.insert(balance.0.clone(), balance.1); + memo + }, + ); + balances_map } #[cfg(test)] mod tests { - use std::fs; + use std::fs; - use configuration::HrmpChannelConfigBuilder; + use configuration::HrmpChannelConfigBuilder; - use super::*; - use crate::{generators, shared::types::NodeAccounts}; + use super::*; + use crate::{generators, shared::types::NodeAccounts}; - const ROCOCO_LOCAL_PLAIN_TESTING: &str = "./testing/rococo-local-plain.json"; + const ROCOCO_LOCAL_PLAIN_TESTING: &str = "./testing/rococo-local-plain.json"; - fn chain_spec_test(file: &str) -> serde_json::Value { - let content = fs::read_to_string(file).unwrap(); - serde_json::from_str(&content).unwrap() - } + fn chain_spec_test(file: &str) -> serde_json::Value { + let content = fs::read_to_string(file).unwrap(); + serde_json::from_str(&content).unwrap() + } - fn chain_spec_with_stake() -> serde_json::Value { - json!({"genesis": { - "runtimeGenesis" : { - "patch": { - "staking": { - "forceEra": "NotForcing", - "invulnerables": [ - "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", - "5HpG9w8EBLe5XCrbczpwq5TSXvedjrBGCwqxK1iQ7qUsSWFc" - ], - "minimumValidatorCount": 1, - "slashRewardFraction": 100000000, - "stakers": [ - [ - "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", - "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", - 100000000000001_u128, - "Validator" - ], - [ - "5HpG9w8EBLe5XCrbczpwq5TSXvedjrBGCwqxK1iQ7qUsSWFc", - "5HpG9w8EBLe5XCrbczpwq5TSXvedjrBGCwqxK1iQ7qUsSWFc", - 100000000000000_u128, - "Validator" - ] - ], - "validatorCount": 2 - }, - } - } - }}) - } + fn chain_spec_with_stake() -> serde_json::Value { + json!({"genesis": { + "runtimeGenesis" : { + "patch": { + "staking": { + "forceEra": "NotForcing", + "invulnerables": [ + "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", + "5HpG9w8EBLe5XCrbczpwq5TSXvedjrBGCwqxK1iQ7qUsSWFc" + ], + "minimumValidatorCount": 1, + "slashRewardFraction": 100000000, + "stakers": [ + [ + "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", + "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", + 100000000000001_u128, + "Validator" + ], + [ + "5HpG9w8EBLe5XCrbczpwq5TSXvedjrBGCwqxK1iQ7qUsSWFc", + "5HpG9w8EBLe5XCrbczpwq5TSXvedjrBGCwqxK1iQ7qUsSWFc", + 100000000000000_u128, + "Validator" + ] + ], + "validatorCount": 2 + }, + } + } + }}) + } - fn chain_spec_with_dev_stakers() -> serde_json::Value { - json!({"genesis": { - "runtimeGenesis" : { - "patch": { - "staking": { - "activeEra": [ - 0, - 0, - 0 - ], - "canceledPayout": 0, - "devStakers": [ - 2000, - 25000 - ], - "forceEra": "NotForcing", - "invulnerables": [], - "maxNominatorCount": null, - "maxValidatorCount": null, - "minNominatorBond": 0, - "minValidatorBond": 0, - "slashRewardFraction": 0, - "stakers": [], - "validatorCount": 500 - }, - } - } - }}) - } + fn chain_spec_with_dev_stakers() -> serde_json::Value { + json!({"genesis": { + "runtimeGenesis" : { + "patch": { + "staking": { + "activeEra": [ + 0, + 0, + 0 + ], + "canceledPayout": 0, + "devStakers": [ + 2000, + 25000 + ], + "forceEra": "NotForcing", + "invulnerables": [], + "maxNominatorCount": null, + "maxValidatorCount": null, + "minNominatorBond": 0, + "minValidatorBond": 0, + "slashRewardFraction": 0, + "stakers": [], + "validatorCount": 500 + }, + } + } + }}) + } - #[test] - fn get_min_stake_works() { - let mut chain_spec_json = chain_spec_with_stake(); + #[test] + fn get_min_stake_works() { + let mut chain_spec_json = chain_spec_with_stake(); - let pointer = get_runtime_config_pointer(&chain_spec_json).unwrap(); - let min = get_staking_min(&pointer, &mut chain_spec_json); + let pointer = get_runtime_config_pointer(&chain_spec_json).unwrap(); + let min = get_staking_min(&pointer, &mut chain_spec_json); - assert_eq!(100000000000001, min); - } + assert_eq!(100000000000001, min); + } - #[test] - fn dev_stakers_not_override_count_works() { - let mut chain_spec_json = chain_spec_with_dev_stakers(); + #[test] + fn dev_stakers_not_override_count_works() { + let mut chain_spec_json = chain_spec_with_dev_stakers(); - let pointer = get_runtime_config_pointer(&chain_spec_json).unwrap(); - clear_authorities(&pointer, &mut chain_spec_json, &Context::Relay); + let pointer = get_runtime_config_pointer(&chain_spec_json).unwrap(); + clear_authorities(&pointer, &mut chain_spec_json, &Context::Relay); - let validator_count = chain_spec_json - .pointer(&format!("{pointer}/staking/validatorCount")) - .unwrap(); - assert_eq!(validator_count, &json!(500)); - } + let validator_count = + chain_spec_json.pointer(&format!("{pointer}/staking/validatorCount")).unwrap(); + assert_eq!(validator_count, &json!(500)); + } - #[test] - fn dev_stakers_override_count_works() { - let mut chain_spec_json = chain_spec_with_stake(); + #[test] + fn dev_stakers_override_count_works() { + let mut chain_spec_json = chain_spec_with_stake(); - let pointer = get_runtime_config_pointer(&chain_spec_json).unwrap(); - clear_authorities(&pointer, &mut chain_spec_json, &Context::Relay); + let pointer = get_runtime_config_pointer(&chain_spec_json).unwrap(); + clear_authorities(&pointer, &mut chain_spec_json, &Context::Relay); - let validator_count = chain_spec_json - .pointer(&format!("{pointer}/staking/validatorCount")) - .unwrap(); - assert_eq!(validator_count, &json!(0)); - } + let validator_count = + chain_spec_json.pointer(&format!("{pointer}/staking/validatorCount")).unwrap(); + assert_eq!(validator_count, &json!(0)); + } - #[test] - fn overrides_from_toml_works() { - use serde::{Deserialize, Serialize}; + #[test] + fn overrides_from_toml_works() { + use serde::{Deserialize, Serialize}; - #[derive(Debug, Serialize, Deserialize)] - struct MockConfig { - #[serde(rename = "genesis", skip_serializing_if = "Option::is_none")] - genesis_overrides: Option, - } + #[derive(Debug, Serialize, Deserialize)] + struct MockConfig { + #[serde(rename = "genesis", skip_serializing_if = "Option::is_none")] + genesis_overrides: Option, + } - let mut chain_spec_json = chain_spec_test(ROCOCO_LOCAL_PLAIN_TESTING); - // Could also be something like [genesis.runtimeGenesis.patch.balances] - const TOML: &str = "[genesis.runtime.balances] + let mut chain_spec_json = chain_spec_test(ROCOCO_LOCAL_PLAIN_TESTING); + // Could also be something like [genesis.runtimeGenesis.patch.balances] + const TOML: &str = "[genesis.runtime.balances] devAccounts = [ 20000, 1000000000000000000, \"//Sender//{}\" ]"; - let override_toml: MockConfig = toml::from_str(TOML).unwrap(); - let overrides = override_toml.genesis_overrides.unwrap(); - let pointer = get_runtime_config_pointer(&chain_spec_json).unwrap(); + let override_toml: MockConfig = toml::from_str(TOML).unwrap(); + let overrides = override_toml.genesis_overrides.unwrap(); + let pointer = get_runtime_config_pointer(&chain_spec_json).unwrap(); - let percolated_overrides = percolate_overrides(&pointer, &overrides) - .map_err(|e| GeneratorError::ChainSpecGeneration(e.to_string())) - .unwrap(); - trace!("percolated_overrides: {:#?}", percolated_overrides); - if let Some(genesis) = chain_spec_json.pointer_mut(&pointer) { - merge(genesis, percolated_overrides); - } + let percolated_overrides = percolate_overrides(&pointer, &overrides) + .map_err(|e| GeneratorError::ChainSpecGeneration(e.to_string())) + .unwrap(); + trace!("percolated_overrides: {:#?}", percolated_overrides); + if let Some(genesis) = chain_spec_json.pointer_mut(&pointer) { + merge(genesis, percolated_overrides); + } - trace!("chain spec: {chain_spec_json:#?}"); - assert!(chain_spec_json - .pointer("/genesis/runtime/balances/devAccounts") - .is_some()); - } + trace!("chain spec: {chain_spec_json:#?}"); + assert!(chain_spec_json.pointer("/genesis/runtime/balances/devAccounts").is_some()); + } - #[test] - fn add_balances_works() { - let mut spec_plain = chain_spec_test(ROCOCO_LOCAL_PLAIN_TESTING); - let mut name = String::from("luca"); - let initial_balance = 1_000_000_000_000_u128; - let seed = format!("//{}{name}", name.remove(0).to_uppercase()); - let accounts = NodeAccounts { - accounts: generators::generate_node_keys(&seed).unwrap(), - seed, - }; - let node = NodeSpec { - name, - accounts, - initial_balance, - ..Default::default() - }; + #[test] + fn add_balances_works() { + let mut spec_plain = chain_spec_test(ROCOCO_LOCAL_PLAIN_TESTING); + let mut name = String::from("luca"); + let initial_balance = 1_000_000_000_000_u128; + let seed = format!("//{}{name}", name.remove(0).to_uppercase()); + let accounts = + NodeAccounts { accounts: generators::generate_node_keys(&seed).unwrap(), seed }; + let node = NodeSpec { name, accounts, initial_balance, ..Default::default() }; - let nodes = vec![node]; - add_balances("/genesis/runtime", &mut spec_plain, &nodes, 12, 0); + let nodes = vec![node]; + add_balances("/genesis/runtime", &mut spec_plain, &nodes, 12, 0); - let new_balances = spec_plain - .pointer("/genesis/runtime/balances/balances") - .unwrap(); + let new_balances = spec_plain.pointer("/genesis/runtime/balances/balances").unwrap(); - let balances_map = generate_balance_map(new_balances); + let balances_map = generate_balance_map(new_balances); - // sr and sr_stash keys exists - let sr = nodes[0].accounts.accounts.get("sr").unwrap(); - let sr_stash = nodes[0].accounts.accounts.get("sr_stash").unwrap(); - assert_eq!(balances_map.get(&sr.address).unwrap(), &initial_balance); - assert_eq!( - balances_map.get(&sr_stash.address).unwrap(), - &initial_balance - ); - } + // sr and sr_stash keys exists + let sr = nodes[0].accounts.accounts.get("sr").unwrap(); + let sr_stash = nodes[0].accounts.accounts.get("sr_stash").unwrap(); + assert_eq!(balances_map.get(&sr.address).unwrap(), &initial_balance); + assert_eq!(balances_map.get(&sr_stash.address).unwrap(), &initial_balance); + } - #[test] - fn add_balances_ensure_zombie_account() { - let mut spec_plain = chain_spec_test(ROCOCO_LOCAL_PLAIN_TESTING); + #[test] + fn add_balances_ensure_zombie_account() { + let mut spec_plain = chain_spec_test(ROCOCO_LOCAL_PLAIN_TESTING); - let balances = spec_plain - .pointer("/genesis/runtime/balances/balances") - .unwrap(); - let balances_map = generate_balance_map(balances); + let balances = spec_plain.pointer("/genesis/runtime/balances/balances").unwrap(); + let balances_map = generate_balance_map(balances); - let nodes: Vec = vec![]; - add_balances("/genesis/runtime", &mut spec_plain, &nodes, 12, 0); + let nodes: Vec = vec![]; + add_balances("/genesis/runtime", &mut spec_plain, &nodes, 12, 0); - let new_balances = spec_plain - .pointer("/genesis/runtime/balances/balances") - .unwrap(); + let new_balances = spec_plain.pointer("/genesis/runtime/balances/balances").unwrap(); - let new_balances_map = generate_balance_map(new_balances); + let new_balances_map = generate_balance_map(new_balances); - // sr and sr_stash keys exists - assert!(new_balances_map.contains_key("5FTcLfwFc7ctvqp3RhbEig6UuHLHcHVRujuUm8r21wy4dAR8")); - assert_eq!(new_balances_map.len(), balances_map.len() + 1); - } + // sr and sr_stash keys exists + assert!(new_balances_map.contains_key("5FTcLfwFc7ctvqp3RhbEig6UuHLHcHVRujuUm8r21wy4dAR8")); + assert_eq!(new_balances_map.len(), balances_map.len() + 1); + } - #[test] - fn add_balances_spec_without_balances() { - let mut spec_plain = chain_spec_test(ROCOCO_LOCAL_PLAIN_TESTING); + #[test] + fn add_balances_spec_without_balances() { + let mut spec_plain = chain_spec_test(ROCOCO_LOCAL_PLAIN_TESTING); - { - let balances = spec_plain.pointer_mut("/genesis/runtime/balances").unwrap(); - *balances = json!(serde_json::Value::Null); - } + { + let balances = spec_plain.pointer_mut("/genesis/runtime/balances").unwrap(); + *balances = json!(serde_json::Value::Null); + } - let mut name = String::from("luca"); - let initial_balance = 1_000_000_000_000_u128; - let seed = format!("//{}{name}", name.remove(0).to_uppercase()); - let accounts = NodeAccounts { - accounts: generators::generate_node_keys(&seed).unwrap(), - seed, - }; - let node = NodeSpec { - name, - accounts, - initial_balance, - ..Default::default() - }; + let mut name = String::from("luca"); + let initial_balance = 1_000_000_000_000_u128; + let seed = format!("//{}{name}", name.remove(0).to_uppercase()); + let accounts = + NodeAccounts { accounts: generators::generate_node_keys(&seed).unwrap(), seed }; + let node = NodeSpec { name, accounts, initial_balance, ..Default::default() }; - let nodes = vec![node]; - add_balances("/genesis/runtime", &mut spec_plain, &nodes, 12, 0); + let nodes = vec![node]; + add_balances("/genesis/runtime", &mut spec_plain, &nodes, 12, 0); - let new_balances = spec_plain.pointer("/genesis/runtime/balances/balances"); + let new_balances = spec_plain.pointer("/genesis/runtime/balances/balances"); - // assert 'balances' is not created - assert_eq!(new_balances, None); - } + // assert 'balances' is not created + assert_eq!(new_balances, None); + } - #[test] - fn add_staking_works() { - let mut chain_spec_json = chain_spec_with_stake(); - let mut name = String::from("luca"); - let initial_balance = 1_000_000_000_000_u128; - let seed = format!("//{}{name}", name.remove(0).to_uppercase()); - let accounts = NodeAccounts { - accounts: generators::generate_node_keys(&seed).unwrap(), - seed, - }; - let node = NodeSpec { - name, - accounts, - initial_balance, - ..Default::default() - }; + #[test] + fn add_staking_works() { + let mut chain_spec_json = chain_spec_with_stake(); + let mut name = String::from("luca"); + let initial_balance = 1_000_000_000_000_u128; + let seed = format!("//{}{name}", name.remove(0).to_uppercase()); + let accounts = + NodeAccounts { accounts: generators::generate_node_keys(&seed).unwrap(), seed }; + let node = NodeSpec { name, accounts, initial_balance, ..Default::default() }; - let pointer = get_runtime_config_pointer(&chain_spec_json).unwrap(); - let min = get_staking_min(&pointer, &mut chain_spec_json); + let pointer = get_runtime_config_pointer(&chain_spec_json).unwrap(); + let min = get_staking_min(&pointer, &mut chain_spec_json); - let nodes = vec![node]; - add_staking(&pointer, &mut chain_spec_json, &nodes, min); + let nodes = vec![node]; + add_staking(&pointer, &mut chain_spec_json, &nodes, min); - let new_staking = chain_spec_json - .pointer("/genesis/runtimeGenesis/patch/staking") - .unwrap(); + let new_staking = chain_spec_json.pointer("/genesis/runtimeGenesis/patch/staking").unwrap(); - // stakers should be one (with the luca sr_stash accounts) - let sr_stash = nodes[0].accounts.accounts.get("sr_stash").unwrap(); - assert_eq!(new_staking["stakers"][0][0], json!(sr_stash.address)); - // with the calculated minimal bound - assert_eq!(new_staking["stakers"][0][2], json!(min)); - // and only one - assert_eq!(new_staking["stakers"].as_array().unwrap().len(), 1); - } + // stakers should be one (with the luca sr_stash accounts) + let sr_stash = nodes[0].accounts.accounts.get("sr_stash").unwrap(); + assert_eq!(new_staking["stakers"][0][0], json!(sr_stash.address)); + // with the calculated minimal bound + assert_eq!(new_staking["stakers"][0][2], json!(min)); + // and only one + assert_eq!(new_staking["stakers"].as_array().unwrap().len(), 1); + } - #[test] - fn adding_hrmp_channels_works() { - let mut spec_plain = chain_spec_test(ROCOCO_LOCAL_PLAIN_TESTING); + #[test] + fn adding_hrmp_channels_works() { + let mut spec_plain = chain_spec_test(ROCOCO_LOCAL_PLAIN_TESTING); - { - let current_hrmp_channels = spec_plain - .pointer("/genesis/runtime/hrmp/preopenHrmpChannels") - .unwrap(); - // assert should be empty - assert_eq!(current_hrmp_channels, &json!([])); - } + { + let current_hrmp_channels = + spec_plain.pointer("/genesis/runtime/hrmp/preopenHrmpChannels").unwrap(); + // assert should be empty + assert_eq!(current_hrmp_channels, &json!([])); + } - let para_100_101 = HrmpChannelConfigBuilder::new() - .with_sender(100) - .with_recipient(101) - .build(); - let para_101_100 = HrmpChannelConfigBuilder::new() - .with_sender(101) - .with_recipient(100) - .build(); - let channels = vec![para_100_101, para_101_100]; + let para_100_101 = + HrmpChannelConfigBuilder::new().with_sender(100).with_recipient(101).build(); + let para_101_100 = + HrmpChannelConfigBuilder::new().with_sender(101).with_recipient(100).build(); + let channels = vec![para_100_101, para_101_100]; - add_hrmp_channels("/genesis/runtime", &mut spec_plain, &channels); - let new_hrmp_channels = spec_plain - .pointer("/genesis/runtime/hrmp/preopenHrmpChannels") - .unwrap() - .as_array() - .unwrap(); + add_hrmp_channels("/genesis/runtime", &mut spec_plain, &channels); + let new_hrmp_channels = spec_plain + .pointer("/genesis/runtime/hrmp/preopenHrmpChannels") + .unwrap() + .as_array() + .unwrap(); - assert_eq!(new_hrmp_channels.len(), 2); - assert_eq!(new_hrmp_channels.first().unwrap()[0], 100); - assert_eq!(new_hrmp_channels.first().unwrap()[1], 101); - assert_eq!(new_hrmp_channels.last().unwrap()[0], 101); - assert_eq!(new_hrmp_channels.last().unwrap()[1], 100); - } + assert_eq!(new_hrmp_channels.len(), 2); + assert_eq!(new_hrmp_channels.first().unwrap()[0], 100); + assert_eq!(new_hrmp_channels.first().unwrap()[1], 101); + assert_eq!(new_hrmp_channels.last().unwrap()[0], 101); + assert_eq!(new_hrmp_channels.last().unwrap()[1], 100); + } - #[test] - fn adding_hrmp_channels_to_an_spec_without_channels() { - let mut spec_plain = chain_spec_test("./testing/rococo-local-plain.json"); + #[test] + fn adding_hrmp_channels_to_an_spec_without_channels() { + let mut spec_plain = chain_spec_test("./testing/rococo-local-plain.json"); - { - let hrmp = spec_plain.pointer_mut("/genesis/runtime/hrmp").unwrap(); - *hrmp = json!(serde_json::Value::Null); - } + { + let hrmp = spec_plain.pointer_mut("/genesis/runtime/hrmp").unwrap(); + *hrmp = json!(serde_json::Value::Null); + } - let para_100_101 = HrmpChannelConfigBuilder::new() - .with_sender(100) - .with_recipient(101) - .build(); - let para_101_100 = HrmpChannelConfigBuilder::new() - .with_sender(101) - .with_recipient(100) - .build(); - let channels = vec![para_100_101, para_101_100]; + let para_100_101 = + HrmpChannelConfigBuilder::new().with_sender(100).with_recipient(101).build(); + let para_101_100 = + HrmpChannelConfigBuilder::new().with_sender(101).with_recipient(100).build(); + let channels = vec![para_100_101, para_101_100]; - add_hrmp_channels("/genesis/runtime", &mut spec_plain, &channels); - let new_hrmp_channels = spec_plain.pointer("/genesis/runtime/hrmp/preopenHrmpChannels"); + add_hrmp_channels("/genesis/runtime", &mut spec_plain, &channels); + let new_hrmp_channels = spec_plain.pointer("/genesis/runtime/hrmp/preopenHrmpChannels"); - // assert 'preopenHrmpChannels' is not created - assert_eq!(new_hrmp_channels, None); - } + // assert 'preopenHrmpChannels' is not created + assert_eq!(new_hrmp_channels, None); + } - #[test] - fn get_node_keys_works() { - let mut name = String::from("luca"); - let seed = format!("//{}{name}", name.remove(0).to_uppercase()); - let accounts = NodeAccounts { - accounts: generators::generate_node_keys(&seed).unwrap(), - seed, - }; - let node = NodeSpec { - name, - accounts, - ..Default::default() - }; + #[test] + fn get_node_keys_works() { + let mut name = String::from("luca"); + let seed = format!("//{}{name}", name.remove(0).to_uppercase()); + let accounts = + NodeAccounts { accounts: generators::generate_node_keys(&seed).unwrap(), seed }; + let node = NodeSpec { name, accounts, ..Default::default() }; - let sr = &node.accounts.accounts["sr"]; - let keys = [ - ("babe".into(), sr.address.clone()), - ("im_online".into(), sr.address.clone()), - ("parachain_validator".into(), sr.address.clone()), - ("authority_discovery".into(), sr.address.clone()), - ("para_validator".into(), sr.address.clone()), - ("para_assignment".into(), sr.address.clone()), - ("aura".into(), sr.address.clone()), - ("nimbus".into(), sr.address.clone()), - ("vrf".into(), sr.address.clone()), - ( - "grandpa".into(), - node.accounts.accounts["ed"].address.clone(), - ), - ("beefy".into(), node.accounts.accounts["ec"].address.clone()), - ("eth".into(), node.accounts.accounts["eth"].address.clone()), - ] - .into(); + let sr = &node.accounts.accounts["sr"]; + let keys = [ + ("babe".into(), sr.address.clone()), + ("im_online".into(), sr.address.clone()), + ("parachain_validator".into(), sr.address.clone()), + ("authority_discovery".into(), sr.address.clone()), + ("para_validator".into(), sr.address.clone()), + ("para_assignment".into(), sr.address.clone()), + ("aura".into(), sr.address.clone()), + ("nimbus".into(), sr.address.clone()), + ("vrf".into(), sr.address.clone()), + ("grandpa".into(), node.accounts.accounts["ed"].address.clone()), + ("beefy".into(), node.accounts.accounts["ec"].address.clone()), + ("eth".into(), node.accounts.accounts["eth"].address.clone()), + ] + .into(); - // Stash - let sr_stash = &node.accounts.accounts["sr_stash"]; - let node_key = get_node_keys(&node, SessionKeyType::Stash, false); - assert_eq!(node_key.0, sr_stash.address); - assert_eq!(node_key.1, sr_stash.address); - assert_eq!(node_key.2, keys); - // Non-stash - let node_key = get_node_keys(&node, SessionKeyType::Default, false); - assert_eq!(node_key.0, sr.address); - assert_eq!(node_key.1, sr.address); - assert_eq!(node_key.2, keys); - } + // Stash + let sr_stash = &node.accounts.accounts["sr_stash"]; + let node_key = get_node_keys(&node, SessionKeyType::Stash, false); + assert_eq!(node_key.0, sr_stash.address); + assert_eq!(node_key.1, sr_stash.address); + assert_eq!(node_key.2, keys); + // Non-stash + let node_key = get_node_keys(&node, SessionKeyType::Default, false); + assert_eq!(node_key.0, sr.address); + assert_eq!(node_key.1, sr.address); + assert_eq!(node_key.2, keys); + } - #[test] - fn get_node_keys_supports_asset_hub_polkadot() { - let mut name = String::from("luca"); - let seed = format!("//{}{name}", name.remove(0).to_uppercase()); - let accounts = NodeAccounts { - accounts: generators::generate_node_keys(&seed).unwrap(), - seed, - }; - let node = NodeSpec { - name, - accounts, - ..Default::default() - }; + #[test] + fn get_node_keys_supports_asset_hub_polkadot() { + let mut name = String::from("luca"); + let seed = format!("//{}{name}", name.remove(0).to_uppercase()); + let accounts = + NodeAccounts { accounts: generators::generate_node_keys(&seed).unwrap(), seed }; + let node = NodeSpec { name, accounts, ..Default::default() }; - let node_key = get_node_keys(&node, SessionKeyType::default(), false); - assert_eq!(node_key.2["aura"], node.accounts.accounts["sr"].address); + let node_key = get_node_keys(&node, SessionKeyType::default(), false); + assert_eq!(node_key.2["aura"], node.accounts.accounts["sr"].address); - let node_key = get_node_keys(&node, SessionKeyType::default(), true); - assert_eq!(node_key.2["aura"], node.accounts.accounts["ed"].address); - } + let node_key = get_node_keys(&node, SessionKeyType::default(), true); + assert_eq!(node_key.2["aura"], node.accounts.accounts["ed"].address); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/command.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/command.rs index 32a60cc3..5f840a9a 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/command.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/command.rs @@ -5,630 +5,542 @@ use super::arg_filter::{apply_arg_removals, parse_removal_args}; use crate::{network_spec::node::NodeSpec, shared::constants::*}; pub struct GenCmdOptions<'a> { - pub relay_chain_name: &'a str, - pub cfg_path: &'a str, - pub data_path: &'a str, - pub relay_data_path: &'a str, - pub use_wrapper: bool, - pub bootnode_addr: Vec, - pub use_default_ports_in_cmd: bool, - pub is_native: bool, + pub relay_chain_name: &'a str, + pub cfg_path: &'a str, + pub data_path: &'a str, + pub relay_data_path: &'a str, + pub use_wrapper: bool, + pub bootnode_addr: Vec, + pub use_default_ports_in_cmd: bool, + pub is_native: bool, } impl Default for GenCmdOptions<'_> { - fn default() -> Self { - Self { - relay_chain_name: "rococo-local", - cfg_path: "/cfg", - data_path: "/data", - relay_data_path: "/relay-data", - use_wrapper: true, - bootnode_addr: vec![], - use_default_ports_in_cmd: false, - is_native: true, - } - } + fn default() -> Self { + Self { + relay_chain_name: "rococo-local", + cfg_path: "/cfg", + data_path: "/data", + relay_data_path: "/relay-data", + use_wrapper: true, + bootnode_addr: vec![], + use_default_ports_in_cmd: false, + is_native: true, + } + } } const FLAGS_ADDED_BY_US: [&str; 3] = ["--no-telemetry", "--collator", "--"]; -const OPS_ADDED_BY_US: [&str; 6] = [ - "--chain", - "--name", - "--rpc-cors", - "--rpc-methods", - "--parachain-id", - "--node-key", -]; +const OPS_ADDED_BY_US: [&str; 6] = + ["--chain", "--name", "--rpc-cors", "--rpc-methods", "--parachain-id", "--node-key"]; // TODO: can we abstract this and use only one fn (or at least split and reuse in small fns) pub fn generate_for_cumulus_node( - node: &NodeSpec, - options: GenCmdOptions, - para_id: u32, + node: &NodeSpec, + options: GenCmdOptions, + para_id: u32, ) -> (String, Vec) { - let NodeSpec { - key, - args, - is_validator, - bootnodes_addresses, - .. - } = node; + let NodeSpec { key, args, is_validator, bootnodes_addresses, .. } = node; - let mut tmp_args: Vec = vec!["--node-key".into(), key.clone()]; + let mut tmp_args: Vec = vec!["--node-key".into(), key.clone()]; - if !args.contains(&Arg::Flag("--prometheus-external".into())) { - tmp_args.push("--prometheus-external".into()) - } + if !args.contains(&Arg::Flag("--prometheus-external".into())) { + tmp_args.push("--prometheus-external".into()) + } - if *is_validator && !args.contains(&Arg::Flag("--validator".into())) { - tmp_args.push("--collator".into()) - } + if *is_validator && !args.contains(&Arg::Flag("--validator".into())) { + tmp_args.push("--collator".into()) + } - if !bootnodes_addresses.is_empty() { - tmp_args.push("--bootnodes".into()); - let bootnodes = bootnodes_addresses - .iter() - .map(|m| m.to_string()) - .collect::>() - .join(" "); - tmp_args.push(bootnodes) - } + if !bootnodes_addresses.is_empty() { + tmp_args.push("--bootnodes".into()); + let bootnodes = + bootnodes_addresses.iter().map(|m| m.to_string()).collect::>().join(" "); + tmp_args.push(bootnodes) + } - // ports - let (prometheus_port, rpc_port, p2p_port) = - resolve_ports(node, options.use_default_ports_in_cmd); + // ports + let (prometheus_port, rpc_port, p2p_port) = + resolve_ports(node, options.use_default_ports_in_cmd); - tmp_args.push("--prometheus-port".into()); - tmp_args.push(prometheus_port.to_string()); + tmp_args.push("--prometheus-port".into()); + tmp_args.push(prometheus_port.to_string()); - tmp_args.push("--rpc-port".into()); - tmp_args.push(rpc_port.to_string()); + tmp_args.push("--rpc-port".into()); + tmp_args.push(rpc_port.to_string()); - tmp_args.push("--listen-addr".into()); - tmp_args.push(format!("/ip4/0.0.0.0/tcp/{p2p_port}/ws")); + tmp_args.push("--listen-addr".into()); + tmp_args.push(format!("/ip4/0.0.0.0/tcp/{p2p_port}/ws")); - let mut collator_args: &[Arg] = &[]; - let mut full_node_args: &[Arg] = &[]; - if !args.is_empty() { - if let Some(index) = args.iter().position(|arg| match arg { - Arg::Flag(flag) => flag.eq("--"), - Arg::Option(..) => false, - Arg::Array(..) => false, - }) { - (collator_args, full_node_args) = args.split_at(index); - } else { - // Assume args are those specified for collator only - collator_args = args; - } - } + let mut collator_args: &[Arg] = &[]; + let mut full_node_args: &[Arg] = &[]; + if !args.is_empty() { + if let Some(index) = args.iter().position(|arg| match arg { + Arg::Flag(flag) => flag.eq("--"), + Arg::Option(..) => false, + Arg::Array(..) => false, + }) { + (collator_args, full_node_args) = args.split_at(index); + } else { + // Assume args are those specified for collator only + collator_args = args; + } + } - // set our base path - tmp_args.push("--base-path".into()); - tmp_args.push(options.data_path.into()); + // set our base path + tmp_args.push("--base-path".into()); + tmp_args.push(options.data_path.into()); - let node_specific_bootnodes: Vec = node - .bootnodes_addresses - .iter() - .map(|b| b.to_string()) - .collect(); - let full_bootnodes = [node_specific_bootnodes, options.bootnode_addr].concat(); - if !full_bootnodes.is_empty() { - tmp_args.push("--bootnodes".into()); - tmp_args.push(full_bootnodes.join(" ")); - } + let node_specific_bootnodes: Vec = + node.bootnodes_addresses.iter().map(|b| b.to_string()).collect(); + let full_bootnodes = [node_specific_bootnodes, options.bootnode_addr].concat(); + if !full_bootnodes.is_empty() { + tmp_args.push("--bootnodes".into()); + tmp_args.push(full_bootnodes.join(" ")); + } - let mut full_node_p2p_needs_to_be_injected = true; - let mut full_node_prometheus_needs_to_be_injected = true; - let mut full_node_args_filtered = full_node_args - .iter() - .filter_map(|arg| match arg { - Arg::Flag(flag) => { - if flag.starts_with("-:") || FLAGS_ADDED_BY_US.contains(&flag.as_str()) { - None - } else { - Some(vec![flag.to_owned()]) - } - }, - Arg::Option(k, v) => { - if OPS_ADDED_BY_US.contains(&k.as_str()) { - None - } else if k.eq(&"port") { - if v.eq(&"30333") { - full_node_p2p_needs_to_be_injected = true; - None - } else { - // non default - full_node_p2p_needs_to_be_injected = false; - Some(vec![k.to_owned(), v.to_owned()]) - } - } else if k.eq(&"--prometheus-port") { - if v.eq(&"9616") { - full_node_prometheus_needs_to_be_injected = true; - None - } else { - // non default - full_node_prometheus_needs_to_be_injected = false; - Some(vec![k.to_owned(), v.to_owned()]) - } - } else { - Some(vec![k.to_owned(), v.to_owned()]) - } - }, - Arg::Array(k, v) => { - let mut args = vec![k.to_owned()]; - args.extend(v.to_owned()); - Some(args) - }, - }) - .flatten() - .collect::>(); + let mut full_node_p2p_needs_to_be_injected = true; + let mut full_node_prometheus_needs_to_be_injected = true; + let mut full_node_args_filtered = full_node_args + .iter() + .filter_map(|arg| match arg { + Arg::Flag(flag) => { + if flag.starts_with("-:") || FLAGS_ADDED_BY_US.contains(&flag.as_str()) { + None + } else { + Some(vec![flag.to_owned()]) + } + }, + Arg::Option(k, v) => { + if OPS_ADDED_BY_US.contains(&k.as_str()) { + None + } else if k.eq(&"port") { + if v.eq(&"30333") { + full_node_p2p_needs_to_be_injected = true; + None + } else { + // non default + full_node_p2p_needs_to_be_injected = false; + Some(vec![k.to_owned(), v.to_owned()]) + } + } else if k.eq(&"--prometheus-port") { + if v.eq(&"9616") { + full_node_prometheus_needs_to_be_injected = true; + None + } else { + // non default + full_node_prometheus_needs_to_be_injected = false; + Some(vec![k.to_owned(), v.to_owned()]) + } + } else { + Some(vec![k.to_owned(), v.to_owned()]) + } + }, + Arg::Array(k, v) => { + let mut args = vec![k.to_owned()]; + args.extend(v.to_owned()); + Some(args) + }, + }) + .flatten() + .collect::>(); - let full_p2p_port = node - .full_node_p2p_port - .as_ref() - .expect(&format!( - "full node p2p_port should be specifed: {THIS_IS_A_BUG}" - )) - .0; - let full_prometheus_port = node - .full_node_prometheus_port - .as_ref() - .expect(&format!( - "full node prometheus_port should be specifed: {THIS_IS_A_BUG}" - )) - .0; + let full_p2p_port = node + .full_node_p2p_port + .as_ref() + .expect(&format!("full node p2p_port should be specifed: {THIS_IS_A_BUG}")) + .0; + let full_prometheus_port = node + .full_node_prometheus_port + .as_ref() + .expect(&format!("full node prometheus_port should be specifed: {THIS_IS_A_BUG}")) + .0; - // full_node: change p2p port if is the default - if full_node_p2p_needs_to_be_injected { - full_node_args_filtered.push("--port".into()); - full_node_args_filtered.push(full_p2p_port.to_string()); - } + // full_node: change p2p port if is the default + if full_node_p2p_needs_to_be_injected { + full_node_args_filtered.push("--port".into()); + full_node_args_filtered.push(full_p2p_port.to_string()); + } - // full_node: change prometheus port if is the default - if full_node_prometheus_needs_to_be_injected { - full_node_args_filtered.push("--prometheus-port".into()); - full_node_args_filtered.push(full_prometheus_port.to_string()); - } + // full_node: change prometheus port if is the default + if full_node_prometheus_needs_to_be_injected { + full_node_args_filtered.push("--prometheus-port".into()); + full_node_args_filtered.push(full_prometheus_port.to_string()); + } - let mut args_filtered = collator_args - .iter() - .filter_map(|arg| match arg { - Arg::Flag(flag) => { - if flag.starts_with("-:") || FLAGS_ADDED_BY_US.contains(&flag.as_str()) { - None - } else { - Some(vec![flag.to_owned()]) - } - }, - Arg::Option(k, v) => { - if OPS_ADDED_BY_US.contains(&k.as_str()) { - None - } else { - Some(vec![k.to_owned(), v.to_owned()]) - } - }, - Arg::Array(k, v) => { - let mut args = vec![k.to_owned()]; - args.extend(v.to_owned()); - Some(args) - }, - }) - .flatten() - .collect::>(); + let mut args_filtered = collator_args + .iter() + .filter_map(|arg| match arg { + Arg::Flag(flag) => { + if flag.starts_with("-:") || FLAGS_ADDED_BY_US.contains(&flag.as_str()) { + None + } else { + Some(vec![flag.to_owned()]) + } + }, + Arg::Option(k, v) => { + if OPS_ADDED_BY_US.contains(&k.as_str()) { + None + } else { + Some(vec![k.to_owned(), v.to_owned()]) + } + }, + Arg::Array(k, v) => { + let mut args = vec![k.to_owned()]; + args.extend(v.to_owned()); + Some(args) + }, + }) + .flatten() + .collect::>(); - tmp_args.append(&mut args_filtered); + tmp_args.append(&mut args_filtered); - let parachain_spec_path = format!("{}/{}.json", options.cfg_path, para_id); - let mut final_args = vec![ - node.command.as_str().to_string(), - "--chain".into(), - parachain_spec_path, - "--name".into(), - node.name.clone(), - "--rpc-cors".into(), - "all".into(), - "--rpc-methods".into(), - "unsafe".into(), - ]; + let parachain_spec_path = format!("{}/{}.json", options.cfg_path, para_id); + let mut final_args = vec![ + node.command.as_str().to_string(), + "--chain".into(), + parachain_spec_path, + "--name".into(), + node.name.clone(), + "--rpc-cors".into(), + "all".into(), + "--rpc-methods".into(), + "unsafe".into(), + ]; - // The `--unsafe-rpc-external` option spawns an additional RPC server on a random port, - // which can conflict with reserved ports, causing an "Address already in use" error - // when using the `native` provider. Since this option isn't needed for `native`, - // it should be omitted in that case. - if !options.is_native { - final_args.push("--unsafe-rpc-external".into()); - } + // The `--unsafe-rpc-external` option spawns an additional RPC server on a random port, + // which can conflict with reserved ports, causing an "Address already in use" error + // when using the `native` provider. Since this option isn't needed for `native`, + // it should be omitted in that case. + if !options.is_native { + final_args.push("--unsafe-rpc-external".into()); + } - final_args.append(&mut tmp_args); + final_args.append(&mut tmp_args); - let relaychain_spec_path = format!("{}/{}.json", options.cfg_path, options.relay_chain_name); - let mut full_node_injected: Vec = vec![ - "--".into(), - "--base-path".into(), - options.relay_data_path.into(), - "--chain".into(), - relaychain_spec_path, - "--execution".into(), - "wasm".into(), - ]; + let relaychain_spec_path = format!("{}/{}.json", options.cfg_path, options.relay_chain_name); + let mut full_node_injected: Vec = vec![ + "--".into(), + "--base-path".into(), + options.relay_data_path.into(), + "--chain".into(), + relaychain_spec_path, + "--execution".into(), + "wasm".into(), + ]; - final_args.append(&mut full_node_injected); - final_args.append(&mut full_node_args_filtered); + final_args.append(&mut full_node_injected); + final_args.append(&mut full_node_args_filtered); - let removals = parse_removal_args(args); - final_args = apply_arg_removals(final_args, &removals); + let removals = parse_removal_args(args); + final_args = apply_arg_removals(final_args, &removals); - if options.use_wrapper { - ("/cfg/zombie-wrapper.sh".to_string(), final_args) - } else { - (final_args.remove(0), final_args) - } + if options.use_wrapper { + ("/cfg/zombie-wrapper.sh".to_string(), final_args) + } else { + (final_args.remove(0), final_args) + } } pub fn generate_for_node( - node: &NodeSpec, - options: GenCmdOptions, - para_id: Option, + node: &NodeSpec, + options: GenCmdOptions, + para_id: Option, ) -> (String, Vec) { - let NodeSpec { - key, - args, - is_validator, - bootnodes_addresses, - .. - } = node; - let mut tmp_args: Vec = vec![ - "--node-key".into(), - key.clone(), - // TODO:(team) we should allow to set the telemetry url from config - "--no-telemetry".into(), - ]; + let NodeSpec { key, args, is_validator, bootnodes_addresses, .. } = node; + let mut tmp_args: Vec = vec![ + "--node-key".into(), + key.clone(), + // TODO:(team) we should allow to set the telemetry url from config + "--no-telemetry".into(), + ]; - if !args.contains(&Arg::Flag("--prometheus-external".into())) { - tmp_args.push("--prometheus-external".into()) - } + if !args.contains(&Arg::Flag("--prometheus-external".into())) { + tmp_args.push("--prometheus-external".into()) + } - if let Some(para_id) = para_id { - tmp_args.push("--parachain-id".into()); - tmp_args.push(para_id.to_string()); - } + if let Some(para_id) = para_id { + tmp_args.push("--parachain-id".into()); + tmp_args.push(para_id.to_string()); + } - if *is_validator && !args.contains(&Arg::Flag("--validator".into())) { - tmp_args.push("--validator".into()); - if node.supports_arg("--insecure-validator-i-know-what-i-do") { - tmp_args.push("--insecure-validator-i-know-what-i-do".into()); - } - } + if *is_validator && !args.contains(&Arg::Flag("--validator".into())) { + tmp_args.push("--validator".into()); + if node.supports_arg("--insecure-validator-i-know-what-i-do") { + tmp_args.push("--insecure-validator-i-know-what-i-do".into()); + } + } - if !bootnodes_addresses.is_empty() { - tmp_args.push("--bootnodes".into()); - let bootnodes = bootnodes_addresses - .iter() - .map(|m| m.to_string()) - .collect::>() - .join(" "); - tmp_args.push(bootnodes) - } + if !bootnodes_addresses.is_empty() { + tmp_args.push("--bootnodes".into()); + let bootnodes = + bootnodes_addresses.iter().map(|m| m.to_string()).collect::>().join(" "); + tmp_args.push(bootnodes) + } - // ports - let (prometheus_port, rpc_port, p2p_port) = - resolve_ports(node, options.use_default_ports_in_cmd); + // ports + let (prometheus_port, rpc_port, p2p_port) = + resolve_ports(node, options.use_default_ports_in_cmd); - // Prometheus - tmp_args.push("--prometheus-port".into()); - tmp_args.push(prometheus_port.to_string()); + // Prometheus + tmp_args.push("--prometheus-port".into()); + tmp_args.push(prometheus_port.to_string()); - // RPC - // TODO (team): do we want to support old --ws-port? - tmp_args.push("--rpc-port".into()); - tmp_args.push(rpc_port.to_string()); + // RPC + // TODO (team): do we want to support old --ws-port? + tmp_args.push("--rpc-port".into()); + tmp_args.push(rpc_port.to_string()); - let listen_value = if let Some(listen_val) = args.iter().find_map(|arg| match arg { - Arg::Flag(_) => None, - Arg::Option(k, v) => { - if k.eq("--listen-addr") { - Some(v) - } else { - None - } - }, - Arg::Array(..) => None, - }) { - let mut parts = listen_val.split('/').collect::>(); - // TODO: move this to error - let port_part = parts - .get_mut(4) - .expect(&format!("should have at least 5 parts {THIS_IS_A_BUG}")); - let port_to_use = p2p_port.to_string(); - *port_part = port_to_use.as_str(); - parts.join("/") - } else { - format!("/ip4/0.0.0.0/tcp/{p2p_port}/ws") - }; + let listen_value = if let Some(listen_val) = args.iter().find_map(|arg| match arg { + Arg::Flag(_) => None, + Arg::Option(k, v) => { + if k.eq("--listen-addr") { + Some(v) + } else { + None + } + }, + Arg::Array(..) => None, + }) { + let mut parts = listen_val.split('/').collect::>(); + // TODO: move this to error + let port_part = + parts.get_mut(4).expect(&format!("should have at least 5 parts {THIS_IS_A_BUG}")); + let port_to_use = p2p_port.to_string(); + *port_part = port_to_use.as_str(); + parts.join("/") + } else { + format!("/ip4/0.0.0.0/tcp/{p2p_port}/ws") + }; - tmp_args.push("--listen-addr".into()); - tmp_args.push(listen_value); + tmp_args.push("--listen-addr".into()); + tmp_args.push(listen_value); - // set our base path - tmp_args.push("--base-path".into()); - tmp_args.push(options.data_path.into()); + // set our base path + tmp_args.push("--base-path".into()); + tmp_args.push(options.data_path.into()); - let node_specific_bootnodes: Vec = node - .bootnodes_addresses - .iter() - .map(|b| b.to_string()) - .collect(); - let full_bootnodes = [node_specific_bootnodes, options.bootnode_addr].concat(); - if !full_bootnodes.is_empty() { - tmp_args.push("--bootnodes".into()); - tmp_args.push(full_bootnodes.join(" ")); - } + let node_specific_bootnodes: Vec = + node.bootnodes_addresses.iter().map(|b| b.to_string()).collect(); + let full_bootnodes = [node_specific_bootnodes, options.bootnode_addr].concat(); + if !full_bootnodes.is_empty() { + tmp_args.push("--bootnodes".into()); + tmp_args.push(full_bootnodes.join(" ")); + } - // add the rest of the args - let mut args_filtered = args - .iter() - .filter_map(|arg| match arg { - Arg::Flag(flag) => { - if flag.starts_with("-:") || FLAGS_ADDED_BY_US.contains(&flag.as_str()) { - None - } else { - Some(vec![flag.to_owned()]) - } - }, - Arg::Option(k, v) => { - if OPS_ADDED_BY_US.contains(&k.as_str()) { - None - } else { - Some(vec![k.to_owned(), v.to_owned()]) - } - }, - Arg::Array(k, v) => { - let mut args = vec![k.to_owned()]; - args.extend(v.to_owned()); - Some(args) - }, - }) - .flatten() - .collect::>(); + // add the rest of the args + let mut args_filtered = args + .iter() + .filter_map(|arg| match arg { + Arg::Flag(flag) => { + if flag.starts_with("-:") || FLAGS_ADDED_BY_US.contains(&flag.as_str()) { + None + } else { + Some(vec![flag.to_owned()]) + } + }, + Arg::Option(k, v) => { + if OPS_ADDED_BY_US.contains(&k.as_str()) { + None + } else { + Some(vec![k.to_owned(), v.to_owned()]) + } + }, + Arg::Array(k, v) => { + let mut args = vec![k.to_owned()]; + args.extend(v.to_owned()); + Some(args) + }, + }) + .flatten() + .collect::>(); - tmp_args.append(&mut args_filtered); + tmp_args.append(&mut args_filtered); - let chain_spec_path = format!("{}/{}.json", options.cfg_path, options.relay_chain_name); - let mut final_args = vec![ - node.command.as_str().to_string(), - "--chain".into(), - chain_spec_path, - "--name".into(), - node.name.clone(), - "--rpc-cors".into(), - "all".into(), - "--rpc-methods".into(), - "unsafe".into(), - ]; + let chain_spec_path = format!("{}/{}.json", options.cfg_path, options.relay_chain_name); + let mut final_args = vec![ + node.command.as_str().to_string(), + "--chain".into(), + chain_spec_path, + "--name".into(), + node.name.clone(), + "--rpc-cors".into(), + "all".into(), + "--rpc-methods".into(), + "unsafe".into(), + ]; - // The `--unsafe-rpc-external` option spawns an additional RPC server on a random port, - // which can conflict with reserved ports, causing an "Address already in use" error - // when using the `native` provider. Since this option isn't needed for `native`, - // it should be omitted in that case. - if !options.is_native { - final_args.push("--unsafe-rpc-external".into()); - } + // The `--unsafe-rpc-external` option spawns an additional RPC server on a random port, + // which can conflict with reserved ports, causing an "Address already in use" error + // when using the `native` provider. Since this option isn't needed for `native`, + // it should be omitted in that case. + if !options.is_native { + final_args.push("--unsafe-rpc-external".into()); + } - final_args.append(&mut tmp_args); + final_args.append(&mut tmp_args); - if let Some(ref subcommand) = node.subcommand { - final_args.insert(1, subcommand.as_str().to_string()); - } + if let Some(ref subcommand) = node.subcommand { + final_args.insert(1, subcommand.as_str().to_string()); + } - let removals = parse_removal_args(args); - final_args = apply_arg_removals(final_args, &removals); + let removals = parse_removal_args(args); + final_args = apply_arg_removals(final_args, &removals); - if options.use_wrapper { - ("/cfg/zombie-wrapper.sh".to_string(), final_args) - } else { - (final_args.remove(0), final_args) - } + if options.use_wrapper { + ("/cfg/zombie-wrapper.sh".to_string(), final_args) + } else { + (final_args.remove(0), final_args) + } } /// Returns (prometheus, rpc, p2p) ports to use in the command fn resolve_ports(node: &NodeSpec, use_default_ports_in_cmd: bool) -> (u16, u16, u16) { - if use_default_ports_in_cmd { - (PROMETHEUS_PORT, RPC_PORT, P2P_PORT) - } else { - (node.prometheus_port.0, node.rpc_port.0, node.p2p_port.0) - } + if use_default_ports_in_cmd { + (PROMETHEUS_PORT, RPC_PORT, P2P_PORT) + } else { + (node.prometheus_port.0, node.rpc_port.0, node.p2p_port.0) + } } #[cfg(test)] mod tests { - use super::*; - use crate::{generators, shared::types::NodeAccounts}; + use super::*; + use crate::{generators, shared::types::NodeAccounts}; - fn get_node_spec(full_node_present: bool) -> NodeSpec { - let mut name = String::from("luca"); - let initial_balance = 1_000_000_000_000_u128; - let seed = format!("//{}{name}", name.remove(0).to_uppercase()); - let accounts = NodeAccounts { - accounts: generators::generate_node_keys(&seed).unwrap(), - seed, - }; - let (full_node_p2p_port, full_node_prometheus_port) = if full_node_present { - ( - Some(generators::generate_node_port(None).unwrap()), - Some(generators::generate_node_port(None).unwrap()), - ) - } else { - (None, None) - }; - NodeSpec { - name, - accounts, - initial_balance, - full_node_p2p_port, - full_node_prometheus_port, - ..Default::default() - } - } + fn get_node_spec(full_node_present: bool) -> NodeSpec { + let mut name = String::from("luca"); + let initial_balance = 1_000_000_000_000_u128; + let seed = format!("//{}{name}", name.remove(0).to_uppercase()); + let accounts = + NodeAccounts { accounts: generators::generate_node_keys(&seed).unwrap(), seed }; + let (full_node_p2p_port, full_node_prometheus_port) = if full_node_present { + ( + Some(generators::generate_node_port(None).unwrap()), + Some(generators::generate_node_port(None).unwrap()), + ) + } else { + (None, None) + }; + NodeSpec { + name, + accounts, + initial_balance, + full_node_p2p_port, + full_node_prometheus_port, + ..Default::default() + } + } - #[test] - fn generate_for_native_cumulus_node_works() { - let node = get_node_spec(true); - let opts = GenCmdOptions { - use_wrapper: false, - is_native: true, - ..GenCmdOptions::default() - }; + #[test] + fn generate_for_native_cumulus_node_works() { + let node = get_node_spec(true); + let opts = + GenCmdOptions { use_wrapper: false, is_native: true, ..GenCmdOptions::default() }; - let (program, args) = generate_for_cumulus_node(&node, opts, 1000); - assert_eq!(program.as_str(), "polkadot"); + let (program, args) = generate_for_cumulus_node(&node, opts, 1000); + assert_eq!(program.as_str(), "polkadot"); - let divider_flag = args.iter().position(|x| x == "--").unwrap(); + let divider_flag = args.iter().position(|x| x == "--").unwrap(); - // ensure full node ports - let i = args[divider_flag..] - .iter() - .position(|x| { - x == node - .full_node_p2p_port - .as_ref() - .unwrap() - .0 - .to_string() - .as_str() - }) - .unwrap(); - assert_eq!(&args[divider_flag + i - 1], "--port"); + // ensure full node ports + let i = args[divider_flag..] + .iter() + .position(|x| x == node.full_node_p2p_port.as_ref().unwrap().0.to_string().as_str()) + .unwrap(); + assert_eq!(&args[divider_flag + i - 1], "--port"); - let i = args[divider_flag..] - .iter() - .position(|x| { - x == node - .full_node_prometheus_port - .as_ref() - .unwrap() - .0 - .to_string() - .as_str() - }) - .unwrap(); - assert_eq!(&args[divider_flag + i - 1], "--prometheus-port"); + let i = args[divider_flag..] + .iter() + .position(|x| { + x == node.full_node_prometheus_port.as_ref().unwrap().0.to_string().as_str() + }) + .unwrap(); + assert_eq!(&args[divider_flag + i - 1], "--prometheus-port"); - assert!(!args.iter().any(|arg| arg == "--unsafe-rpc-external")); - } + assert!(!args.iter().any(|arg| arg == "--unsafe-rpc-external")); + } - #[test] - fn generate_for_native_cumulus_node_rpc_external_is_not_removed_if_is_set_by_user() { - let mut node = get_node_spec(true); - node.args.push("--unsafe-rpc-external".into()); - let opts = GenCmdOptions { - use_wrapper: false, - is_native: true, - ..GenCmdOptions::default() - }; + #[test] + fn generate_for_native_cumulus_node_rpc_external_is_not_removed_if_is_set_by_user() { + let mut node = get_node_spec(true); + node.args.push("--unsafe-rpc-external".into()); + let opts = + GenCmdOptions { use_wrapper: false, is_native: true, ..GenCmdOptions::default() }; - let (_, args) = generate_for_cumulus_node(&node, opts, 1000); + let (_, args) = generate_for_cumulus_node(&node, opts, 1000); - assert!(args.iter().any(|arg| arg == "--unsafe-rpc-external")); - } + assert!(args.iter().any(|arg| arg == "--unsafe-rpc-external")); + } - #[test] - fn generate_for_non_native_cumulus_node_works() { - let node = get_node_spec(true); - let opts = GenCmdOptions { - use_wrapper: false, - is_native: false, - ..GenCmdOptions::default() - }; + #[test] + fn generate_for_non_native_cumulus_node_works() { + let node = get_node_spec(true); + let opts = + GenCmdOptions { use_wrapper: false, is_native: false, ..GenCmdOptions::default() }; - let (program, args) = generate_for_cumulus_node(&node, opts, 1000); - assert_eq!(program.as_str(), "polkadot"); + let (program, args) = generate_for_cumulus_node(&node, opts, 1000); + assert_eq!(program.as_str(), "polkadot"); - let divider_flag = args.iter().position(|x| x == "--").unwrap(); + let divider_flag = args.iter().position(|x| x == "--").unwrap(); - // ensure full node ports - let i = args[divider_flag..] - .iter() - .position(|x| { - x == node - .full_node_p2p_port - .as_ref() - .unwrap() - .0 - .to_string() - .as_str() - }) - .unwrap(); - assert_eq!(&args[divider_flag + i - 1], "--port"); + // ensure full node ports + let i = args[divider_flag..] + .iter() + .position(|x| x == node.full_node_p2p_port.as_ref().unwrap().0.to_string().as_str()) + .unwrap(); + assert_eq!(&args[divider_flag + i - 1], "--port"); - let i = args[divider_flag..] - .iter() - .position(|x| { - x == node - .full_node_prometheus_port - .as_ref() - .unwrap() - .0 - .to_string() - .as_str() - }) - .unwrap(); - assert_eq!(&args[divider_flag + i - 1], "--prometheus-port"); + let i = args[divider_flag..] + .iter() + .position(|x| { + x == node.full_node_prometheus_port.as_ref().unwrap().0.to_string().as_str() + }) + .unwrap(); + assert_eq!(&args[divider_flag + i - 1], "--prometheus-port"); - // we expect to find this arg in collator node part - assert!(&args[0..divider_flag] - .iter() - .any(|arg| arg == "--unsafe-rpc-external")); - } + // we expect to find this arg in collator node part + assert!(&args[0..divider_flag].iter().any(|arg| arg == "--unsafe-rpc-external")); + } - #[test] - fn generate_for_native_node_rpc_external_works() { - let node = get_node_spec(false); - let opts = GenCmdOptions { - use_wrapper: false, - is_native: true, - ..GenCmdOptions::default() - }; + #[test] + fn generate_for_native_node_rpc_external_works() { + let node = get_node_spec(false); + let opts = + GenCmdOptions { use_wrapper: false, is_native: true, ..GenCmdOptions::default() }; - let (program, args) = generate_for_node(&node, opts, Some(1000)); - assert_eq!(program.as_str(), "polkadot"); + let (program, args) = generate_for_node(&node, opts, Some(1000)); + assert_eq!(program.as_str(), "polkadot"); - assert!(!args.iter().any(|arg| arg == "--unsafe-rpc-external")); - } + assert!(!args.iter().any(|arg| arg == "--unsafe-rpc-external")); + } - #[test] - fn generate_for_non_native_node_rpc_external_works() { - let node = get_node_spec(false); - let opts = GenCmdOptions { - use_wrapper: false, - is_native: false, - ..GenCmdOptions::default() - }; + #[test] + fn generate_for_non_native_node_rpc_external_works() { + let node = get_node_spec(false); + let opts = + GenCmdOptions { use_wrapper: false, is_native: false, ..GenCmdOptions::default() }; - let (program, args) = generate_for_node(&node, opts, Some(1000)); - assert_eq!(program.as_str(), "polkadot"); + let (program, args) = generate_for_node(&node, opts, Some(1000)); + assert_eq!(program.as_str(), "polkadot"); - assert!(args.iter().any(|arg| arg == "--unsafe-rpc-external")); - } + assert!(args.iter().any(|arg| arg == "--unsafe-rpc-external")); + } - #[test] - fn test_arg_removal_removes_insecure_validator_flag() { - let mut node = get_node_spec(false); - node.args - .push(Arg::Flag("-:--insecure-validator-i-know-what-i-do".into())); - node.is_validator = true; - node.available_args_output = Some("--insecure-validator-i-know-what-i-do".to_string()); + #[test] + fn test_arg_removal_removes_insecure_validator_flag() { + let mut node = get_node_spec(false); + node.args.push(Arg::Flag("-:--insecure-validator-i-know-what-i-do".into())); + node.is_validator = true; + node.available_args_output = Some("--insecure-validator-i-know-what-i-do".to_string()); - let opts = GenCmdOptions { - use_wrapper: false, - is_native: true, - ..GenCmdOptions::default() - }; + let opts = + GenCmdOptions { use_wrapper: false, is_native: true, ..GenCmdOptions::default() }; - let (program, args) = generate_for_node(&node, opts, Some(1000)); - assert_eq!(program.as_str(), "polkadot"); - assert!(args.iter().any(|arg| arg == "--validator")); - assert!(!args - .iter() - .any(|arg| arg == "--insecure-validator-i-know-what-i-do")); - } + let (program, args) = generate_for_node(&node, opts, Some(1000)); + assert_eq!(program.as_str(), "polkadot"); + assert!(args.iter().any(|arg| arg == "--validator")); + assert!(!args.iter().any(|arg| arg == "--insecure-validator-i-know-what-i-do")); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/errors.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/errors.rs index 5828cbe4..9516beba 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/errors.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/errors.rs @@ -3,22 +3,22 @@ use support::fs::FileSystemError; #[derive(Debug, thiserror::Error)] pub enum GeneratorError { - #[error("Generating key {0} with input {1}")] - KeyGeneration(String, String), - #[error("Generating port {0}, err {1}")] - PortGeneration(u16, String), - #[error("Chain-spec build error: {0}")] - ChainSpecGeneration(String), - #[error("Provider error: {0}")] - ProviderError(#[from] ProviderError), - #[error("FileSystem error")] - FileSystemError(#[from] FileSystemError), - #[error("Generating identity, err {0}")] - IdentityGeneration(String), - #[error("Generating bootnode address, err {0}")] - BootnodeAddrGeneration(String), - #[error("Error overriding wasm on raw chain-spec, err {0}")] - OverridingWasm(String), - #[error("Error overriding raw chain-spec, err {0}")] - OverridingRawSpec(String), + #[error("Generating key {0} with input {1}")] + KeyGeneration(String, String), + #[error("Generating port {0}, err {1}")] + PortGeneration(u16, String), + #[error("Chain-spec build error: {0}")] + ChainSpecGeneration(String), + #[error("Provider error: {0}")] + ProviderError(#[from] ProviderError), + #[error("FileSystem error")] + FileSystemError(#[from] FileSystemError), + #[error("Generating identity, err {0}")] + IdentityGeneration(String), + #[error("Generating bootnode address, err {0}")] + BootnodeAddrGeneration(String), + #[error("Error overriding wasm on raw chain-spec, err {0}")] + OverridingWasm(String), + #[error("Error overriding raw chain-spec, err {0}")] + OverridingRawSpec(String), } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/identity.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/identity.rs index 62ac5f36..787932d9 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/identity.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/identity.rs @@ -7,35 +7,29 @@ use super::errors::GeneratorError; // Generate p2p identity for node // return `node-key` and `peerId` pub fn generate(node_name: &str) -> Result<(String, String), GeneratorError> { - let key = hex::encode(sha2::Sha256::digest(node_name)); + let key = hex::encode(sha2::Sha256::digest(node_name)); - let bytes = <[u8; 32]>::from_hex(key.clone()).map_err(|_| { - GeneratorError::IdentityGeneration("can not transform hex to [u8;32]".into()) - })?; - let sk = ed25519::SecretKey::try_from_bytes(bytes) - .map_err(|_| GeneratorError::IdentityGeneration("can not create sk from bytes".into()))?; - let local_identity: Keypair = ed25519::Keypair::from(sk).into(); - let local_public = local_identity.public(); - let local_peer_id = local_public.to_peer_id(); + let bytes = <[u8; 32]>::from_hex(key.clone()).map_err(|_| { + GeneratorError::IdentityGeneration("can not transform hex to [u8;32]".into()) + })?; + let sk = ed25519::SecretKey::try_from_bytes(bytes) + .map_err(|_| GeneratorError::IdentityGeneration("can not create sk from bytes".into()))?; + let local_identity: Keypair = ed25519::Keypair::from(sk).into(); + let local_public = local_identity.public(); + let local_peer_id = local_public.to_peer_id(); - Ok((key, local_peer_id.to_base58())) + Ok((key, local_peer_id.to_base58())) } #[cfg(test)] mod tests { - use super::*; - #[test] - fn generate_for_alice() { - let s = "alice"; - let (key, peer_id) = generate(s).unwrap(); - assert_eq!( - &key, - "2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db186d6e90" - ); - assert_eq!( - &peer_id, - "12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm" - ); - } + use super::*; + #[test] + fn generate_for_alice() { + let s = "alice"; + let (key, peer_id) = generate(s).unwrap(); + assert_eq!(&key, "2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db186d6e90"); + assert_eq!(&peer_id, "12D3KooWQCkBm1BYtkHpocxCwMgR8yjitEeHGx8spzcDLGt2gkBm"); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/key.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/key.rs index 55183858..dcff2718 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/key.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/key.rs @@ -1,151 +1,141 @@ -use pezsp_core::{crypto::SecretStringError, ecdsa, ed25519, keccak_256, sr25519, Pair, H160, H256}; +use pezsp_core::{ + crypto::SecretStringError, ecdsa, ed25519, keccak_256, sr25519, Pair, H160, H256, +}; use super::errors::GeneratorError; use crate::shared::types::{Accounts, NodeAccount}; const KEYS: [&str; 5] = ["sr", "sr_stash", "ed", "ec", "eth"]; pub fn generate_pair(seed: &str) -> Result { - let pair = T::Pair::from_string(seed, None)?; - Ok(pair) + let pair = T::Pair::from_string(seed, None)?; + Ok(pair) } pub fn generate(seed: &str) -> Result { - let mut accounts: Accounts = Default::default(); - for k in KEYS { - let (address, public_key) = match k { - "sr" => { - let pair = generate_pair::(seed) - .map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?; - (pair.public().to_string(), hex::encode(pair.public())) - }, - "sr_stash" => { - let pair = generate_pair::(&format!("{seed}//stash")) - .map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?; - (pair.public().to_string(), hex::encode(pair.public())) - }, - "ed" => { - let pair = generate_pair::(seed) - .map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?; - (pair.public().to_string(), hex::encode(pair.public())) - }, - "ec" => { - let pair = generate_pair::(seed) - .map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?; - (pair.public().to_string(), hex::encode(pair.public())) - }, - "eth" => { - let pair = generate_pair::(seed) - .map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?; + let mut accounts: Accounts = Default::default(); + for k in KEYS { + let (address, public_key) = match k { + "sr" => { + let pair = generate_pair::(seed) + .map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?; + (pair.public().to_string(), hex::encode(pair.public())) + }, + "sr_stash" => { + let pair = generate_pair::(&format!("{seed}//stash")) + .map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?; + (pair.public().to_string(), hex::encode(pair.public())) + }, + "ed" => { + let pair = generate_pair::(seed) + .map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?; + (pair.public().to_string(), hex::encode(pair.public())) + }, + "ec" => { + let pair = generate_pair::(seed) + .map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?; + (pair.public().to_string(), hex::encode(pair.public())) + }, + "eth" => { + let pair = generate_pair::(seed) + .map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))?; - let decompressed = libsecp256k1::PublicKey::parse_compressed(&pair.public().0) - .map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))? - .serialize(); - let mut m = [0u8; 64]; - m.copy_from_slice(&decompressed[1..65]); - let account = H160::from(H256::from(keccak_256(&m))); + let decompressed = libsecp256k1::PublicKey::parse_compressed(&pair.public().0) + .map_err(|_| GeneratorError::KeyGeneration(k.into(), seed.into()))? + .serialize(); + let mut m = [0u8; 64]; + m.copy_from_slice(&decompressed[1..65]); + let account = H160::from(H256::from(keccak_256(&m))); - (hex::encode(account), hex::encode(account)) - }, - _ => unreachable!(), - }; - accounts.insert(k.into(), NodeAccount::new(address, public_key)); - } - Ok(accounts) + (hex::encode(account), hex::encode(account)) + }, + _ => unreachable!(), + }; + accounts.insert(k.into(), NodeAccount::new(address, public_key)); + } + Ok(accounts) } #[cfg(test)] mod tests { - use super::*; - #[test] - fn generate_for_alice() { - use pezsp_core::crypto::Ss58Codec; - let s = "Alice"; - let seed = format!("//{s}"); + use super::*; + #[test] + fn generate_for_alice() { + use pezsp_core::crypto::Ss58Codec; + let s = "Alice"; + let seed = format!("//{s}"); - let pair = generate_pair::(&seed).unwrap(); - assert_eq!( - "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", - pair.public().to_ss58check() - ); + let pair = generate_pair::(&seed).unwrap(); + assert_eq!( + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + pair.public().to_ss58check() + ); - let pair = generate_pair::(&seed).unwrap(); - assert_eq!( - "0x020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1", - format!("0x{}", hex::encode(pair.public())) - ); + let pair = generate_pair::(&seed).unwrap(); + assert_eq!( + "0x020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1", + format!("0x{}", hex::encode(pair.public())) + ); - let pair = generate_pair::(&seed).unwrap(); - assert_eq!( - "5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu", - pair.public().to_ss58check() - ); - } + let pair = generate_pair::(&seed).unwrap(); + assert_eq!( + "5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu", + pair.public().to_ss58check() + ); + } - #[test] - fn generate_for_zombie() { - use pezsp_core::crypto::Ss58Codec; - let s = "Zombie"; - let seed = format!("//{s}"); + #[test] + fn generate_for_zombie() { + use pezsp_core::crypto::Ss58Codec; + let s = "Zombie"; + let seed = format!("//{s}"); - let pair = generate_pair::(&seed).unwrap(); - assert_eq!( - "5FTcLfwFc7ctvqp3RhbEig6UuHLHcHVRujuUm8r21wy4dAR8", - pair.public().to_ss58check() - ); - } + let pair = generate_pair::(&seed).unwrap(); + assert_eq!( + "5FTcLfwFc7ctvqp3RhbEig6UuHLHcHVRujuUm8r21wy4dAR8", + pair.public().to_ss58check() + ); + } - #[test] - fn generate_pair_invalid_should_fail() { - let s = "Alice"; - let seed = s.to_string(); + #[test] + fn generate_pair_invalid_should_fail() { + let s = "Alice"; + let seed = s.to_string(); - let pair = generate_pair::(&seed); - assert!(pair.is_err()); - } + let pair = generate_pair::(&seed); + assert!(pair.is_err()); + } - #[test] - fn generate_invalid_should_fail() { - let s = "Alice"; - let seed = s.to_string(); + #[test] + fn generate_invalid_should_fail() { + let s = "Alice"; + let seed = s.to_string(); - let pair = generate(&seed); - assert!(pair.is_err()); - assert!(matches!(pair, Err(GeneratorError::KeyGeneration(_, _)))); - } + let pair = generate(&seed); + assert!(pair.is_err()); + assert!(matches!(pair, Err(GeneratorError::KeyGeneration(_, _)))); + } - #[test] - fn generate_work() { - let s = "Alice"; - let seed = format!("//{s}"); + #[test] + fn generate_work() { + let s = "Alice"; + let seed = format!("//{s}"); - let pair = generate(&seed).unwrap(); - let sr = pair.get("sr").unwrap(); - let sr_stash = pair.get("sr_stash").unwrap(); - let ed = pair.get("ed").unwrap(); - let ec = pair.get("ec").unwrap(); - let eth = pair.get("eth").unwrap(); + let pair = generate(&seed).unwrap(); + let sr = pair.get("sr").unwrap(); + let sr_stash = pair.get("sr_stash").unwrap(); + let ed = pair.get("ed").unwrap(); + let ec = pair.get("ec").unwrap(); + let eth = pair.get("eth").unwrap(); - assert_eq!( - sr.address, - "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" - ); - assert_eq!( - sr_stash.address, - "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY" - ); - assert_eq!( - ed.address, - "5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu" - ); - assert_eq!( - format!("0x{}", ec.public_key), - "0x020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1" - ); + assert_eq!(sr.address, "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"); + assert_eq!(sr_stash.address, "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY"); + assert_eq!(ed.address, "5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu"); + assert_eq!( + format!("0x{}", ec.public_key), + "0x020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1" + ); - assert_eq!( - format!("0x{}", eth.public_key), - "0xe04cc55ebee1cbce552f250e85c57b70b2e2625b" - ) - } + assert_eq!(format!("0x{}", eth.public_key), "0xe04cc55ebee1cbce552f250e85c57b70b2e2625b") + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/keystore.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/keystore.rs index 2a04d56b..1237f1cc 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/keystore.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/keystore.rs @@ -1,6 +1,6 @@ use std::{ - path::{Path, PathBuf}, - vec, + path::{Path, PathBuf}, + vec, }; use hex::encode; @@ -8,9 +8,9 @@ use support::{constants::THIS_IS_A_BUG, fs::FileSystem}; use super::errors::GeneratorError; use crate::{ - generators::keystore_key_types::{parse_keystore_key_types, KeystoreKeyType}, - shared::types::NodeAccounts, - ScopedFilesystem, + generators::keystore_key_types::{parse_keystore_key_types, KeystoreKeyType}, + shared::types::NodeAccounts, + ScopedFilesystem, }; /// Generates keystore files for a node. @@ -25,266 +25,230 @@ use crate::{ /// If `keystore_key_types` is empty, all default key types will be generated. /// Otherwise, only the specified key types will be generated. pub async fn generate<'a, T>( - acc: &NodeAccounts, - node_files_path: impl AsRef, - scoped_fs: &ScopedFilesystem<'a, T>, - asset_hub_polkadot: bool, - keystore_key_types: Vec<&str>, + acc: &NodeAccounts, + node_files_path: impl AsRef, + scoped_fs: &ScopedFilesystem<'a, T>, + asset_hub_polkadot: bool, + keystore_key_types: Vec<&str>, ) -> Result, GeneratorError> where - T: FileSystem, + T: FileSystem, { - // Create local keystore - scoped_fs.create_dir_all(node_files_path.as_ref()).await?; - let mut filenames = vec![]; + // Create local keystore + scoped_fs.create_dir_all(node_files_path.as_ref()).await?; + let mut filenames = vec![]; - // Parse the key type specifications - let key_types = parse_keystore_key_types(&keystore_key_types, asset_hub_polkadot); + // Parse the key type specifications + let key_types = parse_keystore_key_types(&keystore_key_types, asset_hub_polkadot); - let futures: Vec<_> = key_types - .iter() - .map(|key_type| { - let filename = generate_keystore_filename(key_type, acc); - let file_path = PathBuf::from(format!( - "{}/{}", - node_files_path.as_ref().to_string_lossy(), - filename - )); - let content = format!("\"{}\"", acc.seed); - (filename, scoped_fs.write(file_path, content)) - }) - .collect(); + let futures: Vec<_> = key_types + .iter() + .map(|key_type| { + let filename = generate_keystore_filename(key_type, acc); + let file_path = PathBuf::from(format!( + "{}/{}", + node_files_path.as_ref().to_string_lossy(), + filename + )); + let content = format!("\"{}\"", acc.seed); + (filename, scoped_fs.write(file_path, content)) + }) + .collect(); - for (filename, future) in futures { - future.await?; - filenames.push(PathBuf::from(filename)); - } + for (filename, future) in futures { + future.await?; + filenames.push(PathBuf::from(filename)); + } - Ok(filenames) + Ok(filenames) } /// Generates the keystore filename for a given key type. /// /// The filename format is: `{hex_encoded_key_type}{public_key}` fn generate_keystore_filename(key_type: &KeystoreKeyType, acc: &NodeAccounts) -> String { - let account_key = key_type.scheme.account_key(); - let pk = acc - .accounts - .get(account_key) - .expect(&format!( - "Key '{}' should be set for node {THIS_IS_A_BUG}", - account_key - )) - .public_key - .as_str(); + let account_key = key_type.scheme.account_key(); + let pk = acc + .accounts + .get(account_key) + .expect(&format!("Key '{}' should be set for node {THIS_IS_A_BUG}", account_key)) + .public_key + .as_str(); - format!("{}{}", encode(&key_type.key_type), pk) + format!("{}{}", encode(&key_type.key_type), pk) } #[cfg(test)] mod tests { - use std::{collections::HashMap, ffi::OsString, str::FromStr}; + use std::{collections::HashMap, ffi::OsString, str::FromStr}; - use support::fs::in_memory::{InMemoryFile, InMemoryFileSystem}; + use support::fs::in_memory::{InMemoryFile, InMemoryFileSystem}; - use super::*; - use crate::shared::types::{NodeAccount, NodeAccounts}; + use super::*; + use crate::shared::types::{NodeAccount, NodeAccounts}; - fn create_test_accounts() -> NodeAccounts { - let mut accounts = HashMap::new(); - accounts.insert( - "sr".to_string(), - NodeAccount::new("sr_address", "sr_public_key"), - ); - accounts.insert( - "ed".to_string(), - NodeAccount::new("ed_address", "ed_public_key"), - ); - accounts.insert( - "ec".to_string(), - NodeAccount::new("ec_address", "ec_public_key"), - ); - NodeAccounts { - seed: "//Alice".to_string(), - accounts, - } - } + fn create_test_accounts() -> NodeAccounts { + let mut accounts = HashMap::new(); + accounts.insert("sr".to_string(), NodeAccount::new("sr_address", "sr_public_key")); + accounts.insert("ed".to_string(), NodeAccount::new("ed_address", "ed_public_key")); + accounts.insert("ec".to_string(), NodeAccount::new("ec_address", "ec_public_key")); + NodeAccounts { seed: "//Alice".to_string(), accounts } + } - fn create_test_fs() -> InMemoryFileSystem { - InMemoryFileSystem::new(HashMap::from([( - OsString::from_str("/").unwrap(), - InMemoryFile::dir(), - )])) - } + fn create_test_fs() -> InMemoryFileSystem { + InMemoryFileSystem::new(HashMap::from([( + OsString::from_str("/").unwrap(), + InMemoryFile::dir(), + )])) + } - #[tokio::test] - async fn generate_creates_default_keystore_files_when_no_key_types_specified() { - let accounts = create_test_accounts(); - let fs = create_test_fs(); - let base_dir = "/tmp/test"; + #[tokio::test] + async fn generate_creates_default_keystore_files_when_no_key_types_specified() { + let accounts = create_test_accounts(); + let fs = create_test_fs(); + let base_dir = "/tmp/test"; - let scoped_fs = ScopedFilesystem { fs: &fs, base_dir }; - let key_types: Vec<&str> = vec![]; + let scoped_fs = ScopedFilesystem { fs: &fs, base_dir }; + let key_types: Vec<&str> = vec![]; - let res = generate(&accounts, "node1", &scoped_fs, false, key_types).await; - assert!(res.is_ok()); + let res = generate(&accounts, "node1", &scoped_fs, false, key_types).await; + assert!(res.is_ok()); - let filenames = res.unwrap(); + let filenames = res.unwrap(); - assert!(filenames.len() > 10); + assert!(filenames.len() > 10); - let filename_strs: Vec = filenames - .iter() - .map(|p| p.to_string_lossy().to_string()) - .collect(); + let filename_strs: Vec = + filenames.iter().map(|p| p.to_string_lossy().to_string()).collect(); - // Check that aura key is generated (hex of "aura" is 61757261) - assert!(filename_strs.iter().any(|f| f.starts_with("61757261"))); - // Check that babe key is generated (hex of "babe" is 62616265) - assert!(filename_strs.iter().any(|f| f.starts_with("62616265"))); - // Check that gran key is generated (hex of "gran" is 6772616e) - assert!(filename_strs.iter().any(|f| f.starts_with("6772616e"))); - } + // Check that aura key is generated (hex of "aura" is 61757261) + assert!(filename_strs.iter().any(|f| f.starts_with("61757261"))); + // Check that babe key is generated (hex of "babe" is 62616265) + assert!(filename_strs.iter().any(|f| f.starts_with("62616265"))); + // Check that gran key is generated (hex of "gran" is 6772616e) + assert!(filename_strs.iter().any(|f| f.starts_with("6772616e"))); + } - #[tokio::test] - async fn generate_creates_only_specified_keystore_files() { - let accounts = create_test_accounts(); - let fs = create_test_fs(); - let base_dir = "/tmp/test"; + #[tokio::test] + async fn generate_creates_only_specified_keystore_files() { + let accounts = create_test_accounts(); + let fs = create_test_fs(); + let base_dir = "/tmp/test"; - let scoped_fs = ScopedFilesystem { fs: &fs, base_dir }; - let key_types = vec!["audi", "gran"]; + let scoped_fs = ScopedFilesystem { fs: &fs, base_dir }; + let key_types = vec!["audi", "gran"]; - let res = generate(&accounts, "node1", &scoped_fs, false, key_types).await; + let res = generate(&accounts, "node1", &scoped_fs, false, key_types).await; - assert!(res.is_ok()); + assert!(res.is_ok()); - let filenames = res.unwrap(); - assert_eq!(filenames.len(), 2); + let filenames = res.unwrap(); + assert_eq!(filenames.len(), 2); - let filename_strs: Vec = filenames - .iter() - .map(|p| p.to_string_lossy().to_string()) - .collect(); + let filename_strs: Vec = + filenames.iter().map(|p| p.to_string_lossy().to_string()).collect(); - // audi uses sr scheme by default - assert!(filename_strs - .iter() - .any(|f| f.starts_with("61756469") && f.contains("sr_public_key"))); - // gran uses ed scheme by default - assert!(filename_strs - .iter() - .any(|f| f.starts_with("6772616e") && f.contains("ed_public_key"))); - } + // audi uses sr scheme by default + assert!(filename_strs + .iter() + .any(|f| f.starts_with("61756469") && f.contains("sr_public_key"))); + // gran uses ed scheme by default + assert!(filename_strs + .iter() + .any(|f| f.starts_with("6772616e") && f.contains("ed_public_key"))); + } - #[tokio::test] - async fn generate_produces_correct_keystore_files() { - struct TestCase { - name: &'static str, - key_types: Vec<&'static str>, - asset_hub_polkadot: bool, - expected_prefix: &'static str, - expected_public_key: &'static str, - } + #[tokio::test] + async fn generate_produces_correct_keystore_files() { + struct TestCase { + name: &'static str, + key_types: Vec<&'static str>, + asset_hub_polkadot: bool, + expected_prefix: &'static str, + expected_public_key: &'static str, + } - let test_cases = vec![ - TestCase { - name: "explicit scheme override (gran_sr)", - key_types: vec!["gran_sr"], - asset_hub_polkadot: false, - expected_prefix: "6772616e", // "gran" in hex - expected_public_key: "sr_public_key", - }, - TestCase { - name: "aura with asset_hub_polkadot uses ed", - key_types: vec!["aura"], - asset_hub_polkadot: true, - expected_prefix: "61757261", // "aura" in hex - expected_public_key: "ed_public_key", - }, - TestCase { - name: "aura without asset_hub_polkadot uses sr", - key_types: vec!["aura"], - asset_hub_polkadot: false, - expected_prefix: "61757261", // "aura" in hex - expected_public_key: "sr_public_key", - }, - TestCase { - name: "custom key type with explicit ec scheme", - key_types: vec!["cust_ec"], - asset_hub_polkadot: false, - expected_prefix: "63757374", // "cust" in hex - expected_public_key: "ec_public_key", - }, - ]; + let test_cases = vec![ + TestCase { + name: "explicit scheme override (gran_sr)", + key_types: vec!["gran_sr"], + asset_hub_polkadot: false, + expected_prefix: "6772616e", // "gran" in hex + expected_public_key: "sr_public_key", + }, + TestCase { + name: "aura with asset_hub_polkadot uses ed", + key_types: vec!["aura"], + asset_hub_polkadot: true, + expected_prefix: "61757261", // "aura" in hex + expected_public_key: "ed_public_key", + }, + TestCase { + name: "aura without asset_hub_polkadot uses sr", + key_types: vec!["aura"], + asset_hub_polkadot: false, + expected_prefix: "61757261", // "aura" in hex + expected_public_key: "sr_public_key", + }, + TestCase { + name: "custom key type with explicit ec scheme", + key_types: vec!["cust_ec"], + asset_hub_polkadot: false, + expected_prefix: "63757374", // "cust" in hex + expected_public_key: "ec_public_key", + }, + ]; - for tc in test_cases { - let accounts = create_test_accounts(); - let fs = create_test_fs(); - let scoped_fs = ScopedFilesystem { - fs: &fs, - base_dir: "/tmp/test", - }; + for tc in test_cases { + let accounts = create_test_accounts(); + let fs = create_test_fs(); + let scoped_fs = ScopedFilesystem { fs: &fs, base_dir: "/tmp/test" }; - let key_types: Vec<&str> = tc.key_types.clone(); - let res = generate( - &accounts, - "node1", - &scoped_fs, - tc.asset_hub_polkadot, - key_types, - ) - .await; + let key_types: Vec<&str> = tc.key_types.clone(); + let res = + generate(&accounts, "node1", &scoped_fs, tc.asset_hub_polkadot, key_types).await; - assert!( - res.is_ok(), - "[{}] Expected Ok but got: {:?}", - tc.name, - res.err() - ); - let filenames = res.unwrap(); + assert!(res.is_ok(), "[{}] Expected Ok but got: {:?}", tc.name, res.err()); + let filenames = res.unwrap(); - assert_eq!(filenames.len(), 1, "[{}] Expected 1 file", tc.name); + assert_eq!(filenames.len(), 1, "[{}] Expected 1 file", tc.name); - let filename = filenames[0].to_string_lossy().to_string(); - assert!( - filename.starts_with(tc.expected_prefix), - "[{}] Expected prefix '{}', got '{}'", - tc.name, - tc.expected_prefix, - filename - ); - assert!( - filename.contains(tc.expected_public_key), - "[{}] Expected public key '{}' in '{}'", - tc.name, - tc.expected_public_key, - filename - ); - } - } + let filename = filenames[0].to_string_lossy().to_string(); + assert!( + filename.starts_with(tc.expected_prefix), + "[{}] Expected prefix '{}', got '{}'", + tc.name, + tc.expected_prefix, + filename + ); + assert!( + filename.contains(tc.expected_public_key), + "[{}] Expected public key '{}' in '{}'", + tc.name, + tc.expected_public_key, + filename + ); + } + } - #[tokio::test] - async fn generate_ignores_invalid_key_specs_and_uses_defaults() { - let accounts = create_test_accounts(); - let fs = create_test_fs(); - let scoped_fs = ScopedFilesystem { - fs: &fs, - base_dir: "/tmp/test", - }; + #[tokio::test] + async fn generate_ignores_invalid_key_specs_and_uses_defaults() { + let accounts = create_test_accounts(); + let fs = create_test_fs(); + let scoped_fs = ScopedFilesystem { fs: &fs, base_dir: "/tmp/test" }; - let key_types = vec![ - "invalid", // Too long - "xxx", // Too short - "audi_xx", // Invalid sceme - ]; + let key_types = vec![ + "invalid", // Too long + "xxx", // Too short + "audi_xx", // Invalid sceme + ]; - let res = generate(&accounts, "node1", &scoped_fs, false, key_types).await; + let res = generate(&accounts, "node1", &scoped_fs, false, key_types).await; - assert!(res.is_ok()); - let filenames = res.unwrap(); + assert!(res.is_ok()); + let filenames = res.unwrap(); - // Should fall back to defaults since all specs are invalid - assert!(filenames.len() > 10); - } + // Should fall back to defaults since all specs are invalid + assert!(filenames.len() > 10); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/keystore_key_types.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/keystore_key_types.rs index 324f6a50..5f9c81bb 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/keystore_key_types.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/keystore_key_types.rs @@ -5,94 +5,91 @@ use serde::{Deserialize, Serialize}; /// Supported cryptographic schemes for keystore keys. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum KeyScheme { - /// Sr25519 scheme - Sr, - /// Ed25519 scheme - Ed, - /// ECDSA scheme - Ec, + /// Sr25519 scheme + Sr, + /// Ed25519 scheme + Ed, + /// ECDSA scheme + Ec, } impl KeyScheme { - /// Returns the account key suffix used in `NodeAccounts` for this scheme. - pub fn account_key(&self) -> &'static str { - match self { - KeyScheme::Sr => "sr", - KeyScheme::Ed => "ed", - KeyScheme::Ec => "ec", - } - } + /// Returns the account key suffix used in `NodeAccounts` for this scheme. + pub fn account_key(&self) -> &'static str { + match self { + KeyScheme::Sr => "sr", + KeyScheme::Ed => "ed", + KeyScheme::Ec => "ec", + } + } } impl std::fmt::Display for KeyScheme { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - KeyScheme::Sr => write!(f, "sr"), - KeyScheme::Ed => write!(f, "ed"), - KeyScheme::Ec => write!(f, "ec"), - } - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + KeyScheme::Sr => write!(f, "sr"), + KeyScheme::Ed => write!(f, "ed"), + KeyScheme::Ec => write!(f, "ec"), + } + } } impl TryFrom<&str> for KeyScheme { - type Error = String; + type Error = String; - fn try_from(value: &str) -> Result { - match value.to_lowercase().as_str() { - "sr" => Ok(KeyScheme::Sr), - "ed" => Ok(KeyScheme::Ed), - "ec" => Ok(KeyScheme::Ec), - _ => Err(format!("Unsupported key scheme: {}", value)), - } - } + fn try_from(value: &str) -> Result { + match value.to_lowercase().as_str() { + "sr" => Ok(KeyScheme::Sr), + "ed" => Ok(KeyScheme::Ed), + "ec" => Ok(KeyScheme::Ec), + _ => Err(format!("Unsupported key scheme: {}", value)), + } + } } /// A parsed keystore key type. #[derive(Debug, Clone, PartialEq, Eq)] pub struct KeystoreKeyType { - /// The 4-character key type identifier (e.g., "aura", "babe", "gran"). - pub key_type: String, - /// The cryptographic scheme to use for this key type. - pub scheme: KeyScheme, + /// The 4-character key type identifier (e.g., "aura", "babe", "gran"). + pub key_type: String, + /// The cryptographic scheme to use for this key type. + pub scheme: KeyScheme, } impl KeystoreKeyType { - pub fn new(key_type: impl Into, scheme: KeyScheme) -> Self { - Self { - key_type: key_type.into(), - scheme, - } - } + pub fn new(key_type: impl Into, scheme: KeyScheme) -> Self { + Self { key_type: key_type.into(), scheme } + } } /// Returns the default predefined key schemes for known key types. /// Special handling for `aura` when `is_asset_hub_polkadot` is true. fn get_predefined_schemes(is_asset_hub_polkadot: bool) -> HashMap<&'static str, KeyScheme> { - let mut schemes = HashMap::new(); + let mut schemes = HashMap::new(); - // aura has special handling for asset-hub-polkadot - if is_asset_hub_polkadot { - schemes.insert("aura", KeyScheme::Ed); - } else { - schemes.insert("aura", KeyScheme::Sr); - } + // aura has special handling for asset-hub-polkadot + if is_asset_hub_polkadot { + schemes.insert("aura", KeyScheme::Ed); + } else { + schemes.insert("aura", KeyScheme::Sr); + } - schemes.insert("babe", KeyScheme::Sr); - schemes.insert("imon", KeyScheme::Sr); - schemes.insert("gran", KeyScheme::Ed); - schemes.insert("audi", KeyScheme::Sr); - schemes.insert("asgn", KeyScheme::Sr); - schemes.insert("para", KeyScheme::Sr); - schemes.insert("beef", KeyScheme::Ec); - schemes.insert("nmbs", KeyScheme::Sr); // Nimbus - schemes.insert("rand", KeyScheme::Sr); // Randomness (Moonbeam) - schemes.insert("rate", KeyScheme::Ed); // Equilibrium rate module - schemes.insert("acco", KeyScheme::Sr); - schemes.insert("bcsv", KeyScheme::Sr); // BlockchainSrvc (StorageHub) - schemes.insert("ftsv", KeyScheme::Ed); // FileTransferSrvc (StorageHub) - schemes.insert("mixn", KeyScheme::Sr); // Mixnet + schemes.insert("babe", KeyScheme::Sr); + schemes.insert("imon", KeyScheme::Sr); + schemes.insert("gran", KeyScheme::Ed); + schemes.insert("audi", KeyScheme::Sr); + schemes.insert("asgn", KeyScheme::Sr); + schemes.insert("para", KeyScheme::Sr); + schemes.insert("beef", KeyScheme::Ec); + schemes.insert("nmbs", KeyScheme::Sr); // Nimbus + schemes.insert("rand", KeyScheme::Sr); // Randomness (Moonbeam) + schemes.insert("rate", KeyScheme::Ed); // Equilibrium rate module + schemes.insert("acco", KeyScheme::Sr); + schemes.insert("bcsv", KeyScheme::Sr); // BlockchainSrvc (StorageHub) + schemes.insert("ftsv", KeyScheme::Ed); // FileTransferSrvc (StorageHub) + schemes.insert("mixn", KeyScheme::Sr); // Mixnet - schemes + schemes } /// Parses a single keystore key type specification string. @@ -103,26 +100,26 @@ fn get_predefined_schemes(is_asset_hub_polkadot: bool) -> HashMap<&'static str, /// /// Returns `None` if the spec is invalid or doesn't match the expected format. fn parse_key_spec(spec: &str, predefined: &HashMap<&str, KeyScheme>) -> Option { - let spec = spec.trim(); + let spec = spec.trim(); - // Try parsing as long form first: key_type_scheme (e.g., "audi_sr") - if let Some((key_type, scheme_str)) = spec.split_once('_') { - if key_type.len() != 4 { - return None; - } + // Try parsing as long form first: key_type_scheme (e.g., "audi_sr") + if let Some((key_type, scheme_str)) = spec.split_once('_') { + if key_type.len() != 4 { + return None; + } - let scheme = KeyScheme::try_from(scheme_str).ok()?; - return Some(KeystoreKeyType::new(key_type, scheme)); - } + let scheme = KeyScheme::try_from(scheme_str).ok()?; + return Some(KeystoreKeyType::new(key_type, scheme)); + } - // Try parsing as short form: key_type only (e.g., "audi") - if spec.len() == 4 { - // Look up predefined scheme; default to Sr if not found - let scheme = predefined.get(spec).copied().unwrap_or(KeyScheme::Sr); - return Some(KeystoreKeyType::new(spec, scheme)); - } + // Try parsing as short form: key_type only (e.g., "audi") + if spec.len() == 4 { + // Look up predefined scheme; default to Sr if not found + let scheme = predefined.get(spec).copied().unwrap_or(KeyScheme::Sr); + return Some(KeystoreKeyType::new(spec, scheme)); + } - None + None } /// Parses a list of keystore key type specifications. @@ -132,151 +129,149 @@ fn parse_key_spec(spec: &str, predefined: &HashMap<&str, KeyScheme>) -> Option>( - specs: &[T], - is_asset_hub_polkadot: bool, + specs: &[T], + is_asset_hub_polkadot: bool, ) -> Vec { - let predefined_schemes = get_predefined_schemes(is_asset_hub_polkadot); + let predefined_schemes = get_predefined_schemes(is_asset_hub_polkadot); - let parsed: Vec = specs - .iter() - .filter_map(|spec| parse_key_spec(spec.as_ref(), &predefined_schemes)) - .collect(); + let parsed: Vec = specs + .iter() + .filter_map(|spec| parse_key_spec(spec.as_ref(), &predefined_schemes)) + .collect(); - if parsed.is_empty() { - get_default_keystore_key_types(is_asset_hub_polkadot) - } else { - parsed - } + if parsed.is_empty() { + get_default_keystore_key_types(is_asset_hub_polkadot) + } else { + parsed + } } /// Returns the default keystore key types when none are specified. pub fn get_default_keystore_key_types(is_asset_hub_polkadot: bool) -> Vec { - let predefined_schemes = get_predefined_schemes(is_asset_hub_polkadot); - let default_keys = [ - "aura", "babe", "imon", "gran", "audi", "asgn", "para", "beef", "nmbs", "rand", "rate", - "mixn", "bcsv", "ftsv", - ]; + let predefined_schemes = get_predefined_schemes(is_asset_hub_polkadot); + let default_keys = [ + "aura", "babe", "imon", "gran", "audi", "asgn", "para", "beef", "nmbs", "rand", "rate", + "mixn", "bcsv", "ftsv", + ]; - default_keys - .iter() - .filter_map(|key_type| { - predefined_schemes - .get(*key_type) - .map(|scheme| KeystoreKeyType::new(*key_type, *scheme)) - }) - .collect() + default_keys + .iter() + .filter_map(|key_type| { + predefined_schemes.get(*key_type).map(|scheme| KeystoreKeyType::new(*key_type, *scheme)) + }) + .collect() } #[cfg(test)] mod tests { - use super::*; + use super::*; - #[test] - fn parse_keystore_key_types_ignores_invalid_specs() { - let specs = vec![ - "audi".to_string(), - "invalid".to_string(), // Too long - ignored - "xxx".to_string(), // Too short - ignored - "xxxx".to_string(), // Unknown key - defaults to sr - "audi_xx".to_string(), // Invalid scheme - ignored - "gran".to_string(), - ]; + #[test] + fn parse_keystore_key_types_ignores_invalid_specs() { + let specs = vec![ + "audi".to_string(), + "invalid".to_string(), // Too long - ignored + "xxx".to_string(), // Too short - ignored + "xxxx".to_string(), // Unknown key - defaults to sr + "audi_xx".to_string(), // Invalid scheme - ignored + "gran".to_string(), + ]; - let result = parse_keystore_key_types(&specs, false); - assert_eq!(result.len(), 3); - assert_eq!(result[1], KeystoreKeyType::new("xxxx", KeyScheme::Sr)); // Unknown defaults to sr - assert_eq!(result[2], KeystoreKeyType::new("gran", KeyScheme::Ed)); - } + let result = parse_keystore_key_types(&specs, false); + assert_eq!(result.len(), 3); + assert_eq!(result[1], KeystoreKeyType::new("xxxx", KeyScheme::Sr)); // Unknown defaults to sr + assert_eq!(result[2], KeystoreKeyType::new("gran", KeyScheme::Ed)); + } - #[test] - fn parse_keystore_key_types_returns_specified_keys() { - let specs = vec!["audi".to_string(), "gran".to_string()]; - let res = parse_keystore_key_types(&specs, false); + #[test] + fn parse_keystore_key_types_returns_specified_keys() { + let specs = vec!["audi".to_string(), "gran".to_string()]; + let res = parse_keystore_key_types(&specs, false); - assert_eq!(res.len(), 2); - assert_eq!(res[0], KeystoreKeyType::new("audi", KeyScheme::Sr)); - assert_eq!(res[1], KeystoreKeyType::new("gran", KeyScheme::Ed)); - } + assert_eq!(res.len(), 2); + assert_eq!(res[0], KeystoreKeyType::new("audi", KeyScheme::Sr)); + assert_eq!(res[1], KeystoreKeyType::new("gran", KeyScheme::Ed)); + } - #[test] - fn parse_keystore_key_types_mixed_short_and_long_forms() { - let specs = vec![ - "audi".to_string(), - "gran_sr".to_string(), // Override gran's default ed to sr - "gran".to_string(), - "beef".to_string(), - ]; - let res = parse_keystore_key_types(&specs, false); + #[test] + fn parse_keystore_key_types_mixed_short_and_long_forms() { + let specs = vec![ + "audi".to_string(), + "gran_sr".to_string(), // Override gran's default ed to sr + "gran".to_string(), + "beef".to_string(), + ]; + let res = parse_keystore_key_types(&specs, false); - assert_eq!(res.len(), 4); - assert_eq!(res[0], KeystoreKeyType::new("audi", KeyScheme::Sr)); - assert_eq!(res[1], KeystoreKeyType::new("gran", KeyScheme::Sr)); // Overridden - assert_eq!(res[2], KeystoreKeyType::new("gran", KeyScheme::Ed)); - assert_eq!(res[3], KeystoreKeyType::new("beef", KeyScheme::Ec)); - } + assert_eq!(res.len(), 4); + assert_eq!(res[0], KeystoreKeyType::new("audi", KeyScheme::Sr)); + assert_eq!(res[1], KeystoreKeyType::new("gran", KeyScheme::Sr)); // Overridden + assert_eq!(res[2], KeystoreKeyType::new("gran", KeyScheme::Ed)); + assert_eq!(res[3], KeystoreKeyType::new("beef", KeyScheme::Ec)); + } - #[test] - fn parse_keystore_key_types_returns_defaults_when_empty() { - let specs: Vec = vec![]; - let res = parse_keystore_key_types(&specs, false); + #[test] + fn parse_keystore_key_types_returns_defaults_when_empty() { + let specs: Vec = vec![]; + let res = parse_keystore_key_types(&specs, false); - // Should return all default keys - assert!(!res.is_empty()); - assert!(res.iter().any(|k| k.key_type == "aura")); - assert!(res.iter().any(|k| k.key_type == "babe")); - assert!(res.iter().any(|k| k.key_type == "gran")); - } + // Should return all default keys + assert!(!res.is_empty()); + assert!(res.iter().any(|k| k.key_type == "aura")); + assert!(res.iter().any(|k| k.key_type == "babe")); + assert!(res.iter().any(|k| k.key_type == "gran")); + } - #[test] - fn parse_keystore_key_types_allows_custom_key_with_explicit_scheme() { - let specs = vec![ - "cust_sr".to_string(), // Custom key with explicit scheme - "audi".to_string(), - ]; - let result = parse_keystore_key_types(&specs, false); + #[test] + fn parse_keystore_key_types_allows_custom_key_with_explicit_scheme() { + let specs = vec![ + "cust_sr".to_string(), // Custom key with explicit scheme + "audi".to_string(), + ]; + let result = parse_keystore_key_types(&specs, false); - assert_eq!(result.len(), 2); - assert_eq!(result[0], KeystoreKeyType::new("cust", KeyScheme::Sr)); - assert_eq!(result[1], KeystoreKeyType::new("audi", KeyScheme::Sr)); - } + assert_eq!(result.len(), 2); + assert_eq!(result[0], KeystoreKeyType::new("cust", KeyScheme::Sr)); + assert_eq!(result[1], KeystoreKeyType::new("audi", KeyScheme::Sr)); + } - #[test] - fn full_workflow_asset_hub_polkadot() { - // For asset-hub-polkadot, aura should default to ed - let specs = vec!["aura".to_string(), "babe".to_string()]; + #[test] + fn full_workflow_asset_hub_polkadot() { + // For asset-hub-polkadot, aura should default to ed + let specs = vec!["aura".to_string(), "babe".to_string()]; - let res = parse_keystore_key_types(&specs, true); + let res = parse_keystore_key_types(&specs, true); - assert_eq!(res.len(), 2); - assert_eq!(res[0].key_type, "aura"); - assert_eq!(res[0].scheme, KeyScheme::Ed); // ed for asset-hub-polkadot + assert_eq!(res.len(), 2); + assert_eq!(res[0].key_type, "aura"); + assert_eq!(res[0].scheme, KeyScheme::Ed); // ed for asset-hub-polkadot - assert_eq!(res[1].key_type, "babe"); - assert_eq!(res[1].scheme, KeyScheme::Sr); - } + assert_eq!(res[1].key_type, "babe"); + assert_eq!(res[1].scheme, KeyScheme::Sr); + } - #[test] - fn full_workflow_custom_key_types() { - let specs = vec![ - "aura".to_string(), // Use default scheme - "gran_sr".to_string(), // Override gran to use sr instead of ed - "cust_ec".to_string(), // Custom key type with ecdsa - ]; + #[test] + fn full_workflow_custom_key_types() { + let specs = vec![ + "aura".to_string(), // Use default scheme + "gran_sr".to_string(), // Override gran to use sr instead of ed + "cust_ec".to_string(), // Custom key type with ecdsa + ]; - let res = parse_keystore_key_types(&specs, false); + let res = parse_keystore_key_types(&specs, false); - assert_eq!(res.len(), 3); + assert_eq!(res.len(), 3); - // aura uses default sr - assert_eq!(res[0].key_type, "aura"); - assert_eq!(res[0].scheme, KeyScheme::Sr); + // aura uses default sr + assert_eq!(res[0].key_type, "aura"); + assert_eq!(res[0].scheme, KeyScheme::Sr); - // gran overridden to sr - assert_eq!(res[1].key_type, "gran"); - assert_eq!(res[1].scheme, KeyScheme::Sr); + // gran overridden to sr + assert_eq!(res[1].key_type, "gran"); + assert_eq!(res[1].scheme, KeyScheme::Sr); - // custom key with ec - assert_eq!(res[2].key_type, "cust"); - assert_eq!(res[2].scheme, KeyScheme::Ec); - } + // custom key with ec + assert_eq!(res[2].key_type, "cust"); + assert_eq!(res[2].scheme, KeyScheme::Ec); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/para_artifact.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/para_artifact.rs index d82695b5..cca6cf8c 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/para_artifact.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/para_artifact.rs @@ -2,9 +2,9 @@ use std::path::{Path, PathBuf}; use configuration::types::CommandWithCustomArgs; use provider::{ - constants::NODE_CONFIG_DIR, - types::{GenerateFileCommand, GenerateFilesOptions, TransferedFile}, - DynNamespace, + constants::NODE_CONFIG_DIR, + types::{GenerateFileCommand, GenerateFilesOptions, TransferedFile}, + DynNamespace, }; use serde::{Deserialize, Serialize}; use support::fs::FileSystem; @@ -15,151 +15,137 @@ use crate::ScopedFilesystem; #[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) enum ParaArtifactType { - Wasm, - State, + Wasm, + State, } #[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) enum ParaArtifactBuildOption { - Path(String), - Command(String), - CommandWithCustomArgs(CommandWithCustomArgs), + Path(String), + Command(String), + CommandWithCustomArgs(CommandWithCustomArgs), } /// Parachain artifact (could be either the genesis state or genesis wasm) #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ParaArtifact { - artifact_type: ParaArtifactType, - build_option: ParaArtifactBuildOption, - artifact_path: Option, - // image to use for building the para artifact - image: Option, + artifact_type: ParaArtifactType, + build_option: ParaArtifactBuildOption, + artifact_path: Option, + // image to use for building the para artifact + image: Option, } impl ParaArtifact { - pub(crate) fn new( - artifact_type: ParaArtifactType, - build_option: ParaArtifactBuildOption, - ) -> Self { - Self { - artifact_type, - build_option, - artifact_path: None, - image: None, - } - } + pub(crate) fn new( + artifact_type: ParaArtifactType, + build_option: ParaArtifactBuildOption, + ) -> Self { + Self { artifact_type, build_option, artifact_path: None, image: None } + } - pub(crate) fn image(mut self, image: Option) -> Self { - self.image = image; - self - } + pub(crate) fn image(mut self, image: Option) -> Self { + self.image = image; + self + } - pub(crate) fn artifact_path(&self) -> Option<&PathBuf> { - self.artifact_path.as_ref() - } + pub(crate) fn artifact_path(&self) -> Option<&PathBuf> { + self.artifact_path.as_ref() + } - pub(crate) async fn build<'a, T>( - &mut self, - chain_spec_path: Option>, - artifact_path: impl AsRef, - ns: &DynNamespace, - scoped_fs: &ScopedFilesystem<'a, T>, - maybe_output_path: Option, - ) -> Result<(), GeneratorError> - where - T: FileSystem, - { - let (cmd, custom_args) = match &self.build_option { - ParaArtifactBuildOption::Path(path) => { - let t = TransferedFile::new(PathBuf::from(path), artifact_path.as_ref().into()); - scoped_fs.copy_files(vec![&t]).await?; - self.artifact_path = Some(artifact_path.as_ref().into()); - return Ok(()); // work done! - }, - ParaArtifactBuildOption::Command(cmd) => (cmd, &vec![]), - ParaArtifactBuildOption::CommandWithCustomArgs(cmd_with_custom_args) => { - ( - &cmd_with_custom_args.cmd().as_str().to_string(), - cmd_with_custom_args.args(), - ) - // (cmd.cmd_as_str().to_string(), cmd.1) - }, - }; + pub(crate) async fn build<'a, T>( + &mut self, + chain_spec_path: Option>, + artifact_path: impl AsRef, + ns: &DynNamespace, + scoped_fs: &ScopedFilesystem<'a, T>, + maybe_output_path: Option, + ) -> Result<(), GeneratorError> + where + T: FileSystem, + { + let (cmd, custom_args) = match &self.build_option { + ParaArtifactBuildOption::Path(path) => { + let t = TransferedFile::new(PathBuf::from(path), artifact_path.as_ref().into()); + scoped_fs.copy_files(vec![&t]).await?; + self.artifact_path = Some(artifact_path.as_ref().into()); + return Ok(()); // work done! + }, + ParaArtifactBuildOption::Command(cmd) => (cmd, &vec![]), + ParaArtifactBuildOption::CommandWithCustomArgs(cmd_with_custom_args) => { + (&cmd_with_custom_args.cmd().as_str().to_string(), cmd_with_custom_args.args()) + // (cmd.cmd_as_str().to_string(), cmd.1) + }, + }; - let generate_subcmd = match self.artifact_type { - ParaArtifactType::Wasm => "export-genesis-wasm", - ParaArtifactType::State => "export-genesis-state", - }; + let generate_subcmd = match self.artifact_type { + ParaArtifactType::Wasm => "export-genesis-wasm", + ParaArtifactType::State => "export-genesis-state", + }; - // TODO: replace uuid with para_id-random - let temp_name = format!("temp-{}-{}", generate_subcmd, Uuid::new_v4()); - let mut args: Vec = vec![generate_subcmd.into()]; + // TODO: replace uuid with para_id-random + let temp_name = format!("temp-{}-{}", generate_subcmd, Uuid::new_v4()); + let mut args: Vec = vec![generate_subcmd.into()]; - let files_to_inject = if let Some(chain_spec_path) = chain_spec_path { - // TODO: we should get the full path from the scoped filesystem - let chain_spec_path_local = format!( - "{}/{}", - ns.base_dir().to_string_lossy(), - chain_spec_path.as_ref().to_string_lossy() - ); - // Remote path to be injected - let chain_spec_path_in_pod = format!( - "{}/{}", - NODE_CONFIG_DIR, - chain_spec_path.as_ref().to_string_lossy() - ); - // Path in the context of the node, this can be different in the context of the providers (e.g native) - let chain_spec_path_in_args = if ns.capabilities().prefix_with_full_path { - // In native - format!( - "{}/{}{}", - ns.base_dir().to_string_lossy(), - &temp_name, - &chain_spec_path_in_pod - ) - } else { - chain_spec_path_in_pod.clone() - }; + let files_to_inject = if let Some(chain_spec_path) = chain_spec_path { + // TODO: we should get the full path from the scoped filesystem + let chain_spec_path_local = format!( + "{}/{}", + ns.base_dir().to_string_lossy(), + chain_spec_path.as_ref().to_string_lossy() + ); + // Remote path to be injected + let chain_spec_path_in_pod = + format!("{}/{}", NODE_CONFIG_DIR, chain_spec_path.as_ref().to_string_lossy()); + // Path in the context of the node, this can be different in the context of the providers (e.g native) + let chain_spec_path_in_args = if ns.capabilities().prefix_with_full_path { + // In native + format!( + "{}/{}{}", + ns.base_dir().to_string_lossy(), + &temp_name, + &chain_spec_path_in_pod + ) + } else { + chain_spec_path_in_pod.clone() + }; - args.push("--chain".into()); - args.push(chain_spec_path_in_args); + args.push("--chain".into()); + args.push(chain_spec_path_in_args); - for custom_arg in custom_args { - match custom_arg { - configuration::types::Arg::Flag(flag) => { - args.push(flag.into()); - }, - configuration::types::Arg::Option(flag, flag_value) => { - args.push(flag.into()); - args.push(flag_value.into()); - }, - configuration::types::Arg::Array(flag, values) => { - args.push(flag.into()); - values.iter().for_each(|v| args.push(v.into())); - }, - } - } + for custom_arg in custom_args { + match custom_arg { + configuration::types::Arg::Flag(flag) => { + args.push(flag.into()); + }, + configuration::types::Arg::Option(flag, flag_value) => { + args.push(flag.into()); + args.push(flag_value.into()); + }, + configuration::types::Arg::Array(flag, values) => { + args.push(flag.into()); + values.iter().for_each(|v| args.push(v.into())); + }, + } + } - vec![TransferedFile::new( - chain_spec_path_local, - chain_spec_path_in_pod, - )] - } else { - vec![] - }; + vec![TransferedFile::new(chain_spec_path_local, chain_spec_path_in_pod)] + } else { + vec![] + }; - let artifact_path_ref = artifact_path.as_ref(); - let generate_command = GenerateFileCommand::new(cmd.as_str(), artifact_path_ref).args(args); - let options = GenerateFilesOptions::with_files( - vec![generate_command], - self.image.clone(), - &files_to_inject, - maybe_output_path, - ) - .temp_name(temp_name); - ns.generate_files(options).await?; - self.artifact_path = Some(artifact_path_ref.into()); + let artifact_path_ref = artifact_path.as_ref(); + let generate_command = GenerateFileCommand::new(cmd.as_str(), artifact_path_ref).args(args); + let options = GenerateFilesOptions::with_files( + vec![generate_command], + self.image.clone(), + &files_to_inject, + maybe_output_path, + ) + .temp_name(temp_name); + ns.generate_files(options).await?; + self.artifact_path = Some(artifact_path_ref.into()); - Ok(()) - } + Ok(()) + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/port.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/port.rs index 409061e0..3140588e 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/port.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/generators/port.rs @@ -8,41 +8,39 @@ use crate::shared::types::ParkedPort; // TODO: (team), we want to continue support ws_port? No enum PortTypes { - Rpc, - P2P, - Prometheus, + Rpc, + P2P, + Prometheus, } pub fn generate(port: Option) -> Result { - let port = port.unwrap_or(0); - let listener = TcpListener::bind(format!("0.0.0.0:{port}")) - .map_err(|_e| GeneratorError::PortGeneration(port, "Can't bind".into()))?; - let port = listener - .local_addr() - .expect(&format!( - "We should always get the local_addr from the listener {THIS_IS_A_BUG}" - )) - .port(); - Ok(ParkedPort::new(port, listener)) + let port = port.unwrap_or(0); + let listener = TcpListener::bind(format!("0.0.0.0:{port}")) + .map_err(|_e| GeneratorError::PortGeneration(port, "Can't bind".into()))?; + let port = listener + .local_addr() + .expect(&format!("We should always get the local_addr from the listener {THIS_IS_A_BUG}")) + .port(); + Ok(ParkedPort::new(port, listener)) } #[cfg(test)] mod tests { - use super::*; - #[test] - fn generate_random() { - let port = generate(None).unwrap(); - let listener = port.1.write().unwrap(); + use super::*; + #[test] + fn generate_random() { + let port = generate(None).unwrap(); + let listener = port.1.write().unwrap(); - assert!(listener.is_some()); - } + assert!(listener.is_some()); + } - #[test] - fn generate_fixed_port() { - let port = generate(Some(33056)).unwrap(); - let listener = port.1.write().unwrap(); + #[test] + fn generate_fixed_port() { + let port = generate(Some(33056)).unwrap(); + let listener = port.1.write().unwrap(); - assert!(listener.is_some()); - assert_eq!(port.0, 33056); - } + assert!(listener.is_some()); + assert_eq!(port.0, 33056); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/lib.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/lib.rs index fa467c99..b64e53ff 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/lib.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/lib.rs @@ -13,11 +13,11 @@ mod spawner; mod utils; use std::{ - collections::{HashMap, HashSet, VecDeque}, - env, - net::IpAddr, - path::{Path, PathBuf}, - time::{Duration, SystemTime}, + collections::{HashMap, HashSet, VecDeque}, + env, + net::IpAddr, + path::{Path, PathBuf}, + time::{Duration, SystemTime}, }; use configuration::{NetworkConfig, RegistrationStrategy}; @@ -28,812 +28,764 @@ use network::{node::NetworkNode, relaychain::Relaychain, teyrchain::Parachain, N pub use network_spec::NetworkSpec; use network_spec::{node::NodeSpec, teyrchain::TeyrchainSpec}; use provider::{ - types::{ProviderCapabilities, TransferedFile}, - DynNamespace, DynProvider, + types::{ProviderCapabilities, TransferedFile}, + DynNamespace, DynProvider, }; use serde_json::json; use support::{ - constants::{ - GRAPH_CONTAINS_DEP, GRAPH_CONTAINS_NAME, INDEGREE_CONTAINS_NAME, QUEUE_NOT_EMPTY, - THIS_IS_A_BUG, - }, - fs::{FileSystem, FileSystemError}, - replacer::{get_tokens_to_replace, has_tokens}, + constants::{ + GRAPH_CONTAINS_DEP, GRAPH_CONTAINS_NAME, INDEGREE_CONTAINS_NAME, QUEUE_NOT_EMPTY, + THIS_IS_A_BUG, + }, + fs::{FileSystem, FileSystemError}, + replacer::{get_tokens_to_replace, has_tokens}, }; use tokio::time::timeout; use tracing::{debug, info, trace, warn}; use crate::{ - network::{node::RawNetworkNode, relaychain::RawRelaychain, teyrchain::RawParachain}, - shared::types::RegisterParachainOptions, - spawner::SpawnNodeCtx, + network::{node::RawNetworkNode, relaychain::RawRelaychain, teyrchain::RawParachain}, + shared::types::RegisterParachainOptions, + spawner::SpawnNodeCtx, }; pub struct Orchestrator where - T: FileSystem + Sync + Send, + T: FileSystem + Sync + Send, { - filesystem: T, - provider: DynProvider, + filesystem: T, + provider: DynProvider, } impl Orchestrator where - T: FileSystem + Sync + Send + Clone, + T: FileSystem + Sync + Send + Clone, { - pub fn new(filesystem: T, provider: DynProvider) -> Self { - Self { - filesystem, - provider, - } - } - - pub async fn spawn( - &self, - network_config: NetworkConfig, - ) -> Result, OrchestratorError> { - let global_timeout = network_config.global_settings().network_spawn_timeout(); - let network_spec = NetworkSpec::from_config(&network_config).await?; - - let res = timeout( - Duration::from_secs(global_timeout.into()), - self.spawn_inner(network_spec), - ) - .await - .map_err(|_| OrchestratorError::GlobalTimeOut(global_timeout)); - res? - } - - pub async fn spawn_from_spec( - &self, - network_spec: NetworkSpec, - ) -> Result, OrchestratorError> { - let global_timeout = network_spec.global_settings.network_spawn_timeout(); - let res = timeout( - Duration::from_secs(global_timeout as u64), - self.spawn_inner(network_spec), - ) - .await - .map_err(|_| OrchestratorError::GlobalTimeOut(global_timeout)); - res? - } - - pub async fn attach_to_live( - &self, - zombie_json_path: &Path, - ) -> Result, OrchestratorError> { - info!("attaching to live network..."); - info!("reading zombie.json from {:?}", zombie_json_path); - - let zombie_json_content = self.filesystem.read_to_string(zombie_json_path).await?; - let zombie_json: serde_json::Value = serde_json::from_str(&zombie_json_content)?; - - info!("recreating namespace..."); - let ns: DynNamespace = self - .provider - .create_namespace_from_json(&zombie_json) - .await?; - - info!("recreating relaychain..."); - let (relay, initial_spec) = - recreate_relaychain_from_json(&zombie_json, ns.clone(), self.provider.name()).await?; - let relay_nodes = relay.nodes.clone(); - - let mut network = - Network::new_with_relay(relay, ns.clone(), self.filesystem.clone(), initial_spec); - - for node in relay_nodes { - network.insert_node(node); - } - - info!("recreating parachains..."); - let parachains_map = - recreate_parachains_from_json(&zombie_json, ns.clone(), self.provider.name()).await?; - let para_nodes = parachains_map - .values() - .flat_map(|paras| paras.iter().flat_map(|para| para.collators.clone())) - .collect::>(); - - network.set_parachains(parachains_map); - for node in para_nodes { - network.insert_node(node); - } - - Ok(network) - } - - async fn spawn_inner( - &self, - mut network_spec: NetworkSpec, - ) -> Result, OrchestratorError> { - // main driver for spawn the network - debug!(network_spec = ?network_spec,"Network spec to spawn"); - - // TODO: move to Provider trait - validate_spec_with_provider_capabilities(&network_spec, self.provider.capabilities()) - .map_err(|err| { - OrchestratorError::InvalidConfigForProvider( - self.provider.name().into(), - err.to_string(), - ) - })?; - - // create namespace - let ns = if let Some(base_dir) = network_spec.global_settings.base_dir() { - self.provider - .create_namespace_with_base_dir(base_dir) - .await? - } else { - self.provider.create_namespace().await? - }; - - // set the spawn_concurrency - let (spawn_concurrency, limited_by_tokens) = calculate_concurrency(&network_spec)?; - - let start_time = SystemTime::now(); - info!("🧰 ns: {}", ns.name()); - info!("🧰 base_dir: {:?}", ns.base_dir()); - info!("🕰 start time: {:?}", start_time); - info!("⚙️ spawn concurrency: {spawn_concurrency} (limited by tokens: {limited_by_tokens})"); - - network_spec - .populate_nodes_available_args(ns.clone()) - .await?; - - let base_dir = ns.base_dir().to_string_lossy(); - let scoped_fs = ScopedFilesystem::new(&self.filesystem, &base_dir); - // Create chain-spec for relaychain - network_spec - .relaychain - .chain_spec - .build(&ns, &scoped_fs) - .await?; - - debug!("relaychain spec built!"); - // Create parachain artifacts (chain-spec, wasm, state) - let relay_chain_id = network_spec - .relaychain - .chain_spec - .read_chain_id(&scoped_fs) - .await?; - - let relay_chain_name = network_spec.relaychain.chain.as_str().to_owned(); - let base_dir_exists = network_spec.global_settings.base_dir().is_some(); - network_spec - .build_parachain_artifacts(ns.clone(), &scoped_fs, &relay_chain_id, base_dir_exists) - .await?; - - // Gather the parachains to register in genesis and the ones to register with extrinsic - let (para_to_register_in_genesis, para_to_register_with_extrinsic): ( - Vec<&TeyrchainSpec>, - Vec<&TeyrchainSpec>, - ) = network_spec - .parachains - .iter() - .filter(|para| para.registration_strategy != RegistrationStrategy::Manual) - .partition(|para| { - matches!(para.registration_strategy, RegistrationStrategy::InGenesis) - }); - - let mut para_artifacts = vec![]; - for para in para_to_register_in_genesis { - let genesis_config = para.get_genesis_config()?; - para_artifacts.push(genesis_config) - } - - // Customize relaychain - network_spec - .relaychain - .chain_spec - .customize_relay( - &network_spec.relaychain, - &network_spec.hrmp_channels, - para_artifacts, - &scoped_fs, - ) - .await?; - - // Build raw version - network_spec - .relaychain - .chain_spec - .build_raw(&ns, &scoped_fs, None) - .await?; - - // override wasm if needed - if let Some(ref wasm_override) = network_spec.relaychain.wasm_override { - network_spec - .relaychain - .chain_spec - .override_code(&scoped_fs, wasm_override) - .await?; - } - - // override raw spec if needed - if let Some(ref raw_spec_override) = network_spec.relaychain.raw_spec_override { - network_spec - .relaychain - .chain_spec - .override_raw_spec(&scoped_fs, raw_spec_override) - .await?; - } - - let (bootnodes, relaynodes) = - split_nodes_by_bootnodes(&network_spec.relaychain.nodes, false); - - // TODO: we want to still supporting spawn a dedicated bootnode?? - let mut ctx = SpawnNodeCtx { - chain_id: &relay_chain_id, - parachain_id: None, - chain: relay_chain_name.as_str(), - role: ZombieRole::Node, - ns: &ns, - scoped_fs: &scoped_fs, - parachain: None, - bootnodes_addr: &vec![], - wait_ready: false, - nodes_by_name: json!({}), - global_settings: &network_spec.global_settings, - }; - - let global_files_to_inject = vec![TransferedFile::new( - PathBuf::from(format!( - "{}/{relay_chain_name}.json", - ns.base_dir().to_string_lossy() - )), - PathBuf::from(format!("/cfg/{relay_chain_name}.json")), - )]; - - let r = Relaychain::new( - relay_chain_name.to_string(), - relay_chain_id.clone(), - PathBuf::from(network_spec.relaychain.chain_spec.raw_path().ok_or( - OrchestratorError::InvariantError("chain-spec raw path should be set now"), - )?), - ); - let mut network = - Network::new_with_relay(r, ns.clone(), self.filesystem.clone(), network_spec.clone()); - - // Initiate the node_ws_url which will be later used in the Parachain_with_extrinsic config - let mut node_ws_url: String = "".to_string(); - - // Calculate the bootnodes addr from the running nodes - let mut bootnodes_addr: Vec = vec![]; - - for level in dependency_levels_among(&bootnodes)? { - let mut running_nodes_per_level = vec![]; - for chunk in level.chunks(spawn_concurrency) { - let spawning_tasks = chunk - .iter() - .map(|node| spawner::spawn_node(node, global_files_to_inject.clone(), &ctx)); - - for node in futures::future::try_join_all(spawning_tasks).await? { - let bootnode_multiaddr = node.multiaddr(); - - bootnodes_addr.push(bootnode_multiaddr.to_string()); - - // Is used in the register_para_options (We need to get this from the relay and not the collators) - if node_ws_url.is_empty() { - node_ws_url.clone_from(&node.ws_uri) - } - - running_nodes_per_level.push(node); - } - } - info!( - "🕰 waiting for level: {:?} to be up...", - level.iter().map(|n| n.name.clone()).collect::>() - ); - - // Wait for all nodes in the current level to be up - let waiting_tasks = running_nodes_per_level.iter().map(|node| { - node.wait_until_is_up(network_spec.global_settings.network_spawn_timeout()) - }); - - let _ = futures::future::try_join_all(waiting_tasks).await?; - - for node in running_nodes_per_level { - // Add the node to the context and `Network` instance - ctx.nodes_by_name[node.name().to_owned()] = serde_json::to_value(&node)?; - network.add_running_node(node, None).await; - } - } - - // Add the bootnodes to the relaychain spec file and ctx - network_spec - .relaychain - .chain_spec - .add_bootnodes(&scoped_fs, &bootnodes_addr) - .await?; - - ctx.bootnodes_addr = &bootnodes_addr; - - for level in dependency_levels_among(&relaynodes)? { - let mut running_nodes_per_level = vec![]; - for chunk in level.chunks(spawn_concurrency) { - let spawning_tasks = chunk - .iter() - .map(|node| spawner::spawn_node(node, global_files_to_inject.clone(), &ctx)); - - for node in futures::future::try_join_all(spawning_tasks).await? { - running_nodes_per_level.push(node); - } - } - info!( - "🕰 waiting for level: {:?} to be up...", - level.iter().map(|n| n.name.clone()).collect::>() - ); - - // Wait for all nodes in the current level to be up - let waiting_tasks = running_nodes_per_level.iter().map(|node| { - node.wait_until_is_up(network_spec.global_settings.network_spawn_timeout()) - }); - - let _ = futures::future::try_join_all(waiting_tasks).await?; - - for node in running_nodes_per_level { - ctx.nodes_by_name[node.name().to_owned()] = serde_json::to_value(&node)?; - network.add_running_node(node, None).await; - } - } - - // spawn paras - for para in network_spec.parachains.iter() { - // Create parachain (in the context of the running network) - let parachain = Parachain::from_spec(para, &global_files_to_inject, &scoped_fs).await?; - let parachain_id = parachain.chain_id.clone(); - - let (bootnodes, collators) = - split_nodes_by_bootnodes(¶.collators, para.no_default_bootnodes); - - // Create `ctx` for spawn parachain nodes - let mut ctx_para = SpawnNodeCtx { - parachain: Some(para), - parachain_id: parachain_id.as_deref(), - role: if para.is_cumulus_based { - ZombieRole::CumulusCollator - } else { - ZombieRole::Collator - }, - bootnodes_addr: &vec![], - ..ctx.clone() - }; - - // Calculate the bootnodes addr from the running nodes - let mut bootnodes_addr: Vec = vec![]; - let mut running_nodes: Vec = vec![]; - - for level in dependency_levels_among(&bootnodes)? { - let mut running_nodes_per_level = vec![]; - for chunk in level.chunks(spawn_concurrency) { - let spawning_tasks = chunk.iter().map(|node| { - spawner::spawn_node(node, parachain.files_to_inject.clone(), &ctx_para) - }); - - for node in futures::future::try_join_all(spawning_tasks).await? { - let bootnode_multiaddr = node.multiaddr(); - - bootnodes_addr.push(bootnode_multiaddr.to_string()); - - running_nodes_per_level.push(node); - } - } - info!( - "🕰 waiting for level: {:?} to be up...", - level.iter().map(|n| n.name.clone()).collect::>() - ); - - // Wait for all nodes in the current level to be up - let waiting_tasks = running_nodes_per_level.iter().map(|node| { - node.wait_until_is_up(network_spec.global_settings.network_spawn_timeout()) - }); - - let _ = futures::future::try_join_all(waiting_tasks).await?; - - for node in running_nodes_per_level { - ctx_para.nodes_by_name[node.name().to_owned()] = serde_json::to_value(&node)?; - running_nodes.push(node); - } - } - - if let Some(para_chain_spec) = para.chain_spec.as_ref() { - para_chain_spec - .add_bootnodes(&scoped_fs, &bootnodes_addr) - .await?; - } - - ctx_para.bootnodes_addr = &bootnodes_addr; - - // Spawn the rest of the nodes - for level in dependency_levels_among(&collators)? { - let mut running_nodes_per_level = vec![]; - for chunk in level.chunks(spawn_concurrency) { - let spawning_tasks = chunk.iter().map(|node| { - spawner::spawn_node(node, parachain.files_to_inject.clone(), &ctx_para) - }); - - for node in futures::future::try_join_all(spawning_tasks).await? { - running_nodes_per_level.push(node); - } - } - info!( - "🕰 waiting for level: {:?} to be up...", - level.iter().map(|n| n.name.clone()).collect::>() - ); - - // Wait for all nodes in the current level to be up - let waiting_tasks = running_nodes_per_level.iter().map(|node| { - node.wait_until_is_up(network_spec.global_settings.network_spawn_timeout()) - }); - - let _ = futures::future::try_join_all(waiting_tasks).await?; - - for node in running_nodes_per_level { - ctx_para.nodes_by_name[node.name().to_owned()] = serde_json::to_value(&node)?; - running_nodes.push(node); - } - } - - let running_para_id = parachain.para_id; - network.add_para(parachain); - for node in running_nodes { - network.add_running_node(node, Some(running_para_id)).await; - } - } - - // TODO: - // - add-ons (introspector/tracing/etc) - - // verify nodes - // network_helper::verifier::verify_nodes(&network.nodes()).await?; - - // Now we need to register the paras with extrinsic from the Vec collected before; - for para in para_to_register_with_extrinsic { - let register_para_options: RegisterParachainOptions = RegisterParachainOptions { - id: para.id, - // This needs to resolve correctly - wasm_path: para - .genesis_wasm - .artifact_path() - .ok_or(OrchestratorError::InvariantError( - "artifact path for wasm must be set at this point", - ))? - .to_path_buf(), - state_path: para - .genesis_state - .artifact_path() - .ok_or(OrchestratorError::InvariantError( - "artifact path for state must be set at this point", - ))? - .to_path_buf(), - node_ws_url: node_ws_url.clone(), - onboard_as_para: para.onboard_as_parachain, - seed: None, // TODO: Seed is passed by? - finalization: false, - }; - - Parachain::register(register_para_options, &scoped_fs).await?; - } - - // - write zombie.json state file - let mut zombie_json = serde_json::to_value(&network)?; - zombie_json["local_base_dir"] = serde_json::value::Value::String(base_dir.to_string()); - zombie_json["ns"] = serde_json::value::Value::String(ns.name().to_string()); - - if let Ok(start_time_ts) = start_time.duration_since(SystemTime::UNIX_EPOCH) { - zombie_json["start_time_ts"] = - serde_json::value::Value::String(start_time_ts.as_millis().to_string()); - } else { - // Just warn, do not propagate the err (this should not happens) - warn!("⚠️ Error getting start_time timestamp"); - } - - scoped_fs - .write("zombie.json", serde_json::to_string_pretty(&zombie_json)?) - .await?; - - if network_spec.global_settings.tear_down_on_failure() { - network.spawn_watching_task(); - } - - Ok(network) - } + pub fn new(filesystem: T, provider: DynProvider) -> Self { + Self { filesystem, provider } + } + + pub async fn spawn( + &self, + network_config: NetworkConfig, + ) -> Result, OrchestratorError> { + let global_timeout = network_config.global_settings().network_spawn_timeout(); + let network_spec = NetworkSpec::from_config(&network_config).await?; + + let res = + timeout(Duration::from_secs(global_timeout.into()), self.spawn_inner(network_spec)) + .await + .map_err(|_| OrchestratorError::GlobalTimeOut(global_timeout)); + res? + } + + pub async fn spawn_from_spec( + &self, + network_spec: NetworkSpec, + ) -> Result, OrchestratorError> { + let global_timeout = network_spec.global_settings.network_spawn_timeout(); + let res = + timeout(Duration::from_secs(global_timeout as u64), self.spawn_inner(network_spec)) + .await + .map_err(|_| OrchestratorError::GlobalTimeOut(global_timeout)); + res? + } + + pub async fn attach_to_live( + &self, + zombie_json_path: &Path, + ) -> Result, OrchestratorError> { + info!("attaching to live network..."); + info!("reading zombie.json from {:?}", zombie_json_path); + + let zombie_json_content = self.filesystem.read_to_string(zombie_json_path).await?; + let zombie_json: serde_json::Value = serde_json::from_str(&zombie_json_content)?; + + info!("recreating namespace..."); + let ns: DynNamespace = self.provider.create_namespace_from_json(&zombie_json).await?; + + info!("recreating relaychain..."); + let (relay, initial_spec) = + recreate_relaychain_from_json(&zombie_json, ns.clone(), self.provider.name()).await?; + let relay_nodes = relay.nodes.clone(); + + let mut network = + Network::new_with_relay(relay, ns.clone(), self.filesystem.clone(), initial_spec); + + for node in relay_nodes { + network.insert_node(node); + } + + info!("recreating parachains..."); + let parachains_map = + recreate_parachains_from_json(&zombie_json, ns.clone(), self.provider.name()).await?; + let para_nodes = parachains_map + .values() + .flat_map(|paras| paras.iter().flat_map(|para| para.collators.clone())) + .collect::>(); + + network.set_parachains(parachains_map); + for node in para_nodes { + network.insert_node(node); + } + + Ok(network) + } + + async fn spawn_inner( + &self, + mut network_spec: NetworkSpec, + ) -> Result, OrchestratorError> { + // main driver for spawn the network + debug!(network_spec = ?network_spec,"Network spec to spawn"); + + // TODO: move to Provider trait + validate_spec_with_provider_capabilities(&network_spec, self.provider.capabilities()) + .map_err(|err| { + OrchestratorError::InvalidConfigForProvider( + self.provider.name().into(), + err.to_string(), + ) + })?; + + // create namespace + let ns = if let Some(base_dir) = network_spec.global_settings.base_dir() { + self.provider.create_namespace_with_base_dir(base_dir).await? + } else { + self.provider.create_namespace().await? + }; + + // set the spawn_concurrency + let (spawn_concurrency, limited_by_tokens) = calculate_concurrency(&network_spec)?; + + let start_time = SystemTime::now(); + info!("🧰 ns: {}", ns.name()); + info!("🧰 base_dir: {:?}", ns.base_dir()); + info!("🕰 start time: {:?}", start_time); + info!("⚙️ spawn concurrency: {spawn_concurrency} (limited by tokens: {limited_by_tokens})"); + + network_spec.populate_nodes_available_args(ns.clone()).await?; + + let base_dir = ns.base_dir().to_string_lossy(); + let scoped_fs = ScopedFilesystem::new(&self.filesystem, &base_dir); + // Create chain-spec for relaychain + network_spec.relaychain.chain_spec.build(&ns, &scoped_fs).await?; + + debug!("relaychain spec built!"); + // Create parachain artifacts (chain-spec, wasm, state) + let relay_chain_id = network_spec.relaychain.chain_spec.read_chain_id(&scoped_fs).await?; + + let relay_chain_name = network_spec.relaychain.chain.as_str().to_owned(); + let base_dir_exists = network_spec.global_settings.base_dir().is_some(); + network_spec + .build_parachain_artifacts(ns.clone(), &scoped_fs, &relay_chain_id, base_dir_exists) + .await?; + + // Gather the parachains to register in genesis and the ones to register with extrinsic + let (para_to_register_in_genesis, para_to_register_with_extrinsic): ( + Vec<&TeyrchainSpec>, + Vec<&TeyrchainSpec>, + ) = network_spec + .parachains + .iter() + .filter(|para| para.registration_strategy != RegistrationStrategy::Manual) + .partition(|para| { + matches!(para.registration_strategy, RegistrationStrategy::InGenesis) + }); + + let mut para_artifacts = vec![]; + for para in para_to_register_in_genesis { + let genesis_config = para.get_genesis_config()?; + para_artifacts.push(genesis_config) + } + + // Customize relaychain + network_spec + .relaychain + .chain_spec + .customize_relay( + &network_spec.relaychain, + &network_spec.hrmp_channels, + para_artifacts, + &scoped_fs, + ) + .await?; + + // Build raw version + network_spec.relaychain.chain_spec.build_raw(&ns, &scoped_fs, None).await?; + + // override wasm if needed + if let Some(ref wasm_override) = network_spec.relaychain.wasm_override { + network_spec.relaychain.chain_spec.override_code(&scoped_fs, wasm_override).await?; + } + + // override raw spec if needed + if let Some(ref raw_spec_override) = network_spec.relaychain.raw_spec_override { + network_spec + .relaychain + .chain_spec + .override_raw_spec(&scoped_fs, raw_spec_override) + .await?; + } + + let (bootnodes, relaynodes) = + split_nodes_by_bootnodes(&network_spec.relaychain.nodes, false); + + // TODO: we want to still supporting spawn a dedicated bootnode?? + let mut ctx = SpawnNodeCtx { + chain_id: &relay_chain_id, + parachain_id: None, + chain: relay_chain_name.as_str(), + role: ZombieRole::Node, + ns: &ns, + scoped_fs: &scoped_fs, + parachain: None, + bootnodes_addr: &vec![], + wait_ready: false, + nodes_by_name: json!({}), + global_settings: &network_spec.global_settings, + }; + + let global_files_to_inject = vec![TransferedFile::new( + PathBuf::from(format!("{}/{relay_chain_name}.json", ns.base_dir().to_string_lossy())), + PathBuf::from(format!("/cfg/{relay_chain_name}.json")), + )]; + + let r = Relaychain::new( + relay_chain_name.to_string(), + relay_chain_id.clone(), + PathBuf::from(network_spec.relaychain.chain_spec.raw_path().ok_or( + OrchestratorError::InvariantError("chain-spec raw path should be set now"), + )?), + ); + let mut network = + Network::new_with_relay(r, ns.clone(), self.filesystem.clone(), network_spec.clone()); + + // Initiate the node_ws_url which will be later used in the Parachain_with_extrinsic config + let mut node_ws_url: String = "".to_string(); + + // Calculate the bootnodes addr from the running nodes + let mut bootnodes_addr: Vec = vec![]; + + for level in dependency_levels_among(&bootnodes)? { + let mut running_nodes_per_level = vec![]; + for chunk in level.chunks(spawn_concurrency) { + let spawning_tasks = chunk + .iter() + .map(|node| spawner::spawn_node(node, global_files_to_inject.clone(), &ctx)); + + for node in futures::future::try_join_all(spawning_tasks).await? { + let bootnode_multiaddr = node.multiaddr(); + + bootnodes_addr.push(bootnode_multiaddr.to_string()); + + // Is used in the register_para_options (We need to get this from the relay and not the collators) + if node_ws_url.is_empty() { + node_ws_url.clone_from(&node.ws_uri) + } + + running_nodes_per_level.push(node); + } + } + info!( + "🕰 waiting for level: {:?} to be up...", + level.iter().map(|n| n.name.clone()).collect::>() + ); + + // Wait for all nodes in the current level to be up + let waiting_tasks = running_nodes_per_level.iter().map(|node| { + node.wait_until_is_up(network_spec.global_settings.network_spawn_timeout()) + }); + + let _ = futures::future::try_join_all(waiting_tasks).await?; + + for node in running_nodes_per_level { + // Add the node to the context and `Network` instance + ctx.nodes_by_name[node.name().to_owned()] = serde_json::to_value(&node)?; + network.add_running_node(node, None).await; + } + } + + // Add the bootnodes to the relaychain spec file and ctx + network_spec.relaychain.chain_spec.add_bootnodes(&scoped_fs, &bootnodes_addr).await?; + + ctx.bootnodes_addr = &bootnodes_addr; + + for level in dependency_levels_among(&relaynodes)? { + let mut running_nodes_per_level = vec![]; + for chunk in level.chunks(spawn_concurrency) { + let spawning_tasks = chunk + .iter() + .map(|node| spawner::spawn_node(node, global_files_to_inject.clone(), &ctx)); + + for node in futures::future::try_join_all(spawning_tasks).await? { + running_nodes_per_level.push(node); + } + } + info!( + "🕰 waiting for level: {:?} to be up...", + level.iter().map(|n| n.name.clone()).collect::>() + ); + + // Wait for all nodes in the current level to be up + let waiting_tasks = running_nodes_per_level.iter().map(|node| { + node.wait_until_is_up(network_spec.global_settings.network_spawn_timeout()) + }); + + let _ = futures::future::try_join_all(waiting_tasks).await?; + + for node in running_nodes_per_level { + ctx.nodes_by_name[node.name().to_owned()] = serde_json::to_value(&node)?; + network.add_running_node(node, None).await; + } + } + + // spawn paras + for para in network_spec.parachains.iter() { + // Create parachain (in the context of the running network) + let parachain = Parachain::from_spec(para, &global_files_to_inject, &scoped_fs).await?; + let parachain_id = parachain.chain_id.clone(); + + let (bootnodes, collators) = + split_nodes_by_bootnodes(¶.collators, para.no_default_bootnodes); + + // Create `ctx` for spawn parachain nodes + let mut ctx_para = SpawnNodeCtx { + parachain: Some(para), + parachain_id: parachain_id.as_deref(), + role: if para.is_cumulus_based { + ZombieRole::CumulusCollator + } else { + ZombieRole::Collator + }, + bootnodes_addr: &vec![], + ..ctx.clone() + }; + + // Calculate the bootnodes addr from the running nodes + let mut bootnodes_addr: Vec = vec![]; + let mut running_nodes: Vec = vec![]; + + for level in dependency_levels_among(&bootnodes)? { + let mut running_nodes_per_level = vec![]; + for chunk in level.chunks(spawn_concurrency) { + let spawning_tasks = chunk.iter().map(|node| { + spawner::spawn_node(node, parachain.files_to_inject.clone(), &ctx_para) + }); + + for node in futures::future::try_join_all(spawning_tasks).await? { + let bootnode_multiaddr = node.multiaddr(); + + bootnodes_addr.push(bootnode_multiaddr.to_string()); + + running_nodes_per_level.push(node); + } + } + info!( + "🕰 waiting for level: {:?} to be up...", + level.iter().map(|n| n.name.clone()).collect::>() + ); + + // Wait for all nodes in the current level to be up + let waiting_tasks = running_nodes_per_level.iter().map(|node| { + node.wait_until_is_up(network_spec.global_settings.network_spawn_timeout()) + }); + + let _ = futures::future::try_join_all(waiting_tasks).await?; + + for node in running_nodes_per_level { + ctx_para.nodes_by_name[node.name().to_owned()] = serde_json::to_value(&node)?; + running_nodes.push(node); + } + } + + if let Some(para_chain_spec) = para.chain_spec.as_ref() { + para_chain_spec.add_bootnodes(&scoped_fs, &bootnodes_addr).await?; + } + + ctx_para.bootnodes_addr = &bootnodes_addr; + + // Spawn the rest of the nodes + for level in dependency_levels_among(&collators)? { + let mut running_nodes_per_level = vec![]; + for chunk in level.chunks(spawn_concurrency) { + let spawning_tasks = chunk.iter().map(|node| { + spawner::spawn_node(node, parachain.files_to_inject.clone(), &ctx_para) + }); + + for node in futures::future::try_join_all(spawning_tasks).await? { + running_nodes_per_level.push(node); + } + } + info!( + "🕰 waiting for level: {:?} to be up...", + level.iter().map(|n| n.name.clone()).collect::>() + ); + + // Wait for all nodes in the current level to be up + let waiting_tasks = running_nodes_per_level.iter().map(|node| { + node.wait_until_is_up(network_spec.global_settings.network_spawn_timeout()) + }); + + let _ = futures::future::try_join_all(waiting_tasks).await?; + + for node in running_nodes_per_level { + ctx_para.nodes_by_name[node.name().to_owned()] = serde_json::to_value(&node)?; + running_nodes.push(node); + } + } + + let running_para_id = parachain.para_id; + network.add_para(parachain); + for node in running_nodes { + network.add_running_node(node, Some(running_para_id)).await; + } + } + + // TODO: + // - add-ons (introspector/tracing/etc) + + // verify nodes + // network_helper::verifier::verify_nodes(&network.nodes()).await?; + + // Now we need to register the paras with extrinsic from the Vec collected before; + for para in para_to_register_with_extrinsic { + let register_para_options: RegisterParachainOptions = RegisterParachainOptions { + id: para.id, + // This needs to resolve correctly + wasm_path: para + .genesis_wasm + .artifact_path() + .ok_or(OrchestratorError::InvariantError( + "artifact path for wasm must be set at this point", + ))? + .to_path_buf(), + state_path: para + .genesis_state + .artifact_path() + .ok_or(OrchestratorError::InvariantError( + "artifact path for state must be set at this point", + ))? + .to_path_buf(), + node_ws_url: node_ws_url.clone(), + onboard_as_para: para.onboard_as_parachain, + seed: None, // TODO: Seed is passed by? + finalization: false, + }; + + Parachain::register(register_para_options, &scoped_fs).await?; + } + + // - write zombie.json state file + let mut zombie_json = serde_json::to_value(&network)?; + zombie_json["local_base_dir"] = serde_json::value::Value::String(base_dir.to_string()); + zombie_json["ns"] = serde_json::value::Value::String(ns.name().to_string()); + + if let Ok(start_time_ts) = start_time.duration_since(SystemTime::UNIX_EPOCH) { + zombie_json["start_time_ts"] = + serde_json::value::Value::String(start_time_ts.as_millis().to_string()); + } else { + // Just warn, do not propagate the err (this should not happens) + warn!("⚠️ Error getting start_time timestamp"); + } + + scoped_fs.write("zombie.json", serde_json::to_string_pretty(&zombie_json)?).await?; + + if network_spec.global_settings.tear_down_on_failure() { + network.spawn_watching_task(); + } + + Ok(network) + } } // Helpers async fn recreate_network_nodes_from_json( - nodes_json: &serde_json::Value, - ns: DynNamespace, - provider_name: &str, + nodes_json: &serde_json::Value, + ns: DynNamespace, + provider_name: &str, ) -> Result, OrchestratorError> { - let raw_nodes: Vec = serde_json::from_value(nodes_json.clone())?; + let raw_nodes: Vec = serde_json::from_value(nodes_json.clone())?; - let mut nodes = Vec::with_capacity(raw_nodes.len()); - for raw in raw_nodes { - // validate provider tag - let provider_tag = raw - .inner - .get("provider_tag") - .and_then(|v| v.as_str()) - .ok_or_else(|| { - OrchestratorError::InvalidConfig("Missing `provider_tag` in inner node JSON".into()) - })?; + let mut nodes = Vec::with_capacity(raw_nodes.len()); + for raw in raw_nodes { + // validate provider tag + let provider_tag = + raw.inner.get("provider_tag").and_then(|v| v.as_str()).ok_or_else(|| { + OrchestratorError::InvalidConfig("Missing `provider_tag` in inner node JSON".into()) + })?; - if provider_tag != provider_name { - return Err(OrchestratorError::InvalidConfigForProvider( - provider_name.to_string(), - provider_tag.to_string(), - )); - } - let inner = ns.spawn_node_from_json(&raw.inner).await?; - let relay_node = NetworkNode::new( - raw.name, - raw.ws_uri, - raw.prometheus_uri, - raw.multiaddr, - raw.spec, - inner, - ); - nodes.push(relay_node); - } + if provider_tag != provider_name { + return Err(OrchestratorError::InvalidConfigForProvider( + provider_name.to_string(), + provider_tag.to_string(), + )); + } + let inner = ns.spawn_node_from_json(&raw.inner).await?; + let relay_node = NetworkNode::new( + raw.name, + raw.ws_uri, + raw.prometheus_uri, + raw.multiaddr, + raw.spec, + inner, + ); + nodes.push(relay_node); + } - Ok(nodes) + Ok(nodes) } async fn recreate_relaychain_from_json( - zombie_json: &serde_json::Value, - ns: DynNamespace, - provider_name: &str, + zombie_json: &serde_json::Value, + ns: DynNamespace, + provider_name: &str, ) -> Result<(Relaychain, NetworkSpec), OrchestratorError> { - let relay_json = zombie_json - .get("relay") - .ok_or(OrchestratorError::InvalidConfig( - "Missing `relay` field in zombie.json".into(), - ))? - .clone(); + let relay_json = zombie_json + .get("relay") + .ok_or(OrchestratorError::InvalidConfig("Missing `relay` field in zombie.json".into()))? + .clone(); - let mut relay_raw: RawRelaychain = serde_json::from_value(relay_json)?; + let mut relay_raw: RawRelaychain = serde_json::from_value(relay_json)?; - let initial_spec: NetworkSpec = serde_json::from_value( - zombie_json - .get("initial_spec") - .ok_or(OrchestratorError::InvalidConfig( - "Missing `initial_spec` field in zombie.json".into(), - ))? - .clone(), - )?; + let initial_spec: NetworkSpec = serde_json::from_value( + zombie_json + .get("initial_spec") + .ok_or(OrchestratorError::InvalidConfig( + "Missing `initial_spec` field in zombie.json".into(), + ))? + .clone(), + )?; - // Populate relay nodes - let nodes = - recreate_network_nodes_from_json(&relay_raw.nodes, ns.clone(), provider_name).await?; - relay_raw.inner.nodes = nodes; + // Populate relay nodes + let nodes = + recreate_network_nodes_from_json(&relay_raw.nodes, ns.clone(), provider_name).await?; + relay_raw.inner.nodes = nodes; - Ok((relay_raw.inner, initial_spec)) + Ok((relay_raw.inner, initial_spec)) } async fn recreate_parachains_from_json( - zombie_json: &serde_json::Value, - ns: DynNamespace, - provider_name: &str, + zombie_json: &serde_json::Value, + ns: DynNamespace, + provider_name: &str, ) -> Result>, OrchestratorError> { - let paras_json = zombie_json - .get("parachains") - .ok_or(OrchestratorError::InvalidConfig( - "Missing `parachains` field in zombie.json".into(), - ))? - .clone(); + let paras_json = zombie_json + .get("parachains") + .ok_or(OrchestratorError::InvalidConfig( + "Missing `parachains` field in zombie.json".into(), + ))? + .clone(); - let raw_paras: HashMap> = serde_json::from_value(paras_json)?; + let raw_paras: HashMap> = serde_json::from_value(paras_json)?; - let mut parachains_map = HashMap::new(); + let mut parachains_map = HashMap::new(); - for (id, parachain_entries) in raw_paras { - let mut parsed_vec = Vec::with_capacity(parachain_entries.len()); + for (id, parachain_entries) in raw_paras { + let mut parsed_vec = Vec::with_capacity(parachain_entries.len()); - for raw_para in parachain_entries { - let mut para = raw_para.inner; - para.collators = - recreate_network_nodes_from_json(&raw_para.collators, ns.clone(), provider_name) - .await?; - parsed_vec.push(para); - } + for raw_para in parachain_entries { + let mut para = raw_para.inner; + para.collators = + recreate_network_nodes_from_json(&raw_para.collators, ns.clone(), provider_name) + .await?; + parsed_vec.push(para); + } - parachains_map.insert(id, parsed_vec); - } + parachains_map.insert(id, parsed_vec); + } - Ok(parachains_map) + Ok(parachains_map) } // Split the node list depending if it's bootnode or not // NOTE: if there isn't a bootnode declared we use the first one fn split_nodes_by_bootnodes( - nodes: &[NodeSpec], - no_default_bootnodes: bool, + nodes: &[NodeSpec], + no_default_bootnodes: bool, ) -> (Vec<&NodeSpec>, Vec<&NodeSpec>) { - // get the bootnodes to spawn first and calculate the bootnode string for use later - let mut bootnodes = vec![]; - let mut other_nodes = vec![]; - nodes.iter().for_each(|node| { - if node.is_bootnode { - bootnodes.push(node) - } else { - other_nodes.push(node) - } - }); + // get the bootnodes to spawn first and calculate the bootnode string for use later + let mut bootnodes = vec![]; + let mut other_nodes = vec![]; + nodes.iter().for_each(|node| { + if node.is_bootnode { + bootnodes.push(node) + } else { + other_nodes.push(node) + } + }); - if bootnodes.is_empty() && !no_default_bootnodes { - bootnodes.push(other_nodes.remove(0)) - } + if bootnodes.is_empty() && !no_default_bootnodes { + bootnodes.push(other_nodes.remove(0)) + } - (bootnodes, other_nodes) + (bootnodes, other_nodes) } // Generate a bootnode multiaddress and return as string fn generate_bootnode_addr( - node: &NetworkNode, - ip: &IpAddr, - port: u16, + node: &NetworkNode, + ip: &IpAddr, + port: u16, ) -> Result { - generators::generate_node_bootnode_addr( - &node.spec.peer_id, - ip, - port, - node.inner.args().as_ref(), - &node.spec.p2p_cert_hash, - ) + generators::generate_node_bootnode_addr( + &node.spec.peer_id, + ip, + port, + node.inner.args().as_ref(), + &node.spec.p2p_cert_hash, + ) } // Validate that the config fulfill all the requirements of the provider fn validate_spec_with_provider_capabilities( - network_spec: &NetworkSpec, - capabilities: &ProviderCapabilities, + network_spec: &NetworkSpec, + capabilities: &ProviderCapabilities, ) -> Result<(), anyhow::Error> { - let mut errs: Vec = vec![]; + let mut errs: Vec = vec![]; - if capabilities.requires_image { - // Relaychain - if network_spec.relaychain.default_image.is_none() { - // we should check if each node have an image - let nodes = &network_spec.relaychain.nodes; - if nodes.iter().any(|node| node.image.is_none()) { - errs.push(String::from( - "Missing image for node, and not default is set at relaychain", - )); - } - }; + if capabilities.requires_image { + // Relaychain + if network_spec.relaychain.default_image.is_none() { + // we should check if each node have an image + let nodes = &network_spec.relaychain.nodes; + if nodes.iter().any(|node| node.image.is_none()) { + errs.push(String::from( + "Missing image for node, and not default is set at relaychain", + )); + } + }; - // Paras - for para in &network_spec.parachains { - if para.default_image.is_none() { - let nodes = ¶.collators; - if nodes.iter().any(|node| node.image.is_none()) { - errs.push(format!( - "Missing image for node, and not default is set at parachain {}", - para.id - )); - } - } - } - } else { - // native - // We need to get all the `cmds` and verify if are part of the path - let mut cmds: HashSet<&str> = Default::default(); - if let Some(cmd) = network_spec.relaychain.default_command.as_ref() { - cmds.insert(cmd.as_str()); - } - for node in network_spec.relaychain().nodes.iter() { - cmds.insert(node.command()); - } + // Paras + for para in &network_spec.parachains { + if para.default_image.is_none() { + let nodes = ¶.collators; + if nodes.iter().any(|node| node.image.is_none()) { + errs.push(format!( + "Missing image for node, and not default is set at parachain {}", + para.id + )); + } + } + } + } else { + // native + // We need to get all the `cmds` and verify if are part of the path + let mut cmds: HashSet<&str> = Default::default(); + if let Some(cmd) = network_spec.relaychain.default_command.as_ref() { + cmds.insert(cmd.as_str()); + } + for node in network_spec.relaychain().nodes.iter() { + cmds.insert(node.command()); + } - // Paras - for para in &network_spec.parachains { - if let Some(cmd) = para.default_command.as_ref() { - cmds.insert(cmd.as_str()); - } + // Paras + for para in &network_spec.parachains { + if let Some(cmd) = para.default_command.as_ref() { + cmds.insert(cmd.as_str()); + } - for node in para.collators.iter() { - cmds.insert(node.command()); - } - } + for node in para.collators.iter() { + cmds.insert(node.command()); + } + } - // now check the binaries - let path = std::env::var("PATH").unwrap_or_default(); // path should always be set - trace!("current PATH: {path}"); - let parts: Vec<_> = path.split(":").collect(); - for cmd in cmds { - let missing = if cmd.contains('/') { - trace!("checking {cmd}"); - if std::fs::metadata(cmd).is_err() { - true - } else { - info!("🔎 We will use the full path {cmd} to spawn nodes."); - false - } - } else { - // should be in the PATH - !parts.iter().any(|part| { - let path_to = format!("{part}/{cmd}"); - trace!("checking {path_to}"); - let check_result = std::fs::metadata(&path_to); - trace!("result {:?}", check_result); - if check_result.is_ok() { - info!("🔎 We will use the cmd: '{cmd}' at path {path_to} to spawn nodes."); - true - } else { - false - } - }) - }; + // now check the binaries + let path = std::env::var("PATH").unwrap_or_default(); // path should always be set + trace!("current PATH: {path}"); + let parts: Vec<_> = path.split(":").collect(); + for cmd in cmds { + let missing = if cmd.contains('/') { + trace!("checking {cmd}"); + if std::fs::metadata(cmd).is_err() { + true + } else { + info!("🔎 We will use the full path {cmd} to spawn nodes."); + false + } + } else { + // should be in the PATH + !parts.iter().any(|part| { + let path_to = format!("{part}/{cmd}"); + trace!("checking {path_to}"); + let check_result = std::fs::metadata(&path_to); + trace!("result {:?}", check_result); + if check_result.is_ok() { + info!("🔎 We will use the cmd: '{cmd}' at path {path_to} to spawn nodes."); + true + } else { + false + } + }) + }; - if missing { - errs.push(help_msg(cmd)); - } - } - } + if missing { + errs.push(help_msg(cmd)); + } + } + } - if !errs.is_empty() { - let msg = errs.join("\n"); - return Err(anyhow::anyhow!(format!("Invalid configuration: \n {msg}"))); - } + if !errs.is_empty() { + let msg = errs.join("\n"); + return Err(anyhow::anyhow!(format!("Invalid configuration: \n {msg}"))); + } - Ok(()) + Ok(()) } fn help_msg(cmd: &str) -> String { - match cmd { - "parachain-template-node" | "solochain-template-node" | "minimal-template-node" => { - format!("Missing binary {cmd}, compile by running: \n\tcargo build --package {cmd} --release") - }, - "polkadot" => { - format!("Missing binary {cmd}, compile by running (in the polkadot-sdk repo): \n\t cargo build --locked --release --features fast-runtime --bin {cmd} --bin polkadot-prepare-worker --bin polkadot-execute-worker") - }, - "polkadot-parachain" => { - format!("Missing binary {cmd}, compile by running (in the polkadot-sdk repo): \n\t cargo build --release --locked -p {cmd}-bin --bin {cmd}") - }, - _ => { - format!("Missing binary {cmd}, please compile it.") - }, - } + match cmd { + "parachain-template-node" | "solochain-template-node" | "minimal-template-node" => { + format!("Missing binary {cmd}, compile by running: \n\tcargo build --package {cmd} --release") + }, + "polkadot" => { + format!("Missing binary {cmd}, compile by running (in the polkadot-sdk repo): \n\t cargo build --locked --release --features fast-runtime --bin {cmd} --bin polkadot-prepare-worker --bin polkadot-execute-worker") + }, + "polkadot-parachain" => { + format!("Missing binary {cmd}, compile by running (in the polkadot-sdk repo): \n\t cargo build --release --locked -p {cmd}-bin --bin {cmd}") + }, + _ => { + format!("Missing binary {cmd}, please compile it.") + }, + } } /// Allow to set the default concurrency through env var `ZOMBIE_SPAWN_CONCURRENCY` fn spawn_concurrency_from_env() -> Option { - if let Ok(concurrency) = env::var("ZOMBIE_SPAWN_CONCURRENCY") { - concurrency.parse::().ok() - } else { - None - } + if let Ok(concurrency) = env::var("ZOMBIE_SPAWN_CONCURRENCY") { + concurrency.parse::().ok() + } else { + None + } } fn calculate_concurrency(spec: &NetworkSpec) -> Result<(usize, bool), anyhow::Error> { - let desired_spawn_concurrency = match ( - spawn_concurrency_from_env(), - spec.global_settings.spawn_concurrency(), - ) { - (Some(n), _) => Some(n), - (None, Some(n)) => Some(n), - _ => None, - }; + let desired_spawn_concurrency = + match (spawn_concurrency_from_env(), spec.global_settings.spawn_concurrency()) { + (Some(n), _) => Some(n), + (None, Some(n)) => Some(n), + _ => None, + }; - let (spawn_concurrency, limited_by_tokens) = - if let Some(spawn_concurrency) = desired_spawn_concurrency { - if spawn_concurrency == 1 { - (1, false) - } else if has_tokens(&serde_json::to_string(spec)?) { - (1, true) - } else { - (spawn_concurrency, false) - } - } else { - // not set - if has_tokens(&serde_json::to_string(spec)?) { - (1, true) - } else { - // use 100 as max concurrency, we can set a max by provider later - (100, false) - } - }; + let (spawn_concurrency, limited_by_tokens) = + if let Some(spawn_concurrency) = desired_spawn_concurrency { + if spawn_concurrency == 1 { + (1, false) + } else if has_tokens(&serde_json::to_string(spec)?) { + (1, true) + } else { + (spawn_concurrency, false) + } + } else { + // not set + if has_tokens(&serde_json::to_string(spec)?) { + (1, true) + } else { + // use 100 as max concurrency, we can set a max by provider later + (100, false) + } + }; - Ok((spawn_concurrency, limited_by_tokens)) + Ok((spawn_concurrency, limited_by_tokens)) } /// Build deterministic dependency **levels** among the given nodes. @@ -841,98 +793,92 @@ fn calculate_concurrency(spec: &NetworkSpec) -> Result<(usize, bool), anyhow::Er /// - Unknown/out-of-scope references are ignored. /// - Self-dependencies are ignored. fn dependency_levels_among<'a>( - nodes: &'a [&'a NodeSpec], + nodes: &'a [&'a NodeSpec], ) -> Result>, OrchestratorError> { - let by_name = nodes - .iter() - .map(|n| (n.name.as_str(), *n)) - .collect::>(); + let by_name = nodes.iter().map(|n| (n.name.as_str(), *n)).collect::>(); - let mut graph = HashMap::with_capacity(nodes.len()); - let mut indegree = HashMap::with_capacity(nodes.len()); + let mut graph = HashMap::with_capacity(nodes.len()); + let mut indegree = HashMap::with_capacity(nodes.len()); - for node in nodes { - graph.insert(node.name.as_str(), Vec::new()); - indegree.insert(node.name.as_str(), 0); - } + for node in nodes { + graph.insert(node.name.as_str(), Vec::new()); + indegree.insert(node.name.as_str(), 0); + } - // build dependency graph - for &node in nodes { - if let Ok(args_json) = serde_json::to_string(&node.args) { - // collect dependencies - let unique_deps = get_tokens_to_replace(&args_json) - .into_iter() - .filter(|dep| dep != &node.name) - .filter_map(|dep| by_name.get(dep.as_str())) - .map(|&dep_node| dep_node.name.as_str()) - .collect::>(); + // build dependency graph + for &node in nodes { + if let Ok(args_json) = serde_json::to_string(&node.args) { + // collect dependencies + let unique_deps = get_tokens_to_replace(&args_json) + .into_iter() + .filter(|dep| dep != &node.name) + .filter_map(|dep| by_name.get(dep.as_str())) + .map(|&dep_node| dep_node.name.as_str()) + .collect::>(); - for dep_name in unique_deps { - graph - .get_mut(dep_name) - .expect(&format!("{GRAPH_CONTAINS_DEP} {THIS_IS_A_BUG}")) - .push(node); - *indegree - .get_mut(node.name.as_str()) - .expect(&format!("{INDEGREE_CONTAINS_NAME} {THIS_IS_A_BUG}")) += 1; - } - } - } + for dep_name in unique_deps { + graph + .get_mut(dep_name) + .expect(&format!("{GRAPH_CONTAINS_DEP} {THIS_IS_A_BUG}")) + .push(node); + *indegree + .get_mut(node.name.as_str()) + .expect(&format!("{INDEGREE_CONTAINS_NAME} {THIS_IS_A_BUG}")) += 1; + } + } + } - // find all nodes with no dependencies - let mut queue = nodes - .iter() - .filter(|n| { - *indegree - .get(n.name.as_str()) - .expect(&format!("{INDEGREE_CONTAINS_NAME} {THIS_IS_A_BUG}")) - == 0 - }) - .copied() - .collect::>(); + // find all nodes with no dependencies + let mut queue = nodes + .iter() + .filter(|n| { + *indegree + .get(n.name.as_str()) + .expect(&format!("{INDEGREE_CONTAINS_NAME} {THIS_IS_A_BUG}")) + == 0 + }) + .copied() + .collect::>(); - let mut processed_count = 0; - let mut levels = Vec::new(); + let mut processed_count = 0; + let mut levels = Vec::new(); - // Kahn's algorithm - while !queue.is_empty() { - let level_size = queue.len(); - let mut current_level = Vec::with_capacity(level_size); + // Kahn's algorithm + while !queue.is_empty() { + let level_size = queue.len(); + let mut current_level = Vec::with_capacity(level_size); - for _ in 0..level_size { - let n = queue - .pop_front() - .expect(&format!("{QUEUE_NOT_EMPTY} {THIS_IS_A_BUG}")); - current_level.push(n); - processed_count += 1; + for _ in 0..level_size { + let n = queue.pop_front().expect(&format!("{QUEUE_NOT_EMPTY} {THIS_IS_A_BUG}")); + current_level.push(n); + processed_count += 1; - for &neighbour in graph - .get(n.name.as_str()) - .expect(&format!("{GRAPH_CONTAINS_NAME} {THIS_IS_A_BUG}")) - { - let neighbour_indegree = indegree - .get_mut(neighbour.name.as_str()) - .expect(&format!("{INDEGREE_CONTAINS_NAME} {THIS_IS_A_BUG}")); - *neighbour_indegree -= 1; + for &neighbour in + graph.get(n.name.as_str()).expect(&format!("{GRAPH_CONTAINS_NAME} {THIS_IS_A_BUG}")) + { + let neighbour_indegree = indegree + .get_mut(neighbour.name.as_str()) + .expect(&format!("{INDEGREE_CONTAINS_NAME} {THIS_IS_A_BUG}")); + *neighbour_indegree -= 1; - if *neighbour_indegree == 0 { - queue.push_back(neighbour); - } - } - } + if *neighbour_indegree == 0 { + queue.push_back(neighbour); + } + } + } - current_level.sort_by_key(|n| &n.name); - levels.push(current_level); - } + current_level.sort_by_key(|n| &n.name); + levels.push(current_level); + } - // cycles detected, e.g A -> B -> A - if processed_count != nodes.len() { - return Err(OrchestratorError::InvalidConfig( - "Tokens have cyclical dependencies".to_string(), - )); - } + // cycles detected, e.g A -> B -> A + if processed_count != nodes.len() { + return Err(OrchestratorError::InvalidConfig( + "Tokens have cyclical dependencies".to_string(), + )); + } - Ok(levels) + Ok(levels) } // TODO: get the fs from `DynNamespace` will make this not needed @@ -944,110 +890,97 @@ fn dependency_levels_among<'a>( // directly to the support crate, it can be useful in the future #[derive(Clone, Debug)] pub struct ScopedFilesystem<'a, FS: FileSystem> { - fs: &'a FS, - base_dir: &'a str, + fs: &'a FS, + base_dir: &'a str, } impl<'a, FS: FileSystem> ScopedFilesystem<'a, FS> { - pub fn new(fs: &'a FS, base_dir: &'a str) -> Self { - Self { fs, base_dir } - } + pub fn new(fs: &'a FS, base_dir: &'a str) -> Self { + Self { fs, base_dir } + } - async fn copy_files(&self, files: Vec<&TransferedFile>) -> Result<(), FileSystemError> { - for file in files { - let full_remote_path = PathBuf::from(format!( - "{}/{}", - self.base_dir, - file.remote_path.to_string_lossy() - )); - trace!("coping file: {file}"); - self.fs - .copy(file.local_path.as_path(), full_remote_path) - .await?; - } - Ok(()) - } + async fn copy_files(&self, files: Vec<&TransferedFile>) -> Result<(), FileSystemError> { + for file in files { + let full_remote_path = + PathBuf::from(format!("{}/{}", self.base_dir, file.remote_path.to_string_lossy())); + trace!("coping file: {file}"); + self.fs.copy(file.local_path.as_path(), full_remote_path).await?; + } + Ok(()) + } - async fn read(&self, file: impl AsRef) -> Result, FileSystemError> { - let file = file.as_ref(); + async fn read(&self, file: impl AsRef) -> Result, FileSystemError> { + let file = file.as_ref(); - let full_path = if file.is_absolute() { - file.to_owned() - } else { - PathBuf::from(format!("{}/{}", self.base_dir, file.to_string_lossy())) - }; - let content = self.fs.read(full_path).await?; - Ok(content) - } + let full_path = if file.is_absolute() { + file.to_owned() + } else { + PathBuf::from(format!("{}/{}", self.base_dir, file.to_string_lossy())) + }; + let content = self.fs.read(full_path).await?; + Ok(content) + } - async fn read_to_string(&self, file: impl AsRef) -> Result { - let file = file.as_ref(); + async fn read_to_string(&self, file: impl AsRef) -> Result { + let file = file.as_ref(); - let full_path = if file.is_absolute() { - file.to_owned() - } else { - PathBuf::from(format!("{}/{}", self.base_dir, file.to_string_lossy())) - }; - let content = self.fs.read_to_string(full_path).await?; - Ok(content) - } + let full_path = if file.is_absolute() { + file.to_owned() + } else { + PathBuf::from(format!("{}/{}", self.base_dir, file.to_string_lossy())) + }; + let content = self.fs.read_to_string(full_path).await?; + Ok(content) + } - async fn create_dir(&self, path: impl AsRef) -> Result<(), FileSystemError> { - let path = PathBuf::from(format!( - "{}/{}", - self.base_dir, - path.as_ref().to_string_lossy() - )); - self.fs.create_dir(path).await - } + async fn create_dir(&self, path: impl AsRef) -> Result<(), FileSystemError> { + let path = PathBuf::from(format!("{}/{}", self.base_dir, path.as_ref().to_string_lossy())); + self.fs.create_dir(path).await + } - async fn create_dir_all(&self, path: impl AsRef) -> Result<(), FileSystemError> { - let path = PathBuf::from(format!( - "{}/{}", - self.base_dir, - path.as_ref().to_string_lossy() - )); - self.fs.create_dir_all(path).await - } + async fn create_dir_all(&self, path: impl AsRef) -> Result<(), FileSystemError> { + let path = PathBuf::from(format!("{}/{}", self.base_dir, path.as_ref().to_string_lossy())); + self.fs.create_dir_all(path).await + } - async fn write( - &self, - path: impl AsRef, - contents: impl AsRef<[u8]> + Send, - ) -> Result<(), FileSystemError> { - let path = path.as_ref(); + async fn write( + &self, + path: impl AsRef, + contents: impl AsRef<[u8]> + Send, + ) -> Result<(), FileSystemError> { + let path = path.as_ref(); - let full_path = if path.is_absolute() { - path.to_owned() - } else { - PathBuf::from(format!("{}/{}", self.base_dir, path.to_string_lossy())) - }; + let full_path = if path.is_absolute() { + path.to_owned() + } else { + PathBuf::from(format!("{}/{}", self.base_dir, path.to_string_lossy())) + }; - self.fs.write(full_path, contents).await - } + self.fs.write(full_path, contents).await + } - /// Get the full_path in the scoped FS - fn full_path(&self, path: impl AsRef) -> PathBuf { - let path = path.as_ref(); + /// Get the full_path in the scoped FS + fn full_path(&self, path: impl AsRef) -> PathBuf { + let path = path.as_ref(); - let full_path = if path.is_absolute() { - path.to_owned() - } else { - PathBuf::from(format!("{}/{}", self.base_dir, path.to_string_lossy())) - }; + let full_path = if path.is_absolute() { + path.to_owned() + } else { + PathBuf::from(format!("{}/{}", self.base_dir, path.to_string_lossy())) + }; - full_path - } + full_path + } } #[derive(Clone, Debug)] pub enum ZombieRole { - Temp, - Node, - Bootnode, - Collator, - CumulusCollator, - Companion, + Temp, + Node, + Bootnode, + Collator, + CumulusCollator, + Companion, } // re-exports @@ -1057,296 +990,280 @@ pub use pezsc_chain_spec; #[cfg(test)] mod tests { - use configuration::{GlobalSettingsBuilder, NetworkConfigBuilder}; - use lazy_static::lazy_static; - use tokio::sync::Mutex; + use configuration::{GlobalSettingsBuilder, NetworkConfigBuilder}; + use lazy_static::lazy_static; + use tokio::sync::Mutex; - use super::*; + use super::*; - const ENV_KEY: &str = "ZOMBIE_SPAWN_CONCURRENCY"; - // mutex for test that use env - lazy_static! { - static ref ENV_MUTEX: Mutex<()> = Mutex::new(()); - } + const ENV_KEY: &str = "ZOMBIE_SPAWN_CONCURRENCY"; + // mutex for test that use env + lazy_static! { + static ref ENV_MUTEX: Mutex<()> = Mutex::new(()); + } - fn set_env(concurrency: Option) { - if let Some(value) = concurrency { - env::set_var(ENV_KEY, value.to_string()); - } else { - env::remove_var(ENV_KEY); - } - } + fn set_env(concurrency: Option) { + if let Some(value) = concurrency { + env::set_var(ENV_KEY, value.to_string()); + } else { + env::remove_var(ENV_KEY); + } + } - fn generate( - with_image: bool, - with_cmd: Option<&'static str>, - ) -> Result> { - NetworkConfigBuilder::new() - .with_relaychain(|r| { - let mut relay = r - .with_chain("rococo-local") - .with_default_command(with_cmd.unwrap_or("polkadot")); - if with_image { - relay = relay.with_default_image("docker.io/parity/polkadot") - } + fn generate( + with_image: bool, + with_cmd: Option<&'static str>, + ) -> Result> { + NetworkConfigBuilder::new() + .with_relaychain(|r| { + let mut relay = r + .with_chain("rococo-local") + .with_default_command(with_cmd.unwrap_or("polkadot")); + if with_image { + relay = relay.with_default_image("docker.io/parity/polkadot") + } - relay - .with_validator(|node| node.with_name("alice")) - .with_validator(|node| node.with_name("bob")) - }) - .with_parachain(|p| { - p.with_id(2000).cumulus_based(true).with_collator(|n| { - let node = n - .with_name("collator") - .with_command(with_cmd.unwrap_or("polkadot-parachain")); - if with_image { - node.with_image("docker.io/paritypr/test-parachain") - } else { - node - } - }) - }) - .build() - } + relay + .with_validator(|node| node.with_name("alice")) + .with_validator(|node| node.with_name("bob")) + }) + .with_parachain(|p| { + p.with_id(2000).cumulus_based(true).with_collator(|n| { + let node = n + .with_name("collator") + .with_command(with_cmd.unwrap_or("polkadot-parachain")); + if with_image { + node.with_image("docker.io/paritypr/test-parachain") + } else { + node + } + }) + }) + .build() + } - fn get_node_with_dependencies(name: &str, dependencies: Option>) -> NodeSpec { - let mut spec = NodeSpec { - name: name.to_string(), - ..Default::default() - }; - if let Some(dependencies) = dependencies { - for node in dependencies { - spec.args.push( - format!("{{{{ZOMBIE:{}:someField}}}}", node.name) - .as_str() - .into(), - ); - } - } - spec - } + fn get_node_with_dependencies(name: &str, dependencies: Option>) -> NodeSpec { + let mut spec = NodeSpec { name: name.to_string(), ..Default::default() }; + if let Some(dependencies) = dependencies { + for node in dependencies { + spec.args.push(format!("{{{{ZOMBIE:{}:someField}}}}", node.name).as_str().into()); + } + } + spec + } - fn verify_levels(actual_levels: Vec>, expected_levels: Vec>) { - actual_levels - .iter() - .zip(expected_levels) - .for_each(|(actual_level, expected_level)| { - assert_eq!(actual_level.len(), expected_level.len()); - actual_level - .iter() - .zip(expected_level.iter()) - .for_each(|(node, expected_name)| assert_eq!(node.name, *expected_name)); - }); - } + fn verify_levels(actual_levels: Vec>, expected_levels: Vec>) { + actual_levels.iter().zip(expected_levels).for_each(|(actual_level, expected_level)| { + assert_eq!(actual_level.len(), expected_level.len()); + actual_level + .iter() + .zip(expected_level.iter()) + .for_each(|(node, expected_name)| assert_eq!(node.name, *expected_name)); + }); + } - #[tokio::test] - async fn valid_config_with_image() { - let network_config = generate(true, None).unwrap(); - let spec = NetworkSpec::from_config(&network_config).await.unwrap(); - let caps = ProviderCapabilities { - requires_image: true, - has_resources: false, - prefix_with_full_path: false, - use_default_ports_in_cmd: false, - }; + #[tokio::test] + async fn valid_config_with_image() { + let network_config = generate(true, None).unwrap(); + let spec = NetworkSpec::from_config(&network_config).await.unwrap(); + let caps = ProviderCapabilities { + requires_image: true, + has_resources: false, + prefix_with_full_path: false, + use_default_ports_in_cmd: false, + }; - let valid = validate_spec_with_provider_capabilities(&spec, &caps); - assert!(valid.is_ok()) - } + let valid = validate_spec_with_provider_capabilities(&spec, &caps); + assert!(valid.is_ok()) + } - #[tokio::test] - async fn invalid_config_without_image() { - let network_config = generate(false, None).unwrap(); - let spec = NetworkSpec::from_config(&network_config).await.unwrap(); - let caps = ProviderCapabilities { - requires_image: true, - has_resources: false, - prefix_with_full_path: false, - use_default_ports_in_cmd: false, - }; + #[tokio::test] + async fn invalid_config_without_image() { + let network_config = generate(false, None).unwrap(); + let spec = NetworkSpec::from_config(&network_config).await.unwrap(); + let caps = ProviderCapabilities { + requires_image: true, + has_resources: false, + prefix_with_full_path: false, + use_default_ports_in_cmd: false, + }; - let valid = validate_spec_with_provider_capabilities(&spec, &caps); - assert!(valid.is_err()) - } + let valid = validate_spec_with_provider_capabilities(&spec, &caps); + assert!(valid.is_err()) + } - #[tokio::test] - async fn invalid_config_missing_cmd() { - let network_config = generate(false, Some("other")).unwrap(); - let spec = NetworkSpec::from_config(&network_config).await.unwrap(); - let caps = ProviderCapabilities { - requires_image: false, - has_resources: false, - prefix_with_full_path: false, - use_default_ports_in_cmd: false, - }; + #[tokio::test] + async fn invalid_config_missing_cmd() { + let network_config = generate(false, Some("other")).unwrap(); + let spec = NetworkSpec::from_config(&network_config).await.unwrap(); + let caps = ProviderCapabilities { + requires_image: false, + has_resources: false, + prefix_with_full_path: false, + use_default_ports_in_cmd: false, + }; - let valid = validate_spec_with_provider_capabilities(&spec, &caps); - assert!(valid.is_err()) - } + let valid = validate_spec_with_provider_capabilities(&spec, &caps); + assert!(valid.is_err()) + } - #[tokio::test] - async fn valid_config_present_cmd() { - let network_config = generate(false, Some("cargo")).unwrap(); - let spec = NetworkSpec::from_config(&network_config).await.unwrap(); - let caps = ProviderCapabilities { - requires_image: false, - has_resources: false, - prefix_with_full_path: false, - use_default_ports_in_cmd: false, - }; + #[tokio::test] + async fn valid_config_present_cmd() { + let network_config = generate(false, Some("cargo")).unwrap(); + let spec = NetworkSpec::from_config(&network_config).await.unwrap(); + let caps = ProviderCapabilities { + requires_image: false, + has_resources: false, + prefix_with_full_path: false, + use_default_ports_in_cmd: false, + }; - let valid = validate_spec_with_provider_capabilities(&spec, &caps); - println!("{valid:?}"); - assert!(valid.is_ok()) - } + let valid = validate_spec_with_provider_capabilities(&spec, &caps); + println!("{valid:?}"); + assert!(valid.is_ok()) + } - #[tokio::test] - async fn default_spawn_concurrency() { - let _g = ENV_MUTEX.lock().await; - set_env(None); - let network_config = generate(false, Some("cargo")).unwrap(); - let spec = NetworkSpec::from_config(&network_config).await.unwrap(); - let (concurrency, _) = calculate_concurrency(&spec).unwrap(); - assert_eq!(concurrency, 100); - } + #[tokio::test] + async fn default_spawn_concurrency() { + let _g = ENV_MUTEX.lock().await; + set_env(None); + let network_config = generate(false, Some("cargo")).unwrap(); + let spec = NetworkSpec::from_config(&network_config).await.unwrap(); + let (concurrency, _) = calculate_concurrency(&spec).unwrap(); + assert_eq!(concurrency, 100); + } - #[tokio::test] - async fn set_spawn_concurrency() { - let _g = ENV_MUTEX.lock().await; - set_env(None); + #[tokio::test] + async fn set_spawn_concurrency() { + let _g = ENV_MUTEX.lock().await; + set_env(None); - let network_config = generate(false, Some("cargo")).unwrap(); - let mut spec = NetworkSpec::from_config(&network_config).await.unwrap(); + let network_config = generate(false, Some("cargo")).unwrap(); + let mut spec = NetworkSpec::from_config(&network_config).await.unwrap(); - let global_settings = GlobalSettingsBuilder::new() - .with_spawn_concurrency(4) - .build() - .unwrap(); + let global_settings = + GlobalSettingsBuilder::new().with_spawn_concurrency(4).build().unwrap(); - spec.set_global_settings(global_settings); - let (concurrency, limited) = calculate_concurrency(&spec).unwrap(); - assert_eq!(concurrency, 4); - assert!(!limited); - } + spec.set_global_settings(global_settings); + let (concurrency, limited) = calculate_concurrency(&spec).unwrap(); + assert_eq!(concurrency, 4); + assert!(!limited); + } - #[tokio::test] - async fn set_spawn_concurrency_but_limited() { - let _g = ENV_MUTEX.lock().await; - set_env(None); + #[tokio::test] + async fn set_spawn_concurrency_but_limited() { + let _g = ENV_MUTEX.lock().await; + set_env(None); - let network_config = generate(false, Some("cargo")).unwrap(); - let mut spec = NetworkSpec::from_config(&network_config).await.unwrap(); + let network_config = generate(false, Some("cargo")).unwrap(); + let mut spec = NetworkSpec::from_config(&network_config).await.unwrap(); - let global_settings = GlobalSettingsBuilder::new() - .with_spawn_concurrency(4) - .build() - .unwrap(); + let global_settings = + GlobalSettingsBuilder::new().with_spawn_concurrency(4).build().unwrap(); - spec.set_global_settings(global_settings); - let node = spec.relaychain.nodes.first_mut().unwrap(); - node.args - .push("--bootnodes {{ZOMBIE:bob:multiAddress')}}".into()); - let (concurrency, limited) = calculate_concurrency(&spec).unwrap(); - assert_eq!(concurrency, 1); - assert!(limited); - } + spec.set_global_settings(global_settings); + let node = spec.relaychain.nodes.first_mut().unwrap(); + node.args.push("--bootnodes {{ZOMBIE:bob:multiAddress')}}".into()); + let (concurrency, limited) = calculate_concurrency(&spec).unwrap(); + assert_eq!(concurrency, 1); + assert!(limited); + } - #[tokio::test] - async fn set_spawn_concurrency_from_env() { - let _g = ENV_MUTEX.lock().await; - set_env(Some(10)); + #[tokio::test] + async fn set_spawn_concurrency_from_env() { + let _g = ENV_MUTEX.lock().await; + set_env(Some(10)); - let network_config = generate(false, Some("cargo")).unwrap(); - let spec = NetworkSpec::from_config(&network_config).await.unwrap(); - let (concurrency, limited) = calculate_concurrency(&spec).unwrap(); - assert_eq!(concurrency, 10); - assert!(!limited); - } + let network_config = generate(false, Some("cargo")).unwrap(); + let spec = NetworkSpec::from_config(&network_config).await.unwrap(); + let (concurrency, limited) = calculate_concurrency(&spec).unwrap(); + assert_eq!(concurrency, 10); + assert!(!limited); + } - #[tokio::test] - async fn set_spawn_concurrency_from_env_but_limited() { - let _g = ENV_MUTEX.lock().await; - set_env(Some(12)); + #[tokio::test] + async fn set_spawn_concurrency_from_env_but_limited() { + let _g = ENV_MUTEX.lock().await; + set_env(Some(12)); - let network_config = generate(false, Some("cargo")).unwrap(); - let mut spec = NetworkSpec::from_config(&network_config).await.unwrap(); - let node = spec.relaychain.nodes.first_mut().unwrap(); - node.args - .push("--bootnodes {{ZOMBIE:bob:multiAddress')}}".into()); - let (concurrency, limited) = calculate_concurrency(&spec).unwrap(); - assert_eq!(concurrency, 1); - assert!(limited); - } + let network_config = generate(false, Some("cargo")).unwrap(); + let mut spec = NetworkSpec::from_config(&network_config).await.unwrap(); + let node = spec.relaychain.nodes.first_mut().unwrap(); + node.args.push("--bootnodes {{ZOMBIE:bob:multiAddress')}}".into()); + let (concurrency, limited) = calculate_concurrency(&spec).unwrap(); + assert_eq!(concurrency, 1); + assert!(limited); + } - #[test] - fn dependency_levels_among_should_work() { - // no nodes - assert!(dependency_levels_among(&[]).unwrap().is_empty()); + #[test] + fn dependency_levels_among_should_work() { + // no nodes + assert!(dependency_levels_among(&[]).unwrap().is_empty()); - // one node - let alice = get_node_with_dependencies("alice", None); - let nodes = [&alice]; + // one node + let alice = get_node_with_dependencies("alice", None); + let nodes = [&alice]; - let levels = dependency_levels_among(&nodes).unwrap(); - let expected = vec![vec!["alice"]]; + let levels = dependency_levels_among(&nodes).unwrap(); + let expected = vec![vec!["alice"]]; - verify_levels(levels, expected); + verify_levels(levels, expected); - // two independent nodes - let alice = get_node_with_dependencies("alice", None); - let bob = get_node_with_dependencies("bob", None); - let nodes = [&alice, &bob]; + // two independent nodes + let alice = get_node_with_dependencies("alice", None); + let bob = get_node_with_dependencies("bob", None); + let nodes = [&alice, &bob]; - let levels = dependency_levels_among(&nodes).unwrap(); - let expected = vec![vec!["alice", "bob"]]; + let levels = dependency_levels_among(&nodes).unwrap(); + let expected = vec![vec!["alice", "bob"]]; - verify_levels(levels, expected); + verify_levels(levels, expected); - // alice -> bob -> charlie - let alice = get_node_with_dependencies("alice", None); - let bob = get_node_with_dependencies("bob", Some(vec![&alice])); - let charlie = get_node_with_dependencies("charlie", Some(vec![&bob])); - let nodes = [&alice, &bob, &charlie]; + // alice -> bob -> charlie + let alice = get_node_with_dependencies("alice", None); + let bob = get_node_with_dependencies("bob", Some(vec![&alice])); + let charlie = get_node_with_dependencies("charlie", Some(vec![&bob])); + let nodes = [&alice, &bob, &charlie]; - let levels = dependency_levels_among(&nodes).unwrap(); - let expected = vec![vec!["alice"], vec!["bob"], vec!["charlie"]]; + let levels = dependency_levels_among(&nodes).unwrap(); + let expected = vec![vec!["alice"], vec!["bob"], vec!["charlie"]]; - verify_levels(levels, expected); + verify_levels(levels, expected); - // ┌─> bob - // alice ──| - // └─> charlie - let alice = get_node_with_dependencies("alice", None); - let bob = get_node_with_dependencies("bob", Some(vec![&alice])); - let charlie = get_node_with_dependencies("charlie", Some(vec![&alice])); - let nodes = [&alice, &bob, &charlie]; + // ┌─> bob + // alice ──| + // └─> charlie + let alice = get_node_with_dependencies("alice", None); + let bob = get_node_with_dependencies("bob", Some(vec![&alice])); + let charlie = get_node_with_dependencies("charlie", Some(vec![&alice])); + let nodes = [&alice, &bob, &charlie]; - let levels = dependency_levels_among(&nodes).unwrap(); - let expected = vec![vec!["alice"], vec!["bob", "charlie"]]; + let levels = dependency_levels_among(&nodes).unwrap(); + let expected = vec![vec!["alice"], vec!["bob", "charlie"]]; - verify_levels(levels, expected); + verify_levels(levels, expected); - // ┌─> bob ──┐ - // alice ──| ├─> dave - // └─> charlie ┘ - let alice = get_node_with_dependencies("alice", None); - let bob = get_node_with_dependencies("bob", Some(vec![&alice])); - let charlie = get_node_with_dependencies("charlie", Some(vec![&alice])); - let dave = get_node_with_dependencies("dave", Some(vec![&charlie, &bob])); - let nodes = [&alice, &bob, &charlie, &dave]; + // ┌─> bob ──┐ + // alice ──| ├─> dave + // └─> charlie ┘ + let alice = get_node_with_dependencies("alice", None); + let bob = get_node_with_dependencies("bob", Some(vec![&alice])); + let charlie = get_node_with_dependencies("charlie", Some(vec![&alice])); + let dave = get_node_with_dependencies("dave", Some(vec![&charlie, &bob])); + let nodes = [&alice, &bob, &charlie, &dave]; - let levels = dependency_levels_among(&nodes).unwrap(); - let expected = vec![vec!["alice"], vec!["bob", "charlie"], vec!["dave"]]; + let levels = dependency_levels_among(&nodes).unwrap(); + let expected = vec![vec!["alice"], vec!["bob", "charlie"], vec!["dave"]]; - verify_levels(levels, expected); - } + verify_levels(levels, expected); + } - #[test] - fn dependency_levels_among_should_detect_cycles() { - let mut alice = get_node_with_dependencies("alice", None); - let bob = get_node_with_dependencies("bob", Some(vec![&alice])); - alice.args.push("{{ZOMBIE:bob:someField}}".into()); + #[test] + fn dependency_levels_among_should_detect_cycles() { + let mut alice = get_node_with_dependencies("alice", None); + let bob = get_node_with_dependencies("bob", Some(vec![&alice])); + alice.args.push("{{ZOMBIE:bob:someField}}".into()); - assert!(dependency_levels_among(&[&alice, &bob]).is_err()) - } + assert!(dependency_levels_among(&[&alice, &bob]).is_err()) + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network.rs index 3a0694f5..e47fee72 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network.rs @@ -6,10 +6,10 @@ pub mod teyrchain; use std::{cell::RefCell, collections::HashMap, path::PathBuf, rc::Rc, sync::Arc, time::Duration}; use configuration::{ - para_states::{Initial, Running}, - shared::{helpers::generate_unique_node_name_from_names, node::EnvVar}, - types::{Arg, Command, Image, Port, ValidationContext}, - ParachainConfig, ParachainConfigBuilder, RegistrationStrategy, + para_states::{Initial, Running}, + shared::{helpers::generate_unique_node_name_from_names, node::EnvVar}, + types::{Arg, Command, Image, Port, ValidationContext}, + ParachainConfig, ParachainConfigBuilder, RegistrationStrategy, }; use provider::{types::TransferedFile, DynNamespace, ProviderError}; use serde::Serialize; @@ -19,824 +19,776 @@ use tracing::{error, warn}; use self::{node::NetworkNode, relaychain::Relaychain, teyrchain::Parachain}; use crate::{ - generators::chain_spec::ChainSpec, - network_spec::{self, NetworkSpec}, - shared::{ - constants::{NODE_MONITORING_FAILURE_THRESHOLD_SECONDS, NODE_MONITORING_INTERVAL_SECONDS}, - macros, - types::{ChainDefaultContext, RegisterParachainOptions}, - }, - spawner::{self, SpawnNodeCtx}, - ScopedFilesystem, ZombieRole, + generators::chain_spec::ChainSpec, + network_spec::{self, NetworkSpec}, + shared::{ + constants::{NODE_MONITORING_FAILURE_THRESHOLD_SECONDS, NODE_MONITORING_INTERVAL_SECONDS}, + macros, + types::{ChainDefaultContext, RegisterParachainOptions}, + }, + spawner::{self, SpawnNodeCtx}, + ScopedFilesystem, ZombieRole, }; #[derive(Serialize)] pub struct Network { - #[serde(skip)] - ns: DynNamespace, - #[serde(skip)] - filesystem: T, - relay: Relaychain, - initial_spec: NetworkSpec, - parachains: HashMap>, - #[serde(skip)] - nodes_by_name: HashMap, - #[serde(skip)] - nodes_to_watch: Arc>>, + #[serde(skip)] + ns: DynNamespace, + #[serde(skip)] + filesystem: T, + relay: Relaychain, + initial_spec: NetworkSpec, + parachains: HashMap>, + #[serde(skip)] + nodes_by_name: HashMap, + #[serde(skip)] + nodes_to_watch: Arc>>, } impl std::fmt::Debug for Network { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Network") - .field("ns", &"ns_skipped") - .field("relay", &self.relay) - .field("initial_spec", &self.initial_spec) - .field("parachains", &self.parachains) - .field("nodes_by_name", &self.nodes_by_name) - .finish() - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Network") + .field("ns", &"ns_skipped") + .field("relay", &self.relay) + .field("initial_spec", &self.initial_spec) + .field("parachains", &self.parachains) + .field("nodes_by_name", &self.nodes_by_name) + .finish() + } } macros::create_add_options!(AddNodeOptions { - chain_spec: Option, - override_eth_key: Option + chain_spec: Option, + override_eth_key: Option }); macros::create_add_options!(AddCollatorOptions { - chain_spec: Option, - chain_spec_relay: Option, - override_eth_key: Option + chain_spec: Option, + chain_spec_relay: Option, + override_eth_key: Option }); impl Network { - pub(crate) fn new_with_relay( - relay: Relaychain, - ns: DynNamespace, - fs: T, - initial_spec: NetworkSpec, - ) -> Self { - Self { - ns, - filesystem: fs, - relay, - initial_spec, - parachains: Default::default(), - nodes_by_name: Default::default(), - nodes_to_watch: Default::default(), - } - } - - // Pubic API - pub fn ns_name(&self) -> String { - self.ns.name().to_string() - } - - pub fn base_dir(&self) -> Option<&str> { - self.ns.base_dir().to_str() - } - - pub fn relaychain(&self) -> &Relaychain { - &self.relay - } - - // Teardown the network - pub async fn destroy(self) -> Result<(), ProviderError> { - self.ns.destroy().await - } - - /// Add a node to the relaychain - // The new node is added to the running network instance. - /// # Example: - /// ```rust - /// # use provider::NativeProvider; - /// # use support::{fs::local::LocalFileSystem}; - /// # use zombienet_orchestrator::{errors, AddNodeOptions, Orchestrator}; - /// # use configuration::NetworkConfig; - /// # async fn example() -> Result<(), errors::OrchestratorError> { - /// # let provider = NativeProvider::new(LocalFileSystem {}); - /// # let orchestrator = Orchestrator::new(LocalFileSystem {}, provider); - /// # let config = NetworkConfig::load_from_toml("config.toml")?; - /// let mut network = orchestrator.spawn(config).await?; - /// - /// // Create the options to add the new node - /// let opts = AddNodeOptions { - /// rpc_port: Some(9444), - /// is_validator: true, - /// ..Default::default() - /// }; - /// - /// network.add_node("new-node", opts).await?; - /// # Ok(()) - /// # } - /// ``` - pub async fn add_node( - &mut self, - name: impl Into, - options: AddNodeOptions, - ) -> Result<(), anyhow::Error> { - let name = generate_unique_node_name_from_names( - name, - &mut self.nodes_by_name.keys().cloned().collect(), - ); - - let relaychain = self.relaychain(); - - let chain_spec_path = if let Some(chain_spec_custom_path) = &options.chain_spec { - chain_spec_custom_path.clone() - } else { - PathBuf::from(format!( - "{}/{}.json", - self.ns.base_dir().to_string_lossy(), - relaychain.chain - )) - }; - - let chain_context = ChainDefaultContext { - default_command: self.initial_spec.relaychain.default_command.as_ref(), - default_image: self.initial_spec.relaychain.default_image.as_ref(), - default_resources: self.initial_spec.relaychain.default_resources.as_ref(), - default_db_snapshot: self.initial_spec.relaychain.default_db_snapshot.as_ref(), - default_args: self.initial_spec.relaychain.default_args.iter().collect(), - }; - - let mut node_spec = network_spec::node::NodeSpec::from_ad_hoc( - &name, - options.into(), - &chain_context, - false, - false, - )?; - - node_spec.available_args_output = Some( - self.initial_spec - .node_available_args_output(&node_spec, self.ns.clone()) - .await?, - ); - - let base_dir = self.ns.base_dir().to_string_lossy(); - let scoped_fs = ScopedFilesystem::new(&self.filesystem, &base_dir); - - let ctx = SpawnNodeCtx { - chain_id: &relaychain.chain_id, - parachain_id: None, - chain: &relaychain.chain, - role: ZombieRole::Node, - ns: &self.ns, - scoped_fs: &scoped_fs, - parachain: None, - bootnodes_addr: &vec![], - wait_ready: true, - nodes_by_name: serde_json::to_value(&self.nodes_by_name)?, - global_settings: &self.initial_spec.global_settings, - }; - - let global_files_to_inject = vec![TransferedFile::new( - chain_spec_path, - PathBuf::from(format!("/cfg/{}.json", relaychain.chain)), - )]; - - let node = spawner::spawn_node(&node_spec, global_files_to_inject, &ctx).await?; - - // TODO: register the new node as validator in the relaychain - // STEPS: - // - check balance of `stash` derivation for validator account - // - call rotate_keys on the new validator - // - call setKeys on the new validator - // if node_spec.is_validator { - // let running_node = self.relay.nodes.first().unwrap(); - // // tx_helper::validator_actions::register(vec![&node], &running_node.ws_uri, None).await?; - // } - - // Let's make sure node is up before adding - node.wait_until_is_up(self.initial_spec.global_settings.network_spawn_timeout()) - .await?; - - // Add node to relaychain data - self.add_running_node(node.clone(), None).await; - - Ok(()) - } - - /// Add a new collator to a parachain - /// - /// NOTE: if more parachains with given id available (rare corner case) - /// then it adds collator to the first parachain - /// - /// # Example: - /// ```rust - /// # use provider::NativeProvider; - /// # use support::{fs::local::LocalFileSystem}; - /// # use zombienet_orchestrator::{errors, AddCollatorOptions, Orchestrator}; - /// # use configuration::NetworkConfig; - /// # async fn example() -> Result<(), anyhow::Error> { - /// # let provider = NativeProvider::new(LocalFileSystem {}); - /// # let orchestrator = Orchestrator::new(LocalFileSystem {}, provider); - /// # let config = NetworkConfig::load_from_toml("config.toml")?; - /// let mut network = orchestrator.spawn(config).await?; - /// - /// let col_opts = AddCollatorOptions { - /// command: Some("polkadot-parachain".try_into()?), - /// ..Default::default() - /// }; - /// - /// network.add_collator("new-col-1", col_opts, 100).await?; - /// # Ok(()) - /// # } - /// ``` - pub async fn add_collator( - &mut self, - name: impl Into, - options: AddCollatorOptions, - para_id: u32, - ) -> Result<(), anyhow::Error> { - let name = generate_unique_node_name_from_names( - name, - &mut self.nodes_by_name.keys().cloned().collect(), - ); - let spec = self - .initial_spec - .parachains - .iter() - .find(|para| para.id == para_id) - .ok_or(anyhow::anyhow!(format!("parachain: {para_id} not found!")))?; - let role = if spec.is_cumulus_based { - ZombieRole::CumulusCollator - } else { - ZombieRole::Collator - }; - let chain_context = ChainDefaultContext { - default_command: spec.default_command.as_ref(), - default_image: spec.default_image.as_ref(), - default_resources: spec.default_resources.as_ref(), - default_db_snapshot: spec.default_db_snapshot.as_ref(), - default_args: spec.default_args.iter().collect(), - }; - - let parachain = self - .parachains - .get_mut(¶_id) - .ok_or(anyhow::anyhow!(format!("parachain: {para_id} not found!")))? - .get_mut(0) - .ok_or(anyhow::anyhow!(format!("parachain: {para_id} not found!")))?; - - let base_dir = self.ns.base_dir().to_string_lossy(); - let scoped_fs = ScopedFilesystem::new(&self.filesystem, &base_dir); - - // TODO: we want to still supporting spawn a dedicated bootnode?? - let ctx = SpawnNodeCtx { - chain_id: &self.relay.chain_id, - parachain_id: parachain.chain_id.as_deref(), - chain: &self.relay.chain, - role, - ns: &self.ns, - scoped_fs: &scoped_fs, - parachain: Some(spec), - bootnodes_addr: &vec![], - wait_ready: true, - nodes_by_name: serde_json::to_value(&self.nodes_by_name)?, - global_settings: &self.initial_spec.global_settings, - }; - - let relaychain_spec_path = if let Some(chain_spec_custom_path) = &options.chain_spec_relay { - chain_spec_custom_path.clone() - } else { - PathBuf::from(format!( - "{}/{}.json", - self.ns.base_dir().to_string_lossy(), - self.relay.chain - )) - }; - - let mut global_files_to_inject = vec![TransferedFile::new( - relaychain_spec_path, - PathBuf::from(format!("/cfg/{}.json", self.relay.chain)), - )]; - - let para_chain_spec_local_path = if let Some(para_chain_spec_custom) = &options.chain_spec { - Some(para_chain_spec_custom.clone()) - } else if let Some(para_spec_path) = ¶chain.chain_spec_path { - Some(PathBuf::from(format!( - "{}/{}", - self.ns.base_dir().to_string_lossy(), - para_spec_path.to_string_lossy() - ))) - } else { - None - }; - - if let Some(para_spec_path) = para_chain_spec_local_path { - global_files_to_inject.push(TransferedFile::new( - para_spec_path, - PathBuf::from(format!("/cfg/{para_id}.json")), - )); - } - - let mut node_spec = network_spec::node::NodeSpec::from_ad_hoc( - name, - options.into(), - &chain_context, - true, - spec.is_evm_based, - )?; - - node_spec.available_args_output = Some( - self.initial_spec - .node_available_args_output(&node_spec, self.ns.clone()) - .await?, - ); - - let node = spawner::spawn_node(&node_spec, global_files_to_inject, &ctx).await?; - - // Let's make sure node is up before adding - node.wait_until_is_up(self.initial_spec.global_settings.network_spawn_timeout()) - .await?; - - parachain.collators.push(node.clone()); - self.add_running_node(node, None).await; - - Ok(()) - } - - /// Get a parachain config builder from a running network - /// - /// This allow you to build a new parachain config to be deployed into - /// the running network. - pub fn para_config_builder(&self) -> ParachainConfigBuilder { - let used_ports = self - .nodes_iter() - .map(|node| node.spec()) - .flat_map(|spec| { - [ - spec.ws_port.0, - spec.rpc_port.0, - spec.prometheus_port.0, - spec.p2p_port.0, - ] - }) - .collect(); - - let used_nodes_names = self.nodes_by_name.keys().cloned().collect(); - - // need to inverse logic of generate_unique_para_id - let used_para_ids = self - .parachains - .iter() - .map(|(id, paras)| (*id, paras.len().saturating_sub(1) as u8)) - .collect(); - - let context = ValidationContext { - used_ports, - used_nodes_names, - used_para_ids, - }; - let context = Rc::new(RefCell::new(context)); - - ParachainConfigBuilder::new_with_running(context) - } - - /// Add a new parachain to the running network - /// - /// # Arguments - /// * `para_config` - Parachain configuration to deploy - /// * `custom_relaychain_spec` - Optional path to a custom relaychain spec to use - /// * `custom_parchain_fs_prefix` - Optional prefix to use when artifacts are created - /// - /// - /// # Example: - /// ```rust - /// # use anyhow::anyhow; - /// # use provider::NativeProvider; - /// # use support::{fs::local::LocalFileSystem}; - /// # use zombienet_orchestrator::{errors, AddCollatorOptions, Orchestrator}; - /// # use configuration::NetworkConfig; - /// # async fn example() -> Result<(), anyhow::Error> { - /// # let provider = NativeProvider::new(LocalFileSystem {}); - /// # let orchestrator = Orchestrator::new(LocalFileSystem {}, provider); - /// # let config = NetworkConfig::load_from_toml("config.toml")?; - /// let mut network = orchestrator.spawn(config).await?; - /// let para_config = network - /// .para_config_builder() - /// .with_id(100) - /// .with_default_command("polkadot-parachain") - /// .with_collator(|c| c.with_name("col-100-1")) - /// .build() - /// .map_err(|_e| anyhow!("Building config"))?; - /// - /// network.add_parachain(¶_config, None, None).await?; - /// - /// # Ok(()) - /// # } - /// ``` - pub async fn add_parachain( - &mut self, - para_config: &ParachainConfig, - custom_relaychain_spec: Option, - custom_parchain_fs_prefix: Option, - ) -> Result<(), anyhow::Error> { - let base_dir = self.ns.base_dir().to_string_lossy().to_string(); - let scoped_fs = ScopedFilesystem::new(&self.filesystem, &base_dir); - - let mut global_files_to_inject = vec![]; - - // get relaychain id - let relay_chain_id = if let Some(custom_path) = custom_relaychain_spec { - // use this file as relaychain spec - global_files_to_inject.push(TransferedFile::new( - custom_path.clone(), - PathBuf::from(format!("/cfg/{}.json", self.relaychain().chain)), - )); - let content = std::fs::read_to_string(custom_path)?; - ChainSpec::chain_id_from_spec(&content)? - } else { - global_files_to_inject.push(TransferedFile::new( - PathBuf::from(format!( - "{}/{}", - scoped_fs.base_dir, - self.relaychain().chain_spec_path.to_string_lossy() - )), - PathBuf::from(format!("/cfg/{}.json", self.relaychain().chain)), - )); - self.relay.chain_id.clone() - }; - - let mut para_spec = network_spec::teyrchain::TeyrchainSpec::from_config( - para_config, - relay_chain_id.as_str().try_into()?, - )?; - - let chain_spec_raw_path = para_spec - .build_chain_spec(&relay_chain_id, &self.ns, &scoped_fs) - .await?; - - // Para artifacts - let para_path_prefix = if let Some(custom_prefix) = custom_parchain_fs_prefix { - custom_prefix - } else { - para_spec.id.to_string() - }; - - scoped_fs.create_dir(¶_path_prefix).await?; - // create wasm/state - para_spec - .genesis_state - .build( - chain_spec_raw_path.as_ref(), - format!("{}/genesis-state", ¶_path_prefix), - &self.ns, - &scoped_fs, - None, - ) - .await?; - para_spec - .genesis_wasm - .build( - chain_spec_raw_path.as_ref(), - format!("{}/para_spec-wasm", ¶_path_prefix), - &self.ns, - &scoped_fs, - None, - ) - .await?; - - let parachain = - Parachain::from_spec(¶_spec, &global_files_to_inject, &scoped_fs).await?; - let parachain_id = parachain.chain_id.clone(); - - // Create `ctx` for spawn the nodes - let ctx_para = SpawnNodeCtx { - parachain: Some(¶_spec), - parachain_id: parachain_id.as_deref(), - role: if para_spec.is_cumulus_based { - ZombieRole::CumulusCollator - } else { - ZombieRole::Collator - }, - bootnodes_addr: ¶_config - .bootnodes_addresses() - .iter() - .map(|&a| a.to_string()) - .collect(), - chain_id: &self.relaychain().chain_id, - chain: &self.relaychain().chain, - ns: &self.ns, - scoped_fs: &scoped_fs, - wait_ready: false, - nodes_by_name: serde_json::to_value(&self.nodes_by_name)?, - global_settings: &self.initial_spec.global_settings, - }; - - // Register the parachain to the running network - let first_node_url = self - .relaychain() - .nodes - .first() - .ok_or(anyhow::anyhow!( - "At least one node of the relaychain should be running" - ))? - .ws_uri(); - - if para_config.registration_strategy() == Some(&RegistrationStrategy::UsingExtrinsic) { - let register_para_options = RegisterParachainOptions { - id: parachain.para_id, - // This needs to resolve correctly - wasm_path: para_spec - .genesis_wasm - .artifact_path() - .ok_or(anyhow::anyhow!( - "artifact path for wasm must be set at this point", - ))? - .to_path_buf(), - state_path: para_spec - .genesis_state - .artifact_path() - .ok_or(anyhow::anyhow!( - "artifact path for state must be set at this point", - ))? - .to_path_buf(), - node_ws_url: first_node_url.to_string(), - onboard_as_para: para_spec.onboard_as_parachain, - seed: None, // TODO: Seed is passed by? - finalization: false, - }; - - Parachain::register(register_para_options, &scoped_fs).await?; - } - - // Spawn the nodes - let spawning_tasks = para_spec - .collators - .iter() - .map(|node| spawner::spawn_node(node, parachain.files_to_inject.clone(), &ctx_para)); - - let running_nodes = futures::future::try_join_all(spawning_tasks).await?; - - // Let's make sure nodes are up before adding them - let waiting_tasks = running_nodes.iter().map(|node| { - node.wait_until_is_up(self.initial_spec.global_settings.network_spawn_timeout()) - }); - - let _ = futures::future::try_join_all(waiting_tasks).await?; - - let running_para_id = parachain.para_id; - self.add_para(parachain); - for node in running_nodes { - self.add_running_node(node, Some(running_para_id)).await; - } - - Ok(()) - } - - /// Register a parachain, which has already been added to the network (with manual registration - /// strategy) - /// - /// # Arguments - /// * `para_id` - Parachain Id - /// - /// - /// # Example: - /// ```rust - /// # use anyhow::anyhow; - /// # use provider::NativeProvider; - /// # use support::{fs::local::LocalFileSystem}; - /// # use zombienet_orchestrator::Orchestrator; - /// # use configuration::{NetworkConfig, NetworkConfigBuilder, RegistrationStrategy}; - /// # async fn example() -> Result<(), anyhow::Error> { - /// # let provider = NativeProvider::new(LocalFileSystem {}); - /// # let orchestrator = Orchestrator::new(LocalFileSystem {}, provider); - /// # let config = NetworkConfigBuilder::new() - /// # .with_relaychain(|r| { - /// # r.with_chain("rococo-local") - /// # .with_default_command("polkadot") - /// # .with_node(|node| node.with_name("alice")) - /// # }) - /// # .with_parachain(|p| { - /// # p.with_id(100) - /// # .with_registration_strategy(RegistrationStrategy::Manual) - /// # .with_default_command("test-parachain") - /// # .with_collator(|n| n.with_name("dave").validator(false)) - /// # }) - /// # .build() - /// # .map_err(|_e| anyhow!("Building config"))?; - /// let mut network = orchestrator.spawn(config).await?; - /// - /// network.register_parachain(100).await?; - /// - /// # Ok(()) - /// # } - /// ``` - pub async fn register_parachain(&mut self, para_id: u32) -> Result<(), anyhow::Error> { - let para = self - .initial_spec - .parachains - .iter() - .find(|p| p.id == para_id) - .ok_or(anyhow::anyhow!( - "no parachain with id = {para_id} available", - ))?; - let para_genesis_config = para.get_genesis_config()?; - let first_node_url = self - .relaychain() - .nodes - .first() - .ok_or(anyhow::anyhow!( - "At least one node of the relaychain should be running" - ))? - .ws_uri(); - let register_para_options: RegisterParachainOptions = RegisterParachainOptions { - id: para_id, - // This needs to resolve correctly - wasm_path: para_genesis_config.wasm_path.clone(), - state_path: para_genesis_config.state_path.clone(), - node_ws_url: first_node_url.to_string(), - onboard_as_para: para_genesis_config.as_parachain, - seed: None, // TODO: Seed is passed by? - finalization: false, - }; - let base_dir = self.ns.base_dir().to_string_lossy().to_string(); - let scoped_fs = ScopedFilesystem::new(&self.filesystem, &base_dir); - Parachain::register(register_para_options, &scoped_fs).await?; - - Ok(()) - } - - // deregister and stop the collator? - // remove_parachain() - - pub fn get_node(&self, name: impl Into) -> Result<&NetworkNode, anyhow::Error> { - let name = name.into(); - if let Some(node) = self.nodes_iter().find(|&n| n.name == name) { - return Ok(node); - } - - let list = self - .nodes_iter() - .map(|n| &n.name) - .cloned() - .collect::>() - .join(", "); - - Err(anyhow::anyhow!( - "can't find node with name: {name:?}, should be one of {list}" - )) - } - - pub fn get_node_mut( - &mut self, - name: impl Into, - ) -> Result<&mut NetworkNode, anyhow::Error> { - let name = name.into(); - self.nodes_iter_mut() - .find(|n| n.name == name) - .ok_or(anyhow::anyhow!("can't find node with name: {name:?}")) - } - - pub fn nodes(&self) -> Vec<&NetworkNode> { - self.nodes_by_name.values().collect::>() - } - - pub async fn detach(&self) { - self.ns.detach().await - } - - // Internal API - pub(crate) async fn add_running_node(&mut self, node: NetworkNode, para_id: Option) { - if let Some(para_id) = para_id { - if let Some(para) = self.parachains.get_mut(¶_id).and_then(|p| p.get_mut(0)) { - para.collators.push(node.clone()); - } else { - // is the first node of the para, let create the entry - unreachable!() - } - } else { - self.relay.nodes.push(node.clone()); - } - // TODO: we should hold a ref to the node in the vec in the future. - node.set_is_running(true); - let node_name = node.name.clone(); - self.nodes_by_name.insert(node_name, node.clone()); - self.nodes_to_watch.write().await.push(node); - } - - pub(crate) fn add_para(&mut self, para: Parachain) { - self.parachains.entry(para.para_id).or_default().push(para); - } - - pub fn name(&self) -> &str { - self.ns.name() - } - - /// Get a first parachain from the list of the parachains with specified id. - /// NOTE! - /// Usually the list will contain only one parachain. - /// Multiple parachains with the same id is a corner case. - /// If this is the case then one can get such parachain with - /// `parachain_by_unique_id()` method - /// - /// # Arguments - /// * `para_id` - Parachain Id - pub fn parachain(&self, para_id: u32) -> Option<&Parachain> { - self.parachains.get(¶_id)?.first() - } - - /// Get a parachain by its unique id. - /// - /// This is particularly useful if there are multiple parachains - /// with the same id (this is a rare corner case). - /// - /// # Arguments - /// * `unique_id` - unique id of the parachain - pub fn parachain_by_unique_id(&self, unique_id: impl AsRef) -> Option<&Parachain> { - self.parachains - .values() - .flat_map(|p| p.iter()) - .find(|p| p.unique_id == unique_id.as_ref()) - } - - pub fn parachains(&self) -> Vec<&Parachain> { - self.parachains.values().flatten().collect() - } - - pub(crate) fn nodes_iter(&self) -> impl Iterator { - self.relay.nodes.iter().chain( - self.parachains - .values() - .flat_map(|p| p.iter()) - .flat_map(|p| &p.collators), - ) - } - - pub(crate) fn nodes_iter_mut(&mut self) -> impl Iterator { - self.relay.nodes.iter_mut().chain( - self.parachains - .values_mut() - .flat_map(|p| p.iter_mut()) - .flat_map(|p| &mut p.collators), - ) - } - - /// Waits given number of seconds until all nodes in the network report that they are - /// up and running. - /// - /// # Arguments - /// * `timeout_secs` - The number of seconds to wait. - /// - /// # Returns - /// * `Ok()` if the node is up before timeout occured. - /// * `Err(e)` if timeout or other error occurred while waiting. - pub async fn wait_until_is_up(&self, timeout_secs: u64) -> Result<(), anyhow::Error> { - let handles = self - .nodes_iter() - .map(|node| node.wait_until_is_up(timeout_secs)); - - futures::future::try_join_all(handles).await?; - - Ok(()) - } - - pub(crate) fn spawn_watching_task(&self) { - let nodes_to_watch = Arc::clone(&self.nodes_to_watch); - let ns = Arc::clone(&self.ns); - - tokio::spawn(async move { - loop { - tokio::time::sleep(Duration::from_secs(NODE_MONITORING_INTERVAL_SECONDS)).await; - - let all_running = { - let guard = nodes_to_watch.read().await; - let nodes = guard.iter().filter(|n| n.is_running()).collect::>(); - - let all_running = - futures::future::try_join_all(nodes.iter().map(|n| { - n.wait_until_is_up(NODE_MONITORING_FAILURE_THRESHOLD_SECONDS) - })) - .await; - - // Re-check `is_running` to make sure we don't kill the network unnecessarily - if nodes.iter().any(|n| !n.is_running()) { - continue; - } else { - all_running - } - }; - - if let Err(e) = all_running { - warn!("\n\t🧟 One of the nodes crashed: {e}. tearing the network down..."); - - if let Err(e) = ns.destroy().await { - error!("an error occurred during network teardown: {}", e); - } - - std::process::exit(1); - } - } - }); - } - - pub(crate) fn set_parachains(&mut self, parachains: HashMap>) { - self.parachains = parachains; - } - - pub(crate) fn insert_node(&mut self, node: NetworkNode) { - self.nodes_by_name.insert(node.name.clone(), node); - } + pub(crate) fn new_with_relay( + relay: Relaychain, + ns: DynNamespace, + fs: T, + initial_spec: NetworkSpec, + ) -> Self { + Self { + ns, + filesystem: fs, + relay, + initial_spec, + parachains: Default::default(), + nodes_by_name: Default::default(), + nodes_to_watch: Default::default(), + } + } + + // Pubic API + pub fn ns_name(&self) -> String { + self.ns.name().to_string() + } + + pub fn base_dir(&self) -> Option<&str> { + self.ns.base_dir().to_str() + } + + pub fn relaychain(&self) -> &Relaychain { + &self.relay + } + + // Teardown the network + pub async fn destroy(self) -> Result<(), ProviderError> { + self.ns.destroy().await + } + + /// Add a node to the relaychain + // The new node is added to the running network instance. + /// # Example: + /// ```rust + /// # use provider::NativeProvider; + /// # use support::{fs::local::LocalFileSystem}; + /// # use zombienet_orchestrator::{errors, AddNodeOptions, Orchestrator}; + /// # use configuration::NetworkConfig; + /// # async fn example() -> Result<(), errors::OrchestratorError> { + /// # let provider = NativeProvider::new(LocalFileSystem {}); + /// # let orchestrator = Orchestrator::new(LocalFileSystem {}, provider); + /// # let config = NetworkConfig::load_from_toml("config.toml")?; + /// let mut network = orchestrator.spawn(config).await?; + /// + /// // Create the options to add the new node + /// let opts = AddNodeOptions { + /// rpc_port: Some(9444), + /// is_validator: true, + /// ..Default::default() + /// }; + /// + /// network.add_node("new-node", opts).await?; + /// # Ok(()) + /// # } + /// ``` + pub async fn add_node( + &mut self, + name: impl Into, + options: AddNodeOptions, + ) -> Result<(), anyhow::Error> { + let name = generate_unique_node_name_from_names( + name, + &mut self.nodes_by_name.keys().cloned().collect(), + ); + + let relaychain = self.relaychain(); + + let chain_spec_path = if let Some(chain_spec_custom_path) = &options.chain_spec { + chain_spec_custom_path.clone() + } else { + PathBuf::from(format!( + "{}/{}.json", + self.ns.base_dir().to_string_lossy(), + relaychain.chain + )) + }; + + let chain_context = ChainDefaultContext { + default_command: self.initial_spec.relaychain.default_command.as_ref(), + default_image: self.initial_spec.relaychain.default_image.as_ref(), + default_resources: self.initial_spec.relaychain.default_resources.as_ref(), + default_db_snapshot: self.initial_spec.relaychain.default_db_snapshot.as_ref(), + default_args: self.initial_spec.relaychain.default_args.iter().collect(), + }; + + let mut node_spec = network_spec::node::NodeSpec::from_ad_hoc( + &name, + options.into(), + &chain_context, + false, + false, + )?; + + node_spec.available_args_output = + Some(self.initial_spec.node_available_args_output(&node_spec, self.ns.clone()).await?); + + let base_dir = self.ns.base_dir().to_string_lossy(); + let scoped_fs = ScopedFilesystem::new(&self.filesystem, &base_dir); + + let ctx = SpawnNodeCtx { + chain_id: &relaychain.chain_id, + parachain_id: None, + chain: &relaychain.chain, + role: ZombieRole::Node, + ns: &self.ns, + scoped_fs: &scoped_fs, + parachain: None, + bootnodes_addr: &vec![], + wait_ready: true, + nodes_by_name: serde_json::to_value(&self.nodes_by_name)?, + global_settings: &self.initial_spec.global_settings, + }; + + let global_files_to_inject = vec![TransferedFile::new( + chain_spec_path, + PathBuf::from(format!("/cfg/{}.json", relaychain.chain)), + )]; + + let node = spawner::spawn_node(&node_spec, global_files_to_inject, &ctx).await?; + + // TODO: register the new node as validator in the relaychain + // STEPS: + // - check balance of `stash` derivation for validator account + // - call rotate_keys on the new validator + // - call setKeys on the new validator + // if node_spec.is_validator { + // let running_node = self.relay.nodes.first().unwrap(); + // // tx_helper::validator_actions::register(vec![&node], &running_node.ws_uri, None).await?; + // } + + // Let's make sure node is up before adding + node.wait_until_is_up(self.initial_spec.global_settings.network_spawn_timeout()).await?; + + // Add node to relaychain data + self.add_running_node(node.clone(), None).await; + + Ok(()) + } + + /// Add a new collator to a parachain + /// + /// NOTE: if more parachains with given id available (rare corner case) + /// then it adds collator to the first parachain + /// + /// # Example: + /// ```rust + /// # use provider::NativeProvider; + /// # use support::{fs::local::LocalFileSystem}; + /// # use zombienet_orchestrator::{errors, AddCollatorOptions, Orchestrator}; + /// # use configuration::NetworkConfig; + /// # async fn example() -> Result<(), anyhow::Error> { + /// # let provider = NativeProvider::new(LocalFileSystem {}); + /// # let orchestrator = Orchestrator::new(LocalFileSystem {}, provider); + /// # let config = NetworkConfig::load_from_toml("config.toml")?; + /// let mut network = orchestrator.spawn(config).await?; + /// + /// let col_opts = AddCollatorOptions { + /// command: Some("polkadot-parachain".try_into()?), + /// ..Default::default() + /// }; + /// + /// network.add_collator("new-col-1", col_opts, 100).await?; + /// # Ok(()) + /// # } + /// ``` + pub async fn add_collator( + &mut self, + name: impl Into, + options: AddCollatorOptions, + para_id: u32, + ) -> Result<(), anyhow::Error> { + let name = generate_unique_node_name_from_names( + name, + &mut self.nodes_by_name.keys().cloned().collect(), + ); + let spec = self + .initial_spec + .parachains + .iter() + .find(|para| para.id == para_id) + .ok_or(anyhow::anyhow!(format!("parachain: {para_id} not found!")))?; + let role = + if spec.is_cumulus_based { ZombieRole::CumulusCollator } else { ZombieRole::Collator }; + let chain_context = ChainDefaultContext { + default_command: spec.default_command.as_ref(), + default_image: spec.default_image.as_ref(), + default_resources: spec.default_resources.as_ref(), + default_db_snapshot: spec.default_db_snapshot.as_ref(), + default_args: spec.default_args.iter().collect(), + }; + + let parachain = self + .parachains + .get_mut(¶_id) + .ok_or(anyhow::anyhow!(format!("parachain: {para_id} not found!")))? + .get_mut(0) + .ok_or(anyhow::anyhow!(format!("parachain: {para_id} not found!")))?; + + let base_dir = self.ns.base_dir().to_string_lossy(); + let scoped_fs = ScopedFilesystem::new(&self.filesystem, &base_dir); + + // TODO: we want to still supporting spawn a dedicated bootnode?? + let ctx = SpawnNodeCtx { + chain_id: &self.relay.chain_id, + parachain_id: parachain.chain_id.as_deref(), + chain: &self.relay.chain, + role, + ns: &self.ns, + scoped_fs: &scoped_fs, + parachain: Some(spec), + bootnodes_addr: &vec![], + wait_ready: true, + nodes_by_name: serde_json::to_value(&self.nodes_by_name)?, + global_settings: &self.initial_spec.global_settings, + }; + + let relaychain_spec_path = if let Some(chain_spec_custom_path) = &options.chain_spec_relay { + chain_spec_custom_path.clone() + } else { + PathBuf::from(format!( + "{}/{}.json", + self.ns.base_dir().to_string_lossy(), + self.relay.chain + )) + }; + + let mut global_files_to_inject = vec![TransferedFile::new( + relaychain_spec_path, + PathBuf::from(format!("/cfg/{}.json", self.relay.chain)), + )]; + + let para_chain_spec_local_path = if let Some(para_chain_spec_custom) = &options.chain_spec { + Some(para_chain_spec_custom.clone()) + } else if let Some(para_spec_path) = ¶chain.chain_spec_path { + Some(PathBuf::from(format!( + "{}/{}", + self.ns.base_dir().to_string_lossy(), + para_spec_path.to_string_lossy() + ))) + } else { + None + }; + + if let Some(para_spec_path) = para_chain_spec_local_path { + global_files_to_inject.push(TransferedFile::new( + para_spec_path, + PathBuf::from(format!("/cfg/{para_id}.json")), + )); + } + + let mut node_spec = network_spec::node::NodeSpec::from_ad_hoc( + name, + options.into(), + &chain_context, + true, + spec.is_evm_based, + )?; + + node_spec.available_args_output = + Some(self.initial_spec.node_available_args_output(&node_spec, self.ns.clone()).await?); + + let node = spawner::spawn_node(&node_spec, global_files_to_inject, &ctx).await?; + + // Let's make sure node is up before adding + node.wait_until_is_up(self.initial_spec.global_settings.network_spawn_timeout()).await?; + + parachain.collators.push(node.clone()); + self.add_running_node(node, None).await; + + Ok(()) + } + + /// Get a parachain config builder from a running network + /// + /// This allow you to build a new parachain config to be deployed into + /// the running network. + pub fn para_config_builder(&self) -> ParachainConfigBuilder { + let used_ports = self + .nodes_iter() + .map(|node| node.spec()) + .flat_map(|spec| { + [spec.ws_port.0, spec.rpc_port.0, spec.prometheus_port.0, spec.p2p_port.0] + }) + .collect(); + + let used_nodes_names = self.nodes_by_name.keys().cloned().collect(); + + // need to inverse logic of generate_unique_para_id + let used_para_ids = self + .parachains + .iter() + .map(|(id, paras)| (*id, paras.len().saturating_sub(1) as u8)) + .collect(); + + let context = ValidationContext { used_ports, used_nodes_names, used_para_ids }; + let context = Rc::new(RefCell::new(context)); + + ParachainConfigBuilder::new_with_running(context) + } + + /// Add a new parachain to the running network + /// + /// # Arguments + /// * `para_config` - Parachain configuration to deploy + /// * `custom_relaychain_spec` - Optional path to a custom relaychain spec to use + /// * `custom_parchain_fs_prefix` - Optional prefix to use when artifacts are created + /// + /// + /// # Example: + /// ```rust + /// # use anyhow::anyhow; + /// # use provider::NativeProvider; + /// # use support::{fs::local::LocalFileSystem}; + /// # use zombienet_orchestrator::{errors, AddCollatorOptions, Orchestrator}; + /// # use configuration::NetworkConfig; + /// # async fn example() -> Result<(), anyhow::Error> { + /// # let provider = NativeProvider::new(LocalFileSystem {}); + /// # let orchestrator = Orchestrator::new(LocalFileSystem {}, provider); + /// # let config = NetworkConfig::load_from_toml("config.toml")?; + /// let mut network = orchestrator.spawn(config).await?; + /// let para_config = network + /// .para_config_builder() + /// .with_id(100) + /// .with_default_command("polkadot-parachain") + /// .with_collator(|c| c.with_name("col-100-1")) + /// .build() + /// .map_err(|_e| anyhow!("Building config"))?; + /// + /// network.add_parachain(¶_config, None, None).await?; + /// + /// # Ok(()) + /// # } + /// ``` + pub async fn add_parachain( + &mut self, + para_config: &ParachainConfig, + custom_relaychain_spec: Option, + custom_parchain_fs_prefix: Option, + ) -> Result<(), anyhow::Error> { + let base_dir = self.ns.base_dir().to_string_lossy().to_string(); + let scoped_fs = ScopedFilesystem::new(&self.filesystem, &base_dir); + + let mut global_files_to_inject = vec![]; + + // get relaychain id + let relay_chain_id = if let Some(custom_path) = custom_relaychain_spec { + // use this file as relaychain spec + global_files_to_inject.push(TransferedFile::new( + custom_path.clone(), + PathBuf::from(format!("/cfg/{}.json", self.relaychain().chain)), + )); + let content = std::fs::read_to_string(custom_path)?; + ChainSpec::chain_id_from_spec(&content)? + } else { + global_files_to_inject.push(TransferedFile::new( + PathBuf::from(format!( + "{}/{}", + scoped_fs.base_dir, + self.relaychain().chain_spec_path.to_string_lossy() + )), + PathBuf::from(format!("/cfg/{}.json", self.relaychain().chain)), + )); + self.relay.chain_id.clone() + }; + + let mut para_spec = network_spec::teyrchain::TeyrchainSpec::from_config( + para_config, + relay_chain_id.as_str().try_into()?, + )?; + + let chain_spec_raw_path = + para_spec.build_chain_spec(&relay_chain_id, &self.ns, &scoped_fs).await?; + + // Para artifacts + let para_path_prefix = if let Some(custom_prefix) = custom_parchain_fs_prefix { + custom_prefix + } else { + para_spec.id.to_string() + }; + + scoped_fs.create_dir(¶_path_prefix).await?; + // create wasm/state + para_spec + .genesis_state + .build( + chain_spec_raw_path.as_ref(), + format!("{}/genesis-state", ¶_path_prefix), + &self.ns, + &scoped_fs, + None, + ) + .await?; + para_spec + .genesis_wasm + .build( + chain_spec_raw_path.as_ref(), + format!("{}/para_spec-wasm", ¶_path_prefix), + &self.ns, + &scoped_fs, + None, + ) + .await?; + + let parachain = + Parachain::from_spec(¶_spec, &global_files_to_inject, &scoped_fs).await?; + let parachain_id = parachain.chain_id.clone(); + + // Create `ctx` for spawn the nodes + let ctx_para = SpawnNodeCtx { + parachain: Some(¶_spec), + parachain_id: parachain_id.as_deref(), + role: if para_spec.is_cumulus_based { + ZombieRole::CumulusCollator + } else { + ZombieRole::Collator + }, + bootnodes_addr: ¶_config + .bootnodes_addresses() + .iter() + .map(|&a| a.to_string()) + .collect(), + chain_id: &self.relaychain().chain_id, + chain: &self.relaychain().chain, + ns: &self.ns, + scoped_fs: &scoped_fs, + wait_ready: false, + nodes_by_name: serde_json::to_value(&self.nodes_by_name)?, + global_settings: &self.initial_spec.global_settings, + }; + + // Register the parachain to the running network + let first_node_url = self + .relaychain() + .nodes + .first() + .ok_or(anyhow::anyhow!("At least one node of the relaychain should be running"))? + .ws_uri(); + + if para_config.registration_strategy() == Some(&RegistrationStrategy::UsingExtrinsic) { + let register_para_options = RegisterParachainOptions { + id: parachain.para_id, + // This needs to resolve correctly + wasm_path: para_spec + .genesis_wasm + .artifact_path() + .ok_or(anyhow::anyhow!("artifact path for wasm must be set at this point",))? + .to_path_buf(), + state_path: para_spec + .genesis_state + .artifact_path() + .ok_or(anyhow::anyhow!("artifact path for state must be set at this point",))? + .to_path_buf(), + node_ws_url: first_node_url.to_string(), + onboard_as_para: para_spec.onboard_as_parachain, + seed: None, // TODO: Seed is passed by? + finalization: false, + }; + + Parachain::register(register_para_options, &scoped_fs).await?; + } + + // Spawn the nodes + let spawning_tasks = para_spec + .collators + .iter() + .map(|node| spawner::spawn_node(node, parachain.files_to_inject.clone(), &ctx_para)); + + let running_nodes = futures::future::try_join_all(spawning_tasks).await?; + + // Let's make sure nodes are up before adding them + let waiting_tasks = running_nodes.iter().map(|node| { + node.wait_until_is_up(self.initial_spec.global_settings.network_spawn_timeout()) + }); + + let _ = futures::future::try_join_all(waiting_tasks).await?; + + let running_para_id = parachain.para_id; + self.add_para(parachain); + for node in running_nodes { + self.add_running_node(node, Some(running_para_id)).await; + } + + Ok(()) + } + + /// Register a parachain, which has already been added to the network (with manual registration + /// strategy) + /// + /// # Arguments + /// * `para_id` - Parachain Id + /// + /// + /// # Example: + /// ```rust + /// # use anyhow::anyhow; + /// # use provider::NativeProvider; + /// # use support::{fs::local::LocalFileSystem}; + /// # use zombienet_orchestrator::Orchestrator; + /// # use configuration::{NetworkConfig, NetworkConfigBuilder, RegistrationStrategy}; + /// # async fn example() -> Result<(), anyhow::Error> { + /// # let provider = NativeProvider::new(LocalFileSystem {}); + /// # let orchestrator = Orchestrator::new(LocalFileSystem {}, provider); + /// # let config = NetworkConfigBuilder::new() + /// # .with_relaychain(|r| { + /// # r.with_chain("rococo-local") + /// # .with_default_command("polkadot") + /// # .with_node(|node| node.with_name("alice")) + /// # }) + /// # .with_parachain(|p| { + /// # p.with_id(100) + /// # .with_registration_strategy(RegistrationStrategy::Manual) + /// # .with_default_command("test-parachain") + /// # .with_collator(|n| n.with_name("dave").validator(false)) + /// # }) + /// # .build() + /// # .map_err(|_e| anyhow!("Building config"))?; + /// let mut network = orchestrator.spawn(config).await?; + /// + /// network.register_parachain(100).await?; + /// + /// # Ok(()) + /// # } + /// ``` + pub async fn register_parachain(&mut self, para_id: u32) -> Result<(), anyhow::Error> { + let para = self + .initial_spec + .parachains + .iter() + .find(|p| p.id == para_id) + .ok_or(anyhow::anyhow!("no parachain with id = {para_id} available",))?; + let para_genesis_config = para.get_genesis_config()?; + let first_node_url = self + .relaychain() + .nodes + .first() + .ok_or(anyhow::anyhow!("At least one node of the relaychain should be running"))? + .ws_uri(); + let register_para_options: RegisterParachainOptions = RegisterParachainOptions { + id: para_id, + // This needs to resolve correctly + wasm_path: para_genesis_config.wasm_path.clone(), + state_path: para_genesis_config.state_path.clone(), + node_ws_url: first_node_url.to_string(), + onboard_as_para: para_genesis_config.as_parachain, + seed: None, // TODO: Seed is passed by? + finalization: false, + }; + let base_dir = self.ns.base_dir().to_string_lossy().to_string(); + let scoped_fs = ScopedFilesystem::new(&self.filesystem, &base_dir); + Parachain::register(register_para_options, &scoped_fs).await?; + + Ok(()) + } + + // deregister and stop the collator? + // remove_parachain() + + pub fn get_node(&self, name: impl Into) -> Result<&NetworkNode, anyhow::Error> { + let name = name.into(); + if let Some(node) = self.nodes_iter().find(|&n| n.name == name) { + return Ok(node); + } + + let list = self.nodes_iter().map(|n| &n.name).cloned().collect::>().join(", "); + + Err(anyhow::anyhow!("can't find node with name: {name:?}, should be one of {list}")) + } + + pub fn get_node_mut( + &mut self, + name: impl Into, + ) -> Result<&mut NetworkNode, anyhow::Error> { + let name = name.into(); + self.nodes_iter_mut() + .find(|n| n.name == name) + .ok_or(anyhow::anyhow!("can't find node with name: {name:?}")) + } + + pub fn nodes(&self) -> Vec<&NetworkNode> { + self.nodes_by_name.values().collect::>() + } + + pub async fn detach(&self) { + self.ns.detach().await + } + + // Internal API + pub(crate) async fn add_running_node(&mut self, node: NetworkNode, para_id: Option) { + if let Some(para_id) = para_id { + if let Some(para) = self.parachains.get_mut(¶_id).and_then(|p| p.get_mut(0)) { + para.collators.push(node.clone()); + } else { + // is the first node of the para, let create the entry + unreachable!() + } + } else { + self.relay.nodes.push(node.clone()); + } + // TODO: we should hold a ref to the node in the vec in the future. + node.set_is_running(true); + let node_name = node.name.clone(); + self.nodes_by_name.insert(node_name, node.clone()); + self.nodes_to_watch.write().await.push(node); + } + + pub(crate) fn add_para(&mut self, para: Parachain) { + self.parachains.entry(para.para_id).or_default().push(para); + } + + pub fn name(&self) -> &str { + self.ns.name() + } + + /// Get a first parachain from the list of the parachains with specified id. + /// NOTE! + /// Usually the list will contain only one parachain. + /// Multiple parachains with the same id is a corner case. + /// If this is the case then one can get such parachain with + /// `parachain_by_unique_id()` method + /// + /// # Arguments + /// * `para_id` - Parachain Id + pub fn parachain(&self, para_id: u32) -> Option<&Parachain> { + self.parachains.get(¶_id)?.first() + } + + /// Get a parachain by its unique id. + /// + /// This is particularly useful if there are multiple parachains + /// with the same id (this is a rare corner case). + /// + /// # Arguments + /// * `unique_id` - unique id of the parachain + pub fn parachain_by_unique_id(&self, unique_id: impl AsRef) -> Option<&Parachain> { + self.parachains.values().flat_map(|p| p.iter()).find(|p| p.unique_id == unique_id.as_ref()) + } + + pub fn parachains(&self) -> Vec<&Parachain> { + self.parachains.values().flatten().collect() + } + + pub(crate) fn nodes_iter(&self) -> impl Iterator { + self.relay + .nodes + .iter() + .chain(self.parachains.values().flat_map(|p| p.iter()).flat_map(|p| &p.collators)) + } + + pub(crate) fn nodes_iter_mut(&mut self) -> impl Iterator { + self.relay.nodes.iter_mut().chain( + self.parachains.values_mut().flat_map(|p| p.iter_mut()).flat_map(|p| &mut p.collators), + ) + } + + /// Waits given number of seconds until all nodes in the network report that they are + /// up and running. + /// + /// # Arguments + /// * `timeout_secs` - The number of seconds to wait. + /// + /// # Returns + /// * `Ok()` if the node is up before timeout occured. + /// * `Err(e)` if timeout or other error occurred while waiting. + pub async fn wait_until_is_up(&self, timeout_secs: u64) -> Result<(), anyhow::Error> { + let handles = self.nodes_iter().map(|node| node.wait_until_is_up(timeout_secs)); + + futures::future::try_join_all(handles).await?; + + Ok(()) + } + + pub(crate) fn spawn_watching_task(&self) { + let nodes_to_watch = Arc::clone(&self.nodes_to_watch); + let ns = Arc::clone(&self.ns); + + tokio::spawn(async move { + loop { + tokio::time::sleep(Duration::from_secs(NODE_MONITORING_INTERVAL_SECONDS)).await; + + let all_running = { + let guard = nodes_to_watch.read().await; + let nodes = guard.iter().filter(|n| n.is_running()).collect::>(); + + let all_running = + futures::future::try_join_all(nodes.iter().map(|n| { + n.wait_until_is_up(NODE_MONITORING_FAILURE_THRESHOLD_SECONDS) + })) + .await; + + // Re-check `is_running` to make sure we don't kill the network unnecessarily + if nodes.iter().any(|n| !n.is_running()) { + continue; + } else { + all_running + } + }; + + if let Err(e) = all_running { + warn!("\n\t🧟 One of the nodes crashed: {e}. tearing the network down..."); + + if let Err(e) = ns.destroy().await { + error!("an error occurred during network teardown: {}", e); + } + + std::process::exit(1); + } + } + }); + } + + pub(crate) fn set_parachains(&mut self, parachains: HashMap>) { + self.parachains = parachains; + } + + pub(crate) fn insert_node(&mut self, node: NetworkNode) { + self.nodes_by_name.insert(node.name.clone(), node); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network/chain_upgrade.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network/chain_upgrade.rs index bfa9a474..217048f3 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network/chain_upgrade.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network/chain_upgrade.rs @@ -9,33 +9,33 @@ use crate::{shared::types::RuntimeUpgradeOptions, tx_helper}; #[async_trait] pub trait ChainUpgrade { - /// Perform a runtime upgrade (with sudo) - /// - /// This call 'System.set_code_without_checks' wrapped in - /// 'Sudo.sudo_unchecked_weight' - async fn runtime_upgrade(&self, options: RuntimeUpgradeOptions) -> Result<(), anyhow::Error>; + /// Perform a runtime upgrade (with sudo) + /// + /// This call 'System.set_code_without_checks' wrapped in + /// 'Sudo.sudo_unchecked_weight' + async fn runtime_upgrade(&self, options: RuntimeUpgradeOptions) -> Result<(), anyhow::Error>; - /// Perform a runtime upgrade (with sudo), inner call with the node pass as arg. - /// - /// This call 'System.set_code_without_checks' wrapped in - /// 'Sudo.sudo_unchecked_weight' - async fn perform_runtime_upgrade( - &self, - node: &NetworkNode, - options: RuntimeUpgradeOptions, - ) -> Result<(), anyhow::Error> { - let sudo = if let Some(possible_seed) = options.seed { - Keypair::from_secret_key(possible_seed) - .map_err(|_| anyhow!("seed should return a Keypair"))? - } else { - let uri = SecretUri::from_str("//Alice")?; - Keypair::from_uri(&uri).map_err(|_| anyhow!("'//Alice' should return a Keypair"))? - }; + /// Perform a runtime upgrade (with sudo), inner call with the node pass as arg. + /// + /// This call 'System.set_code_without_checks' wrapped in + /// 'Sudo.sudo_unchecked_weight' + async fn perform_runtime_upgrade( + &self, + node: &NetworkNode, + options: RuntimeUpgradeOptions, + ) -> Result<(), anyhow::Error> { + let sudo = if let Some(possible_seed) = options.seed { + Keypair::from_secret_key(possible_seed) + .map_err(|_| anyhow!("seed should return a Keypair"))? + } else { + let uri = SecretUri::from_str("//Alice")?; + Keypair::from_uri(&uri).map_err(|_| anyhow!("'//Alice' should return a Keypair"))? + }; - let wasm_data = options.wasm.get_asset().await?; + let wasm_data = options.wasm.get_asset().await?; - tx_helper::runtime_upgrade::upgrade(node, &wasm_data, &sudo).await?; + tx_helper::runtime_upgrade::upgrade(node, &wasm_data, &sudo).await?; - Ok(()) - } + Ok(()) + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network/node.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network/node.rs index 901a072d..eb0a308b 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network/node.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network/node.rs @@ -1,9 +1,9 @@ use std::{ - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, - time::Duration, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + time::Duration, }; use anyhow::anyhow; @@ -24,35 +24,35 @@ type BoxedClosure = Box Result + Send + Syn #[derive(Error, Debug)] pub enum NetworkNodeError { - #[error("metric '{0}' not found!")] - MetricNotFound(String), + #[error("metric '{0}' not found!")] + MetricNotFound(String), } #[derive(Clone, Serialize)] pub struct NetworkNode { - #[serde(serialize_with = "serialize_provider_node")] - pub(crate) inner: DynNode, - // TODO: do we need the full spec here? - // Maybe a reduce set of values. - pub(crate) spec: NodeSpec, - pub(crate) name: String, - pub(crate) ws_uri: String, - pub(crate) multiaddr: String, - pub(crate) prometheus_uri: String, - #[serde(skip)] - metrics_cache: Arc>, - #[serde(skip)] - is_running: Arc, + #[serde(serialize_with = "serialize_provider_node")] + pub(crate) inner: DynNode, + // TODO: do we need the full spec here? + // Maybe a reduce set of values. + pub(crate) spec: NodeSpec, + pub(crate) name: String, + pub(crate) ws_uri: String, + pub(crate) multiaddr: String, + pub(crate) prometheus_uri: String, + #[serde(skip)] + metrics_cache: Arc>, + #[serde(skip)] + is_running: Arc, } #[derive(Deserialize)] pub(crate) struct RawNetworkNode { - pub(crate) name: String, - pub(crate) ws_uri: String, - pub(crate) prometheus_uri: String, - pub(crate) multiaddr: String, - pub(crate) spec: NodeSpec, - pub(crate) inner: serde_json::Value, + pub(crate) name: String, + pub(crate) ws_uri: String, + pub(crate) prometheus_uri: String, + pub(crate) multiaddr: String, + pub(crate) spec: NodeSpec, + pub(crate) inner: serde_json::Value, } /// Result of waiting for a certain number of log lines to appear. @@ -66,17 +66,17 @@ pub(crate) struct RawNetworkNode { /// * `count`: The final number of matching log lines at timeout expiration. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum LogLineCount { - TargetReached(u32), - TargetFailed(u32), + TargetReached(u32), + TargetFailed(u32), } impl LogLineCount { - pub fn success(&self) -> bool { - match self { - Self::TargetReached(..) => true, - Self::TargetFailed(..) => false, - } - } + pub fn success(&self) -> bool { + match self { + Self::TargetReached(..) => true, + Self::TargetFailed(..) => false, + } + } } /// Configuration for controlling log line count waiting behavior. @@ -93,27 +93,23 @@ impl LogLineCount { /// Useful when you need to verify sustained absence or stability (e.g., "ensure no new logs appear"). #[derive(Clone)] pub struct LogLineCountOptions { - pub predicate: Arc bool + Send + Sync>, - pub timeout: Duration, - pub wait_until_timeout_elapses: bool, + pub predicate: Arc bool + Send + Sync>, + pub timeout: Duration, + pub wait_until_timeout_elapses: bool, } impl LogLineCountOptions { - pub fn new( - predicate: impl Fn(u32) -> bool + 'static + Send + Sync, - timeout: Duration, - wait_until_timeout_elapses: bool, - ) -> Self { - Self { - predicate: Arc::new(predicate), - timeout, - wait_until_timeout_elapses, - } - } + pub fn new( + predicate: impl Fn(u32) -> bool + 'static + Send + Sync, + timeout: Duration, + wait_until_timeout_elapses: bool, + ) -> Self { + Self { predicate: Arc::new(predicate), timeout, wait_until_timeout_elapses } + } - pub fn no_occurences_within_timeout(timeout: Duration) -> Self { - Self::new(|n| n == 0, timeout, true) - } + pub fn no_occurences_within_timeout(timeout: Duration) -> Self { + Self::new(|n| n == 0, timeout, true) + } } // #[derive(Clone, Debug)] @@ -129,866 +125,849 @@ impl LogLineCountOptions { // } impl NetworkNode { - /// Create a new NetworkNode - pub(crate) fn new>( - name: T, - ws_uri: T, - prometheus_uri: T, - multiaddr: T, - spec: NodeSpec, - inner: DynNode, - ) -> Self { - Self { - name: name.into(), - ws_uri: ws_uri.into(), - prometheus_uri: prometheus_uri.into(), - inner, - spec, - multiaddr: multiaddr.into(), - metrics_cache: Arc::new(Default::default()), - is_running: Arc::new(AtomicBool::new(false)), - } - } + /// Create a new NetworkNode + pub(crate) fn new>( + name: T, + ws_uri: T, + prometheus_uri: T, + multiaddr: T, + spec: NodeSpec, + inner: DynNode, + ) -> Self { + Self { + name: name.into(), + ws_uri: ws_uri.into(), + prometheus_uri: prometheus_uri.into(), + inner, + spec, + multiaddr: multiaddr.into(), + metrics_cache: Arc::new(Default::default()), + is_running: Arc::new(AtomicBool::new(false)), + } + } - pub(crate) fn is_running(&self) -> bool { - self.is_running.load(Ordering::Acquire) - } + pub(crate) fn is_running(&self) -> bool { + self.is_running.load(Ordering::Acquire) + } - pub(crate) fn set_is_running(&self, is_running: bool) { - self.is_running.store(is_running, Ordering::Release); - } + pub(crate) fn set_is_running(&self, is_running: bool) { + self.is_running.store(is_running, Ordering::Release); + } - pub(crate) fn set_multiaddr(&mut self, multiaddr: impl Into) { - self.multiaddr = multiaddr.into(); - } + pub(crate) fn set_multiaddr(&mut self, multiaddr: impl Into) { + self.multiaddr = multiaddr.into(); + } - pub fn name(&self) -> &str { - &self.name - } + pub fn name(&self) -> &str { + &self.name + } - pub fn args(&self) -> Vec<&str> { - self.inner.args() - } + pub fn args(&self) -> Vec<&str> { + self.inner.args() + } - pub fn spec(&self) -> &NodeSpec { - &self.spec - } + pub fn spec(&self) -> &NodeSpec { + &self.spec + } - pub fn ws_uri(&self) -> &str { - &self.ws_uri - } + pub fn ws_uri(&self) -> &str { + &self.ws_uri + } - pub fn multiaddr(&self) -> &str { - self.multiaddr.as_ref() - } + pub fn multiaddr(&self) -> &str { + self.multiaddr.as_ref() + } - // Subxt + // Subxt - /// Get the rpc client for the node - pub async fn rpc(&self) -> Result { - get_client_from_url(&self.ws_uri).await - } + /// Get the rpc client for the node + pub async fn rpc(&self) -> Result { + get_client_from_url(&self.ws_uri).await + } - /// Get the [online client](subxt::client::OnlineClient) for the node - #[deprecated = "Use `wait_client` instead."] - pub async fn client( - &self, - ) -> Result, pezkuwi_subxt::Error> { - self.try_client().await - } + /// Get the [online client](subxt::client::OnlineClient) for the node + #[deprecated = "Use `wait_client` instead."] + pub async fn client( + &self, + ) -> Result, pezkuwi_subxt::Error> { + self.try_client().await + } - /// Try to connect to the node. - /// - /// Most of the time you only want to use [`NetworkNode::wait_client`] that waits for - /// the node to appear before it connects to it. This function directly tries - /// to connect to the node and returns an error if the node is not yet available - /// at that point in time. - /// - /// Returns a [`OnlineClient`] on success. - pub async fn try_client( - &self, - ) -> Result, pezkuwi_subxt::Error> { - get_client_from_url(&self.ws_uri).await - } + /// Try to connect to the node. + /// + /// Most of the time you only want to use [`NetworkNode::wait_client`] that waits for + /// the node to appear before it connects to it. This function directly tries + /// to connect to the node and returns an error if the node is not yet available + /// at that point in time. + /// + /// Returns a [`OnlineClient`] on success. + pub async fn try_client( + &self, + ) -> Result, pezkuwi_subxt::Error> { + get_client_from_url(&self.ws_uri).await + } - /// Wait until get the [online client](subxt::client::OnlineClient) for the node - pub async fn wait_client( - &self, - ) -> Result, anyhow::Error> { - debug!("wait_client ws_uri: {}", self.ws_uri()); - wait_ws_ready(self.ws_uri()) - .await - .map_err(|e| anyhow!("Error awaiting http_client to ws be ready, err: {e}"))?; + /// Wait until get the [online client](subxt::client::OnlineClient) for the node + pub async fn wait_client( + &self, + ) -> Result, anyhow::Error> { + debug!("wait_client ws_uri: {}", self.ws_uri()); + wait_ws_ready(self.ws_uri()) + .await + .map_err(|e| anyhow!("Error awaiting http_client to ws be ready, err: {e}"))?; - self.try_client() - .await - .map_err(|e| anyhow!("Can't create a subxt client, err: {e}")) - } + self.try_client().await.map_err(|e| anyhow!("Can't create a subxt client, err: {e}")) + } - /// Wait until get the [online client](subxt::client::OnlineClient) for the node with a defined timeout - pub async fn wait_client_with_timeout( - &self, - timeout_secs: impl Into, - ) -> Result, anyhow::Error> { - debug!("waiting until subxt client is ready"); - tokio::time::timeout( - Duration::from_secs(timeout_secs.into()), - self.wait_client::(), - ) - .await? - } + /// Wait until get the [online client](subxt::client::OnlineClient) for the node with a defined timeout + pub async fn wait_client_with_timeout( + &self, + timeout_secs: impl Into, + ) -> Result, anyhow::Error> { + debug!("waiting until subxt client is ready"); + tokio::time::timeout(Duration::from_secs(timeout_secs.into()), self.wait_client::()) + .await? + } - // Commands + // Commands - /// Pause the node, this is implemented by pausing the - /// actual process (e.g polkadot) with sending `SIGSTOP` signal - /// - /// Note: If you're using this method with the native provider on the attached network, the live network has to be running - /// with global setting `teardown_on_failure` disabled. - pub async fn pause(&self) -> Result<(), anyhow::Error> { - self.set_is_running(false); - self.inner.pause().await?; - Ok(()) - } + /// Pause the node, this is implemented by pausing the + /// actual process (e.g polkadot) with sending `SIGSTOP` signal + /// + /// Note: If you're using this method with the native provider on the attached network, the live network has to be running + /// with global setting `teardown_on_failure` disabled. + pub async fn pause(&self) -> Result<(), anyhow::Error> { + self.set_is_running(false); + self.inner.pause().await?; + Ok(()) + } - /// Resume the node, this is implemented by resuming the - /// actual process (e.g polkadot) with sending `SIGCONT` signal - /// - /// Note: If you're using this method with the native provider on the attached network, the live network has to be running - /// with global setting `teardown_on_failure` disabled. - pub async fn resume(&self) -> Result<(), anyhow::Error> { - self.set_is_running(true); - self.inner.resume().await?; - Ok(()) - } + /// Resume the node, this is implemented by resuming the + /// actual process (e.g polkadot) with sending `SIGCONT` signal + /// + /// Note: If you're using this method with the native provider on the attached network, the live network has to be running + /// with global setting `teardown_on_failure` disabled. + pub async fn resume(&self) -> Result<(), anyhow::Error> { + self.set_is_running(true); + self.inner.resume().await?; + Ok(()) + } - /// Restart the node using the same `cmd`, `args` and `env` (and same isolated dir) - /// - /// Note: If you're using this method with the native provider on the attached network, the live network has to be running - /// with global setting `teardown_on_failure` disabled. - pub async fn restart(&self, after: Option) -> Result<(), anyhow::Error> { - self.set_is_running(false); - self.inner.restart(after).await?; - self.set_is_running(true); - Ok(()) - } + /// Restart the node using the same `cmd`, `args` and `env` (and same isolated dir) + /// + /// Note: If you're using this method with the native provider on the attached network, the live network has to be running + /// with global setting `teardown_on_failure` disabled. + pub async fn restart(&self, after: Option) -> Result<(), anyhow::Error> { + self.set_is_running(false); + self.inner.restart(after).await?; + self.set_is_running(true); + Ok(()) + } - // Metrics assertions + // Metrics assertions - /// Get metric value 'by name' from Prometheus (exposed by the node) - /// metric name can be: - /// with prefix (e.g: 'polkadot_') - /// with chain attribute (e.g: 'chain=rococo-local') - /// without prefix and/or without chain attribute - pub async fn reports(&self, metric_name: impl Into) -> Result { - let metric_name = metric_name.into(); - // force cache reload - self.fetch_metrics().await?; - // by default we treat not found as 0 (same in v1) - self.metric(&metric_name, true).await - } + /// Get metric value 'by name' from Prometheus (exposed by the node) + /// metric name can be: + /// with prefix (e.g: 'polkadot_') + /// with chain attribute (e.g: 'chain=rococo-local') + /// without prefix and/or without chain attribute + pub async fn reports(&self, metric_name: impl Into) -> Result { + let metric_name = metric_name.into(); + // force cache reload + self.fetch_metrics().await?; + // by default we treat not found as 0 (same in v1) + self.metric(&metric_name, true).await + } - /// Assert on a metric value 'by name' from Prometheus (exposed by the node) - /// metric name can be: - /// with prefix (e.g: 'polkadot_') - /// with chain attribute (e.g: 'chain=rococo-local') - /// without prefix and/or without chain attribute - /// - /// We first try to assert on the value using the cached metrics and - /// if not meet the criteria we reload the cache and check again - pub async fn assert( - &self, - metric_name: impl Into, - value: impl Into, - ) -> Result { - let value: f64 = value.into(); - self.assert_with(metric_name, |v| v == value).await - } + /// Assert on a metric value 'by name' from Prometheus (exposed by the node) + /// metric name can be: + /// with prefix (e.g: 'polkadot_') + /// with chain attribute (e.g: 'chain=rococo-local') + /// without prefix and/or without chain attribute + /// + /// We first try to assert on the value using the cached metrics and + /// if not meet the criteria we reload the cache and check again + pub async fn assert( + &self, + metric_name: impl Into, + value: impl Into, + ) -> Result { + let value: f64 = value.into(); + self.assert_with(metric_name, |v| v == value).await + } - /// Assert on a metric value using a given predicate. - /// See [`NetworkNode::reports`] description for details on metric name. - pub async fn assert_with( - &self, - metric_name: impl Into, - predicate: impl Fn(f64) -> bool, - ) -> Result { - let metric_name = metric_name.into(); - // reload metrics - self.fetch_metrics().await?; - let val = self.metric(&metric_name, true).await?; - trace!("🔎 Current value {val} passed to the predicated?"); - Ok(predicate(val)) - } + /// Assert on a metric value using a given predicate. + /// See [`NetworkNode::reports`] description for details on metric name. + pub async fn assert_with( + &self, + metric_name: impl Into, + predicate: impl Fn(f64) -> bool, + ) -> Result { + let metric_name = metric_name.into(); + // reload metrics + self.fetch_metrics().await?; + let val = self.metric(&metric_name, true).await?; + trace!("🔎 Current value {val} passed to the predicated?"); + Ok(predicate(val)) + } - // Wait methods for metrics + // Wait methods for metrics - /// Wait until a metric value pass the `predicate` - pub async fn wait_metric( - &self, - metric_name: impl Into, - predicate: impl Fn(f64) -> bool, - ) -> Result<(), anyhow::Error> { - let metric_name = metric_name.into(); - debug!("waiting until metric {metric_name} pass the predicate"); - loop { - let res = self.assert_with(&metric_name, &predicate).await; - match res { - Ok(res) => { - if res { - return Ok(()); - } - }, - Err(e) => match e.downcast::() { - Ok(io_err) => { - if !skip_err_while_waiting(&io_err) { - return Err(io_err.into()); - } - }, - Err(other) => { - match other.downcast::() { - Ok(node_err) => { - if !matches!(node_err, NetworkNodeError::MetricNotFound(_)) { - return Err(node_err.into()); - } - }, - Err(other) => return Err(other), - }; - }, - }, - } + /// Wait until a metric value pass the `predicate` + pub async fn wait_metric( + &self, + metric_name: impl Into, + predicate: impl Fn(f64) -> bool, + ) -> Result<(), anyhow::Error> { + let metric_name = metric_name.into(); + debug!("waiting until metric {metric_name} pass the predicate"); + loop { + let res = self.assert_with(&metric_name, &predicate).await; + match res { + Ok(res) => { + if res { + return Ok(()); + } + }, + Err(e) => match e.downcast::() { + Ok(io_err) => { + if !skip_err_while_waiting(&io_err) { + return Err(io_err.into()); + } + }, + Err(other) => { + match other.downcast::() { + Ok(node_err) => { + if !matches!(node_err, NetworkNodeError::MetricNotFound(_)) { + return Err(node_err.into()); + } + }, + Err(other) => return Err(other), + }; + }, + }, + } - // sleep to not spam prometheus - tokio::time::sleep(Duration::from_secs(1)).await; - } - } + // sleep to not spam prometheus + tokio::time::sleep(Duration::from_secs(1)).await; + } + } - /// Wait until a metric value pass the `predicate` - /// with a timeout (secs) - pub async fn wait_metric_with_timeout( - &self, - metric_name: impl Into, - predicate: impl Fn(f64) -> bool, - timeout_secs: impl Into, - ) -> Result<(), anyhow::Error> { - let metric_name = metric_name.into(); - let secs = timeout_secs.into(); - debug!("waiting until metric {metric_name} pass the predicate"); - let res = tokio::time::timeout( - Duration::from_secs(secs), - self.wait_metric(&metric_name, predicate), - ) - .await; + /// Wait until a metric value pass the `predicate` + /// with a timeout (secs) + pub async fn wait_metric_with_timeout( + &self, + metric_name: impl Into, + predicate: impl Fn(f64) -> bool, + timeout_secs: impl Into, + ) -> Result<(), anyhow::Error> { + let metric_name = metric_name.into(); + let secs = timeout_secs.into(); + debug!("waiting until metric {metric_name} pass the predicate"); + let res = tokio::time::timeout( + Duration::from_secs(secs), + self.wait_metric(&metric_name, predicate), + ) + .await; - if let Ok(inner_res) = res { - match inner_res { - Ok(_) => Ok(()), - Err(e) => Err(anyhow!("Error waiting for metric: {e}")), - } - } else { - // timeout - Err(anyhow!( - "Timeout ({secs}), waiting for metric {metric_name} pass the predicate" - )) - } - } + if let Ok(inner_res) = res { + match inner_res { + Ok(_) => Ok(()), + Err(e) => Err(anyhow!("Error waiting for metric: {e}")), + } + } else { + // timeout + Err(anyhow!("Timeout ({secs}), waiting for metric {metric_name} pass the predicate")) + } + } - // Logs + // Logs - /// Get the logs of the node - /// TODO: do we need the `since` param, maybe we could be handy later for loop filtering - pub async fn logs(&self) -> Result { - Ok(self.inner.logs().await?) - } + /// Get the logs of the node + /// TODO: do we need the `since` param, maybe we could be handy later for loop filtering + pub async fn logs(&self) -> Result { + Ok(self.inner.logs().await?) + } - /// Wait until a the number of matching log lines is reach - pub async fn wait_log_line_count( - &self, - pattern: impl Into, - is_glob: bool, - count: usize, - ) -> Result<(), anyhow::Error> { - let pattern = pattern.into(); - let pattern_clone = pattern.clone(); - debug!("waiting until we find pattern {pattern} {count} times"); - let match_fn: BoxedClosure = if is_glob { - Box::new(move |line: &str| Ok(glob_match(&pattern, line))) - } else { - let re = Regex::new(&pattern)?; - Box::new(move |line: &str| re.is_match(line).map_err(|e| anyhow!(e.to_string()))) - }; + /// Wait until a the number of matching log lines is reach + pub async fn wait_log_line_count( + &self, + pattern: impl Into, + is_glob: bool, + count: usize, + ) -> Result<(), anyhow::Error> { + let pattern = pattern.into(); + let pattern_clone = pattern.clone(); + debug!("waiting until we find pattern {pattern} {count} times"); + let match_fn: BoxedClosure = if is_glob { + Box::new(move |line: &str| Ok(glob_match(&pattern, line))) + } else { + let re = Regex::new(&pattern)?; + Box::new(move |line: &str| re.is_match(line).map_err(|e| anyhow!(e.to_string()))) + }; - loop { - let mut q = 0_usize; - let logs = self.logs().await?; - for line in logs.lines() { - trace!("line is {line}"); - if match_fn(line)? { - trace!("pattern {pattern_clone} match in line {line}"); - q += 1; - if q >= count { - return Ok(()); - } - } - } + loop { + let mut q = 0_usize; + let logs = self.logs().await?; + for line in logs.lines() { + trace!("line is {line}"); + if match_fn(line)? { + trace!("pattern {pattern_clone} match in line {line}"); + q += 1; + if q >= count { + return Ok(()); + } + } + } - tokio::time::sleep(Duration::from_secs(2)).await; - } - } + tokio::time::sleep(Duration::from_secs(2)).await; + } + } - /// Waits until the number of matching log lines satisfies a custom condition, - /// optionally waiting for the entire duration of the timeout. - /// - /// This method searches log lines for a given substring or glob pattern, - /// and evaluates the number of matching lines using a user-provided predicate function. - /// Optionally, it can wait for the full timeout duration to ensure the condition - /// holds consistently (e.g., for verifying absence of logs). - /// - /// # Arguments - /// * `substring` - The substring or pattern to match within log lines. - /// * `is_glob` - Whether to treat `substring` as a glob pattern (`true`) or a regex (`false`). - /// * `options` - Configuration for timeout, match count predicate, and full-duration waiting. - /// - /// # Returns - /// * `Ok(LogLineCount::TargetReached(n))` if the predicate was satisfied within the timeout, - /// * `Ok(LogLineCount::TargetFails(n))` if the predicate was not satisfied in time, - /// * `Err(e)` if an error occurred during log retrieval or matching. - /// - /// # Example - /// ```rust - /// # use std::{sync::Arc, time::Duration}; - /// # use provider::NativeProvider; - /// # use support::{fs::local::LocalFileSystem}; - /// # use zombienet_orchestrator::{Orchestrator, network::node::{NetworkNode, LogLineCountOptions}}; - /// # use configuration::NetworkConfig; - /// # async fn example() -> Result<(), anyhow::Error> { - /// # let provider = NativeProvider::new(LocalFileSystem {}); - /// # let orchestrator = Orchestrator::new(LocalFileSystem {}, provider); - /// # let config = NetworkConfig::load_from_toml("config.toml")?; - /// # let network = orchestrator.spawn(config).await?; - /// let node = network.get_node("alice")?; - /// // Wait (up to 10 seconds) until pattern occurs once - /// let options = LogLineCountOptions { - /// predicate: Arc::new(|count| count == 1), - /// timeout: Duration::from_secs(10), - /// wait_until_timeout_elapses: false, - /// }; - /// let result = node - /// .wait_log_line_count_with_timeout("error", false, options) - /// .await?; - /// # Ok(()) - /// # } - /// ``` - pub async fn wait_log_line_count_with_timeout( - &self, - substring: impl Into, - is_glob: bool, - options: LogLineCountOptions, - ) -> Result { - let substring = substring.into(); - debug!( - "waiting until match lines count within {} seconds", - options.timeout.as_secs_f64() - ); + /// Waits until the number of matching log lines satisfies a custom condition, + /// optionally waiting for the entire duration of the timeout. + /// + /// This method searches log lines for a given substring or glob pattern, + /// and evaluates the number of matching lines using a user-provided predicate function. + /// Optionally, it can wait for the full timeout duration to ensure the condition + /// holds consistently (e.g., for verifying absence of logs). + /// + /// # Arguments + /// * `substring` - The substring or pattern to match within log lines. + /// * `is_glob` - Whether to treat `substring` as a glob pattern (`true`) or a regex (`false`). + /// * `options` - Configuration for timeout, match count predicate, and full-duration waiting. + /// + /// # Returns + /// * `Ok(LogLineCount::TargetReached(n))` if the predicate was satisfied within the timeout, + /// * `Ok(LogLineCount::TargetFails(n))` if the predicate was not satisfied in time, + /// * `Err(e)` if an error occurred during log retrieval or matching. + /// + /// # Example + /// ```rust + /// # use std::{sync::Arc, time::Duration}; + /// # use provider::NativeProvider; + /// # use support::{fs::local::LocalFileSystem}; + /// # use zombienet_orchestrator::{Orchestrator, network::node::{NetworkNode, LogLineCountOptions}}; + /// # use configuration::NetworkConfig; + /// # async fn example() -> Result<(), anyhow::Error> { + /// # let provider = NativeProvider::new(LocalFileSystem {}); + /// # let orchestrator = Orchestrator::new(LocalFileSystem {}, provider); + /// # let config = NetworkConfig::load_from_toml("config.toml")?; + /// # let network = orchestrator.spawn(config).await?; + /// let node = network.get_node("alice")?; + /// // Wait (up to 10 seconds) until pattern occurs once + /// let options = LogLineCountOptions { + /// predicate: Arc::new(|count| count == 1), + /// timeout: Duration::from_secs(10), + /// wait_until_timeout_elapses: false, + /// }; + /// let result = node + /// .wait_log_line_count_with_timeout("error", false, options) + /// .await?; + /// # Ok(()) + /// # } + /// ``` + pub async fn wait_log_line_count_with_timeout( + &self, + substring: impl Into, + is_glob: bool, + options: LogLineCountOptions, + ) -> Result { + let substring = substring.into(); + debug!("waiting until match lines count within {} seconds", options.timeout.as_secs_f64()); - let start = tokio::time::Instant::now(); + let start = tokio::time::Instant::now(); - let match_fn: BoxedClosure = if is_glob { - Box::new(move |line: &str| Ok(glob_match(&substring, line))) - } else { - let re = Regex::new(&substring)?; - Box::new(move |line: &str| re.is_match(line).map_err(|e| anyhow!(e.to_string()))) - }; + let match_fn: BoxedClosure = if is_glob { + Box::new(move |line: &str| Ok(glob_match(&substring, line))) + } else { + let re = Regex::new(&substring)?; + Box::new(move |line: &str| re.is_match(line).map_err(|e| anyhow!(e.to_string()))) + }; - if options.wait_until_timeout_elapses { - tokio::time::sleep(options.timeout).await; - } + if options.wait_until_timeout_elapses { + tokio::time::sleep(options.timeout).await; + } - let mut q; - loop { - q = 0_u32; - let logs = self.logs().await?; - for line in logs.lines() { - if match_fn(line)? { - q += 1; + let mut q; + loop { + q = 0_u32; + let logs = self.logs().await?; + for line in logs.lines() { + if match_fn(line)? { + q += 1; - // If `wait_until_timeout_elapses` is set then check the condition just once at the - // end after the whole log file is processed. This is to address the cases when the - // predicate becomes true and false again. - // eg. expected exactly 2 matching lines are expected but 3 are present - if !options.wait_until_timeout_elapses && (options.predicate)(q) { - return Ok(LogLineCount::TargetReached(q)); - } - } - } + // If `wait_until_timeout_elapses` is set then check the condition just once at the + // end after the whole log file is processed. This is to address the cases when the + // predicate becomes true and false again. + // eg. expected exactly 2 matching lines are expected but 3 are present + if !options.wait_until_timeout_elapses && (options.predicate)(q) { + return Ok(LogLineCount::TargetReached(q)); + } + } + } - if start.elapsed() >= options.timeout { - break; - } + if start.elapsed() >= options.timeout { + break; + } - tokio::time::sleep(Duration::from_secs(2)).await; - } + tokio::time::sleep(Duration::from_secs(2)).await; + } - if (options.predicate)(q) { - Ok(LogLineCount::TargetReached(q)) - } else { - Ok(LogLineCount::TargetFailed(q)) - } - } + if (options.predicate)(q) { + Ok(LogLineCount::TargetReached(q)) + } else { + Ok(LogLineCount::TargetFailed(q)) + } + } - async fn fetch_metrics(&self) -> Result<(), anyhow::Error> { - let response = reqwest::get(&self.prometheus_uri).await?; - let metrics = prom_metrics_parser::parse(&response.text().await?)?; - let mut cache = self.metrics_cache.write().await; - *cache = metrics; - Ok(()) - } + async fn fetch_metrics(&self) -> Result<(), anyhow::Error> { + let response = reqwest::get(&self.prometheus_uri).await?; + let metrics = prom_metrics_parser::parse(&response.text().await?)?; + let mut cache = self.metrics_cache.write().await; + *cache = metrics; + Ok(()) + } - /// Query individual metric by name - async fn metric( - &self, - metric_name: &str, - treat_not_found_as_zero: bool, - ) -> Result { - let mut metrics_map = self.metrics_cache.read().await; - if metrics_map.is_empty() { - // reload metrics - drop(metrics_map); - self.fetch_metrics().await?; - metrics_map = self.metrics_cache.read().await; - } + /// Query individual metric by name + async fn metric( + &self, + metric_name: &str, + treat_not_found_as_zero: bool, + ) -> Result { + let mut metrics_map = self.metrics_cache.read().await; + if metrics_map.is_empty() { + // reload metrics + drop(metrics_map); + self.fetch_metrics().await?; + metrics_map = self.metrics_cache.read().await; + } - if let Some(val) = metrics_map.get(metric_name) { - Ok(*val) - } else if treat_not_found_as_zero { - Ok(0_f64) - } else { - Err(NetworkNodeError::MetricNotFound(metric_name.into()).into()) - } - } + if let Some(val) = metrics_map.get(metric_name) { + Ok(*val) + } else if treat_not_found_as_zero { + Ok(0_f64) + } else { + Err(NetworkNodeError::MetricNotFound(metric_name.into()).into()) + } + } - /// Waits given number of seconds until node reports that it is up and running, which - /// is determined by metric 'process_start_time_seconds', which should appear, - /// when node finished booting up. - /// - /// - /// # Arguments - /// * `timeout_secs` - The number of seconds to wait. - /// - /// # Returns - /// * `Ok()` if the node is up before timeout occured. - /// * `Err(e)` if timeout or other error occurred while waiting. - pub async fn wait_until_is_up( - &self, - timeout_secs: impl Into, - ) -> Result<(), anyhow::Error> { - self.wait_metric_with_timeout("process_start_time_seconds", |b| b >= 1.0, timeout_secs) - .await - .map_err(|err| anyhow::anyhow!("{}: {:?}", self.name(), err)) - } + /// Waits given number of seconds until node reports that it is up and running, which + /// is determined by metric 'process_start_time_seconds', which should appear, + /// when node finished booting up. + /// + /// + /// # Arguments + /// * `timeout_secs` - The number of seconds to wait. + /// + /// # Returns + /// * `Ok()` if the node is up before timeout occured. + /// * `Err(e)` if timeout or other error occurred while waiting. + pub async fn wait_until_is_up( + &self, + timeout_secs: impl Into, + ) -> Result<(), anyhow::Error> { + self.wait_metric_with_timeout("process_start_time_seconds", |b| b >= 1.0, timeout_secs) + .await + .map_err(|err| anyhow::anyhow!("{}: {:?}", self.name(), err)) + } } impl std::fmt::Debug for NetworkNode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("NetworkNode") - .field("inner", &"inner_skipped") - .field("spec", &self.spec) - .field("name", &self.name) - .field("ws_uri", &self.ws_uri) - .field("prometheus_uri", &self.prometheus_uri) - .finish() - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("NetworkNode") + .field("inner", &"inner_skipped") + .field("spec", &self.spec) + .field("name", &self.name) + .field("ws_uri", &self.ws_uri) + .field("prometheus_uri", &self.prometheus_uri) + .finish() + } } fn serialize_provider_node(node: &DynNode, serializer: S) -> Result where - S: Serializer, + S: Serializer, { - erased_serde::serialize(node.as_ref(), serializer) + erased_serde::serialize(node.as_ref(), serializer) } // TODO: mock and impl more unit tests #[cfg(test)] mod tests { - use std::{ - path::{Path, PathBuf}, - sync::{Arc, Mutex}, - }; - - use async_trait::async_trait; - use provider::{types::*, ProviderError, ProviderNode}; - - use super::*; - - #[derive(Serialize)] - struct MockNode { - logs: Arc>>, - } - - impl MockNode { - fn new() -> Self { - Self { - logs: Arc::new(Mutex::new(vec![])), - } - } - - fn logs_push(&self, lines: Vec>) { - self.logs - .lock() - .unwrap() - .extend(lines.into_iter().map(|l| l.into())); - } - } - - #[async_trait] - impl ProviderNode for MockNode { - fn name(&self) -> &str { - todo!() - } - - fn args(&self) -> Vec<&str> { - todo!() - } - - fn base_dir(&self) -> &PathBuf { - todo!() - } - - fn config_dir(&self) -> &PathBuf { - todo!() - } - - fn data_dir(&self) -> &PathBuf { - todo!() - } - - fn relay_data_dir(&self) -> &PathBuf { - todo!() - } - - fn scripts_dir(&self) -> &PathBuf { - todo!() - } - - fn log_path(&self) -> &PathBuf { - todo!() - } - - fn log_cmd(&self) -> String { - todo!() - } - - fn path_in_node(&self, _file: &Path) -> PathBuf { - todo!() - } - - async fn logs(&self) -> Result { - Ok(self.logs.lock().unwrap().join("\n")) - } - - async fn dump_logs(&self, _local_dest: PathBuf) -> Result<(), ProviderError> { - todo!() - } - - async fn run_command( - &self, - _options: RunCommandOptions, - ) -> Result { - todo!() - } - - async fn run_script( - &self, - _options: RunScriptOptions, - ) -> Result { - todo!() - } - - async fn send_file( - &self, - _local_file_path: &Path, - _remote_file_path: &Path, - _mode: &str, - ) -> Result<(), ProviderError> { - todo!() - } - - async fn receive_file( - &self, - _remote_file_path: &Path, - _local_file_path: &Path, - ) -> Result<(), ProviderError> { - todo!() - } - - async fn pause(&self) -> Result<(), ProviderError> { - todo!() - } - - async fn resume(&self) -> Result<(), ProviderError> { - todo!() - } - - async fn restart(&self, _after: Option) -> Result<(), ProviderError> { - todo!() - } - - async fn destroy(&self) -> Result<(), ProviderError> { - todo!() - } - } - - #[tokio::test(flavor = "multi_thread")] - async fn test_wait_log_count_target_reached_immediately() -> Result<(), anyhow::Error> { - let mock_provider = Arc::new(MockNode::new()); - let mock_node = NetworkNode::new( - "node1", - "ws_uri", - "prometheus_uri", - "multiaddr", - NodeSpec::default(), - mock_provider.clone(), - ); - - mock_provider.logs_push(vec![ - "system booting", - "stub line 1", - "stub line 2", - "system ready", - ]); - - // Wait (up to 10 seconds) until pattern occurs once - let options = LogLineCountOptions { - predicate: Arc::new(|n| n == 1), - timeout: Duration::from_secs(10), - wait_until_timeout_elapses: false, - }; - - let log_line_count = mock_node - .wait_log_line_count_with_timeout("system ready", false, options) - .await?; - - assert!(matches!(log_line_count, LogLineCount::TargetReached(1))); - - Ok(()) - } - - #[tokio::test(flavor = "multi_thread")] - async fn test_wait_log_count_target_reached_after_delay() -> Result<(), anyhow::Error> { - let mock_provider = Arc::new(MockNode::new()); - let mock_node = NetworkNode::new( - "node1", - "ws_uri", - "prometheus_uri", - "multiaddr", - NodeSpec::default(), - mock_provider.clone(), - ); - - mock_provider.logs_push(vec![ - "system booting", - "stub line 1", - "stub line 2", - "system ready", - ]); - - // Wait (up to 4 seconds) until pattern occurs twice - let options = LogLineCountOptions { - predicate: Arc::new(|n| n == 2), - timeout: Duration::from_secs(4), - wait_until_timeout_elapses: false, - }; - - let task = tokio::spawn({ - async move { - mock_node - .wait_log_line_count_with_timeout("system ready", false, options) - .await - .unwrap() - } - }); - - tokio::time::sleep(Duration::from_secs(2)).await; - - mock_provider.logs_push(vec!["system ready"]); - - let log_line_count = task.await?; - - assert!(matches!(log_line_count, LogLineCount::TargetReached(2))); - - Ok(()) - } - - #[tokio::test(flavor = "multi_thread")] - async fn test_wait_log_count_target_failed_timeout() -> Result<(), anyhow::Error> { - let mock_provider = Arc::new(MockNode::new()); - let mock_node = NetworkNode::new( - "node1", - "ws_uri", - "prometheus_uri", - "multiaddr", - NodeSpec::default(), - mock_provider.clone(), - ); - - mock_provider.logs_push(vec![ - "system booting", - "stub line 1", - "stub line 2", - "system ready", - ]); - - // Wait (up to 2 seconds) until pattern occurs twice - let options = LogLineCountOptions { - predicate: Arc::new(|n| n == 2), - timeout: Duration::from_secs(2), - wait_until_timeout_elapses: false, - }; - - let log_line_count = mock_node - .wait_log_line_count_with_timeout("system ready", false, options) - .await?; - - assert!(matches!(log_line_count, LogLineCount::TargetFailed(1))); - - Ok(()) - } - - #[tokio::test(flavor = "multi_thread")] - async fn test_wait_log_count_target_failed_exceeded() -> Result<(), anyhow::Error> { - let mock_provider = Arc::new(MockNode::new()); - let mock_node = NetworkNode::new( - "node1", - "ws_uri", - "prometheus_uri", - "multiaddr", - NodeSpec::default(), - mock_provider.clone(), - ); - - mock_provider.logs_push(vec![ - "system booting", - "stub line 1", - "stub line 2", - "system ready", - ]); - - // Wait until timeout and check if pattern occurs exactly twice - let options = LogLineCountOptions { - predicate: Arc::new(|n| n == 2), - timeout: Duration::from_secs(2), - wait_until_timeout_elapses: true, - }; - - let task = tokio::spawn({ - async move { - mock_node - .wait_log_line_count_with_timeout("system ready", false, options) - .await - .unwrap() - } - }); - - tokio::time::sleep(Duration::from_secs(1)).await; - - mock_provider.logs_push(vec!["system ready"]); - mock_provider.logs_push(vec!["system ready"]); - - let log_line_count = task.await?; - - assert!(matches!(log_line_count, LogLineCount::TargetFailed(3))); - - Ok(()) - } - - #[tokio::test(flavor = "multi_thread")] - async fn test_wait_log_count_target_reached_no_occurences() -> Result<(), anyhow::Error> { - let mock_provider = Arc::new(MockNode::new()); - let mock_node = NetworkNode::new( - "node1", - "ws_uri", - "prometheus_uri", - "multiaddr", - NodeSpec::default(), - mock_provider.clone(), - ); - - mock_provider.logs_push(vec!["system booting", "stub line 1", "stub line 2"]); - - let task = tokio::spawn({ - async move { - mock_node - .wait_log_line_count_with_timeout( - "system ready", - false, - // Wait until timeout and make sure pattern occurred zero times - LogLineCountOptions::no_occurences_within_timeout(Duration::from_secs(2)), - ) - .await - .unwrap() - } - }); - - tokio::time::sleep(Duration::from_secs(1)).await; - - mock_provider.logs_push(vec!["stub line 3"]); - - assert!(task.await?.success()); - - Ok(()) - } - - #[tokio::test(flavor = "multi_thread")] - async fn test_wait_log_count_target_reached_in_range() -> Result<(), anyhow::Error> { - let mock_provider = Arc::new(MockNode::new()); - let mock_node = NetworkNode::new( - "node1", - "ws_uri", - "prometheus_uri", - "multiaddr", - NodeSpec::default(), - mock_provider.clone(), - ); - - mock_provider.logs_push(vec!["system booting", "stub line 1", "stub line 2"]); - - // Wait until timeout and make sure pattern occurrence count is in range between 2 and 5 - let options = LogLineCountOptions { - predicate: Arc::new(|n| (2..=5).contains(&n)), - timeout: Duration::from_secs(2), - wait_until_timeout_elapses: true, - }; - - let task = tokio::spawn({ - async move { - mock_node - .wait_log_line_count_with_timeout("system ready", false, options) - .await - .unwrap() - } - }); - - tokio::time::sleep(Duration::from_secs(1)).await; - - mock_provider.logs_push(vec!["system ready", "system ready", "system ready"]); - - assert!(task.await?.success()); - - Ok(()) - } - - #[tokio::test(flavor = "multi_thread")] - async fn test_wait_log_count_with_timeout_with_lookahead_regex() -> Result<(), anyhow::Error> { - let mock_provider = Arc::new(MockNode::new()); - let mock_node = NetworkNode::new( - "node1", - "ws_uri", - "prometheus_uri", - "multiaddr", - NodeSpec::default(), - mock_provider.clone(), - ); - - mock_provider.logs_push(vec![ + use std::{ + path::{Path, PathBuf}, + sync::{Arc, Mutex}, + }; + + use async_trait::async_trait; + use provider::{types::*, ProviderError, ProviderNode}; + + use super::*; + + #[derive(Serialize)] + struct MockNode { + logs: Arc>>, + } + + impl MockNode { + fn new() -> Self { + Self { logs: Arc::new(Mutex::new(vec![])) } + } + + fn logs_push(&self, lines: Vec>) { + self.logs.lock().unwrap().extend(lines.into_iter().map(|l| l.into())); + } + } + + #[async_trait] + impl ProviderNode for MockNode { + fn name(&self) -> &str { + todo!() + } + + fn args(&self) -> Vec<&str> { + todo!() + } + + fn base_dir(&self) -> &PathBuf { + todo!() + } + + fn config_dir(&self) -> &PathBuf { + todo!() + } + + fn data_dir(&self) -> &PathBuf { + todo!() + } + + fn relay_data_dir(&self) -> &PathBuf { + todo!() + } + + fn scripts_dir(&self) -> &PathBuf { + todo!() + } + + fn log_path(&self) -> &PathBuf { + todo!() + } + + fn log_cmd(&self) -> String { + todo!() + } + + fn path_in_node(&self, _file: &Path) -> PathBuf { + todo!() + } + + async fn logs(&self) -> Result { + Ok(self.logs.lock().unwrap().join("\n")) + } + + async fn dump_logs(&self, _local_dest: PathBuf) -> Result<(), ProviderError> { + todo!() + } + + async fn run_command( + &self, + _options: RunCommandOptions, + ) -> Result { + todo!() + } + + async fn run_script( + &self, + _options: RunScriptOptions, + ) -> Result { + todo!() + } + + async fn send_file( + &self, + _local_file_path: &Path, + _remote_file_path: &Path, + _mode: &str, + ) -> Result<(), ProviderError> { + todo!() + } + + async fn receive_file( + &self, + _remote_file_path: &Path, + _local_file_path: &Path, + ) -> Result<(), ProviderError> { + todo!() + } + + async fn pause(&self) -> Result<(), ProviderError> { + todo!() + } + + async fn resume(&self) -> Result<(), ProviderError> { + todo!() + } + + async fn restart(&self, _after: Option) -> Result<(), ProviderError> { + todo!() + } + + async fn destroy(&self) -> Result<(), ProviderError> { + todo!() + } + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_wait_log_count_target_reached_immediately() -> Result<(), anyhow::Error> { + let mock_provider = Arc::new(MockNode::new()); + let mock_node = NetworkNode::new( + "node1", + "ws_uri", + "prometheus_uri", + "multiaddr", + NodeSpec::default(), + mock_provider.clone(), + ); + + mock_provider.logs_push(vec![ + "system booting", + "stub line 1", + "stub line 2", + "system ready", + ]); + + // Wait (up to 10 seconds) until pattern occurs once + let options = LogLineCountOptions { + predicate: Arc::new(|n| n == 1), + timeout: Duration::from_secs(10), + wait_until_timeout_elapses: false, + }; + + let log_line_count = + mock_node.wait_log_line_count_with_timeout("system ready", false, options).await?; + + assert!(matches!(log_line_count, LogLineCount::TargetReached(1))); + + Ok(()) + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_wait_log_count_target_reached_after_delay() -> Result<(), anyhow::Error> { + let mock_provider = Arc::new(MockNode::new()); + let mock_node = NetworkNode::new( + "node1", + "ws_uri", + "prometheus_uri", + "multiaddr", + NodeSpec::default(), + mock_provider.clone(), + ); + + mock_provider.logs_push(vec![ + "system booting", + "stub line 1", + "stub line 2", + "system ready", + ]); + + // Wait (up to 4 seconds) until pattern occurs twice + let options = LogLineCountOptions { + predicate: Arc::new(|n| n == 2), + timeout: Duration::from_secs(4), + wait_until_timeout_elapses: false, + }; + + let task = tokio::spawn({ + async move { + mock_node + .wait_log_line_count_with_timeout("system ready", false, options) + .await + .unwrap() + } + }); + + tokio::time::sleep(Duration::from_secs(2)).await; + + mock_provider.logs_push(vec!["system ready"]); + + let log_line_count = task.await?; + + assert!(matches!(log_line_count, LogLineCount::TargetReached(2))); + + Ok(()) + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_wait_log_count_target_failed_timeout() -> Result<(), anyhow::Error> { + let mock_provider = Arc::new(MockNode::new()); + let mock_node = NetworkNode::new( + "node1", + "ws_uri", + "prometheus_uri", + "multiaddr", + NodeSpec::default(), + mock_provider.clone(), + ); + + mock_provider.logs_push(vec![ + "system booting", + "stub line 1", + "stub line 2", + "system ready", + ]); + + // Wait (up to 2 seconds) until pattern occurs twice + let options = LogLineCountOptions { + predicate: Arc::new(|n| n == 2), + timeout: Duration::from_secs(2), + wait_until_timeout_elapses: false, + }; + + let log_line_count = + mock_node.wait_log_line_count_with_timeout("system ready", false, options).await?; + + assert!(matches!(log_line_count, LogLineCount::TargetFailed(1))); + + Ok(()) + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_wait_log_count_target_failed_exceeded() -> Result<(), anyhow::Error> { + let mock_provider = Arc::new(MockNode::new()); + let mock_node = NetworkNode::new( + "node1", + "ws_uri", + "prometheus_uri", + "multiaddr", + NodeSpec::default(), + mock_provider.clone(), + ); + + mock_provider.logs_push(vec![ + "system booting", + "stub line 1", + "stub line 2", + "system ready", + ]); + + // Wait until timeout and check if pattern occurs exactly twice + let options = LogLineCountOptions { + predicate: Arc::new(|n| n == 2), + timeout: Duration::from_secs(2), + wait_until_timeout_elapses: true, + }; + + let task = tokio::spawn({ + async move { + mock_node + .wait_log_line_count_with_timeout("system ready", false, options) + .await + .unwrap() + } + }); + + tokio::time::sleep(Duration::from_secs(1)).await; + + mock_provider.logs_push(vec!["system ready"]); + mock_provider.logs_push(vec!["system ready"]); + + let log_line_count = task.await?; + + assert!(matches!(log_line_count, LogLineCount::TargetFailed(3))); + + Ok(()) + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_wait_log_count_target_reached_no_occurences() -> Result<(), anyhow::Error> { + let mock_provider = Arc::new(MockNode::new()); + let mock_node = NetworkNode::new( + "node1", + "ws_uri", + "prometheus_uri", + "multiaddr", + NodeSpec::default(), + mock_provider.clone(), + ); + + mock_provider.logs_push(vec!["system booting", "stub line 1", "stub line 2"]); + + let task = tokio::spawn({ + async move { + mock_node + .wait_log_line_count_with_timeout( + "system ready", + false, + // Wait until timeout and make sure pattern occurred zero times + LogLineCountOptions::no_occurences_within_timeout(Duration::from_secs(2)), + ) + .await + .unwrap() + } + }); + + tokio::time::sleep(Duration::from_secs(1)).await; + + mock_provider.logs_push(vec!["stub line 3"]); + + assert!(task.await?.success()); + + Ok(()) + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_wait_log_count_target_reached_in_range() -> Result<(), anyhow::Error> { + let mock_provider = Arc::new(MockNode::new()); + let mock_node = NetworkNode::new( + "node1", + "ws_uri", + "prometheus_uri", + "multiaddr", + NodeSpec::default(), + mock_provider.clone(), + ); + + mock_provider.logs_push(vec!["system booting", "stub line 1", "stub line 2"]); + + // Wait until timeout and make sure pattern occurrence count is in range between 2 and 5 + let options = LogLineCountOptions { + predicate: Arc::new(|n| (2..=5).contains(&n)), + timeout: Duration::from_secs(2), + wait_until_timeout_elapses: true, + }; + + let task = tokio::spawn({ + async move { + mock_node + .wait_log_line_count_with_timeout("system ready", false, options) + .await + .unwrap() + } + }); + + tokio::time::sleep(Duration::from_secs(1)).await; + + mock_provider.logs_push(vec!["system ready", "system ready", "system ready"]); + + assert!(task.await?.success()); + + Ok(()) + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_wait_log_count_with_timeout_with_lookahead_regex() -> Result<(), anyhow::Error> { + let mock_provider = Arc::new(MockNode::new()); + let mock_node = NetworkNode::new( + "node1", + "ws_uri", + "prometheus_uri", + "multiaddr", + NodeSpec::default(), + mock_provider.clone(), + ); + + mock_provider.logs_push(vec![ "system booting", "stub line 1", // this line should not match @@ -996,53 +975,53 @@ mod tests { "stub line 2" ]); - let options = LogLineCountOptions { - predicate: Arc::new(|n| n == 1), - timeout: Duration::from_secs(3), - wait_until_timeout_elapses: true, - }; + let options = LogLineCountOptions { + predicate: Arc::new(|n| n == 1), + timeout: Duration::from_secs(3), + wait_until_timeout_elapses: true, + }; - let task = tokio::spawn({ - async move { - mock_node - .wait_log_line_count_with_timeout( - "error(?! importing block .*: block has an unknown parent)", - false, - options, - ) - .await - .unwrap() - } - }); + let task = tokio::spawn({ + async move { + mock_node + .wait_log_line_count_with_timeout( + "error(?! importing block .*: block has an unknown parent)", + false, + options, + ) + .await + .unwrap() + } + }); - tokio::time::sleep(Duration::from_secs(1)).await; + tokio::time::sleep(Duration::from_secs(1)).await; - mock_provider.logs_push(vec![ - "system ready", - // this line should match - "system error", - "system ready", - ]); + mock_provider.logs_push(vec![ + "system ready", + // this line should match + "system error", + "system ready", + ]); - assert!(task.await?.success()); + assert!(task.await?.success()); - Ok(()) - } + Ok(()) + } - #[tokio::test(flavor = "multi_thread")] - async fn test_wait_log_count_with_timeout_with_lookahead_regex_fails( - ) -> Result<(), anyhow::Error> { - let mock_provider = Arc::new(MockNode::new()); - let mock_node = NetworkNode::new( - "node1", - "ws_uri", - "prometheus_uri", - "multiaddr", - NodeSpec::default(), - mock_provider.clone(), - ); + #[tokio::test(flavor = "multi_thread")] + async fn test_wait_log_count_with_timeout_with_lookahead_regex_fails( + ) -> Result<(), anyhow::Error> { + let mock_provider = Arc::new(MockNode::new()); + let mock_node = NetworkNode::new( + "node1", + "ws_uri", + "prometheus_uri", + "multiaddr", + NodeSpec::default(), + mock_provider.clone(), + ); - mock_provider.logs_push(vec![ + mock_provider.logs_push(vec![ "system booting", "stub line 1", // this line should not match @@ -1050,47 +1029,47 @@ mod tests { "stub line 2" ]); - let options = LogLineCountOptions { - predicate: Arc::new(|n| n == 1), - timeout: Duration::from_secs(6), - wait_until_timeout_elapses: true, - }; + let options = LogLineCountOptions { + predicate: Arc::new(|n| n == 1), + timeout: Duration::from_secs(6), + wait_until_timeout_elapses: true, + }; - let task = tokio::spawn({ - async move { - mock_node - .wait_log_line_count_with_timeout( - "error(?! importing block .*: block has an unknown parent)", - false, - options, - ) - .await - .unwrap() - } - }); + let task = tokio::spawn({ + async move { + mock_node + .wait_log_line_count_with_timeout( + "error(?! importing block .*: block has an unknown parent)", + false, + options, + ) + .await + .unwrap() + } + }); - tokio::time::sleep(Duration::from_secs(1)).await; + tokio::time::sleep(Duration::from_secs(1)).await; - mock_provider.logs_push(vec!["system ready", "system ready"]); + mock_provider.logs_push(vec!["system ready", "system ready"]); - assert!(!task.await?.success()); + assert!(!task.await?.success()); - Ok(()) - } + Ok(()) + } - #[tokio::test(flavor = "multi_thread")] - async fn test_wait_log_count_with_lockahead_regex() -> Result<(), anyhow::Error> { - let mock_provider = Arc::new(MockNode::new()); - let mock_node = NetworkNode::new( - "node1", - "ws_uri", - "prometheus_uri", - "multiaddr", - NodeSpec::default(), - mock_provider.clone(), - ); + #[tokio::test(flavor = "multi_thread")] + async fn test_wait_log_count_with_lockahead_regex() -> Result<(), anyhow::Error> { + let mock_provider = Arc::new(MockNode::new()); + let mock_node = NetworkNode::new( + "node1", + "ws_uri", + "prometheus_uri", + "multiaddr", + NodeSpec::default(), + mock_provider.clone(), + ); - mock_provider.logs_push(vec![ + mock_provider.logs_push(vec![ "system booting", "stub line 1", // this line should not match @@ -1098,46 +1077,46 @@ mod tests { "stub line 2" ]); - let task = tokio::spawn({ - async move { - mock_node - .wait_log_line_count( - "error(?! importing block .*: block has an unknown parent)", - false, - 1, - ) - .await - .unwrap() - } - }); + let task = tokio::spawn({ + async move { + mock_node + .wait_log_line_count( + "error(?! importing block .*: block has an unknown parent)", + false, + 1, + ) + .await + .unwrap() + } + }); - tokio::time::sleep(Duration::from_secs(1)).await; + tokio::time::sleep(Duration::from_secs(1)).await; - mock_provider.logs_push(vec![ - "system ready", - // this line should match - "system error", - "system ready", - ]); + mock_provider.logs_push(vec![ + "system ready", + // this line should match + "system error", + "system ready", + ]); - assert!(task.await.is_ok()); + assert!(task.await.is_ok()); - Ok(()) - } + Ok(()) + } - #[tokio::test(flavor = "multi_thread")] - async fn test_wait_log_count_with_lookahead_regex_fails() -> Result<(), anyhow::Error> { - let mock_provider = Arc::new(MockNode::new()); - let mock_node = NetworkNode::new( - "node1", - "ws_uri", - "prometheus_uri", - "multiaddr", - NodeSpec::default(), - mock_provider.clone(), - ); + #[tokio::test(flavor = "multi_thread")] + async fn test_wait_log_count_with_lookahead_regex_fails() -> Result<(), anyhow::Error> { + let mock_provider = Arc::new(MockNode::new()); + let mock_node = NetworkNode::new( + "node1", + "ws_uri", + "prometheus_uri", + "multiaddr", + NodeSpec::default(), + mock_provider.clone(), + ); - mock_provider.logs_push(vec![ + mock_provider.logs_push(vec![ "system booting", "stub line 1", // this line should not match @@ -1145,32 +1124,32 @@ mod tests { "stub line 2" ]); - let options = LogLineCountOptions { - predicate: Arc::new(|count| count == 1), - timeout: Duration::from_secs(2), - wait_until_timeout_elapses: true, - }; + let options = LogLineCountOptions { + predicate: Arc::new(|count| count == 1), + timeout: Duration::from_secs(2), + wait_until_timeout_elapses: true, + }; - let task = tokio::spawn({ - async move { - // we expect no match, thus wait with timeout - mock_node - .wait_log_line_count_with_timeout( - "error(?! importing block .*: block has an unknown parent)", - false, - options, - ) - .await - .unwrap() - } - }); + let task = tokio::spawn({ + async move { + // we expect no match, thus wait with timeout + mock_node + .wait_log_line_count_with_timeout( + "error(?! importing block .*: block has an unknown parent)", + false, + options, + ) + .await + .unwrap() + } + }); - tokio::time::sleep(Duration::from_secs(1)).await; + tokio::time::sleep(Duration::from_secs(1)).await; - mock_provider.logs_push(vec!["system ready", "system ready"]); + mock_provider.logs_push(vec!["system ready", "system ready"]); - assert!(!task.await?.success()); + assert!(!task.await?.success()); - Ok(()) - } + Ok(()) + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network/relaychain.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network/relaychain.rs index 7cea074d..d0cb06b9 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network/relaychain.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network/relaychain.rs @@ -6,70 +6,61 @@ use serde::{Deserialize, Serialize}; use super::node::NetworkNode; use crate::{ - network::chain_upgrade::ChainUpgrade, shared::types::RuntimeUpgradeOptions, - utils::default_as_empty_vec, + network::chain_upgrade::ChainUpgrade, shared::types::RuntimeUpgradeOptions, + utils::default_as_empty_vec, }; #[derive(Debug, Serialize, Deserialize)] pub struct Relaychain { - pub(crate) chain: String, - pub(crate) chain_id: String, - pub(crate) chain_spec_path: PathBuf, - #[serde(default, deserialize_with = "default_as_empty_vec")] - pub(crate) nodes: Vec, + pub(crate) chain: String, + pub(crate) chain_id: String, + pub(crate) chain_spec_path: PathBuf, + #[serde(default, deserialize_with = "default_as_empty_vec")] + pub(crate) nodes: Vec, } #[derive(Debug, Deserialize)] pub(crate) struct RawRelaychain { - #[serde(flatten)] - pub(crate) inner: Relaychain, - pub(crate) nodes: serde_json::Value, + #[serde(flatten)] + pub(crate) inner: Relaychain, + pub(crate) nodes: serde_json::Value, } #[async_trait] impl ChainUpgrade for Relaychain { - async fn runtime_upgrade(&self, options: RuntimeUpgradeOptions) -> Result<(), anyhow::Error> { - // check if the node is valid first - let node = if let Some(node_name) = &options.node_name { - if let Some(node) = self - .nodes() - .into_iter() - .find(|node| node.name() == node_name) - { - node - } else { - return Err(anyhow!("Node: {node_name} is not part of the set of nodes")); - } - } else { - // take the first node - if let Some(node) = self.nodes().first() { - node - } else { - return Err(anyhow!("chain doesn't have any node!")); - } - }; + async fn runtime_upgrade(&self, options: RuntimeUpgradeOptions) -> Result<(), anyhow::Error> { + // check if the node is valid first + let node = if let Some(node_name) = &options.node_name { + if let Some(node) = self.nodes().into_iter().find(|node| node.name() == node_name) { + node + } else { + return Err(anyhow!("Node: {node_name} is not part of the set of nodes")); + } + } else { + // take the first node + if let Some(node) = self.nodes().first() { + node + } else { + return Err(anyhow!("chain doesn't have any node!")); + } + }; - self.perform_runtime_upgrade(node, options).await - } + self.perform_runtime_upgrade(node, options).await + } } impl Relaychain { - pub(crate) fn new(chain: String, chain_id: String, chain_spec_path: PathBuf) -> Self { - Self { - chain, - chain_id, - chain_spec_path, - nodes: Default::default(), - } - } + pub(crate) fn new(chain: String, chain_id: String, chain_spec_path: PathBuf) -> Self { + Self { chain, chain_id, chain_spec_path, nodes: Default::default() } + } - // Public API - pub fn nodes(&self) -> Vec<&NetworkNode> { - self.nodes.iter().collect() - } + // Public API + pub fn nodes(&self) -> Vec<&NetworkNode> { + self.nodes.iter().collect() + } - /// Get chain name - pub fn chain(&self) -> &str { - &self.chain - } + /// Get chain name + pub fn chain(&self) -> &str { + &self.chain + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network/teyrchain.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network/teyrchain.rs index e0ff315a..f9cb54cd 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network/teyrchain.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network/teyrchain.rs @@ -1,6 +1,6 @@ use std::{ - path::{Path, PathBuf}, - str::FromStr, + path::{Path, PathBuf}, + str::FromStr, }; use anyhow::anyhow; @@ -14,317 +14,282 @@ use tracing::info; use super::{chain_upgrade::ChainUpgrade, node::NetworkNode}; use crate::{ - network_spec::teyrchain::TeyrchainSpec, - shared::types::{RegisterParachainOptions, RuntimeUpgradeOptions}, - tx_helper::client::get_client_from_url, - utils::default_as_empty_vec, - ScopedFilesystem, + network_spec::teyrchain::TeyrchainSpec, + shared::types::{RegisterParachainOptions, RuntimeUpgradeOptions}, + tx_helper::client::get_client_from_url, + utils::default_as_empty_vec, + ScopedFilesystem, }; #[derive(Debug, Serialize, Deserialize)] pub struct Parachain { - pub(crate) chain: Option, - pub(crate) para_id: u32, - // unique_id is internally used to allow multiple parachains with the same id - // See `ParachainConfig` for more details - pub(crate) unique_id: String, - pub(crate) chain_id: Option, - pub(crate) chain_spec_path: Option, - #[serde(default, deserialize_with = "default_as_empty_vec")] - pub(crate) collators: Vec, - pub(crate) files_to_inject: Vec, - pub(crate) bootnodes_addresses: Vec, + pub(crate) chain: Option, + pub(crate) para_id: u32, + // unique_id is internally used to allow multiple parachains with the same id + // See `ParachainConfig` for more details + pub(crate) unique_id: String, + pub(crate) chain_id: Option, + pub(crate) chain_spec_path: Option, + #[serde(default, deserialize_with = "default_as_empty_vec")] + pub(crate) collators: Vec, + pub(crate) files_to_inject: Vec, + pub(crate) bootnodes_addresses: Vec, } #[derive(Debug, Deserialize)] pub(crate) struct RawParachain { - #[serde(flatten)] - pub(crate) inner: Parachain, - pub(crate) collators: serde_json::Value, + #[serde(flatten)] + pub(crate) inner: Parachain, + pub(crate) collators: serde_json::Value, } #[async_trait] impl ChainUpgrade for Parachain { - async fn runtime_upgrade(&self, options: RuntimeUpgradeOptions) -> Result<(), anyhow::Error> { - // check if the node is valid first - let node = if let Some(node_name) = &options.node_name { - if let Some(node) = self - .collators() - .into_iter() - .find(|node| node.name() == node_name) - { - node - } else { - return Err(anyhow!("Node: {node_name} is not part of the set of nodes")); - } - } else { - // take the first node - if let Some(node) = self.collators().first() { - node - } else { - return Err(anyhow!("chain doesn't have any node!")); - } - }; + async fn runtime_upgrade(&self, options: RuntimeUpgradeOptions) -> Result<(), anyhow::Error> { + // check if the node is valid first + let node = if let Some(node_name) = &options.node_name { + if let Some(node) = self.collators().into_iter().find(|node| node.name() == node_name) { + node + } else { + return Err(anyhow!("Node: {node_name} is not part of the set of nodes")); + } + } else { + // take the first node + if let Some(node) = self.collators().first() { + node + } else { + return Err(anyhow!("chain doesn't have any node!")); + } + }; - self.perform_runtime_upgrade(node, options).await - } + self.perform_runtime_upgrade(node, options).await + } } impl Parachain { - pub(crate) fn new(para_id: u32, unique_id: impl Into) -> Self { - Self { - chain: None, - para_id, - unique_id: unique_id.into(), - chain_id: None, - chain_spec_path: None, - collators: Default::default(), - files_to_inject: Default::default(), - bootnodes_addresses: vec![], - } - } + pub(crate) fn new(para_id: u32, unique_id: impl Into) -> Self { + Self { + chain: None, + para_id, + unique_id: unique_id.into(), + chain_id: None, + chain_spec_path: None, + collators: Default::default(), + files_to_inject: Default::default(), + bootnodes_addresses: vec![], + } + } - pub(crate) fn with_chain_spec( - para_id: u32, - unique_id: impl Into, - chain_id: impl Into, - chain_spec_path: impl AsRef, - ) -> Self { - Self { - para_id, - unique_id: unique_id.into(), - chain: None, - chain_id: Some(chain_id.into()), - chain_spec_path: Some(chain_spec_path.as_ref().into()), - collators: Default::default(), - files_to_inject: Default::default(), - bootnodes_addresses: vec![], - } - } + pub(crate) fn with_chain_spec( + para_id: u32, + unique_id: impl Into, + chain_id: impl Into, + chain_spec_path: impl AsRef, + ) -> Self { + Self { + para_id, + unique_id: unique_id.into(), + chain: None, + chain_id: Some(chain_id.into()), + chain_spec_path: Some(chain_spec_path.as_ref().into()), + collators: Default::default(), + files_to_inject: Default::default(), + bootnodes_addresses: vec![], + } + } - pub(crate) async fn from_spec( - para: &TeyrchainSpec, - files_to_inject: &[TransferedFile], - scoped_fs: &ScopedFilesystem<'_, impl FileSystem>, - ) -> Result { - let mut para_files_to_inject = files_to_inject.to_owned(); + pub(crate) async fn from_spec( + para: &TeyrchainSpec, + files_to_inject: &[TransferedFile], + scoped_fs: &ScopedFilesystem<'_, impl FileSystem>, + ) -> Result { + let mut para_files_to_inject = files_to_inject.to_owned(); - // parachain id is used for the keystore - let mut parachain = if let Some(chain_spec) = para.chain_spec.as_ref() { - let id = chain_spec.read_chain_id(scoped_fs).await?; + // parachain id is used for the keystore + let mut parachain = if let Some(chain_spec) = para.chain_spec.as_ref() { + let id = chain_spec.read_chain_id(scoped_fs).await?; - // add the spec to global files to inject - let spec_name = chain_spec.chain_spec_name(); - let base = PathBuf::from_str(scoped_fs.base_dir)?; - para_files_to_inject.push(TransferedFile::new( - base.join(format!("{spec_name}.json")), - PathBuf::from(format!("/cfg/{}.json", para.id)), - )); + // add the spec to global files to inject + let spec_name = chain_spec.chain_spec_name(); + let base = PathBuf::from_str(scoped_fs.base_dir)?; + para_files_to_inject.push(TransferedFile::new( + base.join(format!("{spec_name}.json")), + PathBuf::from(format!("/cfg/{}.json", para.id)), + )); - let raw_path = chain_spec - .raw_path() - .ok_or(anyhow::anyhow!("chain-spec path should be set by now.",))?; - let mut running_para = - Parachain::with_chain_spec(para.id, ¶.unique_id, id, raw_path); - if let Some(chain_name) = chain_spec.chain_name() { - running_para.chain = Some(chain_name.to_string()); - } + let raw_path = chain_spec + .raw_path() + .ok_or(anyhow::anyhow!("chain-spec path should be set by now.",))?; + let mut running_para = + Parachain::with_chain_spec(para.id, ¶.unique_id, id, raw_path); + if let Some(chain_name) = chain_spec.chain_name() { + running_para.chain = Some(chain_name.to_string()); + } - running_para - } else { - Parachain::new(para.id, ¶.unique_id) - }; + running_para + } else { + Parachain::new(para.id, ¶.unique_id) + }; - parachain.bootnodes_addresses = para.bootnodes_addresses().into_iter().cloned().collect(); - parachain.files_to_inject = para_files_to_inject; + parachain.bootnodes_addresses = para.bootnodes_addresses().into_iter().cloned().collect(); + parachain.files_to_inject = para_files_to_inject; - Ok(parachain) - } + Ok(parachain) + } - pub async fn register( - options: RegisterParachainOptions, - scoped_fs: &ScopedFilesystem<'_, impl FileSystem>, - ) -> Result<(), anyhow::Error> { - info!("Registering parachain: {:?}", options); - // get the seed - let sudo: Keypair; - if let Some(possible_seed) = options.seed { - sudo = Keypair::from_secret_key(possible_seed) - .expect(&format!("seed should return a Keypair {THIS_IS_A_BUG}")); - } else { - let uri = SecretUri::from_str("//Alice")?; - sudo = Keypair::from_uri(&uri)?; - } + pub async fn register( + options: RegisterParachainOptions, + scoped_fs: &ScopedFilesystem<'_, impl FileSystem>, + ) -> Result<(), anyhow::Error> { + info!("Registering parachain: {:?}", options); + // get the seed + let sudo: Keypair; + if let Some(possible_seed) = options.seed { + sudo = Keypair::from_secret_key(possible_seed) + .expect(&format!("seed should return a Keypair {THIS_IS_A_BUG}")); + } else { + let uri = SecretUri::from_str("//Alice")?; + sudo = Keypair::from_uri(&uri)?; + } - let genesis_state = scoped_fs - .read_to_string(options.state_path) - .await - .expect(&format!( - "State Path should be ok by this point {THIS_IS_A_BUG}" - )); - let wasm_data = scoped_fs - .read_to_string(options.wasm_path) - .await - .expect(&format!( - "Wasm Path should be ok by this point {THIS_IS_A_BUG}" - )); + let genesis_state = scoped_fs + .read_to_string(options.state_path) + .await + .expect(&format!("State Path should be ok by this point {THIS_IS_A_BUG}")); + let wasm_data = scoped_fs + .read_to_string(options.wasm_path) + .await + .expect(&format!("Wasm Path should be ok by this point {THIS_IS_A_BUG}")); - wait_ws_ready(options.node_ws_url.as_str()) - .await - .map_err(|_| { - anyhow::anyhow!( - "Error waiting for ws to be ready, at {}", - options.node_ws_url.as_str() - ) - })?; + wait_ws_ready(options.node_ws_url.as_str()).await.map_err(|_| { + anyhow::anyhow!("Error waiting for ws to be ready, at {}", options.node_ws_url.as_str()) + })?; - let api: OnlineClient = get_client_from_url(&options.node_ws_url).await?; + let api: OnlineClient = get_client_from_url(&options.node_ws_url).await?; - let schedule_para = pezkuwi_subxt::dynamic::tx( - "ParasSudoWrapper", - "sudo_schedule_para_initialize", - vec![ - Value::primitive(options.id.into()), - Value::named_composite([ - ( - "genesis_head", - Value::from_bytes(hex::decode(&genesis_state[2..])?), - ), - ( - "validation_code", - Value::from_bytes(hex::decode(&wasm_data[2..])?), - ), - ("para_kind", Value::bool(options.onboard_as_para)), - ]), - ], - ); + let schedule_para = pezkuwi_subxt::dynamic::tx( + "ParasSudoWrapper", + "sudo_schedule_para_initialize", + vec![ + Value::primitive(options.id.into()), + Value::named_composite([ + ("genesis_head", Value::from_bytes(hex::decode(&genesis_state[2..])?)), + ("validation_code", Value::from_bytes(hex::decode(&wasm_data[2..])?)), + ("para_kind", Value::bool(options.onboard_as_para)), + ]), + ], + ); - let sudo_call = - pezkuwi_subxt::dynamic::tx("Sudo", "sudo", vec![schedule_para.into_value()]); + let sudo_call = + pezkuwi_subxt::dynamic::tx("Sudo", "sudo", vec![schedule_para.into_value()]); - // TODO: uncomment below and fix the sign and submit (and follow afterwards until - // finalized block) to register the parachain - let mut tx = api - .tx() - .sign_and_submit_then_watch_default(&sudo_call, &sudo) - .await?; + // TODO: uncomment below and fix the sign and submit (and follow afterwards until + // finalized block) to register the parachain + let mut tx = api.tx().sign_and_submit_then_watch_default(&sudo_call, &sudo).await?; - // Below we use the low level API to replicate the `wait_for_in_block` behaviour - // which was removed in subxt 0.33.0. See https://github.com/paritytech/subxt/pull/1237. - while let Some(status) = tx.next().await { - match status? { - TxStatus::InBestBlock(tx_in_block) | TxStatus::InFinalizedBlock(tx_in_block) => { - let _result = tx_in_block.wait_for_success().await?; - info!("In block: {:#?}", tx_in_block.block_hash()); - }, - TxStatus::Error { message } - | TxStatus::Invalid { message } - | TxStatus::Dropped { message } => { - return Err(anyhow::format_err!("Error submitting tx: {message}")); - }, - _ => continue, - } - } + // Below we use the low level API to replicate the `wait_for_in_block` behaviour + // which was removed in subxt 0.33.0. See https://github.com/paritytech/subxt/pull/1237. + while let Some(status) = tx.next().await { + match status? { + TxStatus::InBestBlock(tx_in_block) | TxStatus::InFinalizedBlock(tx_in_block) => { + let _result = tx_in_block.wait_for_success().await?; + info!("In block: {:#?}", tx_in_block.block_hash()); + }, + TxStatus::Error { message } + | TxStatus::Invalid { message } + | TxStatus::Dropped { message } => { + return Err(anyhow::format_err!("Error submitting tx: {message}")); + }, + _ => continue, + } + } - Ok(()) - } + Ok(()) + } - pub fn para_id(&self) -> u32 { - self.para_id - } + pub fn para_id(&self) -> u32 { + self.para_id + } - pub fn unique_id(&self) -> &str { - self.unique_id.as_str() - } + pub fn unique_id(&self) -> &str { + self.unique_id.as_str() + } - pub fn chain_id(&self) -> Option<&str> { - self.chain_id.as_deref() - } + pub fn chain_id(&self) -> Option<&str> { + self.chain_id.as_deref() + } - pub fn collators(&self) -> Vec<&NetworkNode> { - self.collators.iter().collect() - } + pub fn collators(&self) -> Vec<&NetworkNode> { + self.collators.iter().collect() + } - pub fn bootnodes_addresses(&self) -> Vec<&multiaddr::Multiaddr> { - self.bootnodes_addresses.iter().collect() - } + pub fn bootnodes_addresses(&self) -> Vec<&multiaddr::Multiaddr> { + self.bootnodes_addresses.iter().collect() + } } #[cfg(test)] mod tests { - use std::collections::HashMap; + use std::collections::HashMap; - use super::*; + use super::*; - #[test] - fn create_with_is_works() { - let para = Parachain::new(100, "100"); - // only para_id and unique_id should be set - assert_eq!(para.para_id, 100); - assert_eq!(para.unique_id, "100"); - assert_eq!(para.chain_id, None); - assert_eq!(para.chain, None); - assert_eq!(para.chain_spec_path, None); - } + #[test] + fn create_with_is_works() { + let para = Parachain::new(100, "100"); + // only para_id and unique_id should be set + assert_eq!(para.para_id, 100); + assert_eq!(para.unique_id, "100"); + assert_eq!(para.chain_id, None); + assert_eq!(para.chain, None); + assert_eq!(para.chain_spec_path, None); + } - #[test] - fn create_with_chain_spec_works() { - let para = Parachain::with_chain_spec(100, "100", "rococo-local", "/tmp/rococo-local.json"); - assert_eq!(para.para_id, 100); - assert_eq!(para.unique_id, "100"); - assert_eq!(para.chain_id, Some("rococo-local".to_string())); - assert_eq!(para.chain, None); - assert_eq!( - para.chain_spec_path, - Some(PathBuf::from("/tmp/rococo-local.json")) - ); - } + #[test] + fn create_with_chain_spec_works() { + let para = Parachain::with_chain_spec(100, "100", "rococo-local", "/tmp/rococo-local.json"); + assert_eq!(para.para_id, 100); + assert_eq!(para.unique_id, "100"); + assert_eq!(para.chain_id, Some("rococo-local".to_string())); + assert_eq!(para.chain, None); + assert_eq!(para.chain_spec_path, Some(PathBuf::from("/tmp/rococo-local.json"))); + } - #[tokio::test] - async fn create_with_para_spec_works() { - use configuration::ParachainConfigBuilder; + #[tokio::test] + async fn create_with_para_spec_works() { + use configuration::ParachainConfigBuilder; - use crate::network_spec::teyrchain::TeyrchainSpec; + use crate::network_spec::teyrchain::TeyrchainSpec; - let bootnode_addresses = vec!["/ip4/10.41.122.55/tcp/45421"]; + let bootnode_addresses = vec!["/ip4/10.41.122.55/tcp/45421"]; - let para_config = ParachainConfigBuilder::new(Default::default()) - .with_id(100) - .cumulus_based(false) - .with_default_command("adder-collator") - .with_raw_bootnodes_addresses(bootnode_addresses.clone()) - .with_collator(|c| c.with_name("col")) - .build() - .unwrap(); + let para_config = ParachainConfigBuilder::new(Default::default()) + .with_id(100) + .cumulus_based(false) + .with_default_command("adder-collator") + .with_raw_bootnodes_addresses(bootnode_addresses.clone()) + .with_collator(|c| c.with_name("col")) + .build() + .unwrap(); - let para_spec = - TeyrchainSpec::from_config(¶_config, "rococo-local".try_into().unwrap()).unwrap(); - let fs = support::fs::in_memory::InMemoryFileSystem::new(HashMap::default()); - let scoped_fs = ScopedFilesystem { - fs: &fs, - base_dir: "/tmp/some", - }; + let para_spec = + TeyrchainSpec::from_config(¶_config, "rococo-local".try_into().unwrap()).unwrap(); + let fs = support::fs::in_memory::InMemoryFileSystem::new(HashMap::default()); + let scoped_fs = ScopedFilesystem { fs: &fs, base_dir: "/tmp/some" }; - let files = vec![TransferedFile::new( - PathBuf::from("/tmp/some"), - PathBuf::from("/tmp/some"), - )]; - let para = Parachain::from_spec(¶_spec, &files, &scoped_fs) - .await - .unwrap(); - println!("{para:#?}"); - assert_eq!(para.para_id, 100); - assert_eq!(para.unique_id, "100"); - assert_eq!(para.chain_id, None); - assert_eq!(para.chain, None); - // one file should be added. - assert_eq!(para.files_to_inject.len(), 1); - assert_eq!( - para.bootnodes_addresses() - .iter() - .map(|addr| addr.to_string()) - .collect::>(), - bootnode_addresses - ); - } + let files = + vec![TransferedFile::new(PathBuf::from("/tmp/some"), PathBuf::from("/tmp/some"))]; + let para = Parachain::from_spec(¶_spec, &files, &scoped_fs).await.unwrap(); + println!("{para:#?}"); + assert_eq!(para.para_id, 100); + assert_eq!(para.unique_id, "100"); + assert_eq!(para.chain_id, None); + assert_eq!(para.chain, None); + // one file should be added. + assert_eq!(para.files_to_inject.len(), 1); + assert_eq!( + para.bootnodes_addresses().iter().map(|addr| addr.to_string()).collect::>(), + bootnode_addresses + ); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_helper/metrics.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_helper/metrics.rs index 48db5cfc..cb0708d4 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_helper/metrics.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_helper/metrics.rs @@ -5,58 +5,56 @@ use reqwest::Url; #[async_trait] pub trait MetricsHelper { - async fn metric(&self, metric_name: &str) -> Result; - async fn metric_with_url( - metric: impl AsRef + Send, - endpoint: impl Into + Send, - ) -> Result; + async fn metric(&self, metric_name: &str) -> Result; + async fn metric_with_url( + metric: impl AsRef + Send, + endpoint: impl Into + Send, + ) -> Result; } pub struct Metrics { - endpoint: Url, + endpoint: Url, } impl Metrics { - fn new(endpoint: impl Into) -> Self { - Self { - endpoint: endpoint.into(), - } - } + fn new(endpoint: impl Into) -> Self { + Self { endpoint: endpoint.into() } + } - async fn fetch_metrics( - endpoint: impl AsRef, - ) -> Result, anyhow::Error> { - let response = reqwest::get(endpoint.as_ref()).await?; - Ok(prom_metrics_parser::parse(&response.text().await?)?) - } + async fn fetch_metrics( + endpoint: impl AsRef, + ) -> Result, anyhow::Error> { + let response = reqwest::get(endpoint.as_ref()).await?; + Ok(prom_metrics_parser::parse(&response.text().await?)?) + } - fn get_metric( - metrics_map: HashMap, - metric_name: &str, - ) -> Result { - let treat_not_found_as_zero = true; - if let Some(val) = metrics_map.get(metric_name) { - Ok(*val) - } else if treat_not_found_as_zero { - Ok(0_f64) - } else { - Err(anyhow::anyhow!("MetricNotFound: {metric_name}")) - } - } + fn get_metric( + metrics_map: HashMap, + metric_name: &str, + ) -> Result { + let treat_not_found_as_zero = true; + if let Some(val) = metrics_map.get(metric_name) { + Ok(*val) + } else if treat_not_found_as_zero { + Ok(0_f64) + } else { + Err(anyhow::anyhow!("MetricNotFound: {metric_name}")) + } + } } #[async_trait] impl MetricsHelper for Metrics { - async fn metric(&self, metric_name: &str) -> Result { - let metrics_map = Metrics::fetch_metrics(self.endpoint.as_str()).await?; - Metrics::get_metric(metrics_map, metric_name) - } + async fn metric(&self, metric_name: &str) -> Result { + let metrics_map = Metrics::fetch_metrics(self.endpoint.as_str()).await?; + Metrics::get_metric(metrics_map, metric_name) + } - async fn metric_with_url( - metric_name: impl AsRef + Send, - endpoint: impl Into + Send, - ) -> Result { - let metrics_map = Metrics::fetch_metrics(endpoint.into()).await?; - Metrics::get_metric(metrics_map, metric_name.as_ref()) - } + async fn metric_with_url( + metric_name: impl AsRef + Send, + endpoint: impl Into + Send, + ) -> Result { + let metrics_map = Metrics::fetch_metrics(endpoint.into()).await?; + Metrics::get_metric(metrics_map, metric_name.as_ref()) + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_helper/verifier.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_helper/verifier.rs index 73aca1dc..b3f10a8e 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_helper/verifier.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_helper/verifier.rs @@ -6,29 +6,29 @@ use tracing::trace; use crate::network::node::NetworkNode; pub(crate) async fn verify_nodes(nodes: &[&NetworkNode]) -> Result<(), anyhow::Error> { - timeout(Duration::from_secs(90), check_nodes(nodes)) - .await - .map_err(|_| anyhow::anyhow!("one or more nodes are not ready!")) + timeout(Duration::from_secs(90), check_nodes(nodes)) + .await + .map_err(|_| anyhow::anyhow!("one or more nodes are not ready!")) } // TODO: we should inject in someway the logic to make the request // in order to allow us to `mock` and easily test this. // maybe moved to the provider with a NodeStatus, and some helpers like wait_running, wait_ready, etc... ? to be discussed async fn check_nodes(nodes: &[&NetworkNode]) { - loop { - let tasks: Vec<_> = nodes - .iter() - .map(|node| { - trace!("🔎 checking node: {} ", node.name); - reqwest::get(node.prometheus_uri.clone()) - }) - .collect(); + loop { + let tasks: Vec<_> = nodes + .iter() + .map(|node| { + trace!("🔎 checking node: {} ", node.name); + reqwest::get(node.prometheus_uri.clone()) + }) + .collect(); - let all_ready = futures::future::try_join_all(tasks).await; - if all_ready.is_ok() { - return; - } + let all_ready = futures::future::try_join_all(tasks).await; + if all_ready.is_ok() { + return; + } - tokio::time::sleep(Duration::from_millis(1000)).await; - } + tokio::time::sleep(Duration::from_millis(1000)).await; + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_spec.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_spec.rs index 53c72a48..b15bfcbb 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_spec.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_spec.rs @@ -1,6 +1,6 @@ use std::{ - collections::{hash_map::Entry, HashMap}, - sync::Arc, + collections::{hash_map::Entry, HashMap}, + sync::Arc, }; use configuration::{GlobalSettings, HrmpChannelConfig, NetworkConfig}; @@ -20,311 +20,289 @@ use self::{node::NodeSpec, relaychain::RelaychainSpec, teyrchain::TeyrchainSpec} #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NetworkSpec { - /// Relaychain configuration. - pub(crate) relaychain: RelaychainSpec, + /// Relaychain configuration. + pub(crate) relaychain: RelaychainSpec, - /// Parachains configurations. - pub(crate) parachains: Vec, + /// Parachains configurations. + pub(crate) parachains: Vec, - /// HRMP channels configurations. - pub(crate) hrmp_channels: Vec, + /// HRMP channels configurations. + pub(crate) hrmp_channels: Vec, - /// Global settings - pub(crate) global_settings: GlobalSettings, + /// Global settings + pub(crate) global_settings: GlobalSettings, } impl NetworkSpec { - pub async fn from_config( - network_config: &NetworkConfig, - ) -> Result { - let mut errs = vec![]; - let relaychain = RelaychainSpec::from_config(network_config.relaychain())?; - let mut parachains = vec![]; + pub async fn from_config( + network_config: &NetworkConfig, + ) -> Result { + let mut errs = vec![]; + let relaychain = RelaychainSpec::from_config(network_config.relaychain())?; + let mut parachains = vec![]; - // TODO: move to `fold` or map+fold - for para_config in network_config.parachains() { - match TeyrchainSpec::from_config(para_config, relaychain.chain.clone()) { - Ok(para) => parachains.push(para), - Err(err) => errs.push(err), - } - } + // TODO: move to `fold` or map+fold + for para_config in network_config.parachains() { + match TeyrchainSpec::from_config(para_config, relaychain.chain.clone()) { + Ok(para) => parachains.push(para), + Err(err) => errs.push(err), + } + } - if errs.is_empty() { - Ok(NetworkSpec { - relaychain, - parachains, - hrmp_channels: network_config - .hrmp_channels() - .into_iter() - .cloned() - .collect(), - global_settings: network_config.global_settings().clone(), - }) - } else { - let errs_str = errs - .into_iter() - .map(|e| e.to_string()) - .collect::>() - .join("\n"); - Err(OrchestratorError::InvalidConfig(errs_str)) - } - } + if errs.is_empty() { + Ok(NetworkSpec { + relaychain, + parachains, + hrmp_channels: network_config.hrmp_channels().into_iter().cloned().collect(), + global_settings: network_config.global_settings().clone(), + }) + } else { + let errs_str = + errs.into_iter().map(|e| e.to_string()).collect::>().join("\n"); + Err(OrchestratorError::InvalidConfig(errs_str)) + } + } - pub async fn populate_nodes_available_args( - &mut self, - ns: Arc, - ) -> Result<(), OrchestratorError> { - let network_nodes = self.collect_network_nodes(); + pub async fn populate_nodes_available_args( + &mut self, + ns: Arc, + ) -> Result<(), OrchestratorError> { + let network_nodes = self.collect_network_nodes(); - let mut image_command_to_nodes_mapping = - Self::create_image_command_to_nodes_mapping(network_nodes); + let mut image_command_to_nodes_mapping = + Self::create_image_command_to_nodes_mapping(network_nodes); - let available_args_outputs = - Self::retrieve_all_nodes_available_args_output(ns, &image_command_to_nodes_mapping) - .await?; + let available_args_outputs = + Self::retrieve_all_nodes_available_args_output(ns, &image_command_to_nodes_mapping) + .await?; - Self::update_nodes_available_args_output( - &mut image_command_to_nodes_mapping, - available_args_outputs, - ); + Self::update_nodes_available_args_output( + &mut image_command_to_nodes_mapping, + available_args_outputs, + ); - Ok(()) - } + Ok(()) + } - // - pub async fn node_available_args_output( - &self, - node_spec: &NodeSpec, - ns: Arc, - ) -> Result { - // try to find a node that use the same combination of image/cmd - let cmp_fn = |ad_hoc: &&NodeSpec| -> bool { - ad_hoc.image == node_spec.image && ad_hoc.command == node_spec.command - }; + // + pub async fn node_available_args_output( + &self, + node_spec: &NodeSpec, + ns: Arc, + ) -> Result { + // try to find a node that use the same combination of image/cmd + let cmp_fn = |ad_hoc: &&NodeSpec| -> bool { + ad_hoc.image == node_spec.image && ad_hoc.command == node_spec.command + }; - // check if we already had computed the args output for this cmd/[image] - let node = self.relaychain.nodes.iter().find(cmp_fn); - let node = if let Some(node) = node { - Some(node) - } else { - let node = self - .parachains - .iter() - .find_map(|para| para.collators.iter().find(cmp_fn)); + // check if we already had computed the args output for this cmd/[image] + let node = self.relaychain.nodes.iter().find(cmp_fn); + let node = if let Some(node) = node { + Some(node) + } else { + let node = self.parachains.iter().find_map(|para| para.collators.iter().find(cmp_fn)); - node - }; + node + }; - let output = if let Some(node) = node { - node.available_args_output.clone().expect(&format!( - "args_output should be set for running nodes {THIS_IS_A_BUG}" - )) - } else { - // we need to compute the args output - let image = node_spec - .image - .as_ref() - .map(|image| image.as_str().to_string()); - let command = node_spec.command.as_str().to_string(); + let output = if let Some(node) = node { + node.available_args_output + .clone() + .expect(&format!("args_output should be set for running nodes {THIS_IS_A_BUG}")) + } else { + // we need to compute the args output + let image = node_spec.image.as_ref().map(|image| image.as_str().to_string()); + let command = node_spec.command.as_str().to_string(); - ns.get_node_available_args((command, image)).await? - }; + ns.get_node_available_args((command, image)).await? + }; - Ok(output) - } + Ok(output) + } - pub fn relaychain(&self) -> &RelaychainSpec { - &self.relaychain - } + pub fn relaychain(&self) -> &RelaychainSpec { + &self.relaychain + } - pub fn relaychain_mut(&mut self) -> &mut RelaychainSpec { - &mut self.relaychain - } + pub fn relaychain_mut(&mut self) -> &mut RelaychainSpec { + &mut self.relaychain + } - pub fn parachains_iter(&self) -> impl Iterator { - self.parachains.iter() - } + pub fn parachains_iter(&self) -> impl Iterator { + self.parachains.iter() + } - pub fn parachains_iter_mut(&mut self) -> impl Iterator { - self.parachains.iter_mut() - } + pub fn parachains_iter_mut(&mut self) -> impl Iterator { + self.parachains.iter_mut() + } - pub fn set_global_settings(&mut self, global_settings: GlobalSettings) { - self.global_settings = global_settings; - } + pub fn set_global_settings(&mut self, global_settings: GlobalSettings) { + self.global_settings = global_settings; + } - pub async fn build_parachain_artifacts<'a, T: FileSystem>( - &mut self, - ns: DynNamespace, - scoped_fs: &ScopedFilesystem<'a, T>, - relaychain_id: &str, - base_dir_exists: bool, - ) -> Result<(), anyhow::Error> { - for para in self.parachains.iter_mut() { - let chain_spec_raw_path = para.build_chain_spec(relaychain_id, &ns, scoped_fs).await?; + pub async fn build_parachain_artifacts<'a, T: FileSystem>( + &mut self, + ns: DynNamespace, + scoped_fs: &ScopedFilesystem<'a, T>, + relaychain_id: &str, + base_dir_exists: bool, + ) -> Result<(), anyhow::Error> { + for para in self.parachains.iter_mut() { + let chain_spec_raw_path = para.build_chain_spec(relaychain_id, &ns, scoped_fs).await?; - trace!("creating dirs for {}", ¶.unique_id); - if base_dir_exists { - scoped_fs.create_dir_all(¶.unique_id).await?; - } else { - scoped_fs.create_dir(¶.unique_id).await?; - }; - trace!("created dirs for {}", ¶.unique_id); + trace!("creating dirs for {}", ¶.unique_id); + if base_dir_exists { + scoped_fs.create_dir_all(¶.unique_id).await?; + } else { + scoped_fs.create_dir(¶.unique_id).await?; + }; + trace!("created dirs for {}", ¶.unique_id); - // create wasm/state - para.genesis_state - .build( - chain_spec_raw_path.clone(), - format!("{}/genesis-state", para.unique_id), - &ns, - scoped_fs, - None, - ) - .await?; - debug!("parachain genesis state built!"); - para.genesis_wasm - .build( - chain_spec_raw_path, - format!("{}/genesis-wasm", para.unique_id), - &ns, - scoped_fs, - None, - ) - .await?; - debug!("parachain genesis wasm built!"); - } + // create wasm/state + para.genesis_state + .build( + chain_spec_raw_path.clone(), + format!("{}/genesis-state", para.unique_id), + &ns, + scoped_fs, + None, + ) + .await?; + debug!("parachain genesis state built!"); + para.genesis_wasm + .build( + chain_spec_raw_path, + format!("{}/genesis-wasm", para.unique_id), + &ns, + scoped_fs, + None, + ) + .await?; + debug!("parachain genesis wasm built!"); + } - Ok(()) - } + Ok(()) + } - // collect mutable references to all nodes from relaychain and parachains - fn collect_network_nodes(&mut self) -> Vec<&mut NodeSpec> { - vec![ - self.relaychain.nodes.iter_mut().collect::>(), - self.parachains - .iter_mut() - .flat_map(|para| para.collators.iter_mut()) - .collect(), - ] - .into_iter() - .flatten() - .collect::>() - } + // collect mutable references to all nodes from relaychain and parachains + fn collect_network_nodes(&mut self) -> Vec<&mut NodeSpec> { + vec![ + self.relaychain.nodes.iter_mut().collect::>(), + self.parachains.iter_mut().flat_map(|para| para.collators.iter_mut()).collect(), + ] + .into_iter() + .flatten() + .collect::>() + } - // initialize the mapping of all possible node image/commands to corresponding nodes - fn create_image_command_to_nodes_mapping( - network_nodes: Vec<&mut NodeSpec>, - ) -> HashMap<(Option, String), Vec<&mut NodeSpec>> { - network_nodes.into_iter().fold( - HashMap::new(), - |mut acc: HashMap<(Option, String), Vec<&mut node::NodeSpec>>, node| { - // build mapping key using image and command if image is present or command only - let key = node - .image - .as_ref() - .map(|image| { - ( - Some(image.as_str().to_string()), - node.command.as_str().to_string(), - ) - }) - .unwrap_or_else(|| (None, node.command.as_str().to_string())); + // initialize the mapping of all possible node image/commands to corresponding nodes + fn create_image_command_to_nodes_mapping( + network_nodes: Vec<&mut NodeSpec>, + ) -> HashMap<(Option, String), Vec<&mut NodeSpec>> { + network_nodes.into_iter().fold( + HashMap::new(), + |mut acc: HashMap<(Option, String), Vec<&mut node::NodeSpec>>, node| { + // build mapping key using image and command if image is present or command only + let key = node + .image + .as_ref() + .map(|image| { + (Some(image.as_str().to_string()), node.command.as_str().to_string()) + }) + .unwrap_or_else(|| (None, node.command.as_str().to_string())); - // append the node to the vector of nodes for this image/command tuple - if let Entry::Vacant(entry) = acc.entry(key.clone()) { - entry.insert(vec![node]); - } else { - acc.get_mut(&key).unwrap().push(node); - } + // append the node to the vector of nodes for this image/command tuple + if let Entry::Vacant(entry) = acc.entry(key.clone()) { + entry.insert(vec![node]); + } else { + acc.get_mut(&key).unwrap().push(node); + } - acc - }, - ) - } + acc + }, + ) + } - async fn retrieve_all_nodes_available_args_output( - ns: Arc, - image_command_to_nodes_mapping: &HashMap<(Option, String), Vec<&mut NodeSpec>>, - ) -> Result, String, String)>, OrchestratorError> { - try_join_all( - image_command_to_nodes_mapping - .keys() - .map(|(image, command)| { - let ns = ns.clone(); - let image = image.clone(); - let command = command.clone(); - async move { - // get node available args output from image/command - let available_args = ns - .get_node_available_args((command.clone(), image.clone())) - .await?; - debug!( - "retrieved available args for image: {:?}, command: {}", - image, command - ); + async fn retrieve_all_nodes_available_args_output( + ns: Arc, + image_command_to_nodes_mapping: &HashMap<(Option, String), Vec<&mut NodeSpec>>, + ) -> Result, String, String)>, OrchestratorError> { + try_join_all( + image_command_to_nodes_mapping + .keys() + .map(|(image, command)| { + let ns = ns.clone(); + let image = image.clone(); + let command = command.clone(); + async move { + // get node available args output from image/command + let available_args = + ns.get_node_available_args((command.clone(), image.clone())).await?; + debug!( + "retrieved available args for image: {:?}, command: {}", + image, command + ); - // map the result to include image and command - Ok::<_, OrchestratorError>((image, command, available_args)) - } - }) - .collect::>(), - ) - .await - } + // map the result to include image and command + Ok::<_, OrchestratorError>((image, command, available_args)) + } + }) + .collect::>(), + ) + .await + } - fn update_nodes_available_args_output( - image_command_to_nodes_mapping: &mut HashMap<(Option, String), Vec<&mut NodeSpec>>, - available_args_outputs: Vec<(Option, String, String)>, - ) { - for (image, command, available_args_output) in available_args_outputs { - let nodes = image_command_to_nodes_mapping - .get_mut(&(image, command)) - .expect(&format!( - "node image/command key should exist {THIS_IS_A_BUG}" - )); + fn update_nodes_available_args_output( + image_command_to_nodes_mapping: &mut HashMap<(Option, String), Vec<&mut NodeSpec>>, + available_args_outputs: Vec<(Option, String, String)>, + ) { + for (image, command, available_args_output) in available_args_outputs { + let nodes = image_command_to_nodes_mapping + .get_mut(&(image, command)) + .expect(&format!("node image/command key should exist {THIS_IS_A_BUG}")); - for node in nodes { - node.available_args_output = Some(available_args_output.clone()); - } - } - } + for node in nodes { + node.available_args_output = Some(available_args_output.clone()); + } + } + } } #[cfg(test)] mod tests { - #[tokio::test] - async fn small_network_config_get_spec() { - use configuration::NetworkConfigBuilder; + #[tokio::test] + async fn small_network_config_get_spec() { + use configuration::NetworkConfigBuilder; - use super::*; + use super::*; - let config = NetworkConfigBuilder::new() - .with_relaychain(|r| { - r.with_chain("rococo-local") - .with_default_command("polkadot") - .with_validator(|node| node.with_name("alice")) - .with_fullnode(|node| node.with_name("bob").with_command("polkadot1")) - }) - .with_parachain(|p| { - p.with_id(100) - .with_default_command("adder-collator") - .with_collator(|c| c.with_name("collator1")) - }) - .build() - .unwrap(); + let config = NetworkConfigBuilder::new() + .with_relaychain(|r| { + r.with_chain("rococo-local") + .with_default_command("polkadot") + .with_validator(|node| node.with_name("alice")) + .with_fullnode(|node| node.with_name("bob").with_command("polkadot1")) + }) + .with_parachain(|p| { + p.with_id(100) + .with_default_command("adder-collator") + .with_collator(|c| c.with_name("collator1")) + }) + .build() + .unwrap(); - let network_spec = NetworkSpec::from_config(&config).await.unwrap(); - let alice = network_spec.relaychain.nodes.first().unwrap(); - let bob = network_spec.relaychain.nodes.get(1).unwrap(); - assert_eq!(alice.command.as_str(), "polkadot"); - assert_eq!(bob.command.as_str(), "polkadot1"); - assert!(alice.is_validator); - assert!(!bob.is_validator); + let network_spec = NetworkSpec::from_config(&config).await.unwrap(); + let alice = network_spec.relaychain.nodes.first().unwrap(); + let bob = network_spec.relaychain.nodes.get(1).unwrap(); + assert_eq!(alice.command.as_str(), "polkadot"); + assert_eq!(bob.command.as_str(), "polkadot1"); + assert!(alice.is_validator); + assert!(!bob.is_validator); - // paras - assert_eq!(network_spec.parachains.len(), 1); - let para_100 = network_spec.parachains.first().unwrap(); - assert_eq!(para_100.id, 100); - } + // paras + assert_eq!(network_spec.parachains.len(), 1); + let para_100 = network_spec.parachains.first().unwrap(); + assert_eq!(para_100.id, 100); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_spec/node.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_spec/node.rs index 98d6d4a4..e250b7b8 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_spec/node.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_spec/node.rs @@ -1,9 +1,9 @@ use std::path::PathBuf; use configuration::shared::{ - node::{EnvVar, NodeConfig}, - resources::Resources, - types::{Arg, AssetLocation, Command, Image}, + node::{EnvVar, NodeConfig}, + resources::Resources, + types::{Arg, AssetLocation, Command, Image}, }; use multiaddr::Multiaddr; use provider::types::Port; @@ -11,39 +11,39 @@ use serde::{Deserialize, Serialize}; use support::constants::THIS_IS_A_BUG; use crate::{ - errors::OrchestratorError, - generators, - network::AddNodeOptions, - shared::{ - macros, - types::{ChainDefaultContext, NodeAccount, NodeAccounts, ParkedPort}, - }, - AddCollatorOptions, + errors::OrchestratorError, + generators, + network::AddNodeOptions, + shared::{ + macros, + types::{ChainDefaultContext, NodeAccount, NodeAccounts, ParkedPort}, + }, + AddCollatorOptions, }; macros::create_add_options!(AddNodeSpecOpts { - override_eth_key: Option + override_eth_key: Option }); macro_rules! impl_from_for_add_node_opts { - ($struct:ident) => { - impl From<$struct> for AddNodeSpecOpts { - fn from(value: $struct) -> Self { - Self { - image: value.image, - command: value.command, - subcommand: value.subcommand, - args: value.args, - env: value.env, - is_validator: value.is_validator, - rpc_port: value.rpc_port, - prometheus_port: value.prometheus_port, - p2p_port: value.p2p_port, - override_eth_key: value.override_eth_key, - } - } - } - }; + ($struct:ident) => { + impl From<$struct> for AddNodeSpecOpts { + fn from(value: $struct) -> Self { + Self { + image: value.image, + command: value.command, + subcommand: value.subcommand, + args: value.args, + env: value.env, + is_validator: value.is_validator, + rpc_port: value.rpc_port, + prometheus_port: value.prometheus_port, + p2p_port: value.p2p_port, + override_eth_key: value.override_eth_key, + } + } + } + }; } impl_from_for_add_node_opts!(AddNodeOptions); @@ -52,305 +52,281 @@ impl_from_for_add_node_opts!(AddCollatorOptions); /// A node configuration, with fine-grained configuration options. #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct NodeSpec { - // Node name (should be unique or an index will be appended). - pub(crate) name: String, + // Node name (should be unique or an index will be appended). + pub(crate) name: String, - /// Node key, used for compute the p2p identity. - pub(crate) key: String, + /// Node key, used for compute the p2p identity. + pub(crate) key: String, - // libp2p local identity - pub(crate) peer_id: String, + // libp2p local identity + pub(crate) peer_id: String, - /// Accounts to be injected in the keystore. - pub(crate) accounts: NodeAccounts, + /// Accounts to be injected in the keystore. + pub(crate) accounts: NodeAccounts, - /// Image to run (only podman/k8s). Override the default. - pub(crate) image: Option, + /// Image to run (only podman/k8s). Override the default. + pub(crate) image: Option, - /// Command to run the node. Override the default. - pub(crate) command: Command, + /// Command to run the node. Override the default. + pub(crate) command: Command, - /// Optional subcommand for the node. - pub(crate) subcommand: Option, + /// Optional subcommand for the node. + pub(crate) subcommand: Option, - /// Arguments to use for node. Appended to default. - pub(crate) args: Vec, + /// Arguments to use for node. Appended to default. + pub(crate) args: Vec, - // The help command output containing the available arguments. - pub(crate) available_args_output: Option, + // The help command output containing the available arguments. + pub(crate) available_args_output: Option, - /// Wether the node is a validator. - pub(crate) is_validator: bool, + /// Wether the node is a validator. + pub(crate) is_validator: bool, - /// Whether the node keys must be added to invulnerables. - pub(crate) is_invulnerable: bool, + /// Whether the node keys must be added to invulnerables. + pub(crate) is_invulnerable: bool, - /// Whether the node is a bootnode. - pub(crate) is_bootnode: bool, + /// Whether the node is a bootnode. + pub(crate) is_bootnode: bool, - /// Node initial balance present in genesis. - pub(crate) initial_balance: u128, + /// Node initial balance present in genesis. + pub(crate) initial_balance: u128, - /// Environment variables to set (inside pod for podman/k8s, inside shell for native). - pub(crate) env: Vec, + /// Environment variables to set (inside pod for podman/k8s, inside shell for native). + pub(crate) env: Vec, - /// List of node's bootnodes addresses to use. Appended to default. - pub(crate) bootnodes_addresses: Vec, + /// List of node's bootnodes addresses to use. Appended to default. + pub(crate) bootnodes_addresses: Vec, - /// Default resources. Override the default. - pub(crate) resources: Option, + /// Default resources. Override the default. + pub(crate) resources: Option, - /// Websocket port to use. - pub(crate) ws_port: ParkedPort, + /// Websocket port to use. + pub(crate) ws_port: ParkedPort, - /// RPC port to use. - pub(crate) rpc_port: ParkedPort, + /// RPC port to use. + pub(crate) rpc_port: ParkedPort, - /// Prometheus port to use. - pub(crate) prometheus_port: ParkedPort, + /// Prometheus port to use. + pub(crate) prometheus_port: ParkedPort, - /// P2P port to use. - pub(crate) p2p_port: ParkedPort, + /// P2P port to use. + pub(crate) p2p_port: ParkedPort, - /// libp2p cert hash to use with `webrtc` transport. - pub(crate) p2p_cert_hash: Option, + /// libp2p cert hash to use with `webrtc` transport. + pub(crate) p2p_cert_hash: Option, - /// Database snapshot. Override the default. - pub(crate) db_snapshot: Option, + /// Database snapshot. Override the default. + pub(crate) db_snapshot: Option, - /// P2P port to use by full node if this is the case - pub(crate) full_node_p2p_port: Option, - /// Prometheus port to use by full node if this is the case - pub(crate) full_node_prometheus_port: Option, + /// P2P port to use by full node if this is the case + pub(crate) full_node_p2p_port: Option, + /// Prometheus port to use by full node if this is the case + pub(crate) full_node_prometheus_port: Option, - /// Optionally specify a log path for the node - pub(crate) node_log_path: Option, + /// Optionally specify a log path for the node + pub(crate) node_log_path: Option, - /// Optionally specify a keystore path for the node - pub(crate) keystore_path: Option, + /// Optionally specify a keystore path for the node + pub(crate) keystore_path: Option, - /// Keystore key types to generate. - /// Supports short form (e.g., "audi") using predefined schemas, - /// or long form (e.g., "audi_sr") with explicit schema (sr, ed, ec). - pub(crate) keystore_key_types: Vec, + /// Keystore key types to generate. + /// Supports short form (e.g., "audi") using predefined schemas, + /// or long form (e.g., "audi_sr") with explicit schema (sr, ed, ec). + pub(crate) keystore_key_types: Vec, } impl NodeSpec { - pub fn from_config( - node_config: &NodeConfig, - chain_context: &ChainDefaultContext, - full_node_present: bool, - evm_based: bool, - ) -> Result { - // Check first if the image is set at node level, then try with the default - let image = node_config.image().or(chain_context.default_image).cloned(); + pub fn from_config( + node_config: &NodeConfig, + chain_context: &ChainDefaultContext, + full_node_present: bool, + evm_based: bool, + ) -> Result { + // Check first if the image is set at node level, then try with the default + let image = node_config.image().or(chain_context.default_image).cloned(); - // Check first if the command is set at node level, then try with the default - let command = if let Some(cmd) = node_config.command() { - cmd.clone() - } else if let Some(cmd) = chain_context.default_command { - cmd.clone() - } else { - return Err(OrchestratorError::InvalidNodeConfig( - node_config.name().into(), - "command".to_string(), - )); - }; + // Check first if the command is set at node level, then try with the default + let command = if let Some(cmd) = node_config.command() { + cmd.clone() + } else if let Some(cmd) = chain_context.default_command { + cmd.clone() + } else { + return Err(OrchestratorError::InvalidNodeConfig( + node_config.name().into(), + "command".to_string(), + )); + }; - let subcommand = node_config.subcommand().cloned(); + let subcommand = node_config.subcommand().cloned(); - // If `args` is set at `node` level use them - // otherwise use the default_args (can be empty). - let args: Vec = if node_config.args().is_empty() { - chain_context - .default_args - .iter() - .map(|x| x.to_owned().clone()) - .collect() - } else { - node_config.args().into_iter().cloned().collect() - }; + // If `args` is set at `node` level use them + // otherwise use the default_args (can be empty). + let args: Vec = if node_config.args().is_empty() { + chain_context.default_args.iter().map(|x| x.to_owned().clone()).collect() + } else { + node_config.args().into_iter().cloned().collect() + }; - let (key, peer_id) = generators::generate_node_identity(node_config.name())?; + let (key, peer_id) = generators::generate_node_identity(node_config.name())?; - let mut name = node_config.name().to_string(); - let seed = format!("//{}{name}", name.remove(0).to_uppercase()); - let accounts = generators::generate_node_keys(&seed)?; - let mut accounts = NodeAccounts { seed, accounts }; + let mut name = node_config.name().to_string(); + let seed = format!("//{}{name}", name.remove(0).to_uppercase()); + let accounts = generators::generate_node_keys(&seed)?; + let mut accounts = NodeAccounts { seed, accounts }; - if evm_based { - if let Some(session_key) = node_config.override_eth_key() { - accounts - .accounts - .insert("eth".into(), NodeAccount::new(session_key, session_key)); - } - } + if evm_based { + if let Some(session_key) = node_config.override_eth_key() { + accounts.accounts.insert("eth".into(), NodeAccount::new(session_key, session_key)); + } + } - let db_snapshot = match (node_config.db_snapshot(), chain_context.default_db_snapshot) { - (Some(db_snapshot), _) => Some(db_snapshot), - (None, Some(db_snapshot)) => Some(db_snapshot), - _ => None, - }; + let db_snapshot = match (node_config.db_snapshot(), chain_context.default_db_snapshot) { + (Some(db_snapshot), _) => Some(db_snapshot), + (None, Some(db_snapshot)) => Some(db_snapshot), + _ => None, + }; - let (full_node_p2p_port, full_node_prometheus_port) = if full_node_present { - ( - Some(generators::generate_node_port(None)?), - Some(generators::generate_node_port(None)?), - ) - } else { - (None, None) - }; + let (full_node_p2p_port, full_node_prometheus_port) = if full_node_present { + ( + Some(generators::generate_node_port(None)?), + Some(generators::generate_node_port(None)?), + ) + } else { + (None, None) + }; - Ok(Self { - name: node_config.name().to_string(), - key, - peer_id, - image, - command, - subcommand, - args, - available_args_output: None, - is_validator: node_config.is_validator(), - is_invulnerable: node_config.is_invulnerable(), - is_bootnode: node_config.is_bootnode(), - initial_balance: node_config.initial_balance(), - env: node_config.env().into_iter().cloned().collect(), - bootnodes_addresses: node_config - .bootnodes_addresses() - .into_iter() - .cloned() - .collect(), - resources: node_config.resources().cloned(), - p2p_cert_hash: node_config.p2p_cert_hash().map(str::to_string), - db_snapshot: db_snapshot.cloned(), - accounts, - ws_port: generators::generate_node_port(node_config.ws_port())?, - rpc_port: generators::generate_node_port(node_config.rpc_port())?, - prometheus_port: generators::generate_node_port(node_config.prometheus_port())?, - p2p_port: generators::generate_node_port(node_config.p2p_port())?, - full_node_p2p_port, - full_node_prometheus_port, - node_log_path: node_config.node_log_path().cloned(), - keystore_path: node_config.keystore_path().cloned(), - keystore_key_types: node_config - .keystore_key_types() - .into_iter() - .map(str::to_string) - .collect(), - }) - } + Ok(Self { + name: node_config.name().to_string(), + key, + peer_id, + image, + command, + subcommand, + args, + available_args_output: None, + is_validator: node_config.is_validator(), + is_invulnerable: node_config.is_invulnerable(), + is_bootnode: node_config.is_bootnode(), + initial_balance: node_config.initial_balance(), + env: node_config.env().into_iter().cloned().collect(), + bootnodes_addresses: node_config.bootnodes_addresses().into_iter().cloned().collect(), + resources: node_config.resources().cloned(), + p2p_cert_hash: node_config.p2p_cert_hash().map(str::to_string), + db_snapshot: db_snapshot.cloned(), + accounts, + ws_port: generators::generate_node_port(node_config.ws_port())?, + rpc_port: generators::generate_node_port(node_config.rpc_port())?, + prometheus_port: generators::generate_node_port(node_config.prometheus_port())?, + p2p_port: generators::generate_node_port(node_config.p2p_port())?, + full_node_p2p_port, + full_node_prometheus_port, + node_log_path: node_config.node_log_path().cloned(), + keystore_path: node_config.keystore_path().cloned(), + keystore_key_types: node_config + .keystore_key_types() + .into_iter() + .map(str::to_string) + .collect(), + }) + } - pub fn from_ad_hoc( - name: impl Into, - options: AddNodeSpecOpts, - chain_context: &ChainDefaultContext, - full_node_present: bool, - evm_based: bool, - ) -> Result { - // Check first if the image is set at node level, then try with the default - let image = if let Some(img) = options.image { - Some(img.clone()) - } else { - chain_context.default_image.cloned() - }; + pub fn from_ad_hoc( + name: impl Into, + options: AddNodeSpecOpts, + chain_context: &ChainDefaultContext, + full_node_present: bool, + evm_based: bool, + ) -> Result { + // Check first if the image is set at node level, then try with the default + let image = if let Some(img) = options.image { + Some(img.clone()) + } else { + chain_context.default_image.cloned() + }; - let name = name.into(); - // Check first if the command is set at node level, then try with the default - let command = if let Some(cmd) = options.command { - cmd.clone() - } else if let Some(cmd) = chain_context.default_command { - cmd.clone() - } else { - return Err(OrchestratorError::InvalidNodeConfig( - name, - "command".to_string(), - )); - }; + let name = name.into(); + // Check first if the command is set at node level, then try with the default + let command = if let Some(cmd) = options.command { + cmd.clone() + } else if let Some(cmd) = chain_context.default_command { + cmd.clone() + } else { + return Err(OrchestratorError::InvalidNodeConfig(name, "command".to_string())); + }; - let subcommand = options.subcommand.clone(); + let subcommand = options.subcommand.clone(); - // If `args` is set at `node` level use them - // otherwise use the default_args (can be empty). - let args: Vec = if options.args.is_empty() { - chain_context - .default_args - .iter() - .map(|x| x.to_owned().clone()) - .collect() - } else { - options.args - }; + // If `args` is set at `node` level use them + // otherwise use the default_args (can be empty). + let args: Vec = if options.args.is_empty() { + chain_context.default_args.iter().map(|x| x.to_owned().clone()).collect() + } else { + options.args + }; - let (key, peer_id) = generators::generate_node_identity(&name)?; + let (key, peer_id) = generators::generate_node_identity(&name)?; - let mut name_capitalized = name.clone(); - let seed = format!( - "//{}{name_capitalized}", - name_capitalized.remove(0).to_uppercase() - ); - let accounts = generators::generate_node_keys(&seed)?; - let mut accounts = NodeAccounts { seed, accounts }; + let mut name_capitalized = name.clone(); + let seed = format!("//{}{name_capitalized}", name_capitalized.remove(0).to_uppercase()); + let accounts = generators::generate_node_keys(&seed)?; + let mut accounts = NodeAccounts { seed, accounts }; - if evm_based { - if let Some(session_key) = options.override_eth_key.as_ref() { - accounts - .accounts - .insert("eth".into(), NodeAccount::new(session_key, session_key)); - } - } + if evm_based { + if let Some(session_key) = options.override_eth_key.as_ref() { + accounts.accounts.insert("eth".into(), NodeAccount::new(session_key, session_key)); + } + } - let (full_node_p2p_port, full_node_prometheus_port) = if full_node_present { - ( - Some(generators::generate_node_port(None)?), - Some(generators::generate_node_port(None)?), - ) - } else { - (None, None) - }; + let (full_node_p2p_port, full_node_prometheus_port) = if full_node_present { + ( + Some(generators::generate_node_port(None)?), + Some(generators::generate_node_port(None)?), + ) + } else { + (None, None) + }; - // - Ok(Self { - name, - key, - peer_id, - image, - command, - subcommand, - args, - available_args_output: None, - is_validator: options.is_validator, - is_invulnerable: false, - is_bootnode: false, - initial_balance: 0, - env: options.env, - bootnodes_addresses: vec![], - resources: None, - p2p_cert_hash: None, - db_snapshot: None, - accounts, - // should be deprecated now! - ws_port: generators::generate_node_port(None)?, - rpc_port: generators::generate_node_port(options.rpc_port)?, - prometheus_port: generators::generate_node_port(options.prometheus_port)?, - p2p_port: generators::generate_node_port(options.p2p_port)?, - full_node_p2p_port, - full_node_prometheus_port, - node_log_path: None, - keystore_path: None, - keystore_key_types: vec![], - }) - } + // + Ok(Self { + name, + key, + peer_id, + image, + command, + subcommand, + args, + available_args_output: None, + is_validator: options.is_validator, + is_invulnerable: false, + is_bootnode: false, + initial_balance: 0, + env: options.env, + bootnodes_addresses: vec![], + resources: None, + p2p_cert_hash: None, + db_snapshot: None, + accounts, + // should be deprecated now! + ws_port: generators::generate_node_port(None)?, + rpc_port: generators::generate_node_port(options.rpc_port)?, + prometheus_port: generators::generate_node_port(options.prometheus_port)?, + p2p_port: generators::generate_node_port(options.p2p_port)?, + full_node_p2p_port, + full_node_prometheus_port, + node_log_path: None, + keystore_path: None, + keystore_key_types: vec![], + }) + } - pub(crate) fn supports_arg(&self, arg: impl AsRef) -> bool { - self.available_args_output - .as_ref() - .expect(&format!( - "available args should be present at this point {THIS_IS_A_BUG}" - )) - .contains(arg.as_ref()) - } + pub(crate) fn supports_arg(&self, arg: impl AsRef) -> bool { + self.available_args_output + .as_ref() + .expect(&format!("available args should be present at this point {THIS_IS_A_BUG}")) + .contains(arg.as_ref()) + } - pub fn command(&self) -> &str { - self.command.as_str() - } + pub fn command(&self) -> &str { + self.command.as_str() + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_spec/relaychain.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_spec/relaychain.rs index 0157d831..82bca3d9 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_spec/relaychain.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_spec/relaychain.rs @@ -1,181 +1,181 @@ use std::collections::{HashMap, HashSet}; use configuration::{ - shared::{ - helpers::generate_unique_node_name_from_names, - resources::Resources, - types::{Arg, AssetLocation, Chain, Command, Image}, - }, - types::JsonOverrides, - NodeConfig, RelaychainConfig, + shared::{ + helpers::generate_unique_node_name_from_names, + resources::Resources, + types::{Arg, AssetLocation, Chain, Command, Image}, + }, + types::JsonOverrides, + NodeConfig, RelaychainConfig, }; use serde::{Deserialize, Serialize}; use support::replacer::apply_replacements; use super::node::NodeSpec; use crate::{ - errors::OrchestratorError, - generators::chain_spec::{ChainSpec, Context}, - shared::{constants::DEFAULT_CHAIN_SPEC_TPL_COMMAND, types::ChainDefaultContext}, + errors::OrchestratorError, + generators::chain_spec::{ChainSpec, Context}, + shared::{constants::DEFAULT_CHAIN_SPEC_TPL_COMMAND, types::ChainDefaultContext}, }; /// A relaychain configuration spec #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RelaychainSpec { - /// Chain to use (e.g. rococo-local). - pub(crate) chain: Chain, + /// Chain to use (e.g. rococo-local). + pub(crate) chain: Chain, - /// Default command to run the node. Can be overridden on each node. - pub(crate) default_command: Option, + /// Default command to run the node. Can be overridden on each node. + pub(crate) default_command: Option, - /// Default image to use (only podman/k8s). Can be overridden on each node. - pub(crate) default_image: Option, + /// Default image to use (only podman/k8s). Can be overridden on each node. + pub(crate) default_image: Option, - /// Default resources. Can be overridden on each node. - pub(crate) default_resources: Option, + /// Default resources. Can be overridden on each node. + pub(crate) default_resources: Option, - /// Default database snapshot. Can be overridden on each node. - pub(crate) default_db_snapshot: Option, + /// Default database snapshot. Can be overridden on each node. + pub(crate) default_db_snapshot: Option, - /// Default arguments to use in nodes. Can be overridden on each node. - pub(crate) default_args: Vec, + /// Default arguments to use in nodes. Can be overridden on each node. + pub(crate) default_args: Vec, - // chain_spec_path: Option, - pub(crate) chain_spec: ChainSpec, + // chain_spec_path: Option, + pub(crate) chain_spec: ChainSpec, - /// Set the count of nominators to generator (used with PoS networks). - pub(crate) random_nominators_count: u32, + /// Set the count of nominators to generator (used with PoS networks). + pub(crate) random_nominators_count: u32, - /// Set the max nominators value (used with PoS networks). - pub(crate) max_nominations: u8, + /// Set the max nominators value (used with PoS networks). + pub(crate) max_nominations: u8, - /// Genesis overrides as JSON value. - pub(crate) runtime_genesis_patch: Option, + /// Genesis overrides as JSON value. + pub(crate) runtime_genesis_patch: Option, - /// Wasm override path/url to use. - pub(crate) wasm_override: Option, + /// Wasm override path/url to use. + pub(crate) wasm_override: Option, - /// Nodes to run. - pub(crate) nodes: Vec, + /// Nodes to run. + pub(crate) nodes: Vec, - /// Raw chain-spec override path, url or inline json to use. - pub(crate) raw_spec_override: Option, + /// Raw chain-spec override path, url or inline json to use. + pub(crate) raw_spec_override: Option, } impl RelaychainSpec { - pub fn from_config(config: &RelaychainConfig) -> Result { - // Relaychain main command to use, in order: - // set as `default_command` or - // use the command of the first node. - // If non of those is set, return an error. - let main_cmd = config - .default_command() - .or(config.nodes().first().and_then(|node| node.command())) - .ok_or(OrchestratorError::InvalidConfig( - "Relaychain, either default_command or first node with a command needs to be set." - .to_string(), - ))?; + pub fn from_config(config: &RelaychainConfig) -> Result { + // Relaychain main command to use, in order: + // set as `default_command` or + // use the command of the first node. + // If non of those is set, return an error. + let main_cmd = config + .default_command() + .or(config.nodes().first().and_then(|node| node.command())) + .ok_or(OrchestratorError::InvalidConfig( + "Relaychain, either default_command or first node with a command needs to be set." + .to_string(), + ))?; - // TODO: internally we use image as String - let main_image = config - .default_image() - .or(config.nodes().first().and_then(|node| node.image())) - .map(|image| image.as_str().to_string()); + // TODO: internally we use image as String + let main_image = config + .default_image() + .or(config.nodes().first().and_then(|node| node.image())) + .map(|image| image.as_str().to_string()); - let replacements = HashMap::from([ - ("disableBootnodes", "--disable-default-bootnode"), - ("mainCommand", main_cmd.as_str()), - ]); - let tmpl = if let Some(tmpl) = config.chain_spec_command() { - apply_replacements(tmpl, &replacements) - } else { - apply_replacements(DEFAULT_CHAIN_SPEC_TPL_COMMAND, &replacements) - }; + let replacements = HashMap::from([ + ("disableBootnodes", "--disable-default-bootnode"), + ("mainCommand", main_cmd.as_str()), + ]); + let tmpl = if let Some(tmpl) = config.chain_spec_command() { + apply_replacements(tmpl, &replacements) + } else { + apply_replacements(DEFAULT_CHAIN_SPEC_TPL_COMMAND, &replacements) + }; - let chain_spec = ChainSpec::new(config.chain().as_str(), Context::Relay) - .set_chain_name(config.chain().as_str()) - .command( - tmpl.as_str(), - config.chain_spec_command_is_local(), - config.chain_spec_command_output_path(), - ) - .image(main_image.clone()); + let chain_spec = ChainSpec::new(config.chain().as_str(), Context::Relay) + .set_chain_name(config.chain().as_str()) + .command( + tmpl.as_str(), + config.chain_spec_command_is_local(), + config.chain_spec_command_output_path(), + ) + .image(main_image.clone()); - // Add asset location if present - let chain_spec = if let Some(chain_spec_path) = config.chain_spec_path() { - chain_spec.asset_location(chain_spec_path.clone()) - } else { - chain_spec - }; + // Add asset location if present + let chain_spec = if let Some(chain_spec_path) = config.chain_spec_path() { + chain_spec.asset_location(chain_spec_path.clone()) + } else { + chain_spec + }; - // add chain-spec runtime if present - let chain_spec = if let Some(chain_spec_runtime) = config.chain_spec_runtime() { - chain_spec.runtime(chain_spec_runtime.clone()) - } else { - chain_spec - }; + // add chain-spec runtime if present + let chain_spec = if let Some(chain_spec_runtime) = config.chain_spec_runtime() { + chain_spec.runtime(chain_spec_runtime.clone()) + } else { + chain_spec + }; - // build the `node_specs` - let chain_context = ChainDefaultContext { - default_command: config.default_command(), - default_image: config.default_image(), - default_resources: config.default_resources(), - default_db_snapshot: config.default_db_snapshot(), - default_args: config.default_args(), - }; + // build the `node_specs` + let chain_context = ChainDefaultContext { + default_command: config.default_command(), + default_image: config.default_image(), + default_resources: config.default_resources(), + default_db_snapshot: config.default_db_snapshot(), + default_args: config.default_args(), + }; - let mut nodes: Vec = config.nodes().into_iter().cloned().collect(); - nodes.extend( - config - .group_node_configs() - .into_iter() - .flat_map(|node_group| node_group.expand_group_configs()), - ); + let mut nodes: Vec = config.nodes().into_iter().cloned().collect(); + nodes.extend( + config + .group_node_configs() + .into_iter() + .flat_map(|node_group| node_group.expand_group_configs()), + ); - let mut names = HashSet::new(); - let (nodes, mut errs) = nodes - .iter() - .map(|node_config| NodeSpec::from_config(node_config, &chain_context, false, false)) - .fold((vec![], vec![]), |(mut nodes, mut errs), result| { - match result { - Ok(mut node) => { - let unique_name = - generate_unique_node_name_from_names(node.name, &mut names); - node.name = unique_name; - nodes.push(node); - }, - Err(err) => errs.push(err), - } - (nodes, errs) - }); + let mut names = HashSet::new(); + let (nodes, mut errs) = nodes + .iter() + .map(|node_config| NodeSpec::from_config(node_config, &chain_context, false, false)) + .fold((vec![], vec![]), |(mut nodes, mut errs), result| { + match result { + Ok(mut node) => { + let unique_name = + generate_unique_node_name_from_names(node.name, &mut names); + node.name = unique_name; + nodes.push(node); + }, + Err(err) => errs.push(err), + } + (nodes, errs) + }); - if !errs.is_empty() { - // TODO: merge errs, maybe return something like Result> - return Err(errs.swap_remove(0)); - } + if !errs.is_empty() { + // TODO: merge errs, maybe return something like Result> + return Err(errs.swap_remove(0)); + } - Ok(RelaychainSpec { - chain: config.chain().clone(), - default_command: config.default_command().cloned(), - default_image: config.default_image().cloned(), - default_resources: config.default_resources().cloned(), - default_db_snapshot: config.default_db_snapshot().cloned(), - wasm_override: config.wasm_override().cloned(), - default_args: config.default_args().into_iter().cloned().collect(), - chain_spec, - random_nominators_count: config.random_nominators_count().unwrap_or(0), - max_nominations: config.max_nominations().unwrap_or(24), - runtime_genesis_patch: config.runtime_genesis_patch().cloned(), - nodes, - raw_spec_override: config.raw_spec_override().cloned(), - }) - } + Ok(RelaychainSpec { + chain: config.chain().clone(), + default_command: config.default_command().cloned(), + default_image: config.default_image().cloned(), + default_resources: config.default_resources().cloned(), + default_db_snapshot: config.default_db_snapshot().cloned(), + wasm_override: config.wasm_override().cloned(), + default_args: config.default_args().into_iter().cloned().collect(), + chain_spec, + random_nominators_count: config.random_nominators_count().unwrap_or(0), + max_nominations: config.max_nominations().unwrap_or(24), + runtime_genesis_patch: config.runtime_genesis_patch().cloned(), + nodes, + raw_spec_override: config.raw_spec_override().cloned(), + }) + } - pub fn chain_spec(&self) -> &ChainSpec { - &self.chain_spec - } + pub fn chain_spec(&self) -> &ChainSpec { + &self.chain_spec + } - pub fn chain_spec_mut(&mut self) -> &mut ChainSpec { - &mut self.chain_spec - } + pub fn chain_spec_mut(&mut self) -> &mut ChainSpec { + &mut self.chain_spec + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_spec/teyrchain.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_spec/teyrchain.rs index 2813d9fe..dfac012c 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_spec/teyrchain.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/network_spec/teyrchain.rs @@ -1,12 +1,12 @@ use std::{ - collections::{HashMap, HashSet}, - path::PathBuf, + collections::{HashMap, HashSet}, + path::PathBuf, }; use configuration::{ - shared::{helpers::generate_unique_node_name_from_names, resources::Resources}, - types::{Arg, AssetLocation, Chain, Command, Image, JsonOverrides}, - NodeConfig, ParachainConfig, RegistrationStrategy, + shared::{helpers::generate_unique_node_name_from_names, resources::Resources}, + types::{Arg, AssetLocation, Chain, Command, Image, JsonOverrides}, + NodeConfig, ParachainConfig, RegistrationStrategy, }; use provider::DynNamespace; use serde::{Deserialize, Serialize}; @@ -15,372 +15,348 @@ use tracing::debug; use super::node::NodeSpec; use crate::{ - errors::OrchestratorError, - generators::{ - chain_spec::{ChainSpec, Context, ParaGenesisConfig}, - para_artifact::*, - }, - shared::{constants::DEFAULT_CHAIN_SPEC_TPL_COMMAND, types::ChainDefaultContext}, - ScopedFilesystem, + errors::OrchestratorError, + generators::{ + chain_spec::{ChainSpec, Context, ParaGenesisConfig}, + para_artifact::*, + }, + shared::{constants::DEFAULT_CHAIN_SPEC_TPL_COMMAND, types::ChainDefaultContext}, + ScopedFilesystem, }; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TeyrchainSpec { - // `name` of the parachain (used in some corner cases) - // name: Option, - /// Parachain id - pub(crate) id: u32, + // `name` of the parachain (used in some corner cases) + // name: Option, + /// Parachain id + pub(crate) id: u32, - /// Unique id of the parachain, in the patter of - - /// where the suffix is only present if more than one parachain is set with the same id - pub(crate) unique_id: String, + /// Unique id of the parachain, in the patter of - + /// where the suffix is only present if more than one parachain is set with the same id + pub(crate) unique_id: String, - /// Default command to run the node. Can be overridden on each node. - pub(crate) default_command: Option, + /// Default command to run the node. Can be overridden on each node. + pub(crate) default_command: Option, - /// Default image to use (only podman/k8s). Can be overridden on each node. - pub(crate) default_image: Option, + /// Default image to use (only podman/k8s). Can be overridden on each node. + pub(crate) default_image: Option, - /// Default resources. Can be overridden on each node. - pub(crate) default_resources: Option, + /// Default resources. Can be overridden on each node. + pub(crate) default_resources: Option, - /// Default database snapshot. Can be overridden on each node. - pub(crate) default_db_snapshot: Option, + /// Default database snapshot. Can be overridden on each node. + pub(crate) default_db_snapshot: Option, - /// Default arguments to use in nodes. Can be overridden on each node. - pub(crate) default_args: Vec, + /// Default arguments to use in nodes. Can be overridden on each node. + pub(crate) default_args: Vec, - /// Chain-spec, only needed by cumulus based paras - pub(crate) chain_spec: Option, + /// Chain-spec, only needed by cumulus based paras + pub(crate) chain_spec: Option, - /// Do not automatically assign a bootnode role if no nodes are marked as bootnodes. - pub(crate) no_default_bootnodes: bool, + /// Do not automatically assign a bootnode role if no nodes are marked as bootnodes. + pub(crate) no_default_bootnodes: bool, - /// Registration strategy to use - pub(crate) registration_strategy: RegistrationStrategy, + /// Registration strategy to use + pub(crate) registration_strategy: RegistrationStrategy, - /// Onboard as parachain or parathread - pub(crate) onboard_as_parachain: bool, + /// Onboard as parachain or parathread + pub(crate) onboard_as_parachain: bool, - /// Is the parachain cumulus-based - pub(crate) is_cumulus_based: bool, + /// Is the parachain cumulus-based + pub(crate) is_cumulus_based: bool, - /// Is the parachain evm-based - pub(crate) is_evm_based: bool, + /// Is the parachain evm-based + pub(crate) is_evm_based: bool, - /// Initial balance - pub(crate) initial_balance: u128, + /// Initial balance + pub(crate) initial_balance: u128, - /// Genesis state (head) to register the parachain - pub(crate) genesis_state: ParaArtifact, + /// Genesis state (head) to register the parachain + pub(crate) genesis_state: ParaArtifact, - /// Genesis WASM to register the parachain - pub(crate) genesis_wasm: ParaArtifact, + /// Genesis WASM to register the parachain + pub(crate) genesis_wasm: ParaArtifact, - /// Genesis overrides as JSON value. - pub(crate) genesis_overrides: Option, + /// Genesis overrides as JSON value. + pub(crate) genesis_overrides: Option, - /// Wasm override path/url to use. - pub(crate) wasm_override: Option, + /// Wasm override path/url to use. + pub(crate) wasm_override: Option, - /// Collators to spawn - pub(crate) collators: Vec, + /// Collators to spawn + pub(crate) collators: Vec, - /// Raw chain-spec override path, url or inline json to use. - pub(crate) raw_spec_override: Option, + /// Raw chain-spec override path, url or inline json to use. + pub(crate) raw_spec_override: Option, - /// Bootnodes addresses to use for the parachain nodes - pub(crate) bootnodes_addresses: Vec, + /// Bootnodes addresses to use for the parachain nodes + pub(crate) bootnodes_addresses: Vec, } impl TeyrchainSpec { - pub fn from_config( - config: &ParachainConfig, - relay_chain: Chain, - ) -> Result { - let main_cmd = if let Some(cmd) = config.default_command() { - cmd - } else if let Some(first_node) = config.collators().first() { - let Some(cmd) = first_node.command() else { - return Err(OrchestratorError::InvalidConfig(format!("Parachain {}, either default_command or command in the first node needs to be set.", config.id()))); - }; + pub fn from_config( + config: &ParachainConfig, + relay_chain: Chain, + ) -> Result { + let main_cmd = if let Some(cmd) = config.default_command() { + cmd + } else if let Some(first_node) = config.collators().first() { + let Some(cmd) = first_node.command() else { + return Err(OrchestratorError::InvalidConfig(format!("Parachain {}, either default_command or command in the first node needs to be set.", config.id()))); + }; - cmd - } else { - return Err(OrchestratorError::InvalidConfig(format!( - "Parachain {}, without nodes and default_command isn't set.", - config.id() - ))); - }; + cmd + } else { + return Err(OrchestratorError::InvalidConfig(format!( + "Parachain {}, without nodes and default_command isn't set.", + config.id() + ))); + }; - // TODO: internally we use image as String - let main_image = config - .default_image() - .or(config.collators().first().and_then(|node| node.image())) - .map(|image| image.as_str().to_string()); + // TODO: internally we use image as String + let main_image = config + .default_image() + .or(config.collators().first().and_then(|node| node.image())) + .map(|image| image.as_str().to_string()); - let chain_spec = if config.is_cumulus_based() { - // we need a chain-spec - let chain_name = if let Some(chain_name) = config.chain() { - chain_name.as_str() - } else { - "" - }; + let chain_spec = if config.is_cumulus_based() { + // we need a chain-spec + let chain_name = + if let Some(chain_name) = config.chain() { chain_name.as_str() } else { "" }; - let chain_spec_builder = if chain_name.is_empty() { - // if the chain don't have name use the unique_id for the name of the file - ChainSpec::new( - config.unique_id().to_string(), - Context::Para { - relay_chain, - para_id: config.id(), - }, - ) - } else { - let chain_spec_file_name = if config.unique_id().contains('-') { - &format!("{}-{}", chain_name, config.unique_id()) - } else { - chain_name - }; - ChainSpec::new( - chain_spec_file_name, - Context::Para { - relay_chain, - para_id: config.id(), - }, - ) - }; - let chain_spec_builder = chain_spec_builder.set_chain_name(chain_name); + let chain_spec_builder = if chain_name.is_empty() { + // if the chain don't have name use the unique_id for the name of the file + ChainSpec::new( + config.unique_id().to_string(), + Context::Para { relay_chain, para_id: config.id() }, + ) + } else { + let chain_spec_file_name = if config.unique_id().contains('-') { + &format!("{}-{}", chain_name, config.unique_id()) + } else { + chain_name + }; + ChainSpec::new( + chain_spec_file_name, + Context::Para { relay_chain, para_id: config.id() }, + ) + }; + let chain_spec_builder = chain_spec_builder.set_chain_name(chain_name); - let replacements = HashMap::from([ - ("disableBootnodes", "--disable-default-bootnode"), - ("mainCommand", main_cmd.as_str()), - ]); - let tmpl = if let Some(tmpl) = config.chain_spec_command() { - apply_replacements(tmpl, &replacements) - } else { - apply_replacements(DEFAULT_CHAIN_SPEC_TPL_COMMAND, &replacements) - }; + let replacements = HashMap::from([ + ("disableBootnodes", "--disable-default-bootnode"), + ("mainCommand", main_cmd.as_str()), + ]); + let tmpl = if let Some(tmpl) = config.chain_spec_command() { + apply_replacements(tmpl, &replacements) + } else { + apply_replacements(DEFAULT_CHAIN_SPEC_TPL_COMMAND, &replacements) + }; - let chain_spec = chain_spec_builder - .command( - tmpl.as_str(), - config.chain_spec_command_is_local(), - config.chain_spec_command_output_path(), - ) - .image(main_image.clone()); + let chain_spec = chain_spec_builder + .command( + tmpl.as_str(), + config.chain_spec_command_is_local(), + config.chain_spec_command_output_path(), + ) + .image(main_image.clone()); - let chain_spec = if let Some(chain_spec_path) = config.chain_spec_path() { - chain_spec.asset_location(chain_spec_path.clone()) - } else { - chain_spec - }; + let chain_spec = if let Some(chain_spec_path) = config.chain_spec_path() { + chain_spec.asset_location(chain_spec_path.clone()) + } else { + chain_spec + }; - // add chain-spec runtime if present - let chain_spec = if let Some(chain_spec_runtime) = config.chain_spec_runtime() { - chain_spec.runtime(chain_spec_runtime.clone()) - } else { - chain_spec - }; + // add chain-spec runtime if present + let chain_spec = if let Some(chain_spec_runtime) = config.chain_spec_runtime() { + chain_spec.runtime(chain_spec_runtime.clone()) + } else { + chain_spec + }; - Some(chain_spec) - } else { - None - }; + Some(chain_spec) + } else { + None + }; - // build the `node_specs` - let chain_context = ChainDefaultContext { - default_command: config.default_command(), - default_image: config.default_image(), - default_resources: config.default_resources(), - default_db_snapshot: config.default_db_snapshot(), - default_args: config.default_args(), - }; + // build the `node_specs` + let chain_context = ChainDefaultContext { + default_command: config.default_command(), + default_image: config.default_image(), + default_resources: config.default_resources(), + default_db_snapshot: config.default_db_snapshot(), + default_args: config.default_args(), + }; - // We want to track the errors for all the nodes and report them ones - let mut errs: Vec = Default::default(); - let mut collators: Vec = Default::default(); + // We want to track the errors for all the nodes and report them ones + let mut errs: Vec = Default::default(); + let mut collators: Vec = Default::default(); - let mut nodes: Vec = config.collators().into_iter().cloned().collect(); - nodes.extend( - config - .group_collators_configs() - .into_iter() - .flat_map(|node_group| node_group.expand_group_configs()), - ); + let mut nodes: Vec = config.collators().into_iter().cloned().collect(); + nodes.extend( + config + .group_collators_configs() + .into_iter() + .flat_map(|node_group| node_group.expand_group_configs()), + ); - let mut names = HashSet::new(); - for node_config in nodes { - match NodeSpec::from_config(&node_config, &chain_context, true, config.is_evm_based()) { - Ok(mut node) => { - let unique_name = generate_unique_node_name_from_names(node.name, &mut names); - node.name = unique_name; - collators.push(node) - }, - Err(err) => errs.push(err), - } - } - let genesis_state = if let Some(path) = config.genesis_state_path() { - ParaArtifact::new( - ParaArtifactType::State, - ParaArtifactBuildOption::Path(path.to_string()), - ) - } else { - let cmd = if let Some(cmd) = config.genesis_state_generator() { - cmd.cmd() - } else { - main_cmd - }; - ParaArtifact::new( - ParaArtifactType::State, - ParaArtifactBuildOption::Command(cmd.as_str().into()), - ) - .image(main_image.clone()) - }; + let mut names = HashSet::new(); + for node_config in nodes { + match NodeSpec::from_config(&node_config, &chain_context, true, config.is_evm_based()) { + Ok(mut node) => { + let unique_name = generate_unique_node_name_from_names(node.name, &mut names); + node.name = unique_name; + collators.push(node) + }, + Err(err) => errs.push(err), + } + } + let genesis_state = if let Some(path) = config.genesis_state_path() { + ParaArtifact::new( + ParaArtifactType::State, + ParaArtifactBuildOption::Path(path.to_string()), + ) + } else { + let cmd = + if let Some(cmd) = config.genesis_state_generator() { cmd.cmd() } else { main_cmd }; + ParaArtifact::new( + ParaArtifactType::State, + ParaArtifactBuildOption::Command(cmd.as_str().into()), + ) + .image(main_image.clone()) + }; - let genesis_wasm = if let Some(path) = config.genesis_wasm_path() { - ParaArtifact::new( - ParaArtifactType::Wasm, - ParaArtifactBuildOption::Path(path.to_string()), - ) - } else { - let cmd = if let Some(cmd) = config.genesis_wasm_generator() { - cmd.as_str() - } else { - main_cmd.as_str() - }; - ParaArtifact::new( - ParaArtifactType::Wasm, - ParaArtifactBuildOption::Command(cmd.into()), - ) - .image(main_image.clone()) - }; + let genesis_wasm = if let Some(path) = config.genesis_wasm_path() { + ParaArtifact::new( + ParaArtifactType::Wasm, + ParaArtifactBuildOption::Path(path.to_string()), + ) + } else { + let cmd = if let Some(cmd) = config.genesis_wasm_generator() { + cmd.as_str() + } else { + main_cmd.as_str() + }; + ParaArtifact::new(ParaArtifactType::Wasm, ParaArtifactBuildOption::Command(cmd.into())) + .image(main_image.clone()) + }; - let para_spec = TeyrchainSpec { - id: config.id(), - // ensure unique id is set at this point, if not just set to the para_id - unique_id: if config.unique_id().is_empty() { - config.id().to_string() - } else { - config.unique_id().to_string() - }, - default_command: config.default_command().cloned(), - default_image: config.default_image().cloned(), - default_resources: config.default_resources().cloned(), - default_db_snapshot: config.default_db_snapshot().cloned(), - wasm_override: config.wasm_override().cloned(), - default_args: config.default_args().into_iter().cloned().collect(), - chain_spec, - no_default_bootnodes: config.no_default_bootnodes(), - registration_strategy: config - .registration_strategy() - .unwrap_or(&RegistrationStrategy::InGenesis) - .clone(), - onboard_as_parachain: config.onboard_as_parachain(), - is_cumulus_based: config.is_cumulus_based(), - is_evm_based: config.is_evm_based(), - initial_balance: config.initial_balance(), - genesis_state, - genesis_wasm, - genesis_overrides: config.genesis_overrides().cloned(), - collators, - raw_spec_override: config.raw_spec_override().cloned(), - bootnodes_addresses: config.bootnodes_addresses().into_iter().cloned().collect(), - }; + let para_spec = TeyrchainSpec { + id: config.id(), + // ensure unique id is set at this point, if not just set to the para_id + unique_id: if config.unique_id().is_empty() { + config.id().to_string() + } else { + config.unique_id().to_string() + }, + default_command: config.default_command().cloned(), + default_image: config.default_image().cloned(), + default_resources: config.default_resources().cloned(), + default_db_snapshot: config.default_db_snapshot().cloned(), + wasm_override: config.wasm_override().cloned(), + default_args: config.default_args().into_iter().cloned().collect(), + chain_spec, + no_default_bootnodes: config.no_default_bootnodes(), + registration_strategy: config + .registration_strategy() + .unwrap_or(&RegistrationStrategy::InGenesis) + .clone(), + onboard_as_parachain: config.onboard_as_parachain(), + is_cumulus_based: config.is_cumulus_based(), + is_evm_based: config.is_evm_based(), + initial_balance: config.initial_balance(), + genesis_state, + genesis_wasm, + genesis_overrides: config.genesis_overrides().cloned(), + collators, + raw_spec_override: config.raw_spec_override().cloned(), + bootnodes_addresses: config.bootnodes_addresses().into_iter().cloned().collect(), + }; - Ok(para_spec) - } + Ok(para_spec) + } - pub fn registration_strategy(&self) -> &RegistrationStrategy { - &self.registration_strategy - } + pub fn registration_strategy(&self) -> &RegistrationStrategy { + &self.registration_strategy + } - pub fn get_genesis_config(&self) -> Result, OrchestratorError> { - let genesis_config = ParaGenesisConfig { - state_path: self.genesis_state.artifact_path().ok_or( - OrchestratorError::InvariantError( - "artifact path for state must be set at this point", - ), - )?, - wasm_path: self.genesis_wasm.artifact_path().ok_or( - OrchestratorError::InvariantError( - "artifact path for wasm must be set at this point", - ), - )?, - id: self.id, - as_parachain: self.onboard_as_parachain, - }; - Ok(genesis_config) - } + pub fn get_genesis_config(&self) -> Result, OrchestratorError> { + let genesis_config = ParaGenesisConfig { + state_path: self.genesis_state.artifact_path().ok_or( + OrchestratorError::InvariantError( + "artifact path for state must be set at this point", + ), + )?, + wasm_path: self.genesis_wasm.artifact_path().ok_or( + OrchestratorError::InvariantError( + "artifact path for wasm must be set at this point", + ), + )?, + id: self.id, + as_parachain: self.onboard_as_parachain, + }; + Ok(genesis_config) + } - pub fn id(&self) -> u32 { - self.id - } + pub fn id(&self) -> u32 { + self.id + } - pub fn chain_spec(&self) -> Option<&ChainSpec> { - self.chain_spec.as_ref() - } + pub fn chain_spec(&self) -> Option<&ChainSpec> { + self.chain_spec.as_ref() + } - pub fn chain_spec_mut(&mut self) -> Option<&mut ChainSpec> { - self.chain_spec.as_mut() - } + pub fn chain_spec_mut(&mut self) -> Option<&mut ChainSpec> { + self.chain_spec.as_mut() + } - /// Build parachain chain-spec - /// - /// This function customize the chain-spec (if is possible) and build the raw version - /// of the chain-spec. - pub(crate) async fn build_chain_spec<'a, T>( - &mut self, - relay_chain_id: &str, - ns: &DynNamespace, - scoped_fs: &ScopedFilesystem<'a, T>, - ) -> Result, anyhow::Error> - where - T: FileSystem, - { - let cloned = self.clone(); - let chain_spec_raw_path = if let Some(chain_spec) = self.chain_spec.as_mut() { - debug!("parachain chain-spec building!"); - chain_spec.build(ns, scoped_fs).await?; - debug!("parachain chain-spec built!"); + /// Build parachain chain-spec + /// + /// This function customize the chain-spec (if is possible) and build the raw version + /// of the chain-spec. + pub(crate) async fn build_chain_spec<'a, T>( + &mut self, + relay_chain_id: &str, + ns: &DynNamespace, + scoped_fs: &ScopedFilesystem<'a, T>, + ) -> Result, anyhow::Error> + where + T: FileSystem, + { + let cloned = self.clone(); + let chain_spec_raw_path = if let Some(chain_spec) = self.chain_spec.as_mut() { + debug!("parachain chain-spec building!"); + chain_spec.build(ns, scoped_fs).await?; + debug!("parachain chain-spec built!"); - chain_spec - .customize_para(&cloned, relay_chain_id, scoped_fs) - .await?; - debug!("parachain chain-spec customized!"); - chain_spec - .build_raw(ns, scoped_fs, Some(relay_chain_id.try_into()?)) - .await?; - debug!("parachain chain-spec raw built!"); + chain_spec.customize_para(&cloned, relay_chain_id, scoped_fs).await?; + debug!("parachain chain-spec customized!"); + chain_spec.build_raw(ns, scoped_fs, Some(relay_chain_id.try_into()?)).await?; + debug!("parachain chain-spec raw built!"); - // override wasm if needed - if let Some(ref wasm_override) = self.wasm_override { - chain_spec.override_code(scoped_fs, wasm_override).await?; - } + // override wasm if needed + if let Some(ref wasm_override) = self.wasm_override { + chain_spec.override_code(scoped_fs, wasm_override).await?; + } - // override raw spec if needed - if let Some(ref raw_spec_override) = self.raw_spec_override { - chain_spec - .override_raw_spec(scoped_fs, raw_spec_override) - .await?; - } + // override raw spec if needed + if let Some(ref raw_spec_override) = self.raw_spec_override { + chain_spec.override_raw_spec(scoped_fs, raw_spec_override).await?; + } - let chain_spec_raw_path = - chain_spec - .raw_path() - .ok_or(OrchestratorError::InvariantError( - "chain-spec raw path should be set now", - ))?; + let chain_spec_raw_path = chain_spec.raw_path().ok_or( + OrchestratorError::InvariantError("chain-spec raw path should be set now"), + )?; - Some(chain_spec_raw_path.to_path_buf()) - } else { - None - }; - Ok(chain_spec_raw_path) - } + Some(chain_spec_raw_path.to_path_buf()) + } else { + None + }; + Ok(chain_spec_raw_path) + } - /// Get the bootnodes addresses for the parachain spec - pub(crate) fn bootnodes_addresses(&self) -> Vec<&multiaddr::Multiaddr> { - self.bootnodes_addresses.iter().collect() - } + /// Get the bootnodes addresses for the parachain spec + pub(crate) fn bootnodes_addresses(&self) -> Vec<&multiaddr::Multiaddr> { + self.bootnodes_addresses.iter().collect() + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/shared/constants.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/shared/constants.rs index fa9e3ca8..d146a5c6 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/shared/constants.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/shared/constants.rs @@ -10,7 +10,7 @@ pub const RPC_HTTP_PORT: u16 = 9933; pub const P2P_PORT: u16 = 30333; // default command template to build chain-spec pub const DEFAULT_CHAIN_SPEC_TPL_COMMAND: &str = - "{{mainCommand}} build-spec --chain {{chainName}} {{disableBootnodes}}"; + "{{mainCommand}} build-spec --chain {{chainName}} {{disableBootnodes}}"; // interval to determine how often to run node liveness checks pub const NODE_MONITORING_INTERVAL_SECONDS: u64 = 15; // how long to wait before a node is considered unresponsive diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/shared/types.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/shared/types.rs index 692a4b47..f8279886 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/shared/types.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/shared/types.rs @@ -1,13 +1,13 @@ use std::{ - collections::HashMap, - net::TcpListener, - path::PathBuf, - sync::{Arc, RwLock}, + collections::HashMap, + net::TcpListener, + path::PathBuf, + sync::{Arc, RwLock}, }; use configuration::shared::{ - resources::Resources, - types::{Arg, AssetLocation, Command, Image, Port}, + resources::Resources, + types::{Arg, AssetLocation, Command, Image, Port}, }; use serde::{Deserialize, Serialize}; @@ -15,85 +15,75 @@ pub type Accounts = HashMap; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct NodeAccount { - pub address: String, - pub public_key: String, + pub address: String, + pub public_key: String, } impl NodeAccount { - pub fn new(addr: impl Into, pk: impl Into) -> Self { - Self { - address: addr.into(), - public_key: pk.into(), - } - } + pub fn new(addr: impl Into, pk: impl Into) -> Self { + Self { address: addr.into(), public_key: pk.into() } + } } #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)] pub struct NodeAccounts { - pub seed: String, - pub accounts: Accounts, + pub seed: String, + pub accounts: Accounts, } #[derive(Clone, Default, Debug, Serialize, Deserialize)] -pub struct ParkedPort( - pub(crate) Port, - #[serde(skip)] pub(crate) Arc>>, -); +pub struct ParkedPort(pub(crate) Port, #[serde(skip)] pub(crate) Arc>>); impl ParkedPort { - pub(crate) fn new(port: u16, listener: TcpListener) -> ParkedPort { - let listener = Arc::new(RwLock::new(Some(listener))); - ParkedPort(port, listener) - } + pub(crate) fn new(port: u16, listener: TcpListener) -> ParkedPort { + let listener = Arc::new(RwLock::new(Some(listener))); + ParkedPort(port, listener) + } - pub(crate) fn drop_listener(&self) { - // drop the listener will allow the running node to start listenen connections - let mut l = self.1.write().unwrap(); - *l = None; - } + pub(crate) fn drop_listener(&self) { + // drop the listener will allow the running node to start listenen connections + let mut l = self.1.write().unwrap(); + *l = None; + } } #[derive(Debug, Clone, Default)] pub struct ChainDefaultContext<'a> { - pub default_command: Option<&'a Command>, - pub default_image: Option<&'a Image>, - pub default_resources: Option<&'a Resources>, - pub default_db_snapshot: Option<&'a AssetLocation>, - pub default_args: Vec<&'a Arg>, + pub default_command: Option<&'a Command>, + pub default_image: Option<&'a Image>, + pub default_resources: Option<&'a Resources>, + pub default_db_snapshot: Option<&'a AssetLocation>, + pub default_args: Vec<&'a Arg>, } #[derive(Debug, Clone)] pub struct RegisterParachainOptions { - pub id: u32, - pub wasm_path: PathBuf, - pub state_path: PathBuf, - pub node_ws_url: String, - pub onboard_as_para: bool, - pub seed: Option<[u8; 32]>, - pub finalization: bool, + pub id: u32, + pub wasm_path: PathBuf, + pub state_path: PathBuf, + pub node_ws_url: String, + pub onboard_as_para: bool, + pub seed: Option<[u8; 32]>, + pub finalization: bool, } pub struct RuntimeUpgradeOptions { - /// Location of the wasm file (could be either a local file or an url) - pub wasm: AssetLocation, - /// Name of the node to use as rpc endpoint - pub node_name: Option, - /// Seed to use to sign and submit (default to //Alice) - pub seed: Option<[u8; 32]>, + /// Location of the wasm file (could be either a local file or an url) + pub wasm: AssetLocation, + /// Name of the node to use as rpc endpoint + pub node_name: Option, + /// Seed to use to sign and submit (default to //Alice) + pub seed: Option<[u8; 32]>, } impl RuntimeUpgradeOptions { - pub fn new(wasm: AssetLocation) -> Self { - Self { - wasm, - node_name: None, - seed: None, - } - } + pub fn new(wasm: AssetLocation) -> Self { + Self { wasm, node_name: None, seed: None } + } } #[derive(Debug, Clone)] pub struct ParachainGenesisArgs { - pub genesis_head: String, - pub validation_code: String, - pub parachain: bool, + pub genesis_head: String, + pub validation_code: String, + pub parachain: bool, } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/spawner.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/spawner.rs index 47b0e55a..9c77835e 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/spawner.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/spawner.rs @@ -3,303 +3,273 @@ use std::{collections::HashMap, path::PathBuf}; use anyhow::Context; use configuration::GlobalSettings; use provider::{ - constants::{LOCALHOST, NODE_CONFIG_DIR, NODE_DATA_DIR, NODE_RELAY_DATA_DIR, P2P_PORT}, - shared::helpers::running_in_ci, - types::{SpawnNodeOptions, TransferedFile}, - DynNamespace, + constants::{LOCALHOST, NODE_CONFIG_DIR, NODE_DATA_DIR, NODE_RELAY_DATA_DIR, P2P_PORT}, + shared::helpers::running_in_ci, + types::{SpawnNodeOptions, TransferedFile}, + DynNamespace, }; use support::{ - constants::THIS_IS_A_BUG, fs::FileSystem, replacer::apply_running_network_replacements, + constants::THIS_IS_A_BUG, fs::FileSystem, replacer::apply_running_network_replacements, }; use tracing::info; use crate::{ - generators, - network::node::NetworkNode, - network_spec::{node::NodeSpec, teyrchain::TeyrchainSpec}, - shared::constants::{FULL_NODE_PROMETHEUS_PORT, PROMETHEUS_PORT, RPC_PORT}, - ScopedFilesystem, ZombieRole, + generators, + network::node::NetworkNode, + network_spec::{node::NodeSpec, teyrchain::TeyrchainSpec}, + shared::constants::{FULL_NODE_PROMETHEUS_PORT, PROMETHEUS_PORT, RPC_PORT}, + ScopedFilesystem, ZombieRole, }; #[derive(Clone)] pub struct SpawnNodeCtx<'a, T: FileSystem> { - /// Relaychain id, from the chain-spec (e.g rococo_local_testnet) - pub(crate) chain_id: &'a str, - // Parachain id, from the chain-spec (e.g local_testnet) - pub(crate) parachain_id: Option<&'a str>, - /// Relaychain chain name (e.g rococo-local) - pub(crate) chain: &'a str, - /// Role of the node in the network - pub(crate) role: ZombieRole, - /// Ref to the namespace - pub(crate) ns: &'a DynNamespace, - /// Ref to an scoped filesystem (encapsulate fs actions inside the ns directory) - pub(crate) scoped_fs: &'a ScopedFilesystem<'a, T>, - /// Ref to a parachain (used to spawn collators) - pub(crate) parachain: Option<&'a TeyrchainSpec>, - /// The string representation of the bootnode address to pass to nodes - pub(crate) bootnodes_addr: &'a Vec, - /// Flag to wait node is ready or not - /// Ready state means we can query Prometheus internal server - pub(crate) wait_ready: bool, - /// A json representation of the running nodes with their names as 'key' - pub(crate) nodes_by_name: serde_json::Value, - /// A ref to the global settings - pub(crate) global_settings: &'a GlobalSettings, + /// Relaychain id, from the chain-spec (e.g rococo_local_testnet) + pub(crate) chain_id: &'a str, + // Parachain id, from the chain-spec (e.g local_testnet) + pub(crate) parachain_id: Option<&'a str>, + /// Relaychain chain name (e.g rococo-local) + pub(crate) chain: &'a str, + /// Role of the node in the network + pub(crate) role: ZombieRole, + /// Ref to the namespace + pub(crate) ns: &'a DynNamespace, + /// Ref to an scoped filesystem (encapsulate fs actions inside the ns directory) + pub(crate) scoped_fs: &'a ScopedFilesystem<'a, T>, + /// Ref to a parachain (used to spawn collators) + pub(crate) parachain: Option<&'a TeyrchainSpec>, + /// The string representation of the bootnode address to pass to nodes + pub(crate) bootnodes_addr: &'a Vec, + /// Flag to wait node is ready or not + /// Ready state means we can query Prometheus internal server + pub(crate) wait_ready: bool, + /// A json representation of the running nodes with their names as 'key' + pub(crate) nodes_by_name: serde_json::Value, + /// A ref to the global settings + pub(crate) global_settings: &'a GlobalSettings, } pub async fn spawn_node<'a, T>( - node: &NodeSpec, - mut files_to_inject: Vec, - ctx: &SpawnNodeCtx<'a, T>, + node: &NodeSpec, + mut files_to_inject: Vec, + ctx: &SpawnNodeCtx<'a, T>, ) -> Result where - T: FileSystem, + T: FileSystem, { - let mut created_paths = vec![]; - // Create and inject the keystore IFF - // - The node is validator in the relaychain - // - The node is collator (encoded as validator) and the parachain is cumulus_based - // (parachain_id) should be set then. - if node.is_validator && (ctx.parachain.is_none() || ctx.parachain_id.is_some()) { - // Generate keystore for node - let node_files_path = if let Some(para) = ctx.parachain { - para.id.to_string() - } else { - node.name.clone() - }; - let asset_hub_polkadot = ctx - .parachain_id - .map(|id| id.starts_with("asset-hub-polkadot")) - .unwrap_or_default(); - let keystore_key_types = node.keystore_key_types.iter().map(String::as_str).collect(); - let key_filenames = generators::generate_node_keystore( - &node.accounts, - &node_files_path, - ctx.scoped_fs, - asset_hub_polkadot, - keystore_key_types, - ) - .await - .unwrap(); + let mut created_paths = vec![]; + // Create and inject the keystore IFF + // - The node is validator in the relaychain + // - The node is collator (encoded as validator) and the parachain is cumulus_based + // (parachain_id) should be set then. + if node.is_validator && (ctx.parachain.is_none() || ctx.parachain_id.is_some()) { + // Generate keystore for node + let node_files_path = + if let Some(para) = ctx.parachain { para.id.to_string() } else { node.name.clone() }; + let asset_hub_polkadot = + ctx.parachain_id.map(|id| id.starts_with("asset-hub-polkadot")).unwrap_or_default(); + let keystore_key_types = node.keystore_key_types.iter().map(String::as_str).collect(); + let key_filenames = generators::generate_node_keystore( + &node.accounts, + &node_files_path, + ctx.scoped_fs, + asset_hub_polkadot, + keystore_key_types, + ) + .await + .unwrap(); - // Paths returned are relative to the base dir, we need to convert into - // fullpaths to inject them in the nodes. - let remote_keystore_chain_id = if let Some(id) = ctx.parachain_id { - id - } else { - ctx.chain_id - }; + // Paths returned are relative to the base dir, we need to convert into + // fullpaths to inject them in the nodes. + let remote_keystore_chain_id = + if let Some(id) = ctx.parachain_id { id } else { ctx.chain_id }; - let keystore_path = node.keystore_path.clone().unwrap_or(PathBuf::from(format!( - "/data/chains/{remote_keystore_chain_id}/keystore", - ))); + let keystore_path = node + .keystore_path + .clone() + .unwrap_or(PathBuf::from(format!("/data/chains/{remote_keystore_chain_id}/keystore",))); - for key_filename in key_filenames { - let f = TransferedFile::new( - PathBuf::from(format!( - "{}/{}/{}", - ctx.ns.base_dir().to_string_lossy(), - node_files_path, - key_filename.to_string_lossy() - )), - keystore_path.join(key_filename), - ); - files_to_inject.push(f); - } - created_paths.push(keystore_path); - } + for key_filename in key_filenames { + let f = TransferedFile::new( + PathBuf::from(format!( + "{}/{}/{}", + ctx.ns.base_dir().to_string_lossy(), + node_files_path, + key_filename.to_string_lossy() + )), + keystore_path.join(key_filename), + ); + files_to_inject.push(f); + } + created_paths.push(keystore_path); + } - let base_dir = format!("{}/{}", ctx.ns.base_dir().to_string_lossy(), &node.name); + let base_dir = format!("{}/{}", ctx.ns.base_dir().to_string_lossy(), &node.name); - let (cfg_path, data_path, relay_data_path) = if !ctx.ns.capabilities().prefix_with_full_path { - ( - NODE_CONFIG_DIR.into(), - NODE_DATA_DIR.into(), - NODE_RELAY_DATA_DIR.into(), - ) - } else { - let cfg_path = format!("{}{NODE_CONFIG_DIR}", &base_dir); - let data_path = format!("{}{NODE_DATA_DIR}", &base_dir); - let relay_data_path = format!("{}{NODE_RELAY_DATA_DIR}", &base_dir); - (cfg_path, data_path, relay_data_path) - }; + let (cfg_path, data_path, relay_data_path) = if !ctx.ns.capabilities().prefix_with_full_path { + (NODE_CONFIG_DIR.into(), NODE_DATA_DIR.into(), NODE_RELAY_DATA_DIR.into()) + } else { + let cfg_path = format!("{}{NODE_CONFIG_DIR}", &base_dir); + let data_path = format!("{}{NODE_DATA_DIR}", &base_dir); + let relay_data_path = format!("{}{NODE_RELAY_DATA_DIR}", &base_dir); + (cfg_path, data_path, relay_data_path) + }; - let gen_opts = generators::GenCmdOptions { - relay_chain_name: ctx.chain, - cfg_path: &cfg_path, // TODO: get from provider/ns - data_path: &data_path, // TODO: get from provider - relay_data_path: &relay_data_path, // TODO: get from provider - use_wrapper: false, // TODO: get from provider - bootnode_addr: ctx.bootnodes_addr.clone(), - use_default_ports_in_cmd: ctx.ns.capabilities().use_default_ports_in_cmd, - // IFF the provider require an image (e.g k8s) we know this is not native - is_native: !ctx.ns.capabilities().requires_image, - }; + let gen_opts = generators::GenCmdOptions { + relay_chain_name: ctx.chain, + cfg_path: &cfg_path, // TODO: get from provider/ns + data_path: &data_path, // TODO: get from provider + relay_data_path: &relay_data_path, // TODO: get from provider + use_wrapper: false, // TODO: get from provider + bootnode_addr: ctx.bootnodes_addr.clone(), + use_default_ports_in_cmd: ctx.ns.capabilities().use_default_ports_in_cmd, + // IFF the provider require an image (e.g k8s) we know this is not native + is_native: !ctx.ns.capabilities().requires_image, + }; - let mut collator_full_node_prom_port: Option = None; - let mut collator_full_node_prom_port_external: Option = None; + let mut collator_full_node_prom_port: Option = None; + let mut collator_full_node_prom_port_external: Option = None; - let (program, args) = match ctx.role { - // Collator should be `non-cumulus` one (e.g adder/undying) - ZombieRole::Node | ZombieRole::Collator => { - let maybe_para_id = ctx.parachain.map(|para| para.id); + let (program, args) = match ctx.role { + // Collator should be `non-cumulus` one (e.g adder/undying) + ZombieRole::Node | ZombieRole::Collator => { + let maybe_para_id = ctx.parachain.map(|para| para.id); - generators::generate_node_command(node, gen_opts, maybe_para_id) - }, - ZombieRole::CumulusCollator => { - let para = ctx.parachain.expect(&format!( - "parachain must be part of the context {THIS_IS_A_BUG}" - )); - collator_full_node_prom_port = node.full_node_prometheus_port.as_ref().map(|p| p.0); + generators::generate_node_command(node, gen_opts, maybe_para_id) + }, + ZombieRole::CumulusCollator => { + let para = ctx + .parachain + .expect(&format!("parachain must be part of the context {THIS_IS_A_BUG}")); + collator_full_node_prom_port = node.full_node_prometheus_port.as_ref().map(|p| p.0); - generators::generate_node_command_cumulus(node, gen_opts, para.id) - }, - _ => unreachable!(), /* TODO: do we need those? - * ZombieRole::Bootnode => todo!(), - * ZombieRole::Companion => todo!(), */ - }; + generators::generate_node_command_cumulus(node, gen_opts, para.id) + }, + _ => unreachable!(), /* TODO: do we need those? + * ZombieRole::Bootnode => todo!(), + * ZombieRole::Companion => todo!(), */ + }; - // apply running networ replacements - let args: Vec = args - .iter() - .map(|arg| apply_running_network_replacements(arg, &ctx.nodes_by_name)) - .collect(); + // apply running networ replacements + let args: Vec = args + .iter() + .map(|arg| apply_running_network_replacements(arg, &ctx.nodes_by_name)) + .collect(); - info!( - "🚀 {}, spawning.... with command: {} {}", - node.name, - program, - args.join(" ") - ); + info!("🚀 {}, spawning.... with command: {} {}", node.name, program, args.join(" ")); - let ports = if ctx.ns.capabilities().use_default_ports_in_cmd { - // should use default ports to as internal - [ - (P2P_PORT, node.p2p_port.0), - (RPC_PORT, node.rpc_port.0), - (PROMETHEUS_PORT, node.prometheus_port.0), - ] - } else { - [ - (P2P_PORT, P2P_PORT), - (RPC_PORT, RPC_PORT), - (PROMETHEUS_PORT, PROMETHEUS_PORT), - ] - }; + let ports = if ctx.ns.capabilities().use_default_ports_in_cmd { + // should use default ports to as internal + [ + (P2P_PORT, node.p2p_port.0), + (RPC_PORT, node.rpc_port.0), + (PROMETHEUS_PORT, node.prometheus_port.0), + ] + } else { + [(P2P_PORT, P2P_PORT), (RPC_PORT, RPC_PORT), (PROMETHEUS_PORT, PROMETHEUS_PORT)] + }; - let spawn_ops = SpawnNodeOptions::new(node.name.clone(), program) - .args(args) - .env( - node.env - .iter() - .map(|var| (var.name.clone(), var.value.clone())), - ) - .injected_files(files_to_inject) - .created_paths(created_paths) - .db_snapshot(node.db_snapshot.clone()) - .port_mapping(HashMap::from(ports)) - .node_log_path(node.node_log_path.clone()); + let spawn_ops = SpawnNodeOptions::new(node.name.clone(), program) + .args(args) + .env(node.env.iter().map(|var| (var.name.clone(), var.value.clone()))) + .injected_files(files_to_inject) + .created_paths(created_paths) + .db_snapshot(node.db_snapshot.clone()) + .port_mapping(HashMap::from(ports)) + .node_log_path(node.node_log_path.clone()); - let spawn_ops = if let Some(image) = node.image.as_ref() { - spawn_ops.image(image.as_str()) - } else { - spawn_ops - }; + let spawn_ops = if let Some(image) = node.image.as_ref() { + spawn_ops.image(image.as_str()) + } else { + spawn_ops + }; - // Drops the port parking listeners before spawn - node.ws_port.drop_listener(); - node.p2p_port.drop_listener(); - node.rpc_port.drop_listener(); - node.prometheus_port.drop_listener(); - if let Some(port) = &node.full_node_p2p_port { - port.drop_listener(); - } - if let Some(port) = &node.full_node_prometheus_port { - port.drop_listener(); - } + // Drops the port parking listeners before spawn + node.ws_port.drop_listener(); + node.p2p_port.drop_listener(); + node.rpc_port.drop_listener(); + node.prometheus_port.drop_listener(); + if let Some(port) = &node.full_node_p2p_port { + port.drop_listener(); + } + if let Some(port) = &node.full_node_prometheus_port { + port.drop_listener(); + } - let running_node = ctx.ns.spawn_node(&spawn_ops).await.with_context(|| { - format!( - "Failed to spawn node: {} with opts: {:#?}", - node.name, spawn_ops - ) - })?; + let running_node = ctx.ns.spawn_node(&spawn_ops).await.with_context(|| { + format!("Failed to spawn node: {} with opts: {:#?}", node.name, spawn_ops) + })?; - let mut ip_to_use = if let Some(local_ip) = ctx.global_settings.local_ip() { - *local_ip - } else { - LOCALHOST - }; + let mut ip_to_use = + if let Some(local_ip) = ctx.global_settings.local_ip() { *local_ip } else { LOCALHOST }; - let (rpc_port_external, prometheus_port_external, p2p_external); + let (rpc_port_external, prometheus_port_external, p2p_external); - if running_in_ci() && ctx.ns.provider_name() == "k8s" { - // running kubernets in ci require to use ip and default port - (rpc_port_external, prometheus_port_external, p2p_external) = - (RPC_PORT, PROMETHEUS_PORT, P2P_PORT); - collator_full_node_prom_port_external = Some(FULL_NODE_PROMETHEUS_PORT); - ip_to_use = running_node.ip().await?; - } else { - // Create port-forward iff we are not in CI or provider doesn't use the default ports (native) - let ports = futures::future::try_join_all(vec![ - running_node.create_port_forward(node.rpc_port.0, RPC_PORT), - running_node.create_port_forward(node.prometheus_port.0, PROMETHEUS_PORT), - ]) - .await?; + if running_in_ci() && ctx.ns.provider_name() == "k8s" { + // running kubernets in ci require to use ip and default port + (rpc_port_external, prometheus_port_external, p2p_external) = + (RPC_PORT, PROMETHEUS_PORT, P2P_PORT); + collator_full_node_prom_port_external = Some(FULL_NODE_PROMETHEUS_PORT); + ip_to_use = running_node.ip().await?; + } else { + // Create port-forward iff we are not in CI or provider doesn't use the default ports (native) + let ports = futures::future::try_join_all(vec![ + running_node.create_port_forward(node.rpc_port.0, RPC_PORT), + running_node.create_port_forward(node.prometheus_port.0, PROMETHEUS_PORT), + ]) + .await?; - (rpc_port_external, prometheus_port_external, p2p_external) = ( - ports[0].unwrap_or(node.rpc_port.0), - ports[1].unwrap_or(node.prometheus_port.0), - // p2p don't need port-fwd - node.p2p_port.0, - ); + (rpc_port_external, prometheus_port_external, p2p_external) = ( + ports[0].unwrap_or(node.rpc_port.0), + ports[1].unwrap_or(node.prometheus_port.0), + // p2p don't need port-fwd + node.p2p_port.0, + ); - if let Some(full_node_prom_port) = collator_full_node_prom_port { - let port_fwd = running_node - .create_port_forward(full_node_prom_port, FULL_NODE_PROMETHEUS_PORT) - .await?; - collator_full_node_prom_port_external = Some(port_fwd.unwrap_or(full_node_prom_port)); - } - } + if let Some(full_node_prom_port) = collator_full_node_prom_port { + let port_fwd = running_node + .create_port_forward(full_node_prom_port, FULL_NODE_PROMETHEUS_PORT) + .await?; + collator_full_node_prom_port_external = Some(port_fwd.unwrap_or(full_node_prom_port)); + } + } - let multiaddr = generators::generate_node_bootnode_addr( - &node.peer_id, - &running_node.ip().await?, - p2p_external, - running_node.args().as_ref(), - &node.p2p_cert_hash, - )?; + let multiaddr = generators::generate_node_bootnode_addr( + &node.peer_id, + &running_node.ip().await?, + p2p_external, + running_node.args().as_ref(), + &node.p2p_cert_hash, + )?; - let ws_uri = format!("ws://{ip_to_use}:{rpc_port_external}"); - let prometheus_uri = format!("http://{ip_to_use}:{prometheus_port_external}/metrics"); - info!("🚀 {}, should be running now", node.name); - info!( - "💻 {}: direct link (pjs) https://polkadot.js.org/apps/?rpc={ws_uri}#/explorer", - node.name - ); - info!( + let ws_uri = format!("ws://{ip_to_use}:{rpc_port_external}"); + let prometheus_uri = format!("http://{ip_to_use}:{prometheus_port_external}/metrics"); + info!("🚀 {}, should be running now", node.name); + info!( + "💻 {}: direct link (pjs) https://polkadot.js.org/apps/?rpc={ws_uri}#/explorer", + node.name + ); + info!( "💻 {}: direct link (papi) https://dev.papi.how/explorer#networkId=custom&endpoint={ws_uri}", node.name ); - info!("📊 {}: metrics link {prometheus_uri}", node.name); + info!("📊 {}: metrics link {prometheus_uri}", node.name); - if let Some(full_node_prom_port) = collator_full_node_prom_port_external { - info!( - "📊 {}: collator full-node metrics link http://{}:{}/metrics", - node.name, ip_to_use, full_node_prom_port - ); - } + if let Some(full_node_prom_port) = collator_full_node_prom_port_external { + info!( + "📊 {}: collator full-node metrics link http://{}:{}/metrics", + node.name, ip_to_use, full_node_prom_port + ); + } - info!("📓 logs cmd: {}", running_node.log_cmd()); + info!("📓 logs cmd: {}", running_node.log_cmd()); - Ok(NetworkNode::new( - node.name.clone(), - ws_uri, - prometheus_uri, - multiaddr, - node.clone(), - running_node, - )) + Ok(NetworkNode::new( + node.name.clone(), + ws_uri, + prometheus_uri, + multiaddr, + node.clone(), + running_node, + )) } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/tx_helper/client.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/tx_helper/client.rs index e08b326b..6d1504ce 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/tx_helper/client.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/tx_helper/client.rs @@ -2,42 +2,38 @@ use pezkuwi_subxt::{backend::rpc::RpcClient, OnlineClient}; #[async_trait::async_trait] pub trait ClientFromUrl: Sized { - async fn from_secure_url(url: &str) -> Result; - async fn from_insecure_url(url: &str) -> Result; + async fn from_secure_url(url: &str) -> Result; + async fn from_insecure_url(url: &str) -> Result; } #[async_trait::async_trait] impl ClientFromUrl for OnlineClient { - async fn from_secure_url(url: &str) -> Result { - Self::from_url(url).await.map_err(Into::into) - } + async fn from_secure_url(url: &str) -> Result { + Self::from_url(url).await.map_err(Into::into) + } - async fn from_insecure_url(url: &str) -> Result { - Self::from_insecure_url(url).await.map_err(Into::into) - } + async fn from_insecure_url(url: &str) -> Result { + Self::from_insecure_url(url).await.map_err(Into::into) + } } #[async_trait::async_trait] impl ClientFromUrl for RpcClient { - async fn from_secure_url(url: &str) -> Result { - Self::from_url(url) - .await - .map_err(pezkuwi_subxt::Error::from) - } + async fn from_secure_url(url: &str) -> Result { + Self::from_url(url).await.map_err(pezkuwi_subxt::Error::from) + } - async fn from_insecure_url(url: &str) -> Result { - Self::from_insecure_url(url) - .await - .map_err(pezkuwi_subxt::Error::from) - } + async fn from_insecure_url(url: &str) -> Result { + Self::from_insecure_url(url).await.map_err(pezkuwi_subxt::Error::from) + } } pub async fn get_client_from_url( - url: &str, + url: &str, ) -> Result { - if pezkuwi_subxt::utils::url_is_secure(url)? { - T::from_secure_url(url).await - } else { - T::from_insecure_url(url).await - } + if pezkuwi_subxt::utils::url_is_secure(url)? { + T::from_secure_url(url).await + } else { + T::from_insecure_url(url).await + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/tx_helper/runtime_upgrade.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/tx_helper/runtime_upgrade.rs index 57a65d7d..ee2abea4 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/tx_helper/runtime_upgrade.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/tx_helper/runtime_upgrade.rs @@ -5,65 +5,52 @@ use tracing::{debug, info}; use crate::network::node::NetworkNode; pub async fn upgrade( - node: &NetworkNode, - wasm_data: &[u8], - sudo: &Keypair, + node: &NetworkNode, + wasm_data: &[u8], + sudo: &Keypair, ) -> Result<(), anyhow::Error> { - debug!( - "Upgrading runtime, using node: {} with endpoting {}", - node.name, node.ws_uri - ); - let api: OnlineClient = node.wait_client().await?; + debug!("Upgrading runtime, using node: {} with endpoting {}", node.name, node.ws_uri); + let api: OnlineClient = node.wait_client().await?; - let upgrade = pezkuwi_subxt::dynamic::tx( - "System", - "set_code_without_checks", - vec![Value::from_bytes(wasm_data)], - ); + let upgrade = pezkuwi_subxt::dynamic::tx( + "System", + "set_code_without_checks", + vec![Value::from_bytes(wasm_data)], + ); - let sudo_call = pezkuwi_subxt::dynamic::tx( - "Sudo", - "sudo_unchecked_weight", - vec![ - upgrade.into_value(), - Value::named_composite([ - ("ref_time", Value::primitive(1.into())), - ("proof_size", Value::primitive(1.into())), - ]), - ], - ); + let sudo_call = pezkuwi_subxt::dynamic::tx( + "Sudo", + "sudo_unchecked_weight", + vec![ + upgrade.into_value(), + Value::named_composite([ + ("ref_time", Value::primitive(1.into())), + ("proof_size", Value::primitive(1.into())), + ]), + ], + ); - let mut tx = api - .tx() - .sign_and_submit_then_watch_default(&sudo_call, sudo) - .await?; + let mut tx = api.tx().sign_and_submit_then_watch_default(&sudo_call, sudo).await?; - // Below we use the low level API to replicate the `wait_for_in_block` behaviour - // which was removed in subxt 0.33.0. See https://github.com/paritytech/subxt/pull/1237. - while let Some(status) = tx.next().await { - let status = status?; - match &status { - TxStatus::InBestBlock(tx_in_block) | TxStatus::InFinalizedBlock(tx_in_block) => { - let _result = tx_in_block.wait_for_success().await?; - let block_status = if status.as_finalized().is_some() { - "Finalized" - } else { - "Best" - }; - info!( - "[{}] In block: {:#?}", - block_status, - tx_in_block.block_hash() - ); - }, - TxStatus::Error { message } - | TxStatus::Invalid { message } - | TxStatus::Dropped { message } => { - return Err(anyhow::format_err!("Error submitting tx: {message}")); - }, - _ => continue, - } - } + // Below we use the low level API to replicate the `wait_for_in_block` behaviour + // which was removed in subxt 0.33.0. See https://github.com/paritytech/subxt/pull/1237. + while let Some(status) = tx.next().await { + let status = status?; + match &status { + TxStatus::InBestBlock(tx_in_block) | TxStatus::InFinalizedBlock(tx_in_block) => { + let _result = tx_in_block.wait_for_success().await?; + let block_status = + if status.as_finalized().is_some() { "Finalized" } else { "Best" }; + info!("[{}] In block: {:#?}", block_status, tx_in_block.block_hash()); + }, + TxStatus::Error { message } + | TxStatus::Invalid { message } + | TxStatus::Dropped { message } => { + return Err(anyhow::format_err!("Error submitting tx: {message}")); + }, + _ => continue, + } + } - Ok(()) + Ok(()) } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/utils.rs b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/utils.rs index 7ae3dc1b..4ba5b90e 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/utils.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/orchestrator/src/utils.rs @@ -2,7 +2,7 @@ use serde::Deserializer; pub fn default_as_empty_vec<'de, D, T>(_deserializer: D) -> Result, D::Error> where - D: Deserializer<'de>, + D: Deserializer<'de>, { - Ok(Vec::new()) + Ok(Vec::new()) } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/prom-metrics-parser/src/lib.rs b/vendor/pezkuwi-zombienet-sdk/crates/prom-metrics-parser/src/lib.rs index d9da185a..a2a914cc 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/prom-metrics-parser/src/lib.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/prom-metrics-parser/src/lib.rs @@ -6,12 +6,12 @@ use pest_derive::Parser; /// An error at parsing level. #[derive(thiserror::Error, Debug)] pub enum ParserError { - #[error("error parsing input")] - ParseError(Box>), - #[error("root node should be valid: {0}")] - ParseRootNodeError(String), - #[error("can't cast metric value as f64: {0}")] - CastValueError(#[from] ParseFloatError), + #[error("error parsing input")] + ParseError(Box>), + #[error("root node should be valid: {0}")] + ParseRootNodeError(String), + #[error("can't cast metric value as f64: {0}")] + CastValueError(#[from] ParseFloatError), } // This include forces recompiling this source file if the grammar file changes. @@ -25,154 +25,147 @@ pub struct MetricsParser; pub type MetricMap = HashMap; pub fn parse(input: &str) -> Result { - let mut metric_map: MetricMap = Default::default(); - let mut pairs = MetricsParser::parse(Rule::statement, input) - .map_err(|e| ParserError::ParseError(Box::new(e)))?; + let mut metric_map: MetricMap = Default::default(); + let mut pairs = MetricsParser::parse(Rule::statement, input) + .map_err(|e| ParserError::ParseError(Box::new(e)))?; - let root = pairs - .next() - .ok_or(ParserError::ParseRootNodeError(pairs.as_str().to_string()))?; - for token in root.into_inner() { - if token.as_rule() == Rule::block { - let inner = token.into_inner(); - for value in inner { - match value.as_rule() { - Rule::genericomment | Rule::typexpr | Rule::helpexpr => { - // don't need to collect comments/types/helpers blocks. - continue; - }, - Rule::promstmt => { - let mut key: &str = ""; - let mut labels: Vec<(&str, &str)> = Vec::new(); - let mut val: f64 = 0_f64; - for v in value.clone().into_inner() { - match &v.as_rule() { - Rule::key => { - key = v.as_span().as_str(); - }, - Rule::NaN | Rule::posInf | Rule::negInf => { - // noop (not used in substrate metrics) - }, - Rule::number => { - val = v.as_span().as_str().parse::()?; - }, - Rule::labels => { - // SAFETY: use unwrap should be safe since we are just - // walking the parser struct and if are matching a label - // should have a key/vals - for p in v.into_inner() { - let mut inner = p.into_inner(); - let key = inner.next().unwrap().as_span().as_str(); - let value = inner - .next() - .unwrap() - .into_inner() - .next() - .unwrap() - .as_span() - .as_str(); + let root = pairs.next().ok_or(ParserError::ParseRootNodeError(pairs.as_str().to_string()))?; + for token in root.into_inner() { + if token.as_rule() == Rule::block { + let inner = token.into_inner(); + for value in inner { + match value.as_rule() { + Rule::genericomment | Rule::typexpr | Rule::helpexpr => { + // don't need to collect comments/types/helpers blocks. + continue; + }, + Rule::promstmt => { + let mut key: &str = ""; + let mut labels: Vec<(&str, &str)> = Vec::new(); + let mut val: f64 = 0_f64; + for v in value.clone().into_inner() { + match &v.as_rule() { + Rule::key => { + key = v.as_span().as_str(); + }, + Rule::NaN | Rule::posInf | Rule::negInf => { + // noop (not used in substrate metrics) + }, + Rule::number => { + val = v.as_span().as_str().parse::()?; + }, + Rule::labels => { + // SAFETY: use unwrap should be safe since we are just + // walking the parser struct and if are matching a label + // should have a key/vals + for p in v.into_inner() { + let mut inner = p.into_inner(); + let key = inner.next().unwrap().as_span().as_str(); + let value = inner + .next() + .unwrap() + .into_inner() + .next() + .unwrap() + .as_span() + .as_str(); - labels.push((key, value)); - } - }, - _ => { - todo!("not implemented"); - }, - } - } + labels.push((key, value)); + } + }, + _ => { + todo!("not implemented"); + }, + } + } - // we should store to make it compatible with zombienet v1: - // key_without_prefix - // key_without_prefix_and_without_chain - // key_with_prefix_with_chain - // key_with_prefix_and_without_chain - let key_with_out_prefix = - key.split('_').collect::>()[1..].join("_"); - let (labels_without_chain, labels_with_chain) = - labels.iter().fold((vec![], vec![]), |mut acc, item| { - if item.0.eq("chain") { - acc.1.push(format!("{}=\"{}\"", item.0, item.1)); - } else { - acc.0.push(format!("{}=\"{}\"", item.0, item.1)); - acc.1.push(format!("{}=\"{}\"", item.0, item.1)); - } - acc - }); + // we should store to make it compatible with zombienet v1: + // key_without_prefix + // key_without_prefix_and_without_chain + // key_with_prefix_with_chain + // key_with_prefix_and_without_chain + let key_with_out_prefix = + key.split('_').collect::>()[1..].join("_"); + let (labels_without_chain, labels_with_chain) = + labels.iter().fold((vec![], vec![]), |mut acc, item| { + if item.0.eq("chain") { + acc.1.push(format!("{}=\"{}\"", item.0, item.1)); + } else { + acc.0.push(format!("{}=\"{}\"", item.0, item.1)); + acc.1.push(format!("{}=\"{}\"", item.0, item.1)); + } + acc + }); - let labels_with_chain_str = if labels_with_chain.is_empty() { - String::from("") - } else { - format!("{{{}}}", labels_with_chain.join(",")) - }; + let labels_with_chain_str = if labels_with_chain.is_empty() { + String::from("") + } else { + format!("{{{}}}", labels_with_chain.join(",")) + }; - let labels_without_chain_str = if labels_without_chain.is_empty() { - String::from("") - } else { - format!("{{{}}}", labels_without_chain.join(",")) - }; + let labels_without_chain_str = if labels_without_chain.is_empty() { + String::from("") + } else { + format!("{{{}}}", labels_without_chain.join(",")) + }; - metric_map.insert(format!("{key}{labels_without_chain_str}"), val); - metric_map.insert( - format!("{key_with_out_prefix}{labels_without_chain_str}"), - val, - ); - metric_map.insert(format!("{key}{labels_with_chain_str}"), val); - metric_map - .insert(format!("{key_with_out_prefix}{labels_with_chain_str}"), val); - }, - _ => {}, - } - } - } - } + metric_map.insert(format!("{key}{labels_without_chain_str}"), val); + metric_map.insert( + format!("{key_with_out_prefix}{labels_without_chain_str}"), + val, + ); + metric_map.insert(format!("{key}{labels_with_chain_str}"), val); + metric_map + .insert(format!("{key_with_out_prefix}{labels_with_chain_str}"), val); + }, + _ => {}, + } + } + } + } - Ok(metric_map) + Ok(metric_map) } #[cfg(test)] mod tests { - use std::fs; + use std::fs; - use super::*; + use super::*; - #[test] - fn parse_metrics_works() { - let metrics_raw = fs::read_to_string("./testing/metrics.txt").unwrap(); - let metrics = parse(&metrics_raw).unwrap(); + #[test] + fn parse_metrics_works() { + let metrics_raw = fs::read_to_string("./testing/metrics.txt").unwrap(); + let metrics = parse(&metrics_raw).unwrap(); - // full key - assert_eq!( - metrics - .get("polkadot_node_is_active_validator{chain=\"rococo_local_testnet\"}") - .unwrap(), - &1_f64 - ); - // with prefix and no chain - assert_eq!( - metrics.get("polkadot_node_is_active_validator").unwrap(), - &1_f64 - ); - // no prefix with chain - assert_eq!( - metrics - .get("node_is_active_validator{chain=\"rococo_local_testnet\"}") - .unwrap(), - &1_f64 - ); - // no prefix without chain - assert_eq!(metrics.get("node_is_active_validator").unwrap(), &1_f64); - } + // full key + assert_eq!( + metrics + .get("polkadot_node_is_active_validator{chain=\"rococo_local_testnet\"}") + .unwrap(), + &1_f64 + ); + // with prefix and no chain + assert_eq!(metrics.get("polkadot_node_is_active_validator").unwrap(), &1_f64); + // no prefix with chain + assert_eq!( + metrics.get("node_is_active_validator{chain=\"rococo_local_testnet\"}").unwrap(), + &1_f64 + ); + // no prefix without chain + assert_eq!(metrics.get("node_is_active_validator").unwrap(), &1_f64); + } - #[test] - fn parse_invalid_metrics_str_should_fail() { - let metrics_raw = r" + #[test] + fn parse_invalid_metrics_str_should_fail() { + let metrics_raw = r" # HELP polkadot_node_is_active_validator Tracks if the validator is in the active set. Updates at session boundary. # TYPE polkadot_node_is_active_validator gauge polkadot_node_is_active_validator{chain=} 1 "; - let metrics = parse(metrics_raw); - assert!(metrics.is_err()); - assert!(matches!(metrics, Err(ParserError::ParseError(_)))); - } + let metrics = parse(metrics_raw); + assert!(metrics.is_err()); + assert!(matches!(metrics, Err(ParserError::ParseError(_)))); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/docker/client.rs b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/docker/client.rs index 5fe016ae..dbcdd17d 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/docker/client.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/docker/client.rs @@ -16,581 +16,540 @@ pub type Result = core::result::Result; #[derive(Clone)] pub struct DockerClient { - using_podman: bool, + using_podman: bool, } #[derive(Debug)] pub struct ContainerRunOptions { - image: String, - command: Vec, - env: Option>, - volume_mounts: Option>, - name: Option, - entrypoint: Option, - port_mapping: HashMap, - rm: bool, - detach: bool, + image: String, + command: Vec, + env: Option>, + volume_mounts: Option>, + name: Option, + entrypoint: Option, + port_mapping: HashMap, + rm: bool, + detach: bool, } enum Container { - Docker(DockerContainer), - Podman(PodmanContainer), + Docker(DockerContainer), + Podman(PodmanContainer), } // TODO: we may don't need this #[allow(dead_code)] #[derive(Deserialize, Debug)] struct DockerContainer { - #[serde(alias = "Names", deserialize_with = "deserialize_list")] - names: Vec, - #[serde(alias = "Ports", deserialize_with = "deserialize_list")] - ports: Vec, - #[serde(alias = "State")] - state: String, + #[serde(alias = "Names", deserialize_with = "deserialize_list")] + names: Vec, + #[serde(alias = "Ports", deserialize_with = "deserialize_list")] + ports: Vec, + #[serde(alias = "State")] + state: String, } // TODO: we may don't need this #[allow(dead_code)] #[derive(Deserialize, Debug)] struct PodmanPort { - host_ip: String, - container_port: u16, - host_port: u16, - range: u16, - protocol: String, + host_ip: String, + container_port: u16, + host_port: u16, + range: u16, + protocol: String, } // TODO: we may don't need this #[allow(dead_code)] #[derive(Deserialize, Debug)] struct PodmanContainer { - #[serde(alias = "Id")] - id: String, - #[serde(alias = "Image")] - image: String, - #[serde(alias = "Mounts")] - mounts: Vec, - #[serde(alias = "Names")] - names: Vec, - #[serde(alias = "Ports", deserialize_with = "deserialize_null_as_default")] - ports: Vec, - #[serde(alias = "State")] - state: String, + #[serde(alias = "Id")] + id: String, + #[serde(alias = "Image")] + image: String, + #[serde(alias = "Mounts")] + mounts: Vec, + #[serde(alias = "Names")] + names: Vec, + #[serde(alias = "Ports", deserialize_with = "deserialize_null_as_default")] + ports: Vec, + #[serde(alias = "State")] + state: String, } fn deserialize_list<'de, D>(deserializer: D) -> std::result::Result, D::Error> where - D: Deserializer<'de>, + D: Deserializer<'de>, { - let str_sequence = String::deserialize(deserializer)?; - Ok(str_sequence - .split(',') - .filter(|item| !item.is_empty()) - .map(|item| item.to_owned()) - .collect()) + let str_sequence = String::deserialize(deserializer)?; + Ok(str_sequence + .split(',') + .filter(|item| !item.is_empty()) + .map(|item| item.to_owned()) + .collect()) } fn deserialize_null_as_default<'de, D, T>(deserializer: D) -> std::result::Result where - T: Default + Deserialize<'de>, - D: Deserializer<'de>, + T: Default + Deserialize<'de>, + D: Deserializer<'de>, { - let opt = Option::deserialize(deserializer)?; - Ok(opt.unwrap_or_default()) + let opt = Option::deserialize(deserializer)?; + Ok(opt.unwrap_or_default()) } impl ContainerRunOptions { - pub fn new(image: &str, command: Vec) -> Self - where - S: Into + std::fmt::Debug + Send + Clone, - { - ContainerRunOptions { - image: image.to_string(), - command: command - .clone() - .into_iter() - .map(|s| s.into()) - .collect::>(), - env: None, - volume_mounts: None, - name: None, - entrypoint: None, - port_mapping: HashMap::default(), - rm: false, - detach: true, // add -d flag by default - } - } + pub fn new(image: &str, command: Vec) -> Self + where + S: Into + std::fmt::Debug + Send + Clone, + { + ContainerRunOptions { + image: image.to_string(), + command: command.clone().into_iter().map(|s| s.into()).collect::>(), + env: None, + volume_mounts: None, + name: None, + entrypoint: None, + port_mapping: HashMap::default(), + rm: false, + detach: true, // add -d flag by default + } + } - pub fn env(mut self, env: Vec<(S, S)>) -> Self - where - S: Into + std::fmt::Debug + Send + Clone, - { - self.env = Some( - env.into_iter() - .map(|(name, value)| (name.into(), value.into())) - .collect(), - ); - self - } + pub fn env(mut self, env: Vec<(S, S)>) -> Self + where + S: Into + std::fmt::Debug + Send + Clone, + { + self.env = Some(env.into_iter().map(|(name, value)| (name.into(), value.into())).collect()); + self + } - pub fn volume_mounts(mut self, volume_mounts: HashMap) -> Self - where - S: Into + std::fmt::Debug + Send + Clone, - { - self.volume_mounts = Some( - volume_mounts - .into_iter() - .map(|(source, target)| (source.into(), target.into())) - .collect(), - ); - self - } + pub fn volume_mounts(mut self, volume_mounts: HashMap) -> Self + where + S: Into + std::fmt::Debug + Send + Clone, + { + self.volume_mounts = Some( + volume_mounts + .into_iter() + .map(|(source, target)| (source.into(), target.into())) + .collect(), + ); + self + } - pub fn name(mut self, name: S) -> Self - where - S: Into + std::fmt::Debug + Send + Clone, - { - self.name = Some(name.into()); - self - } + pub fn name(mut self, name: S) -> Self + where + S: Into + std::fmt::Debug + Send + Clone, + { + self.name = Some(name.into()); + self + } - pub fn entrypoint(mut self, entrypoint: S) -> Self - where - S: Into + std::fmt::Debug + Send + Clone, - { - self.entrypoint = Some(entrypoint.into()); - self - } + pub fn entrypoint(mut self, entrypoint: S) -> Self + where + S: Into + std::fmt::Debug + Send + Clone, + { + self.entrypoint = Some(entrypoint.into()); + self + } - pub fn port_mapping(mut self, port_mapping: &HashMap) -> Self { - self.port_mapping.clone_from(port_mapping); - self - } + pub fn port_mapping(mut self, port_mapping: &HashMap) -> Self { + self.port_mapping.clone_from(port_mapping); + self + } - pub fn rm(mut self) -> Self { - self.rm = true; - self - } + pub fn rm(mut self) -> Self { + self.rm = true; + self + } - pub fn detach(mut self, choice: bool) -> Self { - self.detach = choice; - self - } + pub fn detach(mut self, choice: bool) -> Self { + self.detach = choice; + self + } } impl DockerClient { - pub async fn new() -> Result { - let using_podman = Self::is_using_podman().await?; + pub async fn new() -> Result { + let using_podman = Self::is_using_podman().await?; - Ok(DockerClient { using_podman }) - } + Ok(DockerClient { using_podman }) + } - pub fn client_binary(&self) -> String { - String::from(if self.using_podman { - "podman" - } else { - "docker" - }) - } + pub fn client_binary(&self) -> String { + String::from(if self.using_podman { "podman" } else { "docker" }) + } - async fn is_using_podman() -> Result { - if let Ok(output) = tokio::process::Command::new("docker") - .arg("version") - .output() - .await - { - // detect whether we're actually running podman with docker emulation - return Ok(String::from_utf8_lossy(&output.stdout) - .to_lowercase() - .contains("podman")); - } + async fn is_using_podman() -> Result { + if let Ok(output) = tokio::process::Command::new("docker").arg("version").output().await { + // detect whether we're actually running podman with docker emulation + return Ok(String::from_utf8_lossy(&output.stdout).to_lowercase().contains("podman")); + } - tokio::process::Command::new("podman") - .arg("--version") - .output() - .await - .map_err(|err| anyhow!("Failed to detect container engine: {err}"))?; + tokio::process::Command::new("podman") + .arg("--version") + .output() + .await + .map_err(|err| anyhow!("Failed to detect container engine: {err}"))?; - Ok(true) - } + Ok(true) + } } impl DockerClient { - fn client_command(&self) -> tokio::process::Command { - tokio::process::Command::new(self.client_binary()) - } + fn client_command(&self) -> tokio::process::Command { + tokio::process::Command::new(self.client_binary()) + } - pub async fn create_volume(&self, name: &str) -> Result<()> { - let result = self - .client_command() - .args(["volume", "create", name]) - .output() - .await - .map_err(|err| anyhow!("Failed to create volume '{name}': {err}"))?; + pub async fn create_volume(&self, name: &str) -> Result<()> { + let result = self + .client_command() + .args(["volume", "create", name]) + .output() + .await + .map_err(|err| anyhow!("Failed to create volume '{name}': {err}"))?; - if !result.status.success() { - return Err(anyhow!( - "Failed to create volume '{name}': {}", - String::from_utf8_lossy(&result.stderr) - ) - .into()); - } + if !result.status.success() { + return Err(anyhow!( + "Failed to create volume '{name}': {}", + String::from_utf8_lossy(&result.stderr) + ) + .into()); + } - Ok(()) - } + Ok(()) + } - pub async fn container_run(&self, options: ContainerRunOptions) -> Result { - let mut cmd = self.client_command(); - cmd.args(["run", "--platform", "linux/amd64"]); + pub async fn container_run(&self, options: ContainerRunOptions) -> Result { + let mut cmd = self.client_command(); + cmd.args(["run", "--platform", "linux/amd64"]); - if options.detach { - cmd.arg("-d"); - } + if options.detach { + cmd.arg("-d"); + } - Self::apply_cmd_options(&mut cmd, &options); + Self::apply_cmd_options(&mut cmd, &options); - trace!("cmd: {:?}", cmd); + trace!("cmd: {:?}", cmd); - let result = cmd.output().await.map_err(|err| { - anyhow!( - "Failed to run container with image '{image}' and command '{command}': {err}", - image = options.image, - command = options.command.join(" "), - ) - })?; + let result = cmd.output().await.map_err(|err| { + anyhow!( + "Failed to run container with image '{image}' and command '{command}': {err}", + image = options.image, + command = options.command.join(" "), + ) + })?; - if !result.status.success() { - return Err(anyhow!( - "Failed to run container with image '{image}' and command '{command}': {err}", - image = options.image, - command = options.command.join(" "), - err = String::from_utf8_lossy(&result.stderr) - ) - .into()); - } + if !result.status.success() { + return Err(anyhow!( + "Failed to run container with image '{image}' and command '{command}': {err}", + image = options.image, + command = options.command.join(" "), + err = String::from_utf8_lossy(&result.stderr) + ) + .into()); + } - Ok(String::from_utf8_lossy(&result.stdout).to_string()) - } + Ok(String::from_utf8_lossy(&result.stdout).to_string()) + } - pub async fn container_create(&self, options: ContainerRunOptions) -> Result { - let mut cmd = self.client_command(); - cmd.args(["container", "create"]); + pub async fn container_create(&self, options: ContainerRunOptions) -> Result { + let mut cmd = self.client_command(); + cmd.args(["container", "create"]); - Self::apply_cmd_options(&mut cmd, &options); + Self::apply_cmd_options(&mut cmd, &options); - trace!("cmd: {:?}", cmd); + trace!("cmd: {:?}", cmd); - let result = cmd.output().await.map_err(|err| { - anyhow!( - "Failed to run container with image '{image}' and command '{command}': {err}", - image = options.image, - command = options.command.join(" "), - ) - })?; + let result = cmd.output().await.map_err(|err| { + anyhow!( + "Failed to run container with image '{image}' and command '{command}': {err}", + image = options.image, + command = options.command.join(" "), + ) + })?; - if !result.status.success() { - return Err(anyhow!( - "Failed to run container with image '{image}' and command '{command}': {err}", - image = options.image, - command = options.command.join(" "), - err = String::from_utf8_lossy(&result.stderr) - ) - .into()); - } + if !result.status.success() { + return Err(anyhow!( + "Failed to run container with image '{image}' and command '{command}': {err}", + image = options.image, + command = options.command.join(" "), + err = String::from_utf8_lossy(&result.stderr) + ) + .into()); + } - Ok(String::from_utf8_lossy(&result.stdout).to_string()) - } + Ok(String::from_utf8_lossy(&result.stdout).to_string()) + } - pub async fn container_exec( - &self, - name: &str, - command: Vec, - env: Option>, - as_user: Option, - ) -> Result - where - S: Into + std::fmt::Debug + Send + Clone, - { - let mut cmd = self.client_command(); - cmd.arg("exec"); + pub async fn container_exec( + &self, + name: &str, + command: Vec, + env: Option>, + as_user: Option, + ) -> Result + where + S: Into + std::fmt::Debug + Send + Clone, + { + let mut cmd = self.client_command(); + cmd.arg("exec"); - if let Some(env) = env { - for env_var in env { - cmd.args(["-e", &format!("{}={}", env_var.0.into(), env_var.1.into())]); - } - } + if let Some(env) = env { + for env_var in env { + cmd.args(["-e", &format!("{}={}", env_var.0.into(), env_var.1.into())]); + } + } - if let Some(user) = as_user { - cmd.args(["-u", user.into().as_ref()]); - } + if let Some(user) = as_user { + cmd.args(["-u", user.into().as_ref()]); + } - cmd.arg(name); + cmd.arg(name); - cmd.args( - command - .clone() - .into_iter() - .map(|s| >::into(s)), - ); + cmd.args(command.clone().into_iter().map(|s| >::into(s))); - trace!("cmd is : {:?}", cmd); + trace!("cmd is : {:?}", cmd); - let result = cmd.output().await.map_err(|err| { - anyhow!( - "Failed to exec '{}' on '{}': {err}", - command - .into_iter() - .map(|s| >::into(s)) - .collect::>() - .join(" "), - name, - ) - })?; + let result = cmd.output().await.map_err(|err| { + anyhow!( + "Failed to exec '{}' on '{}': {err}", + command + .into_iter() + .map(|s| >::into(s)) + .collect::>() + .join(" "), + name, + ) + })?; - if !result.status.success() { - return Ok(Err(( - result.status, - String::from_utf8_lossy(&result.stderr).to_string(), - ))); - } + if !result.status.success() { + return Ok(Err((result.status, String::from_utf8_lossy(&result.stderr).to_string()))); + } - Ok(Ok(String::from_utf8_lossy(&result.stdout).to_string())) - } + Ok(Ok(String::from_utf8_lossy(&result.stdout).to_string())) + } - pub async fn container_cp( - &self, - name: &str, - local_path: &Path, - remote_path: &Path, - ) -> Result<()> { - let result = self - .client_command() - .args([ - "cp", - local_path.to_string_lossy().as_ref(), - &format!("{name}:{}", remote_path.to_string_lossy().as_ref()), - ]) - .output() - .await - .map_err(|err| { - anyhow!( - "Failed copy file '{file}' to container '{name}': {err}", - file = local_path.to_string_lossy(), - ) - })?; + pub async fn container_cp( + &self, + name: &str, + local_path: &Path, + remote_path: &Path, + ) -> Result<()> { + let result = self + .client_command() + .args([ + "cp", + local_path.to_string_lossy().as_ref(), + &format!("{name}:{}", remote_path.to_string_lossy().as_ref()), + ]) + .output() + .await + .map_err(|err| { + anyhow!( + "Failed copy file '{file}' to container '{name}': {err}", + file = local_path.to_string_lossy(), + ) + })?; - if !result.status.success() { - return Err(anyhow!( - "Failed to copy file '{file}' to container '{name}': {err}", - file = local_path.to_string_lossy(), - err = String::from_utf8_lossy(&result.stderr) - ) - .into()); - } + if !result.status.success() { + return Err(anyhow!( + "Failed to copy file '{file}' to container '{name}': {err}", + file = local_path.to_string_lossy(), + err = String::from_utf8_lossy(&result.stderr) + ) + .into()); + } - Ok(()) - } + Ok(()) + } - pub async fn container_rm(&self, name: &str) -> Result<()> { - let result = self - .client_command() - .args(["rm", "--force", "--volumes", name]) - .output() - .await - .map_err(|err| anyhow!("Failed do remove container '{name}: {err}"))?; + pub async fn container_rm(&self, name: &str) -> Result<()> { + let result = self + .client_command() + .args(["rm", "--force", "--volumes", name]) + .output() + .await + .map_err(|err| anyhow!("Failed do remove container '{name}: {err}"))?; - if !result.status.success() { - return Err(anyhow!( - "Failed to remove container '{name}': {err}", - err = String::from_utf8_lossy(&result.stderr) - ) - .into()); - } + if !result.status.success() { + return Err(anyhow!( + "Failed to remove container '{name}': {err}", + err = String::from_utf8_lossy(&result.stderr) + ) + .into()); + } - Ok(()) - } + Ok(()) + } - pub async fn namespaced_containers_rm(&self, namespace: &str) -> Result<()> { - let container_names: Vec = self - .get_containers() - .await? - .into_iter() - .filter_map(|container| match container { - Container::Docker(container) => { - if let Some(name) = container.names.first() { - if name.starts_with(namespace) { - return Some(name.to_string()); - } - } + pub async fn namespaced_containers_rm(&self, namespace: &str) -> Result<()> { + let container_names: Vec = self + .get_containers() + .await? + .into_iter() + .filter_map(|container| match container { + Container::Docker(container) => { + if let Some(name) = container.names.first() { + if name.starts_with(namespace) { + return Some(name.to_string()); + } + } - None - }, - Container::Podman(container) => { - if let Some(name) = container.names.first() { - if name.starts_with(namespace) { - return Some(name.to_string()); - } - } + None + }, + Container::Podman(container) => { + if let Some(name) = container.names.first() { + if name.starts_with(namespace) { + return Some(name.to_string()); + } + } - None - }, - }) - .collect(); + None + }, + }) + .collect(); - info!("{:?}", container_names); - let futures = container_names - .iter() - .map(|name| self.container_rm(name)) - .collect::>(); - try_join_all(futures).await?; + info!("{:?}", container_names); + let futures = + container_names.iter().map(|name| self.container_rm(name)).collect::>(); + try_join_all(futures).await?; - Ok(()) - } + Ok(()) + } - pub async fn container_ip(&self, container_name: &str) -> Result { - let ip = if self.using_podman { - "127.0.0.1".into() - } else { - let mut cmd = tokio::process::Command::new("docker"); - cmd.args(vec![ - "inspect", - "-f", - "{{ .NetworkSettings.IPAddress }}", - container_name, - ]); + pub async fn container_ip(&self, container_name: &str) -> Result { + let ip = if self.using_podman { + "127.0.0.1".into() + } else { + let mut cmd = tokio::process::Command::new("docker"); + cmd.args(vec!["inspect", "-f", "{{ .NetworkSettings.IPAddress }}", container_name]); - trace!("CMD: {cmd:?}"); + trace!("CMD: {cmd:?}"); - let res = cmd - .output() - .await - .map_err(|err| anyhow!("Failed to get docker container ip, output: {err}"))?; + let res = cmd + .output() + .await + .map_err(|err| anyhow!("Failed to get docker container ip, output: {err}"))?; - String::from_utf8(res.stdout) - .map_err(|err| anyhow!("Failed to get docker container ip, output: {err}"))? - .trim() - .into() - }; + String::from_utf8(res.stdout) + .map_err(|err| anyhow!("Failed to get docker container ip, output: {err}"))? + .trim() + .into() + }; - trace!("IP: {ip}"); - Ok(ip) - } + trace!("IP: {ip}"); + Ok(ip) + } - async fn get_containers(&self) -> Result> { - let containers = if self.using_podman { - self.get_podman_containers() - .await? - .into_iter() - .map(Container::Podman) - .collect() - } else { - self.get_docker_containers() - .await? - .into_iter() - .map(Container::Docker) - .collect() - }; + async fn get_containers(&self) -> Result> { + let containers = if self.using_podman { + self.get_podman_containers().await?.into_iter().map(Container::Podman).collect() + } else { + self.get_docker_containers().await?.into_iter().map(Container::Docker).collect() + }; - Ok(containers) - } + Ok(containers) + } - async fn get_podman_containers(&self) -> Result> { - let res = tokio::process::Command::new("podman") - .args(vec!["ps", "--all", "--no-trunc", "--format", "json"]) - .output() - .await - .map_err(|err| anyhow!("Failed to get podman containers output: {err}"))?; + async fn get_podman_containers(&self) -> Result> { + let res = tokio::process::Command::new("podman") + .args(vec!["ps", "--all", "--no-trunc", "--format", "json"]) + .output() + .await + .map_err(|err| anyhow!("Failed to get podman containers output: {err}"))?; - let stdout = String::from_utf8_lossy(&res.stdout); + let stdout = String::from_utf8_lossy(&res.stdout); - let containers = serde_json::from_str(&stdout) - .map_err(|err| anyhow!("Failed to parse podman containers output: {err}"))?; + let containers = serde_json::from_str(&stdout) + .map_err(|err| anyhow!("Failed to parse podman containers output: {err}"))?; - Ok(containers) - } + Ok(containers) + } - async fn get_docker_containers(&self) -> Result> { - let res = tokio::process::Command::new("docker") - .args(vec!["ps", "--all", "--no-trunc", "--format", "json"]) - .output() - .await - .unwrap(); + async fn get_docker_containers(&self) -> Result> { + let res = tokio::process::Command::new("docker") + .args(vec!["ps", "--all", "--no-trunc", "--format", "json"]) + .output() + .await + .unwrap(); - let stdout = String::from_utf8_lossy(&res.stdout); + let stdout = String::from_utf8_lossy(&res.stdout); - let mut containers = vec![]; - for line in stdout.lines() { - containers.push( - serde_json::from_str::(line) - .map_err(|err| anyhow!("Failed to parse docker container output: {err}"))?, - ); - } + let mut containers = vec![]; + for line in stdout.lines() { + containers.push( + serde_json::from_str::(line) + .map_err(|err| anyhow!("Failed to parse docker container output: {err}"))?, + ); + } - Ok(containers) - } + Ok(containers) + } - pub(crate) async fn container_logs(&self, container_name: &str) -> Result { - let output = Command::new("sh") - .arg("-c") - .arg(format!("docker logs -t '{container_name}' 2>&1")) - .stdout(Stdio::piped()) - .output() - .await - .map_err(|err| { - anyhow!( - "Failed to spawn docker logs command for container '{container_name}': {err}" - ) - })?; + pub(crate) async fn container_logs(&self, container_name: &str) -> Result { + let output = Command::new("sh") + .arg("-c") + .arg(format!("docker logs -t '{container_name}' 2>&1")) + .stdout(Stdio::piped()) + .output() + .await + .map_err(|err| { + anyhow!( + "Failed to spawn docker logs command for container '{container_name}': {err}" + ) + })?; - let logs = String::from_utf8_lossy(&output.stdout).to_string(); + let logs = String::from_utf8_lossy(&output.stdout).to_string(); - if !output.status.success() { - // stderr was redirected to stdout, so logs should contain the error message if any - return Err(anyhow!( - "Failed to get logs for container '{name}': {logs}", - name = container_name, - logs = &logs - ) - .into()); - } + if !output.status.success() { + // stderr was redirected to stdout, so logs should contain the error message if any + return Err(anyhow!( + "Failed to get logs for container '{name}': {logs}", + name = container_name, + logs = &logs + ) + .into()); + } - Ok(logs) - } + Ok(logs) + } - fn apply_cmd_options(cmd: &mut Command, options: &ContainerRunOptions) { - if options.rm { - cmd.arg("--rm"); - } + fn apply_cmd_options(cmd: &mut Command, options: &ContainerRunOptions) { + if options.rm { + cmd.arg("--rm"); + } - if let Some(entrypoint) = options.entrypoint.as_ref() { - cmd.args(["--entrypoint", entrypoint]); - } + if let Some(entrypoint) = options.entrypoint.as_ref() { + cmd.args(["--entrypoint", entrypoint]); + } - if let Some(volume_mounts) = options.volume_mounts.as_ref() { - for (source, target) in volume_mounts { - cmd.args(["-v", &format!("{source}:{target}")]); - } - } + if let Some(volume_mounts) = options.volume_mounts.as_ref() { + for (source, target) in volume_mounts { + cmd.args(["-v", &format!("{source}:{target}")]); + } + } - if let Some(env) = options.env.as_ref() { - for env_var in env { - cmd.args(["-e", &format!("{}={}", env_var.0, env_var.1)]); - } - } + if let Some(env) = options.env.as_ref() { + for env_var in env { + cmd.args(["-e", &format!("{}={}", env_var.0, env_var.1)]); + } + } - // add published ports - for (container_port, host_port) in options.port_mapping.iter() { - cmd.args(["-p", &format!("{host_port}:{container_port}")]); - } + // add published ports + for (container_port, host_port) in options.port_mapping.iter() { + cmd.args(["-p", &format!("{host_port}:{container_port}")]); + } - if let Some(name) = options.name.as_ref() { - cmd.args(["--name", name]); - } + if let Some(name) = options.name.as_ref() { + cmd.args(["--name", name]); + } - cmd.arg(&options.image); + cmd.arg(&options.image); - for arg in &options.command { - cmd.arg(arg); - } - } + for arg in &options.command { + cmd.arg(arg); + } + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/docker/namespace.rs b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/docker/namespace.rs index 5b13df13..cebbe18a 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/docker/namespace.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/docker/namespace.rs @@ -1,8 +1,8 @@ use std::{ - collections::HashMap, - path::{Path, PathBuf}, - sync::{Arc, Weak}, - thread, + collections::HashMap, + path::{Path, PathBuf}, + sync::{Arc, Weak}, + thread, }; use async_trait::async_trait; @@ -12,212 +12,207 @@ use tracing::{debug, trace, warn}; use uuid::Uuid; use super::{ - client::{ContainerRunOptions, DockerClient}, - node::DockerNode, - DockerProvider, + client::{ContainerRunOptions, DockerClient}, + node::DockerNode, + DockerProvider, }; use crate::{ - constants::NAMESPACE_PREFIX, - docker::{ - node::{DeserializableDockerNodeOptions, DockerNodeOptions}, - provider, - }, - shared::helpers::extract_execution_result, - types::{ - GenerateFileCommand, GenerateFilesOptions, ProviderCapabilities, RunCommandOptions, - SpawnNodeOptions, - }, - DynNode, ProviderError, ProviderNamespace, ProviderNode, + constants::NAMESPACE_PREFIX, + docker::{ + node::{DeserializableDockerNodeOptions, DockerNodeOptions}, + provider, + }, + shared::helpers::extract_execution_result, + types::{ + GenerateFileCommand, GenerateFilesOptions, ProviderCapabilities, RunCommandOptions, + SpawnNodeOptions, + }, + DynNode, ProviderError, ProviderNamespace, ProviderNode, }; pub struct DockerNamespace where - FS: FileSystem + Send + Sync + Clone, + FS: FileSystem + Send + Sync + Clone, { - weak: Weak>, - #[allow(dead_code)] - provider: Weak>, - name: String, - base_dir: PathBuf, - capabilities: ProviderCapabilities, - docker_client: DockerClient, - filesystem: FS, - delete_on_drop: Arc>, - pub(super) nodes: RwLock>>>, + weak: Weak>, + #[allow(dead_code)] + provider: Weak>, + name: String, + base_dir: PathBuf, + capabilities: ProviderCapabilities, + docker_client: DockerClient, + filesystem: FS, + delete_on_drop: Arc>, + pub(super) nodes: RwLock>>>, } impl DockerNamespace where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - pub(super) async fn new( - provider: &Weak>, - tmp_dir: &PathBuf, - capabilities: &ProviderCapabilities, - docker_client: &DockerClient, - filesystem: &FS, - custom_base_dir: Option<&Path>, - ) -> Result, ProviderError> { - let name = format!("{}{}", NAMESPACE_PREFIX, Uuid::new_v4()); - let base_dir = if let Some(custom_base_dir) = custom_base_dir { - if !filesystem.exists(custom_base_dir).await { - filesystem.create_dir(custom_base_dir).await?; - } else { - warn!( - "⚠️ Using and existing directory {} as base dir", - custom_base_dir.to_string_lossy() - ); - } - PathBuf::from(custom_base_dir) - } else { - let base_dir = PathBuf::from_iter([tmp_dir, &PathBuf::from(&name)]); - filesystem.create_dir(&base_dir).await?; - base_dir - }; + pub(super) async fn new( + provider: &Weak>, + tmp_dir: &PathBuf, + capabilities: &ProviderCapabilities, + docker_client: &DockerClient, + filesystem: &FS, + custom_base_dir: Option<&Path>, + ) -> Result, ProviderError> { + let name = format!("{}{}", NAMESPACE_PREFIX, Uuid::new_v4()); + let base_dir = if let Some(custom_base_dir) = custom_base_dir { + if !filesystem.exists(custom_base_dir).await { + filesystem.create_dir(custom_base_dir).await?; + } else { + warn!( + "⚠️ Using and existing directory {} as base dir", + custom_base_dir.to_string_lossy() + ); + } + PathBuf::from(custom_base_dir) + } else { + let base_dir = PathBuf::from_iter([tmp_dir, &PathBuf::from(&name)]); + filesystem.create_dir(&base_dir).await?; + base_dir + }; - let namespace = Arc::new_cyclic(|weak| DockerNamespace { - weak: weak.clone(), - provider: provider.clone(), - name, - base_dir, - capabilities: capabilities.clone(), - filesystem: filesystem.clone(), - docker_client: docker_client.clone(), - nodes: RwLock::new(HashMap::new()), - delete_on_drop: Arc::new(Mutex::new(true)), - }); + let namespace = Arc::new_cyclic(|weak| DockerNamespace { + weak: weak.clone(), + provider: provider.clone(), + name, + base_dir, + capabilities: capabilities.clone(), + filesystem: filesystem.clone(), + docker_client: docker_client.clone(), + nodes: RwLock::new(HashMap::new()), + delete_on_drop: Arc::new(Mutex::new(true)), + }); - namespace.initialize().await?; + namespace.initialize().await?; - Ok(namespace) - } + Ok(namespace) + } - pub(super) async fn attach_to_live( - provider: &Weak>, - capabilities: &ProviderCapabilities, - docker_client: &DockerClient, - filesystem: &FS, - custom_base_dir: &Path, - name: &str, - ) -> Result, ProviderError> { - let base_dir = custom_base_dir.to_path_buf(); + pub(super) async fn attach_to_live( + provider: &Weak>, + capabilities: &ProviderCapabilities, + docker_client: &DockerClient, + filesystem: &FS, + custom_base_dir: &Path, + name: &str, + ) -> Result, ProviderError> { + let base_dir = custom_base_dir.to_path_buf(); - let namespace = Arc::new_cyclic(|weak| DockerNamespace { - weak: weak.clone(), - provider: provider.clone(), - name: name.to_owned(), - base_dir, - capabilities: capabilities.clone(), - filesystem: filesystem.clone(), - docker_client: docker_client.clone(), - nodes: RwLock::new(HashMap::new()), - delete_on_drop: Arc::new(Mutex::new(false)), - }); + let namespace = Arc::new_cyclic(|weak| DockerNamespace { + weak: weak.clone(), + provider: provider.clone(), + name: name.to_owned(), + base_dir, + capabilities: capabilities.clone(), + filesystem: filesystem.clone(), + docker_client: docker_client.clone(), + nodes: RwLock::new(HashMap::new()), + delete_on_drop: Arc::new(Mutex::new(false)), + }); - Ok(namespace) - } + Ok(namespace) + } - async fn initialize(&self) -> Result<(), ProviderError> { - // let ns_scripts_shared = PathBuf::from_iter([&self.base_dir, &PathBuf::from("shared-scripts")]); - // self.filesystem.create_dir(&ns_scripts_shared).await?; - self.initialize_zombie_scripts_volume().await?; - self.initialize_helper_binaries_volume().await?; + async fn initialize(&self) -> Result<(), ProviderError> { + // let ns_scripts_shared = PathBuf::from_iter([&self.base_dir, &PathBuf::from("shared-scripts")]); + // self.filesystem.create_dir(&ns_scripts_shared).await?; + self.initialize_zombie_scripts_volume().await?; + self.initialize_helper_binaries_volume().await?; - Ok(()) - } + Ok(()) + } - async fn initialize_zombie_scripts_volume(&self) -> Result<(), ProviderError> { - let local_zombie_wrapper_path = - PathBuf::from_iter([&self.base_dir, &PathBuf::from("zombie-wrapper.sh")]); + async fn initialize_zombie_scripts_volume(&self) -> Result<(), ProviderError> { + let local_zombie_wrapper_path = + PathBuf::from_iter([&self.base_dir, &PathBuf::from("zombie-wrapper.sh")]); - self.filesystem - .write( - &local_zombie_wrapper_path, - include_str!("../shared/scripts/zombie-wrapper.sh"), - ) - .await?; + self.filesystem + .write(&local_zombie_wrapper_path, include_str!("../shared/scripts/zombie-wrapper.sh")) + .await?; - let local_helper_binaries_downloader_path = PathBuf::from_iter([ - &self.base_dir, - &PathBuf::from("helper-binaries-downloader.sh"), - ]); + let local_helper_binaries_downloader_path = + PathBuf::from_iter([&self.base_dir, &PathBuf::from("helper-binaries-downloader.sh")]); - self.filesystem - .write( - &local_helper_binaries_downloader_path, - include_str!("../shared/scripts/helper-binaries-downloader.sh"), - ) - .await?; + self.filesystem + .write( + &local_helper_binaries_downloader_path, + include_str!("../shared/scripts/helper-binaries-downloader.sh"), + ) + .await?; - let zombie_wrapper_volume_name = format!("{}-zombie-wrapper", self.name); - let zombie_wrapper_container_name = format!("{}-scripts", self.name); + let zombie_wrapper_volume_name = format!("{}-zombie-wrapper", self.name); + let zombie_wrapper_container_name = format!("{}-scripts", self.name); - self.docker_client - .create_volume(&zombie_wrapper_volume_name) - .await - .map_err(|err| ProviderError::CreateNamespaceFailed(self.name.clone(), err.into()))?; + self.docker_client + .create_volume(&zombie_wrapper_volume_name) + .await + .map_err(|err| ProviderError::CreateNamespaceFailed(self.name.clone(), err.into()))?; - self.docker_client - .container_create( - ContainerRunOptions::new("alpine:latest", vec!["tail", "-f", "/dev/null"]) - .volume_mounts(HashMap::from([( - zombie_wrapper_volume_name.as_str(), - "/scripts", - )])) - .name(&zombie_wrapper_container_name) - .rm(), - ) - .await - .map_err(|err| ProviderError::CreateNamespaceFailed(self.name.clone(), err.into()))?; + self.docker_client + .container_create( + ContainerRunOptions::new("alpine:latest", vec!["tail", "-f", "/dev/null"]) + .volume_mounts(HashMap::from([( + zombie_wrapper_volume_name.as_str(), + "/scripts", + )])) + .name(&zombie_wrapper_container_name) + .rm(), + ) + .await + .map_err(|err| ProviderError::CreateNamespaceFailed(self.name.clone(), err.into()))?; - // copy the scripts - self.docker_client - .container_cp( - &zombie_wrapper_container_name, - &local_zombie_wrapper_path, - &PathBuf::from("/scripts/zombie-wrapper.sh"), - ) - .await - .map_err(|err| ProviderError::CreateNamespaceFailed(self.name.clone(), err.into()))?; + // copy the scripts + self.docker_client + .container_cp( + &zombie_wrapper_container_name, + &local_zombie_wrapper_path, + &PathBuf::from("/scripts/zombie-wrapper.sh"), + ) + .await + .map_err(|err| ProviderError::CreateNamespaceFailed(self.name.clone(), err.into()))?; - self.docker_client - .container_cp( - &zombie_wrapper_container_name, - &local_helper_binaries_downloader_path, - &PathBuf::from("/scripts/helper-binaries-downloader.sh"), - ) - .await - .map_err(|err| ProviderError::CreateNamespaceFailed(self.name.clone(), err.into()))?; + self.docker_client + .container_cp( + &zombie_wrapper_container_name, + &local_helper_binaries_downloader_path, + &PathBuf::from("/scripts/helper-binaries-downloader.sh"), + ) + .await + .map_err(|err| ProviderError::CreateNamespaceFailed(self.name.clone(), err.into()))?; - // set permissions for rwx on whole volume recursively - self.docker_client - .container_run( - ContainerRunOptions::new("alpine:latest", vec!["chmod", "-R", "777", "/scripts"]) - .volume_mounts(HashMap::from([( - zombie_wrapper_volume_name.as_ref(), - "/scripts", - )])) - .rm(), - ) - .await - .map_err(|err| ProviderError::CreateNamespaceFailed(self.name.clone(), err.into()))?; + // set permissions for rwx on whole volume recursively + self.docker_client + .container_run( + ContainerRunOptions::new("alpine:latest", vec!["chmod", "-R", "777", "/scripts"]) + .volume_mounts(HashMap::from([( + zombie_wrapper_volume_name.as_ref(), + "/scripts", + )])) + .rm(), + ) + .await + .map_err(|err| ProviderError::CreateNamespaceFailed(self.name.clone(), err.into()))?; - Ok(()) - } + Ok(()) + } - async fn initialize_helper_binaries_volume(&self) -> Result<(), ProviderError> { - let helper_binaries_volume_name = format!("{}-helper-binaries", self.name); - let zombie_wrapper_volume_name = format!("{}-zombie-wrapper", self.name); + async fn initialize_helper_binaries_volume(&self) -> Result<(), ProviderError> { + let helper_binaries_volume_name = format!("{}-helper-binaries", self.name); + let zombie_wrapper_volume_name = format!("{}-zombie-wrapper", self.name); - self.docker_client - .create_volume(&helper_binaries_volume_name) - .await - .map_err(|err| ProviderError::CreateNamespaceFailed(self.name.clone(), err.into()))?; + self.docker_client + .create_volume(&helper_binaries_volume_name) + .await + .map_err(|err| ProviderError::CreateNamespaceFailed(self.name.clone(), err.into()))?; - // download binaries to volume - self.docker_client - .container_run( - ContainerRunOptions::new( + // download binaries to volume + self.docker_client + .container_run( + ContainerRunOptions::new( "alpine:latest", vec!["ash", "/scripts/helper-binaries-downloader.sh"], ) @@ -234,261 +229,242 @@ where // wait until complete .detach(false) .rm(), - ) - .await - .map_err(|err| ProviderError::CreateNamespaceFailed(self.name.clone(), err.into()))?; + ) + .await + .map_err(|err| ProviderError::CreateNamespaceFailed(self.name.clone(), err.into()))?; - // set permissions for rwx on whole volume recursively - self.docker_client - .container_run( - ContainerRunOptions::new("alpine:latest", vec!["chmod", "-R", "777", "/helpers"]) - .volume_mounts(HashMap::from([( - helper_binaries_volume_name.as_ref(), - "/helpers", - )])) - .rm(), - ) - .await - .map_err(|err| ProviderError::CreateNamespaceFailed(self.name.clone(), err.into()))?; + // set permissions for rwx on whole volume recursively + self.docker_client + .container_run( + ContainerRunOptions::new("alpine:latest", vec!["chmod", "-R", "777", "/helpers"]) + .volume_mounts(HashMap::from([( + helper_binaries_volume_name.as_ref(), + "/helpers", + )])) + .rm(), + ) + .await + .map_err(|err| ProviderError::CreateNamespaceFailed(self.name.clone(), err.into()))?; - Ok(()) - } + Ok(()) + } - pub async fn set_delete_on_drop(&self, delete_on_drop: bool) { - *self.delete_on_drop.lock().await = delete_on_drop; - } + pub async fn set_delete_on_drop(&self, delete_on_drop: bool) { + *self.delete_on_drop.lock().await = delete_on_drop; + } - pub async fn delete_on_drop(&self) -> bool { - if let Ok(delete_on_drop) = self.delete_on_drop.try_lock() { - *delete_on_drop - } else { - // if we can't lock just remove the ns - true - } - } + pub async fn delete_on_drop(&self) -> bool { + if let Ok(delete_on_drop) = self.delete_on_drop.try_lock() { + *delete_on_drop + } else { + // if we can't lock just remove the ns + true + } + } } #[async_trait] impl ProviderNamespace for DockerNamespace where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - fn name(&self) -> &str { - &self.name - } + fn name(&self) -> &str { + &self.name + } - fn base_dir(&self) -> &PathBuf { - &self.base_dir - } + fn base_dir(&self) -> &PathBuf { + &self.base_dir + } - fn capabilities(&self) -> &ProviderCapabilities { - &self.capabilities - } + fn capabilities(&self) -> &ProviderCapabilities { + &self.capabilities + } - fn provider_name(&self) -> &str { - provider::PROVIDER_NAME - } + fn provider_name(&self) -> &str { + provider::PROVIDER_NAME + } - async fn detach(&self) { - self.set_delete_on_drop(false).await; - } + async fn detach(&self) { + self.set_delete_on_drop(false).await; + } - async fn is_detached(&self) -> bool { - self.delete_on_drop().await - } + async fn is_detached(&self) -> bool { + self.delete_on_drop().await + } - async fn nodes(&self) -> HashMap { - self.nodes - .read() - .await - .iter() - .map(|(name, node)| (name.clone(), node.clone() as DynNode)) - .collect() - } + async fn nodes(&self) -> HashMap { + self.nodes + .read() + .await + .iter() + .map(|(name, node)| (name.clone(), node.clone() as DynNode)) + .collect() + } - async fn get_node_available_args( - &self, - (command, image): (String, Option), - ) -> Result { - let node_image = image.expect(&format!("image should be present when getting node available args with docker provider {THIS_IS_A_BUG}")); + async fn get_node_available_args( + &self, + (command, image): (String, Option), + ) -> Result { + let node_image = image.expect(&format!("image should be present when getting node available args with docker provider {THIS_IS_A_BUG}")); - let temp_node = self - .spawn_node( - &SpawnNodeOptions::new(format!("temp-{}", Uuid::new_v4()), "cat".to_string()) - .image(node_image.clone()), - ) - .await?; + let temp_node = self + .spawn_node( + &SpawnNodeOptions::new(format!("temp-{}", Uuid::new_v4()), "cat".to_string()) + .image(node_image.clone()), + ) + .await?; - let available_args_output = temp_node - .run_command(RunCommandOptions::new(command.clone()).args(vec!["--help"])) - .await? - .map_err(|(_exit, status)| { - ProviderError::NodeAvailableArgsError(node_image, command, status) - })?; + let available_args_output = temp_node + .run_command(RunCommandOptions::new(command.clone()).args(vec!["--help"])) + .await? + .map_err(|(_exit, status)| { + ProviderError::NodeAvailableArgsError(node_image, command, status) + })?; - temp_node.destroy().await?; + temp_node.destroy().await?; - Ok(available_args_output) - } + Ok(available_args_output) + } - async fn spawn_node(&self, options: &SpawnNodeOptions) -> Result { - debug!("spawn option {:?}", options); + async fn spawn_node(&self, options: &SpawnNodeOptions) -> Result { + debug!("spawn option {:?}", options); - let node = DockerNode::new(DockerNodeOptions { - namespace: &self.weak, - namespace_base_dir: &self.base_dir, - name: &options.name, - image: options.image.as_ref(), - program: &options.program, - args: &options.args, - env: &options.env, - startup_files: &options.injected_files, - db_snapshot: options.db_snapshot.as_ref(), - docker_client: &self.docker_client, - container_name: format!("{}-{}", self.name, options.name), - filesystem: &self.filesystem, - port_mapping: options.port_mapping.as_ref().unwrap_or(&HashMap::default()), - }) - .await?; + let node = DockerNode::new(DockerNodeOptions { + namespace: &self.weak, + namespace_base_dir: &self.base_dir, + name: &options.name, + image: options.image.as_ref(), + program: &options.program, + args: &options.args, + env: &options.env, + startup_files: &options.injected_files, + db_snapshot: options.db_snapshot.as_ref(), + docker_client: &self.docker_client, + container_name: format!("{}-{}", self.name, options.name), + filesystem: &self.filesystem, + port_mapping: options.port_mapping.as_ref().unwrap_or(&HashMap::default()), + }) + .await?; - self.nodes - .write() - .await - .insert(node.name().to_string(), node.clone()); + self.nodes.write().await.insert(node.name().to_string(), node.clone()); - Ok(node) - } + Ok(node) + } - async fn spawn_node_from_json( - &self, - json_value: &serde_json::Value, - ) -> Result { - let deserializable: DeserializableDockerNodeOptions = - serde_json::from_value(json_value.clone())?; - let options = DockerNodeOptions::from_deserializable( - &deserializable, - &self.weak, - &self.base_dir, - &self.docker_client, - &self.filesystem, - ); + async fn spawn_node_from_json( + &self, + json_value: &serde_json::Value, + ) -> Result { + let deserializable: DeserializableDockerNodeOptions = + serde_json::from_value(json_value.clone())?; + let options = DockerNodeOptions::from_deserializable( + &deserializable, + &self.weak, + &self.base_dir, + &self.docker_client, + &self.filesystem, + ); - let node = DockerNode::attach_to_live(options).await?; + let node = DockerNode::attach_to_live(options).await?; - self.nodes - .write() - .await - .insert(node.name().to_string(), node.clone()); + self.nodes.write().await.insert(node.name().to_string(), node.clone()); - Ok(node) - } + Ok(node) + } - async fn generate_files(&self, options: GenerateFilesOptions) -> Result<(), ProviderError> { - debug!("generate files options {options:#?}"); + async fn generate_files(&self, options: GenerateFilesOptions) -> Result<(), ProviderError> { + debug!("generate files options {options:#?}"); - let node_name = options - .temp_name - .unwrap_or_else(|| format!("temp-{}", Uuid::new_v4())); - let node_image = options.image.expect(&format!( - "image should be present when generating files with docker provider {THIS_IS_A_BUG}" - )); + let node_name = options.temp_name.unwrap_or_else(|| format!("temp-{}", Uuid::new_v4())); + let node_image = options.image.expect(&format!( + "image should be present when generating files with docker provider {THIS_IS_A_BUG}" + )); - // run dummy command in a new container - let temp_node = self - .spawn_node( - &SpawnNodeOptions::new(node_name, "cat".to_string()) - .injected_files(options.injected_files) - .image(node_image), - ) - .await?; + // run dummy command in a new container + let temp_node = self + .spawn_node( + &SpawnNodeOptions::new(node_name, "cat".to_string()) + .injected_files(options.injected_files) + .image(node_image), + ) + .await?; - for GenerateFileCommand { - program, - args, - env, - local_output_path, - } in options.commands - { - let local_output_full_path = format!( - "{}{}{}", - self.base_dir.to_string_lossy(), - if local_output_path.starts_with("/") { - "" - } else { - "/" - }, - local_output_path.to_string_lossy() - ); + for GenerateFileCommand { program, args, env, local_output_path } in options.commands { + let local_output_full_path = format!( + "{}{}{}", + self.base_dir.to_string_lossy(), + if local_output_path.starts_with("/") { "" } else { "/" }, + local_output_path.to_string_lossy() + ); - let contents = extract_execution_result( - &temp_node, - RunCommandOptions { program, args, env }, - options.expected_path.as_ref(), - ) - .await?; - self.filesystem - .write(local_output_full_path, contents) - .await - .map_err(|err| ProviderError::FileGenerationFailed(err.into()))?; - } + let contents = extract_execution_result( + &temp_node, + RunCommandOptions { program, args, env }, + options.expected_path.as_ref(), + ) + .await?; + self.filesystem + .write(local_output_full_path, contents) + .await + .map_err(|err| ProviderError::FileGenerationFailed(err.into()))?; + } - temp_node.destroy().await - } + temp_node.destroy().await + } - async fn static_setup(&self) -> Result<(), ProviderError> { - todo!() - } + async fn static_setup(&self) -> Result<(), ProviderError> { + todo!() + } - async fn destroy(&self) -> Result<(), ProviderError> { - let _ = self - .docker_client - .namespaced_containers_rm(&self.name) - .await - .map_err(|err| ProviderError::DeleteNamespaceFailed(self.name.clone(), err.into()))?; + async fn destroy(&self) -> Result<(), ProviderError> { + let _ = + self.docker_client.namespaced_containers_rm(&self.name).await.map_err(|err| { + ProviderError::DeleteNamespaceFailed(self.name.clone(), err.into()) + })?; - if let Some(provider) = self.provider.upgrade() { - provider.namespaces.write().await.remove(&self.name); - } + if let Some(provider) = self.provider.upgrade() { + provider.namespaces.write().await.remove(&self.name); + } - Ok(()) - } + Ok(()) + } } impl Drop for DockerNamespace where - FS: FileSystem + Send + Sync + Clone, + FS: FileSystem + Send + Sync + Clone, { - fn drop(&mut self) { - let ns_name = self.name.clone(); - if let Ok(delete_on_drop) = self.delete_on_drop.try_lock() { - if *delete_on_drop { - let client = self.docker_client.clone(); - let provider = self.provider.upgrade(); + fn drop(&mut self) { + let ns_name = self.name.clone(); + if let Ok(delete_on_drop) = self.delete_on_drop.try_lock() { + if *delete_on_drop { + let client = self.docker_client.clone(); + let provider = self.provider.upgrade(); - let handler = thread::spawn(move || { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(async move { - trace!("🧟 deleting ns {ns_name} from cluster"); - let _ = client.namespaced_containers_rm(&ns_name).await; - trace!("✅ deleted"); - }); - }); + let handler = thread::spawn(move || { + let rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(async move { + trace!("🧟 deleting ns {ns_name} from cluster"); + let _ = client.namespaced_containers_rm(&ns_name).await; + trace!("✅ deleted"); + }); + }); - if handler.join().is_ok() { - if let Some(provider) = provider { - if let Ok(mut p) = provider.namespaces.try_write() { - p.remove(&self.name); - } else { - warn!( - "⚠️ Can not acquire write lock to the provider, ns {} not removed", - self.name - ); - } - } - } - } else { - trace!("⚠️ leaking ns {ns_name} in cluster"); - } - }; - } + if handler.join().is_ok() { + if let Some(provider) = provider { + if let Ok(mut p) = provider.namespaces.try_write() { + p.remove(&self.name); + } else { + warn!( + "⚠️ Can not acquire write lock to the provider, ns {} not removed", + self.name + ); + } + } + } + } else { + trace!("⚠️ leaking ns {ns_name} in cluster"); + } + }; + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/docker/node.rs b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/docker/node.rs index 72a4076c..255299ea 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/docker/node.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/docker/node.rs @@ -1,9 +1,9 @@ use std::{ - collections::HashMap, - net::IpAddr, - path::{Component, Path, PathBuf}, - sync::{Arc, Weak}, - time::Duration, + collections::HashMap, + net::IpAddr, + path::{Component, Path, PathBuf}, + sync::{Arc, Weak}, + time::Duration, }; use anyhow::anyhow; @@ -16,644 +16,621 @@ use tokio::{time::sleep, try_join}; use tracing::debug; use super::{ - client::{ContainerRunOptions, DockerClient}, - namespace::DockerNamespace, + client::{ContainerRunOptions, DockerClient}, + namespace::DockerNamespace, }; use crate::{ - constants::{NODE_CONFIG_DIR, NODE_DATA_DIR, NODE_RELAY_DATA_DIR, NODE_SCRIPTS_DIR}, - docker, - types::{ExecutionResult, Port, RunCommandOptions, RunScriptOptions, TransferedFile}, - ProviderError, ProviderNamespace, ProviderNode, + constants::{NODE_CONFIG_DIR, NODE_DATA_DIR, NODE_RELAY_DATA_DIR, NODE_SCRIPTS_DIR}, + docker, + types::{ExecutionResult, Port, RunCommandOptions, RunScriptOptions, TransferedFile}, + ProviderError, ProviderNamespace, ProviderNode, }; pub(super) struct DockerNodeOptions<'a, FS> where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - pub(super) namespace: &'a Weak>, - pub(super) namespace_base_dir: &'a PathBuf, - pub(super) name: &'a str, - pub(super) image: Option<&'a String>, - pub(super) program: &'a str, - pub(super) args: &'a [String], - pub(super) env: &'a [(String, String)], - pub(super) startup_files: &'a [TransferedFile], - pub(super) db_snapshot: Option<&'a AssetLocation>, - pub(super) docker_client: &'a DockerClient, - pub(super) container_name: String, - pub(super) filesystem: &'a FS, - pub(super) port_mapping: &'a HashMap, + pub(super) namespace: &'a Weak>, + pub(super) namespace_base_dir: &'a PathBuf, + pub(super) name: &'a str, + pub(super) image: Option<&'a String>, + pub(super) program: &'a str, + pub(super) args: &'a [String], + pub(super) env: &'a [(String, String)], + pub(super) startup_files: &'a [TransferedFile], + pub(super) db_snapshot: Option<&'a AssetLocation>, + pub(super) docker_client: &'a DockerClient, + pub(super) container_name: String, + pub(super) filesystem: &'a FS, + pub(super) port_mapping: &'a HashMap, } impl<'a, FS> DockerNodeOptions<'a, FS> where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - pub fn from_deserializable( - deserializable: &'a DeserializableDockerNodeOptions, - namespace: &'a Weak>, - namespace_base_dir: &'a PathBuf, - docker_client: &'a DockerClient, - filesystem: &'a FS, - ) -> Self { - DockerNodeOptions { - namespace, - namespace_base_dir, - name: &deserializable.name, - image: deserializable.image.as_ref(), - program: &deserializable.program, - args: &deserializable.args, - env: &deserializable.env, - startup_files: &[], - db_snapshot: None, - docker_client, - container_name: deserializable.container_name.clone(), - filesystem, - port_mapping: &deserializable.port_mapping, - } - } + pub fn from_deserializable( + deserializable: &'a DeserializableDockerNodeOptions, + namespace: &'a Weak>, + namespace_base_dir: &'a PathBuf, + docker_client: &'a DockerClient, + filesystem: &'a FS, + ) -> Self { + DockerNodeOptions { + namespace, + namespace_base_dir, + name: &deserializable.name, + image: deserializable.image.as_ref(), + program: &deserializable.program, + args: &deserializable.args, + env: &deserializable.env, + startup_files: &[], + db_snapshot: None, + docker_client, + container_name: deserializable.container_name.clone(), + filesystem, + port_mapping: &deserializable.port_mapping, + } + } } #[derive(Deserialize)] pub(super) struct DeserializableDockerNodeOptions { - pub(super) name: String, - pub(super) image: Option, - pub(super) program: String, - pub(super) args: Vec, - pub(super) env: Vec<(String, String)>, - pub(super) container_name: String, - pub(super) port_mapping: HashMap, + pub(super) name: String, + pub(super) image: Option, + pub(super) program: String, + pub(super) args: Vec, + pub(super) env: Vec<(String, String)>, + pub(super) container_name: String, + pub(super) port_mapping: HashMap, } #[derive(Serialize)] pub struct DockerNode where - FS: FileSystem + Send + Sync + Clone, + FS: FileSystem + Send + Sync + Clone, { - #[serde(skip)] - namespace: Weak>, - name: String, - image: String, - program: String, - args: Vec, - env: Vec<(String, String)>, - base_dir: PathBuf, - config_dir: PathBuf, - data_dir: PathBuf, - relay_data_dir: PathBuf, - scripts_dir: PathBuf, - log_path: PathBuf, - #[serde(skip)] - docker_client: DockerClient, - container_name: String, - port_mapping: HashMap, - #[allow(dead_code)] - #[serde(skip)] - filesystem: FS, - provider_tag: String, + #[serde(skip)] + namespace: Weak>, + name: String, + image: String, + program: String, + args: Vec, + env: Vec<(String, String)>, + base_dir: PathBuf, + config_dir: PathBuf, + data_dir: PathBuf, + relay_data_dir: PathBuf, + scripts_dir: PathBuf, + log_path: PathBuf, + #[serde(skip)] + docker_client: DockerClient, + container_name: String, + port_mapping: HashMap, + #[allow(dead_code)] + #[serde(skip)] + filesystem: FS, + provider_tag: String, } impl DockerNode where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - pub(super) async fn new( - options: DockerNodeOptions<'_, FS>, - ) -> Result, ProviderError> { - let image = options.image.ok_or_else(|| { - ProviderError::MissingNodeInfo(options.name.to_string(), "missing image".to_string()) - })?; + pub(super) async fn new( + options: DockerNodeOptions<'_, FS>, + ) -> Result, ProviderError> { + let image = options.image.ok_or_else(|| { + ProviderError::MissingNodeInfo(options.name.to_string(), "missing image".to_string()) + })?; - let filesystem = options.filesystem.clone(); + let filesystem = options.filesystem.clone(); - let base_dir = - PathBuf::from_iter([options.namespace_base_dir, &PathBuf::from(options.name)]); - filesystem.create_dir_all(&base_dir).await?; + let base_dir = + PathBuf::from_iter([options.namespace_base_dir, &PathBuf::from(options.name)]); + filesystem.create_dir_all(&base_dir).await?; - let base_dir_raw = base_dir.to_string_lossy(); - let config_dir = PathBuf::from(format!("{base_dir_raw}{NODE_CONFIG_DIR}")); - let data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_DATA_DIR}")); - let relay_data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_RELAY_DATA_DIR}")); - let scripts_dir = PathBuf::from(format!("{base_dir_raw}{NODE_SCRIPTS_DIR}")); - let log_path = base_dir.join("node.log"); + let base_dir_raw = base_dir.to_string_lossy(); + let config_dir = PathBuf::from(format!("{base_dir_raw}{NODE_CONFIG_DIR}")); + let data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_DATA_DIR}")); + let relay_data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_RELAY_DATA_DIR}")); + let scripts_dir = PathBuf::from(format!("{base_dir_raw}{NODE_SCRIPTS_DIR}")); + let log_path = base_dir.join("node.log"); - try_join!( - filesystem.create_dir_all(&config_dir), - filesystem.create_dir_all(&data_dir), - filesystem.create_dir_all(&relay_data_dir), - filesystem.create_dir_all(&scripts_dir), - )?; + try_join!( + filesystem.create_dir_all(&config_dir), + filesystem.create_dir_all(&data_dir), + filesystem.create_dir_all(&relay_data_dir), + filesystem.create_dir_all(&scripts_dir), + )?; - let node = Arc::new(DockerNode { - namespace: options.namespace.clone(), - name: options.name.to_string(), - image: image.to_string(), - program: options.program.to_string(), - args: options.args.to_vec(), - env: options.env.to_vec(), - base_dir, - config_dir, - data_dir, - relay_data_dir, - scripts_dir, - log_path, - filesystem: filesystem.clone(), - docker_client: options.docker_client.clone(), - container_name: options.container_name, - port_mapping: options.port_mapping.clone(), - provider_tag: docker::provider::PROVIDER_NAME.to_string(), - }); + let node = Arc::new(DockerNode { + namespace: options.namespace.clone(), + name: options.name.to_string(), + image: image.to_string(), + program: options.program.to_string(), + args: options.args.to_vec(), + env: options.env.to_vec(), + base_dir, + config_dir, + data_dir, + relay_data_dir, + scripts_dir, + log_path, + filesystem: filesystem.clone(), + docker_client: options.docker_client.clone(), + container_name: options.container_name, + port_mapping: options.port_mapping.clone(), + provider_tag: docker::provider::PROVIDER_NAME.to_string(), + }); - node.initialize_docker().await?; + node.initialize_docker().await?; - if let Some(db_snap) = options.db_snapshot { - node.initialize_db_snapshot(db_snap).await?; - } + if let Some(db_snap) = options.db_snapshot { + node.initialize_db_snapshot(db_snap).await?; + } - node.initialize_startup_files(options.startup_files).await?; + node.initialize_startup_files(options.startup_files).await?; - node.start().await?; + node.start().await?; - Ok(node) - } + Ok(node) + } - pub(super) async fn attach_to_live( - options: DockerNodeOptions<'_, FS>, - ) -> Result, ProviderError> { - let image = options.image.ok_or_else(|| { - ProviderError::MissingNodeInfo(options.name.to_string(), "missing image".to_string()) - })?; + pub(super) async fn attach_to_live( + options: DockerNodeOptions<'_, FS>, + ) -> Result, ProviderError> { + let image = options.image.ok_or_else(|| { + ProviderError::MissingNodeInfo(options.name.to_string(), "missing image".to_string()) + })?; - let filesystem = options.filesystem.clone(); + let filesystem = options.filesystem.clone(); - let base_dir = - PathBuf::from_iter([options.namespace_base_dir, &PathBuf::from(options.name)]); - filesystem.create_dir_all(&base_dir).await?; + let base_dir = + PathBuf::from_iter([options.namespace_base_dir, &PathBuf::from(options.name)]); + filesystem.create_dir_all(&base_dir).await?; - let base_dir_raw = base_dir.to_string_lossy(); - let config_dir = PathBuf::from(format!("{base_dir_raw}{NODE_CONFIG_DIR}")); - let data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_DATA_DIR}")); - let relay_data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_RELAY_DATA_DIR}")); - let scripts_dir = PathBuf::from(format!("{base_dir_raw}{NODE_SCRIPTS_DIR}")); - let log_path = base_dir.join("node.log"); + let base_dir_raw = base_dir.to_string_lossy(); + let config_dir = PathBuf::from(format!("{base_dir_raw}{NODE_CONFIG_DIR}")); + let data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_DATA_DIR}")); + let relay_data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_RELAY_DATA_DIR}")); + let scripts_dir = PathBuf::from(format!("{base_dir_raw}{NODE_SCRIPTS_DIR}")); + let log_path = base_dir.join("node.log"); - let node = Arc::new(DockerNode { - namespace: options.namespace.clone(), - name: options.name.to_string(), - image: image.to_string(), - program: options.program.to_string(), - args: options.args.to_vec(), - env: options.env.to_vec(), - base_dir, - config_dir, - data_dir, - relay_data_dir, - scripts_dir, - log_path, - filesystem: filesystem.clone(), - docker_client: options.docker_client.clone(), - container_name: options.container_name, - port_mapping: options.port_mapping.clone(), - provider_tag: docker::provider::PROVIDER_NAME.to_string(), - }); + let node = Arc::new(DockerNode { + namespace: options.namespace.clone(), + name: options.name.to_string(), + image: image.to_string(), + program: options.program.to_string(), + args: options.args.to_vec(), + env: options.env.to_vec(), + base_dir, + config_dir, + data_dir, + relay_data_dir, + scripts_dir, + log_path, + filesystem: filesystem.clone(), + docker_client: options.docker_client.clone(), + container_name: options.container_name, + port_mapping: options.port_mapping.clone(), + provider_tag: docker::provider::PROVIDER_NAME.to_string(), + }); - Ok(node) - } + Ok(node) + } - async fn initialize_docker(&self) -> Result<(), ProviderError> { - let command = [vec![self.program.to_string()], self.args.to_vec()].concat(); + async fn initialize_docker(&self) -> Result<(), ProviderError> { + let command = [vec![self.program.to_string()], self.args.to_vec()].concat(); - self.docker_client - .container_run( - ContainerRunOptions::new(&self.image, command) - .name(&self.container_name) - .env(self.env.clone()) - .volume_mounts(HashMap::from([ - ( - format!("{}-zombie-wrapper", self.namespace_name(),), - "/scripts".to_string(), - ), - ( - format!("{}-helper-binaries", self.namespace_name()), - "/helpers".to_string(), - ), - ( - self.config_dir.to_string_lossy().into_owned(), - "/cfg".to_string(), - ), - ( - self.data_dir.to_string_lossy().into_owned(), - "/data".to_string(), - ), - ( - self.relay_data_dir.to_string_lossy().into_owned(), - "/relay-data".to_string(), - ), - ])) - .entrypoint("/scripts/zombie-wrapper.sh") - .port_mapping(&self.port_mapping), - ) - .await - .map_err(|err| ProviderError::NodeSpawningFailed(self.name.clone(), err.into()))?; + self.docker_client + .container_run( + ContainerRunOptions::new(&self.image, command) + .name(&self.container_name) + .env(self.env.clone()) + .volume_mounts(HashMap::from([ + ( + format!("{}-zombie-wrapper", self.namespace_name(),), + "/scripts".to_string(), + ), + ( + format!("{}-helper-binaries", self.namespace_name()), + "/helpers".to_string(), + ), + (self.config_dir.to_string_lossy().into_owned(), "/cfg".to_string()), + (self.data_dir.to_string_lossy().into_owned(), "/data".to_string()), + ( + self.relay_data_dir.to_string_lossy().into_owned(), + "/relay-data".to_string(), + ), + ])) + .entrypoint("/scripts/zombie-wrapper.sh") + .port_mapping(&self.port_mapping), + ) + .await + .map_err(|err| ProviderError::NodeSpawningFailed(self.name.clone(), err.into()))?; - // change dirs permission - let _ = self - .docker_client - .container_exec( - &self.container_name, - ["chmod", "777", "/cfg", "/data", "/relay-data"].into(), - None, - Some("root"), - ) - .await - .map_err(|err| ProviderError::NodeSpawningFailed(self.name.clone(), err.into()))?; + // change dirs permission + let _ = self + .docker_client + .container_exec( + &self.container_name, + ["chmod", "777", "/cfg", "/data", "/relay-data"].into(), + None, + Some("root"), + ) + .await + .map_err(|err| ProviderError::NodeSpawningFailed(self.name.clone(), err.into()))?; - Ok(()) - } + Ok(()) + } - async fn initialize_db_snapshot( - &self, - _db_snapshot: &AssetLocation, - ) -> Result<(), ProviderError> { - todo!() - // trace!("snap: {db_snapshot}"); - // let url_of_snap = match db_snapshot { - // AssetLocation::Url(location) => location.clone(), - // AssetLocation::FilePath(filepath) => self.upload_to_fileserver(filepath).await?, - // }; + async fn initialize_db_snapshot( + &self, + _db_snapshot: &AssetLocation, + ) -> Result<(), ProviderError> { + todo!() + // trace!("snap: {db_snapshot}"); + // let url_of_snap = match db_snapshot { + // AssetLocation::Url(location) => location.clone(), + // AssetLocation::FilePath(filepath) => self.upload_to_fileserver(filepath).await?, + // }; - // // we need to get the snapshot from a public access - // // and extract to /data - // let opts = RunCommandOptions::new("mkdir").args([ - // "-p", - // "/data/", - // "&&", - // "mkdir", - // "-p", - // "/relay-data/", - // "&&", - // // Use our version of curl - // "/cfg/curl", - // url_of_snap.as_ref(), - // "--output", - // "/data/db.tgz", - // "&&", - // "cd", - // "/", - // "&&", - // "tar", - // "--skip-old-files", - // "-xzvf", - // "/data/db.tgz", - // ]); + // // we need to get the snapshot from a public access + // // and extract to /data + // let opts = RunCommandOptions::new("mkdir").args([ + // "-p", + // "/data/", + // "&&", + // "mkdir", + // "-p", + // "/relay-data/", + // "&&", + // // Use our version of curl + // "/cfg/curl", + // url_of_snap.as_ref(), + // "--output", + // "/data/db.tgz", + // "&&", + // "cd", + // "/", + // "&&", + // "tar", + // "--skip-old-files", + // "-xzvf", + // "/data/db.tgz", + // ]); - // trace!("cmd opts: {:#?}", opts); - // let _ = self.run_command(opts).await?; + // trace!("cmd opts: {:#?}", opts); + // let _ = self.run_command(opts).await?; - // Ok(()) - } + // Ok(()) + } - async fn initialize_startup_files( - &self, - startup_files: &[TransferedFile], - ) -> Result<(), ProviderError> { - try_join_all( - startup_files - .iter() - .map(|file| self.send_file(&file.local_path, &file.remote_path, &file.mode)), - ) - .await?; + async fn initialize_startup_files( + &self, + startup_files: &[TransferedFile], + ) -> Result<(), ProviderError> { + try_join_all( + startup_files + .iter() + .map(|file| self.send_file(&file.local_path, &file.remote_path, &file.mode)), + ) + .await?; - Ok(()) - } + Ok(()) + } - pub(super) async fn start(&self) -> Result<(), ProviderError> { - self.docker_client - .container_exec( - &self.container_name, - vec!["sh", "-c", "echo start > /tmp/zombiepipe"], - None, - None, - ) - .await - .map_err(|err| { - ProviderError::NodeSpawningFailed( - format!("failed to start pod {} after spawning", self.name), - err.into(), - ) - })? - .map_err(|err| { - ProviderError::NodeSpawningFailed( - format!("failed to start pod {} after spawning", self.name,), - anyhow!("command failed in container: status {}: {}", err.0, err.1), - ) - })?; + pub(super) async fn start(&self) -> Result<(), ProviderError> { + self.docker_client + .container_exec( + &self.container_name, + vec!["sh", "-c", "echo start > /tmp/zombiepipe"], + None, + None, + ) + .await + .map_err(|err| { + ProviderError::NodeSpawningFailed( + format!("failed to start pod {} after spawning", self.name), + err.into(), + ) + })? + .map_err(|err| { + ProviderError::NodeSpawningFailed( + format!("failed to start pod {} after spawning", self.name,), + anyhow!("command failed in container: status {}: {}", err.0, err.1), + ) + })?; - Ok(()) - } + Ok(()) + } - fn get_remote_parent_dir(&self, remote_file_path: &Path) -> Option { - if let Some(remote_parent_dir) = remote_file_path.parent() { - if matches!( - remote_parent_dir.components().rev().peekable().peek(), - Some(Component::Normal(_)) - ) { - return Some(remote_parent_dir.to_path_buf()); - } - } + fn get_remote_parent_dir(&self, remote_file_path: &Path) -> Option { + if let Some(remote_parent_dir) = remote_file_path.parent() { + if matches!( + remote_parent_dir.components().rev().peekable().peek(), + Some(Component::Normal(_)) + ) { + return Some(remote_parent_dir.to_path_buf()); + } + } - None - } + None + } - async fn create_remote_dir(&self, remote_dir: &Path) -> Result<(), ProviderError> { - let _ = self - .docker_client - .container_exec( - &self.container_name, - vec!["mkdir", "-p", &remote_dir.to_string_lossy()], - None, - None, - ) - .await - .map_err(|err| { - ProviderError::NodeSpawningFailed( - format!( - "failed to create dir {} for container {}", - remote_dir.to_string_lossy(), - &self.name - ), - err.into(), - ) - })?; + async fn create_remote_dir(&self, remote_dir: &Path) -> Result<(), ProviderError> { + let _ = self + .docker_client + .container_exec( + &self.container_name, + vec!["mkdir", "-p", &remote_dir.to_string_lossy()], + None, + None, + ) + .await + .map_err(|err| { + ProviderError::NodeSpawningFailed( + format!( + "failed to create dir {} for container {}", + remote_dir.to_string_lossy(), + &self.name + ), + err.into(), + ) + })?; - Ok(()) - } + Ok(()) + } - fn namespace_name(&self) -> String { - self.namespace - .upgrade() - .map(|namespace| namespace.name().to_string()) - .unwrap_or_else(|| panic!("namespace shouldn't be dropped, {THIS_IS_A_BUG}")) - } + fn namespace_name(&self) -> String { + self.namespace + .upgrade() + .map(|namespace| namespace.name().to_string()) + .unwrap_or_else(|| panic!("namespace shouldn't be dropped, {THIS_IS_A_BUG}")) + } } #[async_trait] impl ProviderNode for DockerNode where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - fn name(&self) -> &str { - &self.name - } + fn name(&self) -> &str { + &self.name + } - fn args(&self) -> Vec<&str> { - self.args.iter().map(|arg| arg.as_str()).collect() - } + fn args(&self) -> Vec<&str> { + self.args.iter().map(|arg| arg.as_str()).collect() + } - fn base_dir(&self) -> &PathBuf { - &self.base_dir - } + fn base_dir(&self) -> &PathBuf { + &self.base_dir + } - fn config_dir(&self) -> &PathBuf { - &self.config_dir - } + fn config_dir(&self) -> &PathBuf { + &self.config_dir + } - fn data_dir(&self) -> &PathBuf { - &self.data_dir - } + fn data_dir(&self) -> &PathBuf { + &self.data_dir + } - fn relay_data_dir(&self) -> &PathBuf { - &self.relay_data_dir - } + fn relay_data_dir(&self) -> &PathBuf { + &self.relay_data_dir + } - fn scripts_dir(&self) -> &PathBuf { - &self.scripts_dir - } + fn scripts_dir(&self) -> &PathBuf { + &self.scripts_dir + } - fn log_path(&self) -> &PathBuf { - &self.log_path - } + fn log_path(&self) -> &PathBuf { + &self.log_path + } - fn log_cmd(&self) -> String { - format!( - "{} logs -f {}", - self.docker_client.client_binary(), - self.container_name - ) - } + fn log_cmd(&self) -> String { + format!("{} logs -f {}", self.docker_client.client_binary(), self.container_name) + } - fn path_in_node(&self, file: &Path) -> PathBuf { - // here is just a noop op since we will receive the path - // for the file inside the pod - PathBuf::from(file) - } + fn path_in_node(&self, file: &Path) -> PathBuf { + // here is just a noop op since we will receive the path + // for the file inside the pod + PathBuf::from(file) + } - async fn logs(&self) -> Result { - self.docker_client - .container_logs(&self.container_name) - .await - .map_err(|err| ProviderError::GetLogsFailed(self.name.to_string(), err.into())) - } + async fn logs(&self) -> Result { + self.docker_client + .container_logs(&self.container_name) + .await + .map_err(|err| ProviderError::GetLogsFailed(self.name.to_string(), err.into())) + } - async fn dump_logs(&self, local_dest: PathBuf) -> Result<(), ProviderError> { - let logs = self.logs().await?; + async fn dump_logs(&self, local_dest: PathBuf) -> Result<(), ProviderError> { + let logs = self.logs().await?; - self.filesystem - .write(local_dest, logs) - .await - .map_err(|err| ProviderError::DumpLogsFailed(self.name.to_string(), err.into()))?; + self.filesystem + .write(local_dest, logs) + .await + .map_err(|err| ProviderError::DumpLogsFailed(self.name.to_string(), err.into()))?; - Ok(()) - } + Ok(()) + } - async fn run_command( - &self, - options: RunCommandOptions, - ) -> Result { - debug!( - "running command for {} with options {:?}", - self.name, options - ); - let command = [vec![options.program], options.args].concat(); + async fn run_command( + &self, + options: RunCommandOptions, + ) -> Result { + debug!("running command for {} with options {:?}", self.name, options); + let command = [vec![options.program], options.args].concat(); - self.docker_client - .container_exec( - &self.container_name, - vec!["sh", "-c", &command.join(" ")], - Some( - options - .env - .iter() - .map(|(k, v)| (k.as_ref(), v.as_ref())) - .collect(), - ), - None, - ) - .await - .map_err(|err| { - ProviderError::RunCommandError( - format!("sh -c {}", &command.join(" ")), - format!("in pod {}", self.name), - err.into(), - ) - }) - } + self.docker_client + .container_exec( + &self.container_name, + vec!["sh", "-c", &command.join(" ")], + Some(options.env.iter().map(|(k, v)| (k.as_ref(), v.as_ref())).collect()), + None, + ) + .await + .map_err(|err| { + ProviderError::RunCommandError( + format!("sh -c {}", &command.join(" ")), + format!("in pod {}", self.name), + err.into(), + ) + }) + } - async fn run_script( - &self, - _options: RunScriptOptions, - ) -> Result { - todo!() - } + async fn run_script( + &self, + _options: RunScriptOptions, + ) -> Result { + todo!() + } - async fn send_file( - &self, - local_file_path: &Path, - remote_file_path: &Path, - mode: &str, - ) -> Result<(), ProviderError> { - if let Some(remote_parent_dir) = self.get_remote_parent_dir(remote_file_path) { - self.create_remote_dir(&remote_parent_dir).await?; - } + async fn send_file( + &self, + local_file_path: &Path, + remote_file_path: &Path, + mode: &str, + ) -> Result<(), ProviderError> { + if let Some(remote_parent_dir) = self.get_remote_parent_dir(remote_file_path) { + self.create_remote_dir(&remote_parent_dir).await?; + } - debug!( - "starting sending file for {}: {} to {} with mode {}", - self.name, - local_file_path.to_string_lossy(), - remote_file_path.to_string_lossy(), - mode - ); + debug!( + "starting sending file for {}: {} to {} with mode {}", + self.name, + local_file_path.to_string_lossy(), + remote_file_path.to_string_lossy(), + mode + ); - let _ = self - .docker_client - .container_cp(&self.container_name, local_file_path, remote_file_path) - .await - .map_err(|err| { - ProviderError::SendFile( - local_file_path.to_string_lossy().to_string(), - self.name.clone(), - err.into(), - ) - }); + let _ = self + .docker_client + .container_cp(&self.container_name, local_file_path, remote_file_path) + .await + .map_err(|err| { + ProviderError::SendFile( + local_file_path.to_string_lossy().to_string(), + self.name.clone(), + err.into(), + ) + }); - let _ = self - .docker_client - .container_exec( - &self.container_name, - vec!["chmod", mode, &remote_file_path.to_string_lossy()], - None, - None, - ) - .await - .map_err(|err| { - ProviderError::SendFile( - self.name.clone(), - local_file_path.to_string_lossy().to_string(), - err.into(), - ) - })?; + let _ = self + .docker_client + .container_exec( + &self.container_name, + vec!["chmod", mode, &remote_file_path.to_string_lossy()], + None, + None, + ) + .await + .map_err(|err| { + ProviderError::SendFile( + self.name.clone(), + local_file_path.to_string_lossy().to_string(), + err.into(), + ) + })?; - Ok(()) - } + Ok(()) + } - async fn receive_file( - &self, - _remote_src: &Path, - _local_dest: &Path, - ) -> Result<(), ProviderError> { - Ok(()) - } + async fn receive_file( + &self, + _remote_src: &Path, + _local_dest: &Path, + ) -> Result<(), ProviderError> { + Ok(()) + } - async fn ip(&self) -> Result { - let ip = self - .docker_client - .container_ip(&self.container_name) - .await - .map_err(|err| { - ProviderError::InvalidConfig(format!("Error getting container ip, err: {err}")) - })?; + async fn ip(&self) -> Result { + let ip = self.docker_client.container_ip(&self.container_name).await.map_err(|err| { + ProviderError::InvalidConfig(format!("Error getting container ip, err: {err}")) + })?; - Ok(ip.parse::().map_err(|err| { - ProviderError::InvalidConfig(format!( - "Can not parse the container ip: {ip}, err: {err}" - )) - })?) - } + Ok(ip.parse::().map_err(|err| { + ProviderError::InvalidConfig(format!( + "Can not parse the container ip: {ip}, err: {err}" + )) + })?) + } - async fn pause(&self) -> Result<(), ProviderError> { - self.docker_client - .container_exec( - &self.container_name, - vec!["sh", "-c", "echo pause > /tmp/zombiepipe"], - None, - None, - ) - .await - .map_err(|err| ProviderError::PauseNodeFailed(self.name.to_string(), err.into()))? - .map_err(|err| { - ProviderError::PauseNodeFailed( - self.name.to_string(), - anyhow!("error when pausing node: status {}: {}", err.0, err.1), - ) - })?; + async fn pause(&self) -> Result<(), ProviderError> { + self.docker_client + .container_exec( + &self.container_name, + vec!["sh", "-c", "echo pause > /tmp/zombiepipe"], + None, + None, + ) + .await + .map_err(|err| ProviderError::PauseNodeFailed(self.name.to_string(), err.into()))? + .map_err(|err| { + ProviderError::PauseNodeFailed( + self.name.to_string(), + anyhow!("error when pausing node: status {}: {}", err.0, err.1), + ) + })?; - Ok(()) - } + Ok(()) + } - async fn resume(&self) -> Result<(), ProviderError> { - self.docker_client - .container_exec( - &self.container_name, - vec!["sh", "-c", "echo resume > /tmp/zombiepipe"], - None, - None, - ) - .await - .map_err(|err| ProviderError::PauseNodeFailed(self.name.to_string(), err.into()))? - .map_err(|err| { - ProviderError::PauseNodeFailed( - self.name.to_string(), - anyhow!("error when pausing node: status {}: {}", err.0, err.1), - ) - })?; + async fn resume(&self) -> Result<(), ProviderError> { + self.docker_client + .container_exec( + &self.container_name, + vec!["sh", "-c", "echo resume > /tmp/zombiepipe"], + None, + None, + ) + .await + .map_err(|err| ProviderError::PauseNodeFailed(self.name.to_string(), err.into()))? + .map_err(|err| { + ProviderError::PauseNodeFailed( + self.name.to_string(), + anyhow!("error when pausing node: status {}: {}", err.0, err.1), + ) + })?; - Ok(()) - } + Ok(()) + } - async fn restart(&self, after: Option) -> Result<(), ProviderError> { - if let Some(duration) = after { - sleep(duration).await; - } + async fn restart(&self, after: Option) -> Result<(), ProviderError> { + if let Some(duration) = after { + sleep(duration).await; + } - self.docker_client - .container_exec( - &self.container_name, - vec!["sh", "-c", "echo restart > /tmp/zombiepipe"], - None, - None, - ) - .await - .map_err(|err| ProviderError::PauseNodeFailed(self.name.to_string(), err.into()))? - .map_err(|err| { - ProviderError::PauseNodeFailed( - self.name.to_string(), - anyhow!("error when pausing node: status {}: {}", err.0, err.1), - ) - })?; + self.docker_client + .container_exec( + &self.container_name, + vec!["sh", "-c", "echo restart > /tmp/zombiepipe"], + None, + None, + ) + .await + .map_err(|err| ProviderError::PauseNodeFailed(self.name.to_string(), err.into()))? + .map_err(|err| { + ProviderError::PauseNodeFailed( + self.name.to_string(), + anyhow!("error when pausing node: status {}: {}", err.0, err.1), + ) + })?; - Ok(()) - } + Ok(()) + } - async fn destroy(&self) -> Result<(), ProviderError> { - self.docker_client - .container_rm(&self.container_name) - .await - .map_err(|err| ProviderError::KillNodeFailed(self.name.to_string(), err.into()))?; + async fn destroy(&self) -> Result<(), ProviderError> { + self.docker_client + .container_rm(&self.container_name) + .await + .map_err(|err| ProviderError::KillNodeFailed(self.name.to_string(), err.into()))?; - if let Some(namespace) = self.namespace.upgrade() { - namespace.nodes.write().await.remove(&self.name); - } + if let Some(namespace) = self.namespace.upgrade() { + namespace.nodes.write().await.remove(&self.name); + } - Ok(()) - } + Ok(()) + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/docker/provider.rs b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/docker/provider.rs index 0beaf85a..3d7394c0 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/docker/provider.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/docker/provider.rs @@ -1,7 +1,7 @@ use std::{ - collections::HashMap, - path::{Path, PathBuf}, - sync::{Arc, Weak}, + collections::HashMap, + path::{Path, PathBuf}, + sync::{Arc, Weak}, }; use async_trait::async_trait; @@ -10,152 +10,143 @@ use tokio::sync::RwLock; use super::{client::DockerClient, namespace::DockerNamespace}; use crate::{ - shared::helpers::extract_namespace_info, types::ProviderCapabilities, DynNamespace, Provider, - ProviderError, ProviderNamespace, + shared::helpers::extract_namespace_info, types::ProviderCapabilities, DynNamespace, Provider, + ProviderError, ProviderNamespace, }; pub const PROVIDER_NAME: &str = "docker"; pub struct DockerProvider where - FS: FileSystem + Send + Sync + Clone, + FS: FileSystem + Send + Sync + Clone, { - weak: Weak>, - capabilities: ProviderCapabilities, - tmp_dir: PathBuf, - docker_client: DockerClient, - filesystem: FS, - pub(super) namespaces: RwLock>>>, + weak: Weak>, + capabilities: ProviderCapabilities, + tmp_dir: PathBuf, + docker_client: DockerClient, + filesystem: FS, + pub(super) namespaces: RwLock>>>, } impl DockerProvider where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - pub async fn new(filesystem: FS) -> Arc { - let docker_client = DockerClient::new().await.unwrap(); + pub async fn new(filesystem: FS) -> Arc { + let docker_client = DockerClient::new().await.unwrap(); - let provider = Arc::new_cyclic(|weak| DockerProvider { - weak: weak.clone(), - capabilities: ProviderCapabilities { - requires_image: true, - has_resources: false, - prefix_with_full_path: false, - use_default_ports_in_cmd: true, - }, - tmp_dir: std::env::temp_dir(), - docker_client, - filesystem, - namespaces: RwLock::new(HashMap::new()), - }); + let provider = Arc::new_cyclic(|weak| DockerProvider { + weak: weak.clone(), + capabilities: ProviderCapabilities { + requires_image: true, + has_resources: false, + prefix_with_full_path: false, + use_default_ports_in_cmd: true, + }, + tmp_dir: std::env::temp_dir(), + docker_client, + filesystem, + namespaces: RwLock::new(HashMap::new()), + }); - let cloned_provider = provider.clone(); - tokio::spawn(async move { - tokio::signal::ctrl_c().await.unwrap(); - for (_, ns) in cloned_provider.namespaces().await { - if ns.is_detached().await { - // best effort - let _ = ns.destroy().await; - } - } + let cloned_provider = provider.clone(); + tokio::spawn(async move { + tokio::signal::ctrl_c().await.unwrap(); + for (_, ns) in cloned_provider.namespaces().await { + if ns.is_detached().await { + // best effort + let _ = ns.destroy().await; + } + } - // exit the process (130, SIGINT) - std::process::exit(130) - }); + // exit the process (130, SIGINT) + std::process::exit(130) + }); - provider - } + provider + } - pub fn tmp_dir(mut self, tmp_dir: impl Into) -> Self { - self.tmp_dir = tmp_dir.into(); - self - } + pub fn tmp_dir(mut self, tmp_dir: impl Into) -> Self { + self.tmp_dir = tmp_dir.into(); + self + } } #[async_trait] impl Provider for DockerProvider where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - fn name(&self) -> &str { - PROVIDER_NAME - } + fn name(&self) -> &str { + PROVIDER_NAME + } - fn capabilities(&self) -> &ProviderCapabilities { - &self.capabilities - } + fn capabilities(&self) -> &ProviderCapabilities { + &self.capabilities + } - async fn namespaces(&self) -> HashMap { - self.namespaces - .read() - .await - .iter() - .map(|(name, namespace)| (name.clone(), namespace.clone() as DynNamespace)) - .collect() - } + async fn namespaces(&self) -> HashMap { + self.namespaces + .read() + .await + .iter() + .map(|(name, namespace)| (name.clone(), namespace.clone() as DynNamespace)) + .collect() + } - async fn create_namespace(&self) -> Result { - let namespace = DockerNamespace::new( - &self.weak, - &self.tmp_dir, - &self.capabilities, - &self.docker_client, - &self.filesystem, - None, - ) - .await?; + async fn create_namespace(&self) -> Result { + let namespace = DockerNamespace::new( + &self.weak, + &self.tmp_dir, + &self.capabilities, + &self.docker_client, + &self.filesystem, + None, + ) + .await?; - self.namespaces - .write() - .await - .insert(namespace.name().to_string(), namespace.clone()); + self.namespaces.write().await.insert(namespace.name().to_string(), namespace.clone()); - Ok(namespace) - } + Ok(namespace) + } - async fn create_namespace_with_base_dir( - &self, - base_dir: &Path, - ) -> Result { - let namespace = DockerNamespace::new( - &self.weak, - &self.tmp_dir, - &self.capabilities, - &self.docker_client, - &self.filesystem, - Some(base_dir), - ) - .await?; + async fn create_namespace_with_base_dir( + &self, + base_dir: &Path, + ) -> Result { + let namespace = DockerNamespace::new( + &self.weak, + &self.tmp_dir, + &self.capabilities, + &self.docker_client, + &self.filesystem, + Some(base_dir), + ) + .await?; - self.namespaces - .write() - .await - .insert(namespace.name().to_string(), namespace.clone()); + self.namespaces.write().await.insert(namespace.name().to_string(), namespace.clone()); - Ok(namespace) - } + Ok(namespace) + } - async fn create_namespace_from_json( - &self, - json_value: &serde_json::Value, - ) -> Result { - let (base_dir, name) = extract_namespace_info(json_value)?; + async fn create_namespace_from_json( + &self, + json_value: &serde_json::Value, + ) -> Result { + let (base_dir, name) = extract_namespace_info(json_value)?; - let namespace = DockerNamespace::attach_to_live( - &self.weak, - &self.capabilities, - &self.docker_client, - &self.filesystem, - &base_dir, - &name, - ) - .await?; + let namespace = DockerNamespace::attach_to_live( + &self.weak, + &self.capabilities, + &self.docker_client, + &self.filesystem, + &base_dir, + &name, + ) + .await?; - self.namespaces - .write() - .await - .insert(namespace.name().to_string(), namespace.clone()); + self.namespaces.write().await.insert(namespace.name().to_string(), namespace.clone()); - Ok(namespace) - } + Ok(namespace) + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/client.rs b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/client.rs index 41bffc11..66f8e776 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/client.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/client.rs @@ -1,26 +1,26 @@ use std::{ - collections::BTreeMap, fmt::Debug, os::unix::process::ExitStatusExt, process::ExitStatus, - time::Duration, + collections::BTreeMap, fmt::Debug, os::unix::process::ExitStatusExt, process::ExitStatus, + time::Duration, }; use anyhow::anyhow; use futures::{StreamExt, TryStreamExt}; use k8s_openapi::api::core::v1::{ - ConfigMap, Namespace, Pod, PodSpec, PodStatus, Service, ServiceSpec, + ConfigMap, Namespace, Pod, PodSpec, PodStatus, Service, ServiceSpec, }; use kube::{ - api::{AttachParams, DeleteParams, ListParams, LogParams, PostParams, WatchParams}, - core::{DynamicObject, GroupVersionKind, ObjectMeta, TypeMeta, WatchEvent}, - discovery::ApiResource, - runtime::{conditions, wait::await_condition}, - Api, Resource, + api::{AttachParams, DeleteParams, ListParams, LogParams, PostParams, WatchParams}, + core::{DynamicObject, GroupVersionKind, ObjectMeta, TypeMeta, WatchEvent}, + discovery::ApiResource, + runtime::{conditions, wait::await_condition}, + Api, Resource, }; use serde::de::DeserializeOwned; use support::constants::THIS_IS_A_BUG; use tokio::{ - io::{AsyncRead, ErrorKind}, - net::TcpListener, - task::JoinHandle, + io::{AsyncRead, ErrorKind}, + net::TcpListener, + task::JoinHandle, }; use tokio_util::compat::FuturesAsyncReadCompatExt; use tracing::{debug, trace}; @@ -35,261 +35,234 @@ pub type Result = core::result::Result; #[derive(Clone)] pub struct KubernetesClient { - inner: kube::Client, + inner: kube::Client, } impl KubernetesClient { - pub(super) async fn new() -> Result { - Ok(Self { - // TODO: make it more flexible with path to kube config - inner: kube::Client::try_default() - .await - .map_err(|err| Error::from(anyhow!("error initializing kubers client: {err}")))?, - }) - } + pub(super) async fn new() -> Result { + Ok(Self { + // TODO: make it more flexible with path to kube config + inner: kube::Client::try_default() + .await + .map_err(|err| Error::from(anyhow!("error initializing kubers client: {err}")))?, + }) + } - #[allow(dead_code)] - pub(super) async fn get_namespace(&self, name: &str) -> Result> { - Api::::all(self.inner.clone()) - .get_opt(name.as_ref()) - .await - .map_err(|err| Error::from(anyhow!("error while getting namespace {name}: {err}"))) - } + #[allow(dead_code)] + pub(super) async fn get_namespace(&self, name: &str) -> Result> { + Api::::all(self.inner.clone()) + .get_opt(name.as_ref()) + .await + .map_err(|err| Error::from(anyhow!("error while getting namespace {name}: {err}"))) + } - #[allow(dead_code)] - pub(super) async fn get_namespaces(&self) -> Result> { - Ok(Api::::all(self.inner.clone()) - .list(&ListParams::default()) - .await - .map_err(|err| Error::from(anyhow!("error while getting all namespaces: {err}")))? - .into_iter() - .filter(|ns| matches!(&ns.meta().name, Some(name) if name.starts_with("zombienet"))) - .collect()) - } + #[allow(dead_code)] + pub(super) async fn get_namespaces(&self) -> Result> { + Ok(Api::::all(self.inner.clone()) + .list(&ListParams::default()) + .await + .map_err(|err| Error::from(anyhow!("error while getting all namespaces: {err}")))? + .into_iter() + .filter(|ns| matches!(&ns.meta().name, Some(name) if name.starts_with("zombienet"))) + .collect()) + } - pub(super) async fn create_namespace( - &self, - name: &str, - labels: BTreeMap, - ) -> Result { - let namespaces = Api::::all(self.inner.clone()); + pub(super) async fn create_namespace( + &self, + name: &str, + labels: BTreeMap, + ) -> Result { + let namespaces = Api::::all(self.inner.clone()); - let namespace = Namespace { - metadata: ObjectMeta { - name: Some(name.to_string()), - labels: Some(labels), - ..Default::default() - }, - ..Default::default() - }; + let namespace = Namespace { + metadata: ObjectMeta { + name: Some(name.to_string()), + labels: Some(labels), + ..Default::default() + }, + ..Default::default() + }; - namespaces - .create(&PostParams::default(), &namespace) - .await - .map_err(|err| Error::from(anyhow!("error while created namespace {name}: {err}")))?; + namespaces + .create(&PostParams::default(), &namespace) + .await + .map_err(|err| Error::from(anyhow!("error while created namespace {name}: {err}")))?; - self.wait_created(namespaces, name).await?; + self.wait_created(namespaces, name).await?; - Ok(namespace) - } + Ok(namespace) + } - pub(super) async fn delete_namespace(&self, name: &str) -> Result<()> { - let namespaces = Api::::all(self.inner.clone()); + pub(super) async fn delete_namespace(&self, name: &str) -> Result<()> { + let namespaces = Api::::all(self.inner.clone()); - namespaces - .delete(name, &DeleteParams::default()) - .await - .map_err(|err| Error::from(anyhow!("error while deleting namespace {name}: {err}")))?; + namespaces + .delete(name, &DeleteParams::default()) + .await + .map_err(|err| Error::from(anyhow!("error while deleting namespace {name}: {err}")))?; - Ok(()) - } + Ok(()) + } - pub(super) async fn create_config_map_from_file( - &self, - namespace: &str, - name: &str, - file_name: &str, - file_contents: &str, - labels: BTreeMap, - ) -> Result { - let config_maps = Api::::namespaced(self.inner.clone(), namespace); + pub(super) async fn create_config_map_from_file( + &self, + namespace: &str, + name: &str, + file_name: &str, + file_contents: &str, + labels: BTreeMap, + ) -> Result { + let config_maps = Api::::namespaced(self.inner.clone(), namespace); - let config_map = ConfigMap { - metadata: ObjectMeta { - name: Some(name.to_string()), - namespace: Some(namespace.to_string()), - labels: Some(labels), - ..Default::default() - }, - data: Some(BTreeMap::from([( - file_name.to_string(), - file_contents.to_string(), - )])), - ..Default::default() - }; + let config_map = ConfigMap { + metadata: ObjectMeta { + name: Some(name.to_string()), + namespace: Some(namespace.to_string()), + labels: Some(labels), + ..Default::default() + }, + data: Some(BTreeMap::from([(file_name.to_string(), file_contents.to_string())])), + ..Default::default() + }; - config_maps - .create(&PostParams::default(), &config_map) - .await - .map_err(|err| { - Error::from(anyhow!( - "error while creating config map {name} for {file_name}: {err}" - )) - })?; + config_maps.create(&PostParams::default(), &config_map).await.map_err(|err| { + Error::from(anyhow!("error while creating config map {name} for {file_name}: {err}")) + })?; - self.wait_created(config_maps, name).await?; + self.wait_created(config_maps, name).await?; - Ok(config_map) - } + Ok(config_map) + } - pub(super) async fn create_pod( - &self, - namespace: &str, - name: &str, - spec: PodSpec, - labels: BTreeMap, - ) -> Result { - let pods = Api::::namespaced(self.inner.clone(), namespace); + pub(super) async fn create_pod( + &self, + namespace: &str, + name: &str, + spec: PodSpec, + labels: BTreeMap, + ) -> Result { + let pods = Api::::namespaced(self.inner.clone(), namespace); - let pod = Pod { - metadata: ObjectMeta { - name: Some(name.to_string()), - namespace: Some(namespace.to_string()), - labels: Some(labels), - ..Default::default() - }, - spec: Some(spec), - ..Default::default() - }; + let pod = Pod { + metadata: ObjectMeta { + name: Some(name.to_string()), + namespace: Some(namespace.to_string()), + labels: Some(labels), + ..Default::default() + }, + spec: Some(spec), + ..Default::default() + }; - pods.create(&PostParams::default(), &pod) - .await - .map_err(|err| Error::from(anyhow!("error while creating pod {name}: {err}")))?; + pods.create(&PostParams::default(), &pod) + .await + .map_err(|err| Error::from(anyhow!("error while creating pod {name}: {err}")))?; - trace!("Pod {name} checking for ready state!"); - let wait_ready = await_condition(pods, name, helpers::is_pod_ready()); - // TODO: we should use the `node_spawn_timeout` from global settings here. - let _ = tokio::time::timeout(Duration::from_secs(600), wait_ready) - .await - .map_err(|err| { - Error::from(anyhow!("error while awaiting pod {name} running: {err}")) - })?; + trace!("Pod {name} checking for ready state!"); + let wait_ready = await_condition(pods, name, helpers::is_pod_ready()); + // TODO: we should use the `node_spawn_timeout` from global settings here. + let _ = + tokio::time::timeout(Duration::from_secs(600), wait_ready).await.map_err(|err| { + Error::from(anyhow!("error while awaiting pod {name} running: {err}")) + })?; - debug!("Pod {name} is Ready!"); - Ok(pod) - } + debug!("Pod {name} is Ready!"); + Ok(pod) + } - pub(super) async fn pod_logs(&self, namespace: &str, name: &str) -> Result { - Api::::namespaced(self.inner.clone(), namespace) - .logs( - name, - &LogParams { - pretty: true, - timestamps: true, - ..Default::default() - }, - ) - .await - .map_err(|err| Error::from(anyhow!("error while getting logs for pod {name}: {err}"))) - } + pub(super) async fn pod_logs(&self, namespace: &str, name: &str) -> Result { + Api::::namespaced(self.inner.clone(), namespace) + .logs(name, &LogParams { pretty: true, timestamps: true, ..Default::default() }) + .await + .map_err(|err| Error::from(anyhow!("error while getting logs for pod {name}: {err}"))) + } - pub(super) async fn pod_status(&self, namespace: &str, name: &str) -> Result { - let pod = Api::::namespaced(self.inner.clone(), namespace) - .get(name) - .await - .map_err(|err| Error::from(anyhow!("error while getting pod {name}: {err}")))?; + pub(super) async fn pod_status(&self, namespace: &str, name: &str) -> Result { + let pod = Api::::namespaced(self.inner.clone(), namespace) + .get(name) + .await + .map_err(|err| Error::from(anyhow!("error while getting pod {name}: {err}")))?; - let status = pod.status.ok_or(Error::from(anyhow!( - "error while getting status for pod {name}" - )))?; - Ok(status) - } + let status = + pod.status.ok_or(Error::from(anyhow!("error while getting status for pod {name}")))?; + Ok(status) + } - #[allow(dead_code)] - pub(super) async fn create_pod_logs_stream( - &self, - namespace: &str, - name: &str, - ) -> Result> { - Ok(Box::new( - Api::::namespaced(self.inner.clone(), namespace) - .log_stream( - name, - &LogParams { - follow: true, - pretty: true, - timestamps: true, - ..Default::default() - }, - ) - .await - .map_err(|err| { - Error::from(anyhow!( - "error while getting a log stream for {name}: {err}" - )) - })? - .compat(), - )) - } + #[allow(dead_code)] + pub(super) async fn create_pod_logs_stream( + &self, + namespace: &str, + name: &str, + ) -> Result> { + Ok(Box::new( + Api::::namespaced(self.inner.clone(), namespace) + .log_stream( + name, + &LogParams { + follow: true, + pretty: true, + timestamps: true, + ..Default::default() + }, + ) + .await + .map_err(|err| { + Error::from(anyhow!("error while getting a log stream for {name}: {err}")) + })? + .compat(), + )) + } - pub(super) async fn pod_exec( - &self, - namespace: &str, - name: &str, - command: Vec, - ) -> Result - where - S: Into + std::fmt::Debug + Send, - { - trace!("running command: {command:?} on pod {name} for ns {namespace}"); - let mut process = Api::::namespaced(self.inner.clone(), namespace) - .exec( - name, - command, - &AttachParams::default().stdout(true).stderr(true), - ) - .await - .map_err(|err| Error::from(anyhow!("error while exec in the pod {name}: {err}")))?; + pub(super) async fn pod_exec( + &self, + namespace: &str, + name: &str, + command: Vec, + ) -> Result + where + S: Into + std::fmt::Debug + Send, + { + trace!("running command: {command:?} on pod {name} for ns {namespace}"); + let mut process = Api::::namespaced(self.inner.clone(), namespace) + .exec(name, command, &AttachParams::default().stdout(true).stderr(true)) + .await + .map_err(|err| Error::from(anyhow!("error while exec in the pod {name}: {err}")))?; - let stdout_stream = process.stdout().expect(&format!( - "stdout shouldn't be None when true passed to exec {THIS_IS_A_BUG}" - )); - let stdout = tokio_util::io::ReaderStream::new(stdout_stream) - .filter_map(|r| async { r.ok().and_then(|v| String::from_utf8(v.to_vec()).ok()) }) - .collect::>() - .await - .join(""); - let stderr_stream = process.stderr().expect(&format!( - "stderr shouldn't be None when true passed to exec {THIS_IS_A_BUG}" - )); - let stderr = tokio_util::io::ReaderStream::new(stderr_stream) - .filter_map(|r| async { r.ok().and_then(|v| String::from_utf8(v.to_vec()).ok()) }) - .collect::>() - .await - .join(""); + let stdout_stream = process + .stdout() + .expect(&format!("stdout shouldn't be None when true passed to exec {THIS_IS_A_BUG}")); + let stdout = tokio_util::io::ReaderStream::new(stdout_stream) + .filter_map(|r| async { r.ok().and_then(|v| String::from_utf8(v.to_vec()).ok()) }) + .collect::>() + .await + .join(""); + let stderr_stream = process + .stderr() + .expect(&format!("stderr shouldn't be None when true passed to exec {THIS_IS_A_BUG}")); + let stderr = tokio_util::io::ReaderStream::new(stderr_stream) + .filter_map(|r| async { r.ok().and_then(|v| String::from_utf8(v.to_vec()).ok()) }) + .collect::>() + .await + .join(""); - let status = process - .take_status() - .expect(&format!( - "first call to status shouldn't fail {THIS_IS_A_BUG}" - )) - .await; + let status = process + .take_status() + .expect(&format!("first call to status shouldn't fail {THIS_IS_A_BUG}")) + .await; - // await process to finish - process.join().await.map_err(|err| { - Error::from(anyhow!( - "error while joining process during exec for {name}: {err}" - )) - })?; + // await process to finish + process.join().await.map_err(|err| { + Error::from(anyhow!("error while joining process during exec for {name}: {err}")) + })?; - match status { - // command succeeded with stdout - Some(status) if status.status.as_ref().is_some_and(|s| s == "Success") => { - Ok(Ok(stdout)) - }, - // command failed - Some(status) if status.status.as_ref().is_some_and(|s| s == "Failure") => { - match status.reason { + match status { + // command succeeded with stdout + Some(status) if status.status.as_ref().is_some_and(|s| s == "Success") => { + Ok(Ok(stdout)) + }, + // command failed + Some(status) if status.status.as_ref().is_some_and(|s| s == "Failure") => { + match status.reason { // due to exit code Some(reason) if reason == "NonZeroExitCode" => { let exit_status = status @@ -317,247 +290,234 @@ impl KubernetesClient { panic!("command had status but no reason was present, this is a bug") }, } - }, - Some(_) => { - unreachable!("command had status but it didn't matches either Success or Failure, this is a bug from the kube.rs library itself"); - }, - None => { - panic!("command has no status following its execution, this is a bug"); - }, - } - } + }, + Some(_) => { + unreachable!("command had status but it didn't matches either Success or Failure, this is a bug from the kube.rs library itself"); + }, + None => { + panic!("command has no status following its execution, this is a bug"); + }, + } + } - pub(super) async fn delete_pod(&self, namespace: &str, name: &str) -> Result<()> { - let pods = Api::::namespaced(self.inner.clone(), namespace); + pub(super) async fn delete_pod(&self, namespace: &str, name: &str) -> Result<()> { + let pods = Api::::namespaced(self.inner.clone(), namespace); - pods.delete(name, &DeleteParams::default()) - .await - .map_err(|err| Error::from(anyhow!("error when deleting pod {name}: {err}")))?; + pods.delete(name, &DeleteParams::default()) + .await + .map_err(|err| Error::from(anyhow!("error when deleting pod {name}: {err}")))?; - await_condition(pods, name, conditions::is_deleted(name)) - .await - .map_err(|err| { - Error::from(anyhow!( - "error when waiting for pod {name} to be deleted: {err}" - )) - })?; + await_condition(pods, name, conditions::is_deleted(name)).await.map_err(|err| { + Error::from(anyhow!("error when waiting for pod {name} to be deleted: {err}")) + })?; - Ok(()) - } + Ok(()) + } - pub(super) async fn create_service( - &self, - namespace: &str, - name: &str, - spec: ServiceSpec, - labels: BTreeMap, - ) -> Result { - let services = Api::::namespaced(self.inner.clone(), namespace); + pub(super) async fn create_service( + &self, + namespace: &str, + name: &str, + spec: ServiceSpec, + labels: BTreeMap, + ) -> Result { + let services = Api::::namespaced(self.inner.clone(), namespace); - let service = Service { - metadata: ObjectMeta { - name: Some(name.to_string()), - namespace: Some(namespace.to_string()), - labels: Some(labels), - ..Default::default() - }, - spec: Some(spec), - ..Default::default() - }; + let service = Service { + metadata: ObjectMeta { + name: Some(name.to_string()), + namespace: Some(namespace.to_string()), + labels: Some(labels), + ..Default::default() + }, + spec: Some(spec), + ..Default::default() + }; - services - .create(&PostParams::default(), &service) - .await - .map_err(|err| Error::from(anyhow!("error while creating service {name}: {err}")))?; + services + .create(&PostParams::default(), &service) + .await + .map_err(|err| Error::from(anyhow!("error while creating service {name}: {err}")))?; - Ok(service) - } + Ok(service) + } - pub(super) async fn create_pod_port_forward( - &self, - namespace: &str, - name: &str, - local_port: u16, - remote_port: u16, - ) -> Result<(u16, JoinHandle<()>)> { - let pods = Api::::namespaced(self.inner.clone(), namespace); - let bind = TcpListener::bind((LOCALHOST, local_port)) - .await - .map_err(|err| { - Error::from(anyhow!( - "error binding port {local_port} for pod {name}: {err}" - )) - })?; - let local_port = bind.local_addr().map_err(|err| Error(err.into()))?.port(); - let name = name.to_string(); + pub(super) async fn create_pod_port_forward( + &self, + namespace: &str, + name: &str, + local_port: u16, + remote_port: u16, + ) -> Result<(u16, JoinHandle<()>)> { + let pods = Api::::namespaced(self.inner.clone(), namespace); + let bind = TcpListener::bind((LOCALHOST, local_port)).await.map_err(|err| { + Error::from(anyhow!("error binding port {local_port} for pod {name}: {err}")) + })?; + let local_port = bind.local_addr().map_err(|err| Error(err.into()))?.port(); + let name = name.to_string(); - const MAX_FAILURES: usize = 5; - let monitor_handle = tokio::spawn(async move { - let mut consecutive_failures = 0; - loop { - let (mut client_conn, _) = match bind.accept().await { - Ok(conn) => { - consecutive_failures = 0; - conn - }, - Err(e) => { - if consecutive_failures < MAX_FAILURES { - trace!("Port-forward accept error: {e:?}, retrying in 1s"); - tokio::time::sleep(Duration::from_secs(1)).await; - consecutive_failures += 1; - continue; - } else { - trace!("Port-forward accept failed too many times, giving up"); - break; - } - }, - }; + const MAX_FAILURES: usize = 5; + let monitor_handle = tokio::spawn(async move { + let mut consecutive_failures = 0; + loop { + let (mut client_conn, _) = match bind.accept().await { + Ok(conn) => { + consecutive_failures = 0; + conn + }, + Err(e) => { + if consecutive_failures < MAX_FAILURES { + trace!("Port-forward accept error: {e:?}, retrying in 1s"); + tokio::time::sleep(Duration::from_secs(1)).await; + consecutive_failures += 1; + continue; + } else { + trace!("Port-forward accept failed too many times, giving up"); + break; + } + }, + }; - let peer = match client_conn.peer_addr() { - Ok(addr) => addr, - Err(e) => { - trace!("Failed to get peer address: {e:?}"); - break; - }, - }; + let peer = match client_conn.peer_addr() { + Ok(addr) => addr, + Err(e) => { + trace!("Failed to get peer address: {e:?}"); + break; + }, + }; - trace!("new connection on local_port: {local_port}, peer: {peer}"); - let (name, pods) = (name.clone(), pods.clone()); + trace!("new connection on local_port: {local_port}, peer: {peer}"); + let (name, pods) = (name.clone(), pods.clone()); - tokio::spawn(async move { - loop { - // Try to establish port-forward - let mut forwarder = match pods.portforward(&name, &[remote_port]).await { - Ok(f) => { - consecutive_failures = 0; - f - }, - Err(e) => { - consecutive_failures += 1; - if consecutive_failures < MAX_FAILURES { - trace!("portforward failed to establish ({}/{}): {e:?}, retrying in 1s", + tokio::spawn(async move { + loop { + // Try to establish port-forward + let mut forwarder = match pods.portforward(&name, &[remote_port]).await { + Ok(f) => { + consecutive_failures = 0; + f + }, + Err(e) => { + consecutive_failures += 1; + if consecutive_failures < MAX_FAILURES { + trace!("portforward failed to establish ({}/{}): {e:?}, retrying in 1s", consecutive_failures, MAX_FAILURES); - tokio::time::sleep(Duration::from_secs(1)).await; - continue; - } else { - trace!("portforward failed to establish after {} attempts: {e:?}, closing connection", + tokio::time::sleep(Duration::from_secs(1)).await; + continue; + } else { + trace!("portforward failed to establish after {} attempts: {e:?}, closing connection", consecutive_failures); - break; - } - }, - }; + break; + } + }, + }; - let mut upstream_conn = match forwarder.take_stream(remote_port) { - Some(s) => s, - None => { - trace!("Failed to take stream for remote_port: {remote_port}, retrying in 1s"); - tokio::time::sleep(Duration::from_secs(1)).await; - continue; - }, - }; + let mut upstream_conn = match forwarder.take_stream(remote_port) { + Some(s) => s, + None => { + trace!("Failed to take stream for remote_port: {remote_port}, retrying in 1s"); + tokio::time::sleep(Duration::from_secs(1)).await; + continue; + }, + }; - match tokio::io::copy_bidirectional(&mut client_conn, &mut upstream_conn) - .await - { - Ok((_n1, _n2)) => { - // EOF reached, close connection - trace!("copy_bidirectional finished (EOF), closing connection"); + match tokio::io::copy_bidirectional(&mut client_conn, &mut upstream_conn) + .await + { + Ok((_n1, _n2)) => { + // EOF reached, close connection + trace!("copy_bidirectional finished (EOF), closing connection"); - drop(upstream_conn); - let _ = forwarder.join().await; + drop(upstream_conn); + let _ = forwarder.join().await; - break; - }, - Err(e) => { - let kind = e.kind(); - match kind { - ErrorKind::ConnectionReset - | ErrorKind::ConnectionAborted - | ErrorKind::ConnectionRefused - | ErrorKind::TimedOut => { - consecutive_failures += 1; - if consecutive_failures < MAX_FAILURES { - trace!("Network error ({kind:?}): {e:?}, retrying port-forward for this connection"); - tokio::time::sleep(Duration::from_secs(1)).await; - continue; - } else { - trace!("portforward failed to establish after {} attempts: {e:?}, closing connection", + break; + }, + Err(e) => { + let kind = e.kind(); + match kind { + ErrorKind::ConnectionReset + | ErrorKind::ConnectionAborted + | ErrorKind::ConnectionRefused + | ErrorKind::TimedOut => { + consecutive_failures += 1; + if consecutive_failures < MAX_FAILURES { + trace!("Network error ({kind:?}): {e:?}, retrying port-forward for this connection"); + tokio::time::sleep(Duration::from_secs(1)).await; + continue; + } else { + trace!("portforward failed to establish after {} attempts: {e:?}, closing connection", consecutive_failures); - break; - } - }, - _ => { - trace!("Non-network error ({kind:?}): {e:?}, closing connection"); - break; - }, - } - }, - } - } - }); + break; + } + }, + _ => { + trace!("Non-network error ({kind:?}): {e:?}, closing connection"); + break; + }, + } + }, + } + } + }); - trace!("finished forwarder process for local port: {local_port}, peer: {peer}"); - } - }); + trace!("finished forwarder process for local port: {local_port}, peer: {peer}"); + } + }); - Ok((local_port, monitor_handle)) - } + Ok((local_port, monitor_handle)) + } - /// Create resources from yamls in `static-configs` directory - pub(super) async fn create_static_resource( - &self, - namespace: &str, - raw_manifest: &str, - ) -> Result<()> { - let tm: TypeMeta = serde_yaml::from_str(raw_manifest).map_err(|err| { - Error::from(anyhow!( - "error while extracting TypeMeta from manifest: {raw_manifest}: {err}" - )) - })?; - let gvk = GroupVersionKind::try_from(&tm).map_err(|err| { - Error::from(anyhow!( - "error while extracting GroupVersionKind from manifest: {raw_manifest}: {err}" - )) - })?; + /// Create resources from yamls in `static-configs` directory + pub(super) async fn create_static_resource( + &self, + namespace: &str, + raw_manifest: &str, + ) -> Result<()> { + let tm: TypeMeta = serde_yaml::from_str(raw_manifest).map_err(|err| { + Error::from(anyhow!( + "error while extracting TypeMeta from manifest: {raw_manifest}: {err}" + )) + })?; + let gvk = GroupVersionKind::try_from(&tm).map_err(|err| { + Error::from(anyhow!( + "error while extracting GroupVersionKind from manifest: {raw_manifest}: {err}" + )) + })?; - let ar = ApiResource::from_gvk(&gvk); - let api: Api = Api::namespaced_with(self.inner.clone(), namespace, &ar); + let ar = ApiResource::from_gvk(&gvk); + let api: Api = Api::namespaced_with(self.inner.clone(), namespace, &ar); - api.create( - &PostParams::default(), - &serde_yaml::from_str(raw_manifest).unwrap(), - ) - .await - .map_err(|err| { - Error::from(anyhow!( - "error while creating static-config {raw_manifest}: {err}" - )) - })?; + api.create(&PostParams::default(), &serde_yaml::from_str(raw_manifest).unwrap()) + .await + .map_err(|err| { + Error::from(anyhow!("error while creating static-config {raw_manifest}: {err}")) + })?; - Ok(()) - } + Ok(()) + } - async fn wait_created(&self, api: Api, name: &str) -> Result<()> - where - K: Clone + DeserializeOwned + Debug, - { - let params = &WatchParams::default().fields(&format!("metadata.name={name}")); - let mut stream = api - .watch(params, "0") - .await - .map_err(|err| { - Error::from(anyhow!( - "error while awaiting first response when resource {name} is created: {err}" - )) - })? - .boxed(); + async fn wait_created(&self, api: Api, name: &str) -> Result<()> + where + K: Clone + DeserializeOwned + Debug, + { + let params = &WatchParams::default().fields(&format!("metadata.name={name}")); + let mut stream = api + .watch(params, "0") + .await + .map_err(|err| { + Error::from(anyhow!( + "error while awaiting first response when resource {name} is created: {err}" + )) + })? + .boxed(); - while let Some(status) = stream.try_next().await.map_err(|err| { - Error::from(anyhow!( - "error while awaiting next change after resource {name} is created: {err}" - )) - })? { - match status { + while let Some(status) = stream.try_next().await.map_err(|err| { + Error::from(anyhow!( + "error while awaiting next change after resource {name} is created: {err}" + )) + })? { + match status { WatchEvent::Added(_) => break, WatchEvent::Error(err) => Err(Error::from(anyhow!( "error while awaiting resource {name} is created: {err}" @@ -567,36 +527,36 @@ impl KubernetesClient { } any_other_event => panic!("Unexpected event happened while creating '{name}' {THIS_IS_A_BUG}. Event: {any_other_event:?}"), } - } + } - Ok(()) - } + Ok(()) + } } mod helpers { - use k8s_openapi::api::core::v1::Pod; - use kube::runtime::wait::Condition; - use tracing::trace; + use k8s_openapi::api::core::v1::Pod; + use kube::runtime::wait::Condition; + use tracing::trace; - /// An await condition for `Pod` that returns `true` once it is ready - /// based on [`kube::runtime::wait::conditions::is_pod_running`] - pub fn is_pod_ready() -> impl Condition { - |obj: Option<&Pod>| { - if let Some(pod) = &obj { - if let Some(status) = &pod.status { - if let Some(conditions) = &status.conditions { - let ready = conditions - .iter() - .any(|cond| cond.status == "True" && cond.type_ == "Ready"); + /// An await condition for `Pod` that returns `true` once it is ready + /// based on [`kube::runtime::wait::conditions::is_pod_running`] + pub fn is_pod_ready() -> impl Condition { + |obj: Option<&Pod>| { + if let Some(pod) = &obj { + if let Some(status) = &pod.status { + if let Some(conditions) = &status.conditions { + let ready = conditions + .iter() + .any(|cond| cond.status == "True" && cond.type_ == "Ready"); - if ready { - trace!("{:#?}", status); - return ready; - } - } - } - } - false - } - } + if ready { + trace!("{:#?}", status); + return ready; + } + } + } + } + false + } + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/namespace.rs b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/namespace.rs index c51d9b7d..a32ab4fd 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/namespace.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/namespace.rs @@ -1,16 +1,16 @@ use std::{ - collections::{BTreeMap, HashMap}, - env, - path::{Path, PathBuf}, - sync::{Arc, Weak}, + collections::{BTreeMap, HashMap}, + env, + path::{Path, PathBuf}, + sync::{Arc, Weak}, }; use async_trait::async_trait; use k8s_openapi::{ - api::core::v1::{ - Container, ContainerPort, HTTPGetAction, PodSpec, Probe, ServicePort, ServiceSpec, - }, - apimachinery::pkg::util::intstr::IntOrString, + api::core::v1::{ + Container, ContainerPort, HTTPGetAction, PodSpec, Probe, ServicePort, ServiceSpec, + }, + apimachinery::pkg::util::intstr::IntOrString, }; use support::{constants::THIS_IS_A_BUG, fs::FileSystem, replacer::apply_replacements}; use tokio::sync::{Mutex, RwLock}; @@ -19,17 +19,17 @@ use uuid::Uuid; use super::{client::KubernetesClient, node::KubernetesNode}; use crate::{ - constants::NAMESPACE_PREFIX, - kubernetes::{ - node::{DeserializableKubernetesNodeOptions, KubernetesNodeOptions}, - provider, - }, - shared::helpers::{extract_execution_result, running_in_ci}, - types::{ - GenerateFileCommand, GenerateFilesOptions, ProviderCapabilities, RunCommandOptions, - SpawnNodeOptions, - }, - DynNode, KubernetesProvider, ProviderError, ProviderNamespace, ProviderNode, + constants::NAMESPACE_PREFIX, + kubernetes::{ + node::{DeserializableKubernetesNodeOptions, KubernetesNodeOptions}, + provider, + }, + shared::helpers::{extract_execution_result, running_in_ci}, + types::{ + GenerateFileCommand, GenerateFilesOptions, ProviderCapabilities, RunCommandOptions, + SpawnNodeOptions, + }, + DynNode, KubernetesProvider, ProviderError, ProviderNamespace, ProviderNode, }; const FILE_SERVER_IMAGE: &str = "europe-west3-docker.pkg.dev/parity-zombienet/zombienet-public-images/zombienet-file-server:latest"; @@ -39,562 +39,512 @@ const ZOMBIE_K8S_CI_NAMESPACE: &str = "ZOMBIE_K8S_CI_NAMESPACE"; pub(super) struct KubernetesNamespace where - FS: FileSystem + Send + Sync + Clone, + FS: FileSystem + Send + Sync + Clone, { - weak: Weak>, - provider: Weak>, - name: String, - base_dir: PathBuf, - capabilities: ProviderCapabilities, - k8s_client: KubernetesClient, - filesystem: FS, - file_server_fw_task: RwLock>>, - delete_on_drop: Arc>, - pub(super) file_server_port: RwLock>, - pub(super) nodes: RwLock>>>, + weak: Weak>, + provider: Weak>, + name: String, + base_dir: PathBuf, + capabilities: ProviderCapabilities, + k8s_client: KubernetesClient, + filesystem: FS, + file_server_fw_task: RwLock>>, + delete_on_drop: Arc>, + pub(super) file_server_port: RwLock>, + pub(super) nodes: RwLock>>>, } impl KubernetesNamespace where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - pub(super) async fn new( - provider: &Weak>, - tmp_dir: &PathBuf, - capabilities: &ProviderCapabilities, - k8s_client: &KubernetesClient, - filesystem: &FS, - custom_base_dir: Option<&Path>, - ) -> Result, ProviderError> { - // If the namespace is already provided - let name = if let Ok(name) = env::var(ZOMBIE_K8S_CI_NAMESPACE) { - name - } else { - format!("{}{}", NAMESPACE_PREFIX, Uuid::new_v4()) - }; + pub(super) async fn new( + provider: &Weak>, + tmp_dir: &PathBuf, + capabilities: &ProviderCapabilities, + k8s_client: &KubernetesClient, + filesystem: &FS, + custom_base_dir: Option<&Path>, + ) -> Result, ProviderError> { + // If the namespace is already provided + let name = if let Ok(name) = env::var(ZOMBIE_K8S_CI_NAMESPACE) { + name + } else { + format!("{}{}", NAMESPACE_PREFIX, Uuid::new_v4()) + }; - let base_dir = if let Some(custom_base_dir) = custom_base_dir { - if !filesystem.exists(custom_base_dir).await { - filesystem.create_dir(custom_base_dir).await?; - } else { - warn!( - "⚠️ Using and existing directory {} as base dir", - custom_base_dir.to_string_lossy() - ); - } - PathBuf::from(custom_base_dir) - } else { - let base_dir = PathBuf::from_iter([tmp_dir, &PathBuf::from(&name)]); - filesystem.create_dir(&base_dir).await?; - base_dir - }; + let base_dir = if let Some(custom_base_dir) = custom_base_dir { + if !filesystem.exists(custom_base_dir).await { + filesystem.create_dir(custom_base_dir).await?; + } else { + warn!( + "⚠️ Using and existing directory {} as base dir", + custom_base_dir.to_string_lossy() + ); + } + PathBuf::from(custom_base_dir) + } else { + let base_dir = PathBuf::from_iter([tmp_dir, &PathBuf::from(&name)]); + filesystem.create_dir(&base_dir).await?; + base_dir + }; - let namespace = Arc::new_cyclic(|weak| KubernetesNamespace { - weak: weak.clone(), - provider: provider.clone(), - name, - base_dir, - capabilities: capabilities.clone(), - filesystem: filesystem.clone(), - k8s_client: k8s_client.clone(), - file_server_port: RwLock::new(None), - file_server_fw_task: RwLock::new(None), - nodes: RwLock::new(HashMap::new()), - delete_on_drop: Arc::new(Mutex::new(true)), - }); + let namespace = Arc::new_cyclic(|weak| KubernetesNamespace { + weak: weak.clone(), + provider: provider.clone(), + name, + base_dir, + capabilities: capabilities.clone(), + filesystem: filesystem.clone(), + k8s_client: k8s_client.clone(), + file_server_port: RwLock::new(None), + file_server_fw_task: RwLock::new(None), + nodes: RwLock::new(HashMap::new()), + delete_on_drop: Arc::new(Mutex::new(true)), + }); - namespace.initialize().await?; + namespace.initialize().await?; - Ok(namespace) - } + Ok(namespace) + } - pub(super) async fn attach_to_live( - provider: &Weak>, - capabilities: &ProviderCapabilities, - k8s_client: &KubernetesClient, - filesystem: &FS, - custom_base_dir: &Path, - name: &str, - ) -> Result, ProviderError> { - let base_dir = custom_base_dir.to_path_buf(); + pub(super) async fn attach_to_live( + provider: &Weak>, + capabilities: &ProviderCapabilities, + k8s_client: &KubernetesClient, + filesystem: &FS, + custom_base_dir: &Path, + name: &str, + ) -> Result, ProviderError> { + let base_dir = custom_base_dir.to_path_buf(); - let namespace = Arc::new_cyclic(|weak| KubernetesNamespace { - weak: weak.clone(), - provider: provider.clone(), - name: name.to_owned(), - base_dir, - capabilities: capabilities.clone(), - filesystem: filesystem.clone(), - k8s_client: k8s_client.clone(), - file_server_port: RwLock::new(None), - file_server_fw_task: RwLock::new(None), - nodes: RwLock::new(HashMap::new()), - delete_on_drop: Arc::new(Mutex::new(false)), - }); + let namespace = Arc::new_cyclic(|weak| KubernetesNamespace { + weak: weak.clone(), + provider: provider.clone(), + name: name.to_owned(), + base_dir, + capabilities: capabilities.clone(), + filesystem: filesystem.clone(), + k8s_client: k8s_client.clone(), + file_server_port: RwLock::new(None), + file_server_fw_task: RwLock::new(None), + nodes: RwLock::new(HashMap::new()), + delete_on_drop: Arc::new(Mutex::new(false)), + }); - namespace.setup_file_server_port_fwd("fileserver").await?; + namespace.setup_file_server_port_fwd("fileserver").await?; - Ok(namespace) - } + Ok(namespace) + } - async fn initialize(&self) -> Result<(), ProviderError> { - // Initialize the namespace IFF - // we are not in CI or we don't have the env `ZOMBIE_NAMESPACE` set - if env::var(ZOMBIE_K8S_CI_NAMESPACE).is_err() || !running_in_ci() { - self.initialize_k8s().await?; - } + async fn initialize(&self) -> Result<(), ProviderError> { + // Initialize the namespace IFF + // we are not in CI or we don't have the env `ZOMBIE_NAMESPACE` set + if env::var(ZOMBIE_K8S_CI_NAMESPACE).is_err() || !running_in_ci() { + self.initialize_k8s().await?; + } - // Ensure namespace isolation and minimal resources IFF we are running in CI - if running_in_ci() { - self.initialize_static_resources().await? - } + // Ensure namespace isolation and minimal resources IFF we are running in CI + if running_in_ci() { + self.initialize_static_resources().await? + } - self.initialize_file_server().await?; + self.initialize_file_server().await?; - self.setup_script_config_map( - "zombie-wrapper", - include_str!("../shared/scripts/zombie-wrapper.sh"), - "zombie_wrapper_config_map_manifest.yaml", - // TODO: add correct labels - BTreeMap::new(), - ) - .await?; + self.setup_script_config_map( + "zombie-wrapper", + include_str!("../shared/scripts/zombie-wrapper.sh"), + "zombie_wrapper_config_map_manifest.yaml", + // TODO: add correct labels + BTreeMap::new(), + ) + .await?; - self.setup_script_config_map( - "helper-binaries-downloader", - include_str!("../shared/scripts/helper-binaries-downloader.sh"), - "helper_binaries_downloader_config_map_manifest.yaml", - // TODO: add correct labels - BTreeMap::new(), - ) - .await?; + self.setup_script_config_map( + "helper-binaries-downloader", + include_str!("../shared/scripts/helper-binaries-downloader.sh"), + "helper_binaries_downloader_config_map_manifest.yaml", + // TODO: add correct labels + BTreeMap::new(), + ) + .await?; - Ok(()) - } + Ok(()) + } - async fn initialize_k8s(&self) -> Result<(), ProviderError> { - // TODO (javier): check with Hamid if we are using this labels in any scheduling logic. - let labels = BTreeMap::from([ - ( - "jobId".to_string(), - env::var("CI_JOB_ID").unwrap_or("".to_string()), - ), - ( - "projectName".to_string(), - env::var("CI_PROJECT_NAME").unwrap_or("".to_string()), - ), - ( - "projectId".to_string(), - env::var("CI_PROJECT_ID").unwrap_or("".to_string()), - ), - ]); + async fn initialize_k8s(&self) -> Result<(), ProviderError> { + // TODO (javier): check with Hamid if we are using this labels in any scheduling logic. + let labels = BTreeMap::from([ + ("jobId".to_string(), env::var("CI_JOB_ID").unwrap_or("".to_string())), + ("projectName".to_string(), env::var("CI_PROJECT_NAME").unwrap_or("".to_string())), + ("projectId".to_string(), env::var("CI_PROJECT_ID").unwrap_or("".to_string())), + ]); - let manifest = self - .k8s_client - .create_namespace(&self.name, labels) - .await - .map_err(|err| { - ProviderError::CreateNamespaceFailed(self.name.to_string(), err.into()) - })?; + let manifest = + self.k8s_client.create_namespace(&self.name, labels).await.map_err(|err| { + ProviderError::CreateNamespaceFailed(self.name.to_string(), err.into()) + })?; - let serialized_manifest = serde_yaml::to_string(&manifest).map_err(|err| { - ProviderError::CreateNamespaceFailed(self.name.to_string(), err.into()) - })?; + let serialized_manifest = serde_yaml::to_string(&manifest).map_err(|err| { + ProviderError::CreateNamespaceFailed(self.name.to_string(), err.into()) + })?; - let dest_path = - PathBuf::from_iter([&self.base_dir, &PathBuf::from("namespace_manifest.yaml")]); + let dest_path = + PathBuf::from_iter([&self.base_dir, &PathBuf::from("namespace_manifest.yaml")]); - self.filesystem - .write(dest_path, serialized_manifest) - .await?; + self.filesystem.write(dest_path, serialized_manifest).await?; - Ok(()) - } + Ok(()) + } - async fn initialize_static_resources(&self) -> Result<(), ProviderError> { - let np_manifest = apply_replacements( - include_str!("./static-configs/namespace-network-policy.yaml"), - &HashMap::from([("namespace", self.name())]), - ); + async fn initialize_static_resources(&self) -> Result<(), ProviderError> { + let np_manifest = apply_replacements( + include_str!("./static-configs/namespace-network-policy.yaml"), + &HashMap::from([("namespace", self.name())]), + ); - // Apply NetworkPolicy manifest - self.k8s_client - .create_static_resource(&self.name, &np_manifest) - .await - .map_err(|err| { - ProviderError::CreateNamespaceFailed(self.name.to_string(), err.into()) - })?; + // Apply NetworkPolicy manifest + self.k8s_client.create_static_resource(&self.name, &np_manifest).await.map_err(|err| { + ProviderError::CreateNamespaceFailed(self.name.to_string(), err.into()) + })?; - // Apply LimitRange manifest - self.k8s_client - .create_static_resource( - &self.name, - include_str!("./static-configs/baseline-resources.yaml"), - ) - .await - .map_err(|err| { - ProviderError::CreateNamespaceFailed(self.name.to_string(), err.into()) - })?; - Ok(()) - } + // Apply LimitRange manifest + self.k8s_client + .create_static_resource( + &self.name, + include_str!("./static-configs/baseline-resources.yaml"), + ) + .await + .map_err(|err| { + ProviderError::CreateNamespaceFailed(self.name.to_string(), err.into()) + })?; + Ok(()) + } - async fn initialize_file_server(&self) -> Result<(), ProviderError> { - let name = "fileserver".to_string(); - let labels = BTreeMap::from([ - ("app.kubernetes.io/name".to_string(), name.clone()), - ( - "x-infra-instance".to_string(), - env::var("X_INFRA_INSTANCE").unwrap_or("ondemand".to_string()), - ), - ]); + async fn initialize_file_server(&self) -> Result<(), ProviderError> { + let name = "fileserver".to_string(); + let labels = BTreeMap::from([ + ("app.kubernetes.io/name".to_string(), name.clone()), + ( + "x-infra-instance".to_string(), + env::var("X_INFRA_INSTANCE").unwrap_or("ondemand".to_string()), + ), + ]); - let pod_spec = PodSpec { - hostname: Some(name.clone()), - containers: vec![Container { - name: name.clone(), - image: Some(FILE_SERVER_IMAGE.to_string()), - image_pull_policy: Some("Always".to_string()), - ports: Some(vec![ContainerPort { - container_port: 80, - ..Default::default() - }]), - startup_probe: Some(Probe { - http_get: Some(HTTPGetAction { - path: Some("/".to_string()), - port: IntOrString::Int(80), - ..Default::default() - }), - initial_delay_seconds: Some(1), - period_seconds: Some(2), - failure_threshold: Some(3), - ..Default::default() - }), - ..Default::default() - }], - restart_policy: Some("OnFailure".into()), - ..Default::default() - }; + let pod_spec = PodSpec { + hostname: Some(name.clone()), + containers: vec![Container { + name: name.clone(), + image: Some(FILE_SERVER_IMAGE.to_string()), + image_pull_policy: Some("Always".to_string()), + ports: Some(vec![ContainerPort { container_port: 80, ..Default::default() }]), + startup_probe: Some(Probe { + http_get: Some(HTTPGetAction { + path: Some("/".to_string()), + port: IntOrString::Int(80), + ..Default::default() + }), + initial_delay_seconds: Some(1), + period_seconds: Some(2), + failure_threshold: Some(3), + ..Default::default() + }), + ..Default::default() + }], + restart_policy: Some("OnFailure".into()), + ..Default::default() + }; - let pod_manifest = self - .k8s_client - .create_pod(&self.name, &name, pod_spec, labels.clone()) - .await - .map_err(|err| ProviderError::FileServerSetupError(err.into()))?; + let pod_manifest = self + .k8s_client + .create_pod(&self.name, &name, pod_spec, labels.clone()) + .await + .map_err(|err| ProviderError::FileServerSetupError(err.into()))?; - // TODO: remove duplication across methods - let pod_serialized_manifest = serde_yaml::to_string(&pod_manifest) - .map_err(|err| ProviderError::FileServerSetupError(err.into()))?; + // TODO: remove duplication across methods + let pod_serialized_manifest = serde_yaml::to_string(&pod_manifest) + .map_err(|err| ProviderError::FileServerSetupError(err.into()))?; - let pod_dest_path = PathBuf::from_iter([ - &self.base_dir, - &PathBuf::from("file_server_pod_manifest.yaml"), - ]); + let pod_dest_path = + PathBuf::from_iter([&self.base_dir, &PathBuf::from("file_server_pod_manifest.yaml")]); - self.filesystem - .write(pod_dest_path, pod_serialized_manifest) - .await?; + self.filesystem.write(pod_dest_path, pod_serialized_manifest).await?; - let service_spec = ServiceSpec { - selector: Some(labels.clone()), - ports: Some(vec![ServicePort { - port: 80, - ..Default::default() - }]), - ..Default::default() - }; + let service_spec = ServiceSpec { + selector: Some(labels.clone()), + ports: Some(vec![ServicePort { port: 80, ..Default::default() }]), + ..Default::default() + }; - let service_manifest = self - .k8s_client - .create_service(&self.name, &name, service_spec, labels) - .await - .map_err(|err| ProviderError::FileServerSetupError(err.into()))?; + let service_manifest = self + .k8s_client + .create_service(&self.name, &name, service_spec, labels) + .await + .map_err(|err| ProviderError::FileServerSetupError(err.into()))?; - let serialized_service_manifest = serde_yaml::to_string(&service_manifest) - .map_err(|err| ProviderError::FileServerSetupError(err.into()))?; + let serialized_service_manifest = serde_yaml::to_string(&service_manifest) + .map_err(|err| ProviderError::FileServerSetupError(err.into()))?; - let service_dest_path = PathBuf::from_iter([ - &self.base_dir, - &PathBuf::from("file_server_service_manifest.yaml"), - ]); + let service_dest_path = PathBuf::from_iter([ + &self.base_dir, + &PathBuf::from("file_server_service_manifest.yaml"), + ]); - self.filesystem - .write(service_dest_path, serialized_service_manifest) - .await?; + self.filesystem.write(service_dest_path, serialized_service_manifest).await?; - self.setup_file_server_port_fwd(&name).await?; + self.setup_file_server_port_fwd(&name).await?; - Ok(()) - } + Ok(()) + } - async fn setup_file_server_port_fwd(&self, name: &str) -> Result<(), ProviderError> { - let (port, task) = self - .k8s_client - .create_pod_port_forward(&self.name, name, 0, 80) - .await - .map_err(|err| ProviderError::FileServerSetupError(err.into()))?; + async fn setup_file_server_port_fwd(&self, name: &str) -> Result<(), ProviderError> { + let (port, task) = self + .k8s_client + .create_pod_port_forward(&self.name, name, 0, 80) + .await + .map_err(|err| ProviderError::FileServerSetupError(err.into()))?; - *self.file_server_port.write().await = Some(port); - *self.file_server_fw_task.write().await = Some(task); + *self.file_server_port.write().await = Some(port); + *self.file_server_fw_task.write().await = Some(task); - Ok(()) - } + Ok(()) + } - async fn setup_script_config_map( - &self, - name: &str, - script_contents: &str, - local_manifest_name: &str, - labels: BTreeMap, - ) -> Result<(), ProviderError> { - let manifest = self - .k8s_client - .create_config_map_from_file( - &self.name, - name, - &format!("{name}.sh"), - script_contents, - labels, - ) - .await - .map_err(|err| { - ProviderError::CreateNamespaceFailed(self.name.to_string(), err.into()) - })?; + async fn setup_script_config_map( + &self, + name: &str, + script_contents: &str, + local_manifest_name: &str, + labels: BTreeMap, + ) -> Result<(), ProviderError> { + let manifest = self + .k8s_client + .create_config_map_from_file( + &self.name, + name, + &format!("{name}.sh"), + script_contents, + labels, + ) + .await + .map_err(|err| { + ProviderError::CreateNamespaceFailed(self.name.to_string(), err.into()) + })?; - let serializer_manifest = serde_yaml::to_string(&manifest).map_err(|err| { - ProviderError::CreateNamespaceFailed(self.name.to_string(), err.into()) - })?; + let serializer_manifest = serde_yaml::to_string(&manifest).map_err(|err| { + ProviderError::CreateNamespaceFailed(self.name.to_string(), err.into()) + })?; - let dest_path = PathBuf::from_iter([&self.base_dir, &PathBuf::from(local_manifest_name)]); + let dest_path = PathBuf::from_iter([&self.base_dir, &PathBuf::from(local_manifest_name)]); - self.filesystem - .write(dest_path, serializer_manifest) - .await?; + self.filesystem.write(dest_path, serializer_manifest).await?; - Ok(()) - } + Ok(()) + } - pub async fn set_delete_on_drop(&self, delete_on_drop: bool) { - *self.delete_on_drop.lock().await = delete_on_drop; - } + pub async fn set_delete_on_drop(&self, delete_on_drop: bool) { + *self.delete_on_drop.lock().await = delete_on_drop; + } - pub async fn delete_on_drop(&self) -> bool { - if let Ok(delete_on_drop) = self.delete_on_drop.try_lock() { - *delete_on_drop - } else { - // if we can't lock just remove the ns - true - } - } + pub async fn delete_on_drop(&self) -> bool { + if let Ok(delete_on_drop) = self.delete_on_drop.try_lock() { + *delete_on_drop + } else { + // if we can't lock just remove the ns + true + } + } } impl Drop for KubernetesNamespace where - FS: FileSystem + Send + Sync + Clone, + FS: FileSystem + Send + Sync + Clone, { - fn drop(&mut self) { - let ns_name = self.name.clone(); - if let Ok(delete_on_drop) = self.delete_on_drop.try_lock() { - if *delete_on_drop { - let client = self.k8s_client.clone(); - let provider = self.provider.upgrade(); - futures::executor::block_on(async move { - trace!("🧟 deleting ns {ns_name} from cluster"); - let _ = client.delete_namespace(&ns_name).await; - if let Some(provider) = provider { - provider.namespaces.write().await.remove(&ns_name); - } + fn drop(&mut self) { + let ns_name = self.name.clone(); + if let Ok(delete_on_drop) = self.delete_on_drop.try_lock() { + if *delete_on_drop { + let client = self.k8s_client.clone(); + let provider = self.provider.upgrade(); + futures::executor::block_on(async move { + trace!("🧟 deleting ns {ns_name} from cluster"); + let _ = client.delete_namespace(&ns_name).await; + if let Some(provider) = provider { + provider.namespaces.write().await.remove(&ns_name); + } - trace!("✅ deleted"); - }); - } else { - trace!("⚠️ leaking ns {ns_name} in cluster"); - } - }; - } + trace!("✅ deleted"); + }); + } else { + trace!("⚠️ leaking ns {ns_name} in cluster"); + } + }; + } } #[async_trait] impl ProviderNamespace for KubernetesNamespace where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - fn name(&self) -> &str { - &self.name - } + fn name(&self) -> &str { + &self.name + } - fn base_dir(&self) -> &PathBuf { - &self.base_dir - } + fn base_dir(&self) -> &PathBuf { + &self.base_dir + } - fn capabilities(&self) -> &ProviderCapabilities { - &self.capabilities - } + fn capabilities(&self) -> &ProviderCapabilities { + &self.capabilities + } - fn provider_name(&self) -> &str { - provider::PROVIDER_NAME - } + fn provider_name(&self) -> &str { + provider::PROVIDER_NAME + } - async fn detach(&self) { - self.set_delete_on_drop(false).await; - } + async fn detach(&self) { + self.set_delete_on_drop(false).await; + } - async fn is_detached(&self) -> bool { - self.delete_on_drop().await - } + async fn is_detached(&self) -> bool { + self.delete_on_drop().await + } - async fn nodes(&self) -> HashMap { - self.nodes - .read() - .await - .iter() - .map(|(name, node)| (name.clone(), node.clone() as DynNode)) - .collect() - } + async fn nodes(&self) -> HashMap { + self.nodes + .read() + .await + .iter() + .map(|(name, node)| (name.clone(), node.clone() as DynNode)) + .collect() + } - async fn get_node_available_args( - &self, - (command, image): (String, Option), - ) -> Result { - let node_image = image.expect(&format!("image should be present when getting node available args with kubernetes provider {THIS_IS_A_BUG}")); + async fn get_node_available_args( + &self, + (command, image): (String, Option), + ) -> Result { + let node_image = image.expect(&format!("image should be present when getting node available args with kubernetes provider {THIS_IS_A_BUG}")); - // run dummy command in new pod - let temp_node = self - .spawn_node( - &SpawnNodeOptions::new(format!("temp-{}", Uuid::new_v4()), "cat".to_string()) - .image(node_image.clone()), - ) - .await?; + // run dummy command in new pod + let temp_node = self + .spawn_node( + &SpawnNodeOptions::new(format!("temp-{}", Uuid::new_v4()), "cat".to_string()) + .image(node_image.clone()), + ) + .await?; - let available_args_output = temp_node - .run_command(RunCommandOptions::new(command.clone()).args(vec!["--help"])) - .await? - .map_err(|(_exit, status)| { - ProviderError::NodeAvailableArgsError(node_image, command, status) - })?; + let available_args_output = temp_node + .run_command(RunCommandOptions::new(command.clone()).args(vec!["--help"])) + .await? + .map_err(|(_exit, status)| { + ProviderError::NodeAvailableArgsError(node_image, command, status) + })?; - temp_node.destroy().await?; + temp_node.destroy().await?; - Ok(available_args_output) - } + Ok(available_args_output) + } - async fn spawn_node(&self, options: &SpawnNodeOptions) -> Result { - trace!("spawn node options {options:?}"); + async fn spawn_node(&self, options: &SpawnNodeOptions) -> Result { + trace!("spawn node options {options:?}"); - let node = KubernetesNode::new(KubernetesNodeOptions { - namespace: &self.weak, - namespace_base_dir: &self.base_dir, - name: &options.name, - image: options.image.as_ref(), - program: &options.program, - args: &options.args, - env: &options.env, - startup_files: &options.injected_files, - resources: options.resources.as_ref(), - db_snapshot: options.db_snapshot.as_ref(), - k8s_client: &self.k8s_client, - filesystem: &self.filesystem, - }) - .await?; + let node = KubernetesNode::new(KubernetesNodeOptions { + namespace: &self.weak, + namespace_base_dir: &self.base_dir, + name: &options.name, + image: options.image.as_ref(), + program: &options.program, + args: &options.args, + env: &options.env, + startup_files: &options.injected_files, + resources: options.resources.as_ref(), + db_snapshot: options.db_snapshot.as_ref(), + k8s_client: &self.k8s_client, + filesystem: &self.filesystem, + }) + .await?; - self.nodes - .write() - .await - .insert(node.name().to_string(), node.clone()); + self.nodes.write().await.insert(node.name().to_string(), node.clone()); - Ok(node) - } + Ok(node) + } - async fn spawn_node_from_json( - &self, - json_value: &serde_json::Value, - ) -> Result { - let deserializable: DeserializableKubernetesNodeOptions = - serde_json::from_value(json_value.clone())?; - let options = KubernetesNodeOptions::from_deserializable( - &deserializable, - &self.weak, - &self.base_dir, - &self.k8s_client, - &self.filesystem, - ); + async fn spawn_node_from_json( + &self, + json_value: &serde_json::Value, + ) -> Result { + let deserializable: DeserializableKubernetesNodeOptions = + serde_json::from_value(json_value.clone())?; + let options = KubernetesNodeOptions::from_deserializable( + &deserializable, + &self.weak, + &self.base_dir, + &self.k8s_client, + &self.filesystem, + ); - let node = KubernetesNode::attach_to_live(options).await?; + let node = KubernetesNode::attach_to_live(options).await?; - self.nodes - .write() - .await - .insert(node.name().to_string(), node.clone()); + self.nodes.write().await.insert(node.name().to_string(), node.clone()); - Ok(node) - } + Ok(node) + } - async fn generate_files(&self, options: GenerateFilesOptions) -> Result<(), ProviderError> { - debug!("generate files options {options:#?}"); + async fn generate_files(&self, options: GenerateFilesOptions) -> Result<(), ProviderError> { + debug!("generate files options {options:#?}"); - let node_name = options - .temp_name - .unwrap_or_else(|| format!("temp-{}", Uuid::new_v4())); - let node_image = options + let node_name = options.temp_name.unwrap_or_else(|| format!("temp-{}", Uuid::new_v4())); + let node_image = options .image .expect(&format!("image should be present when generating files with kubernetes provider {THIS_IS_A_BUG}")); - // run dummy command in new pod - let temp_node = self - .spawn_node( - &SpawnNodeOptions::new(node_name, "cat".to_string()) - .injected_files(options.injected_files) - .image(node_image), - ) - .await?; + // run dummy command in new pod + let temp_node = self + .spawn_node( + &SpawnNodeOptions::new(node_name, "cat".to_string()) + .injected_files(options.injected_files) + .image(node_image), + ) + .await?; - for GenerateFileCommand { - program, - args, - env, - local_output_path, - } in options.commands - { - let local_output_full_path = format!( - "{}{}{}", - self.base_dir.to_string_lossy(), - if local_output_path.starts_with("/") { - "" - } else { - "/" - }, - local_output_path.to_string_lossy() - ); + for GenerateFileCommand { program, args, env, local_output_path } in options.commands { + let local_output_full_path = format!( + "{}{}{}", + self.base_dir.to_string_lossy(), + if local_output_path.starts_with("/") { "" } else { "/" }, + local_output_path.to_string_lossy() + ); - let contents = extract_execution_result( - &temp_node, - RunCommandOptions { program, args, env }, - options.expected_path.as_ref(), - ) - .await?; - self.filesystem - .write(local_output_full_path, contents) - .await - .map_err(|err| ProviderError::FileGenerationFailed(err.into()))?; - } + let contents = extract_execution_result( + &temp_node, + RunCommandOptions { program, args, env }, + options.expected_path.as_ref(), + ) + .await?; + self.filesystem + .write(local_output_full_path, contents) + .await + .map_err(|err| ProviderError::FileGenerationFailed(err.into()))?; + } - temp_node.destroy().await - } + temp_node.destroy().await + } - async fn static_setup(&self) -> Result<(), ProviderError> { - todo!() - } + async fn static_setup(&self) -> Result<(), ProviderError> { + todo!() + } - async fn destroy(&self) -> Result<(), ProviderError> { - let _ = self - .k8s_client - .delete_namespace(&self.name) - .await - .map_err(|err| ProviderError::DeleteNamespaceFailed(self.name.clone(), err.into()))?; + async fn destroy(&self) -> Result<(), ProviderError> { + let _ = + self.k8s_client.delete_namespace(&self.name).await.map_err(|err| { + ProviderError::DeleteNamespaceFailed(self.name.clone(), err.into()) + })?; - if let Some(provider) = self.provider.upgrade() { - provider.namespaces.write().await.remove(&self.name); - } + if let Some(provider) = self.provider.upgrade() { + provider.namespaces.write().await.remove(&self.name); + } - Ok(()) - } + Ok(()) + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/node.rs b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/node.rs index 422a9a82..86b97e41 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/node.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/node.rs @@ -1,10 +1,10 @@ use std::{ - collections::{BTreeMap, HashMap}, - env, - net::IpAddr, - path::{Component, Path, PathBuf}, - sync::{Arc, Weak}, - time::Duration, + collections::{BTreeMap, HashMap}, + env, + net::IpAddr, + path::{Component, Path, PathBuf}, + sync::{Arc, Weak}, + time::Duration, }; use anyhow::anyhow; @@ -20,72 +20,72 @@ use tracing::{debug, trace, warn}; use url::Url; use super::{ - client::KubernetesClient, namespace::KubernetesNamespace, pod_spec_builder::PodSpecBuilder, + client::KubernetesClient, namespace::KubernetesNamespace, pod_spec_builder::PodSpecBuilder, }; use crate::{ - constants::{ - NODE_CONFIG_DIR, NODE_DATA_DIR, NODE_RELAY_DATA_DIR, NODE_SCRIPTS_DIR, P2P_PORT, - PROMETHEUS_PORT, RPC_HTTP_PORT, RPC_WS_PORT, - }, - kubernetes, - types::{ExecutionResult, RunCommandOptions, RunScriptOptions, TransferedFile}, - ProviderError, ProviderNamespace, ProviderNode, + constants::{ + NODE_CONFIG_DIR, NODE_DATA_DIR, NODE_RELAY_DATA_DIR, NODE_SCRIPTS_DIR, P2P_PORT, + PROMETHEUS_PORT, RPC_HTTP_PORT, RPC_WS_PORT, + }, + kubernetes, + types::{ExecutionResult, RunCommandOptions, RunScriptOptions, TransferedFile}, + ProviderError, ProviderNamespace, ProviderNode, }; pub(super) struct KubernetesNodeOptions<'a, FS> where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - pub(super) namespace: &'a Weak>, - pub(super) namespace_base_dir: &'a PathBuf, - pub(super) name: &'a str, - pub(super) image: Option<&'a String>, - pub(super) program: &'a str, - pub(super) args: &'a [String], - pub(super) env: &'a [(String, String)], - pub(super) startup_files: &'a [TransferedFile], - pub(super) resources: Option<&'a Resources>, - pub(super) db_snapshot: Option<&'a AssetLocation>, - pub(super) k8s_client: &'a KubernetesClient, - pub(super) filesystem: &'a FS, + pub(super) namespace: &'a Weak>, + pub(super) namespace_base_dir: &'a PathBuf, + pub(super) name: &'a str, + pub(super) image: Option<&'a String>, + pub(super) program: &'a str, + pub(super) args: &'a [String], + pub(super) env: &'a [(String, String)], + pub(super) startup_files: &'a [TransferedFile], + pub(super) resources: Option<&'a Resources>, + pub(super) db_snapshot: Option<&'a AssetLocation>, + pub(super) k8s_client: &'a KubernetesClient, + pub(super) filesystem: &'a FS, } impl<'a, FS> KubernetesNodeOptions<'a, FS> where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - pub(super) fn from_deserializable( - deserializable: &'a DeserializableKubernetesNodeOptions, - namespace: &'a Weak>, - namespace_base_dir: &'a PathBuf, - k8s_client: &'a KubernetesClient, - filesystem: &'a FS, - ) -> KubernetesNodeOptions<'a, FS> { - KubernetesNodeOptions { - namespace, - namespace_base_dir, - name: &deserializable.name, - image: deserializable.image.as_ref(), - program: &deserializable.program, - args: &deserializable.args, - env: &deserializable.env, - startup_files: &[], - resources: deserializable.resources.as_ref(), - db_snapshot: None, - k8s_client, - filesystem, - } - } + pub(super) fn from_deserializable( + deserializable: &'a DeserializableKubernetesNodeOptions, + namespace: &'a Weak>, + namespace_base_dir: &'a PathBuf, + k8s_client: &'a KubernetesClient, + filesystem: &'a FS, + ) -> KubernetesNodeOptions<'a, FS> { + KubernetesNodeOptions { + namespace, + namespace_base_dir, + name: &deserializable.name, + image: deserializable.image.as_ref(), + program: &deserializable.program, + args: &deserializable.args, + env: &deserializable.env, + startup_files: &[], + resources: deserializable.resources.as_ref(), + db_snapshot: None, + k8s_client, + filesystem, + } + } } #[derive(Deserialize)] pub(super) struct DeserializableKubernetesNodeOptions { - pub(super) name: String, - pub(super) image: Option, - pub(super) program: String, - pub(super) args: Vec, - pub(super) env: Vec<(String, String)>, - pub(super) resources: Option, + pub(super) name: String, + pub(super) image: Option, + pub(super) program: String, + pub(super) args: Vec, + pub(super) env: Vec<(String, String)>, + pub(super) resources: Option, } type FwdInfo = (u16, JoinHandle<()>); @@ -93,794 +93,759 @@ type FwdInfo = (u16, JoinHandle<()>); #[derive(Serialize)] pub(super) struct KubernetesNode where - FS: FileSystem + Send + Sync + Clone, + FS: FileSystem + Send + Sync + Clone, { - #[serde(skip)] - namespace: Weak>, - name: String, - image: String, - program: String, - args: Vec, - env: Vec<(String, String)>, - resources: Option, - base_dir: PathBuf, - config_dir: PathBuf, - data_dir: PathBuf, - relay_data_dir: PathBuf, - scripts_dir: PathBuf, - log_path: PathBuf, - #[serde(skip)] - k8s_client: KubernetesClient, - #[serde(skip)] - http_client: reqwest::Client, - #[serde(skip)] - filesystem: FS, - #[serde(skip)] - port_fwds: RwLock>, - provider_tag: String, + #[serde(skip)] + namespace: Weak>, + name: String, + image: String, + program: String, + args: Vec, + env: Vec<(String, String)>, + resources: Option, + base_dir: PathBuf, + config_dir: PathBuf, + data_dir: PathBuf, + relay_data_dir: PathBuf, + scripts_dir: PathBuf, + log_path: PathBuf, + #[serde(skip)] + k8s_client: KubernetesClient, + #[serde(skip)] + http_client: reqwest::Client, + #[serde(skip)] + filesystem: FS, + #[serde(skip)] + port_fwds: RwLock>, + provider_tag: String, } impl KubernetesNode where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - pub(super) async fn new( - options: KubernetesNodeOptions<'_, FS>, - ) -> Result, ProviderError> { - let image = options.image.ok_or_else(|| { - ProviderError::MissingNodeInfo(options.name.to_string(), "missing image".to_string()) - })?; + pub(super) async fn new( + options: KubernetesNodeOptions<'_, FS>, + ) -> Result, ProviderError> { + let image = options.image.ok_or_else(|| { + ProviderError::MissingNodeInfo(options.name.to_string(), "missing image".to_string()) + })?; - let filesystem = options.filesystem.clone(); + let filesystem = options.filesystem.clone(); - let base_dir = - PathBuf::from_iter([options.namespace_base_dir, &PathBuf::from(options.name)]); - filesystem.create_dir_all(&base_dir).await?; + let base_dir = + PathBuf::from_iter([options.namespace_base_dir, &PathBuf::from(options.name)]); + filesystem.create_dir_all(&base_dir).await?; - let base_dir_raw = base_dir.to_string_lossy(); - let config_dir = PathBuf::from(format!("{base_dir_raw}{NODE_CONFIG_DIR}")); - let data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_DATA_DIR}")); - let relay_data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_RELAY_DATA_DIR}")); - let scripts_dir = PathBuf::from(format!("{base_dir_raw}{NODE_SCRIPTS_DIR}")); - let log_path = base_dir.join("node.log"); + let base_dir_raw = base_dir.to_string_lossy(); + let config_dir = PathBuf::from(format!("{base_dir_raw}{NODE_CONFIG_DIR}")); + let data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_DATA_DIR}")); + let relay_data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_RELAY_DATA_DIR}")); + let scripts_dir = PathBuf::from(format!("{base_dir_raw}{NODE_SCRIPTS_DIR}")); + let log_path = base_dir.join("node.log"); - try_join!( - filesystem.create_dir(&config_dir), - filesystem.create_dir(&data_dir), - filesystem.create_dir(&relay_data_dir), - filesystem.create_dir(&scripts_dir), - )?; + try_join!( + filesystem.create_dir(&config_dir), + filesystem.create_dir(&data_dir), + filesystem.create_dir(&relay_data_dir), + filesystem.create_dir(&scripts_dir), + )?; - let node = Arc::new(KubernetesNode { - namespace: options.namespace.clone(), - name: options.name.to_string(), - image: image.to_string(), - program: options.program.to_string(), - args: options.args.to_vec(), - env: options.env.to_vec(), - resources: options.resources.cloned(), - base_dir, - config_dir, - data_dir, - relay_data_dir, - scripts_dir, - log_path, - filesystem: filesystem.clone(), - k8s_client: options.k8s_client.clone(), - http_client: reqwest::Client::new(), - port_fwds: Default::default(), - provider_tag: kubernetes::provider::PROVIDER_NAME.to_string(), - }); + let node = Arc::new(KubernetesNode { + namespace: options.namespace.clone(), + name: options.name.to_string(), + image: image.to_string(), + program: options.program.to_string(), + args: options.args.to_vec(), + env: options.env.to_vec(), + resources: options.resources.cloned(), + base_dir, + config_dir, + data_dir, + relay_data_dir, + scripts_dir, + log_path, + filesystem: filesystem.clone(), + k8s_client: options.k8s_client.clone(), + http_client: reqwest::Client::new(), + port_fwds: Default::default(), + provider_tag: kubernetes::provider::PROVIDER_NAME.to_string(), + }); - node.initialize_k8s().await?; + node.initialize_k8s().await?; - if let Some(db_snap) = options.db_snapshot { - node.initialize_db_snapshot(db_snap).await?; - } + if let Some(db_snap) = options.db_snapshot { + node.initialize_db_snapshot(db_snap).await?; + } - node.initialize_startup_files(options.startup_files).await?; + node.initialize_startup_files(options.startup_files).await?; - node.start().await?; + node.start().await?; - Ok(node) - } + Ok(node) + } - pub(super) async fn attach_to_live( - options: KubernetesNodeOptions<'_, FS>, - ) -> Result, ProviderError> { - let image = options.image.ok_or_else(|| { - ProviderError::MissingNodeInfo(options.name.to_string(), "missing image".to_string()) - })?; + pub(super) async fn attach_to_live( + options: KubernetesNodeOptions<'_, FS>, + ) -> Result, ProviderError> { + let image = options.image.ok_or_else(|| { + ProviderError::MissingNodeInfo(options.name.to_string(), "missing image".to_string()) + })?; - let filesystem = options.filesystem.clone(); + let filesystem = options.filesystem.clone(); - let base_dir = - PathBuf::from_iter([options.namespace_base_dir, &PathBuf::from(options.name)]); - filesystem.create_dir_all(&base_dir).await?; + let base_dir = + PathBuf::from_iter([options.namespace_base_dir, &PathBuf::from(options.name)]); + filesystem.create_dir_all(&base_dir).await?; - let base_dir_raw = base_dir.to_string_lossy(); - let config_dir = PathBuf::from(format!("{base_dir_raw}{NODE_CONFIG_DIR}")); - let data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_DATA_DIR}")); - let relay_data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_RELAY_DATA_DIR}")); - let scripts_dir = PathBuf::from(format!("{base_dir_raw}{NODE_SCRIPTS_DIR}")); - let log_path = base_dir.join("node.log"); + let base_dir_raw = base_dir.to_string_lossy(); + let config_dir = PathBuf::from(format!("{base_dir_raw}{NODE_CONFIG_DIR}")); + let data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_DATA_DIR}")); + let relay_data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_RELAY_DATA_DIR}")); + let scripts_dir = PathBuf::from(format!("{base_dir_raw}{NODE_SCRIPTS_DIR}")); + let log_path = base_dir.join("node.log"); - let node = Arc::new(KubernetesNode { - namespace: options.namespace.clone(), - name: options.name.to_string(), - image: image.to_string(), - program: options.program.to_string(), - args: options.args.to_vec(), - env: options.env.to_vec(), - resources: options.resources.cloned(), - base_dir, - config_dir, - data_dir, - relay_data_dir, - scripts_dir, - log_path, - filesystem: filesystem.clone(), - k8s_client: options.k8s_client.clone(), - http_client: reqwest::Client::new(), - port_fwds: Default::default(), - provider_tag: kubernetes::provider::PROVIDER_NAME.to_string(), - }); + let node = Arc::new(KubernetesNode { + namespace: options.namespace.clone(), + name: options.name.to_string(), + image: image.to_string(), + program: options.program.to_string(), + args: options.args.to_vec(), + env: options.env.to_vec(), + resources: options.resources.cloned(), + base_dir, + config_dir, + data_dir, + relay_data_dir, + scripts_dir, + log_path, + filesystem: filesystem.clone(), + k8s_client: options.k8s_client.clone(), + http_client: reqwest::Client::new(), + port_fwds: Default::default(), + provider_tag: kubernetes::provider::PROVIDER_NAME.to_string(), + }); - Ok(node) - } + Ok(node) + } - async fn initialize_k8s(&self) -> Result<(), ProviderError> { - let labels = BTreeMap::from([ - ( - "app.kubernetes.io/name".to_string(), - self.name().to_string(), - ), - ( - "x-infra-instance".to_string(), - env::var("X_INFRA_INSTANCE").unwrap_or("ondemand".to_string()), - ), - ]); + async fn initialize_k8s(&self) -> Result<(), ProviderError> { + let labels = BTreeMap::from([ + ("app.kubernetes.io/name".to_string(), self.name().to_string()), + ( + "x-infra-instance".to_string(), + env::var("X_INFRA_INSTANCE").unwrap_or("ondemand".to_string()), + ), + ]); - // Create pod - let pod_spec = PodSpecBuilder::build( - &self.name, - &self.image, - self.resources.as_ref(), - &self.program, - &self.args, - &self.env, - ); + // Create pod + let pod_spec = PodSpecBuilder::build( + &self.name, + &self.image, + self.resources.as_ref(), + &self.program, + &self.args, + &self.env, + ); - let manifest = self - .k8s_client - .create_pod(&self.namespace_name(), &self.name, pod_spec, labels.clone()) - .await - .map_err(|err| ProviderError::NodeSpawningFailed(self.name.clone(), err.into()))?; + let manifest = self + .k8s_client + .create_pod(&self.namespace_name(), &self.name, pod_spec, labels.clone()) + .await + .map_err(|err| ProviderError::NodeSpawningFailed(self.name.clone(), err.into()))?; - let serialized_manifest = serde_yaml::to_string(&manifest) - .map_err(|err| ProviderError::NodeSpawningFailed(self.name.to_string(), err.into()))?; + let serialized_manifest = serde_yaml::to_string(&manifest) + .map_err(|err| ProviderError::NodeSpawningFailed(self.name.to_string(), err.into()))?; - let dest_path = PathBuf::from_iter([ - &self.base_dir, - &PathBuf::from(format!("{}_manifest.yaml", &self.name)), - ]); + let dest_path = PathBuf::from_iter([ + &self.base_dir, + &PathBuf::from(format!("{}_manifest.yaml", &self.name)), + ]); - self.filesystem - .write(dest_path, serialized_manifest) - .await - .map_err(|err| ProviderError::NodeSpawningFailed(self.name.to_string(), err.into()))?; + self.filesystem + .write(dest_path, serialized_manifest) + .await + .map_err(|err| ProviderError::NodeSpawningFailed(self.name.to_string(), err.into()))?; - // Create service for pod - let service_spec = ServiceSpec { - selector: Some(labels.clone()), - ports: Some(vec![ - ServicePort { - port: P2P_PORT.into(), - name: Some("p2p".into()), - ..Default::default() - }, - ServicePort { - port: RPC_WS_PORT.into(), - name: Some("rpc".into()), - ..Default::default() - }, - ServicePort { - port: RPC_HTTP_PORT.into(), - name: Some("rpc-http".into()), - ..Default::default() - }, - ServicePort { - port: PROMETHEUS_PORT.into(), - name: Some("prom".into()), - ..Default::default() - }, - ]), - ..Default::default() - }; + // Create service for pod + let service_spec = ServiceSpec { + selector: Some(labels.clone()), + ports: Some(vec![ + ServicePort { + port: P2P_PORT.into(), + name: Some("p2p".into()), + ..Default::default() + }, + ServicePort { + port: RPC_WS_PORT.into(), + name: Some("rpc".into()), + ..Default::default() + }, + ServicePort { + port: RPC_HTTP_PORT.into(), + name: Some("rpc-http".into()), + ..Default::default() + }, + ServicePort { + port: PROMETHEUS_PORT.into(), + name: Some("prom".into()), + ..Default::default() + }, + ]), + ..Default::default() + }; - let service_manifest = self - .k8s_client - .create_service(&self.namespace_name(), &self.name, service_spec, labels) - .await - .map_err(|err| ProviderError::FileServerSetupError(err.into()))?; + let service_manifest = self + .k8s_client + .create_service(&self.namespace_name(), &self.name, service_spec, labels) + .await + .map_err(|err| ProviderError::FileServerSetupError(err.into()))?; - let serialized_service_manifest = serde_yaml::to_string(&service_manifest) - .map_err(|err| ProviderError::FileServerSetupError(err.into()))?; + let serialized_service_manifest = serde_yaml::to_string(&service_manifest) + .map_err(|err| ProviderError::FileServerSetupError(err.into()))?; - let service_dest_path = PathBuf::from_iter([ - &self.base_dir, - &PathBuf::from(format!("{}_svc_manifest.yaml", &self.name)), - ]); + let service_dest_path = PathBuf::from_iter([ + &self.base_dir, + &PathBuf::from(format!("{}_svc_manifest.yaml", &self.name)), + ]); - self.filesystem - .write(service_dest_path, serialized_service_manifest) - .await?; + self.filesystem.write(service_dest_path, serialized_service_manifest).await?; - Ok(()) - } + Ok(()) + } - async fn initialize_db_snapshot( - &self, - db_snapshot: &AssetLocation, - ) -> Result<(), ProviderError> { - trace!("snap: {db_snapshot}"); - let url_of_snap = match db_snapshot { - AssetLocation::Url(location) => location.clone(), - AssetLocation::FilePath(filepath) => { - let (url, _) = self.upload_to_fileserver(filepath).await?; - url - }, - }; + async fn initialize_db_snapshot( + &self, + db_snapshot: &AssetLocation, + ) -> Result<(), ProviderError> { + trace!("snap: {db_snapshot}"); + let url_of_snap = match db_snapshot { + AssetLocation::Url(location) => location.clone(), + AssetLocation::FilePath(filepath) => { + let (url, _) = self.upload_to_fileserver(filepath).await?; + url + }, + }; - // we need to get the snapshot from a public access - // and extract to /data - let opts = RunCommandOptions::new("mkdir").args([ - "-p", - "/data/", - "&&", - "mkdir", - "-p", - "/relay-data/", - "&&", - // Use our version of curl - "/cfg/curl", - url_of_snap.as_ref(), - "--output", - "/data/db.tgz", - "&&", - "cd", - "/", - "&&", - "tar", - "--skip-old-files", - "-xzvf", - "/data/db.tgz", - ]); + // we need to get the snapshot from a public access + // and extract to /data + let opts = RunCommandOptions::new("mkdir").args([ + "-p", + "/data/", + "&&", + "mkdir", + "-p", + "/relay-data/", + "&&", + // Use our version of curl + "/cfg/curl", + url_of_snap.as_ref(), + "--output", + "/data/db.tgz", + "&&", + "cd", + "/", + "&&", + "tar", + "--skip-old-files", + "-xzvf", + "/data/db.tgz", + ]); - trace!("cmd opts: {:#?}", opts); - let _ = self.run_command(opts).await?; + trace!("cmd opts: {:#?}", opts); + let _ = self.run_command(opts).await?; - Ok(()) - } + Ok(()) + } - async fn initialize_startup_files( - &self, - startup_files: &[TransferedFile], - ) -> Result<(), ProviderError> { - try_join_all( - startup_files - .iter() - .map(|file| self.send_file(&file.local_path, &file.remote_path, &file.mode)), - ) - .await?; + async fn initialize_startup_files( + &self, + startup_files: &[TransferedFile], + ) -> Result<(), ProviderError> { + try_join_all( + startup_files + .iter() + .map(|file| self.send_file(&file.local_path, &file.remote_path, &file.mode)), + ) + .await?; - Ok(()) - } + Ok(()) + } - pub(super) async fn start(&self) -> Result<(), ProviderError> { - self.k8s_client - .pod_exec( - &self.namespace_name(), - &self.name, - vec!["sh", "-c", "echo start > /tmp/zombiepipe"], - ) - .await - .map_err(|err| { - ProviderError::NodeSpawningFailed( - format!("failed to start pod {} after spawning", self.name), - err.into(), - ) - })? - .map_err(|err| { - ProviderError::NodeSpawningFailed( - format!("failed to start pod {} after spawning", self.name,), - anyhow!("command failed in container: status {}: {}", err.0, err.1), - ) - })?; + pub(super) async fn start(&self) -> Result<(), ProviderError> { + self.k8s_client + .pod_exec( + &self.namespace_name(), + &self.name, + vec!["sh", "-c", "echo start > /tmp/zombiepipe"], + ) + .await + .map_err(|err| { + ProviderError::NodeSpawningFailed( + format!("failed to start pod {} after spawning", self.name), + err.into(), + ) + })? + .map_err(|err| { + ProviderError::NodeSpawningFailed( + format!("failed to start pod {} after spawning", self.name,), + anyhow!("command failed in container: status {}: {}", err.0, err.1), + ) + })?; - Ok(()) - } + Ok(()) + } - fn get_remote_parent_dir(&self, remote_file_path: &Path) -> Option { - if let Some(remote_parent_dir) = remote_file_path.parent() { - if matches!( - remote_parent_dir.components().rev().peekable().peek(), - Some(Component::Normal(_)) - ) { - return Some(remote_parent_dir.to_path_buf()); - } - } + fn get_remote_parent_dir(&self, remote_file_path: &Path) -> Option { + if let Some(remote_parent_dir) = remote_file_path.parent() { + if matches!( + remote_parent_dir.components().rev().peekable().peek(), + Some(Component::Normal(_)) + ) { + return Some(remote_parent_dir.to_path_buf()); + } + } - None - } + None + } - async fn create_remote_dir(&self, remote_dir: &Path) -> Result<(), ProviderError> { - let _ = self - .k8s_client - .pod_exec( - &self.namespace_name(), - &self.name, - vec!["mkdir", "-p", &remote_dir.to_string_lossy()], - ) - .await - .map_err(|err| { - ProviderError::NodeSpawningFailed( - format!( - "failed to create dir {} for pod {}", - remote_dir.to_string_lossy(), - &self.name - ), - err.into(), - ) - })?; + async fn create_remote_dir(&self, remote_dir: &Path) -> Result<(), ProviderError> { + let _ = self + .k8s_client + .pod_exec( + &self.namespace_name(), + &self.name, + vec!["mkdir", "-p", &remote_dir.to_string_lossy()], + ) + .await + .map_err(|err| { + ProviderError::NodeSpawningFailed( + format!( + "failed to create dir {} for pod {}", + remote_dir.to_string_lossy(), + &self.name + ), + err.into(), + ) + })?; - Ok(()) - } + Ok(()) + } - fn namespace_name(&self) -> String { - self.namespace - .upgrade() - .map(|namespace| namespace.name().to_string()) - .unwrap_or_else(|| panic!("namespace shouldn't be dropped, {THIS_IS_A_BUG}")) - } + fn namespace_name(&self) -> String { + self.namespace + .upgrade() + .map(|namespace| namespace.name().to_string()) + .unwrap_or_else(|| panic!("namespace shouldn't be dropped, {THIS_IS_A_BUG}")) + } - async fn upload_to_fileserver(&self, location: &Path) -> Result<(Url, String), ProviderError> { - let file_name = if let Some(name) = location.file_name() { - name.to_string_lossy() - } else { - "unnamed".into() - }; + async fn upload_to_fileserver(&self, location: &Path) -> Result<(Url, String), ProviderError> { + let file_name = if let Some(name) = location.file_name() { + name.to_string_lossy() + } else { + "unnamed".into() + }; - let data = self.filesystem.read(location).await?; - let content_hashed = hex::encode(sha2::Sha256::digest(&data)); - let req = self - .http_client - .head(format!( - "http://{}/{content_hashed}__{file_name}", - self.file_server_local_host().await? - )) - .build() - .map_err(|err| { - ProviderError::UploadFile(location.to_string_lossy().to_string(), err.into()) - })?; + let data = self.filesystem.read(location).await?; + let content_hashed = hex::encode(sha2::Sha256::digest(&data)); + let req = self + .http_client + .head(format!( + "http://{}/{content_hashed}__{file_name}", + self.file_server_local_host().await? + )) + .build() + .map_err(|err| { + ProviderError::UploadFile(location.to_string_lossy().to_string(), err.into()) + })?; - let url = req.url().clone(); - let res = self.http_client.execute(req).await.map_err(|err| { - ProviderError::UploadFile(location.to_string_lossy().to_string(), err.into()) - })?; + let url = req.url().clone(); + let res = self.http_client.execute(req).await.map_err(|err| { + ProviderError::UploadFile(location.to_string_lossy().to_string(), err.into()) + })?; - if res.status() != reqwest::StatusCode::OK { - // we need to upload the file - self.http_client - .post(url.as_ref()) - .body(data) - .send() - .await - .map_err(|err| { - ProviderError::UploadFile(location.to_string_lossy().to_string(), err.into()) - })?; - } + if res.status() != reqwest::StatusCode::OK { + // we need to upload the file + self.http_client.post(url.as_ref()).body(data).send().await.map_err(|err| { + ProviderError::UploadFile(location.to_string_lossy().to_string(), err.into()) + })?; + } - Ok((url, content_hashed)) - } + Ok((url, content_hashed)) + } - async fn file_server_local_host(&self) -> Result { - if let Some(namespace) = self.namespace.upgrade() { - if let Some(port) = *namespace.file_server_port.read().await { - return Ok(format!("localhost:{port}")); - } - } + async fn file_server_local_host(&self) -> Result { + if let Some(namespace) = self.namespace.upgrade() { + if let Some(port) = *namespace.file_server_port.read().await { + return Ok(format!("localhost:{port}")); + } + } - Err(ProviderError::FileServerSetupError(anyhow!( - "file server port not bound locally" - ))) - } + Err(ProviderError::FileServerSetupError(anyhow!("file server port not bound locally"))) + } - async fn download_file( - &self, - url: &str, - remote_file_path: &Path, - hash: Option<&str>, - ) -> Result<(), ProviderError> { - let r = self - .k8s_client - .pod_exec( - &self.namespace_name(), - &self.name, - vec![ - "/cfg/curl", - url, - "--output", - &remote_file_path.to_string_lossy(), - ], - ) - .await - .map_err(|err| { - ProviderError::DownloadFile( - remote_file_path.to_string_lossy().to_string(), - anyhow!(format!("node: {}, err: {}", self.name(), err)), - ) - })?; + async fn download_file( + &self, + url: &str, + remote_file_path: &Path, + hash: Option<&str>, + ) -> Result<(), ProviderError> { + let r = self + .k8s_client + .pod_exec( + &self.namespace_name(), + &self.name, + vec!["/cfg/curl", url, "--output", &remote_file_path.to_string_lossy()], + ) + .await + .map_err(|err| { + ProviderError::DownloadFile( + remote_file_path.to_string_lossy().to_string(), + anyhow!(format!("node: {}, err: {}", self.name(), err)), + ) + })?; - trace!("download url {} result: {:?}", url, r); + trace!("download url {} result: {:?}", url, r); - if r.is_err() { - return Err(ProviderError::DownloadFile( - remote_file_path.to_string_lossy().to_string(), - anyhow!(format!("node: {}, err downloading file", self.name())), - )); - } + if r.is_err() { + return Err(ProviderError::DownloadFile( + remote_file_path.to_string_lossy().to_string(), + anyhow!(format!("node: {}, err downloading file", self.name())), + )); + } - if let Some(hash) = hash { - // check if the hash of the file is correct - let res = self - .k8s_client - .pod_exec( - &self.namespace_name(), - &self.name, - vec![ - "/cfg/coreutils", - "sha256sum", - &remote_file_path.to_string_lossy(), - ], - ) - .await - .map_err(|err| { - ProviderError::DownloadFile( - remote_file_path.to_string_lossy().to_string(), - anyhow!(format!("node: {}, err: {}", self.name(), err)), - ) - })?; + if let Some(hash) = hash { + // check if the hash of the file is correct + let res = self + .k8s_client + .pod_exec( + &self.namespace_name(), + &self.name, + vec!["/cfg/coreutils", "sha256sum", &remote_file_path.to_string_lossy()], + ) + .await + .map_err(|err| { + ProviderError::DownloadFile( + remote_file_path.to_string_lossy().to_string(), + anyhow!(format!("node: {}, err: {}", self.name(), err)), + ) + })?; - if let Ok(output) = res { - if !output.contains(hash) { - return Err(ProviderError::DownloadFile( + if let Ok(output) = res { + if !output.contains(hash) { + return Err(ProviderError::DownloadFile( remote_file_path.to_string_lossy().to_string(), anyhow!(format!("node: {}, invalid sha256sum hash: {hash} for file, output was {output}", self.name())), )); - } - } else { - return Err(ProviderError::DownloadFile( - remote_file_path.to_string_lossy().to_string(), - anyhow!(format!( - "node: {}, err calculating sha256sum for file {:?}", - self.name(), - res - )), - )); - } - } + } + } else { + return Err(ProviderError::DownloadFile( + remote_file_path.to_string_lossy().to_string(), + anyhow!(format!( + "node: {}, err calculating sha256sum for file {:?}", + self.name(), + res + )), + )); + } + } - Ok(()) - } + Ok(()) + } } #[async_trait] impl ProviderNode for KubernetesNode where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - fn name(&self) -> &str { - &self.name - } + fn name(&self) -> &str { + &self.name + } - fn args(&self) -> Vec<&str> { - self.args.iter().map(|arg| arg.as_str()).collect() - } + fn args(&self) -> Vec<&str> { + self.args.iter().map(|arg| arg.as_str()).collect() + } - fn base_dir(&self) -> &PathBuf { - &self.base_dir - } + fn base_dir(&self) -> &PathBuf { + &self.base_dir + } - fn config_dir(&self) -> &PathBuf { - &self.config_dir - } + fn config_dir(&self) -> &PathBuf { + &self.config_dir + } - fn data_dir(&self) -> &PathBuf { - &self.data_dir - } + fn data_dir(&self) -> &PathBuf { + &self.data_dir + } - fn relay_data_dir(&self) -> &PathBuf { - &self.relay_data_dir - } + fn relay_data_dir(&self) -> &PathBuf { + &self.relay_data_dir + } - fn scripts_dir(&self) -> &PathBuf { - &self.scripts_dir - } + fn scripts_dir(&self) -> &PathBuf { + &self.scripts_dir + } - fn log_path(&self) -> &PathBuf { - &self.log_path - } + fn log_path(&self) -> &PathBuf { + &self.log_path + } - fn log_cmd(&self) -> String { - format!("kubectl -n {} logs {}", self.namespace_name(), self.name) - } + fn log_cmd(&self) -> String { + format!("kubectl -n {} logs {}", self.namespace_name(), self.name) + } - fn path_in_node(&self, file: &Path) -> PathBuf { - // here is just a noop op since we will receive the path - // for the file inside the pod - PathBuf::from(file) - } + fn path_in_node(&self, file: &Path) -> PathBuf { + // here is just a noop op since we will receive the path + // for the file inside the pod + PathBuf::from(file) + } - // TODO: handle log rotation as we do in v1 - async fn logs(&self) -> Result { - self.k8s_client - .pod_logs(&self.namespace_name(), &self.name) - .await - .map_err(|err| ProviderError::GetLogsFailed(self.name.to_string(), err.into())) - } + // TODO: handle log rotation as we do in v1 + async fn logs(&self) -> Result { + self.k8s_client + .pod_logs(&self.namespace_name(), &self.name) + .await + .map_err(|err| ProviderError::GetLogsFailed(self.name.to_string(), err.into())) + } - async fn dump_logs(&self, local_dest: PathBuf) -> Result<(), ProviderError> { - let logs = self.logs().await?; + async fn dump_logs(&self, local_dest: PathBuf) -> Result<(), ProviderError> { + let logs = self.logs().await?; - self.filesystem - .write(local_dest, logs) - .await - .map_err(|err| ProviderError::DumpLogsFailed(self.name.to_string(), err.into()))?; + self.filesystem + .write(local_dest, logs) + .await + .map_err(|err| ProviderError::DumpLogsFailed(self.name.to_string(), err.into()))?; - Ok(()) - } + Ok(()) + } - async fn create_port_forward( - &self, - local_port: u16, - remote_port: u16, - ) -> Result, ProviderError> { - // If the fwd exist just return the local port - if let Some(fwd_info) = self.port_fwds.read().await.get(&remote_port) { - return Ok(Some(fwd_info.0)); - }; + async fn create_port_forward( + &self, + local_port: u16, + remote_port: u16, + ) -> Result, ProviderError> { + // If the fwd exist just return the local port + if let Some(fwd_info) = self.port_fwds.read().await.get(&remote_port) { + return Ok(Some(fwd_info.0)); + }; - let (port, task) = self - .k8s_client - .create_pod_port_forward(&self.namespace_name(), &self.name, local_port, remote_port) - .await - .map_err(|err| ProviderError::PortForwardError(local_port, remote_port, err.into()))?; + let (port, task) = self + .k8s_client + .create_pod_port_forward(&self.namespace_name(), &self.name, local_port, remote_port) + .await + .map_err(|err| ProviderError::PortForwardError(local_port, remote_port, err.into()))?; - self.port_fwds - .write() - .await - .insert(remote_port, (port, task)); + self.port_fwds.write().await.insert(remote_port, (port, task)); - Ok(Some(port)) - } + Ok(Some(port)) + } - async fn run_command( - &self, - options: RunCommandOptions, - ) -> Result { - let mut command = vec![]; + async fn run_command( + &self, + options: RunCommandOptions, + ) -> Result { + let mut command = vec![]; - for (name, value) in options.env { - command.push(format!("export {name}={value};")); - } + for (name, value) in options.env { + command.push(format!("export {name}={value};")); + } - command.push(options.program); + command.push(options.program); - for arg in options.args { - command.push(arg); - } + for arg in options.args { + command.push(arg); + } - self.k8s_client - .pod_exec( - &self.namespace_name(), - &self.name, - vec!["sh", "-c", &command.join(" ")], - ) - .await - .map_err(|err| { - ProviderError::RunCommandError( - format!("sh -c {}", &command.join(" ")), - format!("in pod {}", self.name), - err.into(), - ) - }) - } + self.k8s_client + .pod_exec(&self.namespace_name(), &self.name, vec!["sh", "-c", &command.join(" ")]) + .await + .map_err(|err| { + ProviderError::RunCommandError( + format!("sh -c {}", &command.join(" ")), + format!("in pod {}", self.name), + err.into(), + ) + }) + } - async fn run_script( - &self, - options: RunScriptOptions, - ) -> Result { - let file_name = options - .local_script_path - .file_name() - .expect(&format!( - "file name should be present at this point {THIS_IS_A_BUG}" - )) - .to_string_lossy(); + async fn run_script( + &self, + options: RunScriptOptions, + ) -> Result { + let file_name = options + .local_script_path + .file_name() + .expect(&format!("file name should be present at this point {THIS_IS_A_BUG}")) + .to_string_lossy(); - self.run_command(RunCommandOptions { - program: format!("/tmp/{file_name}"), - args: options.args, - env: options.env, - }) - .await - .map_err(|err| ProviderError::RunScriptError(self.name.to_string(), err.into())) - } + self.run_command(RunCommandOptions { + program: format!("/tmp/{file_name}"), + args: options.args, + env: options.env, + }) + .await + .map_err(|err| ProviderError::RunScriptError(self.name.to_string(), err.into())) + } - async fn send_file( - &self, - local_file_path: &Path, - remote_file_path: &Path, - mode: &str, - ) -> Result<(), ProviderError> { - if let Some(remote_parent_dir) = self.get_remote_parent_dir(remote_file_path) { - self.create_remote_dir(&remote_parent_dir).await?; - } + async fn send_file( + &self, + local_file_path: &Path, + remote_file_path: &Path, + mode: &str, + ) -> Result<(), ProviderError> { + if let Some(remote_parent_dir) = self.get_remote_parent_dir(remote_file_path) { + self.create_remote_dir(&remote_parent_dir).await?; + } - debug!( - "Uploading file: {} IFF not present in the fileserver", - local_file_path.to_string_lossy() - ); + debug!( + "Uploading file: {} IFF not present in the fileserver", + local_file_path.to_string_lossy() + ); - // we need to override the url to use inside the pod - let (mut url, hash) = self.upload_to_fileserver(local_file_path).await?; - let _ = url.set_host(Some("fileserver")); - let _ = url.set_port(Some(80)); + // we need to override the url to use inside the pod + let (mut url, hash) = self.upload_to_fileserver(local_file_path).await?; + let _ = url.set_host(Some("fileserver")); + let _ = url.set_port(Some(80)); - // Sometimes downloading the file fails (the file is corrupted) - // Add at most 5 retries - let mut last_err = None; - for i in 0..5 { - if i > 0 { - warn!("retrying number {i} download file {:?}", remote_file_path); - tokio::time::sleep(Duration::from_secs(i)).await; - } + // Sometimes downloading the file fails (the file is corrupted) + // Add at most 5 retries + let mut last_err = None; + for i in 0..5 { + if i > 0 { + warn!("retrying number {i} download file {:?}", remote_file_path); + tokio::time::sleep(Duration::from_secs(i)).await; + } - let res = self - .download_file(url.as_ref(), remote_file_path, Some(&hash)) - .await; + let res = self.download_file(url.as_ref(), remote_file_path, Some(&hash)).await; - last_err = res.err(); + last_err = res.err(); - if last_err.is_none() { - // ready to continue - break; - } - } + if last_err.is_none() { + // ready to continue + break; + } + } - if let Some(last_err) = last_err { - return Err(last_err); - } + if let Some(last_err) = last_err { + return Err(last_err); + } - let _ = self - .k8s_client - .pod_exec( - &self.namespace_name(), - &self.name, - vec!["chmod", mode, &remote_file_path.to_string_lossy()], - ) - .await - .map_err(|err| { - ProviderError::SendFile( - self.name.clone(), - local_file_path.to_string_lossy().to_string(), - err.into(), - ) - })?; + let _ = self + .k8s_client + .pod_exec( + &self.namespace_name(), + &self.name, + vec!["chmod", mode, &remote_file_path.to_string_lossy()], + ) + .await + .map_err(|err| { + ProviderError::SendFile( + self.name.clone(), + local_file_path.to_string_lossy().to_string(), + err.into(), + ) + })?; - Ok(()) - } + Ok(()) + } - async fn receive_file( - &self, - _remote_src: &Path, - _local_dest: &Path, - ) -> Result<(), ProviderError> { - Ok(()) - } + async fn receive_file( + &self, + _remote_src: &Path, + _local_dest: &Path, + ) -> Result<(), ProviderError> { + Ok(()) + } - async fn ip(&self) -> Result { - let status = self - .k8s_client - .pod_status(&self.namespace_name(), &self.name) - .await - .map_err(|_| ProviderError::MissingNode(self.name.clone()))?; + async fn ip(&self) -> Result { + let status = self + .k8s_client + .pod_status(&self.namespace_name(), &self.name) + .await + .map_err(|_| ProviderError::MissingNode(self.name.clone()))?; - if let Some(ip) = status.pod_ip { - // Pod ip should be parseable - Ok(ip.parse::().map_err(|err| { - ProviderError::InvalidConfig(format!("Can not parse the pod ip: {ip}, err: {err}")) - })?) - } else { - Err(ProviderError::InvalidConfig(format!( - "Can not find ip of pod: {}", - self.name() - ))) - } - } + if let Some(ip) = status.pod_ip { + // Pod ip should be parseable + Ok(ip.parse::().map_err(|err| { + ProviderError::InvalidConfig(format!("Can not parse the pod ip: {ip}, err: {err}")) + })?) + } else { + Err(ProviderError::InvalidConfig(format!("Can not find ip of pod: {}", self.name()))) + } + } - async fn pause(&self) -> Result<(), ProviderError> { - self.k8s_client - .pod_exec( - &self.namespace_name(), - &self.name, - vec!["sh", "-c", "echo pause > /tmp/zombiepipe"], - ) - .await - .map_err(|err| ProviderError::PauseNodeFailed(self.name.to_string(), err.into()))? - .map_err(|err| { - ProviderError::PauseNodeFailed( - self.name.to_string(), - anyhow!("error when pausing node: status {}: {}", err.0, err.1), - ) - })?; + async fn pause(&self) -> Result<(), ProviderError> { + self.k8s_client + .pod_exec( + &self.namespace_name(), + &self.name, + vec!["sh", "-c", "echo pause > /tmp/zombiepipe"], + ) + .await + .map_err(|err| ProviderError::PauseNodeFailed(self.name.to_string(), err.into()))? + .map_err(|err| { + ProviderError::PauseNodeFailed( + self.name.to_string(), + anyhow!("error when pausing node: status {}: {}", err.0, err.1), + ) + })?; - Ok(()) - } + Ok(()) + } - async fn resume(&self) -> Result<(), ProviderError> { - self.k8s_client - .pod_exec( - &self.namespace_name(), - &self.name, - vec!["sh", "-c", "echo resume > /tmp/zombiepipe"], - ) - .await - .map_err(|err| ProviderError::ResumeNodeFailed(self.name.to_string(), err.into()))? - .map_err(|err| { - ProviderError::ResumeNodeFailed( - self.name.to_string(), - anyhow!("error when pausing node: status {}: {}", err.0, err.1), - ) - })?; + async fn resume(&self) -> Result<(), ProviderError> { + self.k8s_client + .pod_exec( + &self.namespace_name(), + &self.name, + vec!["sh", "-c", "echo resume > /tmp/zombiepipe"], + ) + .await + .map_err(|err| ProviderError::ResumeNodeFailed(self.name.to_string(), err.into()))? + .map_err(|err| { + ProviderError::ResumeNodeFailed( + self.name.to_string(), + anyhow!("error when pausing node: status {}: {}", err.0, err.1), + ) + })?; - Ok(()) - } + Ok(()) + } - async fn restart(&self, after: Option) -> Result<(), ProviderError> { - if let Some(duration) = after { - sleep(duration).await; - } + async fn restart(&self, after: Option) -> Result<(), ProviderError> { + if let Some(duration) = after { + sleep(duration).await; + } - self.k8s_client - .pod_exec( - &self.namespace_name(), - &self.name, - vec!["sh", "-c", "echo restart > /tmp/zombiepipe"], - ) - .await - .map_err(|err| ProviderError::RestartNodeFailed(self.name.to_string(), err.into()))? - .map_err(|err| { - ProviderError::RestartNodeFailed( - self.name.to_string(), - anyhow!("error when restarting node: status {}: {}", err.0, err.1), - ) - })?; + self.k8s_client + .pod_exec( + &self.namespace_name(), + &self.name, + vec!["sh", "-c", "echo restart > /tmp/zombiepipe"], + ) + .await + .map_err(|err| ProviderError::RestartNodeFailed(self.name.to_string(), err.into()))? + .map_err(|err| { + ProviderError::RestartNodeFailed( + self.name.to_string(), + anyhow!("error when restarting node: status {}: {}", err.0, err.1), + ) + })?; - Ok(()) - } + Ok(()) + } - async fn destroy(&self) -> Result<(), ProviderError> { - self.k8s_client - .delete_pod(&self.namespace_name(), &self.name) - .await - .map_err(|err| ProviderError::KillNodeFailed(self.name.to_string(), err.into()))?; + async fn destroy(&self) -> Result<(), ProviderError> { + self.k8s_client + .delete_pod(&self.namespace_name(), &self.name) + .await + .map_err(|err| ProviderError::KillNodeFailed(self.name.to_string(), err.into()))?; - if let Some(namespace) = self.namespace.upgrade() { - namespace.nodes.write().await.remove(&self.name); - } + if let Some(namespace) = self.namespace.upgrade() { + namespace.nodes.write().await.remove(&self.name); + } - Ok(()) - } + Ok(()) + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/pod_spec_builder.rs b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/pod_spec_builder.rs index 76a2da77..0c259888 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/pod_spec_builder.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/pod_spec_builder.rs @@ -2,76 +2,73 @@ use std::collections::BTreeMap; use configuration::shared::resources::{ResourceQuantity, Resources}; use k8s_openapi::{ - api::core::v1::{ - ConfigMapVolumeSource, Container, EnvVar, PodSpec, ResourceRequirements, Volume, - VolumeMount, - }, - apimachinery::pkg::api::resource::Quantity, + api::core::v1::{ + ConfigMapVolumeSource, Container, EnvVar, PodSpec, ResourceRequirements, Volume, + VolumeMount, + }, + apimachinery::pkg::api::resource::Quantity, }; pub(super) struct PodSpecBuilder; impl PodSpecBuilder { - pub(super) fn build( - name: &str, - image: &str, - resources: Option<&Resources>, - program: &str, - args: &[String], - env: &[(String, String)], - ) -> PodSpec { - PodSpec { - hostname: Some(name.to_string()), - init_containers: Some(vec![Self::build_helper_binaries_setup_container()]), - containers: vec![Self::build_main_container( - name, image, resources, program, args, env, - )], - volumes: Some(Self::build_volumes()), - ..Default::default() - } - } + pub(super) fn build( + name: &str, + image: &str, + resources: Option<&Resources>, + program: &str, + args: &[String], + env: &[(String, String)], + ) -> PodSpec { + PodSpec { + hostname: Some(name.to_string()), + init_containers: Some(vec![Self::build_helper_binaries_setup_container()]), + containers: vec![Self::build_main_container( + name, image, resources, program, args, env, + )], + volumes: Some(Self::build_volumes()), + ..Default::default() + } + } - fn build_main_container( - name: &str, - image: &str, - resources: Option<&Resources>, - program: &str, - args: &[String], - env: &[(String, String)], - ) -> Container { - Container { - name: name.to_string(), - image: Some(image.to_string()), - image_pull_policy: Some("Always".to_string()), - command: Some( - [ - vec!["/zombie-wrapper.sh".to_string(), program.to_string()], - args.to_vec(), - ] - .concat(), - ), - env: Some( - env.iter() - .map(|(name, value)| EnvVar { - name: name.clone(), - value: Some(value.clone()), - value_from: None, - }) - .collect(), - ), - volume_mounts: Some(Self::build_volume_mounts(vec![VolumeMount { - name: "zombie-wrapper-volume".to_string(), - mount_path: "/zombie-wrapper.sh".to_string(), - sub_path: Some("zombie-wrapper.sh".to_string()), - ..Default::default() - }])), - resources: Self::build_resources_requirements(resources), - ..Default::default() - } - } + fn build_main_container( + name: &str, + image: &str, + resources: Option<&Resources>, + program: &str, + args: &[String], + env: &[(String, String)], + ) -> Container { + Container { + name: name.to_string(), + image: Some(image.to_string()), + image_pull_policy: Some("Always".to_string()), + command: Some( + [vec!["/zombie-wrapper.sh".to_string(), program.to_string()], args.to_vec()] + .concat(), + ), + env: Some( + env.iter() + .map(|(name, value)| EnvVar { + name: name.clone(), + value: Some(value.clone()), + value_from: None, + }) + .collect(), + ), + volume_mounts: Some(Self::build_volume_mounts(vec![VolumeMount { + name: "zombie-wrapper-volume".to_string(), + mount_path: "/zombie-wrapper.sh".to_string(), + sub_path: Some("zombie-wrapper.sh".to_string()), + ..Default::default() + }])), + resources: Self::build_resources_requirements(resources), + ..Default::default() + } + } - fn build_helper_binaries_setup_container() -> Container { - Container { + fn build_helper_binaries_setup_container() -> Container { + Container { name: "helper-binaries-setup".to_string(), image: Some("europe-west3-docker.pkg.dev/parity-zombienet/zombienet-public-images/alpine:latest".to_string()), image_pull_policy: Some("IfNotPresent".to_string()), @@ -87,102 +84,93 @@ impl PodSpecBuilder { ]), ..Default::default() } - } + } - fn build_volumes() -> Vec { - vec![ - Volume { - name: "cfg".to_string(), - ..Default::default() - }, - Volume { - name: "data".to_string(), - ..Default::default() - }, - Volume { - name: "relay-data".to_string(), - ..Default::default() - }, - Volume { - name: "zombie-wrapper-volume".to_string(), - config_map: Some(ConfigMapVolumeSource { - name: Some("zombie-wrapper".to_string()), - default_mode: Some(0o755), - ..Default::default() - }), - ..Default::default() - }, - Volume { - name: "helper-binaries-downloader-volume".to_string(), - config_map: Some(ConfigMapVolumeSource { - name: Some("helper-binaries-downloader".to_string()), - default_mode: Some(0o755), - ..Default::default() - }), - ..Default::default() - }, - ] - } + fn build_volumes() -> Vec { + vec![ + Volume { name: "cfg".to_string(), ..Default::default() }, + Volume { name: "data".to_string(), ..Default::default() }, + Volume { name: "relay-data".to_string(), ..Default::default() }, + Volume { + name: "zombie-wrapper-volume".to_string(), + config_map: Some(ConfigMapVolumeSource { + name: Some("zombie-wrapper".to_string()), + default_mode: Some(0o755), + ..Default::default() + }), + ..Default::default() + }, + Volume { + name: "helper-binaries-downloader-volume".to_string(), + config_map: Some(ConfigMapVolumeSource { + name: Some("helper-binaries-downloader".to_string()), + default_mode: Some(0o755), + ..Default::default() + }), + ..Default::default() + }, + ] + } - fn build_volume_mounts(non_default_mounts: Vec) -> Vec { - [ - vec![ - VolumeMount { - name: "cfg".to_string(), - mount_path: "/cfg".to_string(), - read_only: Some(false), - ..Default::default() - }, - VolumeMount { - name: "data".to_string(), - mount_path: "/data".to_string(), - read_only: Some(false), - ..Default::default() - }, - VolumeMount { - name: "relay-data".to_string(), - mount_path: "/relay-data".to_string(), - read_only: Some(false), - ..Default::default() - }, - ], - non_default_mounts, - ] - .concat() - } + fn build_volume_mounts(non_default_mounts: Vec) -> Vec { + [ + vec![ + VolumeMount { + name: "cfg".to_string(), + mount_path: "/cfg".to_string(), + read_only: Some(false), + ..Default::default() + }, + VolumeMount { + name: "data".to_string(), + mount_path: "/data".to_string(), + read_only: Some(false), + ..Default::default() + }, + VolumeMount { + name: "relay-data".to_string(), + mount_path: "/relay-data".to_string(), + read_only: Some(false), + ..Default::default() + }, + ], + non_default_mounts, + ] + .concat() + } - fn build_resources_requirements(resources: Option<&Resources>) -> Option { - resources.map(|resources| ResourceRequirements { - limits: Self::build_resources_requirements_quantities( - resources.limit_cpu(), - resources.limit_memory(), - ), - requests: Self::build_resources_requirements_quantities( - resources.request_cpu(), - resources.request_memory(), - ), - ..Default::default() - }) - } + fn build_resources_requirements(resources: Option<&Resources>) -> Option { + resources.map(|resources| ResourceRequirements { + limits: Self::build_resources_requirements_quantities( + resources.limit_cpu(), + resources.limit_memory(), + ), + requests: Self::build_resources_requirements_quantities( + resources.request_cpu(), + resources.request_memory(), + ), + ..Default::default() + }) + } - fn build_resources_requirements_quantities( - cpu: Option<&ResourceQuantity>, - memory: Option<&ResourceQuantity>, - ) -> Option> { - let mut quantities = BTreeMap::new(); + fn build_resources_requirements_quantities( + cpu: Option<&ResourceQuantity>, + memory: Option<&ResourceQuantity>, + ) -> Option> { + let mut quantities = BTreeMap::new(); - if let Some(cpu) = cpu { - quantities.insert("cpu".to_string(), Quantity(cpu.as_str().to_string())); - } + if let Some(cpu) = cpu { + quantities.insert("cpu".to_string(), Quantity(cpu.as_str().to_string())); + } - if let Some(memory) = memory { - quantities.insert("memory".to_string(), Quantity(memory.as_str().to_string())); - } + if let Some(memory) = memory { + quantities.insert("memory".to_string(), Quantity(memory.as_str().to_string())); + } - if !quantities.is_empty() { - Some(quantities) - } else { - None - } - } + if !quantities.is_empty() { + Some(quantities) + } else { + None + } + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/provider.rs b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/provider.rs index 2199d160..d5c3151e 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/provider.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/kubernetes/provider.rs @@ -1,7 +1,7 @@ use std::{ - collections::HashMap, - path::{Path, PathBuf}, - sync::{Arc, Weak}, + collections::HashMap, + path::{Path, PathBuf}, + sync::{Arc, Weak}, }; use async_trait::async_trait; @@ -10,136 +10,127 @@ use tokio::sync::RwLock; use super::{client::KubernetesClient, namespace::KubernetesNamespace}; use crate::{ - shared::helpers::extract_namespace_info, types::ProviderCapabilities, DynNamespace, Provider, - ProviderError, ProviderNamespace, + shared::helpers::extract_namespace_info, types::ProviderCapabilities, DynNamespace, Provider, + ProviderError, ProviderNamespace, }; pub const PROVIDER_NAME: &str = "k8s"; pub struct KubernetesProvider where - FS: FileSystem + Send + Sync + Clone, + FS: FileSystem + Send + Sync + Clone, { - weak: Weak>, - capabilities: ProviderCapabilities, - tmp_dir: PathBuf, - k8s_client: KubernetesClient, - filesystem: FS, - pub(super) namespaces: RwLock>>>, + weak: Weak>, + capabilities: ProviderCapabilities, + tmp_dir: PathBuf, + k8s_client: KubernetesClient, + filesystem: FS, + pub(super) namespaces: RwLock>>>, } impl KubernetesProvider where - FS: FileSystem + Send + Sync + Clone, + FS: FileSystem + Send + Sync + Clone, { - pub async fn new(filesystem: FS) -> Arc { - let k8s_client = KubernetesClient::new().await.unwrap(); + pub async fn new(filesystem: FS) -> Arc { + let k8s_client = KubernetesClient::new().await.unwrap(); - Arc::new_cyclic(|weak| KubernetesProvider { - weak: weak.clone(), - capabilities: ProviderCapabilities { - requires_image: true, - has_resources: true, - prefix_with_full_path: false, - use_default_ports_in_cmd: true, - }, - tmp_dir: std::env::temp_dir(), - k8s_client, - filesystem, - namespaces: RwLock::new(HashMap::new()), - }) - } + Arc::new_cyclic(|weak| KubernetesProvider { + weak: weak.clone(), + capabilities: ProviderCapabilities { + requires_image: true, + has_resources: true, + prefix_with_full_path: false, + use_default_ports_in_cmd: true, + }, + tmp_dir: std::env::temp_dir(), + k8s_client, + filesystem, + namespaces: RwLock::new(HashMap::new()), + }) + } - pub fn tmp_dir(mut self, tmp_dir: impl Into) -> Self { - self.tmp_dir = tmp_dir.into(); - self - } + pub fn tmp_dir(mut self, tmp_dir: impl Into) -> Self { + self.tmp_dir = tmp_dir.into(); + self + } } #[async_trait] impl Provider for KubernetesProvider where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - fn name(&self) -> &str { - PROVIDER_NAME - } + fn name(&self) -> &str { + PROVIDER_NAME + } - fn capabilities(&self) -> &ProviderCapabilities { - &self.capabilities - } + fn capabilities(&self) -> &ProviderCapabilities { + &self.capabilities + } - async fn namespaces(&self) -> HashMap { - self.namespaces - .read() - .await - .iter() - .map(|(name, namespace)| (name.clone(), namespace.clone() as DynNamespace)) - .collect() - } + async fn namespaces(&self) -> HashMap { + self.namespaces + .read() + .await + .iter() + .map(|(name, namespace)| (name.clone(), namespace.clone() as DynNamespace)) + .collect() + } - async fn create_namespace(&self) -> Result { - let namespace = KubernetesNamespace::new( - &self.weak, - &self.tmp_dir, - &self.capabilities, - &self.k8s_client, - &self.filesystem, - None, - ) - .await?; + async fn create_namespace(&self) -> Result { + let namespace = KubernetesNamespace::new( + &self.weak, + &self.tmp_dir, + &self.capabilities, + &self.k8s_client, + &self.filesystem, + None, + ) + .await?; - self.namespaces - .write() - .await - .insert(namespace.name().to_string(), namespace.clone()); + self.namespaces.write().await.insert(namespace.name().to_string(), namespace.clone()); - Ok(namespace) - } + Ok(namespace) + } - async fn create_namespace_with_base_dir( - &self, - base_dir: &Path, - ) -> Result { - let namespace = KubernetesNamespace::new( - &self.weak, - &self.tmp_dir, - &self.capabilities, - &self.k8s_client, - &self.filesystem, - Some(base_dir), - ) - .await?; + async fn create_namespace_with_base_dir( + &self, + base_dir: &Path, + ) -> Result { + let namespace = KubernetesNamespace::new( + &self.weak, + &self.tmp_dir, + &self.capabilities, + &self.k8s_client, + &self.filesystem, + Some(base_dir), + ) + .await?; - self.namespaces - .write() - .await - .insert(namespace.name().to_string(), namespace.clone()); + self.namespaces.write().await.insert(namespace.name().to_string(), namespace.clone()); - Ok(namespace) - } + Ok(namespace) + } - async fn create_namespace_from_json( - &self, - json_value: &serde_json::Value, - ) -> Result { - let (base_dir, name) = extract_namespace_info(json_value)?; + async fn create_namespace_from_json( + &self, + json_value: &serde_json::Value, + ) -> Result { + let (base_dir, name) = extract_namespace_info(json_value)?; - let namespace = KubernetesNamespace::attach_to_live( - &self.weak, - &self.capabilities, - &self.k8s_client, - &self.filesystem, - &base_dir, - &name, - ) - .await?; + let namespace = KubernetesNamespace::attach_to_live( + &self.weak, + &self.capabilities, + &self.k8s_client, + &self.filesystem, + &base_dir, + &name, + ) + .await?; - self.namespaces - .write() - .await - .insert(namespace.name().to_string(), namespace.clone()); + self.namespaces.write().await.insert(namespace.name().to_string(), namespace.clone()); - Ok(namespace) - } + Ok(namespace) + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/lib.rs b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/lib.rs index 13166203..80792088 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/lib.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/lib.rs @@ -5,253 +5,253 @@ mod native; pub mod shared; use std::{ - collections::HashMap, - net::IpAddr, - path::{Path, PathBuf}, - sync::Arc, - time::Duration, + collections::HashMap, + net::IpAddr, + path::{Path, PathBuf}, + sync::Arc, + time::Duration, }; use async_trait::async_trait; use shared::{ - constants::LOCALHOST, - types::{ - ExecutionResult, GenerateFilesOptions, ProviderCapabilities, RunCommandOptions, - RunScriptOptions, SpawnNodeOptions, - }, + constants::LOCALHOST, + types::{ + ExecutionResult, GenerateFilesOptions, ProviderCapabilities, RunCommandOptions, + RunScriptOptions, SpawnNodeOptions, + }, }; use support::fs::FileSystemError; #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub enum ProviderError { - #[error("Failed to create client '{0}': {1}")] - CreateClientFailed(String, anyhow::Error), + #[error("Failed to create client '{0}': {1}")] + CreateClientFailed(String, anyhow::Error), - #[error("Failed to create namespace '{0}': {1}")] - CreateNamespaceFailed(String, anyhow::Error), + #[error("Failed to create namespace '{0}': {1}")] + CreateNamespaceFailed(String, anyhow::Error), - #[error("Failed to spawn node '{0}': {1}")] - NodeSpawningFailed(String, anyhow::Error), + #[error("Failed to spawn node '{0}': {1}")] + NodeSpawningFailed(String, anyhow::Error), - #[error("Error running command '{0}' {1}: {2}")] - RunCommandError(String, String, anyhow::Error), + #[error("Error running command '{0}' {1}: {2}")] + RunCommandError(String, String, anyhow::Error), - #[error("Error running script'{0}': {1}")] - RunScriptError(String, anyhow::Error), + #[error("Error running script'{0}': {1}")] + RunScriptError(String, anyhow::Error), - #[error("Invalid network configuration field {0}")] - InvalidConfig(String), + #[error("Invalid network configuration field {0}")] + InvalidConfig(String), - #[error("Failed to retrieve node available args using image {0} and command {1}: {2}")] - NodeAvailableArgsError(String, String, String), + #[error("Failed to retrieve node available args using image {0} and command {1}: {2}")] + NodeAvailableArgsError(String, String, String), - #[error("Can not recover node: {0}")] - MissingNode(String), + #[error("Can not recover node: {0}")] + MissingNode(String), - #[error("Can not recover node: {0} info, field: {1}")] - MissingNodeInfo(String, String), + #[error("Can not recover node: {0} info, field: {1}")] + MissingNodeInfo(String, String), - #[error("File generation failed: {0}")] - FileGenerationFailed(anyhow::Error), + #[error("File generation failed: {0}")] + FileGenerationFailed(anyhow::Error), - #[error(transparent)] - FileSystemError(#[from] FileSystemError), + #[error(transparent)] + FileSystemError(#[from] FileSystemError), - #[error("Invalid script path for {0}")] - InvalidScriptPath(anyhow::Error), + #[error("Invalid script path for {0}")] + InvalidScriptPath(anyhow::Error), - #[error("Script with path {0} not found")] - ScriptNotFound(PathBuf), + #[error("Script with path {0} not found")] + ScriptNotFound(PathBuf), - #[error("Failed to retrieve process ID for node '{0}'")] - ProcessIdRetrievalFailed(String), + #[error("Failed to retrieve process ID for node '{0}'")] + ProcessIdRetrievalFailed(String), - #[error("Failed to pause node '{0}': {1}")] - PauseNodeFailed(String, anyhow::Error), + #[error("Failed to pause node '{0}': {1}")] + PauseNodeFailed(String, anyhow::Error), - #[error("Failed to resume node '{0}': {1}")] - ResumeNodeFailed(String, anyhow::Error), + #[error("Failed to resume node '{0}': {1}")] + ResumeNodeFailed(String, anyhow::Error), - #[error("Failed to kill node '{0}': {1}")] - KillNodeFailed(String, anyhow::Error), + #[error("Failed to kill node '{0}': {1}")] + KillNodeFailed(String, anyhow::Error), - #[error("Failed to restart node '{0}': {1}")] - RestartNodeFailed(String, anyhow::Error), + #[error("Failed to restart node '{0}': {1}")] + RestartNodeFailed(String, anyhow::Error), - #[error("Failed to destroy node '{0}': {1}")] - DestroyNodeFailed(String, anyhow::Error), + #[error("Failed to destroy node '{0}': {1}")] + DestroyNodeFailed(String, anyhow::Error), - #[error("Failed to get logs for node '{0}': {1}")] - GetLogsFailed(String, anyhow::Error), + #[error("Failed to get logs for node '{0}': {1}")] + GetLogsFailed(String, anyhow::Error), - #[error("Failed to dump logs for node '{0}': {1}")] - DumpLogsFailed(String, anyhow::Error), + #[error("Failed to dump logs for node '{0}': {1}")] + DumpLogsFailed(String, anyhow::Error), - #[error("Failed to copy file from node '{0}': {1}")] - CopyFileFromNodeError(String, anyhow::Error), + #[error("Failed to copy file from node '{0}': {1}")] + CopyFileFromNodeError(String, anyhow::Error), - #[error("Failed to setup fileserver: {0}")] - FileServerSetupError(anyhow::Error), + #[error("Failed to setup fileserver: {0}")] + FileServerSetupError(anyhow::Error), - #[error("Error uploading file: '{0}': {1}")] - UploadFile(String, anyhow::Error), + #[error("Error uploading file: '{0}': {1}")] + UploadFile(String, anyhow::Error), - #[error("Error downloading file: '{0}': {1}")] - DownloadFile(String, anyhow::Error), + #[error("Error downloading file: '{0}': {1}")] + DownloadFile(String, anyhow::Error), - #[error("Error sending file '{0}' to {1}: {2}")] - SendFile(String, String, anyhow::Error), + #[error("Error sending file '{0}' to {1}: {2}")] + SendFile(String, String, anyhow::Error), - #[error("Error creating port-forward '{0}:{1}': {2}")] - PortForwardError(u16, u16, anyhow::Error), + #[error("Error creating port-forward '{0}:{1}': {2}")] + PortForwardError(u16, u16, anyhow::Error), - #[error("Failed to delete namespace '{0}': {1}")] - DeleteNamespaceFailed(String, anyhow::Error), + #[error("Failed to delete namespace '{0}': {1}")] + DeleteNamespaceFailed(String, anyhow::Error), - #[error("Serialization error")] - SerializationError(#[from] serde_json::Error), + #[error("Serialization error")] + SerializationError(#[from] serde_json::Error), - #[error("Failed to acquire lock: {0}")] - FailedToAcquireLock(String), + #[error("Failed to acquire lock: {0}")] + FailedToAcquireLock(String), } #[async_trait] pub trait Provider { - fn name(&self) -> &str; + fn name(&self) -> &str; - fn capabilities(&self) -> &ProviderCapabilities; + fn capabilities(&self) -> &ProviderCapabilities; - async fn namespaces(&self) -> HashMap; + async fn namespaces(&self) -> HashMap; - async fn create_namespace(&self) -> Result; + async fn create_namespace(&self) -> Result; - async fn create_namespace_with_base_dir( - &self, - base_dir: &Path, - ) -> Result; + async fn create_namespace_with_base_dir( + &self, + base_dir: &Path, + ) -> Result; - async fn create_namespace_from_json( - &self, - json_value: &serde_json::Value, - ) -> Result; + async fn create_namespace_from_json( + &self, + json_value: &serde_json::Value, + ) -> Result; } pub type DynProvider = Arc; #[async_trait] pub trait ProviderNamespace { - fn name(&self) -> &str; + fn name(&self) -> &str; - fn base_dir(&self) -> &PathBuf; + fn base_dir(&self) -> &PathBuf; - fn capabilities(&self) -> &ProviderCapabilities; + fn capabilities(&self) -> &ProviderCapabilities; - fn provider_name(&self) -> &str; + fn provider_name(&self) -> &str; - async fn detach(&self) { - // noop by default - warn!("Detach is not implemented for {}", self.name()); - } + async fn detach(&self) { + // noop by default + warn!("Detach is not implemented for {}", self.name()); + } - async fn is_detached(&self) -> bool { - // false by default - false - } + async fn is_detached(&self) -> bool { + // false by default + false + } - async fn nodes(&self) -> HashMap; + async fn nodes(&self) -> HashMap; - async fn get_node_available_args( - &self, - options: (String, Option), - ) -> Result; + async fn get_node_available_args( + &self, + options: (String, Option), + ) -> Result; - async fn spawn_node(&self, options: &SpawnNodeOptions) -> Result; + async fn spawn_node(&self, options: &SpawnNodeOptions) -> Result; - async fn spawn_node_from_json( - &self, - json_value: &serde_json::Value, - ) -> Result; + async fn spawn_node_from_json( + &self, + json_value: &serde_json::Value, + ) -> Result; - async fn generate_files(&self, options: GenerateFilesOptions) -> Result<(), ProviderError>; + async fn generate_files(&self, options: GenerateFilesOptions) -> Result<(), ProviderError>; - async fn destroy(&self) -> Result<(), ProviderError>; + async fn destroy(&self) -> Result<(), ProviderError>; - async fn static_setup(&self) -> Result<(), ProviderError>; + async fn static_setup(&self) -> Result<(), ProviderError>; } pub type DynNamespace = Arc; #[async_trait] pub trait ProviderNode: erased_serde::Serialize { - fn name(&self) -> &str; + fn name(&self) -> &str; - fn args(&self) -> Vec<&str>; + fn args(&self) -> Vec<&str>; - fn base_dir(&self) -> &PathBuf; + fn base_dir(&self) -> &PathBuf; - fn config_dir(&self) -> &PathBuf; + fn config_dir(&self) -> &PathBuf; - fn data_dir(&self) -> &PathBuf; + fn data_dir(&self) -> &PathBuf; - fn relay_data_dir(&self) -> &PathBuf; + fn relay_data_dir(&self) -> &PathBuf; - fn scripts_dir(&self) -> &PathBuf; + fn scripts_dir(&self) -> &PathBuf; - fn log_path(&self) -> &PathBuf; + fn log_path(&self) -> &PathBuf; - fn log_cmd(&self) -> String; + fn log_cmd(&self) -> String; - // Return the absolute path to the file in the `node` perspective - // TODO: purpose? - fn path_in_node(&self, file: &Path) -> PathBuf; + // Return the absolute path to the file in the `node` perspective + // TODO: purpose? + fn path_in_node(&self, file: &Path) -> PathBuf; - async fn logs(&self) -> Result; + async fn logs(&self) -> Result; - async fn dump_logs(&self, local_dest: PathBuf) -> Result<(), ProviderError>; + async fn dump_logs(&self, local_dest: PathBuf) -> Result<(), ProviderError>; - // By default return localhost, should be overrided for k8s - async fn ip(&self) -> Result { - Ok(LOCALHOST) - } + // By default return localhost, should be overrided for k8s + async fn ip(&self) -> Result { + Ok(LOCALHOST) + } - // Noop by default (native/docker provider) - async fn create_port_forward( - &self, - _local_port: u16, - _remote_port: u16, - ) -> Result, ProviderError> { - Ok(None) - } + // Noop by default (native/docker provider) + async fn create_port_forward( + &self, + _local_port: u16, + _remote_port: u16, + ) -> Result, ProviderError> { + Ok(None) + } - async fn run_command( - &self, - options: RunCommandOptions, - ) -> Result; + async fn run_command( + &self, + options: RunCommandOptions, + ) -> Result; - async fn run_script(&self, options: RunScriptOptions) - -> Result; + async fn run_script(&self, options: RunScriptOptions) + -> Result; - async fn send_file( - &self, - local_file_path: &Path, - remote_file_path: &Path, - mode: &str, - ) -> Result<(), ProviderError>; + async fn send_file( + &self, + local_file_path: &Path, + remote_file_path: &Path, + mode: &str, + ) -> Result<(), ProviderError>; - async fn receive_file( - &self, - remote_file_path: &Path, - local_file_path: &Path, - ) -> Result<(), ProviderError>; + async fn receive_file( + &self, + remote_file_path: &Path, + local_file_path: &Path, + ) -> Result<(), ProviderError>; - async fn pause(&self) -> Result<(), ProviderError>; + async fn pause(&self) -> Result<(), ProviderError>; - async fn resume(&self) -> Result<(), ProviderError>; + async fn resume(&self) -> Result<(), ProviderError>; - async fn restart(&self, after: Option) -> Result<(), ProviderError>; + async fn restart(&self, after: Option) -> Result<(), ProviderError>; - async fn destroy(&self) -> Result<(), ProviderError>; + async fn destroy(&self) -> Result<(), ProviderError>; } pub type DynNode = Arc; diff --git a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/native/namespace.rs b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/native/namespace.rs index 39936e5c..5045d034 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/native/namespace.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/native/namespace.rs @@ -1,7 +1,7 @@ use std::{ - collections::HashMap, - path::{Path, PathBuf}, - sync::{Arc, Weak}, + collections::HashMap, + path::{Path, PathBuf}, + sync::{Arc, Weak}, }; use async_trait::async_trait; @@ -12,363 +12,337 @@ use uuid::Uuid; use super::node::{NativeNode, NativeNodeOptions}; use crate::{ - constants::NAMESPACE_PREFIX, - native::{node::DeserializableNativeNodeOptions, provider}, - shared::helpers::extract_execution_result, - types::{ - GenerateFileCommand, GenerateFilesOptions, ProviderCapabilities, RunCommandOptions, - SpawnNodeOptions, - }, - DynNode, NativeProvider, ProviderError, ProviderNamespace, ProviderNode, + constants::NAMESPACE_PREFIX, + native::{node::DeserializableNativeNodeOptions, provider}, + shared::helpers::extract_execution_result, + types::{ + GenerateFileCommand, GenerateFilesOptions, ProviderCapabilities, RunCommandOptions, + SpawnNodeOptions, + }, + DynNode, NativeProvider, ProviderError, ProviderNamespace, ProviderNode, }; pub(super) struct NativeNamespace where - FS: FileSystem + Send + Sync + Clone, + FS: FileSystem + Send + Sync + Clone, { - weak: Weak>, - name: String, - provider: Weak>, - base_dir: PathBuf, - capabilities: ProviderCapabilities, - filesystem: FS, - pub(super) nodes: RwLock>>>, + weak: Weak>, + name: String, + provider: Weak>, + base_dir: PathBuf, + capabilities: ProviderCapabilities, + filesystem: FS, + pub(super) nodes: RwLock>>>, } impl NativeNamespace where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - pub(super) async fn new( - provider: &Weak>, - tmp_dir: &PathBuf, - capabilities: &ProviderCapabilities, - filesystem: &FS, - custom_base_dir: Option<&Path>, - ) -> Result, ProviderError> { - let name = format!("{}{}", NAMESPACE_PREFIX, Uuid::new_v4()); - let base_dir = if let Some(custom_base_dir) = custom_base_dir { - if !filesystem.exists(custom_base_dir).await { - filesystem.create_dir_all(custom_base_dir).await?; - } else { - warn!( - "⚠️ Using and existing directory {} as base dir", - custom_base_dir.to_string_lossy() - ); - } - PathBuf::from(custom_base_dir) - } else { - let base_dir = PathBuf::from_iter([tmp_dir, &PathBuf::from(&name)]); - filesystem.create_dir(&base_dir).await?; - base_dir - }; + pub(super) async fn new( + provider: &Weak>, + tmp_dir: &PathBuf, + capabilities: &ProviderCapabilities, + filesystem: &FS, + custom_base_dir: Option<&Path>, + ) -> Result, ProviderError> { + let name = format!("{}{}", NAMESPACE_PREFIX, Uuid::new_v4()); + let base_dir = if let Some(custom_base_dir) = custom_base_dir { + if !filesystem.exists(custom_base_dir).await { + filesystem.create_dir_all(custom_base_dir).await?; + } else { + warn!( + "⚠️ Using and existing directory {} as base dir", + custom_base_dir.to_string_lossy() + ); + } + PathBuf::from(custom_base_dir) + } else { + let base_dir = PathBuf::from_iter([tmp_dir, &PathBuf::from(&name)]); + filesystem.create_dir(&base_dir).await?; + base_dir + }; - Ok(Arc::new_cyclic(|weak| NativeNamespace { - weak: weak.clone(), - provider: provider.clone(), - name, - base_dir, - capabilities: capabilities.clone(), - filesystem: filesystem.clone(), - nodes: RwLock::new(HashMap::new()), - })) - } + Ok(Arc::new_cyclic(|weak| NativeNamespace { + weak: weak.clone(), + provider: provider.clone(), + name, + base_dir, + capabilities: capabilities.clone(), + filesystem: filesystem.clone(), + nodes: RwLock::new(HashMap::new()), + })) + } - pub(super) async fn attach_to_live( - provider: &Weak>, - capabilities: &ProviderCapabilities, - filesystem: &FS, - custom_base_dir: &Path, - name: &str, - ) -> Result, ProviderError> { - let base_dir = custom_base_dir.to_path_buf(); + pub(super) async fn attach_to_live( + provider: &Weak>, + capabilities: &ProviderCapabilities, + filesystem: &FS, + custom_base_dir: &Path, + name: &str, + ) -> Result, ProviderError> { + let base_dir = custom_base_dir.to_path_buf(); - Ok(Arc::new_cyclic(|weak| NativeNamespace { - weak: weak.clone(), - provider: provider.clone(), - name: name.to_string(), - base_dir, - capabilities: capabilities.clone(), - filesystem: filesystem.clone(), - nodes: RwLock::new(HashMap::new()), - })) - } + Ok(Arc::new_cyclic(|weak| NativeNamespace { + weak: weak.clone(), + provider: provider.clone(), + name: name.to_string(), + base_dir, + capabilities: capabilities.clone(), + filesystem: filesystem.clone(), + nodes: RwLock::new(HashMap::new()), + })) + } } #[async_trait] impl ProviderNamespace for NativeNamespace where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - fn name(&self) -> &str { - &self.name - } + fn name(&self) -> &str { + &self.name + } - fn base_dir(&self) -> &PathBuf { - &self.base_dir - } + fn base_dir(&self) -> &PathBuf { + &self.base_dir + } - fn capabilities(&self) -> &ProviderCapabilities { - &self.capabilities - } + fn capabilities(&self) -> &ProviderCapabilities { + &self.capabilities + } - fn provider_name(&self) -> &str { - provider::PROVIDER_NAME - } + fn provider_name(&self) -> &str { + provider::PROVIDER_NAME + } - async fn nodes(&self) -> HashMap { - self.nodes - .read() - .await - .iter() - .map(|(name, node)| (name.clone(), node.clone() as DynNode)) - .collect() - } + async fn nodes(&self) -> HashMap { + self.nodes + .read() + .await + .iter() + .map(|(name, node)| (name.clone(), node.clone() as DynNode)) + .collect() + } - async fn get_node_available_args( - &self, - (command, _image): (String, Option), - ) -> Result { - let temp_node = self - .spawn_node( - &SpawnNodeOptions::new(format!("temp-{}", Uuid::new_v4()), "bash".to_string()) - .args(vec!["-c", "while :; do sleep 1; done"]), - ) - .await?; + async fn get_node_available_args( + &self, + (command, _image): (String, Option), + ) -> Result { + let temp_node = self + .spawn_node( + &SpawnNodeOptions::new(format!("temp-{}", Uuid::new_v4()), "bash".to_string()) + .args(vec!["-c", "while :; do sleep 1; done"]), + ) + .await?; - let available_args_output = temp_node - .run_command(RunCommandOptions::new(command.clone()).args(vec!["--help"])) - .await? - .map_err(|(_exit, status)| { - ProviderError::NodeAvailableArgsError("".to_string(), command, status) - })?; + let available_args_output = temp_node + .run_command(RunCommandOptions::new(command.clone()).args(vec!["--help"])) + .await? + .map_err(|(_exit, status)| { + ProviderError::NodeAvailableArgsError("".to_string(), command, status) + })?; - temp_node.destroy().await?; + temp_node.destroy().await?; - Ok(available_args_output) - } + Ok(available_args_output) + } - async fn spawn_node(&self, options: &SpawnNodeOptions) -> Result { - trace!("spawn node options {options:?}"); + async fn spawn_node(&self, options: &SpawnNodeOptions) -> Result { + trace!("spawn node options {options:?}"); - let node = NativeNode::new(NativeNodeOptions { - namespace: &self.weak, - namespace_base_dir: &self.base_dir, - name: &options.name, - program: &options.program, - args: &options.args, - env: &options.env, - startup_files: &options.injected_files, - created_paths: &options.created_paths, - db_snapshot: options.db_snapshot.as_ref(), - filesystem: &self.filesystem, - node_log_path: options.node_log_path.as_ref(), - }) - .await?; + let node = NativeNode::new(NativeNodeOptions { + namespace: &self.weak, + namespace_base_dir: &self.base_dir, + name: &options.name, + program: &options.program, + args: &options.args, + env: &options.env, + startup_files: &options.injected_files, + created_paths: &options.created_paths, + db_snapshot: options.db_snapshot.as_ref(), + filesystem: &self.filesystem, + node_log_path: options.node_log_path.as_ref(), + }) + .await?; - self.nodes - .write() - .await - .insert(options.name.clone(), node.clone()); + self.nodes.write().await.insert(options.name.clone(), node.clone()); - Ok(node) - } + Ok(node) + } - async fn spawn_node_from_json( - &self, - json_value: &serde_json::Value, - ) -> Result { - let deserializable: DeserializableNativeNodeOptions = - serde_json::from_value(json_value.clone())?; - let options = NativeNodeOptions::from_deserializable( - &deserializable, - &self.weak, - &self.base_dir, - &self.filesystem, - ); + async fn spawn_node_from_json( + &self, + json_value: &serde_json::Value, + ) -> Result { + let deserializable: DeserializableNativeNodeOptions = + serde_json::from_value(json_value.clone())?; + let options = NativeNodeOptions::from_deserializable( + &deserializable, + &self.weak, + &self.base_dir, + &self.filesystem, + ); - let pid = json_value - .get("process_handle") - .and_then(|v| v.as_i64()) - .ok_or_else(|| ProviderError::InvalidConfig("Missing pid field".to_string()))? - as i32; - let node = NativeNode::attach_to_live(options, pid).await?; + let pid = json_value + .get("process_handle") + .and_then(|v| v.as_i64()) + .ok_or_else(|| ProviderError::InvalidConfig("Missing pid field".to_string()))? + as i32; + let node = NativeNode::attach_to_live(options, pid).await?; - self.nodes - .write() - .await - .insert(node.name().to_string(), node.clone()); + self.nodes.write().await.insert(node.name().to_string(), node.clone()); - Ok(node) - } + Ok(node) + } - async fn generate_files(&self, options: GenerateFilesOptions) -> Result<(), ProviderError> { - let node_name = if let Some(name) = options.temp_name { - name - } else { - format!("temp-{}", Uuid::new_v4()) - }; + async fn generate_files(&self, options: GenerateFilesOptions) -> Result<(), ProviderError> { + let node_name = if let Some(name) = options.temp_name { + name + } else { + format!("temp-{}", Uuid::new_v4()) + }; - // we spawn a node doing nothing but looping so we can execute our commands - let temp_node = self - .spawn_node( - &SpawnNodeOptions::new(node_name, "bash".to_string()) - .args(vec!["-c", "while :; do sleep 1; done"]) - .injected_files(options.injected_files), - ) - .await?; + // we spawn a node doing nothing but looping so we can execute our commands + let temp_node = self + .spawn_node( + &SpawnNodeOptions::new(node_name, "bash".to_string()) + .args(vec!["-c", "while :; do sleep 1; done"]) + .injected_files(options.injected_files), + ) + .await?; - for GenerateFileCommand { - program, - args, - env, - local_output_path, - } in options.commands - { - trace!( - "🏗 building file {:?} in path {} with command {} {}", - local_output_path.as_os_str(), - self.base_dir.to_string_lossy(), - program, - args.join(" ") - ); - let local_output_full_path = format!( - "{}{}{}", - self.base_dir.to_string_lossy(), - if local_output_path.starts_with("/") { - "" - } else { - "/" - }, - local_output_path.to_string_lossy() - ); + for GenerateFileCommand { program, args, env, local_output_path } in options.commands { + trace!( + "🏗 building file {:?} in path {} with command {} {}", + local_output_path.as_os_str(), + self.base_dir.to_string_lossy(), + program, + args.join(" ") + ); + let local_output_full_path = format!( + "{}{}{}", + self.base_dir.to_string_lossy(), + if local_output_path.starts_with("/") { "" } else { "/" }, + local_output_path.to_string_lossy() + ); - let contents = extract_execution_result( - &temp_node, - RunCommandOptions { program, args, env }, - options.expected_path.as_ref(), - ) - .await?; - self.filesystem - .write(local_output_full_path, contents) - .await - .map_err(|err| ProviderError::FileGenerationFailed(err.into()))?; - } + let contents = extract_execution_result( + &temp_node, + RunCommandOptions { program, args, env }, + options.expected_path.as_ref(), + ) + .await?; + self.filesystem + .write(local_output_full_path, contents) + .await + .map_err(|err| ProviderError::FileGenerationFailed(err.into()))?; + } - temp_node.destroy().await - } + temp_node.destroy().await + } - async fn static_setup(&self) -> Result<(), ProviderError> { - // no static setup exists for native provider - todo!() - } + async fn static_setup(&self) -> Result<(), ProviderError> { + // no static setup exists for native provider + todo!() + } - async fn destroy(&self) -> Result<(), ProviderError> { - let mut names = vec![]; + async fn destroy(&self) -> Result<(), ProviderError> { + let mut names = vec![]; - for node in self.nodes.read().await.values() { - node.abort() - .await - .map_err(|err| ProviderError::DestroyNodeFailed(node.name().to_string(), err))?; - names.push(node.name().to_string()); - } + for node in self.nodes.read().await.values() { + node.abort() + .await + .map_err(|err| ProviderError::DestroyNodeFailed(node.name().to_string(), err))?; + names.push(node.name().to_string()); + } - let mut nodes = self.nodes.write().await; - for name in names { - nodes.remove(&name); - } + let mut nodes = self.nodes.write().await; + for name in names { + nodes.remove(&name); + } - if let Some(provider) = self.provider.upgrade() { - provider.namespaces.write().await.remove(&self.name); - } + if let Some(provider) = self.provider.upgrade() { + provider.namespaces.write().await.remove(&self.name); + } - Ok(()) - } + Ok(()) + } } #[cfg(test)] mod tests { - use support::fs::local::LocalFileSystem; + use support::fs::local::LocalFileSystem; - use super::*; - use crate::{ - types::{GenerateFileCommand, GenerateFilesOptions}, - NativeProvider, Provider, - }; + use super::*; + use crate::{ + types::{GenerateFileCommand, GenerateFilesOptions}, + NativeProvider, Provider, + }; - fn unique_temp_dir() -> PathBuf { - let mut base = std::env::temp_dir(); - base.push(format!("znet_native_ns_test_{}", uuid::Uuid::new_v4())); - base - } + fn unique_temp_dir() -> PathBuf { + let mut base = std::env::temp_dir(); + base.push(format!("znet_native_ns_test_{}", uuid::Uuid::new_v4())); + base + } - #[tokio::test] - async fn generate_files_uses_expected_path_when_provided() { - let fs = LocalFileSystem; - let provider = NativeProvider::new(fs.clone()); - let base_dir = unique_temp_dir(); - // Namespace builder will create directory if needed - let ns = provider - .create_namespace_with_base_dir(&base_dir) - .await - .expect("namespace should be created"); + #[tokio::test] + async fn generate_files_uses_expected_path_when_provided() { + let fs = LocalFileSystem; + let provider = NativeProvider::new(fs.clone()); + let base_dir = unique_temp_dir(); + // Namespace builder will create directory if needed + let ns = provider + .create_namespace_with_base_dir(&base_dir) + .await + .expect("namespace should be created"); - // Create a unique on-host path that the native node will write to - let expected_path = - std::env::temp_dir().join(format!("znet_expected_{}.json", uuid::Uuid::new_v4())); + // Create a unique on-host path that the native node will write to + let expected_path = + std::env::temp_dir().join(format!("znet_expected_{}.json", uuid::Uuid::new_v4())); - // Command will write JSON into expected_path; stdout will be something else to ensure we don't read it - let program = "bash".to_string(); - let script = format!( - "echo -n '{{\"hello\":\"world\"}}' > {} && echo should_not_be_used", - expected_path.to_string_lossy() - ); - let args: Vec = vec!["-lc".into(), script]; + // Command will write JSON into expected_path; stdout will be something else to ensure we don't read it + let program = "bash".to_string(); + let script = format!( + "echo -n '{{\"hello\":\"world\"}}' > {} && echo should_not_be_used", + expected_path.to_string_lossy() + ); + let args: Vec = vec!["-lc".into(), script]; - let out_name = PathBuf::from("result_expected.json"); - let cmd = GenerateFileCommand::new(program, out_name.clone()).args(args); - let options = GenerateFilesOptions::new(vec![cmd], None, Some(expected_path.clone())); + let out_name = PathBuf::from("result_expected.json"); + let cmd = GenerateFileCommand::new(program, out_name.clone()).args(args); + let options = GenerateFilesOptions::new(vec![cmd], None, Some(expected_path.clone())); - ns.generate_files(options) - .await - .expect("generation should succeed"); + ns.generate_files(options).await.expect("generation should succeed"); - // Read produced file from namespace base_dir - let produced_path = base_dir.join(out_name); - let produced = fs - .read_to_string(&produced_path) - .await - .expect("should read produced file"); - assert_eq!(produced, "{\"hello\":\"world\"}"); - } + // Read produced file from namespace base_dir + let produced_path = base_dir.join(out_name); + let produced = fs.read_to_string(&produced_path).await.expect("should read produced file"); + assert_eq!(produced, "{\"hello\":\"world\"}"); + } - #[tokio::test] - async fn generate_files_uses_stdout_when_expected_path_absent() { - let fs = LocalFileSystem; - let provider = NativeProvider::new(fs.clone()); - let base_dir = unique_temp_dir(); - let ns = provider - .create_namespace_with_base_dir(&base_dir) - .await - .expect("namespace should be created"); + #[tokio::test] + async fn generate_files_uses_stdout_when_expected_path_absent() { + let fs = LocalFileSystem; + let provider = NativeProvider::new(fs.clone()); + let base_dir = unique_temp_dir(); + let ns = provider + .create_namespace_with_base_dir(&base_dir) + .await + .expect("namespace should be created"); - // Command prints to stdout only - let program = "bash".to_string(); - let args: Vec = vec!["-lc".into(), "echo -n 42".into()]; + // Command prints to stdout only + let program = "bash".to_string(); + let args: Vec = vec!["-lc".into(), "echo -n 42".into()]; - let out_name = PathBuf::from("result_stdout.txt"); - let cmd = GenerateFileCommand::new(program, out_name.clone()).args(args); - let options = GenerateFilesOptions::new(vec![cmd], None, None); + let out_name = PathBuf::from("result_stdout.txt"); + let cmd = GenerateFileCommand::new(program, out_name.clone()).args(args); + let options = GenerateFilesOptions::new(vec![cmd], None, None); - ns.generate_files(options) - .await - .expect("generation should succeed"); + ns.generate_files(options).await.expect("generation should succeed"); - let produced_path = base_dir.join(out_name); - let produced = fs - .read_to_string(&produced_path) - .await - .expect("should read produced file"); - assert_eq!(produced, "42"); - } + let produced_path = base_dir.join(out_name); + let produced = fs.read_to_string(&produced_path).await.expect("should read produced file"); + assert_eq!(produced, "42"); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/native/node.rs b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/native/node.rs index fd99e098..2674a7e2 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/native/node.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/native/node.rs @@ -1,10 +1,10 @@ use std::{ - collections::HashMap, - env, - path::{Path, PathBuf}, - process::Stdio, - sync::{Arc, Weak}, - time::Duration, + collections::HashMap, + env, + path::{Path, PathBuf}, + process::Stdio, + sync::{Arc, Weak}, + time::Duration, }; use anyhow::anyhow; @@ -13,335 +13,332 @@ use configuration::types::AssetLocation; use flate2::read::GzDecoder; use futures::future::try_join_all; use nix::{ - sys::signal::{kill, Signal}, - unistd::Pid, + sys::signal::{kill, Signal}, + unistd::Pid, }; use serde::{ser::Error, Deserialize, Serialize, Serializer}; use sha2::Digest; use support::{constants::THIS_IS_A_BUG, fs::FileSystem}; use tar::Archive; use tokio::{ - fs, - io::{AsyncRead, AsyncReadExt, BufReader}, - process::{Child, ChildStderr, ChildStdout, Command}, - sync::{ - mpsc::{self, Sender}, - RwLock, - }, - task::JoinHandle, - time::sleep, - try_join, + fs, + io::{AsyncRead, AsyncReadExt, BufReader}, + process::{Child, ChildStderr, ChildStdout, Command}, + sync::{ + mpsc::{self, Sender}, + RwLock, + }, + task::JoinHandle, + time::sleep, + try_join, }; use tracing::trace; use super::namespace::NativeNamespace; use crate::{ - constants::{NODE_CONFIG_DIR, NODE_DATA_DIR, NODE_RELAY_DATA_DIR, NODE_SCRIPTS_DIR}, - native, - types::{ExecutionResult, RunCommandOptions, RunScriptOptions, TransferedFile}, - ProviderError, ProviderNamespace, ProviderNode, + constants::{NODE_CONFIG_DIR, NODE_DATA_DIR, NODE_RELAY_DATA_DIR, NODE_SCRIPTS_DIR}, + native, + types::{ExecutionResult, RunCommandOptions, RunScriptOptions, TransferedFile}, + ProviderError, ProviderNamespace, ProviderNode, }; pub(super) struct NativeNodeOptions<'a, FS> where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - pub(super) namespace: &'a Weak>, - pub(super) namespace_base_dir: &'a PathBuf, - pub(super) name: &'a str, - pub(super) program: &'a str, - pub(super) args: &'a [String], - pub(super) env: &'a [(String, String)], - pub(super) startup_files: &'a [TransferedFile], - pub(super) created_paths: &'a [PathBuf], - pub(super) db_snapshot: Option<&'a AssetLocation>, - pub(super) filesystem: &'a FS, - pub(super) node_log_path: Option<&'a PathBuf>, + pub(super) namespace: &'a Weak>, + pub(super) namespace_base_dir: &'a PathBuf, + pub(super) name: &'a str, + pub(super) program: &'a str, + pub(super) args: &'a [String], + pub(super) env: &'a [(String, String)], + pub(super) startup_files: &'a [TransferedFile], + pub(super) created_paths: &'a [PathBuf], + pub(super) db_snapshot: Option<&'a AssetLocation>, + pub(super) filesystem: &'a FS, + pub(super) node_log_path: Option<&'a PathBuf>, } impl<'a, FS> NativeNodeOptions<'a, FS> where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - pub(super) fn from_deserializable( - deserializable: &'a DeserializableNativeNodeOptions, - namespace: &'a Weak>, - namespace_base_dir: &'a PathBuf, - filesystem: &'a FS, - ) -> NativeNodeOptions<'a, FS> { - NativeNodeOptions { - namespace, - namespace_base_dir, - name: &deserializable.name, - program: &deserializable.program, - args: &deserializable.args, - env: &deserializable.env, - startup_files: &[], - created_paths: &[], - db_snapshot: None, - filesystem, - node_log_path: deserializable.node_log_path.as_ref(), - } - } + pub(super) fn from_deserializable( + deserializable: &'a DeserializableNativeNodeOptions, + namespace: &'a Weak>, + namespace_base_dir: &'a PathBuf, + filesystem: &'a FS, + ) -> NativeNodeOptions<'a, FS> { + NativeNodeOptions { + namespace, + namespace_base_dir, + name: &deserializable.name, + program: &deserializable.program, + args: &deserializable.args, + env: &deserializable.env, + startup_files: &[], + created_paths: &[], + db_snapshot: None, + filesystem, + node_log_path: deserializable.node_log_path.as_ref(), + } + } } #[derive(Deserialize)] pub(super) struct DeserializableNativeNodeOptions { - pub name: String, - pub program: String, - pub args: Vec, - pub env: Vec<(String, String)>, - pub node_log_path: Option, + pub name: String, + pub program: String, + pub args: Vec, + pub env: Vec<(String, String)>, + pub node_log_path: Option, } enum ProcessHandle { - Spawned(Child, Pid), - Attached(Pid), + Spawned(Child, Pid), + Attached(Pid), } #[derive(Serialize)] pub(super) struct NativeNode where - FS: FileSystem + Send + Sync + Clone, + FS: FileSystem + Send + Sync + Clone, { - #[serde(skip)] - namespace: Weak>, - name: String, - program: String, - args: Vec, - env: Vec<(String, String)>, - base_dir: PathBuf, - config_dir: PathBuf, - data_dir: PathBuf, - relay_data_dir: PathBuf, - scripts_dir: PathBuf, - log_path: PathBuf, - #[serde(serialize_with = "serialize_process_handle")] - // using RwLock from std to serialize properly, generally using sync locks is ok in async code as long as they - // are not held across await points - process_handle: std::sync::RwLock>, - #[serde(skip)] - stdout_reading_task: RwLock>>, - #[serde(skip)] - stderr_reading_task: RwLock>>, - #[serde(skip)] - log_writing_task: RwLock>>, - #[serde(skip)] - filesystem: FS, - provider_tag: String, + #[serde(skip)] + namespace: Weak>, + name: String, + program: String, + args: Vec, + env: Vec<(String, String)>, + base_dir: PathBuf, + config_dir: PathBuf, + data_dir: PathBuf, + relay_data_dir: PathBuf, + scripts_dir: PathBuf, + log_path: PathBuf, + #[serde(serialize_with = "serialize_process_handle")] + // using RwLock from std to serialize properly, generally using sync locks is ok in async code as long as they + // are not held across await points + process_handle: std::sync::RwLock>, + #[serde(skip)] + stdout_reading_task: RwLock>>, + #[serde(skip)] + stderr_reading_task: RwLock>>, + #[serde(skip)] + log_writing_task: RwLock>>, + #[serde(skip)] + filesystem: FS, + provider_tag: String, } impl NativeNode where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - pub(super) async fn new( - options: NativeNodeOptions<'_, FS>, - ) -> Result, ProviderError> { - let filesystem = options.filesystem.clone(); + pub(super) async fn new( + options: NativeNodeOptions<'_, FS>, + ) -> Result, ProviderError> { + let filesystem = options.filesystem.clone(); - let base_dir = - PathBuf::from_iter([options.namespace_base_dir, &PathBuf::from(options.name)]); - trace!("creating base_dir {:?}", base_dir); - options.filesystem.create_dir_all(&base_dir).await?; - trace!("created base_dir {:?}", base_dir); + let base_dir = + PathBuf::from_iter([options.namespace_base_dir, &PathBuf::from(options.name)]); + trace!("creating base_dir {:?}", base_dir); + options.filesystem.create_dir_all(&base_dir).await?; + trace!("created base_dir {:?}", base_dir); - let base_dir_raw = base_dir.to_string_lossy(); - let config_dir = PathBuf::from(format!("{base_dir_raw}{NODE_CONFIG_DIR}")); - let data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_DATA_DIR}")); - let relay_data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_RELAY_DATA_DIR}")); - let scripts_dir = PathBuf::from(format!("{base_dir_raw}{NODE_SCRIPTS_DIR}")); - let log_path = options - .node_log_path - .cloned() - .unwrap_or_else(|| base_dir.join(format!("{}.log", options.name))); + let base_dir_raw = base_dir.to_string_lossy(); + let config_dir = PathBuf::from(format!("{base_dir_raw}{NODE_CONFIG_DIR}")); + let data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_DATA_DIR}")); + let relay_data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_RELAY_DATA_DIR}")); + let scripts_dir = PathBuf::from(format!("{base_dir_raw}{NODE_SCRIPTS_DIR}")); + let log_path = options + .node_log_path + .cloned() + .unwrap_or_else(|| base_dir.join(format!("{}.log", options.name))); - trace!("creating dirs {:?}", config_dir); - try_join!( - filesystem.create_dir_all(&config_dir), - filesystem.create_dir_all(&data_dir), - filesystem.create_dir_all(&relay_data_dir), - filesystem.create_dir_all(&scripts_dir), - )?; - trace!("created!"); + trace!("creating dirs {:?}", config_dir); + try_join!( + filesystem.create_dir_all(&config_dir), + filesystem.create_dir_all(&data_dir), + filesystem.create_dir_all(&relay_data_dir), + filesystem.create_dir_all(&scripts_dir), + )?; + trace!("created!"); - let node = Arc::new(NativeNode { - namespace: options.namespace.clone(), - name: options.name.to_string(), - program: options.program.to_string(), - args: options.args.to_vec(), - env: options.env.to_vec(), - base_dir, - config_dir, - data_dir, - relay_data_dir, - scripts_dir, - log_path, - process_handle: std::sync::RwLock::new(None), - stdout_reading_task: RwLock::new(None), - stderr_reading_task: RwLock::new(None), - log_writing_task: RwLock::new(None), - filesystem: filesystem.clone(), - provider_tag: native::provider::PROVIDER_NAME.to_string(), - }); + let node = Arc::new(NativeNode { + namespace: options.namespace.clone(), + name: options.name.to_string(), + program: options.program.to_string(), + args: options.args.to_vec(), + env: options.env.to_vec(), + base_dir, + config_dir, + data_dir, + relay_data_dir, + scripts_dir, + log_path, + process_handle: std::sync::RwLock::new(None), + stdout_reading_task: RwLock::new(None), + stderr_reading_task: RwLock::new(None), + log_writing_task: RwLock::new(None), + filesystem: filesystem.clone(), + provider_tag: native::provider::PROVIDER_NAME.to_string(), + }); - node.initialize_startup_paths(options.created_paths).await?; - node.initialize_startup_files(options.startup_files).await?; + node.initialize_startup_paths(options.created_paths).await?; + node.initialize_startup_files(options.startup_files).await?; - if let Some(db_snap) = options.db_snapshot { - node.initialize_db_snapshot(db_snap).await?; - } + if let Some(db_snap) = options.db_snapshot { + node.initialize_db_snapshot(db_snap).await?; + } - let (stdout, stderr) = node.initialize_process().await?; + let (stdout, stderr) = node.initialize_process().await?; - node.initialize_log_writing(stdout, stderr).await; + node.initialize_log_writing(stdout, stderr).await; - Ok(node) - } + Ok(node) + } - pub(super) async fn attach_to_live( - options: NativeNodeOptions<'_, FS>, - pid: i32, - ) -> Result, ProviderError> { - let filesystem = options.filesystem.clone(); + pub(super) async fn attach_to_live( + options: NativeNodeOptions<'_, FS>, + pid: i32, + ) -> Result, ProviderError> { + let filesystem = options.filesystem.clone(); - let base_dir = - PathBuf::from_iter([options.namespace_base_dir, &PathBuf::from(options.name)]); - trace!("creating base_dir {:?}", base_dir); - options.filesystem.create_dir_all(&base_dir).await?; - trace!("created base_dir {:?}", base_dir); + let base_dir = + PathBuf::from_iter([options.namespace_base_dir, &PathBuf::from(options.name)]); + trace!("creating base_dir {:?}", base_dir); + options.filesystem.create_dir_all(&base_dir).await?; + trace!("created base_dir {:?}", base_dir); - let base_dir_raw = base_dir.to_string_lossy(); - let config_dir = PathBuf::from(format!("{base_dir_raw}{NODE_CONFIG_DIR}")); - let data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_DATA_DIR}")); - let relay_data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_RELAY_DATA_DIR}")); - let scripts_dir = PathBuf::from(format!("{base_dir_raw}{NODE_SCRIPTS_DIR}")); - let log_path = options - .node_log_path - .cloned() - .unwrap_or_else(|| base_dir.join(format!("{}.log", options.name))); + let base_dir_raw = base_dir.to_string_lossy(); + let config_dir = PathBuf::from(format!("{base_dir_raw}{NODE_CONFIG_DIR}")); + let data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_DATA_DIR}")); + let relay_data_dir = PathBuf::from(format!("{base_dir_raw}{NODE_RELAY_DATA_DIR}")); + let scripts_dir = PathBuf::from(format!("{base_dir_raw}{NODE_SCRIPTS_DIR}")); + let log_path = options + .node_log_path + .cloned() + .unwrap_or_else(|| base_dir.join(format!("{}.log", options.name))); - let pid = Pid::from_raw(pid); + let pid = Pid::from_raw(pid); - let node = Arc::new(NativeNode { - namespace: options.namespace.clone(), - name: options.name.to_string(), - program: options.program.to_string(), - args: options.args.to_vec(), - env: options.env.to_vec(), - base_dir, - config_dir, - data_dir, - relay_data_dir, - scripts_dir, - log_path, - process_handle: std::sync::RwLock::new(Some(ProcessHandle::Attached(pid))), - stdout_reading_task: RwLock::new(None), - stderr_reading_task: RwLock::new(None), - log_writing_task: RwLock::new(None), - filesystem: filesystem.clone(), - provider_tag: native::provider::PROVIDER_NAME.to_string(), - }); + let node = Arc::new(NativeNode { + namespace: options.namespace.clone(), + name: options.name.to_string(), + program: options.program.to_string(), + args: options.args.to_vec(), + env: options.env.to_vec(), + base_dir, + config_dir, + data_dir, + relay_data_dir, + scripts_dir, + log_path, + process_handle: std::sync::RwLock::new(Some(ProcessHandle::Attached(pid))), + stdout_reading_task: RwLock::new(None), + stderr_reading_task: RwLock::new(None), + log_writing_task: RwLock::new(None), + filesystem: filesystem.clone(), + provider_tag: native::provider::PROVIDER_NAME.to_string(), + }); - Ok(node) - } + Ok(node) + } - async fn initialize_startup_paths(&self, paths: &[PathBuf]) -> Result<(), ProviderError> { - trace!("creating paths {:?}", paths); - let base_dir_raw = self.base_dir.to_string_lossy(); - try_join_all(paths.iter().map(|file| { - let full_path = format!("{base_dir_raw}{}", file.to_string_lossy()); - self.filesystem.create_dir_all(full_path) - })) - .await?; - trace!("paths created!"); + async fn initialize_startup_paths(&self, paths: &[PathBuf]) -> Result<(), ProviderError> { + trace!("creating paths {:?}", paths); + let base_dir_raw = self.base_dir.to_string_lossy(); + try_join_all(paths.iter().map(|file| { + let full_path = format!("{base_dir_raw}{}", file.to_string_lossy()); + self.filesystem.create_dir_all(full_path) + })) + .await?; + trace!("paths created!"); - Ok(()) - } + Ok(()) + } - async fn initialize_startup_files( - &self, - startup_files: &[TransferedFile], - ) -> Result<(), ProviderError> { - trace!("creating files {:?}", startup_files); - try_join_all( - startup_files - .iter() - .map(|file| self.send_file(&file.local_path, &file.remote_path, &file.mode)), - ) - .await?; - trace!("files created!"); + async fn initialize_startup_files( + &self, + startup_files: &[TransferedFile], + ) -> Result<(), ProviderError> { + trace!("creating files {:?}", startup_files); + try_join_all( + startup_files + .iter() + .map(|file| self.send_file(&file.local_path, &file.remote_path, &file.mode)), + ) + .await?; + trace!("files created!"); - Ok(()) - } + Ok(()) + } - async fn initialize_db_snapshot( - &self, - db_snapshot: &AssetLocation, - ) -> Result<(), ProviderError> { - trace!("snap: {db_snapshot}"); + async fn initialize_db_snapshot( + &self, + db_snapshot: &AssetLocation, + ) -> Result<(), ProviderError> { + trace!("snap: {db_snapshot}"); - // check if we need to get the db or is already in the ns - let ns_base_dir = self.namespace_base_dir(); - let hashed_location = match db_snapshot { - AssetLocation::Url(location) => hex::encode(sha2::Sha256::digest(location.to_string())), - AssetLocation::FilePath(filepath) => { - hex::encode(sha2::Sha256::digest(filepath.to_string_lossy().to_string())) - }, - }; + // check if we need to get the db or is already in the ns + let ns_base_dir = self.namespace_base_dir(); + let hashed_location = match db_snapshot { + AssetLocation::Url(location) => hex::encode(sha2::Sha256::digest(location.to_string())), + AssetLocation::FilePath(filepath) => { + hex::encode(sha2::Sha256::digest(filepath.to_string_lossy().to_string())) + }, + }; - let full_path = format!("{ns_base_dir}/{hashed_location}.tgz"); - trace!("db_snap fullpath in ns: {full_path}"); - if !self.filesystem.exists(&full_path).await { - // needs to download/copy - self.get_db_snapshot(db_snapshot, &full_path).await?; - } + let full_path = format!("{ns_base_dir}/{hashed_location}.tgz"); + trace!("db_snap fullpath in ns: {full_path}"); + if !self.filesystem.exists(&full_path).await { + // needs to download/copy + self.get_db_snapshot(db_snapshot, &full_path).await?; + } - let contents = self.filesystem.read(&full_path).await.unwrap(); - let gz = GzDecoder::new(&contents[..]); - let mut archive = Archive::new(gz); - archive - .unpack(self.base_dir.to_string_lossy().as_ref()) - .unwrap(); + let contents = self.filesystem.read(&full_path).await.unwrap(); + let gz = GzDecoder::new(&contents[..]); + let mut archive = Archive::new(gz); + archive.unpack(self.base_dir.to_string_lossy().as_ref()).unwrap(); - if std::env::var("ZOMBIE_RM_TGZ_AFTER_EXTRACT").is_ok() { - let res = fs::remove_file(&full_path).await; - trace!("removing {}, result {:?}", full_path, res); - } + if std::env::var("ZOMBIE_RM_TGZ_AFTER_EXTRACT").is_ok() { + let res = fs::remove_file(&full_path).await; + trace!("removing {}, result {:?}", full_path, res); + } - Ok(()) - } + Ok(()) + } - async fn get_db_snapshot( - &self, - location: &AssetLocation, - full_path: &str, - ) -> Result<(), ProviderError> { - trace!("getting db_snapshot from: {:?} to: {full_path}", location); - match location { - AssetLocation::Url(location) => { - let res = reqwest::get(location.as_ref()) - .await - .map_err(|err| ProviderError::DownloadFile(location.to_string(), err.into()))?; + async fn get_db_snapshot( + &self, + location: &AssetLocation, + full_path: &str, + ) -> Result<(), ProviderError> { + trace!("getting db_snapshot from: {:?} to: {full_path}", location); + match location { + AssetLocation::Url(location) => { + let res = reqwest::get(location.as_ref()) + .await + .map_err(|err| ProviderError::DownloadFile(location.to_string(), err.into()))?; - let contents: &[u8] = &res.bytes().await.unwrap(); - trace!("writing: {full_path}"); - self.filesystem.write(full_path, contents).await?; - }, - AssetLocation::FilePath(filepath) => { - self.filesystem.copy(filepath, full_path).await?; - }, - }; + let contents: &[u8] = &res.bytes().await.unwrap(); + trace!("writing: {full_path}"); + self.filesystem.write(full_path, contents).await?; + }, + AssetLocation::FilePath(filepath) => { + self.filesystem.copy(filepath, full_path).await?; + }, + }; - Ok(()) - } + Ok(()) + } - async fn initialize_process(&self) -> Result<(ChildStdout, ChildStderr), ProviderError> { - let filtered_env: HashMap = env::vars() - .filter(|(k, _)| k == "TZ" || k == "LANG" || k == "PATH") - .collect(); + async fn initialize_process(&self) -> Result<(ChildStdout, ChildStderr), ProviderError> { + let filtered_env: HashMap = + env::vars().filter(|(k, _)| k == "TZ" || k == "LANG" || k == "PATH").collect(); - let mut process = Command::new(&self.program) + let mut process = Command::new(&self.program) .args(&self.args) .env_clear() .envs(&filtered_env) // minimal environment @@ -353,382 +350,357 @@ where .current_dir(&self.base_dir) .spawn() .map_err(|err| ProviderError::NodeSpawningFailed(self.name.to_string(), err.into()))?; - let stdout = process - .stdout - .take() - .expect(&format!("infaillible, stdout is piped {THIS_IS_A_BUG}")); - let stderr = process - .stderr - .take() - .expect(&format!("infaillible, stderr is piped {THIS_IS_A_BUG}")); + let stdout = + process.stdout.take().expect(&format!("infaillible, stdout is piped {THIS_IS_A_BUG}")); + let stderr = + process.stderr.take().expect(&format!("infaillible, stderr is piped {THIS_IS_A_BUG}")); - let pid = Pid::from_raw( - process - .id() - .ok_or_else(|| ProviderError::ProcessIdRetrievalFailed(self.name.to_string()))? - as i32, - ); - self.process_handle - .write() - .map_err(|_e| ProviderError::FailedToAcquireLock(self.name.clone()))? - .replace(ProcessHandle::Spawned(process, pid)); + let pid = Pid::from_raw( + process + .id() + .ok_or_else(|| ProviderError::ProcessIdRetrievalFailed(self.name.to_string()))? + as i32, + ); + self.process_handle + .write() + .map_err(|_e| ProviderError::FailedToAcquireLock(self.name.clone()))? + .replace(ProcessHandle::Spawned(process, pid)); - Ok((stdout, stderr)) - } + Ok((stdout, stderr)) + } - async fn initialize_log_writing(&self, stdout: ChildStdout, stderr: ChildStderr) { - let (stdout_tx, mut rx) = mpsc::channel(10); - let stderr_tx = stdout_tx.clone(); + async fn initialize_log_writing(&self, stdout: ChildStdout, stderr: ChildStderr) { + let (stdout_tx, mut rx) = mpsc::channel(10); + let stderr_tx = stdout_tx.clone(); - self.stdout_reading_task - .write() - .await - .replace(self.create_stream_polling_task(stdout, stdout_tx)); - self.stderr_reading_task - .write() - .await - .replace(self.create_stream_polling_task(stderr, stderr_tx)); + self.stdout_reading_task + .write() + .await + .replace(self.create_stream_polling_task(stdout, stdout_tx)); + self.stderr_reading_task + .write() + .await + .replace(self.create_stream_polling_task(stderr, stderr_tx)); - let filesystem = self.filesystem.clone(); - let log_path = self.log_path.clone(); + let filesystem = self.filesystem.clone(); + let log_path = self.log_path.clone(); - self.log_writing_task - .write() - .await - .replace(tokio::spawn(async move { - loop { - while let Some(Ok(data)) = rx.recv().await { - // TODO: find a better way instead of ignoring error ? - let _ = filesystem.append(&log_path, data).await; - } - sleep(Duration::from_millis(250)).await; - } - })); - } + self.log_writing_task.write().await.replace(tokio::spawn(async move { + loop { + while let Some(Ok(data)) = rx.recv().await { + // TODO: find a better way instead of ignoring error ? + let _ = filesystem.append(&log_path, data).await; + } + sleep(Duration::from_millis(250)).await; + } + })); + } - fn create_stream_polling_task( - &self, - stream: impl AsyncRead + Unpin + Send + 'static, - tx: Sender, std::io::Error>>, - ) -> JoinHandle<()> { - tokio::spawn(async move { - let mut reader = BufReader::new(stream); - let mut buffer = vec![0u8; 1024]; + fn create_stream_polling_task( + &self, + stream: impl AsyncRead + Unpin + Send + 'static, + tx: Sender, std::io::Error>>, + ) -> JoinHandle<()> { + tokio::spawn(async move { + let mut reader = BufReader::new(stream); + let mut buffer = vec![0u8; 1024]; - loop { - match reader.read(&mut buffer).await { - Ok(0) => { - let _ = tx.send(Ok(Vec::new())).await; - break; - }, - Ok(n) => { - let _ = tx.send(Ok(buffer[..n].to_vec())).await; - }, - Err(e) => { - let _ = tx.send(Err(e)).await; - break; - }, - } - } - }) - } + loop { + match reader.read(&mut buffer).await { + Ok(0) => { + let _ = tx.send(Ok(Vec::new())).await; + break; + }, + Ok(n) => { + let _ = tx.send(Ok(buffer[..n].to_vec())).await; + }, + Err(e) => { + let _ = tx.send(Err(e)).await; + break; + }, + } + } + }) + } - fn process_id(&self) -> Result { - let pid = self - .process_handle - .read() - .map_err(|_e| ProviderError::FailedToAcquireLock(self.name.clone()))? - .as_ref() - .map(|handle| match handle { - ProcessHandle::Spawned(_, pid) => *pid, - ProcessHandle::Attached(pid) => *pid, - }) - .ok_or_else(|| ProviderError::ProcessIdRetrievalFailed(self.name.to_string()))?; + fn process_id(&self) -> Result { + let pid = self + .process_handle + .read() + .map_err(|_e| ProviderError::FailedToAcquireLock(self.name.clone()))? + .as_ref() + .map(|handle| match handle { + ProcessHandle::Spawned(_, pid) => *pid, + ProcessHandle::Attached(pid) => *pid, + }) + .ok_or_else(|| ProviderError::ProcessIdRetrievalFailed(self.name.to_string()))?; - Ok(pid) - } + Ok(pid) + } - pub(crate) async fn abort(&self) -> anyhow::Result<()> { - if let Some(task) = self.log_writing_task.write().await.take() { - task.abort(); - } - if let Some(task) = self.stdout_reading_task.write().await.take() { - task.abort(); - } - if let Some(task) = self.stderr_reading_task.write().await.take() { - task.abort(); - } + pub(crate) async fn abort(&self) -> anyhow::Result<()> { + if let Some(task) = self.log_writing_task.write().await.take() { + task.abort(); + } + if let Some(task) = self.stdout_reading_task.write().await.take() { + task.abort(); + } + if let Some(task) = self.stderr_reading_task.write().await.take() { + task.abort(); + } - let process_handle = { - let mut guard = self - .process_handle - .write() - .map_err(|_e| ProviderError::FailedToAcquireLock(self.name.clone()))?; - guard - .take() - .ok_or_else(|| anyhow!("no process was attached for the node"))? - }; + let process_handle = { + let mut guard = self + .process_handle + .write() + .map_err(|_e| ProviderError::FailedToAcquireLock(self.name.clone()))?; + guard.take().ok_or_else(|| anyhow!("no process was attached for the node"))? + }; - match process_handle { - ProcessHandle::Spawned(mut child, _pid) => { - child.kill().await?; - }, - ProcessHandle::Attached(pid) => { - kill(pid, Signal::SIGKILL) - .map_err(|err| anyhow!("Failed to kill attached process {pid}: {err}"))?; - }, - } + match process_handle { + ProcessHandle::Spawned(mut child, _pid) => { + child.kill().await?; + }, + ProcessHandle::Attached(pid) => { + kill(pid, Signal::SIGKILL) + .map_err(|err| anyhow!("Failed to kill attached process {pid}: {err}"))?; + }, + } - Ok(()) - } + Ok(()) + } - fn namespace_base_dir(&self) -> String { - self.namespace - .upgrade() - .map(|namespace| namespace.base_dir().to_string_lossy().to_string()) - .unwrap_or_else(|| panic!("namespace shouldn't be dropped, {THIS_IS_A_BUG}")) - } + fn namespace_base_dir(&self) -> String { + self.namespace + .upgrade() + .map(|namespace| namespace.base_dir().to_string_lossy().to_string()) + .unwrap_or_else(|| panic!("namespace shouldn't be dropped, {THIS_IS_A_BUG}")) + } } #[async_trait] impl ProviderNode for NativeNode where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - fn name(&self) -> &str { - &self.name - } + fn name(&self) -> &str { + &self.name + } - fn args(&self) -> Vec<&str> { - self.args.iter().map(|arg| arg.as_str()).collect() - } + fn args(&self) -> Vec<&str> { + self.args.iter().map(|arg| arg.as_str()).collect() + } - fn base_dir(&self) -> &PathBuf { - &self.base_dir - } + fn base_dir(&self) -> &PathBuf { + &self.base_dir + } - fn config_dir(&self) -> &PathBuf { - &self.config_dir - } + fn config_dir(&self) -> &PathBuf { + &self.config_dir + } - fn data_dir(&self) -> &PathBuf { - &self.data_dir - } + fn data_dir(&self) -> &PathBuf { + &self.data_dir + } - fn relay_data_dir(&self) -> &PathBuf { - &self.relay_data_dir - } + fn relay_data_dir(&self) -> &PathBuf { + &self.relay_data_dir + } - fn scripts_dir(&self) -> &PathBuf { - &self.scripts_dir - } + fn scripts_dir(&self) -> &PathBuf { + &self.scripts_dir + } - fn log_path(&self) -> &PathBuf { - &self.log_path - } + fn log_path(&self) -> &PathBuf { + &self.log_path + } - fn log_cmd(&self) -> String { - format!("tail -f {}", self.log_path().to_string_lossy()) - } + fn log_cmd(&self) -> String { + format!("tail -f {}", self.log_path().to_string_lossy()) + } - fn path_in_node(&self, file: &Path) -> PathBuf { - let full_path = format!( - "{}/{}", - self.base_dir.to_string_lossy(), - file.to_string_lossy() - ); - PathBuf::from(full_path) - } + fn path_in_node(&self, file: &Path) -> PathBuf { + let full_path = format!("{}/{}", self.base_dir.to_string_lossy(), file.to_string_lossy()); + PathBuf::from(full_path) + } - async fn logs(&self) -> Result { - Ok(self.filesystem.read_to_string(&self.log_path).await?) - } + async fn logs(&self) -> Result { + Ok(self.filesystem.read_to_string(&self.log_path).await?) + } - async fn dump_logs(&self, local_dest: PathBuf) -> Result<(), ProviderError> { - Ok(self.filesystem.copy(&self.log_path, local_dest).await?) - } + async fn dump_logs(&self, local_dest: PathBuf) -> Result<(), ProviderError> { + Ok(self.filesystem.copy(&self.log_path, local_dest).await?) + } - async fn run_command( - &self, - options: RunCommandOptions, - ) -> Result { - let result = Command::new(options.program.clone()) - .args(options.args.clone()) - .envs(options.env) - .current_dir(&self.base_dir) - .output() - .await - .map_err(|err| { - ProviderError::RunCommandError( - format!("{} {}", &options.program, &options.args.join(" ")), - "locally".to_string(), - err.into(), - ) - })?; + async fn run_command( + &self, + options: RunCommandOptions, + ) -> Result { + let result = Command::new(options.program.clone()) + .args(options.args.clone()) + .envs(options.env) + .current_dir(&self.base_dir) + .output() + .await + .map_err(|err| { + ProviderError::RunCommandError( + format!("{} {}", &options.program, &options.args.join(" ")), + "locally".to_string(), + err.into(), + ) + })?; - if result.status.success() { - Ok(Ok(String::from_utf8_lossy(&result.stdout).to_string())) - } else { - Ok(Err(( - result.status, - String::from_utf8_lossy(&result.stderr).to_string(), - ))) - } - } + if result.status.success() { + Ok(Ok(String::from_utf8_lossy(&result.stdout).to_string())) + } else { + Ok(Err((result.status, String::from_utf8_lossy(&result.stderr).to_string()))) + } + } - async fn run_script( - &self, - options: RunScriptOptions, - ) -> Result { - let local_script_path = PathBuf::from(&options.local_script_path); + async fn run_script( + &self, + options: RunScriptOptions, + ) -> Result { + let local_script_path = PathBuf::from(&options.local_script_path); - if !self.filesystem.exists(&local_script_path).await { - return Err(ProviderError::ScriptNotFound(local_script_path)); - } + if !self.filesystem.exists(&local_script_path).await { + return Err(ProviderError::ScriptNotFound(local_script_path)); + } - // extract file name and build remote file path - let script_file_name = local_script_path - .file_name() - .map(|file_name| file_name.to_string_lossy().to_string()) - .ok_or(ProviderError::InvalidScriptPath(anyhow!( - "Can't retrieve filename from script with path: {:?}", - options.local_script_path - )))?; - let remote_script_path = format!( - "{}/{}", - self.scripts_dir.to_string_lossy(), - script_file_name - ); + // extract file name and build remote file path + let script_file_name = local_script_path + .file_name() + .map(|file_name| file_name.to_string_lossy().to_string()) + .ok_or(ProviderError::InvalidScriptPath(anyhow!( + "Can't retrieve filename from script with path: {:?}", + options.local_script_path + )))?; + let remote_script_path = + format!("{}/{}", self.scripts_dir.to_string_lossy(), script_file_name); - // copy and set script's execute permission - self.filesystem - .copy(local_script_path, &remote_script_path) - .await?; - self.filesystem.set_mode(&remote_script_path, 0o744).await?; + // copy and set script's execute permission + self.filesystem.copy(local_script_path, &remote_script_path).await?; + self.filesystem.set_mode(&remote_script_path, 0o744).await?; - // execute script - self.run_command(RunCommandOptions { - program: remote_script_path, - args: options.args, - env: options.env, - }) - .await - } + // execute script + self.run_command(RunCommandOptions { + program: remote_script_path, + args: options.args, + env: options.env, + }) + .await + } - async fn send_file( - &self, - local_file_path: &Path, - remote_file_path: &Path, - mode: &str, - ) -> Result<(), ProviderError> { - let namespaced_remote_file_path = PathBuf::from(format!( - "{}{}", - &self.base_dir.to_string_lossy(), - remote_file_path.to_string_lossy() - )); + async fn send_file( + &self, + local_file_path: &Path, + remote_file_path: &Path, + mode: &str, + ) -> Result<(), ProviderError> { + let namespaced_remote_file_path = PathBuf::from(format!( + "{}{}", + &self.base_dir.to_string_lossy(), + remote_file_path.to_string_lossy() + )); - self.filesystem - .copy(local_file_path, &namespaced_remote_file_path) - .await?; + self.filesystem.copy(local_file_path, &namespaced_remote_file_path).await?; - self.run_command( - RunCommandOptions::new("chmod") - .args(vec![mode, &namespaced_remote_file_path.to_string_lossy()]), - ) - .await? - .map_err(|(_, err)| { - ProviderError::SendFile( - self.name.clone(), - local_file_path.to_string_lossy().to_string(), - anyhow!("{err}"), - ) - })?; + self.run_command( + RunCommandOptions::new("chmod") + .args(vec![mode, &namespaced_remote_file_path.to_string_lossy()]), + ) + .await? + .map_err(|(_, err)| { + ProviderError::SendFile( + self.name.clone(), + local_file_path.to_string_lossy().to_string(), + anyhow!("{err}"), + ) + })?; - Ok(()) - } + Ok(()) + } - async fn receive_file( - &self, - remote_file_path: &Path, - local_file_path: &Path, - ) -> Result<(), ProviderError> { - let namespaced_remote_file_path = PathBuf::from(format!( - "{}{}", - &self.base_dir.to_string_lossy(), - remote_file_path.to_string_lossy() - )); + async fn receive_file( + &self, + remote_file_path: &Path, + local_file_path: &Path, + ) -> Result<(), ProviderError> { + let namespaced_remote_file_path = PathBuf::from(format!( + "{}{}", + &self.base_dir.to_string_lossy(), + remote_file_path.to_string_lossy() + )); - self.filesystem - .copy(namespaced_remote_file_path, local_file_path) - .await?; + self.filesystem.copy(namespaced_remote_file_path, local_file_path).await?; - Ok(()) - } + Ok(()) + } - async fn pause(&self) -> Result<(), ProviderError> { - let process_id = self.process_id()?; + async fn pause(&self) -> Result<(), ProviderError> { + let process_id = self.process_id()?; - kill(process_id, Signal::SIGSTOP) - .map_err(|err| ProviderError::PauseNodeFailed(self.name.clone(), err.into()))?; + kill(process_id, Signal::SIGSTOP) + .map_err(|err| ProviderError::PauseNodeFailed(self.name.clone(), err.into()))?; - Ok(()) - } + Ok(()) + } - async fn resume(&self) -> Result<(), ProviderError> { - let process_id = self.process_id()?; + async fn resume(&self) -> Result<(), ProviderError> { + let process_id = self.process_id()?; - nix::sys::signal::kill(process_id, Signal::SIGCONT) - .map_err(|err| ProviderError::ResumeNodeFailed(self.name.clone(), err.into()))?; + nix::sys::signal::kill(process_id, Signal::SIGCONT) + .map_err(|err| ProviderError::ResumeNodeFailed(self.name.clone(), err.into()))?; - Ok(()) - } + Ok(()) + } - async fn restart(&self, after: Option) -> Result<(), ProviderError> { - if let Some(duration) = after { - sleep(duration).await; - } + async fn restart(&self, after: Option) -> Result<(), ProviderError> { + if let Some(duration) = after { + sleep(duration).await; + } - self.abort() - .await - .map_err(|err| ProviderError::RestartNodeFailed(self.name.clone(), err))?; + self.abort() + .await + .map_err(|err| ProviderError::RestartNodeFailed(self.name.clone(), err))?; - let (stdout, stderr) = self - .initialize_process() - .await - .map_err(|err| ProviderError::RestartNodeFailed(self.name.clone(), err.into()))?; + let (stdout, stderr) = self + .initialize_process() + .await + .map_err(|err| ProviderError::RestartNodeFailed(self.name.clone(), err.into()))?; - self.initialize_log_writing(stdout, stderr).await; + self.initialize_log_writing(stdout, stderr).await; - Ok(()) - } + Ok(()) + } - async fn destroy(&self) -> Result<(), ProviderError> { - self.abort() - .await - .map_err(|err| ProviderError::DestroyNodeFailed(self.name.clone(), err))?; + async fn destroy(&self) -> Result<(), ProviderError> { + self.abort() + .await + .map_err(|err| ProviderError::DestroyNodeFailed(self.name.clone(), err))?; - if let Some(namespace) = self.namespace.upgrade() { - namespace.nodes.write().await.remove(&self.name); - } + if let Some(namespace) = self.namespace.upgrade() { + namespace.nodes.write().await.remove(&self.name); + } - Ok(()) - } + Ok(()) + } } fn serialize_process_handle( - process_handle: &std::sync::RwLock>, - serializer: S, + process_handle: &std::sync::RwLock>, + serializer: S, ) -> Result where - S: Serializer, + S: Serializer, { - let pid = process_handle - .read() - .map_err(|_e| S::Error::custom("failed to acquire read lock"))? - .as_ref() - .map(|handle| match handle { - ProcessHandle::Spawned(_, pid) => pid.as_raw(), - ProcessHandle::Attached(pid) => pid.as_raw(), - }); - pid.serialize(serializer) + let pid = process_handle + .read() + .map_err(|_e| S::Error::custom("failed to acquire read lock"))? + .as_ref() + .map(|handle| match handle { + ProcessHandle::Spawned(_, pid) => pid.as_raw(), + ProcessHandle::Attached(pid) => pid.as_raw(), + }); + pid.serialize(serializer) } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/native/provider.rs b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/native/provider.rs index 7a943973..a8edee5c 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/native/provider.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/native/provider.rs @@ -1,7 +1,7 @@ use std::{ - collections::HashMap, - path::{Path, PathBuf}, - sync::{Arc, Weak}, + collections::HashMap, + path::{Path, PathBuf}, + sync::{Arc, Weak}, }; use async_trait::async_trait; @@ -10,133 +10,124 @@ use tokio::sync::RwLock; use super::namespace::NativeNamespace; use crate::{ - shared::helpers::extract_namespace_info, types::ProviderCapabilities, DynNamespace, Provider, - ProviderError, ProviderNamespace, + shared::helpers::extract_namespace_info, types::ProviderCapabilities, DynNamespace, Provider, + ProviderError, ProviderNamespace, }; pub const PROVIDER_NAME: &str = "native"; pub struct NativeProvider where - FS: FileSystem + Send + Sync + Clone, + FS: FileSystem + Send + Sync + Clone, { - weak: Weak>, - capabilities: ProviderCapabilities, - tmp_dir: PathBuf, - filesystem: FS, - pub(super) namespaces: RwLock>>>, + weak: Weak>, + capabilities: ProviderCapabilities, + tmp_dir: PathBuf, + filesystem: FS, + pub(super) namespaces: RwLock>>>, } impl NativeProvider where - FS: FileSystem + Send + Sync + Clone, + FS: FileSystem + Send + Sync + Clone, { - pub fn new(filesystem: FS) -> Arc { - Arc::new_cyclic(|weak| NativeProvider { - weak: weak.clone(), - capabilities: ProviderCapabilities { - has_resources: false, - requires_image: false, - prefix_with_full_path: true, - use_default_ports_in_cmd: false, - }, - // NOTE: temp_dir in linux return `/tmp` but on mac something like - // `/var/folders/rz/1cyx7hfj31qgb98d8_cg7jwh0000gn/T/`, having - // one `trailing slash` and the other no can cause issues if - // you try to build a fullpath by concatenate. Use Pathbuf to prevent the issue. - tmp_dir: std::env::temp_dir(), - filesystem, - namespaces: RwLock::new(HashMap::new()), - }) - } + pub fn new(filesystem: FS) -> Arc { + Arc::new_cyclic(|weak| NativeProvider { + weak: weak.clone(), + capabilities: ProviderCapabilities { + has_resources: false, + requires_image: false, + prefix_with_full_path: true, + use_default_ports_in_cmd: false, + }, + // NOTE: temp_dir in linux return `/tmp` but on mac something like + // `/var/folders/rz/1cyx7hfj31qgb98d8_cg7jwh0000gn/T/`, having + // one `trailing slash` and the other no can cause issues if + // you try to build a fullpath by concatenate. Use Pathbuf to prevent the issue. + tmp_dir: std::env::temp_dir(), + filesystem, + namespaces: RwLock::new(HashMap::new()), + }) + } - pub fn tmp_dir(mut self, tmp_dir: impl Into) -> Self { - self.tmp_dir = tmp_dir.into(); - self - } + pub fn tmp_dir(mut self, tmp_dir: impl Into) -> Self { + self.tmp_dir = tmp_dir.into(); + self + } } #[async_trait] impl Provider for NativeProvider where - FS: FileSystem + Send + Sync + Clone + 'static, + FS: FileSystem + Send + Sync + Clone + 'static, { - fn name(&self) -> &str { - PROVIDER_NAME - } + fn name(&self) -> &str { + PROVIDER_NAME + } - fn capabilities(&self) -> &ProviderCapabilities { - &self.capabilities - } + fn capabilities(&self) -> &ProviderCapabilities { + &self.capabilities + } - async fn namespaces(&self) -> HashMap { - self.namespaces - .read() - .await - .iter() - .map(|(name, namespace)| (name.clone(), namespace.clone() as DynNamespace)) - .collect() - } + async fn namespaces(&self) -> HashMap { + self.namespaces + .read() + .await + .iter() + .map(|(name, namespace)| (name.clone(), namespace.clone() as DynNamespace)) + .collect() + } - async fn create_namespace(&self) -> Result { - let namespace = NativeNamespace::new( - &self.weak, - &self.tmp_dir, - &self.capabilities, - &self.filesystem, - None, - ) - .await?; + async fn create_namespace(&self) -> Result { + let namespace = NativeNamespace::new( + &self.weak, + &self.tmp_dir, + &self.capabilities, + &self.filesystem, + None, + ) + .await?; - self.namespaces - .write() - .await - .insert(namespace.name().to_string(), namespace.clone()); + self.namespaces.write().await.insert(namespace.name().to_string(), namespace.clone()); - Ok(namespace) - } + Ok(namespace) + } - async fn create_namespace_with_base_dir( - &self, - base_dir: &Path, - ) -> Result { - let namespace = NativeNamespace::new( - &self.weak, - &self.tmp_dir, - &self.capabilities, - &self.filesystem, - Some(base_dir), - ) - .await?; + async fn create_namespace_with_base_dir( + &self, + base_dir: &Path, + ) -> Result { + let namespace = NativeNamespace::new( + &self.weak, + &self.tmp_dir, + &self.capabilities, + &self.filesystem, + Some(base_dir), + ) + .await?; - self.namespaces - .write() - .await - .insert(namespace.name().to_string(), namespace.clone()); + self.namespaces.write().await.insert(namespace.name().to_string(), namespace.clone()); - Ok(namespace) - } + Ok(namespace) + } - async fn create_namespace_from_json( - &self, - json_value: &serde_json::Value, - ) -> Result { - let (base_dir, name) = extract_namespace_info(json_value)?; + async fn create_namespace_from_json( + &self, + json_value: &serde_json::Value, + ) -> Result { + let (base_dir, name) = extract_namespace_info(json_value)?; - let namespace = NativeNamespace::attach_to_live( - &self.weak, - &self.capabilities, - &self.filesystem, - &base_dir, - &name, - ) - .await?; + let namespace = NativeNamespace::attach_to_live( + &self.weak, + &self.capabilities, + &self.filesystem, + &base_dir, + &name, + ) + .await?; - self.namespaces - .write() - .await - .insert(namespace.name().to_string(), namespace.clone()); + self.namespaces.write().await.insert(namespace.name().to_string(), namespace.clone()); - Ok(namespace) - } + Ok(namespace) + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/shared/helpers.rs b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/shared/helpers.rs index d12c2caa..0360b009 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/shared/helpers.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/shared/helpers.rs @@ -6,74 +6,69 @@ use crate::{types::RunCommandOptions, DynNode, ProviderError}; /// Check if we are running in `CI` by checking the 'RUN_IN_CI' env var pub fn running_in_ci() -> bool { - env::var("RUN_IN_CI").unwrap_or_default() == "1" + env::var("RUN_IN_CI").unwrap_or_default() == "1" } /// Executes a command on a temporary node and extracts the execution result either from the /// standard output or a file. pub async fn extract_execution_result( - temp_node: &DynNode, - options: RunCommandOptions, - expected_path: Option<&PathBuf>, + temp_node: &DynNode, + options: RunCommandOptions, + expected_path: Option<&PathBuf>, ) -> Result { - let output_contents = temp_node - .run_command(options) - .await? - .map_err(|(_, msg)| ProviderError::FileGenerationFailed(anyhow!("{msg}")))?; + let output_contents = temp_node + .run_command(options) + .await? + .map_err(|(_, msg)| ProviderError::FileGenerationFailed(anyhow!("{msg}")))?; - // If an expected_path is provided, read the file contents from inside the container - if let Some(expected_path) = expected_path.as_ref() { - Ok(temp_node - .run_command( - RunCommandOptions::new("cat") - .args(vec![expected_path.to_string_lossy().to_string()]), - ) - .await? - .map_err(|(_, msg)| { - ProviderError::FileGenerationFailed(anyhow!(format!( - "failed reading expected_path {}: {}", - expected_path.display(), - msg - ))) - })?) - } else { - Ok(output_contents) - } + // If an expected_path is provided, read the file contents from inside the container + if let Some(expected_path) = expected_path.as_ref() { + Ok(temp_node + .run_command( + RunCommandOptions::new("cat") + .args(vec![expected_path.to_string_lossy().to_string()]), + ) + .await? + .map_err(|(_, msg)| { + ProviderError::FileGenerationFailed(anyhow!(format!( + "failed reading expected_path {}: {}", + expected_path.display(), + msg + ))) + })?) + } else { + Ok(output_contents) + } } pub fn extract_namespace_info( - json_value: &serde_json::Value, + json_value: &serde_json::Value, ) -> Result<(PathBuf, String), ProviderError> { - let base_dir = json_value - .get("local_base_dir") - .and_then(|v| v.as_str()) - .map(PathBuf::from) - .ok_or(ProviderError::InvalidConfig( - "`field local_base_dir` is missing from zombie.json".to_string(), - ))?; + let base_dir = + json_value.get("local_base_dir").and_then(|v| v.as_str()).map(PathBuf::from).ok_or( + ProviderError::InvalidConfig( + "`field local_base_dir` is missing from zombie.json".to_string(), + ), + )?; - let name = - json_value - .get("ns") - .and_then(|v| v.as_str()) - .ok_or(ProviderError::InvalidConfig( - "field `ns` is missing from zombie.json".to_string(), - ))?; + let name = json_value.get("ns").and_then(|v| v.as_str()).ok_or( + ProviderError::InvalidConfig("field `ns` is missing from zombie.json".to_string()), + )?; - Ok((base_dir, name.to_string())) + Ok((base_dir, name.to_string())) } #[cfg(test)] mod tests { - use super::*; + use super::*; - #[test] - fn check_runing_in_ci_env_var() { - assert!(!running_in_ci()); - // now set the env var - env::set_var("RUN_IN_CI", "1"); - assert!(running_in_ci()); - // reset - env::set_var("RUN_IN_CI", ""); - } + #[test] + fn check_runing_in_ci_env_var() { + assert!(!running_in_ci()); + // now set the env var + env::set_var("RUN_IN_CI", "1"); + assert!(running_in_ci()); + // reset + env::set_var("RUN_IN_CI", ""); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/shared/types.rs b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/shared/types.rs index 82d53b04..727570ee 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/provider/src/shared/types.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/provider/src/shared/types.rs @@ -1,7 +1,7 @@ use std::{ - collections::HashMap, - path::{Path, PathBuf}, - process::ExitStatus, + collections::HashMap, + path::{Path, PathBuf}, + process::ExitStatus, }; use configuration::{shared::resources::Resources, types::AssetLocation}; @@ -13,363 +13,352 @@ pub type ExecutionResult = Result; #[derive(Debug, Clone, PartialEq)] pub struct ProviderCapabilities { - // default ports internal - /// Ensure that we have an image for each node (k8s/podman/docker) - pub requires_image: bool, - /// Allow to customize the resources through manifest (k8s). - pub has_resources: bool, - /// Used in native to prefix filepath with fullpath - pub prefix_with_full_path: bool, - /// Use default ports in node cmd/args. - /// NOTE: generally used in k8s/dockers since the images expose those ports. - pub use_default_ports_in_cmd: bool, + // default ports internal + /// Ensure that we have an image for each node (k8s/podman/docker) + pub requires_image: bool, + /// Allow to customize the resources through manifest (k8s). + pub has_resources: bool, + /// Used in native to prefix filepath with fullpath + pub prefix_with_full_path: bool, + /// Use default ports in node cmd/args. + /// NOTE: generally used in k8s/dockers since the images expose those ports. + pub use_default_ports_in_cmd: bool, } #[derive(Debug, Clone)] pub struct SpawnNodeOptions { - /// Name of the node - pub name: String, - /// Image of the node (IFF is supported by the provider) - pub image: Option, - /// Resources to apply to the node (IFF is supported by the provider) - pub resources: Option, - /// Main command to execute - pub program: String, - /// Arguments to pass to the main command - pub args: Vec, - /// Environment to set when running the `program` - pub env: Vec<(String, String)>, - // TODO: rename startup_files - /// Files to inject at startup - pub injected_files: Vec, - /// Paths to create before start the node (e.g keystore) - /// should be created with `create_dir_all` in order - /// to create the full path even when we have missing parts - pub created_paths: Vec, - /// Database snapshot to be injected (should be a tgz file) - /// Could be a local or remote asset - pub db_snapshot: Option, - pub port_mapping: Option>, - /// Optionally specify a log path for the node - pub node_log_path: Option, + /// Name of the node + pub name: String, + /// Image of the node (IFF is supported by the provider) + pub image: Option, + /// Resources to apply to the node (IFF is supported by the provider) + pub resources: Option, + /// Main command to execute + pub program: String, + /// Arguments to pass to the main command + pub args: Vec, + /// Environment to set when running the `program` + pub env: Vec<(String, String)>, + // TODO: rename startup_files + /// Files to inject at startup + pub injected_files: Vec, + /// Paths to create before start the node (e.g keystore) + /// should be created with `create_dir_all` in order + /// to create the full path even when we have missing parts + pub created_paths: Vec, + /// Database snapshot to be injected (should be a tgz file) + /// Could be a local or remote asset + pub db_snapshot: Option, + pub port_mapping: Option>, + /// Optionally specify a log path for the node + pub node_log_path: Option, } impl SpawnNodeOptions { - pub fn new(name: S, program: S) -> Self - where - S: AsRef, - { - Self { - name: name.as_ref().to_string(), - image: None, - resources: None, - program: program.as_ref().to_string(), - args: vec![], - env: vec![], - injected_files: vec![], - created_paths: vec![], - db_snapshot: None, - port_mapping: None, - node_log_path: None, - } - } + pub fn new(name: S, program: S) -> Self + where + S: AsRef, + { + Self { + name: name.as_ref().to_string(), + image: None, + resources: None, + program: program.as_ref().to_string(), + args: vec![], + env: vec![], + injected_files: vec![], + created_paths: vec![], + db_snapshot: None, + port_mapping: None, + node_log_path: None, + } + } - pub fn image(mut self, image: S) -> Self - where - S: AsRef, - { - self.image = Some(image.as_ref().to_string()); - self - } + pub fn image(mut self, image: S) -> Self + where + S: AsRef, + { + self.image = Some(image.as_ref().to_string()); + self + } - pub fn resources(mut self, resources: Resources) -> Self { - self.resources = Some(resources); - self - } + pub fn resources(mut self, resources: Resources) -> Self { + self.resources = Some(resources); + self + } - pub fn db_snapshot(mut self, db_snap: Option) -> Self { - self.db_snapshot = db_snap; - self - } + pub fn db_snapshot(mut self, db_snap: Option) -> Self { + self.db_snapshot = db_snap; + self + } - pub fn args(mut self, args: I) -> Self - where - S: AsRef, - I: IntoIterator, - { - self.args = args.into_iter().map(|s| s.as_ref().to_string()).collect(); - self - } + pub fn args(mut self, args: I) -> Self + where + S: AsRef, + I: IntoIterator, + { + self.args = args.into_iter().map(|s| s.as_ref().to_string()).collect(); + self + } - pub fn env(mut self, env: I) -> Self - where - S: AsRef, - I: IntoIterator, - { - self.env = env - .into_iter() - .map(|(name, value)| (name.as_ref().to_string(), value.as_ref().to_string())) - .collect(); - self - } + pub fn env(mut self, env: I) -> Self + where + S: AsRef, + I: IntoIterator, + { + self.env = env + .into_iter() + .map(|(name, value)| (name.as_ref().to_string(), value.as_ref().to_string())) + .collect(); + self + } - pub fn injected_files(mut self, injected_files: I) -> Self - where - I: IntoIterator, - { - self.injected_files = injected_files.into_iter().collect(); - self - } + pub fn injected_files(mut self, injected_files: I) -> Self + where + I: IntoIterator, + { + self.injected_files = injected_files.into_iter().collect(); + self + } - pub fn created_paths(mut self, created_paths: I) -> Self - where - P: AsRef, - I: IntoIterator, - { - self.created_paths = created_paths - .into_iter() - .map(|path| path.as_ref().into()) - .collect(); - self - } + pub fn created_paths(mut self, created_paths: I) -> Self + where + P: AsRef, + I: IntoIterator, + { + self.created_paths = created_paths.into_iter().map(|path| path.as_ref().into()).collect(); + self + } - pub fn port_mapping(mut self, ports: HashMap) -> Self { - self.port_mapping = Some(ports); - self - } + pub fn port_mapping(mut self, ports: HashMap) -> Self { + self.port_mapping = Some(ports); + self + } - pub fn node_log_path(mut self, path: Option) -> Self { - self.node_log_path = path; - self - } + pub fn node_log_path(mut self, path: Option) -> Self { + self.node_log_path = path; + self + } } #[derive(Debug)] pub struct GenerateFileCommand { - pub program: String, - pub args: Vec, - pub env: Vec<(String, String)>, - pub local_output_path: PathBuf, + pub program: String, + pub args: Vec, + pub env: Vec<(String, String)>, + pub local_output_path: PathBuf, } impl GenerateFileCommand { - pub fn new(program: S, local_output_path: P) -> Self - where - S: AsRef, - P: AsRef, - { - Self { - program: program.as_ref().to_string(), - args: vec![], - env: vec![], - local_output_path: local_output_path.as_ref().into(), - } - } + pub fn new(program: S, local_output_path: P) -> Self + where + S: AsRef, + P: AsRef, + { + Self { + program: program.as_ref().to_string(), + args: vec![], + env: vec![], + local_output_path: local_output_path.as_ref().into(), + } + } - pub fn args(mut self, args: I) -> Self - where - S: AsRef, - I: IntoIterator, - { - self.args = args.into_iter().map(|s| s.as_ref().to_string()).collect(); - self - } + pub fn args(mut self, args: I) -> Self + where + S: AsRef, + I: IntoIterator, + { + self.args = args.into_iter().map(|s| s.as_ref().to_string()).collect(); + self + } - pub fn env(mut self, env: I) -> Self - where - S: AsRef, - I: IntoIterator, - { - self.env = env - .into_iter() - .map(|(name, value)| (name.as_ref().to_string(), value.as_ref().to_string())) - .collect(); - self - } + pub fn env(mut self, env: I) -> Self + where + S: AsRef, + I: IntoIterator, + { + self.env = env + .into_iter() + .map(|(name, value)| (name.as_ref().to_string(), value.as_ref().to_string())) + .collect(); + self + } } #[derive(Debug)] pub struct GenerateFilesOptions { - pub commands: Vec, - pub image: Option, - pub injected_files: Vec, - // Allow to control the name of the node used to create the files. - pub temp_name: Option, - pub expected_path: Option, + pub commands: Vec, + pub image: Option, + pub injected_files: Vec, + // Allow to control the name of the node used to create the files. + pub temp_name: Option, + pub expected_path: Option, } impl GenerateFilesOptions { - pub fn new(commands: I, image: Option, expected_path: Option) -> Self - where - I: IntoIterator, - { - Self { - commands: commands.into_iter().collect(), - injected_files: vec![], - image, - temp_name: None, - expected_path, - } - } + pub fn new(commands: I, image: Option, expected_path: Option) -> Self + where + I: IntoIterator, + { + Self { + commands: commands.into_iter().collect(), + injected_files: vec![], + image, + temp_name: None, + expected_path, + } + } - pub fn with_files( - commands: I, - image: Option, - injected_files: &[TransferedFile], - expected_path: Option, - ) -> Self - where - I: IntoIterator, - { - Self { - commands: commands.into_iter().collect(), - injected_files: injected_files.into(), - image, - temp_name: None, - expected_path, - } - } + pub fn with_files( + commands: I, + image: Option, + injected_files: &[TransferedFile], + expected_path: Option, + ) -> Self + where + I: IntoIterator, + { + Self { + commands: commands.into_iter().collect(), + injected_files: injected_files.into(), + image, + temp_name: None, + expected_path, + } + } - pub fn image(mut self, image: S) -> Self - where - S: AsRef, - { - self.image = Some(image.as_ref().to_string()); - self - } + pub fn image(mut self, image: S) -> Self + where + S: AsRef, + { + self.image = Some(image.as_ref().to_string()); + self + } - pub fn injected_files(mut self, injected_files: I) -> Self - where - I: IntoIterator, - { - self.injected_files = injected_files.into_iter().collect(); - self - } + pub fn injected_files(mut self, injected_files: I) -> Self + where + I: IntoIterator, + { + self.injected_files = injected_files.into_iter().collect(); + self + } - pub fn temp_name(mut self, name: impl Into) -> Self { - self.temp_name = Some(name.into()); - self - } + pub fn temp_name(mut self, name: impl Into) -> Self { + self.temp_name = Some(name.into()); + self + } } #[derive(Debug)] pub struct RunCommandOptions { - pub program: String, - pub args: Vec, - pub env: Vec<(String, String)>, + pub program: String, + pub args: Vec, + pub env: Vec<(String, String)>, } impl RunCommandOptions { - pub fn new(program: S) -> Self - where - S: AsRef, - { - Self { - program: program.as_ref().to_string(), - args: vec![], - env: vec![], - } - } + pub fn new(program: S) -> Self + where + S: AsRef, + { + Self { program: program.as_ref().to_string(), args: vec![], env: vec![] } + } - pub fn args(mut self, args: I) -> Self - where - S: AsRef, - I: IntoIterator, - { - self.args = args.into_iter().map(|s| s.as_ref().to_string()).collect(); - self - } + pub fn args(mut self, args: I) -> Self + where + S: AsRef, + I: IntoIterator, + { + self.args = args.into_iter().map(|s| s.as_ref().to_string()).collect(); + self + } - pub fn env(mut self, env: I) -> Self - where - S: AsRef, - I: IntoIterator, - { - self.env = env - .into_iter() - .map(|(name, value)| (name.as_ref().to_string(), value.as_ref().to_string())) - .collect(); - self - } + pub fn env(mut self, env: I) -> Self + where + S: AsRef, + I: IntoIterator, + { + self.env = env + .into_iter() + .map(|(name, value)| (name.as_ref().to_string(), value.as_ref().to_string())) + .collect(); + self + } } pub struct RunScriptOptions { - pub local_script_path: PathBuf, - pub args: Vec, - pub env: Vec<(String, String)>, + pub local_script_path: PathBuf, + pub args: Vec, + pub env: Vec<(String, String)>, } impl RunScriptOptions { - pub fn new

(local_script_path: P) -> Self - where - P: AsRef, - { - Self { - local_script_path: local_script_path.as_ref().into(), - args: vec![], - env: vec![], - } - } + pub fn new

(local_script_path: P) -> Self + where + P: AsRef, + { + Self { local_script_path: local_script_path.as_ref().into(), args: vec![], env: vec![] } + } - pub fn args(mut self, args: I) -> Self - where - S: AsRef, - I: IntoIterator, - { - self.args = args.into_iter().map(|s| s.as_ref().to_string()).collect(); - self - } + pub fn args(mut self, args: I) -> Self + where + S: AsRef, + I: IntoIterator, + { + self.args = args.into_iter().map(|s| s.as_ref().to_string()).collect(); + self + } - pub fn env(mut self, env: I) -> Self - where - S: AsRef, - I: IntoIterator, - { - self.env = env - .into_iter() - .map(|(name, value)| (name.as_ref().to_string(), value.as_ref().to_string())) - .collect(); - self - } + pub fn env(mut self, env: I) -> Self + where + S: AsRef, + I: IntoIterator, + { + self.env = env + .into_iter() + .map(|(name, value)| (name.as_ref().to_string(), value.as_ref().to_string())) + .collect(); + self + } } // TODO(team): I think we can rename it to FileMap? #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TransferedFile { - pub local_path: PathBuf, - pub remote_path: PathBuf, - // TODO: Can be narrowed to have strict typing on this? - pub mode: String, + pub local_path: PathBuf, + pub remote_path: PathBuf, + // TODO: Can be narrowed to have strict typing on this? + pub mode: String, } impl TransferedFile { - pub fn new

(local_path: P, remote_path: P) -> Self - where - P: AsRef, - { - Self { - local_path: local_path.as_ref().into(), - remote_path: remote_path.as_ref().into(), - mode: "0644".to_string(), // default to rw-r--r-- - } - } + pub fn new

(local_path: P, remote_path: P) -> Self + where + P: AsRef, + { + Self { + local_path: local_path.as_ref().into(), + remote_path: remote_path.as_ref().into(), + mode: "0644".to_string(), // default to rw-r--r-- + } + } - pub fn mode(mut self, mode: S) -> Self - where - S: AsRef, - { - self.mode = mode.as_ref().to_string(); - self - } + pub fn mode(mut self, mode: S) -> Self + where + S: AsRef, + { + self.mode = mode.as_ref().to_string(); + self + } } impl std::fmt::Display for TransferedFile { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "File to transfer (local: {}, remote: {})", - self.local_path.display(), - self.remote_path.display() - ) - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "File to transfer (local: {}, remote: {})", + self.local_path.display(), + self.remote_path.display() + ) + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/sdk/src/environment.rs b/vendor/pezkuwi-zombienet-sdk/crates/sdk/src/environment.rs index 21eda890..399515ca 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/sdk/src/environment.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/sdk/src/environment.rs @@ -2,8 +2,8 @@ use std::{env, future::Future, path::PathBuf, pin::Pin}; use crate::{ - AttachToLive, AttachToLiveNetwork, LocalFileSystem, Network, NetworkConfig, NetworkConfigExt, - OrchestratorError, + AttachToLive, AttachToLiveNetwork, LocalFileSystem, Network, NetworkConfig, NetworkConfigExt, + OrchestratorError, }; const DEFAULT_POLKADOT_IMAGE: &str = "docker.io/parity/polkadot:latest"; @@ -11,80 +11,80 @@ const DEFAULT_CUMULUS_IMAGE: &str = "docker.io/parity/polkadot-parachain:latest" #[derive(Debug, Default)] pub struct Images { - pub polkadot: String, - pub cumulus: String, + pub polkadot: String, + pub cumulus: String, } impl Images { - /// Alias for polkadot field - returns reference to pezkuwi/polkadot image - pub fn pezkuwi(&self) -> &str { - &self.polkadot - } + /// Alias for polkadot field - returns reference to pezkuwi/polkadot image + pub fn pezkuwi(&self) -> &str { + &self.polkadot + } - /// Alias for cumulus field - returns reference to pezcumulus/cumulus image - pub fn pezcumulus(&self) -> &str { - &self.cumulus - } + /// Alias for cumulus field - returns reference to pezcumulus/cumulus image + pub fn pezcumulus(&self) -> &str { + &self.cumulus + } } pub enum Provider { - Native, - K8s, - Docker, + Native, + K8s, + Docker, } impl Provider { - pub fn get_spawn_fn( - &self, - ) -> fn(NetworkConfig) -> Pin + Send>> { - match self { - Provider::Native => NetworkConfigExt::spawn_native, - Provider::K8s => NetworkConfigExt::spawn_k8s, - Provider::Docker => NetworkConfigExt::spawn_docker, - } - } + pub fn get_spawn_fn( + &self, + ) -> fn(NetworkConfig) -> Pin + Send>> { + match self { + Provider::Native => NetworkConfigExt::spawn_native, + Provider::K8s => NetworkConfigExt::spawn_k8s, + Provider::Docker => NetworkConfigExt::spawn_docker, + } + } } // Use `docker` as default provider impl From for Provider { - fn from(value: String) -> Self { - match value.to_ascii_lowercase().as_ref() { - "native" => Provider::Native, - "k8s" => Provider::K8s, - _ => Provider::Docker, // default provider - } - } + fn from(value: String) -> Self { + match value.to_ascii_lowercase().as_ref() { + "native" => Provider::Native, + "k8s" => Provider::K8s, + _ => Provider::Docker, // default provider + } + } } pub fn get_images_from_env() -> Images { - let polkadot = env::var("POLKADOT_IMAGE").unwrap_or(DEFAULT_POLKADOT_IMAGE.into()); - let cumulus = env::var("CUMULUS_IMAGE").unwrap_or(DEFAULT_CUMULUS_IMAGE.into()); - Images { polkadot, cumulus } + let polkadot = env::var("POLKADOT_IMAGE").unwrap_or(DEFAULT_POLKADOT_IMAGE.into()); + let cumulus = env::var("CUMULUS_IMAGE").unwrap_or(DEFAULT_CUMULUS_IMAGE.into()); + Images { polkadot, cumulus } } pub fn get_provider_from_env() -> Provider { - env::var("ZOMBIE_PROVIDER").unwrap_or_default().into() + env::var("ZOMBIE_PROVIDER").unwrap_or_default().into() } pub type SpawnResult = Result, OrchestratorError>; pub fn get_spawn_fn() -> fn(NetworkConfig) -> Pin + Send>> { - let provider = get_provider_from_env(); + let provider = get_provider_from_env(); - match provider { - Provider::Native => NetworkConfigExt::spawn_native, - Provider::K8s => NetworkConfigExt::spawn_k8s, - Provider::Docker => NetworkConfigExt::spawn_docker, - } + match provider { + Provider::Native => NetworkConfigExt::spawn_native, + Provider::K8s => NetworkConfigExt::spawn_k8s, + Provider::Docker => NetworkConfigExt::spawn_docker, + } } pub type AttachResult = Result, OrchestratorError>; pub fn get_attach_fn() -> fn(PathBuf) -> Pin + Send>> { - let provider = get_provider_from_env(); + let provider = get_provider_from_env(); - match provider { - Provider::Native => AttachToLiveNetwork::attach_native, - Provider::K8s => AttachToLiveNetwork::attach_k8s, - Provider::Docker => AttachToLiveNetwork::attach_docker, - } + match provider { + Provider::Native => AttachToLiveNetwork::attach_native, + Provider::K8s => AttachToLiveNetwork::attach_k8s, + Provider::Docker => AttachToLiveNetwork::attach_docker, + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/sdk/src/lib.rs b/vendor/pezkuwi-zombienet-sdk/crates/sdk/src/lib.rs index 6797a1b0..474e7413 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/sdk/src/lib.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/sdk/src/lib.rs @@ -2,20 +2,20 @@ use std::path::PathBuf; use async_trait::async_trait; pub use configuration::{ - GlobalSettings, GlobalSettingsBuilder, NetworkConfig, NetworkConfigBuilder, - RegistrationStrategy, WithRelaychain, + GlobalSettings, GlobalSettingsBuilder, NetworkConfig, NetworkConfigBuilder, + RegistrationStrategy, WithRelaychain, }; pub use orchestrator::{ - errors::OrchestratorError, - network::{node::NetworkNode, Network}, - pezsc_chain_spec, AddCollatorOptions, AddNodeOptions, Orchestrator, + errors::OrchestratorError, + network::{node::NetworkNode, Network}, + pezsc_chain_spec, AddCollatorOptions, AddNodeOptions, Orchestrator, }; // Helpers used for interact with the network pub mod tx_helper { - pub use orchestrator::{ - network::chain_upgrade::ChainUpgrade, shared::types::RuntimeUpgradeOptions, - }; + pub use orchestrator::{ + network::chain_upgrade::ChainUpgrade, shared::types::RuntimeUpgradeOptions, + }; } use provider::{DockerProvider, KubernetesProvider, NativeProvider}; @@ -32,100 +32,100 @@ pub use pezkuwi_subxt_signer as subxt_signer; #[async_trait] pub trait NetworkConfigExt { - /// Spawns a network using the native or k8s provider. - /// - /// # Example: - /// ```rust - /// # use zombienet_sdk::{NetworkConfig, NetworkConfigExt}; - /// # async fn example() -> Result<(), zombienet_sdk::OrchestratorError> { - /// let network = NetworkConfig::load_from_toml("config.toml")? - /// .spawn_native() - /// .await?; - /// # Ok(()) - /// # } - /// ``` - async fn spawn_native(self) -> Result, OrchestratorError>; - async fn spawn_k8s(self) -> Result, OrchestratorError>; - async fn spawn_docker(self) -> Result, OrchestratorError>; + /// Spawns a network using the native or k8s provider. + /// + /// # Example: + /// ```rust + /// # use zombienet_sdk::{NetworkConfig, NetworkConfigExt}; + /// # async fn example() -> Result<(), zombienet_sdk::OrchestratorError> { + /// let network = NetworkConfig::load_from_toml("config.toml")? + /// .spawn_native() + /// .await?; + /// # Ok(()) + /// # } + /// ``` + async fn spawn_native(self) -> Result, OrchestratorError>; + async fn spawn_k8s(self) -> Result, OrchestratorError>; + async fn spawn_docker(self) -> Result, OrchestratorError>; } #[async_trait] pub trait AttachToLive { - /// Attaches to a running live network using the native, docker or k8s provider. - /// - /// # Example: - /// ```rust - /// # use zombienet_sdk::{AttachToLive, AttachToLiveNetwork}; - /// # use std::path::PathBuf; - /// # async fn example() -> Result<(), zombienet_sdk::OrchestratorError> { - /// let zombie_json_path = PathBuf::from("some/path/zombie.json"); - /// let network = AttachToLiveNetwork::attach_native(zombie_json_path).await?; - /// # Ok(()) - /// # } - /// ``` - async fn attach_native( - zombie_json_path: PathBuf, - ) -> Result, OrchestratorError>; - async fn attach_k8s( - zombie_json_path: PathBuf, - ) -> Result, OrchestratorError>; - async fn attach_docker( - zombie_json_path: PathBuf, - ) -> Result, OrchestratorError>; + /// Attaches to a running live network using the native, docker or k8s provider. + /// + /// # Example: + /// ```rust + /// # use zombienet_sdk::{AttachToLive, AttachToLiveNetwork}; + /// # use std::path::PathBuf; + /// # async fn example() -> Result<(), zombienet_sdk::OrchestratorError> { + /// let zombie_json_path = PathBuf::from("some/path/zombie.json"); + /// let network = AttachToLiveNetwork::attach_native(zombie_json_path).await?; + /// # Ok(()) + /// # } + /// ``` + async fn attach_native( + zombie_json_path: PathBuf, + ) -> Result, OrchestratorError>; + async fn attach_k8s( + zombie_json_path: PathBuf, + ) -> Result, OrchestratorError>; + async fn attach_docker( + zombie_json_path: PathBuf, + ) -> Result, OrchestratorError>; } #[async_trait] impl NetworkConfigExt for NetworkConfig { - async fn spawn_native(self) -> Result, OrchestratorError> { - let filesystem = LocalFileSystem; - let provider = NativeProvider::new(filesystem.clone()); - let orchestrator = Orchestrator::new(filesystem, provider); - orchestrator.spawn(self).await - } + async fn spawn_native(self) -> Result, OrchestratorError> { + let filesystem = LocalFileSystem; + let provider = NativeProvider::new(filesystem.clone()); + let orchestrator = Orchestrator::new(filesystem, provider); + orchestrator.spawn(self).await + } - async fn spawn_k8s(self) -> Result, OrchestratorError> { - let filesystem = LocalFileSystem; - let provider = KubernetesProvider::new(filesystem.clone()).await; - let orchestrator = Orchestrator::new(filesystem, provider); - orchestrator.spawn(self).await - } + async fn spawn_k8s(self) -> Result, OrchestratorError> { + let filesystem = LocalFileSystem; + let provider = KubernetesProvider::new(filesystem.clone()).await; + let orchestrator = Orchestrator::new(filesystem, provider); + orchestrator.spawn(self).await + } - async fn spawn_docker(self) -> Result, OrchestratorError> { - let filesystem = LocalFileSystem; - let provider = DockerProvider::new(filesystem.clone()).await; - let orchestrator = Orchestrator::new(filesystem, provider); - orchestrator.spawn(self).await - } + async fn spawn_docker(self) -> Result, OrchestratorError> { + let filesystem = LocalFileSystem; + let provider = DockerProvider::new(filesystem.clone()).await; + let orchestrator = Orchestrator::new(filesystem, provider); + orchestrator.spawn(self).await + } } pub struct AttachToLiveNetwork; #[async_trait] impl AttachToLive for AttachToLiveNetwork { - async fn attach_native( - zombie_json_path: PathBuf, - ) -> Result, OrchestratorError> { - let filesystem = LocalFileSystem; - let provider = NativeProvider::new(filesystem.clone()); - let orchestrator = Orchestrator::new(filesystem, provider); - orchestrator.attach_to_live(zombie_json_path.as_ref()).await - } + async fn attach_native( + zombie_json_path: PathBuf, + ) -> Result, OrchestratorError> { + let filesystem = LocalFileSystem; + let provider = NativeProvider::new(filesystem.clone()); + let orchestrator = Orchestrator::new(filesystem, provider); + orchestrator.attach_to_live(zombie_json_path.as_ref()).await + } - async fn attach_k8s( - zombie_json_path: PathBuf, - ) -> Result, OrchestratorError> { - let filesystem = LocalFileSystem; - let provider = KubernetesProvider::new(filesystem.clone()).await; - let orchestrator = Orchestrator::new(filesystem, provider); - orchestrator.attach_to_live(zombie_json_path.as_ref()).await - } + async fn attach_k8s( + zombie_json_path: PathBuf, + ) -> Result, OrchestratorError> { + let filesystem = LocalFileSystem; + let provider = KubernetesProvider::new(filesystem.clone()).await; + let orchestrator = Orchestrator::new(filesystem, provider); + orchestrator.attach_to_live(zombie_json_path.as_ref()).await + } - async fn attach_docker( - zombie_json_path: PathBuf, - ) -> Result, OrchestratorError> { - let filesystem = LocalFileSystem; - let provider = DockerProvider::new(filesystem.clone()).await; - let orchestrator = Orchestrator::new(filesystem, provider); - orchestrator.attach_to_live(zombie_json_path.as_ref()).await - } + async fn attach_docker( + zombie_json_path: PathBuf, + ) -> Result, OrchestratorError> { + let filesystem = LocalFileSystem; + let provider = DockerProvider::new(filesystem.clone()).await; + let orchestrator = Orchestrator::new(filesystem, provider); + orchestrator.attach_to_live(zombie_json_path.as_ref()).await + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/chain_spec_runtime_omni_node.rs b/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/chain_spec_runtime_omni_node.rs index 6a4f5106..e8629c96 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/chain_spec_runtime_omni_node.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/chain_spec_runtime_omni_node.rs @@ -5,9 +5,9 @@ const BEST_BLOCK_METRIC: &str = "block_height{status=\"best\"}"; #[tokio::test(flavor = "multi_thread")] async fn rococo_local_with_omni_node_and_wasm_runtime() { - let _ = tracing_subscriber::fmt::try_init(); + let _ = tracing_subscriber::fmt::try_init(); - let config = NetworkConfigBuilder::new() + let config = NetworkConfigBuilder::new() .with_relaychain(|relaychain| { relaychain .with_chain("rococo-local") @@ -29,47 +29,32 @@ async fn rococo_local_with_omni_node_and_wasm_runtime() { .build() .unwrap(); - let spawn_fn = get_spawn_fn(); - let network = spawn_fn(config).await.unwrap(); + let spawn_fn = get_spawn_fn(); + let network = spawn_fn(config).await.unwrap(); - println!("🚀🚀🚀🚀 network deployed"); + println!("🚀🚀🚀🚀 network deployed"); - // wait 2 blocks - let alice = network.get_node("alice").unwrap(); - assert!(alice - .wait_metric(BEST_BLOCK_METRIC, |b| b > 2_f64) - .await - .is_ok()); + // wait 2 blocks + let alice = network.get_node("alice").unwrap(); + assert!(alice.wait_metric(BEST_BLOCK_METRIC, |b| b > 2_f64).await.is_ok()); - // omni-collator-1 - let collator = network.get_node("omni-collator-1").unwrap(); - let client = collator - .wait_client::() - .await - .unwrap(); + // omni-collator-1 + let collator = network.get_node("omni-collator-1").unwrap(); + let client = collator.wait_client::().await.unwrap(); - // wait 1 blocks - let mut blocks = client.blocks().subscribe_finalized().await.unwrap().take(1); - while let Some(block) = blocks.next().await { - println!( - "Block (omni-collator-1) #{}", - block.unwrap().header().number - ); - } + // wait 1 blocks + let mut blocks = client.blocks().subscribe_finalized().await.unwrap().take(1); + while let Some(block) = blocks.next().await { + println!("Block (omni-collator-1) #{}", block.unwrap().header().number); + } - // omni-collator-2 - let collator = network.get_node("omni-collator-2").unwrap(); - let client = collator - .wait_client::() - .await - .unwrap(); + // omni-collator-2 + let collator = network.get_node("omni-collator-2").unwrap(); + let client = collator.wait_client::().await.unwrap(); - // wait 1 blocks - let mut blocks = client.blocks().subscribe_finalized().await.unwrap().take(1); - while let Some(block) = blocks.next().await { - println!( - "Block (omni-collator-2) #{}", - block.unwrap().header().number - ); - } + // wait 1 blocks + let mut blocks = client.blocks().subscribe_finalized().await.unwrap().take(1); + while let Some(block) = blocks.next().await { + println!("Block (omni-collator-2) #{}", block.unwrap().header().number); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/chain_spec_runtime_polkadot.rs b/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/chain_spec_runtime_polkadot.rs index 1aba06d5..ab726291 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/chain_spec_runtime_polkadot.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/chain_spec_runtime_polkadot.rs @@ -5,9 +5,9 @@ const BEST_BLOCK_METRIC: &str = "block_height{status=\"best\"}"; #[tokio::test(flavor = "multi_thread")] async fn polkadot_local_with_chain_spec_runtime() { - let _ = tracing_subscriber::fmt::try_init(); + let _ = tracing_subscriber::fmt::try_init(); - let config = NetworkConfigBuilder::new() + let config = NetworkConfigBuilder::new() .with_relaychain(|relaychain| { relaychain .with_chain("polkadot-local") @@ -30,47 +30,32 @@ async fn polkadot_local_with_chain_spec_runtime() { .build() .unwrap(); - let spawn_fn = get_spawn_fn(); - let network = spawn_fn(config).await.unwrap(); + let spawn_fn = get_spawn_fn(); + let network = spawn_fn(config).await.unwrap(); - println!("🚀🚀🚀🚀 network deployed"); + println!("🚀🚀🚀🚀 network deployed"); - // wait 2 blocks - let alice = network.get_node("alice").unwrap(); - assert!(alice - .wait_metric(BEST_BLOCK_METRIC, |b| b > 2_f64) - .await - .is_ok()); + // wait 2 blocks + let alice = network.get_node("alice").unwrap(); + assert!(alice.wait_metric(BEST_BLOCK_METRIC, |b| b > 2_f64).await.is_ok()); - // asset-hub-collator-1 - let collator = network.get_node("asset-hub-collator-1").unwrap(); - let client = collator - .wait_client::() - .await - .unwrap(); + // asset-hub-collator-1 + let collator = network.get_node("asset-hub-collator-1").unwrap(); + let client = collator.wait_client::().await.unwrap(); - // wait 1 blocks - let mut blocks = client.blocks().subscribe_finalized().await.unwrap().take(1); - while let Some(block) = blocks.next().await { - println!( - "Block (asset-hub-collator-1) #{}", - block.unwrap().header().number - ); - } + // wait 1 blocks + let mut blocks = client.blocks().subscribe_finalized().await.unwrap().take(1); + while let Some(block) = blocks.next().await { + println!("Block (asset-hub-collator-1) #{}", block.unwrap().header().number); + } - // asset-hub-collator-2 - let collator = network.get_node("asset-hub-collator-2").unwrap(); - let client = collator - .wait_client::() - .await - .unwrap(); + // asset-hub-collator-2 + let collator = network.get_node("asset-hub-collator-2").unwrap(); + let client = collator.wait_client::().await.unwrap(); - // wait 1 blocks - let mut blocks = client.blocks().subscribe_finalized().await.unwrap().take(1); - while let Some(block) = blocks.next().await { - println!( - "Block (asset-hub-collator-2) #{}", - block.unwrap().header().number - ); - } + // wait 1 blocks + let mut blocks = client.blocks().subscribe_finalized().await.unwrap().take(1); + while let Some(block) = blocks.next().await { + println!("Block (asset-hub-collator-2) #{}", block.unwrap().header().number); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/smoke-native.rs b/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/smoke-native.rs index b21f0195..75c8f366 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/smoke-native.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/smoke-native.rs @@ -4,7 +4,7 @@ use configuration::{NetworkConfig, NetworkConfigBuilder}; use zombienet_sdk::environment::get_spawn_fn; fn small_network() -> NetworkConfig { - NetworkConfigBuilder::new() + NetworkConfigBuilder::new() .with_relaychain(|r| { r.with_chain("rococo-local") .with_default_command("polkadot") @@ -31,27 +31,24 @@ fn small_network() -> NetworkConfig { #[tokio::test(flavor = "multi_thread")] async fn ci_native_smoke_should_works() { - tracing_subscriber::fmt::init(); - const BEST_BLOCK_METRIC: &str = "block_height{status=\"best\"}"; - let now = Instant::now(); - let config = small_network(); - let spawn_fn = get_spawn_fn(); + tracing_subscriber::fmt::init(); + const BEST_BLOCK_METRIC: &str = "block_height{status=\"best\"}"; + let now = Instant::now(); + let config = small_network(); + let spawn_fn = get_spawn_fn(); - let network = spawn_fn(config).await.unwrap(); + let network = spawn_fn(config).await.unwrap(); - let elapsed = now.elapsed(); - println!("🚀🚀🚀🚀 network deployed in {elapsed:.2?}"); + let elapsed = now.elapsed(); + println!("🚀🚀🚀🚀 network deployed in {elapsed:.2?}"); - network.wait_until_is_up(20).await.unwrap(); + network.wait_until_is_up(20).await.unwrap(); - let elapsed = now.elapsed(); - println!("✅✅✅✅ network is up in {elapsed:.2?}"); + let elapsed = now.elapsed(); + println!("✅✅✅✅ network is up in {elapsed:.2?}"); - // Get a ref to the node - let alice = network.get_node("alice").unwrap(); - // wait 10 blocks - alice - .wait_metric(BEST_BLOCK_METRIC, |x| x > 9_f64) - .await - .unwrap(); + // Get a ref to the node + let alice = network.get_node("alice").unwrap(); + // wait 10 blocks + alice.wait_metric(BEST_BLOCK_METRIC, |x| x > 9_f64).await.unwrap(); } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/smoke.rs b/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/smoke.rs index 4c7ac263..b4c28897 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/smoke.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/smoke.rs @@ -6,174 +6,146 @@ use orchestrator::{AddCollatorOptions, AddNodeOptions}; use zombienet_sdk::environment::{get_attach_fn, get_spawn_fn}; fn small_network() -> NetworkConfig { - NetworkConfigBuilder::new() - .with_relaychain(|r| { - r.with_chain("rococo-local") - .with_default_command("polkadot") - .with_default_image("docker.io/parity/polkadot:v1.20.2") - .with_validator(|node| node.with_name("alice")) - .with_validator(|node| node.with_name("bob")) - }) - .with_parachain(|p| { - p.with_id(2000).cumulus_based(true).with_collator(|n| { - n.with_name("collator") - .with_command("polkadot-parachain") - .with_image("docker.io/parity/polkadot-parachain:1.7.0") - }) - }) - .with_parachain(|p| { - p.with_id(3000).cumulus_based(true).with_collator(|n| { - n.with_name("collator-new") - .with_command("polkadot-parachain") - .with_image("docker.io/parity/polkadot-parachain:v1.20.2") - }) - }) - .with_global_settings(|g| { - g.with_base_dir(PathBuf::from("/tmp/zombie-1")) - .with_tear_down_on_failure(false) - }) - .build() - .unwrap() + NetworkConfigBuilder::new() + .with_relaychain(|r| { + r.with_chain("rococo-local") + .with_default_command("polkadot") + .with_default_image("docker.io/parity/polkadot:v1.20.2") + .with_validator(|node| node.with_name("alice")) + .with_validator(|node| node.with_name("bob")) + }) + .with_parachain(|p| { + p.with_id(2000).cumulus_based(true).with_collator(|n| { + n.with_name("collator") + .with_command("polkadot-parachain") + .with_image("docker.io/parity/polkadot-parachain:1.7.0") + }) + }) + .with_parachain(|p| { + p.with_id(3000).cumulus_based(true).with_collator(|n| { + n.with_name("collator-new") + .with_command("polkadot-parachain") + .with_image("docker.io/parity/polkadot-parachain:v1.20.2") + }) + }) + .with_global_settings(|g| { + g.with_base_dir(PathBuf::from("/tmp/zombie-1")).with_tear_down_on_failure(false) + }) + .build() + .unwrap() } #[tokio::test(flavor = "multi_thread")] async fn ci_k8s_basic_functionalities_should_works() { - let _ = tracing_subscriber::fmt::try_init(); + let _ = tracing_subscriber::fmt::try_init(); - const BEST_BLOCK_METRIC: &str = "block_height{status=\"best\"}"; - let now = Instant::now(); + const BEST_BLOCK_METRIC: &str = "block_height{status=\"best\"}"; + let now = Instant::now(); - let config = small_network(); - let spawn_fn = get_spawn_fn(); + let config = small_network(); + let spawn_fn = get_spawn_fn(); - let network = spawn_fn(config).await.unwrap(); + let network = spawn_fn(config).await.unwrap(); - let elapsed = now.elapsed(); - println!("🚀🚀🚀🚀 network deployed in {elapsed:.2?}"); + let elapsed = now.elapsed(); + println!("🚀🚀🚀🚀 network deployed in {elapsed:.2?}"); - // detach and attach to running - network.detach().await; - drop(network); - let attach_fn = get_attach_fn(); - let zombie_path = PathBuf::from("/tmp/zombie-1/zombie.json"); - let mut network = attach_fn(zombie_path).await.unwrap(); + // detach and attach to running + network.detach().await; + drop(network); + let attach_fn = get_attach_fn(); + let zombie_path = PathBuf::from("/tmp/zombie-1/zombie.json"); + let mut network = attach_fn(zombie_path).await.unwrap(); - // Get a ref to the node - let alice = network.get_node("alice").unwrap(); + // Get a ref to the node + let alice = network.get_node("alice").unwrap(); - let (_best_block_pass, client) = try_join!( - alice.wait_metric(BEST_BLOCK_METRIC, |x| x > 5_f64), - alice.wait_client::() - ) - .unwrap(); + let (_best_block_pass, client) = try_join!( + alice.wait_metric(BEST_BLOCK_METRIC, |x| x > 5_f64), + alice.wait_client::() + ) + .unwrap(); - alice - .wait_log_line_count("*rted #1*", true, 10) - .await - .unwrap(); + alice.wait_log_line_count("*rted #1*", true, 10).await.unwrap(); - // check best block through metrics with timeout - assert!(alice - .wait_metric_with_timeout(BEST_BLOCK_METRIC, |x| x > 10_f64, 45_u32) - .await - .is_ok()); + // check best block through metrics with timeout + assert!(alice + .wait_metric_with_timeout(BEST_BLOCK_METRIC, |x| x > 10_f64, 45_u32) + .await + .is_ok()); - // ensure timeout error - let best_block = alice.reports(BEST_BLOCK_METRIC).await.unwrap(); - let res = alice - .wait_metric_with_timeout(BEST_BLOCK_METRIC, |x| x > (best_block * 2_f64), 10_u32) - .await; + // ensure timeout error + let best_block = alice.reports(BEST_BLOCK_METRIC).await.unwrap(); + let res = alice + .wait_metric_with_timeout(BEST_BLOCK_METRIC, |x| x > (best_block * 2_f64), 10_u32) + .await; - assert!(res.is_err()); + assert!(res.is_err()); - // get single metric - let role = alice.reports("node_roles").await.unwrap(); - println!("Role is {role}"); - assert_eq!(role, 4.0); + // get single metric + let role = alice.reports("node_roles").await.unwrap(); + println!("Role is {role}"); + assert_eq!(role, 4.0); - // subxt - // wait 3 blocks - let mut blocks = client.blocks().subscribe_finalized().await.unwrap().take(3); - while let Some(block) = blocks.next().await { - println!("Block #{}", block.unwrap().header().number); - } + // subxt + // wait 3 blocks + let mut blocks = client.blocks().subscribe_finalized().await.unwrap().take(3); + while let Some(block) = blocks.next().await { + println!("Block #{}", block.unwrap().header().number); + } - // drop the client - drop(client); + // drop the client + drop(client); - // check best block through metrics - let best_block = alice - .reports("block_height{status=\"best\"}") - .await - .unwrap(); + // check best block through metrics + let best_block = alice.reports("block_height{status=\"best\"}").await.unwrap(); - assert!(best_block >= 2.0, "Current best {best_block}"); + assert!(best_block >= 2.0, "Current best {best_block}"); - // collator - let collator = network.get_node("collator").unwrap(); - let client = collator - .wait_client::() - .await - .unwrap(); + // collator + let collator = network.get_node("collator").unwrap(); + let client = collator.wait_client::().await.unwrap(); - // wait 3 blocks - let mut blocks = client.blocks().subscribe_finalized().await.unwrap().take(3); - while let Some(block) = blocks.next().await { - println!("Block (para) #{}", block.unwrap().header().number); - } + // wait 3 blocks + let mut blocks = client.blocks().subscribe_finalized().await.unwrap().take(3); + while let Some(block) = blocks.next().await { + println!("Block (para) #{}", block.unwrap().header().number); + } - // add node - let opts = AddNodeOptions { - rpc_port: Some(9444), - is_validator: true, - ..Default::default() - }; + // add node + let opts = AddNodeOptions { rpc_port: Some(9444), is_validator: true, ..Default::default() }; - network.add_node("new1", opts).await.unwrap(); + network.add_node("new1", opts).await.unwrap(); - // add collator - let col_opts = AddCollatorOptions { - command: Some("polkadot-parachain".try_into().unwrap()), - image: Some( - "docker.io/parity/polkadot-parachain:1.7.0" - .try_into() - .unwrap(), - ), - ..Default::default() - }; + // add collator + let col_opts = AddCollatorOptions { + command: Some("polkadot-parachain".try_into().unwrap()), + image: Some("docker.io/parity/polkadot-parachain:1.7.0".try_into().unwrap()), + ..Default::default() + }; - network - .add_collator("new-col-1", col_opts, 2000) - .await - .unwrap(); + network.add_collator("new-col-1", col_opts, 2000).await.unwrap(); - // pause / resume - let alice = network.get_node("alice").unwrap(); - alice.pause().await.unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + // pause / resume + let alice = network.get_node("alice").unwrap(); + alice.pause().await.unwrap(); + tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; - let res_err = alice - .wait_metric_with_timeout(BEST_BLOCK_METRIC, |x| x > 5_f64, 5_u32) - .await; + let res_err = alice.wait_metric_with_timeout(BEST_BLOCK_METRIC, |x| x > 5_f64, 5_u32).await; - assert!(res_err.is_err()); + assert!(res_err.is_err()); - alice.resume().await.unwrap(); - alice - .wait_metric_with_timeout(BEST_BLOCK_METRIC, |x| x > 5_f64, 5_u32) - .await - .unwrap(); + alice.resume().await.unwrap(); + alice.wait_metric_with_timeout(BEST_BLOCK_METRIC, |x| x > 5_f64, 5_u32).await.unwrap(); - // timeout connecting ws - let collator = network.get_node("collator").unwrap(); - collator.pause().await.unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + // timeout connecting ws + let collator = network.get_node("collator").unwrap(); + collator.pause().await.unwrap(); + tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; - let r = collator - .wait_client_with_timeout::(1_u32) - .await; - assert!(r.is_err()); + let r = collator.wait_client_with_timeout::(1_u32).await; + assert!(r.is_err()); - // tear down (optional if you don't detach the network) - network.destroy().await.unwrap(); + // tear down (optional if you don't detach the network) + network.destroy().await.unwrap(); } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/two-paras-same-id.rs b/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/two-paras-same-id.rs index 9273dadf..7b464ecc 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/two-paras-same-id.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/sdk/tests/two-paras-same-id.rs @@ -2,43 +2,43 @@ use zombienet_sdk::{environment::get_spawn_fn, NetworkConfigBuilder}; #[tokio::test(flavor = "multi_thread")] async fn two_paras_same_id() { - tracing_subscriber::fmt::init(); - let spawn_fn = get_spawn_fn(); - let config = NetworkConfigBuilder::new() - .with_relaychain(|r| { - r.with_chain("rococo-local") - .with_default_command("polkadot") - .with_default_image("docker.io/parity/polkadot:v1.7.0") - .with_validator(|node| node.with_name("alice")) - .with_validator(|node| node.with_name("bob")) - }) - .with_parachain(|p| { - p.with_id(2000) - .with_default_command("polkadot-parachain") - .with_default_image("docker.io/parity/polkadot-parachain:1.7.0") - .with_collator(|n| n.with_name("collator")) - }) - .with_parachain(|p| { - p.with_id(2000) - .with_default_command("polkadot-parachain") - .with_default_image("docker.io/parity/polkadot-parachain:1.7.0") - .with_registration_strategy(zombienet_sdk::RegistrationStrategy::Manual) - .with_collator(|n| n.with_name("collator1")) - }) - .build() - .unwrap(); + tracing_subscriber::fmt::init(); + let spawn_fn = get_spawn_fn(); + let config = NetworkConfigBuilder::new() + .with_relaychain(|r| { + r.with_chain("rococo-local") + .with_default_command("polkadot") + .with_default_image("docker.io/parity/polkadot:v1.7.0") + .with_validator(|node| node.with_name("alice")) + .with_validator(|node| node.with_name("bob")) + }) + .with_parachain(|p| { + p.with_id(2000) + .with_default_command("polkadot-parachain") + .with_default_image("docker.io/parity/polkadot-parachain:1.7.0") + .with_collator(|n| n.with_name("collator")) + }) + .with_parachain(|p| { + p.with_id(2000) + .with_default_command("polkadot-parachain") + .with_default_image("docker.io/parity/polkadot-parachain:1.7.0") + .with_registration_strategy(zombienet_sdk::RegistrationStrategy::Manual) + .with_collator(|n| n.with_name("collator1")) + }) + .build() + .unwrap(); - let network = spawn_fn(config).await.unwrap(); + let network = spawn_fn(config).await.unwrap(); - assert!(network.get_node("collator").is_ok()); - assert!(network.get_node("collator1").is_ok()); + assert!(network.get_node("collator").is_ok()); + assert!(network.get_node("collator1").is_ok()); - // First parachain (out of two) is fetched - assert_eq!(network.parachain(2000).unwrap().unique_id(), "2000"); + // First parachain (out of two) is fetched + assert_eq!(network.parachain(2000).unwrap().unique_id(), "2000"); - // First and second parachain hav the same para_id - assert_eq!( - network.parachain_by_unique_id("2000").unwrap().para_id(), - network.parachain_by_unique_id("2000-1").unwrap().para_id(), - ); + // First and second parachain hav the same para_id + assert_eq!( + network.parachain_by_unique_id("2000").unwrap().para_id(), + network.parachain_by_unique_id("2000-1").unwrap().para_id(), + ); } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/support/src/constants.rs b/vendor/pezkuwi-zombienet-sdk/crates/support/src/constants.rs index 94203589..79585586 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/support/src/constants.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/support/src/constants.rs @@ -11,14 +11,14 @@ pub const VALIDATION_CHECK: &str = "validation failed "; pub const PREFIX_CANT_BE_NONE: &str = "name prefix can't be None if a value exists "; pub const GRAPH_CONTAINS_NAME: &str = - "graph contains node name; we initialize it with all node names"; + "graph contains node name; we initialize it with all node names"; pub const GRAPH_CONTAINS_DEP: &str = "graph contains dep_name; we filter out deps not contained in by_name and populate the graph with all nodes"; pub const INDEGREE_CONTAINS_NAME: &str = - "indegree contains node name; we initialize it with all node names"; + "indegree contains node name; we initialize it with all node names"; pub const QUEUE_NOT_EMPTY: &str = "queue is not empty; we're looping over its length"; pub const THIS_IS_A_BUG: &str = - "- this is a bug please report it: https://github.com/paritytech/zombienet-sdk/issues"; + "- this is a bug please report it: https://github.com/paritytech/zombienet-sdk/issues"; /// environment variable which can be used to override node spawn timeout pub const ZOMBIE_NODE_SPAWN_TIMEOUT_SECONDS: &str = "ZOMBIE_NODE_SPAWN_TIMEOUT_SECONDS"; diff --git a/vendor/pezkuwi-zombienet-sdk/crates/support/src/fs.rs b/vendor/pezkuwi-zombienet-sdk/crates/support/src/fs.rs index 952486e0..a05dadcf 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/support/src/fs.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/support/src/fs.rs @@ -10,51 +10,51 @@ pub mod local; pub struct FileSystemError(#[from] anyhow::Error); impl From for FileSystemError { - fn from(error: std::io::Error) -> Self { - Self(error.into()) - } + fn from(error: std::io::Error) -> Self { + Self(error.into()) + } } pub type FileSystemResult = Result; #[async_trait] pub trait FileSystem { - async fn create_dir

(&self, path: P) -> FileSystemResult<()> - where - P: AsRef + Send; + async fn create_dir

(&self, path: P) -> FileSystemResult<()> + where + P: AsRef + Send; - async fn create_dir_all

(&self, path: P) -> FileSystemResult<()> - where - P: AsRef + Send; + async fn create_dir_all

(&self, path: P) -> FileSystemResult<()> + where + P: AsRef + Send; - async fn read

(&self, path: P) -> FileSystemResult> - where - P: AsRef + Send; + async fn read

(&self, path: P) -> FileSystemResult> + where + P: AsRef + Send; - async fn read_to_string

(&self, path: P) -> FileSystemResult - where - P: AsRef + Send; + async fn read_to_string

(&self, path: P) -> FileSystemResult + where + P: AsRef + Send; - async fn write(&self, path: P, contents: C) -> FileSystemResult<()> - where - P: AsRef + Send, - C: AsRef<[u8]> + Send; + async fn write(&self, path: P, contents: C) -> FileSystemResult<()> + where + P: AsRef + Send, + C: AsRef<[u8]> + Send; - async fn append(&self, path: P, contents: C) -> FileSystemResult<()> - where - P: AsRef + Send, - C: AsRef<[u8]> + Send; + async fn append(&self, path: P, contents: C) -> FileSystemResult<()> + where + P: AsRef + Send, + C: AsRef<[u8]> + Send; - async fn copy(&self, from: P1, to: P2) -> FileSystemResult<()> - where - P1: AsRef + Send, - P2: AsRef + Send; + async fn copy(&self, from: P1, to: P2) -> FileSystemResult<()> + where + P1: AsRef + Send, + P2: AsRef + Send; - async fn set_mode

(&self, path: P, perm: u32) -> FileSystemResult<()> - where - P: AsRef + Send; + async fn set_mode

(&self, path: P, perm: u32) -> FileSystemResult<()> + where + P: AsRef + Send; - async fn exists

(&self, path: P) -> bool - where - P: AsRef + Send; + async fn exists

(&self, path: P) -> bool + where + P: AsRef + Send; } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/support/src/fs/in_memory.rs b/vendor/pezkuwi-zombienet-sdk/crates/support/src/fs/in_memory.rs index 727b14a7..4633fdb7 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/support/src/fs/in_memory.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/support/src/fs/in_memory.rs @@ -8,872 +8,797 @@ use super::{FileSystem, FileSystemResult}; #[derive(Debug, Clone, PartialEq)] pub enum InMemoryFile { - File { mode: u32, contents: Vec }, - Directory { mode: u32 }, + File { mode: u32, contents: Vec }, + Directory { mode: u32 }, } impl InMemoryFile { - pub fn file(contents: C) -> Self - where - C: AsRef, - { - Self::file_raw(contents.as_ref()) - } + pub fn file(contents: C) -> Self + where + C: AsRef, + { + Self::file_raw(contents.as_ref()) + } - pub fn file_raw(contents: C) -> Self - where - C: AsRef<[u8]>, - { - Self::File { - mode: 0o664, - contents: contents.as_ref().to_vec(), - } - } + pub fn file_raw(contents: C) -> Self + where + C: AsRef<[u8]>, + { + Self::File { mode: 0o664, contents: contents.as_ref().to_vec() } + } - pub fn empty() -> Self { - Self::file_raw(vec![]) - } + pub fn empty() -> Self { + Self::file_raw(vec![]) + } - pub fn dir() -> Self { - Self::Directory { mode: 0o775 } - } + pub fn dir() -> Self { + Self::Directory { mode: 0o775 } + } - pub fn mode(&self) -> u32 { - match *self { - Self::File { mode, .. } => mode, - Self::Directory { mode, .. } => mode, - } - } + pub fn mode(&self) -> u32 { + match *self { + Self::File { mode, .. } => mode, + Self::Directory { mode, .. } => mode, + } + } - pub fn contents_raw(&self) -> Option> { - match self { - Self::File { contents, .. } => Some(contents.to_vec()), - Self::Directory { .. } => None, - } - } + pub fn contents_raw(&self) -> Option> { + match self { + Self::File { contents, .. } => Some(contents.to_vec()), + Self::Directory { .. } => None, + } + } - pub fn contents(&self) -> Option { - match self { - Self::File { contents, .. } => Some(String::from_utf8_lossy(contents).to_string()), - Self::Directory { .. } => None, - } - } + pub fn contents(&self) -> Option { + match self { + Self::File { contents, .. } => Some(String::from_utf8_lossy(contents).to_string()), + Self::Directory { .. } => None, + } + } } #[derive(Default, Debug, Clone)] pub struct InMemoryFileSystem { - pub files: Arc>>, + pub files: Arc>>, } impl InMemoryFileSystem { - pub fn new(files: HashMap) -> Self { - Self { - files: Arc::new(RwLock::new(files)), - } - } + pub fn new(files: HashMap) -> Self { + Self { files: Arc::new(RwLock::new(files)) } + } } #[async_trait] impl FileSystem for InMemoryFileSystem { - async fn create_dir

(&self, path: P) -> FileSystemResult<()> - where - P: AsRef + Send, - { - let path = path.as_ref(); - let os_path = path.as_os_str(); - match self.files.read().await.get(os_path) { - Some(InMemoryFile::File { .. }) => { - Err(anyhow!("file {:?} already exists", os_path.to_owned(),))? - }, - Some(InMemoryFile::Directory { .. }) => { - Err(anyhow!("directory {:?} already exists", os_path.to_owned(),))? - }, - None => {}, - }; + async fn create_dir

(&self, path: P) -> FileSystemResult<()> + where + P: AsRef + Send, + { + let path = path.as_ref(); + let os_path = path.as_os_str(); + match self.files.read().await.get(os_path) { + Some(InMemoryFile::File { .. }) => { + Err(anyhow!("file {:?} already exists", os_path.to_owned(),))? + }, + Some(InMemoryFile::Directory { .. }) => { + Err(anyhow!("directory {:?} already exists", os_path.to_owned(),))? + }, + None => {}, + }; - for path in path.ancestors().skip(1) { - match self.files.read().await.get(path.as_os_str()) { - Some(InMemoryFile::Directory { .. }) => continue, - Some(InMemoryFile::File { .. }) => Err(anyhow!( - "ancestor {:?} is not a directory", - path.as_os_str(), - ))?, - None => Err(anyhow!("ancestor {:?} doesn't exists", path.as_os_str(),))?, - }; - } + for path in path.ancestors().skip(1) { + match self.files.read().await.get(path.as_os_str()) { + Some(InMemoryFile::Directory { .. }) => continue, + Some(InMemoryFile::File { .. }) => { + Err(anyhow!("ancestor {:?} is not a directory", path.as_os_str(),))? + }, + None => Err(anyhow!("ancestor {:?} doesn't exists", path.as_os_str(),))?, + }; + } - self.files - .write() - .await - .insert(os_path.to_owned(), InMemoryFile::dir()); + self.files.write().await.insert(os_path.to_owned(), InMemoryFile::dir()); - Ok(()) - } + Ok(()) + } - async fn create_dir_all

(&self, path: P) -> FileSystemResult<()> - where - P: AsRef + Send, - { - let path = path.as_ref(); - let mut files = self.files.write().await; - let ancestors = path - .ancestors() - .collect::>() - .into_iter() - .rev() - .skip(1); + async fn create_dir_all

(&self, path: P) -> FileSystemResult<()> + where + P: AsRef + Send, + { + let path = path.as_ref(); + let mut files = self.files.write().await; + let ancestors = path.ancestors().collect::>().into_iter().rev().skip(1); - for path in ancestors { - match files.get(path.as_os_str()) { - Some(InMemoryFile::Directory { .. }) => continue, - Some(InMemoryFile::File { .. }) => Err(anyhow!( - "ancestor {:?} is not a directory", - path.as_os_str().to_owned(), - ))?, - None => files.insert(path.as_os_str().to_owned(), InMemoryFile::dir()), - }; - } + for path in ancestors { + match files.get(path.as_os_str()) { + Some(InMemoryFile::Directory { .. }) => continue, + Some(InMemoryFile::File { .. }) => { + Err(anyhow!("ancestor {:?} is not a directory", path.as_os_str().to_owned(),))? + }, + None => files.insert(path.as_os_str().to_owned(), InMemoryFile::dir()), + }; + } - Ok(()) - } + Ok(()) + } - async fn read

(&self, path: P) -> FileSystemResult> - where - P: AsRef + Send, - { - let os_path = path.as_ref().as_os_str(); + async fn read

(&self, path: P) -> FileSystemResult> + where + P: AsRef + Send, + { + let os_path = path.as_ref().as_os_str(); - match self.files.read().await.get(os_path) { - Some(InMemoryFile::File { contents, .. }) => Ok(contents.clone()), - Some(InMemoryFile::Directory { .. }) => { - Err(anyhow!("file {os_path:?} is a directory").into()) - }, - None => Err(anyhow!("file {os_path:?} not found").into()), - } - } + match self.files.read().await.get(os_path) { + Some(InMemoryFile::File { contents, .. }) => Ok(contents.clone()), + Some(InMemoryFile::Directory { .. }) => { + Err(anyhow!("file {os_path:?} is a directory").into()) + }, + None => Err(anyhow!("file {os_path:?} not found").into()), + } + } - async fn read_to_string

(&self, path: P) -> FileSystemResult - where - P: AsRef + Send, - { - let os_path = path.as_ref().as_os_str().to_owned(); - let content = self.read(path).await?; + async fn read_to_string

(&self, path: P) -> FileSystemResult + where + P: AsRef + Send, + { + let os_path = path.as_ref().as_os_str().to_owned(); + let content = self.read(path).await?; - String::from_utf8(content) - .map_err(|_| anyhow!("invalid utf-8 encoding for file {os_path:?}").into()) - } + String::from_utf8(content) + .map_err(|_| anyhow!("invalid utf-8 encoding for file {os_path:?}").into()) + } - async fn write(&self, path: P, contents: C) -> FileSystemResult<()> - where - P: AsRef + Send, - C: AsRef<[u8]> + Send, - { - let path = path.as_ref(); - let os_path = path.as_os_str(); - let mut files = self.files.write().await; + async fn write(&self, path: P, contents: C) -> FileSystemResult<()> + where + P: AsRef + Send, + C: AsRef<[u8]> + Send, + { + let path = path.as_ref(); + let os_path = path.as_os_str(); + let mut files = self.files.write().await; - for path in path.ancestors().skip(1) { - match files.get(path.as_os_str()) { - Some(InMemoryFile::Directory { .. }) => continue, - Some(InMemoryFile::File { .. }) => Err(anyhow!( - "ancestor {:?} is not a directory", - path.as_os_str() - ))?, - None => Err(anyhow!("ancestor {:?} doesn't exists", path.as_os_str()))?, - }; - } + for path in path.ancestors().skip(1) { + match files.get(path.as_os_str()) { + Some(InMemoryFile::Directory { .. }) => continue, + Some(InMemoryFile::File { .. }) => { + Err(anyhow!("ancestor {:?} is not a directory", path.as_os_str()))? + }, + None => Err(anyhow!("ancestor {:?} doesn't exists", path.as_os_str()))?, + }; + } - if let Some(InMemoryFile::Directory { .. }) = files.get(os_path) { - return Err(anyhow!("file {os_path:?} is a directory").into()); - } + if let Some(InMemoryFile::Directory { .. }) = files.get(os_path) { + return Err(anyhow!("file {os_path:?} is a directory").into()); + } - files.insert(os_path.to_owned(), InMemoryFile::file_raw(contents)); + files.insert(os_path.to_owned(), InMemoryFile::file_raw(contents)); - Ok(()) - } + Ok(()) + } - async fn append(&self, path: P, contents: C) -> FileSystemResult<()> - where - P: AsRef + Send, - C: AsRef<[u8]> + Send, - { - let path = path.as_ref(); - let mut existing_contents = match self.read(path).await { - Ok(existing_contents) => existing_contents, - Err(err) if err.to_string() == format!("file {:?} not found", path.as_os_str()) => { - vec![] - }, - Err(err) => Err(err)?, - }; - existing_contents.append(&mut contents.as_ref().to_vec()); + async fn append(&self, path: P, contents: C) -> FileSystemResult<()> + where + P: AsRef + Send, + C: AsRef<[u8]> + Send, + { + let path = path.as_ref(); + let mut existing_contents = match self.read(path).await { + Ok(existing_contents) => existing_contents, + Err(err) if err.to_string() == format!("file {:?} not found", path.as_os_str()) => { + vec![] + }, + Err(err) => Err(err)?, + }; + existing_contents.append(&mut contents.as_ref().to_vec()); - self.write(path, existing_contents).await - } + self.write(path, existing_contents).await + } - async fn copy(&self, from: P1, to: P2) -> FileSystemResult<()> - where - P1: AsRef + Send, - P2: AsRef + Send, - { - let from_ref = from.as_ref(); - let to_ref = to.as_ref(); - let content = self.read(from_ref).await?; + async fn copy(&self, from: P1, to: P2) -> FileSystemResult<()> + where + P1: AsRef + Send, + P2: AsRef + Send, + { + let from_ref = from.as_ref(); + let to_ref = to.as_ref(); + let content = self.read(from_ref).await?; - self.write(to_ref, content).await - } + self.write(to_ref, content).await + } - async fn set_mode

(&self, path: P, mode: u32) -> FileSystemResult<()> - where - P: AsRef + Send, - { - let os_path = path.as_ref().as_os_str(); - if let Some(file) = self.files.write().await.get_mut(os_path) { - match file { - InMemoryFile::File { mode: old_mode, .. } => { - *old_mode = mode; - }, - InMemoryFile::Directory { mode: old_mode, .. } => { - *old_mode = mode; - }, - }; - Ok(()) - } else { - Err(anyhow!("file {os_path:?} not found").into()) - } - } + async fn set_mode

(&self, path: P, mode: u32) -> FileSystemResult<()> + where + P: AsRef + Send, + { + let os_path = path.as_ref().as_os_str(); + if let Some(file) = self.files.write().await.get_mut(os_path) { + match file { + InMemoryFile::File { mode: old_mode, .. } => { + *old_mode = mode; + }, + InMemoryFile::Directory { mode: old_mode, .. } => { + *old_mode = mode; + }, + }; + Ok(()) + } else { + Err(anyhow!("file {os_path:?} not found").into()) + } + } - async fn exists

(&self, path: P) -> bool - where - P: AsRef + Send, - { - self.files - .read() - .await - .contains_key(path.as_ref().as_os_str()) - } + async fn exists

(&self, path: P) -> bool + where + P: AsRef + Send, + { + self.files.read().await.contains_key(path.as_ref().as_os_str()) + } } #[cfg(test)] mod tests { - use std::str::FromStr; - - use super::*; - - #[tokio::test] - async fn create_dir_should_create_a_directory_at_root() { - let fs = InMemoryFileSystem::new(HashMap::from([( - OsString::from_str("/").unwrap(), - InMemoryFile::dir(), - )])); - - fs.create_dir("/dir").await.unwrap(); - - assert_eq!(fs.files.read().await.len(), 2); - assert!(matches!( - fs.files - .read() - .await - .get(&OsString::from_str("/dir").unwrap()) - .unwrap(), - InMemoryFile::Directory { mode } if *mode == 0o775 - )); - } - - #[tokio::test] - async fn create_dir_should_return_an_error_if_directory_already_exists() { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - (OsString::from_str("/dir").unwrap(), InMemoryFile::dir()), - ])); - - let err = fs.create_dir("/dir").await.unwrap_err(); - - assert_eq!(fs.files.read().await.len(), 2); - assert_eq!(err.to_string(), "directory \"/dir\" already exists"); - } - - #[tokio::test] - async fn create_dir_should_return_an_error_if_file_already_exists() { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - (OsString::from_str("/dir").unwrap(), InMemoryFile::empty()), - ])); - - let err = fs.create_dir("/dir").await.unwrap_err(); - - assert_eq!(fs.files.read().await.len(), 2); - assert_eq!(err.to_string(), "file \"/dir\" already exists"); - } - - #[tokio::test] - async fn create_dir_should_create_a_directory_if_all_ancestors_exist() { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - (OsString::from_str("/path").unwrap(), InMemoryFile::dir()), - (OsString::from_str("/path/to").unwrap(), InMemoryFile::dir()), - ( - OsString::from_str("/path/to/my").unwrap(), - InMemoryFile::dir(), - ), - ])); - - fs.create_dir("/path/to/my/dir").await.unwrap(); - - assert_eq!(fs.files.read().await.len(), 5); - assert!(matches!( - fs.files - .read() - .await - .get(&OsString::from_str("/path/to/my/dir").unwrap()) - .unwrap(), - InMemoryFile::Directory { mode} if *mode == 0o775 - )); - } - - #[tokio::test] - async fn create_dir_should_return_an_error_if_some_directory_ancestor_doesnt_exists() { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - (OsString::from_str("/path").unwrap(), InMemoryFile::dir()), - (OsString::from_str("/path/to").unwrap(), InMemoryFile::dir()), - ])); - - let err = fs.create_dir("/path/to/my/dir").await.unwrap_err(); - - assert_eq!(fs.files.read().await.len(), 3); - assert_eq!(err.to_string(), "ancestor \"/path/to/my\" doesn't exists"); - } - - #[tokio::test] - async fn create_dir_should_return_an_error_if_some_ancestor_is_not_a_directory() { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - (OsString::from_str("/path").unwrap(), InMemoryFile::empty()), - (OsString::from_str("/path/to").unwrap(), InMemoryFile::dir()), - ( - OsString::from_str("/path/to/my").unwrap(), - InMemoryFile::dir(), - ), - ])); - - let err = fs.create_dir("/path/to/my/dir").await.unwrap_err(); - - assert_eq!(fs.files.read().await.len(), 4); - assert_eq!(err.to_string(), "ancestor \"/path\" is not a directory"); - } - - #[tokio::test] - async fn create_dir_all_should_create_a_directory_and_all_its_ancestors_if_they_dont_exist() { - let fs = InMemoryFileSystem::new(HashMap::from([( - OsString::from_str("/").unwrap(), - InMemoryFile::dir(), - )])); - - fs.create_dir_all("/path/to/my/dir").await.unwrap(); - - assert_eq!(fs.files.read().await.len(), 5); - assert!(matches!( - fs.files - .read() - .await - .get(&OsString::from_str("/path").unwrap()) - .unwrap(), - InMemoryFile::Directory { mode } if *mode == 0o775 - )); - assert!(matches!( - fs.files - .read() - .await - .get(&OsString::from_str("/path/to").unwrap()) - .unwrap(), - InMemoryFile::Directory { mode } if *mode == 0o775 - )); - assert!(matches!( - fs.files - .read() - .await - .get(&OsString::from_str("/path/to/my").unwrap()) - .unwrap(), - InMemoryFile::Directory { mode } if *mode == 0o775 - )); - assert!(matches!( - fs.files - .read() - .await - .get(&OsString::from_str("/path/to/my/dir").unwrap()) - .unwrap(), - InMemoryFile::Directory { mode } if *mode == 0o775 - )); - } - - #[tokio::test] - async fn create_dir_all_should_create_a_directory_and_some_of_its_ancestors_if_they_dont_exist() - { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - (OsString::from_str("/path").unwrap(), InMemoryFile::dir()), - (OsString::from_str("/path/to").unwrap(), InMemoryFile::dir()), - ])); - - fs.create_dir_all("/path/to/my/dir").await.unwrap(); - - assert_eq!(fs.files.read().await.len(), 5); - assert!(matches!( - fs.files - .read() - .await - .get(&OsString::from_str("/path/to/my").unwrap()) - .unwrap(), - InMemoryFile::Directory { mode } if *mode == 0o775 - )); - assert!(matches!( - fs.files - .read() - .await - .get(&OsString::from_str("/path/to/my/dir").unwrap()) - .unwrap(), - InMemoryFile::Directory { mode } if *mode == 0o775 - )); - } - - #[tokio::test] - async fn create_dir_all_should_return_an_error_if_some_ancestor_is_not_a_directory() { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - (OsString::from_str("/path").unwrap(), InMemoryFile::empty()), - (OsString::from_str("/path/to").unwrap(), InMemoryFile::dir()), - ])); - - let err = fs.create_dir_all("/path/to/my/dir").await.unwrap_err(); - - assert_eq!(fs.files.read().await.len(), 3); - assert_eq!(err.to_string(), "ancestor \"/path\" is not a directory"); - } - - #[tokio::test] - async fn read_should_return_the_file_content() { - let fs = InMemoryFileSystem::new(HashMap::from([( - OsString::from_str("/myfile").unwrap(), - InMemoryFile::file("content"), - )])); - - let content = fs.read("/myfile").await.unwrap(); - - assert_eq!(content, "content".as_bytes().to_vec()); - } - - #[tokio::test] - async fn read_should_return_an_error_if_file_doesnt_exists() { - let fs = InMemoryFileSystem::new(HashMap::new()); - - let err = fs.read("/myfile").await.unwrap_err(); - - assert_eq!(err.to_string(), "file \"/myfile\" not found"); - } - - #[tokio::test] - async fn read_should_return_an_error_if_file_is_a_directory() { - let fs = InMemoryFileSystem::new(HashMap::from([( - OsString::from_str("/myfile").unwrap(), - InMemoryFile::dir(), - )])); - - let err = fs.read("/myfile").await.unwrap_err(); - - assert_eq!(err.to_string(), "file \"/myfile\" is a directory"); - } - - #[tokio::test] - async fn read_to_string_should_return_the_file_content_as_a_string() { - let fs = InMemoryFileSystem::new(HashMap::from([( - OsString::from_str("/myfile").unwrap(), - InMemoryFile::file("content"), - )])); - - let content = fs.read_to_string("/myfile").await.unwrap(); - - assert_eq!(content, "content"); - } - - #[tokio::test] - async fn read_to_string_should_return_an_error_if_file_doesnt_exists() { - let fs = InMemoryFileSystem::new(HashMap::new()); - - let err = fs.read_to_string("/myfile").await.unwrap_err(); - - assert_eq!(err.to_string(), "file \"/myfile\" not found"); - } - - #[tokio::test] - async fn read_to_string_should_return_an_error_if_file_is_a_directory() { - let fs = InMemoryFileSystem::new(HashMap::from([( - OsString::from_str("/myfile").unwrap(), - InMemoryFile::dir(), - )])); - - let err = fs.read_to_string("/myfile").await.unwrap_err(); - - assert_eq!(err.to_string(), "file \"/myfile\" is a directory"); - } - - #[tokio::test] - async fn read_to_string_should_return_an_error_if_file_isnt_utf8_encoded() { - let fs = InMemoryFileSystem::new(HashMap::from([( - OsString::from_str("/myfile").unwrap(), - InMemoryFile::file_raw(vec![0xC3, 0x28]), - )])); - - let err = fs.read_to_string("/myfile").await.unwrap_err(); - - assert_eq!( - err.to_string(), - "invalid utf-8 encoding for file \"/myfile\"" - ); - } - - #[tokio::test] - async fn write_should_create_file_with_content_if_file_doesnt_exists() { - let fs = InMemoryFileSystem::new(HashMap::from([( - OsString::from_str("/").unwrap(), - InMemoryFile::dir(), - )])); - - fs.write("/myfile", "my file content").await.unwrap(); - - assert_eq!(fs.files.read().await.len(), 2); - assert!(matches!( - fs.files - .read() - .await - .get(&OsString::from_str("/myfile").unwrap()), - Some(InMemoryFile::File {mode, contents, .. }) if *mode == 0o664 && contents == "my file content".as_bytes() - )); - } - - #[tokio::test] - async fn write_should_overwrite_file_content_if_file_exists() { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - ( - OsString::from_str("/myfile").unwrap(), - InMemoryFile::file("my file content"), - ), - ])); - - fs.write("/myfile", "my new file content").await.unwrap(); - - assert_eq!(fs.files.read().await.len(), 2); - assert!(matches!( - fs.files - .read() - .await - .get(&OsString::from_str("/myfile").unwrap()), - Some(InMemoryFile::File { mode, contents, .. }) if *mode == 0o664 && contents == "my new file content".as_bytes() - )); - } - - #[tokio::test] - async fn write_should_return_an_error_if_file_is_a_directory() { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - (OsString::from_str("/myfile").unwrap(), InMemoryFile::dir()), - ])); - - let err = fs.write("/myfile", "my file content").await.unwrap_err(); - - assert_eq!(fs.files.read().await.len(), 2); - assert_eq!(err.to_string(), "file \"/myfile\" is a directory"); - } - - #[tokio::test] - async fn write_should_return_an_error_if_file_is_new_and_some_ancestor_doesnt_exists() { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - (OsString::from_str("/path/to").unwrap(), InMemoryFile::dir()), - ])); - - let err = fs - .write("/path/to/myfile", "my file content") - .await - .unwrap_err(); - - assert_eq!(fs.files.read().await.len(), 2); - assert_eq!(err.to_string(), "ancestor \"/path\" doesn't exists"); - } - - #[tokio::test] - async fn write_should_return_an_error_if_file_is_new_and_some_ancestor_is_not_a_directory() { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - (OsString::from_str("/path").unwrap(), InMemoryFile::empty()), - (OsString::from_str("/path/to").unwrap(), InMemoryFile::dir()), - ])); - - let err = fs - .write("/path/to/myfile", "my file content") - .await - .unwrap_err(); - - assert_eq!(fs.files.read().await.len(), 3); - assert_eq!(err.to_string(), "ancestor \"/path\" is not a directory"); - } - - #[tokio::test] - async fn append_should_update_file_content_if_file_exists() { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - ( - OsString::from_str("/myfile").unwrap(), - InMemoryFile::file("my file content"), - ), - ])); - - fs.append("/myfile", " has been updated with new things") - .await - .unwrap(); - - assert_eq!(fs.files.read().await.len(), 2); - assert!(matches!( - fs.files - .read() - .await - .get(&OsString::from_str("/myfile").unwrap()), - Some(InMemoryFile::File { mode, contents, .. }) if *mode == 0o664 && contents == "my file content has been updated with new things".as_bytes() - )); - } - - #[tokio::test] - async fn append_should_create_file_with_content_if_file_doesnt_exists() { - let fs = InMemoryFileSystem::new(HashMap::from([( - OsString::from_str("/").unwrap(), - InMemoryFile::dir(), - )])); - - fs.append("/myfile", "my file content").await.unwrap(); - - assert_eq!(fs.files.read().await.len(), 2); - assert!(matches!( - fs.files - .read() - .await - .get(&OsString::from_str("/myfile").unwrap()), - Some(InMemoryFile::File { mode,contents, .. }) if *mode == 0o664 && contents == "my file content".as_bytes() - )); - } - - #[tokio::test] - async fn append_should_return_an_error_if_file_is_a_directory() { - let fs = InMemoryFileSystem::new(HashMap::from([( - OsString::from_str("/myfile").unwrap(), - InMemoryFile::dir(), - )])); - - let err = fs.append("/myfile", "my file content").await.unwrap_err(); - - assert_eq!(err.to_string(), "file \"/myfile\" is a directory"); - } - - #[tokio::test] - async fn append_should_return_an_error_if_file_is_new_and_some_ancestor_doesnt_exists() { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - (OsString::from_str("/path/to").unwrap(), InMemoryFile::dir()), - ])); - - let err = fs - .append("/path/to/myfile", "my file content") - .await - .unwrap_err(); - - assert_eq!(fs.files.read().await.len(), 2); - assert_eq!(err.to_string(), "ancestor \"/path\" doesn't exists"); - } - - #[tokio::test] - async fn append_should_return_an_error_if_file_is_new_and_some_ancestor_is_not_a_directory() { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - (OsString::from_str("/path").unwrap(), InMemoryFile::empty()), - (OsString::from_str("/path/to").unwrap(), InMemoryFile::dir()), - ])); - - let err = fs - .append("/path/to/myfile", "my file content") - .await - .unwrap_err(); - - assert_eq!(fs.files.read().await.len(), 3); - assert_eq!(err.to_string(), "ancestor \"/path\" is not a directory"); - } - - #[tokio::test] - async fn copy_should_creates_new_destination_file_if_it_doesnt_exists() { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - ( - OsString::from_str("/myfile").unwrap(), - InMemoryFile::file("my file content"), - ), - ])); - - fs.copy("/myfile", "/myfilecopy").await.unwrap(); - - assert_eq!(fs.files.read().await.len(), 3); - assert!( - matches!(fs.files.read().await.get(&OsString::from_str("/myfilecopy").unwrap()).unwrap(), InMemoryFile::File { mode, contents, .. } if *mode == 0o664 && contents == "my file content".as_bytes()) - ); - } - - #[tokio::test] - async fn copy_should_updates_the_file_content_of_the_destination_file_if_it_already_exists() { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - ( - OsString::from_str("/myfile").unwrap(), - InMemoryFile::file("my new file content"), - ), - ( - OsString::from_str("/myfilecopy").unwrap(), - InMemoryFile::file("my file content"), - ), - ])); - - fs.copy("/myfile", "/myfilecopy").await.unwrap(); - - assert_eq!(fs.files.read().await.len(), 3); - assert!( - matches!(fs.files.read().await.get(&OsString::from_str("/myfilecopy").unwrap()).unwrap(), InMemoryFile::File { mode, contents, .. } if *mode == 0o664 && contents == "my new file content".as_bytes()) - ); - } - - #[tokio::test] - async fn copy_should_returns_an_error_if_source_file_doesnt_exists() { - let fs = InMemoryFileSystem::new(HashMap::from([( - OsString::from_str("/").unwrap(), - InMemoryFile::dir(), - )])); - - let err = fs.copy("/myfile", "/mfilecopy").await.unwrap_err(); - - assert_eq!(err.to_string(), "file \"/myfile\" not found"); - } - - #[tokio::test] - async fn copy_should_returns_an_error_if_source_file_is_a_directory() { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - (OsString::from_str("/myfile").unwrap(), InMemoryFile::dir()), - ])); - - let err = fs.copy("/myfile", "/mfilecopy").await.unwrap_err(); - - assert_eq!(err.to_string(), "file \"/myfile\" is a directory"); - } - - #[tokio::test] - async fn copy_should_returns_an_error_if_destination_file_is_a_directory() { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - ( - OsString::from_str("/myfile").unwrap(), - InMemoryFile::file("my file content"), - ), - ( - OsString::from_str("/myfilecopy").unwrap(), - InMemoryFile::dir(), - ), - ])); - - let err = fs.copy("/myfile", "/myfilecopy").await.unwrap_err(); - - assert_eq!(err.to_string(), "file \"/myfilecopy\" is a directory"); - } - - #[tokio::test] - async fn copy_should_returns_an_error_if_destination_file_is_new_and_some_ancestor_doesnt_exists( - ) { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - ( - OsString::from_str("/myfile").unwrap(), - InMemoryFile::file("my file content"), - ), - ])); - - let err = fs.copy("/myfile", "/somedir/myfilecopy").await.unwrap_err(); - - assert_eq!(fs.files.read().await.len(), 2); - assert_eq!(err.to_string(), "ancestor \"/somedir\" doesn't exists"); - } - - #[tokio::test] - async fn copy_should_returns_an_error_if_destination_file_is_new_and_some_ancestor_is_not_a_directory( - ) { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - ( - OsString::from_str("/myfile").unwrap(), - InMemoryFile::file("my file content"), - ), - ( - OsString::from_str("/mypath").unwrap(), - InMemoryFile::empty(), - ), - ])); - - let err = fs.copy("/myfile", "/mypath/myfilecopy").await.unwrap_err(); - - assert_eq!(fs.files.read().await.len(), 3); - assert_eq!(err.to_string(), "ancestor \"/mypath\" is not a directory"); - } - - #[tokio::test] - async fn set_mode_should_update_the_file_mode_at_path() { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - ( - OsString::from_str("/myfile").unwrap(), - InMemoryFile::file("my file content"), - ), - ])); - assert!( - matches!(fs.files.read().await.get(&OsString::from_str("/myfile").unwrap()).unwrap(), InMemoryFile::File { mode, .. } if *mode == 0o664) - ); - - fs.set_mode("/myfile", 0o400).await.unwrap(); - - assert!( - matches!(fs.files.read().await.get(&OsString::from_str("/myfile").unwrap()).unwrap(), InMemoryFile::File { mode, .. } if *mode == 0o400) - ); - } - - #[tokio::test] - async fn set_mode_should_update_the_directory_mode_at_path() { - let fs = InMemoryFileSystem::new(HashMap::from([ - (OsString::from_str("/").unwrap(), InMemoryFile::dir()), - (OsString::from_str("/mydir").unwrap(), InMemoryFile::dir()), - ])); - assert!( - matches!(fs.files.read().await.get(&OsString::from_str("/mydir").unwrap()).unwrap(), InMemoryFile::Directory { mode } if *mode == 0o775) - ); - - fs.set_mode("/mydir", 0o700).await.unwrap(); - - assert!( - matches!(fs.files.read().await.get(&OsString::from_str("/mydir").unwrap()).unwrap(), InMemoryFile::Directory { mode } if *mode == 0o700) - ); - } - - #[tokio::test] - async fn set_mode_should_returns_an_error_if_file_doesnt_exists() { - let fs = InMemoryFileSystem::new(HashMap::from([( - OsString::from_str("/").unwrap(), - InMemoryFile::dir(), - )])); - // intentionally forget to create file - - let err = fs.set_mode("/myfile", 0o400).await.unwrap_err(); - - assert_eq!(err.to_string(), "file \"/myfile\" not found"); - } + use std::str::FromStr; + + use super::*; + + #[tokio::test] + async fn create_dir_should_create_a_directory_at_root() { + let fs = InMemoryFileSystem::new(HashMap::from([( + OsString::from_str("/").unwrap(), + InMemoryFile::dir(), + )])); + + fs.create_dir("/dir").await.unwrap(); + + assert_eq!(fs.files.read().await.len(), 2); + assert!(matches!( + fs.files + .read() + .await + .get(&OsString::from_str("/dir").unwrap()) + .unwrap(), + InMemoryFile::Directory { mode } if *mode == 0o775 + )); + } + + #[tokio::test] + async fn create_dir_should_return_an_error_if_directory_already_exists() { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/dir").unwrap(), InMemoryFile::dir()), + ])); + + let err = fs.create_dir("/dir").await.unwrap_err(); + + assert_eq!(fs.files.read().await.len(), 2); + assert_eq!(err.to_string(), "directory \"/dir\" already exists"); + } + + #[tokio::test] + async fn create_dir_should_return_an_error_if_file_already_exists() { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/dir").unwrap(), InMemoryFile::empty()), + ])); + + let err = fs.create_dir("/dir").await.unwrap_err(); + + assert_eq!(fs.files.read().await.len(), 2); + assert_eq!(err.to_string(), "file \"/dir\" already exists"); + } + + #[tokio::test] + async fn create_dir_should_create_a_directory_if_all_ancestors_exist() { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/path").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/path/to").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/path/to/my").unwrap(), InMemoryFile::dir()), + ])); + + fs.create_dir("/path/to/my/dir").await.unwrap(); + + assert_eq!(fs.files.read().await.len(), 5); + assert!(matches!( + fs.files + .read() + .await + .get(&OsString::from_str("/path/to/my/dir").unwrap()) + .unwrap(), + InMemoryFile::Directory { mode} if *mode == 0o775 + )); + } + + #[tokio::test] + async fn create_dir_should_return_an_error_if_some_directory_ancestor_doesnt_exists() { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/path").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/path/to").unwrap(), InMemoryFile::dir()), + ])); + + let err = fs.create_dir("/path/to/my/dir").await.unwrap_err(); + + assert_eq!(fs.files.read().await.len(), 3); + assert_eq!(err.to_string(), "ancestor \"/path/to/my\" doesn't exists"); + } + + #[tokio::test] + async fn create_dir_should_return_an_error_if_some_ancestor_is_not_a_directory() { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/path").unwrap(), InMemoryFile::empty()), + (OsString::from_str("/path/to").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/path/to/my").unwrap(), InMemoryFile::dir()), + ])); + + let err = fs.create_dir("/path/to/my/dir").await.unwrap_err(); + + assert_eq!(fs.files.read().await.len(), 4); + assert_eq!(err.to_string(), "ancestor \"/path\" is not a directory"); + } + + #[tokio::test] + async fn create_dir_all_should_create_a_directory_and_all_its_ancestors_if_they_dont_exist() { + let fs = InMemoryFileSystem::new(HashMap::from([( + OsString::from_str("/").unwrap(), + InMemoryFile::dir(), + )])); + + fs.create_dir_all("/path/to/my/dir").await.unwrap(); + + assert_eq!(fs.files.read().await.len(), 5); + assert!(matches!( + fs.files + .read() + .await + .get(&OsString::from_str("/path").unwrap()) + .unwrap(), + InMemoryFile::Directory { mode } if *mode == 0o775 + )); + assert!(matches!( + fs.files + .read() + .await + .get(&OsString::from_str("/path/to").unwrap()) + .unwrap(), + InMemoryFile::Directory { mode } if *mode == 0o775 + )); + assert!(matches!( + fs.files + .read() + .await + .get(&OsString::from_str("/path/to/my").unwrap()) + .unwrap(), + InMemoryFile::Directory { mode } if *mode == 0o775 + )); + assert!(matches!( + fs.files + .read() + .await + .get(&OsString::from_str("/path/to/my/dir").unwrap()) + .unwrap(), + InMemoryFile::Directory { mode } if *mode == 0o775 + )); + } + + #[tokio::test] + async fn create_dir_all_should_create_a_directory_and_some_of_its_ancestors_if_they_dont_exist() + { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/path").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/path/to").unwrap(), InMemoryFile::dir()), + ])); + + fs.create_dir_all("/path/to/my/dir").await.unwrap(); + + assert_eq!(fs.files.read().await.len(), 5); + assert!(matches!( + fs.files + .read() + .await + .get(&OsString::from_str("/path/to/my").unwrap()) + .unwrap(), + InMemoryFile::Directory { mode } if *mode == 0o775 + )); + assert!(matches!( + fs.files + .read() + .await + .get(&OsString::from_str("/path/to/my/dir").unwrap()) + .unwrap(), + InMemoryFile::Directory { mode } if *mode == 0o775 + )); + } + + #[tokio::test] + async fn create_dir_all_should_return_an_error_if_some_ancestor_is_not_a_directory() { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/path").unwrap(), InMemoryFile::empty()), + (OsString::from_str("/path/to").unwrap(), InMemoryFile::dir()), + ])); + + let err = fs.create_dir_all("/path/to/my/dir").await.unwrap_err(); + + assert_eq!(fs.files.read().await.len(), 3); + assert_eq!(err.to_string(), "ancestor \"/path\" is not a directory"); + } + + #[tokio::test] + async fn read_should_return_the_file_content() { + let fs = InMemoryFileSystem::new(HashMap::from([( + OsString::from_str("/myfile").unwrap(), + InMemoryFile::file("content"), + )])); + + let content = fs.read("/myfile").await.unwrap(); + + assert_eq!(content, "content".as_bytes().to_vec()); + } + + #[tokio::test] + async fn read_should_return_an_error_if_file_doesnt_exists() { + let fs = InMemoryFileSystem::new(HashMap::new()); + + let err = fs.read("/myfile").await.unwrap_err(); + + assert_eq!(err.to_string(), "file \"/myfile\" not found"); + } + + #[tokio::test] + async fn read_should_return_an_error_if_file_is_a_directory() { + let fs = InMemoryFileSystem::new(HashMap::from([( + OsString::from_str("/myfile").unwrap(), + InMemoryFile::dir(), + )])); + + let err = fs.read("/myfile").await.unwrap_err(); + + assert_eq!(err.to_string(), "file \"/myfile\" is a directory"); + } + + #[tokio::test] + async fn read_to_string_should_return_the_file_content_as_a_string() { + let fs = InMemoryFileSystem::new(HashMap::from([( + OsString::from_str("/myfile").unwrap(), + InMemoryFile::file("content"), + )])); + + let content = fs.read_to_string("/myfile").await.unwrap(); + + assert_eq!(content, "content"); + } + + #[tokio::test] + async fn read_to_string_should_return_an_error_if_file_doesnt_exists() { + let fs = InMemoryFileSystem::new(HashMap::new()); + + let err = fs.read_to_string("/myfile").await.unwrap_err(); + + assert_eq!(err.to_string(), "file \"/myfile\" not found"); + } + + #[tokio::test] + async fn read_to_string_should_return_an_error_if_file_is_a_directory() { + let fs = InMemoryFileSystem::new(HashMap::from([( + OsString::from_str("/myfile").unwrap(), + InMemoryFile::dir(), + )])); + + let err = fs.read_to_string("/myfile").await.unwrap_err(); + + assert_eq!(err.to_string(), "file \"/myfile\" is a directory"); + } + + #[tokio::test] + async fn read_to_string_should_return_an_error_if_file_isnt_utf8_encoded() { + let fs = InMemoryFileSystem::new(HashMap::from([( + OsString::from_str("/myfile").unwrap(), + InMemoryFile::file_raw(vec![0xC3, 0x28]), + )])); + + let err = fs.read_to_string("/myfile").await.unwrap_err(); + + assert_eq!(err.to_string(), "invalid utf-8 encoding for file \"/myfile\""); + } + + #[tokio::test] + async fn write_should_create_file_with_content_if_file_doesnt_exists() { + let fs = InMemoryFileSystem::new(HashMap::from([( + OsString::from_str("/").unwrap(), + InMemoryFile::dir(), + )])); + + fs.write("/myfile", "my file content").await.unwrap(); + + assert_eq!(fs.files.read().await.len(), 2); + assert!(matches!( + fs.files + .read() + .await + .get(&OsString::from_str("/myfile").unwrap()), + Some(InMemoryFile::File {mode, contents, .. }) if *mode == 0o664 && contents == "my file content".as_bytes() + )); + } + + #[tokio::test] + async fn write_should_overwrite_file_content_if_file_exists() { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/myfile").unwrap(), InMemoryFile::file("my file content")), + ])); + + fs.write("/myfile", "my new file content").await.unwrap(); + + assert_eq!(fs.files.read().await.len(), 2); + assert!(matches!( + fs.files + .read() + .await + .get(&OsString::from_str("/myfile").unwrap()), + Some(InMemoryFile::File { mode, contents, .. }) if *mode == 0o664 && contents == "my new file content".as_bytes() + )); + } + + #[tokio::test] + async fn write_should_return_an_error_if_file_is_a_directory() { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/myfile").unwrap(), InMemoryFile::dir()), + ])); + + let err = fs.write("/myfile", "my file content").await.unwrap_err(); + + assert_eq!(fs.files.read().await.len(), 2); + assert_eq!(err.to_string(), "file \"/myfile\" is a directory"); + } + + #[tokio::test] + async fn write_should_return_an_error_if_file_is_new_and_some_ancestor_doesnt_exists() { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/path/to").unwrap(), InMemoryFile::dir()), + ])); + + let err = fs.write("/path/to/myfile", "my file content").await.unwrap_err(); + + assert_eq!(fs.files.read().await.len(), 2); + assert_eq!(err.to_string(), "ancestor \"/path\" doesn't exists"); + } + + #[tokio::test] + async fn write_should_return_an_error_if_file_is_new_and_some_ancestor_is_not_a_directory() { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/path").unwrap(), InMemoryFile::empty()), + (OsString::from_str("/path/to").unwrap(), InMemoryFile::dir()), + ])); + + let err = fs.write("/path/to/myfile", "my file content").await.unwrap_err(); + + assert_eq!(fs.files.read().await.len(), 3); + assert_eq!(err.to_string(), "ancestor \"/path\" is not a directory"); + } + + #[tokio::test] + async fn append_should_update_file_content_if_file_exists() { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/myfile").unwrap(), InMemoryFile::file("my file content")), + ])); + + fs.append("/myfile", " has been updated with new things").await.unwrap(); + + assert_eq!(fs.files.read().await.len(), 2); + assert!(matches!( + fs.files + .read() + .await + .get(&OsString::from_str("/myfile").unwrap()), + Some(InMemoryFile::File { mode, contents, .. }) if *mode == 0o664 && contents == "my file content has been updated with new things".as_bytes() + )); + } + + #[tokio::test] + async fn append_should_create_file_with_content_if_file_doesnt_exists() { + let fs = InMemoryFileSystem::new(HashMap::from([( + OsString::from_str("/").unwrap(), + InMemoryFile::dir(), + )])); + + fs.append("/myfile", "my file content").await.unwrap(); + + assert_eq!(fs.files.read().await.len(), 2); + assert!(matches!( + fs.files + .read() + .await + .get(&OsString::from_str("/myfile").unwrap()), + Some(InMemoryFile::File { mode,contents, .. }) if *mode == 0o664 && contents == "my file content".as_bytes() + )); + } + + #[tokio::test] + async fn append_should_return_an_error_if_file_is_a_directory() { + let fs = InMemoryFileSystem::new(HashMap::from([( + OsString::from_str("/myfile").unwrap(), + InMemoryFile::dir(), + )])); + + let err = fs.append("/myfile", "my file content").await.unwrap_err(); + + assert_eq!(err.to_string(), "file \"/myfile\" is a directory"); + } + + #[tokio::test] + async fn append_should_return_an_error_if_file_is_new_and_some_ancestor_doesnt_exists() { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/path/to").unwrap(), InMemoryFile::dir()), + ])); + + let err = fs.append("/path/to/myfile", "my file content").await.unwrap_err(); + + assert_eq!(fs.files.read().await.len(), 2); + assert_eq!(err.to_string(), "ancestor \"/path\" doesn't exists"); + } + + #[tokio::test] + async fn append_should_return_an_error_if_file_is_new_and_some_ancestor_is_not_a_directory() { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/path").unwrap(), InMemoryFile::empty()), + (OsString::from_str("/path/to").unwrap(), InMemoryFile::dir()), + ])); + + let err = fs.append("/path/to/myfile", "my file content").await.unwrap_err(); + + assert_eq!(fs.files.read().await.len(), 3); + assert_eq!(err.to_string(), "ancestor \"/path\" is not a directory"); + } + + #[tokio::test] + async fn copy_should_creates_new_destination_file_if_it_doesnt_exists() { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/myfile").unwrap(), InMemoryFile::file("my file content")), + ])); + + fs.copy("/myfile", "/myfilecopy").await.unwrap(); + + assert_eq!(fs.files.read().await.len(), 3); + assert!( + matches!(fs.files.read().await.get(&OsString::from_str("/myfilecopy").unwrap()).unwrap(), InMemoryFile::File { mode, contents, .. } if *mode == 0o664 && contents == "my file content".as_bytes()) + ); + } + + #[tokio::test] + async fn copy_should_updates_the_file_content_of_the_destination_file_if_it_already_exists() { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/myfile").unwrap(), InMemoryFile::file("my new file content")), + (OsString::from_str("/myfilecopy").unwrap(), InMemoryFile::file("my file content")), + ])); + + fs.copy("/myfile", "/myfilecopy").await.unwrap(); + + assert_eq!(fs.files.read().await.len(), 3); + assert!( + matches!(fs.files.read().await.get(&OsString::from_str("/myfilecopy").unwrap()).unwrap(), InMemoryFile::File { mode, contents, .. } if *mode == 0o664 && contents == "my new file content".as_bytes()) + ); + } + + #[tokio::test] + async fn copy_should_returns_an_error_if_source_file_doesnt_exists() { + let fs = InMemoryFileSystem::new(HashMap::from([( + OsString::from_str("/").unwrap(), + InMemoryFile::dir(), + )])); + + let err = fs.copy("/myfile", "/mfilecopy").await.unwrap_err(); + + assert_eq!(err.to_string(), "file \"/myfile\" not found"); + } + + #[tokio::test] + async fn copy_should_returns_an_error_if_source_file_is_a_directory() { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/myfile").unwrap(), InMemoryFile::dir()), + ])); + + let err = fs.copy("/myfile", "/mfilecopy").await.unwrap_err(); + + assert_eq!(err.to_string(), "file \"/myfile\" is a directory"); + } + + #[tokio::test] + async fn copy_should_returns_an_error_if_destination_file_is_a_directory() { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/myfile").unwrap(), InMemoryFile::file("my file content")), + (OsString::from_str("/myfilecopy").unwrap(), InMemoryFile::dir()), + ])); + + let err = fs.copy("/myfile", "/myfilecopy").await.unwrap_err(); + + assert_eq!(err.to_string(), "file \"/myfilecopy\" is a directory"); + } + + #[tokio::test] + async fn copy_should_returns_an_error_if_destination_file_is_new_and_some_ancestor_doesnt_exists( + ) { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/myfile").unwrap(), InMemoryFile::file("my file content")), + ])); + + let err = fs.copy("/myfile", "/somedir/myfilecopy").await.unwrap_err(); + + assert_eq!(fs.files.read().await.len(), 2); + assert_eq!(err.to_string(), "ancestor \"/somedir\" doesn't exists"); + } + + #[tokio::test] + async fn copy_should_returns_an_error_if_destination_file_is_new_and_some_ancestor_is_not_a_directory( + ) { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/myfile").unwrap(), InMemoryFile::file("my file content")), + (OsString::from_str("/mypath").unwrap(), InMemoryFile::empty()), + ])); + + let err = fs.copy("/myfile", "/mypath/myfilecopy").await.unwrap_err(); + + assert_eq!(fs.files.read().await.len(), 3); + assert_eq!(err.to_string(), "ancestor \"/mypath\" is not a directory"); + } + + #[tokio::test] + async fn set_mode_should_update_the_file_mode_at_path() { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/myfile").unwrap(), InMemoryFile::file("my file content")), + ])); + assert!( + matches!(fs.files.read().await.get(&OsString::from_str("/myfile").unwrap()).unwrap(), InMemoryFile::File { mode, .. } if *mode == 0o664) + ); + + fs.set_mode("/myfile", 0o400).await.unwrap(); + + assert!( + matches!(fs.files.read().await.get(&OsString::from_str("/myfile").unwrap()).unwrap(), InMemoryFile::File { mode, .. } if *mode == 0o400) + ); + } + + #[tokio::test] + async fn set_mode_should_update_the_directory_mode_at_path() { + let fs = InMemoryFileSystem::new(HashMap::from([ + (OsString::from_str("/").unwrap(), InMemoryFile::dir()), + (OsString::from_str("/mydir").unwrap(), InMemoryFile::dir()), + ])); + assert!( + matches!(fs.files.read().await.get(&OsString::from_str("/mydir").unwrap()).unwrap(), InMemoryFile::Directory { mode } if *mode == 0o775) + ); + + fs.set_mode("/mydir", 0o700).await.unwrap(); + + assert!( + matches!(fs.files.read().await.get(&OsString::from_str("/mydir").unwrap()).unwrap(), InMemoryFile::Directory { mode } if *mode == 0o700) + ); + } + + #[tokio::test] + async fn set_mode_should_returns_an_error_if_file_doesnt_exists() { + let fs = InMemoryFileSystem::new(HashMap::from([( + OsString::from_str("/").unwrap(), + InMemoryFile::dir(), + )])); + // intentionally forget to create file + + let err = fs.set_mode("/myfile", 0o400).await.unwrap_err(); + + assert_eq!(err.to_string(), "file \"/myfile\" not found"); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/support/src/fs/local.rs b/vendor/pezkuwi-zombienet-sdk/crates/support/src/fs/local.rs index 6f7d0e56..fab2ea20 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/support/src/fs/local.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/support/src/fs/local.rs @@ -10,381 +10,368 @@ pub struct LocalFileSystem; #[async_trait] impl FileSystem for LocalFileSystem { - async fn create_dir

(&self, path: P) -> FileSystemResult<()> - where - P: AsRef + Send, - { - tokio::fs::create_dir(path).await.map_err(Into::into) - } + async fn create_dir

(&self, path: P) -> FileSystemResult<()> + where + P: AsRef + Send, + { + tokio::fs::create_dir(path).await.map_err(Into::into) + } - async fn create_dir_all

(&self, path: P) -> FileSystemResult<()> - where - P: AsRef + Send, - { - tokio::fs::create_dir_all(path).await.map_err(Into::into) - } + async fn create_dir_all

(&self, path: P) -> FileSystemResult<()> + where + P: AsRef + Send, + { + tokio::fs::create_dir_all(path).await.map_err(Into::into) + } - async fn read

(&self, path: P) -> FileSystemResult> - where - P: AsRef + Send, - { - tokio::fs::read(path).await.map_err(Into::into) - } + async fn read

(&self, path: P) -> FileSystemResult> + where + P: AsRef + Send, + { + tokio::fs::read(path).await.map_err(Into::into) + } - async fn read_to_string

(&self, path: P) -> FileSystemResult - where - P: AsRef + Send, - { - tokio::fs::read_to_string(path).await.map_err(Into::into) - } + async fn read_to_string

(&self, path: P) -> FileSystemResult + where + P: AsRef + Send, + { + tokio::fs::read_to_string(path).await.map_err(Into::into) + } - async fn write(&self, path: P, contents: C) -> FileSystemResult<()> - where - P: AsRef + Send, - C: AsRef<[u8]> + Send, - { - tokio::fs::write(path, contents).await.map_err(Into::into) - } + async fn write(&self, path: P, contents: C) -> FileSystemResult<()> + where + P: AsRef + Send, + C: AsRef<[u8]> + Send, + { + tokio::fs::write(path, contents).await.map_err(Into::into) + } - async fn append(&self, path: P, contents: C) -> FileSystemResult<()> - where - P: AsRef + Send, - C: AsRef<[u8]> + Send, - { - let contents = contents.as_ref(); - let mut file = tokio::fs::OpenOptions::new() - .create(true) - .append(true) - .open(path) - .await - .map_err(Into::::into)?; + async fn append(&self, path: P, contents: C) -> FileSystemResult<()> + where + P: AsRef + Send, + C: AsRef<[u8]> + Send, + { + let contents = contents.as_ref(); + let mut file = tokio::fs::OpenOptions::new() + .create(true) + .append(true) + .open(path) + .await + .map_err(Into::::into)?; - file.write_all(contents) - .await - .map_err(Into::::into)?; + file.write_all(contents).await.map_err(Into::::into)?; - file.flush().await.and(Ok(())).map_err(Into::into) - } + file.flush().await.and(Ok(())).map_err(Into::into) + } - async fn copy(&self, from: P1, to: P2) -> FileSystemResult<()> - where - P1: AsRef + Send, - P2: AsRef + Send, - { - tokio::fs::copy(from, to) - .await - .and(Ok(())) - .map_err(Into::into) - } + async fn copy(&self, from: P1, to: P2) -> FileSystemResult<()> + where + P1: AsRef + Send, + P2: AsRef + Send, + { + tokio::fs::copy(from, to).await.and(Ok(())).map_err(Into::into) + } - async fn set_mode

(&self, path: P, mode: u32) -> FileSystemResult<()> - where - P: AsRef + Send, - { - tokio::fs::set_permissions(path, Permissions::from_mode(mode)) - .await - .map_err(Into::into) - } + async fn set_mode

(&self, path: P, mode: u32) -> FileSystemResult<()> + where + P: AsRef + Send, + { + tokio::fs::set_permissions(path, Permissions::from_mode(mode)).await.map_err(Into::into) + } - async fn exists

(&self, path: P) -> bool - where - P: AsRef + Send, - { - path.as_ref().exists() - } + async fn exists

(&self, path: P) -> bool + where + P: AsRef + Send, + { + path.as_ref().exists() + } } #[cfg(test)] mod tests { - use uuid::Uuid; + use uuid::Uuid; - use super::*; + use super::*; - const FILE_BITS: u32 = 0o100000; - const DIR_BITS: u32 = 0o40000; + const FILE_BITS: u32 = 0o100000; + const DIR_BITS: u32 = 0o40000; - fn setup() -> String { - let test_dir = format!("/tmp/unit_test_{}", Uuid::new_v4()); - std::fs::create_dir(&test_dir).unwrap(); - test_dir - } + fn setup() -> String { + let test_dir = format!("/tmp/unit_test_{}", Uuid::new_v4()); + std::fs::create_dir(&test_dir).unwrap(); + test_dir + } - fn teardown(test_dir: String) { - std::fs::remove_dir_all(test_dir).unwrap(); - } + fn teardown(test_dir: String) { + std::fs::remove_dir_all(test_dir).unwrap(); + } - #[tokio::test] - async fn create_dir_should_create_a_new_directory_at_path() { - let test_dir = setup(); - let fs = LocalFileSystem; + #[tokio::test] + async fn create_dir_should_create_a_new_directory_at_path() { + let test_dir = setup(); + let fs = LocalFileSystem; - let new_dir = format!("{test_dir}/mynewdir"); - fs.create_dir(&new_dir).await.unwrap(); + let new_dir = format!("{test_dir}/mynewdir"); + fs.create_dir(&new_dir).await.unwrap(); - let new_dir_path = Path::new(&new_dir); - assert!(new_dir_path.exists() && new_dir_path.is_dir()); - teardown(test_dir); - } + let new_dir_path = Path::new(&new_dir); + assert!(new_dir_path.exists() && new_dir_path.is_dir()); + teardown(test_dir); + } - #[tokio::test] - async fn create_dir_should_bubble_up_error_if_some_happens() { - let test_dir = setup(); - let fs = LocalFileSystem; + #[tokio::test] + async fn create_dir_should_bubble_up_error_if_some_happens() { + let test_dir = setup(); + let fs = LocalFileSystem; - let new_dir = format!("{test_dir}/mynewdir"); - // intentionally create new dir before calling function to force error - std::fs::create_dir(&new_dir).unwrap(); - let err = fs.create_dir(&new_dir).await.unwrap_err(); + let new_dir = format!("{test_dir}/mynewdir"); + // intentionally create new dir before calling function to force error + std::fs::create_dir(&new_dir).unwrap(); + let err = fs.create_dir(&new_dir).await.unwrap_err(); - assert_eq!(err.to_string(), "File exists (os error 17)"); - teardown(test_dir); - } + assert_eq!(err.to_string(), "File exists (os error 17)"); + teardown(test_dir); + } - #[tokio::test] - async fn create_dir_all_should_create_a_new_directory_and_all_of_it_ancestors_at_path() { - let test_dir = setup(); - let fs = LocalFileSystem; + #[tokio::test] + async fn create_dir_all_should_create_a_new_directory_and_all_of_it_ancestors_at_path() { + let test_dir = setup(); + let fs = LocalFileSystem; - let new_dir = format!("{test_dir}/the/path/to/mynewdir"); - fs.create_dir_all(&new_dir).await.unwrap(); + let new_dir = format!("{test_dir}/the/path/to/mynewdir"); + fs.create_dir_all(&new_dir).await.unwrap(); - let new_dir_path = Path::new(&new_dir); - assert!(new_dir_path.exists() && new_dir_path.is_dir()); - teardown(test_dir); - } + let new_dir_path = Path::new(&new_dir); + assert!(new_dir_path.exists() && new_dir_path.is_dir()); + teardown(test_dir); + } - #[tokio::test] - async fn create_dir_all_should_bubble_up_error_if_some_happens() { - let test_dir = setup(); - let fs = LocalFileSystem; + #[tokio::test] + async fn create_dir_all_should_bubble_up_error_if_some_happens() { + let test_dir = setup(); + let fs = LocalFileSystem; - let new_dir = format!("{test_dir}/the/path/to/mynewdir"); - // intentionally create new file as ancestor before calling function to force error - std::fs::write(format!("{test_dir}/the"), b"test").unwrap(); - let err = fs.create_dir_all(&new_dir).await.unwrap_err(); + let new_dir = format!("{test_dir}/the/path/to/mynewdir"); + // intentionally create new file as ancestor before calling function to force error + std::fs::write(format!("{test_dir}/the"), b"test").unwrap(); + let err = fs.create_dir_all(&new_dir).await.unwrap_err(); - assert_eq!(err.to_string(), "Not a directory (os error 20)"); - teardown(test_dir); - } + assert_eq!(err.to_string(), "Not a directory (os error 20)"); + teardown(test_dir); + } - #[tokio::test] - async fn read_should_return_the_contents_of_the_file_at_path() { - let test_dir = setup(); - let fs = LocalFileSystem; + #[tokio::test] + async fn read_should_return_the_contents_of_the_file_at_path() { + let test_dir = setup(); + let fs = LocalFileSystem; - let file_path = format!("{test_dir}/myfile"); - std::fs::write(&file_path, b"Test").unwrap(); - let contents = fs.read(file_path).await.unwrap(); + let file_path = format!("{test_dir}/myfile"); + std::fs::write(&file_path, b"Test").unwrap(); + let contents = fs.read(file_path).await.unwrap(); - assert_eq!(contents, b"Test"); - teardown(test_dir); - } + assert_eq!(contents, b"Test"); + teardown(test_dir); + } - #[tokio::test] - async fn read_should_bubble_up_error_if_some_happens() { - let test_dir = setup(); - let fs = LocalFileSystem; + #[tokio::test] + async fn read_should_bubble_up_error_if_some_happens() { + let test_dir = setup(); + let fs = LocalFileSystem; - let file_path = format!("{test_dir}/myfile"); - // intentionally forget to create file to force error - let err = fs.read(file_path).await.unwrap_err(); + let file_path = format!("{test_dir}/myfile"); + // intentionally forget to create file to force error + let err = fs.read(file_path).await.unwrap_err(); - assert_eq!(err.to_string(), "No such file or directory (os error 2)"); - teardown(test_dir); - } + assert_eq!(err.to_string(), "No such file or directory (os error 2)"); + teardown(test_dir); + } - #[tokio::test] - async fn read_to_string_should_return_the_contents_of_the_file_at_path_as_string() { - let test_dir = setup(); - let fs = LocalFileSystem; + #[tokio::test] + async fn read_to_string_should_return_the_contents_of_the_file_at_path_as_string() { + let test_dir = setup(); + let fs = LocalFileSystem; - let file_path = format!("{test_dir}/myfile"); - std::fs::write(&file_path, b"Test").unwrap(); - let contents = fs.read_to_string(file_path).await.unwrap(); + let file_path = format!("{test_dir}/myfile"); + std::fs::write(&file_path, b"Test").unwrap(); + let contents = fs.read_to_string(file_path).await.unwrap(); - assert_eq!(contents, "Test"); - teardown(test_dir); - } + assert_eq!(contents, "Test"); + teardown(test_dir); + } - #[tokio::test] - async fn read_to_string_should_bubble_up_error_if_some_happens() { - let test_dir = setup(); - let fs = LocalFileSystem; + #[tokio::test] + async fn read_to_string_should_bubble_up_error_if_some_happens() { + let test_dir = setup(); + let fs = LocalFileSystem; - let file_path = format!("{test_dir}/myfile"); - // intentionally forget to create file to force error - let err = fs.read_to_string(file_path).await.unwrap_err(); + let file_path = format!("{test_dir}/myfile"); + // intentionally forget to create file to force error + let err = fs.read_to_string(file_path).await.unwrap_err(); - assert_eq!(err.to_string(), "No such file or directory (os error 2)"); - teardown(test_dir); - } + assert_eq!(err.to_string(), "No such file or directory (os error 2)"); + teardown(test_dir); + } - #[tokio::test] - async fn write_should_create_a_new_file_at_path_with_contents() { - let test_dir = setup(); - let fs = LocalFileSystem; + #[tokio::test] + async fn write_should_create_a_new_file_at_path_with_contents() { + let test_dir = setup(); + let fs = LocalFileSystem; - let file_path = format!("{test_dir}/myfile"); - fs.write(&file_path, "Test").await.unwrap(); + let file_path = format!("{test_dir}/myfile"); + fs.write(&file_path, "Test").await.unwrap(); - assert_eq!(std::fs::read_to_string(file_path).unwrap(), "Test"); - teardown(test_dir); - } + assert_eq!(std::fs::read_to_string(file_path).unwrap(), "Test"); + teardown(test_dir); + } - #[tokio::test] - async fn write_should_overwrite_an_existing_file_with_contents() { - let test_dir = setup(); - let fs = LocalFileSystem; + #[tokio::test] + async fn write_should_overwrite_an_existing_file_with_contents() { + let test_dir = setup(); + let fs = LocalFileSystem; - let file_path = format!("{test_dir}/myfile"); - std::fs::write(&file_path, "Test").unwrap(); - assert_eq!(std::fs::read_to_string(&file_path).unwrap(), "Test"); - fs.write(&file_path, "Test updated").await.unwrap(); + let file_path = format!("{test_dir}/myfile"); + std::fs::write(&file_path, "Test").unwrap(); + assert_eq!(std::fs::read_to_string(&file_path).unwrap(), "Test"); + fs.write(&file_path, "Test updated").await.unwrap(); - assert_eq!(std::fs::read_to_string(file_path).unwrap(), "Test updated"); - teardown(test_dir); - } + assert_eq!(std::fs::read_to_string(file_path).unwrap(), "Test updated"); + teardown(test_dir); + } - #[tokio::test] - async fn write_should_bubble_up_error_if_some_happens() { - let test_dir = setup(); - let fs = LocalFileSystem; + #[tokio::test] + async fn write_should_bubble_up_error_if_some_happens() { + let test_dir = setup(); + let fs = LocalFileSystem; - let file_path = format!("{test_dir}/myfile"); - // intentionally create directory instead of file to force error - std::fs::create_dir(&file_path).unwrap(); - let err = fs.write(&file_path, "Test").await.unwrap_err(); + let file_path = format!("{test_dir}/myfile"); + // intentionally create directory instead of file to force error + std::fs::create_dir(&file_path).unwrap(); + let err = fs.write(&file_path, "Test").await.unwrap_err(); - assert_eq!(err.to_string(), "Is a directory (os error 21)"); - teardown(test_dir); - } + assert_eq!(err.to_string(), "Is a directory (os error 21)"); + teardown(test_dir); + } - #[tokio::test] - async fn append_should_create_a_new_file_at_path_with_contents() { - let test_dir = setup(); - let fs = LocalFileSystem; + #[tokio::test] + async fn append_should_create_a_new_file_at_path_with_contents() { + let test_dir = setup(); + let fs = LocalFileSystem; - let file_path = format!("{test_dir}/myfile"); - fs.append(&file_path, "Test").await.unwrap(); + let file_path = format!("{test_dir}/myfile"); + fs.append(&file_path, "Test").await.unwrap(); - assert_eq!(std::fs::read_to_string(file_path).unwrap(), "Test"); - teardown(test_dir); - } + assert_eq!(std::fs::read_to_string(file_path).unwrap(), "Test"); + teardown(test_dir); + } - #[tokio::test] - async fn append_should_updates_an_existing_file_by_appending_contents() { - let test_dir = setup(); - let fs = LocalFileSystem; + #[tokio::test] + async fn append_should_updates_an_existing_file_by_appending_contents() { + let test_dir = setup(); + let fs = LocalFileSystem; - let file_path = format!("{test_dir}/myfile"); - std::fs::write(&file_path, "Test").unwrap(); - assert_eq!(std::fs::read_to_string(&file_path).unwrap(), "Test"); - fs.append(&file_path, " updated").await.unwrap(); + let file_path = format!("{test_dir}/myfile"); + std::fs::write(&file_path, "Test").unwrap(); + assert_eq!(std::fs::read_to_string(&file_path).unwrap(), "Test"); + fs.append(&file_path, " updated").await.unwrap(); - assert_eq!(std::fs::read_to_string(file_path).unwrap(), "Test updated"); - teardown(test_dir); - } + assert_eq!(std::fs::read_to_string(file_path).unwrap(), "Test updated"); + teardown(test_dir); + } - #[tokio::test] - async fn append_should_bubble_up_error_if_some_happens() { - let test_dir = setup(); - let fs = LocalFileSystem; + #[tokio::test] + async fn append_should_bubble_up_error_if_some_happens() { + let test_dir = setup(); + let fs = LocalFileSystem; - let file_path = format!("{test_dir}/myfile"); - // intentionally create directory instead of file to force error - std::fs::create_dir(&file_path).unwrap(); - let err = fs.append(&file_path, "Test").await.unwrap_err(); + let file_path = format!("{test_dir}/myfile"); + // intentionally create directory instead of file to force error + std::fs::create_dir(&file_path).unwrap(); + let err = fs.append(&file_path, "Test").await.unwrap_err(); - assert_eq!(err.to_string(), "Is a directory (os error 21)"); - teardown(test_dir); - } + assert_eq!(err.to_string(), "Is a directory (os error 21)"); + teardown(test_dir); + } - #[tokio::test] - async fn copy_should_create_a_duplicate_of_source() { - let test_dir = setup(); - let fs = LocalFileSystem; + #[tokio::test] + async fn copy_should_create_a_duplicate_of_source() { + let test_dir = setup(); + let fs = LocalFileSystem; - let from_path = format!("{test_dir}/myfile"); - std::fs::write(&from_path, "Test").unwrap(); - let to_path = format!("{test_dir}/mycopy"); - fs.copy(&from_path, &to_path).await.unwrap(); + let from_path = format!("{test_dir}/myfile"); + std::fs::write(&from_path, "Test").unwrap(); + let to_path = format!("{test_dir}/mycopy"); + fs.copy(&from_path, &to_path).await.unwrap(); - assert_eq!(std::fs::read_to_string(to_path).unwrap(), "Test"); - teardown(test_dir); - } + assert_eq!(std::fs::read_to_string(to_path).unwrap(), "Test"); + teardown(test_dir); + } - #[tokio::test] - async fn copy_should_ovewrite_destination_if_alread_exists() { - let test_dir = setup(); - let fs = LocalFileSystem; + #[tokio::test] + async fn copy_should_ovewrite_destination_if_alread_exists() { + let test_dir = setup(); + let fs = LocalFileSystem; - let from_path = format!("{test_dir}/myfile"); - std::fs::write(&from_path, "Test").unwrap(); - let to_path = format!("{test_dir}/mycopy"); - std::fs::write(&from_path, "Some content").unwrap(); - fs.copy(&from_path, &to_path).await.unwrap(); + let from_path = format!("{test_dir}/myfile"); + std::fs::write(&from_path, "Test").unwrap(); + let to_path = format!("{test_dir}/mycopy"); + std::fs::write(&from_path, "Some content").unwrap(); + fs.copy(&from_path, &to_path).await.unwrap(); - assert_eq!(std::fs::read_to_string(to_path).unwrap(), "Some content"); - teardown(test_dir); - } + assert_eq!(std::fs::read_to_string(to_path).unwrap(), "Some content"); + teardown(test_dir); + } - #[tokio::test] - async fn copy_should_bubble_up_error_if_some_happens() { - let test_dir = setup(); - let fs = LocalFileSystem; + #[tokio::test] + async fn copy_should_bubble_up_error_if_some_happens() { + let test_dir = setup(); + let fs = LocalFileSystem; - let from_path = format!("{test_dir}/nonexistentfile"); - let to_path = format!("{test_dir}/mycopy"); - let err = fs.copy(&from_path, &to_path).await.unwrap_err(); + let from_path = format!("{test_dir}/nonexistentfile"); + let to_path = format!("{test_dir}/mycopy"); + let err = fs.copy(&from_path, &to_path).await.unwrap_err(); - assert_eq!(err.to_string(), "No such file or directory (os error 2)"); - teardown(test_dir); - } + assert_eq!(err.to_string(), "No such file or directory (os error 2)"); + teardown(test_dir); + } - #[tokio::test] - async fn set_mode_should_update_the_file_mode_at_path() { - let test_dir = setup(); - let fs = LocalFileSystem; - let path = format!("{test_dir}/myfile"); - std::fs::write(&path, "Test").unwrap(); - assert!(std::fs::metadata(&path).unwrap().permissions().mode() != (FILE_BITS + 0o400)); + #[tokio::test] + async fn set_mode_should_update_the_file_mode_at_path() { + let test_dir = setup(); + let fs = LocalFileSystem; + let path = format!("{test_dir}/myfile"); + std::fs::write(&path, "Test").unwrap(); + assert!(std::fs::metadata(&path).unwrap().permissions().mode() != (FILE_BITS + 0o400)); - fs.set_mode(&path, 0o400).await.unwrap(); + fs.set_mode(&path, 0o400).await.unwrap(); - assert_eq!( - std::fs::metadata(&path).unwrap().permissions().mode(), - FILE_BITS + 0o400 - ); - teardown(test_dir); - } + assert_eq!(std::fs::metadata(&path).unwrap().permissions().mode(), FILE_BITS + 0o400); + teardown(test_dir); + } - #[tokio::test] - async fn set_mode_should_update_the_directory_mode_at_path() { - let test_dir = setup(); - let fs = LocalFileSystem; - let path = format!("{test_dir}/mydir"); - std::fs::create_dir(&path).unwrap(); - assert!(std::fs::metadata(&path).unwrap().permissions().mode() != (DIR_BITS + 0o700)); + #[tokio::test] + async fn set_mode_should_update_the_directory_mode_at_path() { + let test_dir = setup(); + let fs = LocalFileSystem; + let path = format!("{test_dir}/mydir"); + std::fs::create_dir(&path).unwrap(); + assert!(std::fs::metadata(&path).unwrap().permissions().mode() != (DIR_BITS + 0o700)); - fs.set_mode(&path, 0o700).await.unwrap(); + fs.set_mode(&path, 0o700).await.unwrap(); - assert_eq!( - std::fs::metadata(&path).unwrap().permissions().mode(), - DIR_BITS + 0o700 - ); - teardown(test_dir); - } + assert_eq!(std::fs::metadata(&path).unwrap().permissions().mode(), DIR_BITS + 0o700); + teardown(test_dir); + } - #[tokio::test] - async fn set_mode_should_bubble_up_error_if_some_happens() { - let test_dir = setup(); - let fs = LocalFileSystem; - let path = format!("{test_dir}/somemissingfile"); - // intentionnally don't create file + #[tokio::test] + async fn set_mode_should_bubble_up_error_if_some_happens() { + let test_dir = setup(); + let fs = LocalFileSystem; + let path = format!("{test_dir}/somemissingfile"); + // intentionnally don't create file - let err = fs.set_mode(&path, 0o400).await.unwrap_err(); + let err = fs.set_mode(&path, 0o400).await.unwrap_err(); - assert_eq!(err.to_string(), "No such file or directory (os error 2)"); - teardown(test_dir); - } + assert_eq!(err.to_string(), "No such file or directory (os error 2)"); + teardown(test_dir); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/support/src/net.rs b/vendor/pezkuwi-zombienet-sdk/crates/support/src/net.rs index 7869b024..298bb4e4 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/support/src/net.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/support/src/net.rs @@ -8,53 +8,53 @@ use crate::constants::THIS_IS_A_BUG; type Result = std::result::Result>; pub async fn download_file(url: String, dest: String) -> Result<()> { - let response = reqwest::get(url).await?; - let mut file = std::fs::File::create(dest)?; - let mut content = Cursor::new(response.bytes().await?); - std::io::copy(&mut content, &mut file)?; - Ok(()) + let response = reqwest::get(url).await?; + let mut file = std::fs::File::create(dest)?; + let mut content = Cursor::new(response.bytes().await?); + std::io::copy(&mut content, &mut file)?; + Ok(()) } pub async fn wait_ws_ready(url: &str) -> Result<()> { - let mut parsed = Url::from_str(url)?; - parsed - .set_scheme("http") - .map_err(|_| anyhow::anyhow!("Can not set the scheme, {THIS_IS_A_BUG}"))?; + let mut parsed = Url::from_str(url)?; + parsed + .set_scheme("http") + .map_err(|_| anyhow::anyhow!("Can not set the scheme, {THIS_IS_A_BUG}"))?; - let http_client = reqwest::Client::new(); - loop { - let req = Request::new(Method::OPTIONS, parsed.clone()); - let res = http_client.execute(req).await; - match res { - Ok(res) => { - if res.status() == StatusCode::OK { - // ready to go! - break; - } + let http_client = reqwest::Client::new(); + loop { + let req = Request::new(Method::OPTIONS, parsed.clone()); + let res = http_client.execute(req).await; + match res { + Ok(res) => { + if res.status() == StatusCode::OK { + // ready to go! + break; + } - trace!("http_client status: {}, continuing...", res.status()); - }, - Err(e) => { - if !skip_err_while_waiting(&e) { - return Err(e.into()); - } + trace!("http_client status: {}, continuing...", res.status()); + }, + Err(e) => { + if !skip_err_while_waiting(&e) { + return Err(e.into()); + } - trace!("http_client err: {}, continuing... ", e.to_string()); - }, - } + trace!("http_client err: {}, continuing... ", e.to_string()); + }, + } - tokio::time::sleep(Duration::from_secs(1)).await; - } + tokio::time::sleep(Duration::from_secs(1)).await; + } - Ok(()) + Ok(()) } pub fn skip_err_while_waiting(e: &reqwest::Error) -> bool { - // if the error is connecting/request could be the case that the node - // is not listening yet, so we keep waiting - // Skipped errs like: - // 'tcp connect error: Connection refused (os error 61)' - // 'operation was canceled: connection closed before message completed' - // 'connection error: Connection reset by peer (os error 54)' - e.is_connect() || e.is_request() + // if the error is connecting/request could be the case that the node + // is not listening yet, so we keep waiting + // Skipped errs like: + // 'tcp connect error: Connection refused (os error 61)' + // 'operation was canceled: connection closed before message completed' + // 'connection error: Connection reset by peer (os error 54)' + e.is_connect() || e.is_request() } diff --git a/vendor/pezkuwi-zombienet-sdk/crates/support/src/replacer.rs b/vendor/pezkuwi-zombienet-sdk/crates/support/src/replacer.rs index 4f5b1b56..d1593e7e 100644 --- a/vendor/pezkuwi-zombienet-sdk/crates/support/src/replacer.rs +++ b/vendor/pezkuwi-zombienet-sdk/crates/support/src/replacer.rs @@ -7,191 +7,189 @@ use tracing::{trace, warn}; use crate::constants::{SHOULD_COMPILE, THIS_IS_A_BUG}; lazy_static! { - static ref RE: Regex = Regex::new(r#"\{\{([a-zA-Z0-9_]*)\}\}"#) - .unwrap_or_else(|_| panic!("{SHOULD_COMPILE}, {THIS_IS_A_BUG}")); - static ref TOKEN_PLACEHOLDER: Regex = Regex::new(r#"\{\{ZOMBIE:(.*?):(.*?)\}\}"#) - .unwrap_or_else(|_| panic!("{SHOULD_COMPILE}, {THIS_IS_A_BUG}")); - static ref PLACEHOLDER_COMPAT: HashMap<&'static str, &'static str> = { - let mut m = HashMap::new(); - m.insert("multiAddress", "multiaddr"); - m.insert("wsUri", "ws_uri"); - m.insert("prometheusUri", "prometheus_uri"); + static ref RE: Regex = Regex::new(r#"\{\{([a-zA-Z0-9_]*)\}\}"#) + .unwrap_or_else(|_| panic!("{SHOULD_COMPILE}, {THIS_IS_A_BUG}")); + static ref TOKEN_PLACEHOLDER: Regex = Regex::new(r#"\{\{ZOMBIE:(.*?):(.*?)\}\}"#) + .unwrap_or_else(|_| panic!("{SHOULD_COMPILE}, {THIS_IS_A_BUG}")); + static ref PLACEHOLDER_COMPAT: HashMap<&'static str, &'static str> = { + let mut m = HashMap::new(); + m.insert("multiAddress", "multiaddr"); + m.insert("wsUri", "ws_uri"); + m.insert("prometheusUri", "prometheus_uri"); - m - }; + m + }; } /// Return true if the text contains any TOKEN_PLACEHOLDER pub fn has_tokens(text: &str) -> bool { - TOKEN_PLACEHOLDER.is_match(text) + TOKEN_PLACEHOLDER.is_match(text) } pub fn apply_replacements(text: &str, replacements: &HashMap<&str, &str>) -> String { - let augmented_text = RE.replace_all(text, |caps: &Captures| { - if let Some(replacements_value) = replacements.get(&caps[1]) { - replacements_value.to_string() - } else { - caps[0].to_string() - } - }); + let augmented_text = RE.replace_all(text, |caps: &Captures| { + if let Some(replacements_value) = replacements.get(&caps[1]) { + replacements_value.to_string() + } else { + caps[0].to_string() + } + }); - augmented_text.to_string() + augmented_text.to_string() } pub fn apply_env_replacements(text: &str) -> String { - let augmented_text = RE.replace_all(text, |caps: &Captures| { - if let Ok(replacements_value) = std::env::var(&caps[1]) { - replacements_value - } else { - caps[0].to_string() - } - }); + let augmented_text = RE.replace_all(text, |caps: &Captures| { + if let Ok(replacements_value) = std::env::var(&caps[1]) { + replacements_value + } else { + caps[0].to_string() + } + }); - augmented_text.to_string() + augmented_text.to_string() } pub fn apply_running_network_replacements(text: &str, network: &serde_json::Value) -> String { - let augmented_text = TOKEN_PLACEHOLDER.replace_all(text, |caps: &Captures| { - trace!("appling replacements for caps: {caps:#?}"); - if let Some(node) = network.get(&caps[1]) { - trace!("caps1 {} - node: {node}", &caps[1]); - let field = *PLACEHOLDER_COMPAT.get(&caps[2]).unwrap_or(&&caps[2]); - if let Some(val) = node.get(field) { - trace!("caps2 {} - node: {node}", field); - val.as_str().unwrap_or("Invalid string").to_string() - } else { - warn!( - "⚠️ The node with name {} doesn't have the value {} in context", - &caps[1], &caps[2] - ); - caps[0].to_string() - } - } else { - warn!("⚠️ No node with name {} in context", &caps[1]); - caps[0].to_string() - } - }); + let augmented_text = TOKEN_PLACEHOLDER.replace_all(text, |caps: &Captures| { + trace!("appling replacements for caps: {caps:#?}"); + if let Some(node) = network.get(&caps[1]) { + trace!("caps1 {} - node: {node}", &caps[1]); + let field = *PLACEHOLDER_COMPAT.get(&caps[2]).unwrap_or(&&caps[2]); + if let Some(val) = node.get(field) { + trace!("caps2 {} - node: {node}", field); + val.as_str().unwrap_or("Invalid string").to_string() + } else { + warn!( + "⚠️ The node with name {} doesn't have the value {} in context", + &caps[1], &caps[2] + ); + caps[0].to_string() + } + } else { + warn!("⚠️ No node with name {} in context", &caps[1]); + caps[0].to_string() + } + }); - augmented_text.to_string() + augmented_text.to_string() } pub fn get_tokens_to_replace(text: &str) -> HashSet { - let mut tokens = HashSet::new(); + let mut tokens = HashSet::new(); - TOKEN_PLACEHOLDER - .captures_iter(text) - .for_each(|caps: Captures| { - tokens.insert(caps[1].to_string()); - }); + TOKEN_PLACEHOLDER.captures_iter(text).for_each(|caps: Captures| { + tokens.insert(caps[1].to_string()); + }); - tokens + tokens } #[cfg(test)] mod tests { - use serde_json::json; + use serde_json::json; - use super::*; + use super::*; - #[test] - fn replace_should_works() { - let text = "some {{namespace}}"; - let mut replacements = HashMap::new(); - replacements.insert("namespace", "demo-123"); - let res = apply_replacements(text, &replacements); - assert_eq!("some demo-123".to_string(), res); - } + #[test] + fn replace_should_works() { + let text = "some {{namespace}}"; + let mut replacements = HashMap::new(); + replacements.insert("namespace", "demo-123"); + let res = apply_replacements(text, &replacements); + assert_eq!("some demo-123".to_string(), res); + } - #[test] - fn replace_env_should_works() { - let text = "some {{namespace}}"; - std::env::set_var("namespace", "demo-123"); - // let mut replacements = HashMap::new(); - // replacements.insert("namespace", "demo-123"); - let res = apply_env_replacements(text); - assert_eq!("some demo-123".to_string(), res); - } + #[test] + fn replace_env_should_works() { + let text = "some {{namespace}}"; + std::env::set_var("namespace", "demo-123"); + // let mut replacements = HashMap::new(); + // replacements.insert("namespace", "demo-123"); + let res = apply_env_replacements(text); + assert_eq!("some demo-123".to_string(), res); + } - #[test] - fn replace_multiple_should_works() { - let text = r#"some {{namespace}} + #[test] + fn replace_multiple_should_works() { + let text = r#"some {{namespace}} other is {{other}}"#; - let augmented_text = r#"some demo-123 + let augmented_text = r#"some demo-123 other is other-123"#; - let mut replacements = HashMap::new(); - replacements.insert("namespace", "demo-123"); - replacements.insert("other", "other-123"); - let res = apply_replacements(text, &replacements); - assert_eq!(augmented_text, res); - } + let mut replacements = HashMap::new(); + replacements.insert("namespace", "demo-123"); + replacements.insert("other", "other-123"); + let res = apply_replacements(text, &replacements); + assert_eq!(augmented_text, res); + } - #[test] - fn replace_multiple_with_missing_should_works() { - let text = r#"some {{namespace}} + #[test] + fn replace_multiple_with_missing_should_works() { + let text = r#"some {{namespace}} other is {{other}}"#; - let augmented_text = r#"some demo-123 + let augmented_text = r#"some demo-123 other is {{other}}"#; - let mut replacements = HashMap::new(); - replacements.insert("namespace", "demo-123"); + let mut replacements = HashMap::new(); + replacements.insert("namespace", "demo-123"); - let res = apply_replacements(text, &replacements); - assert_eq!(augmented_text, res); - } + let res = apply_replacements(text, &replacements); + assert_eq!(augmented_text, res); + } - #[test] - fn replace_without_replacement_should_leave_text_unchanged() { - let text = "some {{namespace}}"; - let mut replacements = HashMap::new(); - replacements.insert("other", "demo-123"); - let res = apply_replacements(text, &replacements); - assert_eq!(text.to_string(), res); - } + #[test] + fn replace_without_replacement_should_leave_text_unchanged() { + let text = "some {{namespace}}"; + let mut replacements = HashMap::new(); + replacements.insert("other", "demo-123"); + let res = apply_replacements(text, &replacements); + assert_eq!(text.to_string(), res); + } - #[test] - fn replace_running_network_should_work() { - let network = json!({ - "alice" : { - "multiaddr": "some/demo/127.0.0.1" - } - }); + #[test] + fn replace_running_network_should_work() { + let network = json!({ + "alice" : { + "multiaddr": "some/demo/127.0.0.1" + } + }); - let res = apply_running_network_replacements("{{ZOMBIE:alice:multiaddr}}", &network); - assert_eq!(res.as_str(), "some/demo/127.0.0.1"); - } + let res = apply_running_network_replacements("{{ZOMBIE:alice:multiaddr}}", &network); + assert_eq!(res.as_str(), "some/demo/127.0.0.1"); + } - #[test] - fn replace_running_network_with_compat_should_work() { - let network = json!({ - "alice" : { - "multiaddr": "some/demo/127.0.0.1" - } - }); + #[test] + fn replace_running_network_with_compat_should_work() { + let network = json!({ + "alice" : { + "multiaddr": "some/demo/127.0.0.1" + } + }); - let res = apply_running_network_replacements("{{ZOMBIE:alice:multiAddress}}", &network); - assert_eq!(res.as_str(), "some/demo/127.0.0.1"); - } + let res = apply_running_network_replacements("{{ZOMBIE:alice:multiAddress}}", &network); + assert_eq!(res.as_str(), "some/demo/127.0.0.1"); + } - #[test] - fn replace_running_network_with_missing_field_should_not_replace_nothing() { - let network = json!({ - "alice" : { - "multiaddr": "some/demo/127.0.0.1" - } - }); + #[test] + fn replace_running_network_with_missing_field_should_not_replace_nothing() { + let network = json!({ + "alice" : { + "multiaddr": "some/demo/127.0.0.1" + } + }); - let res = apply_running_network_replacements("{{ZOMBIE:alice:someField}}", &network); - assert_eq!(res.as_str(), "{{ZOMBIE:alice:someField}}"); - } + let res = apply_running_network_replacements("{{ZOMBIE:alice:someField}}", &network); + assert_eq!(res.as_str(), "{{ZOMBIE:alice:someField}}"); + } - #[test] - fn get_tokens_to_replace_should_work() { - let res = get_tokens_to_replace("{{ZOMBIE:alice:multiaddr}} {{ZOMBIE:bob:multiaddr}}"); - let mut expected = HashSet::new(); - expected.insert("alice".to_string()); - expected.insert("bob".to_string()); + #[test] + fn get_tokens_to_replace_should_work() { + let res = get_tokens_to_replace("{{ZOMBIE:alice:multiaddr}} {{ZOMBIE:bob:multiaddr}}"); + let mut expected = HashSet::new(); + expected.insert("alice".to_string()); + expected.insert("bob".to_string()); - assert_eq!(res, expected); - } + assert_eq!(res, expected); + } } diff --git a/vendor/pezkuwi-zombienet-sdk/rustfmt.toml b/vendor/pezkuwi-zombienet-sdk/rustfmt.toml index 01533ce7..496a98e4 100644 --- a/vendor/pezkuwi-zombienet-sdk/rustfmt.toml +++ b/vendor/pezkuwi-zombienet-sdk/rustfmt.toml @@ -1,26 +1,19 @@ -# https://rust-lang.github.io/rustfmt/?version=v1.7.0 +# Pezkuwi ZombieNet SDK - Stable Rustfmt Configuration +# Only stable features for compatibility with CI -# general -indent_style = "Block" +# Basic (stable) +hard_tabs = true +max_width = 100 +use_small_heuristics = "Max" -# rewriting -condense_wildcard_suffixes = true +# Imports (stable) +reorder_imports = true +reorder_modules = true + +# Consistency (stable) +newline_style = "Unix" + +# Misc (stable) match_block_trailing_comma = true use_field_init_shorthand = true use_try_shorthand = true - -# normalization -normalize_comments = true -normalize_doc_attributes = true - -# reordering -reorder_impl_items = true -reorder_imports = true -reorder_modules = true -imports_granularity = "Crate" -group_imports = "StdExternalCrate" - -# additional formating -format_code_in_doc_comments = true -format_macro_matchers = true -format_macro_bodies = true