From 439a8dea5f54c689095cf0cdd74dc1eeb598f354 Mon Sep 17 00:00:00 2001
From: PG Herveou <pgherveou@gmail.com>
Date: Tue, 7 Nov 2023 22:00:46 +0100
Subject: [PATCH] Expose Top level API through zombienet-sdk crate (#126)

Following up on take 1
https://github.com/paritytech/zombienet-sdk/pull/124

Apart from better logs, I realize that one of the only thing missing now
for us to start consuming this project is a top level package available
(with it's dependencies) on crates.io. This PR serves mainly as a
discussion point to get there.
It would be great if we could publish an early version (0.0.1?) on
crates.io so that we can start using this asap.
---
 Cargo.toml                                    |  1 +
 crates/examples/Cargo.toml                    |  7 +----
 .../examples/simple_network_example.rs        | 18 +++--------
 crates/examples/examples/small_network.rs     |  2 +-
 .../examples/small_network_with_default.rs    | 18 ++++-------
 .../examples/small_network_with_para.rs       | 18 ++++-------
 crates/orchestrator/src/lib.rs                |  4 +--
 crates/orchestrator/src/network.rs            | 21 +++++++++----
 crates/provider/src/lib.rs                    |  2 +-
 crates/sdk/Cargo.toml                         | 16 ++++++++++
 crates/sdk/src/lib.rs                         | 31 +++++++++++++++++++
 11 files changed, 85 insertions(+), 53 deletions(-)
 create mode 100644 crates/sdk/Cargo.toml
 create mode 100644 crates/sdk/src/lib.rs

diff --git a/Cargo.toml b/Cargo.toml
index 83512b9..90b06f5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,7 @@
 [workspace]
 resolver = "2"
 members = [
+  "crates/sdk",
   "crates/examples",
   "crates/support",
   "crates/configuration",
diff --git a/crates/examples/Cargo.toml b/crates/examples/Cargo.toml
index a39fd0e..ca65ee1 100644
--- a/crates/examples/Cargo.toml
+++ b/crates/examples/Cargo.toml
@@ -6,12 +6,7 @@ edition = "2021"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-configuration = { path = "../configuration" }
-orchestrator = { path = "../orchestrator" }
-provider = { path = "../provider" }
-# TODO: we shouldn't need to pull from support, we need
-# to review the exports for neeeded types
-support = { path = "../support" }
+zombienet-sdk = { path = "../sdk" }
 tokio = { workspace = true }
 futures = { workspace = true }
 subxt = { workspace = true }
diff --git a/crates/examples/examples/simple_network_example.rs b/crates/examples/examples/simple_network_example.rs
index a8731cc..5464c99 100644
--- a/crates/examples/examples/simple_network_example.rs
+++ b/crates/examples/examples/simple_network_example.rs
@@ -1,21 +1,13 @@
-// use std::time::Duration;
-
-use configuration::NetworkConfig;
 use futures::stream::StreamExt;
-use orchestrator::Orchestrator;
-use provider::NativeProvider;
-use support::{fs::local::LocalFileSystem, process::os::OsProcessManager};
+use zombienet_sdk::{NetworkConfig, NetworkConfigExt};
 
 #[tokio::main]
 async fn main() -> Result<(), Box<dyn std::error::Error>> {
-    let config = NetworkConfig::load_from_toml("./crates/examples/examples/0001-simple.toml")
-        .expect("errored?");
+    let network = NetworkConfig::load_from_toml("./crates/examples/examples/0001-simple.toml")
+        .expect("errored?")
+        .spawn_native()
+        .await?;
 
-    let fs = LocalFileSystem;
-    let pm = OsProcessManager;
-    let provider = NativeProvider::new(fs.clone(), pm);
-    let orchestrator = Orchestrator::new(fs, provider);
-    let network = orchestrator.spawn(config).await?;
     println!("🚀🚀🚀🚀 network deployed");
 
     let client = network
diff --git a/crates/examples/examples/small_network.rs b/crates/examples/examples/small_network.rs
index bb44ece..6cef33d 100644
--- a/crates/examples/examples/small_network.rs
+++ b/crates/examples/examples/small_network.rs
@@ -1,4 +1,4 @@
-use configuration::NetworkConfigBuilder;
+use zombienet_sdk::NetworkConfigBuilder;
 
 fn main() {
     let config = NetworkConfigBuilder::new()
diff --git a/crates/examples/examples/small_network_with_default.rs b/crates/examples/examples/small_network_with_default.rs
index 663eb6c..5a32d76 100644
--- a/crates/examples/examples/small_network_with_default.rs
+++ b/crates/examples/examples/small_network_with_default.rs
@@ -1,11 +1,8 @@
-use configuration::NetworkConfigBuilder;
-use orchestrator::{AddNodeOpts, Orchestrator};
-use provider::NativeProvider;
-use support::{fs::local::LocalFileSystem, process::os::OsProcessManager};
+use zombienet_sdk::{AddNodeOpts, NetworkConfigBuilder, NetworkConfigExt};
 
 #[tokio::main]
 async fn main() -> Result<(), Box<dyn std::error::Error>> {
-    let config = NetworkConfigBuilder::new()
+    let mut network = NetworkConfigBuilder::new()
         .with_relaychain(|r| {
             r.with_chain("rococo-local")
                 .with_default_command("polkadot")
@@ -18,13 +15,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
                 .with_collator(|n| n.with_name("collator").with_command("polkadot-parachain"))
         })
         .build()
-        .unwrap();
+        .unwrap()
+        .spawn_native()
+        .await?;
 
-    let fs = LocalFileSystem;
-    let pm = OsProcessManager;
-    let provider = NativeProvider::new(fs.clone(), pm);
-    let orchestrator = Orchestrator::new(fs, provider);
-    let mut network = orchestrator.spawn(config).await?;
     println!("🚀🚀🚀🚀 network deployed");
     // add  a new node
     let opts = AddNodeOpts {
@@ -36,7 +30,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
     // TODO: add check to ensure if unique
     network.add_node("new1", opts, None).await?;
 
-    // Example of some opertions that you can do
+    // Example of some operations that you can do
     // with `nodes` (e.g pause, resume, restart)
 
     // Get a ref to the node
diff --git a/crates/examples/examples/small_network_with_para.rs b/crates/examples/examples/small_network_with_para.rs
index 81ac29c..d11b8a3 100644
--- a/crates/examples/examples/small_network_with_para.rs
+++ b/crates/examples/examples/small_network_with_para.rs
@@ -1,13 +1,10 @@
 use std::time::Duration;
 
-use configuration::{NetworkConfigBuilder, RegistrationStrategy};
-use orchestrator::{AddNodeOpts, Orchestrator};
-use provider::NativeProvider;
-use support::{fs::local::LocalFileSystem, process::os::OsProcessManager};
+use zombienet_sdk::{AddNodeOpts, NetworkConfigBuilder, NetworkConfigExt, RegistrationStrategy};
 
 #[tokio::main]
 async fn main() -> Result<(), Box<dyn std::error::Error>> {
-    let config = NetworkConfigBuilder::new()
+    let mut network = NetworkConfigBuilder::new()
         .with_relaychain(|r| {
             r.with_chain("rococo-local")
                 .with_default_command("polkadot")
@@ -21,13 +18,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
                 .with_collator(|n| n.with_name("collator").with_command("polkadot-parachain"))
         })
         .build()
-        .unwrap();
+        .unwrap()
+        .spawn_native()
+        .await?;
 
-    let fs = LocalFileSystem;
-    let pm = OsProcessManager;
-    let provider = NativeProvider::new(fs.clone(), pm);
-    let orchestrator = Orchestrator::new(fs, provider);
-    let mut network = orchestrator.spawn(config).await?;
     println!("🚀🚀🚀🚀 network deployed");
     // add  a new node
     let opts = AddNodeOpts {
@@ -41,7 +35,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
 
     tokio::time::sleep(Duration::from_secs(2)).await;
 
-    // Example of some opertions that you can do
+    // Example of some operations that you can do
     // with `nodes` (e.g pause, resume, restart)
 
     tokio::time::sleep(Duration::from_secs(10)).await;
diff --git a/crates/orchestrator/src/lib.rs b/crates/orchestrator/src/lib.rs
index df24292..a705b72 100644
--- a/crates/orchestrator/src/lib.rs
+++ b/crates/orchestrator/src/lib.rs
@@ -1,9 +1,9 @@
 // TODO(Javier): Remove when we implement the logic in the orchestrator to spawn with the provider.
 #![allow(dead_code)]
 
-mod errors;
+pub mod errors;
 mod generators;
-mod network;
+pub mod network;
 mod network_helper;
 mod network_spec;
 mod shared;
diff --git a/crates/orchestrator/src/network.rs b/crates/orchestrator/src/network.rs
index 27ddc23..20edbd8 100644
--- a/crates/orchestrator/src/network.rs
+++ b/crates/orchestrator/src/network.rs
@@ -182,13 +182,22 @@ impl<T: FileSystem> Network<T> {
     // deregister and stop the collator?
     // remove_parachain()
 
-    pub fn get_node(&self, node_name: impl Into<String>) -> Result<&NetworkNode, anyhow::Error> {
-        let node_name = node_name.into();
-        if let Some(node) = self.nodes_by_name.get(&node_name) {
-            Ok(node)
-        } else {
-            Err(anyhow::anyhow!("can't find the node!"))
+    pub fn get_node(&self, name: impl Into<String>) -> Result<&NetworkNode, anyhow::Error> {
+        let name = &name.into();
+        if let Some(node) = self.nodes_by_name.get(name) {
+            return Ok(node);
         }
+
+        let list = self
+            .nodes_by_name
+            .keys()
+            .cloned()
+            .collect::<Vec<_>>()
+            .join(", ");
+
+        Err(anyhow::anyhow!(
+            "can't find node with name: {name:?}, should be one of {list}"
+        ))
     }
 
     pub fn nodes(&self) -> Vec<&NetworkNode> {
diff --git a/crates/provider/src/lib.rs b/crates/provider/src/lib.rs
index 4f48f0b..3ecb379 100644
--- a/crates/provider/src/lib.rs
+++ b/crates/provider/src/lib.rs
@@ -86,7 +86,7 @@ pub trait ProviderNamespace {
     async fn static_setup(&self) -> Result<(), ProviderError>;
 }
 
-pub type DynNamespace = Arc<dyn ProviderNamespace>;
+pub type DynNamespace = Arc<dyn ProviderNamespace + Send + Sync>;
 
 type ExecutionResult = Result<String, (ExitStatus, String)>;
 
diff --git a/crates/sdk/Cargo.toml b/crates/sdk/Cargo.toml
new file mode 100644
index 0000000..3983f3b
--- /dev/null
+++ b/crates/sdk/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "zombienet-sdk"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+configuration = { path = "../configuration" }
+orchestrator = { path = "../orchestrator" }
+provider = { path = "../provider" }
+support = { path = "../support" }
+async-trait = { workspace = true }
+tokio = { workspace = true }
+futures = { workspace = true }
+subxt = { workspace = true }
diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs
new file mode 100644
index 0000000..f113b2a
--- /dev/null
+++ b/crates/sdk/src/lib.rs
@@ -0,0 +1,31 @@
+use async_trait::async_trait;
+pub use configuration::{NetworkConfig, NetworkConfigBuilder, RegistrationStrategy};
+pub use orchestrator::{errors::OrchestratorError, network::Network, AddNodeOpts, Orchestrator};
+use provider::NativeProvider;
+use support::{fs::local::LocalFileSystem, process::os::OsProcessManager};
+
+#[async_trait]
+pub trait NetworkConfigExt {
+    /// Spawns a network using the native 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<Network<LocalFileSystem>, OrchestratorError>;
+}
+
+#[async_trait]
+impl NetworkConfigExt for NetworkConfig {
+    async fn spawn_native(self) -> Result<Network<LocalFileSystem>, OrchestratorError> {
+        let provider = NativeProvider::new(LocalFileSystem {}, OsProcessManager {});
+        let orchestrator = Orchestrator::new(LocalFileSystem {}, provider);
+        orchestrator.spawn(self).await
+    }
+}
-- 
GitLab