Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zombienet Improve DevX with new methods #261

Merged
merged 10 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ members = [
"crates/provider",
#"crates/test-runner",
"crates/prom-metrics-parser",
"crates/file-server"
"crates/file-server",
]

[workspace.package]
Expand Down
225 changes: 224 additions & 1 deletion crates/configuration/src/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use crate::{
parachain::{self, ParachainConfig, ParachainConfigBuilder},
relaychain::{self, RelaychainConfig, RelaychainConfigBuilder},
shared::{
helpers::merge_errors_vecs,
errors::ConfigError,
helpers::{merge_errors, merge_errors_vecs},
macros::states,
node::NodeConfig,
types::{Arg, AssetLocation, Chain, Command, Image, ValidationContext},
Expand Down Expand Up @@ -324,6 +325,48 @@ impl NetworkConfigBuilder<Initial> {
Self::default()
}

/// uses the default options for both the relay chain and the nodes
/// the only required fields are the name of the nodes,
/// and the name of the relay chain ("rococo-local", "polkadot", etc.)
pub fn with_chain_and_nodes(
relay_name: &str,
node_names: Vec<String>,
) -> NetworkConfigBuilder<WithRelaychain> {
let mut errors: Vec<anyhow::Error> = vec![];

if node_names.is_empty() {
ozgunozerk marked this conversation as resolved.
Show resolved Hide resolved
errors.push(ConfigError::Relaychain(anyhow!("node names list can't be empty")).into())
ozgunozerk marked this conversation as resolved.
Show resolved Hide resolved
}

for name in node_names.iter() {
if name.is_empty() {
errors.push(ConfigError::Relaychain(anyhow!("node name can't be empty")).into());
}
}

if relay_name.is_empty() {
errors.push(ConfigError::Relaychain(anyhow!("relaychain name can't be empty")).into());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
errors.push(ConfigError::Relaychain(anyhow!("relaychain name can't be empty")).into());
errors.push(ConfigError::Relaychain("relaychain name can't be empty").into());

why wrapped in anyhow if we already have an error variant.

Copy link
Contributor Author

@ozgunozerk ozgunozerk Sep 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ConfigError::Relaychain variant expects an anyhow::Error, but the suggestion passes a &str. Casting into() was unfortunately not sufficient to make the compiler happy.

anyhow!("relaychain name can't be empty") wraps the &str into an Error as required by the enum.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want, I can replace anyhow::Error with String in the Error enum in my next PR that will address closures and objects. Makes sense to merge both of the refactorings into a single PR imo.

}

let network_config = NetworkConfigBuilder::new().with_relaychain(|relaychain| {
let mut relaychain_with_node = relaychain.with_chain(relay_name).with_node(|node| {
node.with_name(node_names.first().unwrap_or(&"node1".to_string()))
}); // TODO: in order to not panic this, we provide a dummy value. However, returning a Result may be a better choice for this constructor
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If first fails means that we already have errors, so will be fail the whole config.


for node_name in node_names.iter().skip(1) {
relaychain_with_node = relaychain_with_node
.with_node(|node_builder| node_builder.with_name(node_name));
}
relaychain_with_node
});

Self::transition(
network_config.config,
network_config.validation_context,
errors,
)
}

/// Set the relay chain using a nested [`RelaychainConfigBuilder`].
pub fn with_relaychain(
self,
Expand Down Expand Up @@ -399,6 +442,61 @@ impl NetworkConfigBuilder<WithRelaychain> {
}
}

/// 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<String>) -> Self {
if collator_names.is_empty() {
return Self::transition(
self.config,
self.validation_context,
merge_errors(
self.errors,
ConfigError::Parachain(id, anyhow!("collator names list can't be empty"))
.into(),
),
);
}

for name in collator_names.iter() {
if name.is_empty() {
return Self::transition(
self.config,
self.validation_context,
merge_errors(
self.errors,
ConfigError::Parachain(id, anyhow!("collator name can't be empty")).into(),
),
);
}
}

self.with_parachain(|parachain| {
let mut parachain_config = parachain.with_id(id).with_collator(|collator| {
collator
.with_name(
collator_names
.first()
.expect("collator names list is not empty"),
)
.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));
ozgunozerk marked this conversation as resolved.
Show resolved Hide resolved
}
parachain_config
})

// 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,
Expand Down Expand Up @@ -1437,4 +1535,129 @@ mod tests {
});
});
}

#[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");

// parachains
assert_eq!(network_config.parachains().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();

assert_eq!(
errors.first().unwrap().to_string(),
"relaychain.relaychain name 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();

assert_eq!(
errors.first().unwrap().to_string(),
"relaychain.node names list 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();

assert_eq!(
errors.first().unwrap().to_string(),
"relaychain.node 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();

// 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");

// parachains
assert_eq!(network_config.parachains().len(), 1);
let &parachain1 = network_config.parachains().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);
}

#[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(),
"parachain[1].collator names list 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();

assert_eq!(
errors.first().unwrap().to_string(),
"parachain[1].collator name can't be empty"
);
}
}