Skip to content

Commit 3460ec1

Browse files
authored
Zombienet Improve DevX with new methods (#261)
Revamped version of #252, didn't overwrite that PR in case you guys want to compare both solutions. Fixes #251 TL;DR -> here is the suggestion: ```rust let network_config = NetworkConfigBuilder::with_nodes_and_chain( vec!["alice".to_string(), "bob".to_string()], "rococo-local", ) .with_collators_and_parachain_id(vec!["collator1".to_string(), "collator2".to_string()], 100) .build() .unwrap(); ``` I found an even better way than creating a wrapper. Here is the summary: - I want to protect the safety measures provided by the original crate as we discussed. - At the same time, I don't want to opt-out of the high-degree of configurability that `configuration` crate offers. Even though the aim is to grant better DevX to the community, it should still preserve the configuration options. - “advanced users can still use the `configuration` crate if they wanted to” is not a good argument imo. Here is the reason: - although there are many common settings amongst the projects in the ecosystem, probably most of the projects only tinkers with a specific setting w.r.t their needs, and this specific setting is most likely changing across projects. So, if we do not expose the tinkering options to people with this wrapper approach, most of the projects won’t use this wrapper. Then what is the point? - The aim should be providing the default options with a better DevX, whilst still providing a way to configure niche details somehow within the same API. - As first trial, I completed simple to use wrapper builders with the default settings. However, to expose the niche configurations, I had to copy-paste nearly every other function/method in the `configuration` crate. - And also, in order to comply with the `configuration` crate’s type state pattern, I had to export nearly all the states from `configuration` crate for the builder types in the `lib.rs`. The whole thing was quickly becoming ugly. - The difference between my wrapper and the `configuration` crate was, basically the extra methods that granted better DevX (for initializing the structs with default settings). - So I thought, instead of creating a new wrapper with tremendous amount of duplications, I can simply put these new methods into the `configuration` crate itself! Notes: - there are 2 new methods: (edited after last commit) - `with_nodes_and_chain` - `with_collators_and_parachain_id` - I also expanded the tests for these new methods. - I want to signal to the developers that these new methods are easier and faster to use compared to their equivalents, since they are utilizing the default settings, and should be much less intimidating to newcomers - So, I tried to name the methods accordingly, but they turned out to be a bit long. Don't know whether we can do better, I'm open to all suggestions. Hope it makes sense!
1 parent bd51865 commit 3460ec1

File tree

2 files changed

+191
-2
lines changed

2 files changed

+191
-2
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ members = [
99
"crates/provider",
1010
#"crates/test-runner",
1111
"crates/prom-metrics-parser",
12-
"crates/file-server"
12+
"crates/file-server",
1313
]
1414

1515
[workspace.package]

crates/configuration/src/network.rs

Lines changed: 190 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ use crate::{
1313
parachain::{self, ParachainConfig, ParachainConfigBuilder},
1414
relaychain::{self, RelaychainConfig, RelaychainConfigBuilder},
1515
shared::{
16-
helpers::merge_errors_vecs,
16+
errors::{ConfigError, ValidationError},
17+
helpers::{merge_errors, merge_errors_vecs},
1718
macros::states,
1819
node::NodeConfig,
1920
types::{Arg, AssetLocation, Chain, Command, Image, ValidationContext},
@@ -324,6 +325,32 @@ impl NetworkConfigBuilder<Initial> {
324325
Self::default()
325326
}
326327

328+
/// uses the default options for both the relay chain and the nodes
329+
/// the only required fields are the name of the nodes,
330+
/// and the name of the relay chain ("rococo-local", "polkadot", etc.)
331+
pub fn with_chain_and_nodes(
332+
relay_name: &str,
333+
node_names: Vec<String>,
334+
) -> NetworkConfigBuilder<WithRelaychain> {
335+
let network_config = NetworkConfigBuilder::new().with_relaychain(|relaychain| {
336+
let mut relaychain_with_node = relaychain
337+
.with_chain(relay_name)
338+
.with_node(|node| node.with_name(node_names.first().unwrap_or(&"".to_string())));
339+
340+
for node_name in node_names.iter().skip(1) {
341+
relaychain_with_node = relaychain_with_node
342+
.with_node(|node_builder| node_builder.with_name(node_name));
343+
}
344+
relaychain_with_node
345+
});
346+
347+
Self::transition(
348+
network_config.config,
349+
network_config.validation_context,
350+
network_config.errors,
351+
)
352+
}
353+
327354
/// Set the relay chain using a nested [`RelaychainConfigBuilder`].
328355
pub fn with_relaychain(
329356
self,
@@ -399,6 +426,43 @@ impl NetworkConfigBuilder<WithRelaychain> {
399426
}
400427
}
401428

429+
/// uses default settings for setting for:
430+
/// - the parachain,
431+
/// - the global settings
432+
/// - the hrmp channels
433+
///
434+
/// the only required parameters are the names of the collators as a vector,
435+
/// and the id of the parachain
436+
pub fn with_parachain_id_and_collators(self, id: u32, collator_names: Vec<String>) -> Self {
437+
if collator_names.is_empty() {
438+
return Self::transition(
439+
self.config,
440+
self.validation_context,
441+
merge_errors(
442+
self.errors,
443+
ConfigError::Parachain(id, ValidationError::CantBeEmpty().into()).into(),
444+
),
445+
);
446+
}
447+
448+
self.with_parachain(|parachain| {
449+
let mut parachain_config = parachain.with_id(id).with_collator(|collator| {
450+
collator
451+
.with_name(collator_names.first().unwrap_or(&"".to_string()))
452+
.validator(true)
453+
});
454+
455+
for collator_name in collator_names.iter().skip(1) {
456+
parachain_config = parachain_config
457+
.with_collator(|collator| collator.with_name(collator_name).validator(true));
458+
}
459+
parachain_config
460+
})
461+
462+
// TODO: if need to set global settings and hrmp channels
463+
// we can also do in here
464+
}
465+
402466
/// Add an HRMP channel using a nested [`HrmpChannelConfigBuilder`].
403467
pub fn with_hrmp_channel(
404468
self,
@@ -1437,4 +1501,129 @@ mod tests {
14371501
});
14381502
});
14391503
}
1504+
1505+
#[test]
1506+
fn with_chain_and_nodes_works() {
1507+
let network_config = NetworkConfigBuilder::with_chain_and_nodes(
1508+
"rococo-local",
1509+
vec!["alice".to_string(), "bob".to_string()],
1510+
)
1511+
.build()
1512+
.unwrap();
1513+
1514+
// relaychain
1515+
assert_eq!(network_config.relaychain().chain().as_str(), "rococo-local");
1516+
assert_eq!(network_config.relaychain().nodes().len(), 2);
1517+
let mut node_names = network_config.relaychain().nodes().into_iter();
1518+
let node1 = node_names.next().unwrap().name();
1519+
assert_eq!(node1, "alice");
1520+
let node2 = node_names.next().unwrap().name();
1521+
assert_eq!(node2, "bob");
1522+
1523+
// parachains
1524+
assert_eq!(network_config.parachains().len(), 0);
1525+
}
1526+
1527+
#[test]
1528+
fn with_chain_and_nodes_should_fail_with_empty_relay_name() {
1529+
let errors = NetworkConfigBuilder::with_chain_and_nodes("", vec!["alice".to_string()])
1530+
.build()
1531+
.unwrap_err();
1532+
1533+
assert_eq!(
1534+
errors.first().unwrap().to_string(),
1535+
"relaychain.chain: can't be empty"
1536+
);
1537+
}
1538+
1539+
#[test]
1540+
fn with_chain_and_nodes_should_fail_with_empty_node_list() {
1541+
let errors = NetworkConfigBuilder::with_chain_and_nodes("rococo-local", vec![])
1542+
.build()
1543+
.unwrap_err();
1544+
1545+
assert_eq!(
1546+
errors.first().unwrap().to_string(),
1547+
"relaychain.nodes[''].name: can't be empty"
1548+
);
1549+
}
1550+
1551+
#[test]
1552+
fn with_chain_and_nodes_should_fail_with_empty_node_name() {
1553+
let errors = NetworkConfigBuilder::with_chain_and_nodes(
1554+
"rococo-local",
1555+
vec!["alice".to_string(), "".to_string()],
1556+
)
1557+
.build()
1558+
.unwrap_err();
1559+
1560+
assert_eq!(
1561+
errors.first().unwrap().to_string(),
1562+
"relaychain.nodes[''].name: can't be empty"
1563+
);
1564+
}
1565+
1566+
#[test]
1567+
fn with_parachain_id_and_collators_works() {
1568+
let network_config = NetworkConfigBuilder::with_chain_and_nodes(
1569+
"rococo-local",
1570+
vec!["alice".to_string(), "bob".to_string()],
1571+
)
1572+
.with_parachain_id_and_collators(
1573+
100,
1574+
vec!["collator1".to_string(), "collator2".to_string()],
1575+
)
1576+
.build()
1577+
.unwrap();
1578+
1579+
// relaychain
1580+
assert_eq!(network_config.relaychain().chain().as_str(), "rococo-local");
1581+
assert_eq!(network_config.relaychain().nodes().len(), 2);
1582+
let mut node_names = network_config.relaychain().nodes().into_iter();
1583+
let node1 = node_names.next().unwrap().name();
1584+
assert_eq!(node1, "alice");
1585+
let node2 = node_names.next().unwrap().name();
1586+
assert_eq!(node2, "bob");
1587+
1588+
// parachains
1589+
assert_eq!(network_config.parachains().len(), 1);
1590+
let &parachain1 = network_config.parachains().first().unwrap();
1591+
assert_eq!(parachain1.id(), 100);
1592+
assert_eq!(parachain1.collators().len(), 2);
1593+
let mut collator_names = parachain1.collators().into_iter();
1594+
let collator1 = collator_names.next().unwrap().name();
1595+
assert_eq!(collator1, "collator1");
1596+
let collator2 = collator_names.next().unwrap().name();
1597+
assert_eq!(collator2, "collator2");
1598+
1599+
assert_eq!(parachain1.initial_balance(), 2_000_000_000_000);
1600+
}
1601+
1602+
#[test]
1603+
fn with_parachain_id_and_collators_should_fail_with_empty_collator_list() {
1604+
let errors =
1605+
NetworkConfigBuilder::with_chain_and_nodes("polkadot", vec!["alice".to_string()])
1606+
.with_parachain_id_and_collators(1, vec![])
1607+
.build()
1608+
.unwrap_err();
1609+
1610+
assert_eq!(
1611+
errors.first().unwrap().to_string(),
1612+
"parachain[1].can't be empty"
1613+
);
1614+
}
1615+
1616+
#[test]
1617+
fn with_parachain_id_and_collators_should_fail_with_empty_collator_name() {
1618+
let errors =
1619+
NetworkConfigBuilder::with_chain_and_nodes("polkadot", vec!["alice".to_string()])
1620+
.with_parachain_id_and_collators(1, vec!["collator1".to_string(), "".to_string()])
1621+
.build()
1622+
.unwrap_err();
1623+
1624+
assert_eq!(
1625+
errors.first().unwrap().to_string(),
1626+
"parachain[1].collators[''].name: can't be empty"
1627+
);
1628+
}
14401629
}

0 commit comments

Comments
 (0)