Skip to content

Commit

Permalink
feat(jstzd): implement baker
Browse files Browse the repository at this point in the history
  • Loading branch information
ryutamago committed Oct 22, 2024
1 parent 9942dd3 commit 408b355
Show file tree
Hide file tree
Showing 10 changed files with 461 additions and 46 deletions.
6 changes: 5 additions & 1 deletion crates/jstzd/src/protocol/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ impl Display for ProtocolConstants {
}
}

#[derive(PartialEq, Eq, Debug)]
#[derive(PartialEq, Eq, Debug, Clone)]
pub enum Protocol {
Alpha,
ParisC,
Quebec,
}

impl Default for Protocol {
Expand All @@ -42,6 +44,8 @@ impl Protocol {
fn hash(&self) -> &'static str {
match self {
Protocol::Alpha => "ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK",
Protocol::ParisC => "PsParisCZo7KAh1Z1smVd9ZMZ1HHn5gkzbM94V3PLCpknFWhUAi",
Protocol::Quebec => "PsQubecQubecQubecQubecQubecQubecQubecQubecQubec",
}
}

Expand Down
60 changes: 60 additions & 0 deletions crates/jstzd/src/task/child_wrapper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use async_dropper_simple::{AsyncDrop, AsyncDropper};
use async_trait::async_trait;
use std::sync::Arc;
use tokio::process::Child;
use tokio::sync::RwLock;

pub type SharedChildWrapper = Arc<RwLock<AsyncDropper<ChildWrapper>>>;

#[derive(Default)]
pub struct ChildWrapper {
inner: Option<Child>,
}

impl ChildWrapper {
fn new(child: Child) -> Self {
Self { inner: Some(child) }
}

pub fn new_shared(child: Child) -> SharedChildWrapper {
Arc::new(RwLock::new(AsyncDropper::new(Self::new(child))))
}

pub async fn kill(&mut self) -> anyhow::Result<()> {
if let Some(mut v) = self.inner.take() {
v.kill().await?;
}
Ok(())
}
/// Check if the child process is running
/// mutable borrow because the process id could be reaped if it exited
pub async fn is_running(&mut self) -> bool {
self.inner
.as_mut()
.map_or(false, |child| matches!(child.try_wait(), Ok(None)))
}
}

#[async_trait]
impl AsyncDrop for ChildWrapper {
async fn async_drop(&mut self) {
let _ = self.kill().await;
}
}

#[cfg(test)]
mod test {
use super::*;

#[tokio::test(flavor = "multi_thread")]
async fn test_child() {
let child = tokio::process::Command::new("sleep")
.arg("1")
.spawn()
.unwrap();
let wrapper = ChildWrapper::new_shared(child);
assert!(wrapper.write().await.is_running().await);
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
assert!(!wrapper.write().await.is_running().await);
}
}
10 changes: 10 additions & 0 deletions crates/jstzd/src/task/directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ impl TryFrom<PathBuf> for Directory {
}
}

impl TryFrom<&Directory> for PathBuf {
type Error = anyhow::Error;
fn try_from(dir: &Directory) -> Result<PathBuf> {
match dir {
Directory::TempDir(temp_dir) => Ok(temp_dir.path().to_path_buf()),
Directory::Path(path) => Ok(path.as_path().to_path_buf()),
}
}
}

impl TryFrom<&Directory> for String {
type Error = anyhow::Error;
fn try_from(dir: &Directory) -> Result<Self> {
Expand Down
2 changes: 2 additions & 0 deletions crates/jstzd/src/task/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
mod child_wrapper;
pub mod directory;
pub mod octez_baker;
pub mod octez_client;
pub mod octez_node;

Expand Down
222 changes: 222 additions & 0 deletions crates/jstzd/src/task/octez_baker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
use crate::protocol::Protocol;

use super::{
child_wrapper::{ChildWrapper, SharedChildWrapper},
octez_client::OctezClient,
Task,
};
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use octez::{Endpoint, OctezNodeConfig};

use std::{fmt::Display, path::PathBuf};
use tokio::process::Command;

#[derive(PartialEq, Debug, Clone)]
pub enum BakerBinaryPath {
BuiltIn(Protocol), // The binary exists in $PATH
Custom(PathBuf), // The binary is at the given path
}

impl Display for BakerBinaryPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BakerBinaryPath::BuiltIn(Protocol::Alpha) => write!(f, "octez-baker-alpha"),
BakerBinaryPath::BuiltIn(Protocol::ParisC) => {
write!(f, "octez-baker-PsParisC")
}
BakerBinaryPath::BuiltIn(Protocol::Quebec) => {
write!(f, "octez-baker-PsQuebec")
}
BakerBinaryPath::Custom(path) => write!(f, "{}", path.to_string_lossy()),
}
}
}

#[allow(dead_code)]
pub struct OctezBakerConfig {
binary_path: BakerBinaryPath,
octez_client_base_dir: PathBuf,
octez_node_data_dir: PathBuf,
octez_node_endpoint: Endpoint,
}

#[derive(Default)]
pub struct OctezBakerConfigBuilder {
binary_path: Option<BakerBinaryPath>,
octez_client_base_dir: Option<PathBuf>,
octez_node_data_dir: Option<PathBuf>,
octez_node_endpoint: Option<Endpoint>,
}

impl OctezBakerConfigBuilder {
pub fn new() -> Self {
OctezBakerConfigBuilder::default()
}

pub fn set_binary_path(mut self, binary_path: BakerBinaryPath) -> Self {
self.binary_path = Some(binary_path);
self
}

pub fn set_octez_client_base_dir(mut self, base_dir: &str) -> Self {
self.octez_client_base_dir = Some(PathBuf::from(base_dir));
self
}

pub fn set_octez_node_data_dir(mut self, data_dir: &str) -> Self {
self.octez_node_data_dir = Some(PathBuf::from(data_dir));
self
}

pub fn set_octez_node_endpoint(mut self, endpoint: &Endpoint) -> Self {
self.octez_node_endpoint = Some(endpoint.clone());
self
}

pub fn with_node_and_client(
mut self,
node_config: &OctezNodeConfig,
client: &OctezClient,
) -> Self {
self.octez_node_data_dir = Some(node_config.data_dir.clone());
let endpoint = &node_config.rpc_endpoint;
self.octez_node_endpoint = Some(endpoint.clone());
self.octez_client_base_dir = Some(PathBuf::try_from(client.base_dir()).unwrap());
self
}

pub fn build(self) -> Result<OctezBakerConfig> {
Ok(OctezBakerConfig {
binary_path: self.binary_path.ok_or(anyhow!("binary path not set"))?,
octez_client_base_dir: self
.octez_client_base_dir
.ok_or(anyhow!("octez_client_base_dir not set"))?,
octez_node_data_dir: self
.octez_node_data_dir
.clone()
.ok_or(anyhow!("octez_node_data_dir not set"))?,
octez_node_endpoint: self
.octez_node_endpoint
.ok_or(anyhow!("octez_node_endpoint not set"))?,
})
}
}

#[allow(dead_code)]
pub struct OctezBaker {
inner: SharedChildWrapper,
}

#[async_trait]
impl Task for OctezBaker {
type Config = OctezBakerConfig;

async fn spawn(config: Self::Config) -> Result<Self> {
let mut command = Command::new(config.binary_path.to_string());
command.args([
"--base-dir",
&config.octez_client_base_dir.to_string_lossy(),
"--endpoint",
&config.octez_node_endpoint.to_string(),
"run",
"remotely",
"--liquidity-baking-toggle-vote",
"pass",
]);
let child = command.spawn()?;
let inner = ChildWrapper::new_shared(child);
Ok(OctezBaker { inner })
}

async fn kill(&mut self) -> Result<()> {
let mut lock = self.inner.write().await;
lock.kill().await
}

async fn health_check(&self) -> Result<bool> {
let mut lock = self.inner.write().await;
Ok(lock.inner_mut().is_running().await)
}
}

#[cfg(test)]
mod test {
use http::Uri;
use octez::OctezNodeConfigBuilder;
use tempfile::TempDir;

use crate::task::octez_client::OctezClientBuilder;

use super::*;
#[test]
fn test_octez_baker_config_builder() {
let base_dir = TempDir::new().unwrap();
let data_dir = TempDir::new().unwrap();
let endpoint =
Endpoint::try_from(Uri::from_static("http://localhost:8732")).unwrap();
let config: OctezBakerConfig = OctezBakerConfigBuilder::new()
.set_binary_path(BakerBinaryPath::BuiltIn(Protocol::Alpha))
.set_octez_client_base_dir(base_dir.path().to_str().unwrap())
.set_octez_node_data_dir(data_dir.path().to_str().unwrap())
.set_octez_node_endpoint(&endpoint)
.build()
.unwrap();
assert_eq!(
config.binary_path,
BakerBinaryPath::BuiltIn(Protocol::Alpha)
);
assert_eq!(config.octez_client_base_dir, base_dir.path());
assert_eq!(config.octez_node_data_dir, data_dir.path());
assert_eq!(config.octez_node_endpoint, endpoint);
}

#[test]
fn octez_baker_config_builder_fails_without_binary_path() {
let base_dir = TempDir::new().unwrap();
let data_dir = TempDir::new().unwrap();
let endpoint =
Endpoint::try_from(Uri::from_static("http://localhost:8732")).unwrap();
let config: Result<OctezBakerConfig> = OctezBakerConfigBuilder::new()
.set_octez_client_base_dir(base_dir.path().to_str().unwrap())
.set_octez_node_data_dir(data_dir.path().to_str().unwrap())
.set_octez_node_endpoint(&endpoint)
.build();
assert!(config.is_err_and(|e| e.to_string().contains("binary path not set")));
}

#[tokio::test]
async fn test_with_node_config_and_client() {
let node_endpoint =
Endpoint::try_from(Uri::from_static("http://localhost:8732")).unwrap();
let temp_dir = TempDir::new().unwrap();
let data_dir: &std::path::Path = temp_dir.path();
let node_config = OctezNodeConfigBuilder::new()
.set_binary_path("octez-node")
.set_network("sandbox")
.set_rpc_endpoint(&node_endpoint)
.set_data_dir(data_dir.to_str().unwrap())
.build()
.expect("Failed to build node config");

let temp_dir = TempDir::new().unwrap();
let base_dir: std::path::PathBuf = temp_dir.path().to_path_buf();
let octez_client = OctezClientBuilder::new()
.set_endpoint(node_endpoint.clone())
.set_base_dir(base_dir.clone())
.build()
.expect("Failed to build octez client");
let config: OctezBakerConfig = OctezBakerConfigBuilder::new()
.set_binary_path(BakerBinaryPath::BuiltIn(Protocol::Alpha))
.with_node_and_client(&node_config, &octez_client)
.build()
.unwrap();
assert_eq!(
config.binary_path,
BakerBinaryPath::BuiltIn(Protocol::Alpha)
);
assert_eq!(config.octez_client_base_dir, base_dir);
assert_eq!(config.octez_node_data_dir, data_dir);
assert_eq!(config.octez_node_endpoint, node_endpoint);
}
}
4 changes: 4 additions & 0 deletions crates/jstzd/src/task/octez_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ impl OctezClient {
}
}

pub fn base_dir(&self) -> &Directory {
&self.base_dir
}

pub async fn config_init(&self, output_path: &Path) -> Result<()> {
let output = output_path
.to_str()
Expand Down
Loading

0 comments on commit 408b355

Please sign in to comment.