From 8fa7022a2ca45e97e01e6a0bb3c8e2068b2cf69e Mon Sep 17 00:00:00 2001 From: Zander Franks Date: Sat, 6 Apr 2024 16:34:53 -0500 Subject: [PATCH 1/2] feat(agent): pass env and node_key into node state Signed-off-by: Zander Franks --- crates/snops-agent/src/rpc.rs | 10 +- crates/snops-common/src/state.rs | 15 ++- crates/snops/src/env/mod.rs | 2 +- crates/snops/src/schema/mod.rs | 58 ++++----- crates/snops/src/schema/nodes.rs | 64 +++++---- crates/snops/src/schema/storage.rs | 46 +++---- crates/snops/src/schema/timeline.rs | 193 ++++++++++++++-------------- 7 files changed, 203 insertions(+), 185 deletions(-) diff --git a/crates/snops-agent/src/rpc.rs b/crates/snops-agent/src/rpc.rs index 46ab91fe..2d18cbe5 100644 --- a/crates/snops-agent/src/rpc.rs +++ b/crates/snops-agent/src/rpc.rs @@ -262,10 +262,8 @@ impl AgentService for AgentRpcServer { }; command - // .kill_on_drop(true) .stdout(Stdio::piped()) .stderr(Stdio::piped()) - // .stdin(Stdio::null()) .arg("--log") .arg(state.cli.path.join(SNARKOS_LOG_FILE)) .arg("run") @@ -288,6 +286,11 @@ impl AgentService for AgentRpcServer { .arg("--node") .arg(state.cli.ports.node.to_string()); + // inject environment variables + for (key, val) in &node.env { + command.env(key, val); + } + match node.private_key { KeyState::None => {} KeyState::Local => { @@ -497,7 +500,8 @@ impl AgentService for AgentRpcServer { ) -> Result<(), AgentError> { info!("executing authorization..."); - // TODO: maybe in the env config store a branch label for the binary so it won't be put in storage and won't overwrite itself + // TODO: maybe in the env config store a branch label for the binary so it won't + // be put in storage and won't overwrite itself // download the snarkOS binary api::check_binary( diff --git a/crates/snops-common/src/state.rs b/crates/snops-common/src/state.rs index 0dfa87ff..b2a06da6 100644 --- a/crates/snops-common/src/state.rs +++ b/crates/snops-common/src/state.rs @@ -1,4 +1,5 @@ use std::{ + collections::HashMap, fmt::{Display, Write}, net::SocketAddr, str::FromStr, @@ -55,6 +56,7 @@ impl AgentState { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NodeState { + pub node_key: NodeKey, pub ty: NodeType, pub private_key: KeyState, /// Increment the usize whenever the request is updated. @@ -63,6 +65,7 @@ pub struct NodeState { pub online: bool, pub peers: Vec, pub validators: Vec, + pub env: HashMap, } /// A representation of which key to use for the agent. @@ -234,8 +237,8 @@ mod strings { } /// for some reason bincode does not allow deserialize_any so if i want to allow -/// end users to type "top", 42, or "persist" i need to do have to copies of this -/// where one is not untagged. +/// end users to type "top", 42, or "persist" i need to do have to copies of +/// this where one is not untagged. /// /// bincode. please. #[derive(Debug, Copy, Default, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -248,8 +251,8 @@ pub enum DocHeightRequest { /// Set the height to the given block Absolute(u32), /// Use the same ledger as configured when the same storage was used. - /// WARNING: this may create issues if the same storage id is reused between tests - /// with different nodes. + /// WARNING: this may create issues if the same storage id is reused between + /// tests with different nodes. #[serde(with = "strings::persist")] Persist, // the control plane doesn't know the heights the nodes are at @@ -266,8 +269,8 @@ pub enum HeightRequest { /// Set the height to the given block Absolute(u32), /// Use the same ledger as configured when the same storage was used. - /// WARNING: this may create issues if the same storage id is reused between tests - /// with different nodes. + /// WARNING: this may create issues if the same storage id is reused between + /// tests with different nodes. Persist, // the control plane doesn't know the heights the nodes are at // TruncateHeight(u32), diff --git a/crates/snops/src/env/mod.rs b/crates/snops/src/env/mod.rs index 2728636c..34423ae0 100644 --- a/crates/snops/src/env/mod.rs +++ b/crates/snops/src/env/mod.rs @@ -433,7 +433,7 @@ pub async fn initial_reconcile(env_id: usize, state: &GlobalState) -> Result<(), }; // resolve the peers and validators - let mut node_state = node.into_state(key.ty); + let mut node_state = node.into_state(key.to_owned()); node_state.private_key = node .key .as_ref() diff --git a/crates/snops/src/schema/mod.rs b/crates/snops/src/schema/mod.rs index 36b743f7..c848a1c3 100644 --- a/crates/snops/src/schema/mod.rs +++ b/crates/snops/src/schema/mod.rs @@ -58,42 +58,42 @@ pub enum NodeTargets { Many(Vec), } -struct NodeTargetsVisitor; +impl<'de> Deserialize<'de> for NodeTargets { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct NodeTargetsVisitor; -impl<'de> Visitor<'de> for NodeTargetsVisitor { - type Value = NodeTargets; + impl<'de> Visitor<'de> for NodeTargetsVisitor { + type Value = NodeTargets; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("one or more node targets") - } + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("one or more node targets") + } - fn visit_str(self, v: &str) -> Result { - Ok(NodeTargets::One(FromStr::from_str(v).map_err(E::custom)?)) - } + fn visit_str(self, v: &str) -> Result { + Ok(NodeTargets::One(FromStr::from_str(v).map_err(E::custom)?)) + } - fn visit_seq(self, mut seq: A) -> Result - where - A: serde::de::SeqAccess<'de>, - { - let mut buf = vec![]; + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut buf = vec![]; - while let Some(elem) = seq.next_element()? { - buf.push(NodeTarget::from_str(elem).map_err(A::Error::custom)?); - } + while let Some(elem) = seq.next_element()? { + buf.push(NodeTarget::from_str(elem).map_err(A::Error::custom)?); + } - Ok(if buf.is_empty() { - NodeTargets::None - } else { - NodeTargets::Many(buf) - }) - } -} + Ok(if buf.is_empty() { + NodeTargets::None + } else { + NodeTargets::Many(buf) + }) + } + } -impl<'de> Deserialize<'de> for NodeTargets { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { deserializer.deserialize_any(NodeTargetsVisitor) } } diff --git a/crates/snops/src/schema/nodes.rs b/crates/snops/src/schema/nodes.rs index 8ca5c534..0eb84e18 100644 --- a/crates/snops/src/schema/nodes.rs +++ b/crates/snops/src/schema/nodes.rs @@ -1,4 +1,9 @@ -use std::{collections::HashSet, fmt::Display, net::SocketAddr, str::FromStr}; +use std::{ + collections::{HashMap, HashSet}, + fmt::Display, + net::SocketAddr, + str::FromStr, +}; use fixedbitset::FixedBitSet; use indexmap::IndexMap; @@ -7,7 +12,7 @@ use serde::{de::Visitor, Deserialize, Deserializer, Serialize}; use snops_common::{ lasso::Spur, set::{MaskBit, MASK_PREFIX_LEN}, - state::{AgentId, DocHeightRequest, KeyState, NodeState, NodeType}, + state::{AgentId, DocHeightRequest, NodeState}, INTERN, }; @@ -90,22 +95,25 @@ pub struct Node { /// List of peers for the node to connect to #[serde(default)] pub peers: NodeTargets, + + /// Environment variables to inject into the snarkOS process. + #[serde(default)] + pub env: HashMap, } impl Node { - pub fn into_state(&self, ty: NodeType) -> NodeState { + pub fn into_state(&self, node_key: NodeKey) -> NodeState { NodeState { - ty, - private_key: KeyState::None, - - // TODO + ty: node_key.ty, + node_key, + private_key: Default::default(), height: (0, self.height.into()), - online: self.online, + env: self.env.clone(), // these are resolved later - validators: vec![], - peers: vec![], + validators: Default::default(), + peers: Default::default(), } } @@ -142,28 +150,30 @@ pub enum KeySource { Named(String, Option), } -struct KeySourceVisitor; - -impl<'de> Visitor<'de> for KeySourceVisitor { - type Value = KeySource; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a string that represents an aleo private key, or a file from storage") - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - KeySource::from_str(v).map_err(E::custom) - } -} - impl<'de> Deserialize<'de> for KeySource { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { + struct KeySourceVisitor; + + impl<'de> Visitor<'de> for KeySourceVisitor { + type Value = KeySource; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str( + "a string that represents an aleo private key, or a file from storage", + ) + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + KeySource::from_str(v).map_err(E::custom) + } + } + deserializer.deserialize_str(KeySourceVisitor) } } diff --git a/crates/snops/src/schema/storage.rs b/crates/snops/src/schema/storage.rs index 4631a820..131e55c1 100644 --- a/crates/snops/src/schema/storage.rs +++ b/crates/snops/src/schema/storage.rs @@ -129,34 +129,34 @@ impl Default for LedgerGeneration { #[derive(Debug, Clone, Serialize)] pub struct FilenameString(String); -struct FilenameStringVisitor; - -impl<'de> Visitor<'de> for FilenameStringVisitor { - type Value = FilenameString; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a string that can be used as a filename") - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - if v.contains('/') { - Err(E::custom("filename string cannot have a path separator")) - } else if v == "." || v == ".." { - Err(E::custom("filename string cannot be relative")) - } else { - Ok(FilenameString(String::from(v))) - } - } -} - impl<'de> Deserialize<'de> for FilenameString { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { + struct FilenameStringVisitor; + + impl<'de> Visitor<'de> for FilenameStringVisitor { + type Value = FilenameString; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a string that can be used as a filename") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + if v.contains('/') { + Err(E::custom("filename string cannot have a path separator")) + } else if v == "." || v == ".." { + Err(E::custom("filename string cannot be relative")) + } else { + Ok(FilenameString(String::from(v))) + } + } + } + deserializer.deserialize_str(FilenameStringVisitor) } } diff --git a/crates/snops/src/schema/timeline.rs b/crates/snops/src/schema/timeline.rs index ff4f945b..6231a4a5 100644 --- a/crates/snops/src/schema/timeline.rs +++ b/crates/snops/src/schema/timeline.rs @@ -52,47 +52,47 @@ pub enum Action { Height(IndexMap), } -struct ActionsVisitor; - -impl<'de> Visitor<'de> for ActionsVisitor { - type Value = Actions; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("possibly awaited action map") - } - - fn visit_map(self, mut map: A) -> Result - where - A: serde::de::MapAccess<'de>, - { - let mut buf = vec![]; - - while let Some(key) = map.next_key::<&str>()? { - // determine if this action is being awaited - let (key, awaited) = match key { - key if key.ends_with(".await") => (key.split_at(key.len() - 6).0, true), - _ => (key, false), - }; - - buf.push(ActionInstance { - awaited, - action: match key { - "online" => Action::Online(map.next_value()?), - "offline" => Action::Offline(map.next_value()?), - "cannon" => Action::Cannon(map.next_value()?), - "height" => Action::Height(map.next_value()?), - - _ => return Err(A::Error::custom(format!("unsupported action {key}"))), - }, - }); - } - - Ok(Actions(buf)) - } -} - impl<'de> Deserialize<'de> for Actions { fn deserialize>(deserializer: D) -> Result { + struct ActionsVisitor; + + impl<'de> Visitor<'de> for ActionsVisitor { + type Value = Actions; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("possibly awaited action map") + } + + fn visit_map(self, mut map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + let mut buf = vec![]; + + while let Some(key) = map.next_key::<&str>()? { + // determine if this action is being awaited + let (key, awaited) = match key { + key if key.ends_with(".await") => (key.split_at(key.len() - 6).0, true), + _ => (key, false), + }; + + buf.push(ActionInstance { + awaited, + action: match key { + "online" => Action::Online(map.next_value()?), + "offline" => Action::Offline(map.next_value()?), + "cannon" => Action::Cannon(map.next_value()?), + "height" => Action::Height(map.next_value()?), + + _ => return Err(A::Error::custom(format!("unsupported action {key}"))), + }, + }); + } + + Ok(Actions(buf)) + } + } + deserializer.deserialize_map(ActionsVisitor) } } @@ -104,39 +104,39 @@ pub enum NodeTarget { All, } -struct NodeTargetVisitor; - -impl<'de> Visitor<'de> for NodeTargetVisitor { - type Value = NodeTarget; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a list of node IDs or the string \"all\"") - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - match v { - "all" => Ok(NodeTarget::All), - _ => Err(E::custom("string must be \"all\"")), - } - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: serde::de::SeqAccess<'de>, - { - let mut buf = Vec::new(); - while let Some(id) = seq.next_element()? { - buf.push(id); - } - Ok(NodeTarget::Some(buf)) - } -} - impl<'de> Deserialize<'de> for NodeTarget { fn deserialize>(deserializer: D) -> Result { + struct NodeTargetVisitor; + + impl<'de> Visitor<'de> for NodeTargetVisitor { + type Value = NodeTarget; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a list of node IDs or the string \"all\"") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + match v { + "all" => Ok(NodeTarget::All), + _ => Err(E::custom("string must be \"all\"")), + } + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut buf = Vec::new(); + while let Some(id) = seq.next_element()? { + buf.push(id); + } + Ok(NodeTarget::Some(buf)) + } + } + deserializer.deserialize_any(NodeTargetVisitor) } } @@ -147,34 +147,35 @@ pub enum EventDuration { Blocks(u64), } -struct EventDurationVisitor; - -impl<'de> Visitor<'de> for EventDurationVisitor { - type Value = EventDuration; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a string duration or an integer number of blocks to be produced") - } - - fn visit_u64(self, v: u64) -> Result - where - E: serde::de::Error, - { - Ok(EventDuration::Blocks(v)) - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - Ok(EventDuration::Time( - duration_str::parse(v).map_err(E::custom)?, - )) - } -} - impl<'de> Deserialize<'de> for EventDuration { fn deserialize>(deserializer: D) -> Result { + struct EventDurationVisitor; + + impl<'de> Visitor<'de> for EventDurationVisitor { + type Value = EventDuration; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter + .write_str("a string duration or an integer number of blocks to be produced") + } + + fn visit_u64(self, v: u64) -> Result + where + E: serde::de::Error, + { + Ok(EventDuration::Blocks(v)) + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + Ok(EventDuration::Time( + duration_str::parse(v).map_err(E::custom)?, + )) + } + } + deserializer.deserialize_any(EventDurationVisitor) } } From 87c509e99404be0474f83f4913be1ae404519930 Mon Sep 17 00:00:00 2001 From: Zander Franks Date: Sat, 6 Apr 2024 20:39:54 -0500 Subject: [PATCH 2/2] refactor(agent): use .envs instead of .env Signed-off-by: Zander Franks --- crates/snops-agent/src/rpc.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/snops-agent/src/rpc.rs b/crates/snops-agent/src/rpc.rs index 2d18cbe5..32f78bd0 100644 --- a/crates/snops-agent/src/rpc.rs +++ b/crates/snops-agent/src/rpc.rs @@ -264,6 +264,7 @@ impl AgentService for AgentRpcServer { command .stdout(Stdio::piped()) .stderr(Stdio::piped()) + .envs(&node.env) .arg("--log") .arg(state.cli.path.join(SNARKOS_LOG_FILE)) .arg("run") @@ -286,11 +287,6 @@ impl AgentService for AgentRpcServer { .arg("--node") .arg(state.cli.ports.node.to_string()); - // inject environment variables - for (key, val) in &node.env { - command.env(key, val); - } - match node.private_key { KeyState::None => {} KeyState::Local => {