-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a211452
commit 62b1ffa
Showing
8 changed files
with
364 additions
and
31 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
pub mod octez_node; | ||
|
||
use anyhow::Result; | ||
use async_trait::async_trait; | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,294 @@ | ||
use super::Task; | ||
use anyhow::Result; | ||
use async_dropper_simple::{AsyncDrop, AsyncDropper}; | ||
use async_trait::async_trait; | ||
use std::{fs::File, path::PathBuf, sync::Arc}; | ||
use tokio::sync::RwLock; | ||
|
||
#[cfg(test)] | ||
use tests::{Child, InnerOctezNode}; | ||
|
||
#[cfg(not(test))] | ||
use octez::OctezNode as InnerOctezNode; | ||
#[cfg(not(test))] | ||
use std::process::Child; | ||
|
||
#[derive(Clone)] | ||
pub struct OctezNodeConfig { | ||
binary_path: PathBuf, | ||
data_dir: PathBuf, | ||
network: String, | ||
rpc_endpoint: String, | ||
log_file: PathBuf, | ||
options: Vec<String>, | ||
} | ||
|
||
#[derive(Default)] | ||
pub struct OctezNodeConfigBuilder { | ||
binary_path: Option<PathBuf>, | ||
data_dir: Option<PathBuf>, | ||
network: Option<String>, | ||
rpc_endpoint: Option<String>, | ||
log_file: Option<PathBuf>, | ||
options: Option<Vec<String>>, | ||
} | ||
|
||
impl OctezNodeConfigBuilder { | ||
pub fn new() -> Self { | ||
OctezNodeConfigBuilder::default() | ||
} | ||
|
||
pub fn set_binary_path(&mut self, path: &str) -> &mut Self { | ||
self.binary_path = Some(PathBuf::from(path)); | ||
self | ||
} | ||
|
||
pub fn set_data_dir(&mut self, path: &str) -> &mut Self { | ||
self.data_dir = Some(PathBuf::from(path)); | ||
self | ||
} | ||
|
||
pub fn set_network(&mut self, network: &str) -> &mut Self { | ||
self.network = Some(network.to_owned()); | ||
self | ||
} | ||
|
||
pub fn set_rpc_endpoint(&mut self, endpoint: &str) -> &mut Self { | ||
self.rpc_endpoint = Some(endpoint.to_owned()); | ||
self | ||
} | ||
|
||
pub fn set_log_file(&mut self, path: &str) -> &mut Self { | ||
self.log_file = Some(PathBuf::from(path)); | ||
self | ||
} | ||
|
||
pub fn set_run_options(&mut self, options: &[&str]) -> &mut Self { | ||
self.options = Some( | ||
options | ||
.iter() | ||
.map(|v| (*v).to_owned()) | ||
.collect::<Vec<String>>(), | ||
); | ||
self | ||
} | ||
|
||
pub fn build(&mut self) -> Result<OctezNodeConfig> { | ||
Ok(OctezNodeConfig { | ||
binary_path: self | ||
.binary_path | ||
.take() | ||
.unwrap_or(PathBuf::from("octez-node")), | ||
data_dir: self | ||
.data_dir | ||
.take() | ||
.unwrap_or(PathBuf::from("/tmp/.octez-node")), | ||
network: self.network.take().unwrap_or("sandbox".to_owned()), | ||
rpc_endpoint: self | ||
.rpc_endpoint | ||
.take() | ||
.unwrap_or("localhost:8732".to_owned()), | ||
log_file: self | ||
.log_file | ||
.take() | ||
.unwrap_or(PathBuf::from("/tmp/octez-node.log")), | ||
options: self.options.take().unwrap_or_default(), | ||
}) | ||
} | ||
} | ||
|
||
#[derive(Default)] | ||
struct ChildWrapper { | ||
inner: Option<Child>, | ||
} | ||
|
||
impl ChildWrapper { | ||
pub async fn kill(&mut self) -> anyhow::Result<()> { | ||
if let Some(mut v) = self.inner.take() { | ||
return Ok(v.kill()?); | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
#[async_trait] | ||
impl AsyncDrop for ChildWrapper { | ||
async fn async_drop(&mut self) { | ||
let _ = self.kill().await; | ||
} | ||
} | ||
|
||
#[derive(Default, Clone)] | ||
pub struct OctezNode { | ||
inner: Arc<RwLock<AsyncDropper<ChildWrapper>>>, | ||
} | ||
|
||
#[async_trait] | ||
impl Task for OctezNode { | ||
type Config = OctezNodeConfig; | ||
|
||
/// Spins up the task with the given config. | ||
async fn spawn(config: Self::Config) -> Result<Self> { | ||
let node = InnerOctezNode { | ||
octez_node_bin: Some(config.binary_path), | ||
octez_node_dir: config.data_dir, | ||
}; | ||
|
||
node.config_init(&config.network, "localhost:8731", &config.rpc_endpoint, 0)?; | ||
node.generate_identity()?; | ||
Ok(OctezNode { | ||
inner: Arc::new(RwLock::new(AsyncDropper::new(ChildWrapper { | ||
inner: Some( | ||
node.run( | ||
&File::create(&config.log_file)?, | ||
config | ||
.options | ||
.iter() | ||
.map(|s| s as &str) | ||
.collect::<Vec<&str>>() | ||
.as_slice(), | ||
)?, | ||
), | ||
}))), | ||
}) | ||
} | ||
|
||
/// Aborts the running task. | ||
async fn kill(&mut self) -> Result<()> { | ||
let mut inner = self.inner.write().await; | ||
Ok(inner.inner_mut().kill().await?) | ||
} | ||
|
||
/// Conducts a health check on the running task. | ||
async fn health_check(&self) -> Result<bool> { | ||
todo!() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::{OctezNode, OctezNodeConfigBuilder}; | ||
use crate::task::Task; | ||
use anyhow::Result; | ||
use rand::{distributions::Alphanumeric, Rng}; | ||
use std::{fs::File, path::PathBuf}; | ||
|
||
pub struct Child { | ||
file_path: Option<String>, | ||
} | ||
|
||
impl Child { | ||
pub fn kill(&mut self) -> std::io::Result<()> { | ||
if self.file_path.is_none() { | ||
return Err(std::io::Error::new( | ||
std::io::ErrorKind::Other, | ||
anyhow::anyhow!("failed"), | ||
)); | ||
} | ||
File::create(self.file_path.as_ref().unwrap()).unwrap(); | ||
Ok(()) | ||
} | ||
} | ||
|
||
pub struct InnerOctezNode { | ||
pub octez_node_bin: Option<PathBuf>, | ||
pub octez_node_dir: PathBuf, | ||
} | ||
|
||
impl InnerOctezNode { | ||
pub fn config_init( | ||
&self, | ||
_network: &str, | ||
_http_endpoint: &str, | ||
_rpc_endpoint: &str, | ||
_num_connections: u32, | ||
) -> Result<()> { | ||
Ok(()) | ||
} | ||
|
||
pub fn run(&self, _log_file: &File, options: &[&str]) -> Result<Child> { | ||
let file_path = if !options.is_empty() { | ||
Some(options[0].to_owned()) | ||
} else { | ||
None | ||
}; | ||
Ok(Child { file_path }) | ||
} | ||
|
||
pub fn generate_identity(&self) -> Result<()> { | ||
Ok(()) | ||
} | ||
} | ||
|
||
fn get_random_string() -> String { | ||
rand::thread_rng() | ||
.sample_iter(&Alphanumeric) | ||
.take(16) | ||
.map(char::from) | ||
.collect::<String>() | ||
} | ||
|
||
fn get_temp_file_path() -> String { | ||
let dir = std::env::temp_dir(); | ||
let path = dir.join(format!("jstzd-test-{}.txt", get_random_string())); | ||
path.into_os_string().into_string().unwrap() | ||
} | ||
|
||
#[tokio::test(flavor = "multi_thread")] | ||
async fn kill_should_succeed() { | ||
let file_path = get_temp_file_path(); | ||
|
||
// The mock child is made to create a file based on the first item in | ||
// `options` when it is killed | ||
let mut f = OctezNode::spawn( | ||
OctezNodeConfigBuilder::new() | ||
.set_binary_path("/tmp/binary") | ||
.set_data_dir("/tmp/something") | ||
.set_network("sandbox") | ||
.set_rpc_endpoint("localhost:8732") | ||
.set_log_file(&get_temp_file_path()) | ||
.set_run_options(&[&file_path]) | ||
.build() | ||
.unwrap(), | ||
) | ||
.await | ||
.unwrap(); | ||
|
||
let target_file = PathBuf::from(&file_path); | ||
let op = f.inner.read().await; | ||
assert!(op.inner.is_some()); | ||
assert!(!target_file.exists()); | ||
drop(op); | ||
|
||
assert!(f.kill().await.is_ok()); | ||
let op = f.inner.read().await; | ||
assert!(op.inner.is_none()); | ||
assert!(target_file.exists()); | ||
} | ||
|
||
#[tokio::test(flavor = "multi_thread")] | ||
async fn kill_should_fail() { | ||
// The mock child is made to fail the `kill` call when it is given | ||
// an empty list of options | ||
let mut f = OctezNode::spawn( | ||
OctezNodeConfigBuilder::new() | ||
.set_binary_path("/tmp/binary") | ||
.set_data_dir("/tmp/something") | ||
.set_network("sandbox") | ||
.set_rpc_endpoint("localhost:8732") | ||
.set_log_file(&get_temp_file_path()) | ||
.set_run_options(&[]) | ||
.build() | ||
.unwrap(), | ||
) | ||
.await | ||
.unwrap(); | ||
|
||
let op = f.inner.read().await; | ||
assert!(op.inner.is_some()); | ||
drop(op); | ||
|
||
let res = f.kill().await; | ||
assert_eq!(res.unwrap_err().to_string(), "failed"); | ||
} | ||
} |
Oops, something went wrong.