Skip to main content

zombienet_provider/
lib.rs

1#![allow(clippy::expect_fun_call)]
2mod docker;
3mod kubernetes;
4mod native;
5pub mod shared;
6
7use std::{
8    collections::HashMap,
9    net::IpAddr,
10    path::{Path, PathBuf},
11    sync::Arc,
12    time::Duration,
13};
14
15use async_trait::async_trait;
16use configuration::types::AssetLocation;
17use shared::{
18    constants::LOCALHOST,
19    types::{
20        ExecutionResult, GenerateFilesOptions, ProviderCapabilities, RunCommandOptions,
21        RunScriptOptions, SpawnNodeOptions,
22    },
23};
24use support::fs::FileSystemError;
25use tracing::warn;
26
27use crate::shared::types::InnerSnapshotDb;
28
29#[derive(Debug, thiserror::Error)]
30#[allow(missing_docs)]
31pub enum ProviderError {
32    #[error("Failed to create client '{0}': {1}")]
33    CreateClientFailed(String, anyhow::Error),
34
35    #[error("Failed to create namespace '{0}': {1}")]
36    CreateNamespaceFailed(String, anyhow::Error),
37
38    #[error("Failed to spawn node '{0}': {1}")]
39    NodeSpawningFailed(String, anyhow::Error),
40
41    #[error("Error running command '{0}' {1}: {2}")]
42    RunCommandError(String, String, anyhow::Error),
43
44    #[error("Error running script'{0}': {1}")]
45    RunScriptError(String, anyhow::Error),
46
47    #[error("Invalid network configuration field {0}")]
48    InvalidConfig(String),
49
50    #[error("Failed to retrieve node available args using image {0} and command {1}: {2}")]
51    NodeAvailableArgsError(String, String, String),
52
53    #[error("Can not recover node: {0}")]
54    MissingNode(String),
55
56    #[error("Can not recover node: {0} info, field: {1}")]
57    MissingNodeInfo(String, String),
58
59    #[error("File generation failed: {0}")]
60    FileGenerationFailed(anyhow::Error),
61
62    #[error(transparent)]
63    FileSystemError(#[from] FileSystemError),
64
65    #[error("Invalid script path for {0}")]
66    InvalidScriptPath(anyhow::Error),
67
68    #[error("Script with path {0} not found")]
69    ScriptNotFound(PathBuf),
70
71    #[error("Failed to retrieve process ID for node '{0}'")]
72    ProcessIdRetrievalFailed(String),
73
74    #[error("Failed to pause node '{0}': {1}")]
75    PauseNodeFailed(String, anyhow::Error),
76
77    #[error("Failed to resume node '{0}': {1}")]
78    ResumeNodeFailed(String, anyhow::Error),
79
80    #[error("Failed to kill node '{0}': {1}")]
81    KillNodeFailed(String, anyhow::Error),
82
83    #[error("Failed to restart node '{0}': {1}")]
84    RestartNodeFailed(String, anyhow::Error),
85
86    #[error("Failed to destroy node '{0}': {1}")]
87    DestroyNodeFailed(String, anyhow::Error),
88
89    #[error("Failed to get logs for node '{0}': {1}")]
90    GetLogsFailed(String, anyhow::Error),
91
92    #[error("Failed to dump logs for node '{0}': {1}")]
93    DumpLogsFailed(String, anyhow::Error),
94
95    #[error("Failed to copy file from node '{0}': {1}")]
96    CopyFileFromNodeError(String, anyhow::Error),
97
98    #[error("Failed to setup fileserver: {0}")]
99    FileServerSetupError(anyhow::Error),
100
101    #[error("Error uploading file: '{0}': {1}")]
102    UploadFile(String, anyhow::Error),
103
104    #[error("Error downloading file: '{0}': {1}")]
105    DownloadFile(String, anyhow::Error),
106
107    #[error("Error sending file '{0}' to {1}: {2}")]
108    SendFile(String, String, anyhow::Error),
109
110    #[error("Error creating port-forward '{0}:{1}': {2}")]
111    PortForwardError(u16, u16, anyhow::Error),
112
113    #[error("Failed to delete namespace '{0}': {1}")]
114    DeleteNamespaceFailed(String, anyhow::Error),
115
116    #[error("Serialization error: {0}")]
117    SerializationError(#[from] serde_json::Error),
118
119    #[error("Failed to acquire lock: {0}")]
120    FailedToAcquireLock(String),
121
122    #[error("Failed to generate the snapshot for node {0}:  {1}")]
123    SnapshotDb(String, anyhow::Error),
124}
125
126#[async_trait]
127pub trait Provider {
128    fn name(&self) -> &str;
129
130    fn capabilities(&self) -> &ProviderCapabilities;
131
132    async fn namespaces(&self) -> HashMap<String, DynNamespace>;
133
134    async fn create_namespace(&self) -> Result<DynNamespace, ProviderError>;
135
136    async fn create_namespace_with_base_dir(
137        &self,
138        base_dir: &Path,
139    ) -> Result<DynNamespace, ProviderError>;
140
141    async fn create_namespace_from_json(
142        &self,
143        json_value: &serde_json::Value,
144    ) -> Result<DynNamespace, ProviderError>;
145}
146
147pub type DynProvider = Arc<dyn Provider + Send + Sync>;
148
149#[async_trait]
150pub trait ProviderNamespace {
151    fn name(&self) -> &str;
152
153    fn base_dir(&self) -> &PathBuf;
154
155    fn capabilities(&self) -> &ProviderCapabilities;
156
157    fn provider_name(&self) -> &str;
158
159    async fn detach(&self) {
160        // noop by default
161        warn!("Detach is not implemented for {}", self.name());
162    }
163
164    async fn is_detached(&self) -> bool {
165        // false by default
166        false
167    }
168
169    async fn nodes(&self) -> HashMap<String, DynNode>;
170
171    async fn get_node_available_args(
172        &self,
173        options: (String, Option<String>),
174    ) -> Result<String, ProviderError>;
175
176    async fn spawn_node(&self, options: &SpawnNodeOptions) -> Result<DynNode, ProviderError>;
177
178    async fn spawn_node_from_json(
179        &self,
180        json_value: &serde_json::Value,
181    ) -> Result<DynNode, ProviderError>;
182
183    async fn generate_files(&self, options: GenerateFilesOptions) -> Result<(), ProviderError>;
184
185    async fn destroy(&self) -> Result<(), ProviderError>;
186
187    async fn static_setup(&self) -> Result<(), ProviderError>;
188}
189
190pub type DynNamespace = Arc<dyn ProviderNamespace + Send + Sync>;
191
192#[async_trait]
193pub trait ProviderNode: erased_serde::Serialize {
194    fn name(&self) -> &str;
195
196    fn args(&self) -> Vec<&str>;
197
198    fn base_dir(&self) -> &PathBuf;
199
200    fn config_dir(&self) -> &PathBuf;
201
202    fn data_dir(&self) -> &PathBuf;
203
204    fn relay_data_dir(&self) -> &PathBuf;
205
206    fn scripts_dir(&self) -> &PathBuf;
207
208    fn log_path(&self) -> &PathBuf;
209
210    fn log_cmd(&self) -> String;
211
212    // Return the absolute path to the file in the `node` perspective
213    // TODO: purpose?
214    fn path_in_node(&self, file: &Path) -> PathBuf;
215
216    async fn logs(&self) -> Result<String, ProviderError>;
217
218    async fn dump_logs(&self, local_dest: PathBuf) -> Result<(), ProviderError>;
219
220    // By default return localhost, should be overrided for k8s
221    async fn ip(&self) -> Result<IpAddr, ProviderError> {
222        Ok(LOCALHOST)
223    }
224
225    // Noop by default (native/docker provider)
226    async fn create_port_forward(
227        &self,
228        _local_port: u16,
229        _remote_port: u16,
230    ) -> Result<Option<u16>, ProviderError> {
231        Ok(None)
232    }
233
234    async fn run_command(
235        &self,
236        options: RunCommandOptions,
237    ) -> Result<ExecutionResult, ProviderError>;
238
239    async fn run_script(&self, options: RunScriptOptions)
240        -> Result<ExecutionResult, ProviderError>;
241
242    async fn send_file(
243        &self,
244        local_file_path: &Path,
245        remote_file_path: &Path,
246        mode: &str,
247    ) -> Result<(), ProviderError>;
248
249    async fn receive_file(
250        &self,
251        remote_file_path: &Path,
252        local_file_path: &Path,
253    ) -> Result<(), ProviderError>;
254
255    async fn pause(&self) -> Result<(), ProviderError>;
256
257    async fn resume(&self) -> Result<(), ProviderError>;
258
259    async fn restart(&self, after: Option<Duration>) -> Result<(), ProviderError>;
260
261    async fn restart_with(
262        &self,
263        assets: &[AssetLocation],
264        cmd: &str,
265        args: &[String],
266        after: Option<Duration>,
267    ) -> Result<(), ProviderError>;
268
269    async fn destroy(&self) -> Result<(), ProviderError>;
270
271    async fn snapshot_db(&self, is_cumulus_based: bool) -> Result<InnerSnapshotDb, ProviderError>;
272}
273
274pub type DynNode = Arc<dyn ProviderNode + Send + Sync>;
275
276// re-export
277pub use docker::*;
278pub use kubernetes::*;
279pub use native::*;
280pub use shared::{constants, types};